API keys
Mint, list and revoke the credentials every other call is made with.
Download the full API pública projection: openapi.json.
Issue and rotate your API keys explains this resource in prose, with runnable examples.
| Method | Path | What it does |
|---|---|---|
GET | /api-keys | List API keys |
POST | /api-keys | Create API key (returns plaintext secret once) |
DELETE | /api-keys/{id} | Revoke API key |
POST | /api-keys/{id}/rotate | Rotate API key secret (returns new plaintext secret once) |
GET /api-keys
List API keys
Every workspace credential minted through this family — an API key (workspace-wide) and a personal token (acts as one member) alike. The plaintext secret is never returned again after creation; every other field, including prefix (the first characters, for telling keys apart in a list), is safe to display. Also lists the test-mode (sk_test_) keys of this workspace's sandbox, marked livemode: false and carrying the sandbox's tenant_id.
curl https://api.vitrinadev.com/api/v1/api-keys \
-H "Authorization: Bearer $VITRINA_KEY"Example response (200)
{
"data": [
{
"id": "c1c1c1c1-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"name": "Website integration",
"prefix": "sk_dCG1N",
"scopes": [
"contacts:read",
"conversations:write"
],
"created_at": "2026-09-15T20:11:31.575Z",
"last_used_at": "2026-09-20T14:02:47.916Z",
"expires_at": null,
"revoked_at": null,
"user_id": null,
"rotated_at": null,
"livemode": true
}
]
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /api-keys
Create API key (returns plaintext secret once)
scopes must come from this workspace’s permission catalogue (GET /me lists what the caller may grant) — an unknown or misspelled scope is refused with 400, never silently dropped. The response’s secret is the ONLY time the plaintext token is shown; store it now, the platform keeps only a hash from then on. livemode: false mints an sk_test_ key bound to this workspace's sandbox: it authenticates only there, so it can never read or write this workspace's real data (409 SANDBOX_NOT_PROVISIONED until a sandbox exists — POST /sandbox/automotive or POST /sandbox/clinic).
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
name | string | yes | mín. 1, máx. 120 |
tenant_id | uuid | — | |
scopes | ai_agents:read \ | ai_agents:write \ | ai_agents:simulate \ |
expires_at | string | — | date-time |
livemode | boolean | — |
curl -X POST https://api.vitrinadev.com/api/v1/api-keys \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Website integration",
"scopes": [
"contacts:read",
"conversations:write"
]
}'Example response (201)
{
"data": {
"id": "c1c1c1c1-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"name": "Website integration",
"prefix": "sk_dCG1N",
"scopes": [
"contacts:read",
"conversations:write"
],
"created_at": "2026-09-15T20:11:31.575Z",
"last_used_at": null,
"expires_at": null,
"revoked_at": null,
"user_id": null,
"rotated_at": null,
"livemode": true,
"secret": "sk_dCG1N…"
}
}Answers: 201 · 400 · 401 · 403 · 404 · 409 · 429
DELETE /api-keys/{id}
Revoke API key
Immediate and irreversible: every request bearing this key’s secret 401s from this point on. There is no un-revoke — mint a new key instead.
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
curl -X DELETE https://api.vitrinadev.com/api/v1/api-keys/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Answers: 204 · 400 · 401 · 403 · 404 · 409 · 429
POST /api-keys/{id}/rotate
Rotate API key secret (returns new plaintext secret once)
A NEW secret for the SAME key — same id, name and scopes, so an integration keyed on this id keeps its identity. The OLD secret keeps authenticating for grace_period_hours (0-72, default 24) so a redeploy has time to pick up the new one; after that it 401s like any revoked credential. prefix changes — it is derived from the new secret. Refuses (404) a revoked or foreign key: rotating cannot bring one back to life. Audited (api_keys.rotate).
| Parameter | In | Type | Required | Constraints |
|---|---|---|---|---|
id | path | uuid | yes |
Body
| Field | Type | Required | Constraints |
|---|---|---|---|
grace_period_hours | integer | — | ≥ 0, ≤ 72 |
curl -X POST https://api.vitrinadev.com/api/v1/api-keys/<id>/rotate \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"grace_period_hours": 24
}'Example response (200)
{
"data": {
"id": "c1c1c1c1-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"name": "Website integration",
"prefix": "sk_9pQ2R",
"scopes": [
"contacts:read",
"conversations:write"
],
"created_at": "2026-09-15T20:11:31.575Z",
"last_used_at": "2026-09-20T14:02:47.916Z",
"expires_at": null,
"revoked_at": null,
"user_id": null,
"rotated_at": "2026-09-22T20:11:31.575Z",
"livemode": true,
"secret": "sk_9pQ2R…",
"grace_period_ends_at": "2026-09-23T20:11:31.575Z"
}
}Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429
Analytics
The dashboard's reports: conversation volume and resolution, human and AI performance, tool and token usage, and latency.
Appointments
The hours that are booked and the catalogue of what gets booked: availability, book, move, close and cancel. A test drive and a consultation are the same resource.