Getting started
Authentication
One scheme: a bearer token in the Authorization header. There are no cookies, no sessions and no OAuth flow on this API.
The header
Send the key as a bearer token on every request. There is no unauthenticated endpoint, including the ones that return nothing sensitive: an API where some calls are open and some are not is an API where somebody eventually gets that wrong.
curl "https://api.cresva.ai/v1/brands" \ -H "Authorization: Bearer cresva_sk_live_XXXXXXXXXXXXXXXX"Key kinds
The prefix is the whole distinction and it is visible at a glance in a log, a config file or a screenshot, which is the point of putting it in the string.
cresva_sk_live_cresva_sk_test_meta.simulated to true.The MCP tools use the same keys, and the same rule
The tools in cresva-mcp-server read your account through the same keys as this API, and a test key means the same thing there as it does here: cresva_sk_test_ answers with deterministic simulated rows and reads no brand data, while cresva_sk_live_ reads your real ingested data.
Every MCP response says which it was in a top-level mode field, either "test" or "live", and repeats it in an X-Cresva-Mode header so it is readable without parsing the body. The field is absent on a refused request, because there is no key to report a mode for.
This was not always true
Point CI at a test key
Storage and rotation
A key is shown once, at creation, and stored hashed. Nothing in the product can retrieve the original string afterwards, including support. That is deliberate and it has a consequence worth planning for: losing a key means creating a new one, not looking the old one up.
- Keep the key in an environment variable or a secret manager, never in source control and never in client-side code.
- Create a separate key per integration. Revoking one then costs you one integration rather than all of them.
- Revoked and expired keys are refused at the first check, before any data is read.
# .env, never committedCRESVA_API_KEY=cresva_sk_live_XXXXXXXXXXXXXXXX # and in your shellexport CRESVA_API_KEY="$(op read op://vault/cresva/api-key)"Scoping a key to one brand
A key can be narrowed to a single brand. A narrowed key does not need the brand_id parameter, and asking it for a different brand is a 403 forbidden rather than an empty result. An agency running one integration per client is the case this exists for.
When authentication fails
Two different failures, deliberately distinguished, because they call for different fixes.
401 unauthorized403 forbidden{ "error": { "code": "unauthorized", "message": "Missing or invalid Authorization header" }}Rate-limit headers on a 401
X-RateLimit-* headers. It did once not, which meant a client written to read those headers crashed on the one response it was most likely to receive while being set up.