Authenticating Requests →
Every request to the Dailybin API needs a tenant-scoped JWT. You get one automatically when you sign in to the dashboard, but you can also create dedicated tokens for individual agents.
Pass your token in the
Authorization header as a Bearer token.
| Method | Example |
|---|---|
| Bearer header | Authorization: Bearer <jwt> |
Understanding scopes
Tokens are scoped so you can give each agent only the permissions it needs. A weather
agent that only posts content needs ingest:write;
a monitoring script that checks delivery status needs digest:read.
| Scope | Grants |
|---|---|
| ingest:write | Submit content via /v1/mail |
| digest:read | List upcoming digests, preview, list sends |
| digest:retry | Retry failed digest sends |
| token:manage | Create and revoke agent tokens |
Adding a Section →
To add content to your next daily digest, POST a JSON object with a
section name and some markdown
content.
Dailybin groups entries by section, deduplicates them per day, and rolls everything
into a single email on schedule.
If two agents post the same content to the same section on the same day, only one copy makes it into the digest. You can safely fire-and-forget from multiple sources without worrying about duplicates.
Scope: ingest:write
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| section | string | yes | Section heading (1-64 chars) |
| content | string | yes | Markdown content (1-20KB) |
| source | string | no | Agent name / identifier (1-80 chars) |
Response (202)
{
"accepted": true,
"duplicate": false,
"digestDate": "2026-02-15",
"submissionId": "sub_01abc..."
} Example
Here's a weather agent posting its morning report. The section name becomes the heading in the digest email:
$ curl -X POST https://api.dailybin.dev/v1/mail \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"section": "weather",
"content": "Sunny, 72°F. Great day ahead.",
"source": "weather-agent"
}' Managing Tokens →
The dashboard gives you a default token that works for most setups. If you need finer control — separate tokens per agent, limited scopes, or expiration dates — you can create and revoke tokens through the API.
Requires token:manage scope.
Creating a token
Give the token a name you'll recognize later and the scopes it needs. The response includes the signed JWT — store it somewhere safe, because you won't see it again.
POST /v1/admin/tokens
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | yes | Human-readable token name (1-80 chars) |
| scopes | string[] | yes | Array of scope strings |
| expiresAt | datetime | no | ISO 8601 expiration time |
{
"id": "tok_01abc...",
"name": "weather-agent",
"jti": "jti_...",
"scopes": ["ingest:write"],
"expiresAt": null,
"createdAt": "2026-02-15T12:00:00Z",
"token": "eyJhbGciOi..."
} Revoking a token
If a token is compromised or an agent is retired, revoke it. The token becomes immediately unusable — any in-flight requests using it will fail with a 401.
POST /v1/admin/tokens/:tokenId/revoke
{
"revoked": true,
"tokenId": "tok_01abc...",
"revokedAt": "2026-02-15T14:30:00Z"
} Viewing Digests →
To see what's queued up for your next email, list your upcoming digests. Each entry shows the scheduled date, current status, and how many send attempts have been made.
Scope: digest:read
GET /v1/admin/digests/upcoming
{
"items": [
{
"id": "dig_01abc...",
"digestDate": "2026-02-15",
"scheduledAt": "2026-02-15T14:00:00Z",
"status": "pending",
"attemptCount": 0
}
]
} Previewing a Digest →
Before a digest is sent, you can preview exactly what it will look like. The response includes both the raw markdown and the rendered HTML, so you can inspect the content or pipe it into your own tooling.
Scope: digest:read
GET /v1/admin/digests/:digestId/preview
{
"id": "dig_01abc...",
"digestDate": "2026-02-15",
"status": "pending",
"markdownBody": "## Weather\nSunny, 72°F...",
"htmlBody": "<h2>Weather</h2><p>Sunny, 72°F...</p>"
} Retrying a Failed Send →
If a digest fails to send (usually a transient email provider issue), you can retry it.
Dailybin will re-render the content and attempt delivery again. The digest must be in a
failed state —
you'll get a 409 if you try to retry a digest that's already sent or still pending.
Scope: digest:retry
POST /v1/admin/digests/:digestId/retry
{
"accepted": true,
"digestId": "dig_01abc..."
} Checking Send History →
To confirm your digests are being delivered, pull your recent send history. Each entry shows whether the attempt succeeded or failed, the provider message ID for tracking, and any error messages.
Scope: digest:read
GET /v1/admin/sends
{
"items": [
{
"digestId": "dig_01abc...",
"attemptNo": 1,
"provider": "ses",
"status": "success",
"providerMessageId": "01abc...",
"errorMessage": null,
"attemptedAt": "2026-02-15T14:00:05Z"
}
]
} Connecting via MCP →
If your AI agent supports the Model Context Protocol, you can skip the REST API entirely.
Dailybin exposes an MCP server at
/mcp
using Streamable HTTP transport. Point your MCP client at it and your agent can post
sections, send emails, and check digest status using native tool calls.
Setting up Claude Desktop
Add this block to your Claude Desktop config. Replace the token with your own:
{
"mcpServers": {
"dailybin": {
"url": "https://api.dailybin.dev/mcp",
"headers": {
"Authorization": "Bearer <your-token>"
}
}
}
} Setting up Claude Code
If you're using Claude Code, you can add Dailybin as an MCP server from the CLI. Run this once and it's available in every session:
$ claude mcp add dailybin https://api.dailybin.dev/mcp --transport http --scope user --header "Authorization: Bearer <your-token>"
Handling Errors →
When something goes wrong, Dailybin returns a consistent error envelope with a machine-readable code and a human-readable message. You can match on the code in your agent's error handling logic.
{
"error": {
"code": "INVALID_PAYLOAD",
"message": "section is required"
}
} Error codes
| Code | HTTP | When you'll see it |
|---|---|---|
| UNAUTHORIZED | 401 | Token is missing, expired, or malformed |
| FORBIDDEN | 403 | Token doesn't have the required scope, or was revoked |
| INVALID_PAYLOAD | 400 | Request body is missing fields or has invalid values |
| NOT_FOUND | 404 | The digest or token you referenced doesn't exist |
| CONFLICT | 409 | You tried to retry a digest that isn't in a failed state |
| RATE_LIMITED | 429 | You've exceeded 60 requests per minute for this token |
| INTERNAL_ERROR | 500 | Something broke on our end — safe to retry |