Authentication

API key authentication, tier system, rate limits, key rotation, and creating an account via Stripe checkout.

The API uses a single API key for authentication. No sessions, no OAuth, no passwords. One key, one header.

API key format

All keys start with bm_ followed by a hex string:

bm_54249af28748e0e24b4d2782122e2de9335627ef0f800747

Pass it in the X-API-Key header on every request:

curl -H "X-API-Key: $BM_API_KEY" \
  https://api.boringmarketing.com/auth/me

Store your key in an environment variable. Never hardcode it in source files.

export BM_API_KEY="bm_your_key_here"

Getting an API key

Accounts are created via Stripe checkout. You choose a tier, pay, then retrieve your key from the dashboard. There is no free tier and no self-serve /auth/register endpoint.

curl -s -X POST \
  https://api.boringmarketing.com/auth/checkout \
  -H "Content-Type: application/json" \
  -d '{
    "email": "you@example.com",
    "name": "Your Name",
    "tier": "solo"
  }'

Response:

{
  "status": "email_sent",
  "session_id": "bmc_k7f3...",
  "poll_url": "https://api.boringmarketing.com/auth/checkout/bmc_k7f3.../status",
  "sent_at": "2026-04-08T18:42:11.123Z"
}

Path split. The next steps diverge based on who's reading.

If you're a human: check your email for the Stripe link, click it, complete payment. Then poll poll_url until it returns {status: "completed", dashboard_url}, open that URL, sign in with a magic-link, and copy your key from Settings → API Access.

If you're an agent running unattended: surface session_id and poll_url to your operator and stop. Polling will return completed after payment, but the response never contains an API key — the operator must sign into the dashboard and paste the key back before you can continue. To skip this handoff when you already hold a key (e.g. tier upgrade), use the authenticated fast path on POST /auth/checkout.

Check your inbox. The server emailed you a Stripe checkout link — click it, complete payment, then poll poll_url until status is completed:

{
  "status": "completed",
  "dashboard_url": "https://dashboard.boringmarketing.com"
}

The poll endpoint does not return your API key. Open dashboard_url in a browser, request a magic-link with your email, and copy your key from Settings → API Access. Then export it:

export BM_API_KEY="bm_your_key_here"

Already have a key but need to upgrade? The same POST /auth/checkout endpoint accepts an X-API-Key header for an authenticated fast path. In that mode, the response includes checkout_url inline (no email) and opens the Stripe billing portal for active users. See the checkout endpoint reference for details.

Tiers

BuilderProAgency
Price$49/mo$149/mo$399/mo
Calls/day5005,00050,000
Brands1310

All paid tiers include the full pipeline, brief generation, and content drafting — the only differences are daily call volume and concurrent brands. Start with Builder to run a full pipeline on one brand; upgrade to Pro or Agency when you need more brands or higher daily call volume.

Rate limits

Rate limits protect infrastructure. They are separate from tier call limits.

EndpointLimit
POST /auth/checkout (anonymous)3 per hour per destination email + 5 per hour per IP
POST /auth/checkout (authenticated)5 per hour per IP
POST /auth/recover3 per hour per IP
All authenticated endpointsTier-based daily call limit

When rate limited, the response body includes a retry_after field (seconds) and the response carries a Retry-After HTTP header with the same value:

HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json
{
  "error": "rate_limit_exceeded",
  "message": "Too many checkout requests. Try again later.",
  "retry_after": 60
}

Error handling

StatusError CodeMeaningWhat to Do
401authentication_requiredMissing X-API-Key headerSet the header on your request
401invalid_api_keyHeader present but key is not recognizedVerify the key or rotate via POST /auth/rotate-key
402credits_exhaustedDaily call limit reached for your tierUpgrade via POST /auth/billing-portal
404—Resource not foundVerify the brand_id or resource ID
409—Pipeline run already in progressPoll the existing run instead (check X-Running-Run-Id header)
422—Validation errorCheck request body against the docs
429—Rate limitedWait and retry after Retry-After seconds

Error responses come in two flat shapes — both at the top level of the JSON body, with no detail wrapper:

// Structured (most authenticated endpoints — auth gating, tier limits, rate limits)
{
  "error": "error_code",
  "message": "Human-readable explanation",
  "actions": [
    {
      "rel": "checkout",
      "href": "/auth/checkout",
      "method": "POST",
      "description": "Start Stripe checkout to create an account"
    }
  ]
}
// Plain (some endpoints raise HTTPException with a string detail — e.g. 404 brand not found, 403 brand-limit-exceeded)
{ "detail": "Brand not found." }

The actions array, when present, provides machine-readable next steps — useful for AI agents to self-recover. Not every error includes one; agents should treat its absence as "no automated recovery available, escalate to a human."

HATEOAS actions

Error responses include an actions array with machine-readable recovery steps. This is designed for AI agents:

{
  "error": "credits_exhausted",
  "message": "Daily limit reached for your tier.",
  "usage": { "limit": 500, "used": 500 },
  "actions": [
    {
      "rel": "upgrade",
      "href": "/auth/billing-portal",
      "method": "POST",
      "description": "Open Stripe billing portal to change plan"
    }
  ]
}

An agent can read the actions array and automatically offer the user an upgrade path.

Key rotation

Rotate your key without downtime. The old key is invalidated immediately when the new one is issued:

curl -s -X POST \
  -H "X-API-Key: $BM_API_KEY" \
  https://api.boringmarketing.com/auth/rotate-key
{
  "api_key": "bm_new_key_here",
  "message": "API key rotated. Old key is now invalid."
}

Update your environment variable immediately after rotation.

Upgrading your plan

Change plans via the Stripe billing portal. Open the portal URL in a browser to manage subscription, payment method, or invoices:

curl -s -X POST \
  -H "X-API-Key: $BM_API_KEY" \
  https://api.boringmarketing.com/auth/billing-portal

Lost your API key?

If you lose your key, sign into the dashboard with your email to view or rotate it from the Settings page. The dashboard uses a magic-link login — no password required.

Path split. Recovery always terminates at a browser sign-in. Humans: follow the link above. Agents running unattended: you cannot self-recover — surface https://dashboard.boringmarketing.com to your operator. Prefer storing the key durably at issue time rather than recovering it later.