Sume Format API staging: api.dev.sume.com and the 401 on a wrong host
Sume serves the Format API on api.sume.com and api.dev.sume.com with the same routes. A key works only on its own host; the other answers 401 unauthorized.

Sume runs the Format API on two hosts with one contract: https://api.sume.com for production and https://api.dev.sume.com for integration and staging. The routes, receipts and webhook delivery are the same. A key works only on the host it was created for, and the other host answers 401 unauthorized.
That is the whole story behind most "my key is valid but I get 401 in staging" reports. The Format API overview documents it in a short table.
What differs between the two hosts?
Almost nothing in the contract. What differs is what the key spends and who issues it.
| Host | Use it for | Keys |
|---|---|---|
https://api.sume.com | Production. Runs spend real credits from your workspace | Created at the dashboard's API keys page |
https://api.dev.sume.com | Integration and staging. Same routes, same receipts, same webhook delivery | Issued for a development workspace; ask your Sume contact |
Why do both keys look the same?
Both look like sume_live_..., so a prefix check cannot tell them apart. The docs' advice is to name your environment variables by host rather than by prefix. Names such as SUME_PROD_API_KEY and SUME_DEV_API_KEY are our suggestion, and so is keeping the base URL beside the key so the pair cannot drift apart.
A 401 from a wrong-host key looks like any other bad credential: no key, a malformed or revoked key, a key for the other host, or two credentials at once all return unauthorized with next_action: authenticate. Send one of Authorization: Bearer or x-api-key, never both.
How do I switch a script between hosts?
Keep one variable for the base URL and one for the key, and change both together. Nothing else in the request changes:
export SUME_BASE="https://api.dev.sume.com"
export SUME_API_KEY="$SUME_DEV_API_KEY"
curl -sS "$SUME_BASE/v1/formats?limit=10" \
-H "Authorization: Bearer $SUME_API_KEY" \
| jq -r '.data[] | "\(.handle)/\(.slug) \(.status)"'What should I test on the dev host?
Everything that is expensive to find out in production: the shape of your input, your output_schema, the webhook receiver and its signature check, and your Idempotency-Key derivation. Webhooks are delivered on both hosts, so a receiver can be proven end to end before launch; see Format run lifecycle.
Remember that identifiers do not travel. A Format, a run id or a handle on one host is not on the other, and the dev host has its own development workspace and Formats. The Contents API examples in the docs use the dev host.
What does staging not give me?
The docs do not say the dev host is free or that it simulates generation, so assume runs there do real work against the workspace you were issued, and set a generation_spend_cap_usd on test runs. Sume documents no self-serve signup for a dev key; the docs say to ask your Sume contact.
What is a release checklist for moving from dev to production?
Staging only helps if the move to production changes as little as possible. Because the contract is shared, the list below is mostly about configuration, not code.
- Create a production key with
formats:readandformats:writeat the API keys page, since scopes cannot be added to an existing key. - If the Format is owned by a team workspace, create the key inside that workspace, or the call is
403 workspace_key_required. - Recreate or fork the Format in the production workspace and read its
vanity_invoke_urlfromGET /v1/formats, rather than copying a dev address. - Read the production webhook signing secret and compare the fingerprint header with the one shown next to it.
- Set
generation_spend_cap_usdexplicitly on the first production runs, because production spends real credits from your workspace.
Sources
Related posts
More in Formats
- Format bulk queue: no queue webhook, no cancel-queue endpoint
A Sume bulk queue has no webhook and no cancel call. Poll the queue, put webhooks on items, and cancel the child runs one by one with their run ids.
- Format Contents API: read the whole package, commit many files at once
Read a Sume Format package with ?recursive=1 and write several files as one commit and one version bump. A change set, not the package; deletes stay separate.
- Why your order_id comes back null in a Sume structured output
If a Sume run's filled_by is projection, the fallback never sees your input or instruction, so an order_id you sent comes back null. Keep ids on your side.
- Optional field in a Sume Format output_schema: use a null union
A Sume output_schema has no optional properties. List every key in required and give optional ones a type of ["string","null"], or the create fails with 400.
Written by Sume