Keys and identity
Base URL: https://app.trymaglev.com/api/v1. All endpoint families.
GET /health
Section titled “GET /health”Service health, what an interview turn would actually do right now, and the endpoint families available.
Public health probe that reaches the key store. Returns supported endpoint families and interviewer configuration or recent degraded state; neither establishes a successful model completion or supplier execution.
Authentication: see endpoint notes; no bearer key required.
200 response schema
{ "type": "object", "properties": { "status": { "type": "string", "const": "ok" }, "version": { "type": "string" }, "interviewer": { "type": "string", "enum": [ "model", "guided", "degraded" ] }, "baseUrl": { "type": "string" }, "families": { "type": "array", "items": { "type": "string" } }, "requestId": { "type": "string" } }, "required": [ "status", "version", "interviewer", "baseUrl", "families", "requestId" ]}Errors: 403 Insufficient access; 500 Server failure. See recovery guidance.
GET /openapi.json
Section titled “GET /openapi.json”This document.
Authentication: see endpoint notes; no bearer key required.
200 response schema
{ "type": "object", "properties": { "requestId": { "type": "string" } }}Errors: 400 Validation error; 401 Missing or invalid key; 403 Scope or mode does not allow this; 404 Not found; 429 Rate limit reached. See recovery guidance.
GET /keys
Section titled “GET /keys”List the signed in organisation’s keys (browser session).
Uses the browser session. An unauthenticated call returns signedIn false and an empty key list. No secret is returned.
Authentication: see endpoint notes; no bearer key required.
200 response schema
{ "oneOf": [ { "type": "object", "properties": { "signedIn": { "const": false }, "keys": { "type": "array", "items": { "$ref": "#/components/schemas/KeyInfo" } }, "requestId": { "type": "string" } }, "required": [ "signedIn", "keys", "requestId" ] }, { "type": "object", "properties": { "signedIn": { "const": true }, "organisation": { "type": "string" }, "keys": { "type": "array", "items": { "$ref": "#/components/schemas/KeyInfo" } }, "requestId": { "type": "string" } }, "required": [ "signedIn", "organisation", "keys", "requestId" ] } ]}Errors: 403 Insufficient access; 500 Server failure. See recovery guidance.
POST /keys
Section titled “POST /keys”Issue a key. Sandbox keys are anonymous and live one day; live keys need a signed in organisation owner and live 90 days.
Sandbox issuance is unauthenticated and rate-limited. Live issuance requires a signed-in organization owner session in the console. Scopes are assigned from key mode and organization role and enforced per operation. Custom caller-selected scopes are not supported by this endpoint.
Authentication: see endpoint notes; no bearer key required.
| Field | Type | Required | Details |
|---|---|---|---|
mode |
string | Yes | One of: sandbox, live |
name |
string | No |
201 response schema
{ "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "key": { "type": "string" }, "mode": { "type": "string", "enum": [ "sandbox", "live" ] }, "expiresAt": { "type": "string", "format": "date-time" }, "scopes": { "type": "array", "items": { "type": "string" } }, "note": { "type": "string" }, "requestId": { "type": "string" } }, "required": [ "id", "key", "mode", "expiresAt", "scopes", "note", "requestId" ]}Errors: 400 Invalid input or missing idempotency header; 401 Invalid key or missing sign-in; 403 Insufficient access; 429 Rate limit reached; 500 Server failure. See recovery guidance.
DELETE /keys/{id}
Section titled “DELETE /keys/{id}”Revoke a key: the current key with a bearer token, or any of the organisation’s keys with an owner session.
A bearer key can revoke only itself. A signed-in organization owner can revoke a key belonging to the organization.
Authentication: bearer key or browser session.
| Parameter | Location | Required | Details |
|---|---|---|---|
id |
path | Yes |
200 response schema
{ "type": "object", "properties": { "revoked": { "type": "boolean", "const": true }, "requestId": { "type": "string" } }, "required": [ "revoked", "requestId" ]}Errors: 400 Invalid input or missing idempotency header; 401 Invalid key or missing sign-in; 403 Insufficient access; 404 Endpoint or record unavailable; 429 Rate limit reached; 500 Server failure. See recovery guidance.
GET /me
Section titled “GET /me”The calling key: mode, organisation kind and effective scopes.
Authentication: bearer key.
200 response schema
{ "type": "object", "properties": { "id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "mode": { "type": "string", "enum": [ "sandbox", "live" ] }, "expiresAt": { "type": "string", "format": "date-time" }, "organisationKind": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "scopes": { "type": "array", "items": { "type": "string" } }, "requestId": { "type": "string" } }, "required": [ "id", "name", "mode", "expiresAt", "organisationKind", "scopes", "requestId" ]}Errors: 401 Invalid key or missing sign-in; 403 Insufficient access; 429 Rate limit reached; 500 Server failure. See recovery guidance.
POST /auth/device
Section titled “POST /auth/device”Start a short-lived browser connection for Maglev CLI. No API key is created until an organization owner explicitly reviews and approves the matching terminal code.
JSON Maglev pairing protocol inspired by device authorization; not an OAuth/OIDC interoperability endpoint. Codes expire after ten minutes. Keep deviceCode private; only userCode enters the browser URL. Only the fixed Maglev application URL is returned in production. Ten starts per IP per hour.
Authentication: see endpoint notes; no bearer key required.
| Field | Type | Required | Details |
|---|---|---|---|
client |
“maglev-cli” | Yes | |
name |
string | Yes | Min length 1. Max length 80 |
mode |
string | Yes | One of: sandbox, live |
201 response schema
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "deviceCode": { "type": "string", "pattern": "^[A-Za-z0-9_-]{43}$" }, "userCode": { "type": "string", "pattern": "^[A-HJ-NP-Z2-9]{4}-[A-HJ-NP-Z2-9]{4}$" }, "verificationUri": { "type": "string", "format": "uri" }, "verificationUriComplete": { "type": "string", "format": "uri" }, "expiresAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$" }, "expiresIn": { "type": "number", "const": 600 }, "interval": { "type": "number", "const": 5 }, "note": { "type": "string" }, "requestId": { "type": "string" } }, "required": [ "deviceCode", "userCode", "verificationUri", "verificationUriComplete", "expiresAt", "expiresIn", "interval", "note", "requestId" ], "additionalProperties": false}Errors: 400 Validation error; 401 Missing or invalid key; 403 Scope or mode does not allow this; 404 Not found; 429 Rate limit reached. See recovery guidance.
POST /auth/device/poll
Section titled “POST /auth/device/poll”Check the same browser connection after its polling interval. Receives the same temporary API key after approval, including after a lost response.
Wait at least interval seconds. HTTP 400 authorization_pending means wait; slow_down returns an increased details.interval (up to thirty seconds), persisted by the server. access_denied, expired_token, issuance_limit and device_consumed are terminal outcomes. The temporary key expires with the ten-minute request. Save it privately and acknowledge using that key before expiry; acknowledgement extends its normal lifetime. Owner/session/organization/scope changes or revoked keys cannot silently mint a replacement.
Authentication: see endpoint notes; no bearer key required.
| Field | Type | Required | Details |
|---|---|---|---|
deviceCode |
string | Yes | Pattern: ^[A-Za-z0-9_-]{43}$ |
200 response schema
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "key": { "type": "string", "pattern": "^maglev_(test|live)_[A-Za-z0-9_-]{43}$" }, "mode": { "type": "string", "enum": [ "sandbox", "live" ] }, "expiresAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$" }, "scopes": { "type": "array", "items": { "type": "string" } }, "acknowledgementRequired": { "type": "boolean", "const": true }, "keyLifetimeDays": { "anyOf": [ { "type": "number", "const": 1 }, { "type": "number", "const": 90 } ] }, "requestId": { "type": "string" } }, "required": [ "id", "key", "mode", "expiresAt", "scopes", "acknowledgementRequired", "keyLifetimeDays", "requestId" ], "additionalProperties": false}Errors: 400 Validation error; 401 Missing or invalid key; 403 Scope or mode does not allow this; 404 Not found; 429 Rate limit reached. See recovery guidance.
POST /auth/device/acknowledge
Section titled “POST /auth/device/acknowledge”Confirm that this terminal privately saved its own new key; requires that exact key as Bearer authorization.
Call only after durable local credential persistence. Extends live keys to ninety days or sandbox keys to one day from key creation. Same-request acknowledgement is replayable; it never issues another key. After acknowledgement device polling cannot retrieve the token again. Revocation remains available through DELETE /keys/{id}.
Authentication: bearer key.
| Field | Type | Required | Details |
|---|---|---|---|
deviceCode |
string | Yes | Pattern: ^[A-Za-z0-9_-]{43}$ |
200 response schema
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "acknowledged": { "type": "boolean", "const": true }, "id": { "type": "string", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" }, "mode": { "type": "string", "enum": [ "sandbox", "live" ] }, "expiresAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$" }, "requestId": { "type": "string" } }, "required": [ "acknowledged", "id", "mode", "expiresAt", "requestId" ], "additionalProperties": false}Errors: 400 Validation error; 401 Missing or invalid key; 403 Scope or mode does not allow this; 404 Not found; 429 Rate limit reached. See recovery guidance.
POST /auth/device/cancel
Section titled “POST /auth/device/cancel”Cancel an unacknowledged terminal request using its private deviceCode. Any issued temporary key is revoked atomically.
Does not revoke an acknowledged installation; use the ordinary key revocation endpoint for that. Repeating cancellation is safe.
Authentication: see endpoint notes; no bearer key required.
| Field | Type | Required | Details |
|---|---|---|---|
deviceCode |
string | Yes | Pattern: ^[A-Za-z0-9_-]{43}$ |
200 response schema
{ "type": "object", "properties": { "cancelled": { "const": true }, "requestId": { "type": "string" } }, "required": [ "cancelled", "requestId" ]}Errors: 400 Validation error; 401 Missing or invalid key; 403 Scope or mode does not allow this; 404 Not found; 429 Rate limit reached. See recovery guidance.