Skip to content

Built for the agents that build websites.

Everything a human can do in the dashboard, an agent can do with an API key. The exit test for this property: a coding agent in a fresh site repo, given only a key, ships a working contact form with email and Telegram in one conversation.

Four calls from nothing to verified.

  1. 1 Discover

    Read GET /llms.txt (Markdown, written for agents) or GET /openapi.json (the full contract, generated from the live routes). Then GET /v1/me: organization, scopes, plan limits and counts of what already exists.

    curl https://postbag.dev/llms.txt
    curl https://postbag.dev/v1/me -H "Authorization: Bearer pb_live_…"
  2. 2 Create

    POST /v1/quickstart creates the project if missing, the form, an email destination and a direct route, idempotently by (project, name). It returns the submit URL, embed snippets for HTML, fetch, React, Astro and Next.js, a verification recipe, and next[] calls.

    curl -X POST https://postbag.dev/v1/quickstart -H "Authorization: Bearer pb_live_…" -d '{
      "name": "Portfolio contact",
      "notify_email": "[email protected]",
      "origin": "https://example.com",
      "project": "portfolio"
    }'
  3. 3 Verify

    Post a test submission. The response carries the submission id and delivery ids; poll a delivery until it is sent. No inbox to check, no human in the loop.

    curl -X POST https://postbag.dev/s/fm_8f3kq2 -H "content-type: application/json" \
      -d '{"email":"[email protected]","message":"test","_test":true}'
    # → { "ok": true, "submission_id": "sb_4d2k91", "status": "received", "deliveries": ["dl_a91x02"] }
    
    curl https://postbag.dev/v1/deliveries/dl_a91x02 -H "Authorization: Bearer pb_live_…"
    # → { "status": "sent", "attempts": 1, "response": { "status": 200, "latency_ms": 412 } }
  4. 4 Wire

    Add destinations and routes with individual calls, test any destination with POST /v1/destinations/{id}/test, and write postbag.json into the repo so the next session finds it.

    curl -X POST https://postbag.dev/v1/destinations -H "Authorization: Bearer pb_live_…" -d '{
      "type": "webhook", "name": "CRM", "config": { "url": "https://crm.example.com/hooks/postbag", "secret": "…" }
    }'
    curl -X POST https://postbag.dev/v1/destinations/ds_7hm2q0/test -H "Authorization: Bearer pb_live_…"

What agent-native means here

It is a property the whole surface has, checked on every change, not a page in the docs.

Discoverable
llms.txt, openapi.json, and /v1/me. Accept: text/markdown on the site root returns the agent onboarding page.
One call to a working form
POST /v1/quickstart. Everything it does is also available as individual calls; it is a convenience, not a special path.
Verifiable
_test submissions return delivery ids to poll. Destinations have a /test endpoint that returns the provider's response.
Schema-aware, fleet-aware
GET /v1/streams/{id} returns the stream schema and a form template; POST /v1/forms with from_template creates a form that is pre-attached with a valid mapping. An agent cannot produce a form the stream does not understand.
Idempotent and safe to retry
Idempotency-Key on every POST. if_exists: "return" on creates. Re-running setup is a no-op, not a mess.
Errors that teach
{ code, message, hint, docs } on every error; next[] on every create. Ids are prefixed so they are self-describing in logs and conversation.

Leave the wiring where the next agent will look.

Site factories and agent scaffolds should write postbag.json into the repo and a line into CLAUDE.md or AGENTS.md. The next session, human or agent, finds the form id without guessing. Never hand-write a submit URL.

Coming next as thin clients over the same API: npx postbag init (writes postbag.json) and an MCP server whose tools mirror the API one to one plus postbag_quickstart and postbag_explain.

CLAUDE.md
# CLAUDE.md / AGENTS.md (site repo)
Forms on this site post to Postbag. Config lives in postbag.json (form_id, submit_url, project).
To add a form: POST https://postbag.dev/v1/forms with the project slug, then use the embed from the response.
Never hand-write a submit URL. Verify with a _test submission and poll the delivery.

Agent questions

What does an AI agent need to use Postbag?
An API key (pb_live_…) with the manage scope. From there it can read /llms.txt and /openapi.json, call GET /v1/me, create a routed form with POST /v1/quickstart, verify with a _test submission, and add destinations or streams, all without a browser.
How does an agent verify that a form actually works?
It posts to the submit URL with _test: true. The response includes the submission id and delivery ids; polling GET /v1/deliveries/{id} shows sent along with the provider's response. Test submissions are excluded from quotas.
Is there an MCP server or CLI?
Both are in progress and will be thin clients over the same /v1 API and the generated SDK. Today the recommended path is the HTTP API directly; it is small and fully described by /openapi.json. Nothing the CLI or MCP server will do is impossible over HTTP.
What happens when an agent makes a mistake?
Every error is { code, message, hint, docs }. The hint says what to do next; docs is a deep link into the error reference. Validation errors list the offending fields. Incomplete stream mappings fail at attach time with the missing fields listed.
Is it safe for an agent to re-run a setup script?
Yes. Idempotency-Key is honoured on every POST under /v1, and creates accept if_exists: "return" so re-running returns the same objects instead of duplicates or errors.
How should a site repo remember its Postbag wiring?
The convention is a postbag.json at the repo root with form_id, submit_url and project, and a line in CLAUDE.md or AGENTS.md saying forms on this site post to Postbag. Later sessions, human or agent, find the wiring there.

Give your agent a key.

Create an account, mint an API key with the manage scope, and hand it to Claude Code, Cursor or Codex with one instruction: read /llms.txt and set up a contact form.