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.

Set up your agent in seconds.

Three ways in, none of them a dashboard.

Paste to your agent

One line. The agent reads llms.txt, mints its own key by email code, and builds the form.

Set up a contact form on this site with Postbag. Read https://postbag.dev/llms.txt first.

Install the skill

An Agent Skill with the decision tree, the quickstart call and the embed snippet already in it.

npx skills add faahim/postbag --skill postbag
# works once the repo is public — or fetch the file directly:
curl https://postbag.dev/.well-known/skills/postbag/SKILL.md

MCP or CLI

Tools that mirror the API one to one, for agents and terminals that prefer not to shell out to curl.

npx -y @postbag/mcp   # env: POSTBAG_API_KEY=pb_live_…
npx postbag login     # in progress — CLI/MCP not yet published to npm

How does the agent get a key?

Two calls, one human step in between. A new email provisions an organization first; an existing email reuses its own. The key is shown once.

request-code → verify-code
$ curl -X POST https://postbag.dev/v1/auth/request-code -d '{"email":"[email protected]"}'
{ "ok": true, "expires_in": 600, "next": "POST /v1/auth/verify-code with { email, code, key_name }" }

# — a human reads the 6-digit code out of their inbox and gives it to the agent —

$ curl -X POST https://postbag.dev/v1/auth/verify-code -d '{"email":"[email protected]","code":"482913"}'
{ "api_key": "pb_live_…", "organization": { "id": "org_…", "slug": "…", "name": "…" },
  "user": { "email": "[email protected]", "created": true },
  "next": [ "POST /v1/quickstart to create a routed form in one call", "GET /v1/me to see limits and what exists" ] }

Four calls from key 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?
Nothing to start — not even an API key. It emails itself a 6-digit code (POST /v1/auth/request-code), a human reads the code out of their inbox, and POST /v1/auth/verify-code returns a manage-scoped key (and provisions an organization for a new email). 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 get an API key without a browser?
Two calls, one human step in between: POST /v1/auth/request-code {"email":"…"} emails a 6-digit code good for 10 minutes; the human reads it out and gives it to the agent; POST /v1/auth/verify-code {"email":"…","code":"…"} returns a pb_live_… key. postbag login (once published) or npx skills add faahim/postbag --skill postbag drives this same flow.
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 — not yet published to npm — but the commands already exist and work against a real Postbag instance today: npx postbag login, npx postbag init, and npx -y @postbag/mcp. Today the recommended path is the HTTP API directly; it is small and fully described by /openapi.json. Nothing the CLI or MCP server does 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.