POST /v1/export
Export a scope's events as JSONL.
Authorization
bearer Authorization: Bearer . Four credentials authenticate: (1) the operator key, the static CORTEX_API_KEY: the trusted operator, acting as the principal X-Cortex-Actor names (default user:local), in billing tenant default; (2) a PASETO v4 public token this server's minter signed (POST /v1/auth/tokens, POST /v1/auth/signup, the mint_offline tool), of audience CORTEX_V1_AUDIENCE or, with CORTEX_V1_TENANT_AUDIENCES=1, a billing tenant's own cortexdb:tenant:; (3) since 0.14.0 (principal_chain_v1), a JWT (RS256 or ES256) or PASETO v4 public token of an issuers-file entry (CORTEX_AUTH_ISSUERS_FILE; [auth] issuers_file on a cluster data node), whose subject, audience and token id (:) the entry maps; (4) a commitment transport token, on /v1/memory/commitments/* only. HS256, none and every symmetric JWT algorithm are refused. With auth disabled (no operator key: CORTEX_API_KEY unset on a loopback bind, or CORTEX_INSECURE_NO_AUTH) no token is verified and every request is served as the trusted operator, as the X-Cortex-Actor it names or user:local. On the operator key, with auth disabled and in the unsigned development mode, an X-Cortex-Actor that is not a principal id is 400 INVALID_HEADER, details {part: header, field: X-Cortex-Actor, reason: the parse error} (since 0.14.0; before, 401 INVALID_BODY). X-Cortex-Actor must equal the token's sub; a token of an issuers-file entry in prefix, hash or directory mode and a call of /mcp may leave it out (when present it must match). Any other token sent without it is verified first: a bearer of no known token form (a mistyped operator key) is 401 MISSING_TOKEN, any other bad token gets its own refusal, as with the header, and only a token that verifies answers 401 ACTOR_MISMATCH, the header is required. Every authenticated response carries X-Cortex-Token-Expires-In and X-Cortex-Token-Expires-At (and a Warning header within 72 h of expiry); every error of the auth layer carries X-Cortex-Request-ID.
Authentication refusals, the same on every authenticated route, none retriable unless marked. A token that carries none of the principal-chain claims and was not signed by an issuers-file entry gets each code with no details, as in 0.13.x; an entry's token, and a token that carries a chain claim other than minter, gets details.issuer (the entry's id, minter, commitment or unknown) and the details named here. 401 MISSING_TOKEN: no Authorization: Bearer header, or a bearer that is neither the operator key nor a PASETO v4 public or JWT token (a wrong operator key), with or without X-Cortex-Actor. 401 INVALID_TOKEN: a PASETO token no registered key verifies, a token signed by a key other than the issuer its iss names included. 401 INVALID_TOKEN_SIGNATURE: a bad signature, an iss no issuer is registered under, a JWT algorithm other than RS256 or ES256, a malformed token, or, on a token without chain claims, an iat ahead of the clock; for an issuers-file entry's token details.reason issuer_disabled, token_type, critical_header, algorithm, kid_revoked or unknown_kid (none for a bad signature). 401 EXPIRED_TOKEN. 401 REVOKED_TOKEN: the token's id is revoked (an entry's token goes by :). 401 WRONG_TENANT: an aud the token's issuer may not name, or one that names no tenant. 401 INVALID_TOKEN_CLAIM (since 0.14.0), details.claim and details.reason: a principal-chain claim malformed (malformed, not_an_agent, same_as_sub, not_a_service, person_with_agent, run_start_without_run, after_iat, run_too_old, out_of_range; unknown_critical_claim, with details.critical); an issuers-file entry's token without exp, iat or jti (missing), with iat or nbf ahead of the clock (ahead), or living longer than the entry's max_lifetime_s (claim exp, lifetime_over_maximum, details.lifetime_s, details.ceiling_s); a token this server's minter signed whose exp - iat is above the ceiling for its subject as this server runs it (CORTEX_MINT_MAX_LIFETIME_PERSON, _AGENT and _SERVICE since 0.14.1; by default 7,776,000 s for a user:, 604,800 s for an agent:, 2,592,000 s for a service: or system:) (claim exp, lifetime_over_ceiling, details.issuer minter, details.lifetime_s, details.ceiling_s; checked before the revocation, and counted instead while CORTEX_MINT_LIFETIME_ENFORCE=off); and, whatever issuer signed it, a token whose act names an AI employee of its tenant, the self of an employee record that is not delegable (claim act, act_not_delegable). 401 CLAIM_NOT_PERMITTED (since 0.14.0), details.claim: a chain claim the token's issuer is not bound to assert (an issuers-file entry's [issuer.claims]; minter from any issuer but this server's minter; the minter's own chain claims without the chain assertion), or an issuers-file entry's scopes entry outside its token's tenant root org: (claim scopes, details.entry). 401 SUBJECT_UNMAPPED (since 0.14.0), details.reason: an issuers-file entry's subject mapping produced no principal (missing, not_a_principal, kind, service, foreign_namespace, personal_data_in_id, malformed, too_long, not_in_directory, directory_not_here); and, on every route whatever issuer signed the credential, a principal whose entry in its tenant's directory (org:) is deactivated (deactivated; never the trusted operator). 410 TENANT_DELETED (since 0.14.0): the credential's tenant root, org:, is being deleted or was (POST /v1/erasures/tenant); never the trusted operator. 503 REVOCATION_UNAVAILABLE (retriable): the revocation list cannot answer, so every token is refused as an outage, never 401 REVOKED_TOKEN, and the list still fails closed: on a cluster data node while the node's revocation lease has expired (the metadata group has not confirmed its revocation set for 10 s, or the node has just started), and on a single node once the list's journal failed (a refused write latches the list). 503 ISSUER_KEYS_UNAVAILABLE (retriable): an issuers-file entry's JWKS cannot be fetched and no pinned or cached key verifies the token; Retry-After is the whole seconds until the key cache's next fetch (its backoff, 3 s doubling to 60 s; 3 s before the first fetch), at least 1, also in details.retry_after_seconds. 503 WAL_UNAVAILABLE (retriable): the directory did not read, so a directory-mode subject's principal, or a principal's deactivation, is unknown.
Authorization refusals any route gives where it checks a capability or a roster, none retriable. 403 POLICY_DENIED, details.capability and details.denied_by_tier (deployment, tenant, actor or scope): the deployment's or the tenant's policy denies the capability, the token's caps leave it out, a scopes entry names it but not at this scope, or the scope's roster does not admit the caller. Since 0.14.0 a token that carries scopes holds only the capabilities an entry names (: its subtree, ! that one scope, :* every scope and the checks made without one); any other is refused with details.link confinement, and counted instead while CORTEX_TOKEN_CONFINEMENT_ENFORCE=off. An agent chain token (one that carries act or run, or app or client resolving to agent) is confined so whatever the switch, and without scopes holds no capability. A tenant-audience token's :* entry at a scope outside its own org: root is details.link tenant. A token another principal minted (minter a principal other than its sub) also needs that minter to hold the capability under the deployment's and the tenant's policy and the role on the scope's roster: details.link minter, denied_by_tier scope, counted instead while CORTEX_MINTER_LINK_ENFORCE=off. A read whose credential stops being current while it runs (expired or revoked, or its issuers-file entry disabled or reloaded without its audience) is refused 403 POLICY_DENIED before it delivers. 403 APP_NOT_ADMITTED (since 0.14.0): in a tenant whose root sets apps_enforced, a chain whose application (app, or unlisted) is not admitted at the scope. 403 RUN_REQUIRED (since 0.14.0), details.scope: on the write routes (POST /v1/experience, each item of /v1/experience/bulk and /v1/experiences, each JSONL import line, POST /v1/beliefs), a token with act and no run.
The operator key under CORTEX_OPERATOR_DATA_READS (since 0.14.0, operator_break_glass_v1): allow, audit or deny; unset, deny on the cloud_shared_saas preset and allow on the other presets and with auth disabled; a tenant's own mode (PUT /v1/admin/access/tenant-settings) overrides it in that tenant. 403 BREAK_GLASS_REQUIRED, details {mode, class, route}: under deny, without a well-formed X-Cortex-Break-Glass, an operator-key action of class stored_rows (a route that returns stored rows, or a forget or erasure preview whose selector names about_subject, about_entity or predicate), admin_content (a content-returning admin route), private_region (a read whose scope or extra_scopes lie in a private region) or in_band (a mint of a token that carries a data capability, can mint, carries a registry-write capability or is for an Owner of a private region; a registration, an auto-provisioning first write included; a roster or tree edit that widens a private region or adds a service: principal; a write into a private region or a shared org-tree scope; a migration out of a private region; lowering a tenant's mode from deny; a deployment minter's mint of a token that carries a data capability). 400 INVALID_HEADER, in every mode: that header malformed, sent twice or longer than 512 bytes, details {part: header, field: X-Cortex-Break-Glass, reason: what is wrong with it}. Both refusals write the request's audit row as decision deny, each with its denial (check operator_access, path request#x-cortex-break-glass, decided_by_tier request; reason the 403's class, or break_glass_invalid for the 400, which names no scope), within the operator key's bound (its billing tenant and X-Cortex-Actor) (AuditRow). Its form: reason=incident|legal|support|security|migration|restore|other; ticket=1 to 64 characters of [A-Za-z0-9._:-]; optionally note=percent-encoded UTF-8 of at most 256 bytes; no other key, separated by semicolons. It is read only on the operator key's requests and on a deployment minter's POST /v1/auth/tokens, and ignored on every other credential; a cross-origin browser page cannot send it. 503 NOTICE_UNAVAILABLE: under deny, a header-bearing read of a private region whose notice to the region's person did not commit, so nothing was delivered: details.reason moving, limit, members_behind (Retry-After: 30) or refused (details.refusal), each retriable; arming_disabled and reserved_path_in_use, not retriable. A by-id read's notice is its row's region's, committed once the row is read (since 0.14.0; scope_unnamed is gone).
Before authentication, a build that enforces licenses answers every data route 402 LICENSE_EXPIRED, or 402 LICENSE_REQUIRED when it never held a license, not retriable; health, version, ready, /metrics, the published contract (the OpenAPI document, GET /v1/schemas and /v1/schemas/{name}), the console, every GET /v1/admin/* route, GET /v1/auth/whoami, POST /v1/export and, since 0.14.0, deleting data (POST /v1/forget and its preview, POST /v1/erasures and its preview, /v1/erasures/person, /v1/erasures/tenant, /v1/erasures/{id}/cancel, and GET /v1/erasures/{id}, /verify and /receipt) pass. Not an authentication refusal, and answered only where a write needs a newer shard apply format than the node's shard is armed for (change sets, application records, directory and SCIM writes, a person's erasure and a tenant's deletion, writes that carry atoms or provenance, rehomes and watermarks, a declared belief with provenance): 503 FORMAT_UPGRADE_PENDING, details {reason, format, node, paths, count} (reason, format and node alone on the directory, SCIM, rehome and watermark writes; reason alone on an application record or a reference root), retriable with Retry-After: 30 for members_behind only (arming_disabled and reserved_path_in_use wait for the operator). A plain node arms for format 10 at its first boot of 0.14.0 under the default ceiling, one way, and then never answers it.
In: header
Header Parameters
operator_break_glass_v1 (API reference §8.15.3): read only on a request the operator key (CORTEX_API_KEY) authenticates, and on a deployment minter's POST /v1/auth/tokens; ignored on every other credential. reason=<incident|legal|support|security|migration|restore|other>; ticket=<1 to 64 of [A-Za-z0-9._:-]>[; note=<percent-encoded UTF-8, at most 256 bytes>], exactly one header of at most 512 bytes; any other form is 400 INVALID_HEADER in every mode (details {part: header, field: X-Cortex-Break-Glass, reason}), and the request's audit row a deny. Under CORTEX_OPERATOR_DATA_READS=deny (the deployment's, or the tenant's own) an operator-key request that reads stored rows, a content-returning admin route, a private region's read, or an in-band path needs it, else 403 BREAK_GLASS_REQUIRED (details {mode, class, route}). A well-formed header is written to the audit row as {reason, ticket}, never the note.
length <= 512subject_token_shadow_v1: on a request the operator key authenticates, the token of the principal the operator acts for, verified by the same verifier (without waiting on the network and without the X-Cortex-Actor step) and evaluated in shadow by the access report (GET /v1/admin/access/report). It never changes the response: a repeated, oversized (over 8,192 bytes) or invalid header, or the operator key itself, is recorded as the shadow's invalid_token and the request runs as sent.
length <= 8192Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Any other key is 422 INVALID_BODY, details.field naming it.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/v1/export" \ -H "Content-Type: application/json" \ -d '{ "scope": "string" }'{ "export_id": "string", "format": "jsonl", "data": "string", "bytes": 0}Export returns events only — not derived records
POST /v1/export { scope, format } exports events only. On a scope with facts / beliefs /
episodes / concepts, the JSONL data contains only the event records — no derived layers. This is
consistent with import re-deriving them; just don't expect a full-graph dump. The event export uses the
real { id, …, context: { … } } layout.
Response
POST /v1/export → 200 { export_id, format, bytes, data } with the payload inline (one JSON
record per line in data). format defaults to jsonl when omitted; any other value → 422
("supports format=jsonl only"). Stability: beta.
Read the export from the POST response
The export is returned inline in the POST response's data; the export_id is a reference, not a
download handle (GET /v1/export/{id} answers 404).