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": "builder"
  }'

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"
}

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
Content productionYesYesYes

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 includes a Retry-After header:

// 429 Too Many Requests
{
  "error": "rate_limited",
  "message": "Too many requests. Retry after 60 seconds."
}

Error handling

StatusError CodeMeaningWhat to Do
401authentication_requiredMissing or invalid X-API-Key headerCheck your API key is set correctly
402tier_limit_exceededDaily call limit reached for your tierUpgrade via Stripe billing portal
404Resource not foundVerify the brand_id or resource ID
409Pipeline run already in progressPoll the existing run instead (check X-Running-Run-Id header)
422Validation errorCheck request body against the docs
429Rate limitedWait and retry after Retry-After seconds

All error responses follow this shape:

{
  "error": "error_code",
  "message": "Human-readable explanation",
  "actions": [
    {
      "rel": "checkout",
      "href": "/auth/checkout",
      "method": "POST",
      "description": "Start Stripe checkout to create an account"
    }
  ]
}

The actions array provides machine-readable next steps — useful for AI agents to self-recover from errors.

HATEOAS actions

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

{
  "error": "tier_limit_exceeded",
  "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 in the same transaction that creates the new one:

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.