models
GET /models
List model versions
Model versions of the resolved project + environment, newest first (cursor pagination, 003 §1). ?status= filters by the closed 002 §9 lifecycle set; ?gates=passed|failed filters by the latest eval report's gates_passed (models with no report are excluded by any gates filter). gates_passed remains a report fact; the additive promotion field is the server-resolved policy verdict used to decide whether a model is promotable (WP-M14).
Operation ID: listModels · Service: gateway-api
Authentication
apiKey(http (bearer)) — Project API key (003 §2):tsk_live_…ortsk_test_…inAuthorization: Bearer. Scoped to exactly one project + environment; scopes are a subset ofingest,read,admin. Keys are stored hashed and shown once at creation.consoleSession(http (bearer)) — Console session (003 §2): Identity Platform JWT. Data-plane calls addX-Tessera-Project+X-Tessera-Environmentheaders, validated against org membership.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
cursor | query | no | string | Opaque pagination cursor from a previous response's next_cursor. |
gates | query | no | string | Filter by the latest eval report's gates_passed; models with no eval report are excluded by either value. |
limit | query | no | integer | Page size (cursor pagination, 003 §1). Maximum 200. |
status | query | no | string | Filter by 002 §9 lifecycle status (closed set). |
Pagination
Cursor pagination (003 §1): pass limit (max 200) and cursor; the
response is the pagination envelope — data plus next_cursor
(null on the last page).
Responses
| Status | Schema | Description |
|---|---|---|
200 | ModelVersionPage | Page of model versions, newest first. |
default | ErrorEnvelope | Error response (003 §1). Every non-2xx response uses this envelope; the HTTP status matches error.code. Rate-limited requests get 429 resource_exhausted with retry_after_seconds and a Retry-After header; the X-RateLimit-* headers ride on EVERY response, success or error (003 §1; plan-driven per API key + endpoint class — WP-B2). A 503 unavailable carries retry_after_seconds too for a lock-wait timeout (SQLSTATE 55P03), a rule preview at capacity (preview_capacity_exhausted), or a label preview during which the erasure of every entity holding its censor clock began (label_preview_clock_entity_erased): how long to wait before retrying. |
GET /models/{model_id}
Get a model version
One 002 §9 ModelVersion: pinned dataset fingerprint, 005 §4 config, checkpoint URI + fingerprint, latest eval_report_id with its derived gates_passed, server-resolved promotion verdict, and lifecycle status. Another tenant's model id is indistinguishable from a missing one (404 not_found, 003 §3). Since WP-M26 a sequence model whose evaluation report predates the competitiveness check (it was never compared with the safeguard arms) lists G1 in promotion's advisory under the default policy, so it reads needs_acknowledgement; a report that cannot be read or verified resolves blocked with every mandatory gate in blocking, which is not a quality verdict (retry; when it persists, the bundle refusal names the cause, and a re-evaluation records a new report).
Operation ID: getModel · Service: gateway-api
Authentication
apiKey(http (bearer)) — Project API key (003 §2):tsk_live_…ortsk_test_…inAuthorization: Bearer. Scoped to exactly one project + environment; scopes are a subset ofingest,read,admin. Keys are stored hashed and shown once at creation.consoleSession(http (bearer)) — Console session (003 §2): Identity Platform JWT. Data-plane calls addX-Tessera-Project+X-Tessera-Environmentheaders, validated against org membership.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
model_id | path | yes | string | Model version id (mdl_ ULID, 002 §1). |
Responses
| Status | Schema | Description |
|---|---|---|
200 | ModelVersion | The model version. |
default | ErrorEnvelope | Error response (003 §1). Every non-2xx response uses this envelope; the HTTP status matches error.code. Rate-limited requests get 429 resource_exhausted with retry_after_seconds and a Retry-After header; the X-RateLimit-* headers ride on EVERY response, success or error (003 §1; plan-driven per API key + endpoint class — WP-B2). A 503 unavailable carries retry_after_seconds too for a lock-wait timeout (SQLSTATE 55P03), a rule preview at capacity (preview_capacity_exhausted), or a label preview during which the erasure of every entity holding its censor clock began (label_preview_clock_entity_erased): how long to wait before retrying. |
GET /models/{model_id}/bundle-readiness
Preview what creating a bundle of the model needs
A read-only dry run of createModelBundle (ERRATA 2026-10-15, inform, don't block) on the model's latest verified report, under the project's current promotion policy, so a client can show every fact before the user chooses. It runs the same preconditions, gate classification and evidence resolution as creation and, when no gate blocks, its read-only manifest-assembly checks in creation's order (a first diagnostic for each advisory gate, the model card's sources, a shared import's pins, the project semantic representation); only the shared manifest's content validation and the manifest's own invariants are checked by creation alone. A native model card's sources verify through the bounded process cache; a shared import's card verifies the export's checkpoint manifest and weights from storage, once (the dry run does not stream them a second time). It writes nothing, takes no lock and records no audit event. It returns the verdict (ready, needs_acknowledgement or blocked), every failing gate with its severity, reason, metrics and diagnostics, every bundle finding with its facts and facts fingerprint (the same a refusal of createModelBundle carries), a hard refusal outside the gates (refusal: its error code, message, typed details and whether it is retryable, such as ablation_runs_pending; never when a gate blocks), and the exact acknowledge_gates and acknowledge_findings (codes with their fingerprints) a request needs. observation_time and signals evaluate the request as if those members were enabled; observation_time defaults to true for a model that consumes the elapsed-silence token (it can only be bundled enabled) and to false otherwise. hazard=true makes the read a dry run of createModelHazardBundle with hazard.enabled: true (WP-M17 req 7c, ERRATA 2026-10-21): the hazard-ablation findings (hazard_ablation_missing, hazard_ablation_failed, hazard_ablation_incomplete) join findings and acknowledge_findings with the facts and fingerprint that request acknowledges, and a hazard precondition, hazard_ablation_unverifiable or a hazard calibration that fails re-verification (hazard_not_evaluated, checked on a throwaway sidecar that is never stored) is the refusal, as that request returns them. Beside an evidence refusal (which that request returns before it reaches the hazard gate) the hazard findings are listed and the hazard hard reasons follow the evidence details, as information (with one, retryable is false). Under strict mode findings cannot be acknowledged, so a model with findings is blocked with the refusal creation returns (the findings' details and policy_strict). When the evidence refuses, every finding that still resolves is listed beside the refusal. The verdict is resolved now: the report, the policy and the studies can change before the bundle request, which validates everything again. Each read runs the bounded study resolution and, when no gate blocks, re-reads a native card's sources (through the process cache), a shared import's checkpoint weights (one stream) or a project semantic.v2 model's semantic inputs, as a bundle request does; poll it sparingly. A transient artifact-storage failure is 503 unavailable with retry_after_seconds, as for createModelBundle. Another tenant's model id, an event model id and an entity-baseline model id are 404 not_found.
Operation ID: getModelBundleReadiness · Service: gateway-api
Authentication
apiKey(http (bearer)) — Project API key (003 §2):tsk_live_…ortsk_test_…inAuthorization: Bearer. Scoped to exactly one project + environment; scopes are a subset ofingest,read,admin. Keys are stored hashed and shown once at creation.consoleSession(http (bearer)) — Console session (003 §2): Identity Platform JWT. Data-plane calls addX-Tessera-Project+X-Tessera-Environmentheaders, validated against org membership.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
model_id | path | yes | string | Model version id (mdl_ ULID, 002 §1). |
hazard | query | no | boolean | Evaluate createModelHazardBundle with hazard.enabled: true (default false: createModelBundle, which never serves hazard heads). Adds the hazard gate's findings and refusals (WP-M17 req 7c, ERRATA 2026-10-21); evaluated does not echo it. |
observation_time | query | no | boolean | Evaluate observation_time.enabled: true (default: whether the model consumes the elapsed-silence token). |
signals | query | no | boolean | Evaluate signals.enabled: true (default false). |
Responses
| Status | Schema | Description |
|---|---|---|
200 | BundleReadiness | The bundle readiness of the model, resolved now. |
default | ErrorEnvelope | Error response (003 §1). Every non-2xx response uses this envelope; the HTTP status matches error.code. Rate-limited requests get 429 resource_exhausted with retry_after_seconds and a Retry-After header; the X-RateLimit-* headers ride on EVERY response, success or error (003 §1; plan-driven per API key + endpoint class — WP-B2). A 503 unavailable carries retry_after_seconds too for a lock-wait timeout (SQLSTATE 55P03), a rule preview at capacity (preview_capacity_exhausted), or a label preview during which the erasure of every entity holding its censor clock began (label_preview_clock_entity_erased): how long to wait before retrying. |
GET /models/{model_id}/bundles
List a model's promotion bundles
The model's promotion bundle manifests, newest first (cursor pagination, 003 §1). Bundles are immutable; any change is a new bundle (002 §9).
Operation ID: listModelBundles · Service: gateway-api
Authentication
apiKey(http (bearer)) — Project API key (003 §2):tsk_live_…ortsk_test_…inAuthorization: Bearer. Scoped to exactly one project + environment; scopes are a subset ofingest,read,admin. Keys are stored hashed and shown once at creation.consoleSession(http (bearer)) — Console session (003 §2): Identity Platform JWT. Data-plane calls addX-Tessera-Project+X-Tessera-Environmentheaders, validated against org membership.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
model_id | path | yes | string | Model version id (mdl_ ULID, 002 §1). |
cursor | query | no | string | Opaque pagination cursor from a previous response's next_cursor. |
limit | query | no | integer | Page size (cursor pagination, 003 §1). Maximum 200. |
Pagination
Cursor pagination (003 §1): pass limit (max 200) and cursor; the
response is the pagination envelope — data plus next_cursor
(null on the last page).
Responses
| Status | Schema | Description |
|---|---|---|
200 | PromotionBundlePage | Page of promotion bundles, newest first. |
default | ErrorEnvelope | Error response (003 §1). Every non-2xx response uses this envelope; the HTTP status matches error.code. Rate-limited requests get 429 resource_exhausted with retry_after_seconds and a Retry-After header; the X-RateLimit-* headers ride on EVERY response, success or error (003 §1; plan-driven per API key + endpoint class — WP-B2). A 503 unavailable carries retry_after_seconds too for a lock-wait timeout (SQLSTATE 55P03), a rule preview at capacity (preview_capacity_exhausted), or a label preview during which the erasure of every entity holding its censor clock began (label_preview_clock_entity_erased): how long to wait before retrying. |
POST /models/{model_id}/bundles
Create a promotion bundle
Assembles an immutable, fingerprinted PromotionBundle from the model (WP-M5 req 1-2; WP-M14). When the project's dual-control policy enables promotion_acknowledge, a call with acknowledge_gates is held for a second person (400 failed_precondition, dual_control detail second_actor_required; see the dual-control tag). Failed integrity gates G4–G6 — and every failed gate under strict — refuse with 400 failed_precondition and typed [\{gate, status, severity: blocking\}] details. Under warn, failed advisory quality gates G1–G3 still refuse unless acknowledge_gates exactly covers the currently failing advisory set with a non-empty reason; a successful acknowledgement is recorded in the manifest and same-transaction audit log. A sequence model whose evaluation report predates the WP-M26 competitiveness check (its G1 records no competitiveness per label) resolves G1 as a failing advisory gate with the open-details reason evaluated_before_competitiveness_check: acknowledge it with a reason, or let the release's legacy re-evaluation (in staging; in production once the platform owner enables it) or a createModelSignalsEvaluation (which runs the safeguard arms) record the comparison. A report that cannot be read or verified refuses with its typed reason (evaluation_missing, evaluation_mismatch or malformed_gate_result). On success the bundle pins the six 002 §9 fingerprints, copies the gate results verbatim (including acknowledged advisory failures), embeds the runtime block scorers read (005 §4 model_config, dataset window, label heads), records the \{kind, uri, fingerprint\} artifact triples re-verified at every activation, sets the model's status to promoted and publishes a bundle.created webhook. The manifest is also stored as canonical JSON in the artifacts bucket — the fingerprinted source of truth. Re-POSTing the same model creates a new bundle; bundles are never mutated. The optional body enables behavioral signals (WP-M9): signals.enabled: true requires the pinned eval report to carry a signals block (else 400 failed_precondition with typed detail \{"reason": "signals_not_evaluated"\}), copies the report's signals.config verbatim into runtime.signals and appends the signals_calibration artifact triple; an absent body or field leaves signals disabled. observation_time.enabled: true (WP-M16) requires an observation-time dataset, a model that consumes the token and its training run in this project and environment (else 400 failed_precondition with a typed reason detail) and copies the dataset manifest's observation_time verbatim into runtime.observation_time; a model that consumes the token cannot be bundled without it. A project-trained model whose config consumes the description channel (tokenizer_format descriptions.v1 or semantic.v2; WP-M7 req 9) has its own G1–G6 classified and acknowledged like any other model's (ERRATA 2026-10-15). A model whose run trains no gating label (ERRATA 2026-10-14) needs nothing more. For a model with a gating label every study of its recipe is resolved at bundle time from runs, builds and verified reports (studies are recorded with POST /models/\{model_id\}/description-ablations; an unrecorded current document no longer refuses, and every study's fingerprint, builds and verdict are pinned in the bundle.create audit event). Bundle findings (ERRATA 2026-10-15): evidence the user decides on is reported, not enforced. The findings are observation_time_evidence_missing and observation_time_not_recommended (the evidence summary and its failing arms and labels), desc_channel_study_missing (the recipe has no study), desc_channel_study_failed (studies that do not pass req 9, with each row's status, reason and arms, per-label deltas against -0.002 and on-run gates) and desc_channel_study_unresolved (a study with several v1 twin builds or more than 16 seeds, or a per-build or per-study bound of the resolution, each naming its on build and, for one run group, its run_group digest, at most 16 shown with the total unresolved count and a resolution_fingerprint over all; every other build and study of the recipe is still resolved, and only a recipe-wide bound resolves none); the code vocabulary is open and grows without a schema change. A blocking gate refuses first, whatever else is found: its promotion_gate details, then every finding that resolves (best-effort, each step on its own) as bundle_finding details, then the typed details of each step that refused. An invalid acknowledge_gates is refused before the evidence is read. Otherwise every finding is resolved before the refusal, and without an acknowledgement the request is refused once with 400 failed_precondition naming everything still to acknowledge: the advisory promotion_gate details, each finding's previous typed reason (observation_time_ablation_missing or observation_time_ablation_not_recommended, desc_channel_ablation_missing, desc_channel_ablation_failed, the resolution reason with bundle_model_id), one \{"type": "bundle_finding", "finding", "fingerprint", "facts"\} detail per finding (a finding's previous reasons come before its bundle_finding detail, and only that detail's fingerprint is the facts digest to echo) and acknowledgement_required. Under mode strict (the admin's opt-in) findings cannot be acknowledged: the refusal names policy_strict instead of acknowledgement_required. With an acknowledge_findings whose entries echo each finding's code and facts fingerprint, the bundle is created and the same transaction records one bundle.finding_acknowledged audit event per finding (finding, fingerprint, facts, reason, actor, model, report, bundle) and the codes in the bundle.create event's acknowledged_findings (always present, empty when none); the manifest does not change. An acknowledgement that does not match the findings resolved now is 400 invalid_argument with a finding_acknowledgement detail (finding_exact_cover with expected and received codes, finding_facts_changed with the expected and received fingerprints and the current facts, duplicate_finding, acknowledgement_reason_required); under strict it is policy_strict. These stay refusals whatever is acknowledged: ablation_runs_pending (retryable; it names the pending run, seed and model), desc_channel_run_missing, desc_channel_ablation_invalid and the integrity reasons of a study resolution, passed through with their own keys plus bundle_model_id (ablation_pairs_invalid for malformed rows, ablation_run_mismatch, ablation_recipe_mismatch, ablation_dataset_mismatch, ablation_label_mismatch, ablation_format_mismatch, ablation_arm_not_ready, ablation_evidence_invalid, ablation_artifact_mismatch, ablation_artifact_missing, ablation_dataset_unverifiable, ablation_arm_inconsistent, desc_channel_recipe_invalid). Shared-checkpoint imports are validated by their platform release instead, and v1 models are unaffected. A model with no evaluation report, or whose report row is missing, is refused with 400 failed_precondition and a typed precondition_failure detail \{"reason": "evaluation_missing"\}; a report that no longer matches its recorded fingerprint is refused with \{"reason": "evaluation_mismatch"\}. A transient artifact-storage failure (a 500, 502, 503 or 504 from the storage service, throttling, a deadline or exhausted retry, a connection, timeout or broken-stream error, or a credential refresh that cannot reach its endpoint) is 503 unavailable with retry_after_seconds and Retry-After; the transaction is rolled back, so the request can be retried. Slot activation remains fingerprint-only and never re-evaluates gates or policy.
Operation ID: createModelBundle · Service: gateway-api
Authentication
apiKey(http (bearer)) — Project API key (003 §2):tsk_live_…ortsk_test_…inAuthorization: Bearer. Scoped to exactly one project + environment; scopes are a subset ofingest,read,admin. Keys are stored hashed and shown once at creation.consoleSession(http (bearer)) — Console session (003 §2): Identity Platform JWT. Data-plane calls addX-Tessera-Project+X-Tessera-Environmentheaders, validated against org membership.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
Idempotency-Key | header | no | string | Idempotency key accepted by all POSTs (003 §1): 24 h dedup window, keyed per project. POSTs that are reads (batchSlotScores, scoreSlotEvent, deriveSlotBands, validateSourceRawPreset, sampleSource, watchEventFeedHeads and the preview operations: previewSourceMapping, previewCatalogMapping, previewLabel, previewPatternLabel, previewMarker, previewDatasetEligibility, previewSlotAudience, previewSharedCheckpointImport, previewSlotAtlasInquiry) accept the key but never replay a stored response: a retry with the same key is executed again (and metered again) against current data. A stored response that names an entity is removed when that entity is erased. A key that already answered answers only a retry of the request that stored its response: the same caller, environment, operation, path, query, scoping headers and body (a JSON body by its content, a gzip body by its inflated content). The same key with any other request is 409 already_exists with details[0].reason idempotency_key_reused, and that request is not run. The window holds no in-flight marker: two first requests sent at once can both run. A response stored by a release before this rule cannot be matched to its request and is refused the same way until it expires; send the request again with a new key. A body over 8 MiB (ingest: over its 5 MiB request limit) is run and its response is not stored. The ingest batch (POST /events) is the exception, except for an application's credentials: under a key that answered another batch it runs again, uncached, and its events' ids deduplicate them; so it does when its stored response cannot be checked. The operations that return a one-time secret store their response sealed: the stored copy opens only for the same key and credential, and one that cannot be opened is 409 idempotency_key_reused. While the server cannot check a stored response (it cannot read the key that checks it, for one), the request is 503 unavailable with Retry-After: retry with the same key. An operation whose handler claims the key itself answers the same 503, before it runs, while the key-hash pepper cannot be read; any other request sent with a key runs meanwhile, and its response is not stored. |
model_id | path | yes | string | Model version id (mdl_ ULID, 002 §1). |
Idempotency
This operation accepts an Idempotency-Key header (003 §1): within
24 hours, keyed per project and caller, a retry of the same request
gets the first response, and the same key with another request is
409 already_exists (idempotency_key_reused). See the
Idempotency-Key parameter for the exceptions.
Request body
CreateBundleRequest (optional)
Responses
| Status | Schema | Description |
|---|---|---|
200 | PromotionBundle | The created promotion bundle manifest. |
default | ErrorEnvelope | Error response (003 §1). Every non-2xx response uses this envelope; the HTTP status matches error.code. Rate-limited requests get 429 resource_exhausted with retry_after_seconds and a Retry-After header; the X-RateLimit-* headers ride on EVERY response, success or error (003 §1; plan-driven per API key + endpoint class — WP-B2). A 503 unavailable carries retry_after_seconds too for a lock-wait timeout (SQLSTATE 55P03), a rule preview at capacity (preview_capacity_exhausted), or a label preview during which the erasure of every entity holding its censor clock began (label_preview_clock_entity_erased): how long to wait before retrying. |
POST /models/{model_id}/bundles:hazard
Create a promotion bundle that may serve hazard heads
WP-M17 req 7. Exactly POST /models/\{model_id\}/bundles (the same gates, acknowledgements, signals and observation-time handling, idempotency, audit event, bundle.created webhook and response) with the required hazard toggle of CreateHazardBundleRequest; the released operation keeps its body and never serves hazard heads. hazard.enabled: true enables serving the model's hazard heads once its preconditions hold (see HazardBundleToggle): the manifest is unchanged and the hazard runtime is recorded in a sidecar of the bundle. hazard.enabled: false creates exactly the bundle the released operation would. Slot activation remains fingerprint-only and never re-evaluates gates or policy.
Operation ID: createModelHazardBundle · Service: gateway-api
Authentication
apiKey(http (bearer)) — Project API key (003 §2):tsk_live_…ortsk_test_…inAuthorization: Bearer. Scoped to exactly one project + environment; scopes are a subset ofingest,read,admin. Keys are stored hashed and shown once at creation.consoleSession(http (bearer)) — Console session (003 §2): Identity Platform JWT. Data-plane calls addX-Tessera-Project+X-Tessera-Environmentheaders, validated against org membership.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
Idempotency-Key | header | no | string | Idempotency key accepted by all POSTs (003 §1): 24 h dedup window, keyed per project. POSTs that are reads (batchSlotScores, scoreSlotEvent, deriveSlotBands, validateSourceRawPreset, sampleSource, watchEventFeedHeads and the preview operations: previewSourceMapping, previewCatalogMapping, previewLabel, previewPatternLabel, previewMarker, previewDatasetEligibility, previewSlotAudience, previewSharedCheckpointImport, previewSlotAtlasInquiry) accept the key but never replay a stored response: a retry with the same key is executed again (and metered again) against current data. A stored response that names an entity is removed when that entity is erased. A key that already answered answers only a retry of the request that stored its response: the same caller, environment, operation, path, query, scoping headers and body (a JSON body by its content, a gzip body by its inflated content). The same key with any other request is 409 already_exists with details[0].reason idempotency_key_reused, and that request is not run. The window holds no in-flight marker: two first requests sent at once can both run. A response stored by a release before this rule cannot be matched to its request and is refused the same way until it expires; send the request again with a new key. A body over 8 MiB (ingest: over its 5 MiB request limit) is run and its response is not stored. The ingest batch (POST /events) is the exception, except for an application's credentials: under a key that answered another batch it runs again, uncached, and its events' ids deduplicate them; so it does when its stored response cannot be checked. The operations that return a one-time secret store their response sealed: the stored copy opens only for the same key and credential, and one that cannot be opened is 409 idempotency_key_reused. While the server cannot check a stored response (it cannot read the key that checks it, for one), the request is 503 unavailable with Retry-After: retry with the same key. An operation whose handler claims the key itself answers the same 503, before it runs, while the key-hash pepper cannot be read; any other request sent with a key runs meanwhile, and its response is not stored. |
model_id | path | yes | string | Model version id (mdl_ ULID, 002 §1). |
Idempotency
This operation accepts an Idempotency-Key header (003 §1): within
24 hours, keyed per project and caller, a retry of the same request
gets the first response, and the same key with another request is
409 already_exists (idempotency_key_reused). See the
Idempotency-Key parameter for the exceptions.
Request body
Responses
| Status | Schema | Description |
|---|---|---|
200 | PromotionBundle | The created promotion bundle manifest. |
default | ErrorEnvelope | Error response (003 §1). Every non-2xx response uses this envelope; the HTTP status matches error.code. Rate-limited requests get 429 resource_exhausted with retry_after_seconds and a Retry-After header; the X-RateLimit-* headers ride on EVERY response, success or error (003 §1; plan-driven per API key + endpoint class — WP-B2). A 503 unavailable carries retry_after_seconds too for a lock-wait timeout (SQLSTATE 55P03), a rule preview at capacity (preview_capacity_exhausted), or a label preview during which the erasure of every entity holding its censor clock began (label_preview_clock_entity_erased): how long to wait before retrying. |
GET /models/{model_id}/card
Get a model's verifiable model card
A schema-versioned current-state trust dossier assembled on read from fingerprint-verified dataset, run, evaluation and promotion facts (https://spec.tessera.dev/schemas/model_card.json, WP-M15). The card carries lineage pins, the resolved trainer and adaptive-stop summary, per-label evaluation and server-resolved gate severity, optional promotion acknowledgement provenance, downsampled loss curves and fact-derived limitations. No card artifact is stored and later promotion can change subsequent reads. A model that has not been evaluated yet — or another tenant's model id — returns 404 not_found (003 §3). Any source fingerprint mismatch fails the read closed with typed \{kind, uri, expected, actual\} detail.
Operation ID: getModelCard · Service: gateway-api
Authentication
apiKey(http (bearer)) — Project API key (003 §2):tsk_live_…ortsk_test_…inAuthorization: Bearer. Scoped to exactly one project + environment; scopes are a subset ofingest,read,admin. Keys are stored hashed and shown once at creation.consoleSession(http (bearer)) — Console session (003 §2): Identity Platform JWT. Data-plane calls addX-Tessera-Project+X-Tessera-Environmentheaders, validated against org membership.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
model_id | path | yes | string | Model version id (mdl_ ULID, 002 §1). |
Responses
| Status | Schema | Description |
|---|---|---|
200 | ModelCard | The assembled model card JSON. |
default | ErrorEnvelope | Error response (003 §1). Every non-2xx response uses this envelope; the HTTP status matches error.code. Rate-limited requests get 429 resource_exhausted with retry_after_seconds and a Retry-After header; the X-RateLimit-* headers ride on EVERY response, success or error (003 §1; plan-driven per API key + endpoint class — WP-B2). A 503 unavailable carries retry_after_seconds too for a lock-wait timeout (SQLSTATE 55P03), a rule preview at capacity (preview_capacity_exhausted), or a label preview during which the erasure of every entity holding its censor clock began (label_preview_clock_entity_erased): how long to wait before retrying. |
GET /models/{model_id}/description-ablations
List a model's description-channel ablation evidence
WP-M7 req 9 (ERRATA 2026-10-13). Every stored description-channel ablation of the model's recipe, newest first (cursor pagination, 003 §1): each is a snapshot of one study, recorded by createDescriptionChannelAblation. The recipe is the contract, leakage policy and description registry the model's dataset pins (plus the semantic tokenization for semantic.v2), the model config, the run's label definitions and its training settings. Bundle creation resolves the current document of every study of the recipe itself and reports a missing, failing or unresolved study as a bundle finding the user acknowledges (ERRATA 2026-10-15; mode strict, an admin's opt-in, refuses it); an unrecorded current document is pinned in the bundle.create audit event; older snapshots are history. match is direct when the model is an on arm of the document and carry_over otherwise, such as a retrain on a new dataset build (the dataset fingerprint is provenance only). A v1 model and a shared-checkpoint import have no recipe: 400 failed_precondition with detail reason: desc_channel_model_required. Another tenant's model id is 404 not_found.
Operation ID: listDescriptionChannelAblations · Service: gateway-api
Authentication
apiKey(http (bearer)) — Project API key (003 §2):tsk_live_…ortsk_test_…inAuthorization: Bearer. Scoped to exactly one project + environment; scopes are a subset ofingest,read,admin. Keys are stored hashed and shown once at creation.consoleSession(http (bearer)) — Console session (003 §2): Identity Platform JWT. Data-plane calls addX-Tessera-Project+X-Tessera-Environmentheaders, validated against org membership.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
model_id | path | yes | string | Model version id (mdl_ ULID, 002 §1). |
cursor | query | no | string | Opaque pagination cursor from a previous response's next_cursor. |
limit | query | no | integer | Page size (cursor pagination, 003 §1). Maximum 200. |
Pagination
Cursor pagination (003 §1): pass limit (max 200) and cursor; the
response is the pagination envelope — data plus next_cursor
(null on the last page).
Responses
| Status | Schema | Description |
|---|---|---|
200 | DescriptionChannelAblationPage | Page of the recipe's ablation evidence, newest first. |
default | ErrorEnvelope | Error response (003 §1). Every non-2xx response uses this envelope; the HTTP status matches error.code. Rate-limited requests get 429 resource_exhausted with retry_after_seconds and a Retry-After header; the X-RateLimit-* headers ride on EVERY response, success or error (003 §1; plan-driven per API key + endpoint class — WP-B2). A 503 unavailable carries retry_after_seconds too for a lock-wait timeout (SQLSTATE 55P03), a rule preview at capacity (preview_capacity_exhausted), or a label preview during which the erasure of every entity holding its censor clock began (label_preview_clock_entity_erased): how long to wait before retrying. |
POST /models/{model_id}/description-ablations
Record description-channel ablation evidence
WP-M7 req 9 (ERRATA 2026-10-13). Records the current document of the path model's STUDY. The path model is a project-trained descriptions.v1 or semantic.v2 model; the caller names nothing else. The server resolves the study itself: the path model's dataset build (the on build), its unique v1 twin build (stored request identical outside the format's dataset pin keys, every pinned input shared: snapshot, contract, leakage policy, label definitions, entity type, window, split, counts, row counts, label support, encoders), and EVERY run over the two builds whose resolved config is the path model's config (on) or its v1 twin (off), seeds ignored, failed and cancelled runs included. Per seed and arm the earliest succeeded and evaluated attempt counts, graded on the model's own verified evaluation report; a seed without one is recorded as a non-complete row (on_failed, on_unevaluable, off_failed, off_unevaluable) and fails the gate. While a run or evaluation that could still count is in flight the request is refused with 400 failed_precondition and the retryable detail reason: ablation_runs_pending. No twin build is ablation_off_build_missing, several are ablation_off_build_ambiguous (with the candidates), and a twin build without a twin run is ablation_off_runs_missing; any other violation is 400 failed_precondition with a typed reason (for example ablation_dataset_mismatch, ablation_run_mismatch, ablation_recipe_mismatch, ablation_label_mismatch, ablation_format_mismatch, ablation_arm_inconsistent, ablation_arm_not_trained) plus key-level details, and nothing is stored. A completed document is stored whatever it shows; it is a snapshot of the study, and bundle creation re-resolves the current document of every study of the recipe; a study that does not pass (summary.gate_pass: every row complete, every pair and gating label on \>= off - 0.002, G1 pass and G2–G6 pass or not_applicable) is the bundle finding desc_channel_study_failed, which the user acknowledges with its facts (ERRATA 2026-10-15; mode strict, an admin's opt-in, refuses it). summary.recommend_enable (also at least 3 pairs and a positive mean delta per label) is reported only. Returns 201 with the stored evidence, or 200 when the identical document was stored before. Another tenant's model id is 404 not_found.
Operation ID: createDescriptionChannelAblation · Service: gateway-api
Authentication
apiKey(http (bearer)) — Project API key (003 §2):tsk_live_…ortsk_test_…inAuthorization: Bearer. Scoped to exactly one project + environment; scopes are a subset ofingest,read,admin. Keys are stored hashed and shown once at creation.consoleSession(http (bearer)) — Console session (003 §2): Identity Platform JWT. Data-plane calls addX-Tessera-Project+X-Tessera-Environmentheaders, validated against org membership.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
Idempotency-Key | header | no | string | Idempotency key accepted by all POSTs (003 §1): 24 h dedup window, keyed per project. POSTs that are reads (batchSlotScores, scoreSlotEvent, deriveSlotBands, validateSourceRawPreset, sampleSource, watchEventFeedHeads and the preview operations: previewSourceMapping, previewCatalogMapping, previewLabel, previewPatternLabel, previewMarker, previewDatasetEligibility, previewSlotAudience, previewSharedCheckpointImport, previewSlotAtlasInquiry) accept the key but never replay a stored response: a retry with the same key is executed again (and metered again) against current data. A stored response that names an entity is removed when that entity is erased. A key that already answered answers only a retry of the request that stored its response: the same caller, environment, operation, path, query, scoping headers and body (a JSON body by its content, a gzip body by its inflated content). The same key with any other request is 409 already_exists with details[0].reason idempotency_key_reused, and that request is not run. The window holds no in-flight marker: two first requests sent at once can both run. A response stored by a release before this rule cannot be matched to its request and is refused the same way until it expires; send the request again with a new key. A body over 8 MiB (ingest: over its 5 MiB request limit) is run and its response is not stored. The ingest batch (POST /events) is the exception, except for an application's credentials: under a key that answered another batch it runs again, uncached, and its events' ids deduplicate them; so it does when its stored response cannot be checked. The operations that return a one-time secret store their response sealed: the stored copy opens only for the same key and credential, and one that cannot be opened is 409 idempotency_key_reused. While the server cannot check a stored response (it cannot read the key that checks it, for one), the request is 503 unavailable with Retry-After: retry with the same key. An operation whose handler claims the key itself answers the same 503, before it runs, while the key-hash pepper cannot be read; any other request sent with a key runs meanwhile, and its response is not stored. |
model_id | path | yes | string | Model version id (mdl_ ULID, 002 §1). |
Idempotency
This operation accepts an Idempotency-Key header (003 §1): within
24 hours, keyed per project and caller, a retry of the same request
gets the first response, and the same key with another request is
409 already_exists (idempotency_key_reused). See the
Idempotency-Key parameter for the exceptions.
Request body
DescriptionChannelAblationRequest (optional)
Responses
| Status | Schema | Description |
|---|---|---|
200 | DescriptionChannelAblation | The identical evidence, stored before. |
201 | DescriptionChannelAblation | The stored evidence. |
default | ErrorEnvelope | Error response (003 §1). Every non-2xx response uses this envelope; the HTTP status matches error.code. Rate-limited requests get 429 resource_exhausted with retry_after_seconds and a Retry-After header; the X-RateLimit-* headers ride on EVERY response, success or error (003 §1; plan-driven per API key + endpoint class — WP-B2). A 503 unavailable carries retry_after_seconds too for a lock-wait timeout (SQLSTATE 55P03), a rule preview at capacity (preview_capacity_exhausted), or a label preview during which the erasure of every entity holding its censor clock began (label_preview_clock_entity_erased): how long to wait before retrying. |
GET /models/{model_id}/eval-report
Get a model's evaluation report
The stored eval_report.json for the model's latest evaluation (https://spec.tessera.dev/schemas/eval_report.json, WP-M4): 005 §6 promotion gates G1–G6 with status + metrics + actionable diagnostics, per-label metric blocks with 95% bootstrap CIs, and the G6 drift baseline (presence rates, value distributions, per-label score histograms). Since WP-M26 a sequence model's G1 metrics record, per label, competitiveness (the strongest completed safeguard arm, its estimate, the paired delta and its 95% CI, both margins and the verdict, or \{passed: false, reason: baseline_unavailable\} when no arm completed) and the evaluated holdout kind; G2 records an informational log_loss_gain per classification label; a G3 result carries the advisory note. All of these live inside the open gate metrics payloads. The artifact is re-hashed against the recorded report fingerprint before it is served (002 §10 — fail closed). A model that has not been evaluated yet — or another tenant's model id — returns 404 not_found (003 §3). A sequence model still training whose automatic evaluation failed on every launch the controller allows (three) returns the same 404 with the typed detail details[0] = \{type: precondition_failure, reason: evaluation_failed, model_id, report_id, failures, last_failure\}; a successful POST /models/\{model_id\}/signals-evaluations makes the model evaluated, and the next release retries the automatic evaluation once.
Operation ID: getModelEvalReport · Service: gateway-api
Authentication
apiKey(http (bearer)) — Project API key (003 §2):tsk_live_…ortsk_test_…inAuthorization: Bearer. Scoped to exactly one project + environment; scopes are a subset ofingest,read,admin. Keys are stored hashed and shown once at creation.consoleSession(http (bearer)) — Console session (003 §2): Identity Platform JWT. Data-plane calls addX-Tessera-Project+X-Tessera-Environmentheaders, validated against org membership.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
model_id | path | yes | string | Model version id (mdl_ ULID, 002 §1). |
Responses
| Status | Schema | Description |
|---|---|---|
200 | EvalReport | The evaluation report JSON. |
default | ErrorEnvelope | Error response (003 §1). Every non-2xx response uses this envelope; the HTTP status matches error.code. Rate-limited requests get 429 resource_exhausted with retry_after_seconds and a Retry-After header; the X-RateLimit-* headers ride on EVERY response, success or error (003 §1; plan-driven per API key + endpoint class — WP-B2). A 503 unavailable carries retry_after_seconds too for a lock-wait timeout (SQLSTATE 55P03), a rule preview at capacity (preview_capacity_exhausted), or a label preview during which the erasure of every entity holding its censor clock began (label_preview_clock_entity_erased): how long to wait before retrying. |
POST /models/{model_id}:archive
Archive a model version
Sets the model's 002 §9 status to archived (003 §1 :action). Refused with 400 failed_precondition while any deployment slot's active bundle belongs to this model (WP-M5 req 9); archiving an already-archived model is a no-op returning the model unchanged.
Operation ID: archiveModel · Service: gateway-api
Authentication
apiKey(http (bearer)) — Project API key (003 §2):tsk_live_…ortsk_test_…inAuthorization: Bearer. Scoped to exactly one project + environment; scopes are a subset ofingest,read,admin. Keys are stored hashed and shown once at creation.consoleSession(http (bearer)) — Console session (003 §2): Identity Platform JWT. Data-plane calls addX-Tessera-Project+X-Tessera-Environmentheaders, validated against org membership.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
Idempotency-Key | header | no | string | Idempotency key accepted by all POSTs (003 §1): 24 h dedup window, keyed per project. POSTs that are reads (batchSlotScores, scoreSlotEvent, deriveSlotBands, validateSourceRawPreset, sampleSource, watchEventFeedHeads and the preview operations: previewSourceMapping, previewCatalogMapping, previewLabel, previewPatternLabel, previewMarker, previewDatasetEligibility, previewSlotAudience, previewSharedCheckpointImport, previewSlotAtlasInquiry) accept the key but never replay a stored response: a retry with the same key is executed again (and metered again) against current data. A stored response that names an entity is removed when that entity is erased. A key that already answered answers only a retry of the request that stored its response: the same caller, environment, operation, path, query, scoping headers and body (a JSON body by its content, a gzip body by its inflated content). The same key with any other request is 409 already_exists with details[0].reason idempotency_key_reused, and that request is not run. The window holds no in-flight marker: two first requests sent at once can both run. A response stored by a release before this rule cannot be matched to its request and is refused the same way until it expires; send the request again with a new key. A body over 8 MiB (ingest: over its 5 MiB request limit) is run and its response is not stored. The ingest batch (POST /events) is the exception, except for an application's credentials: under a key that answered another batch it runs again, uncached, and its events' ids deduplicate them; so it does when its stored response cannot be checked. The operations that return a one-time secret store their response sealed: the stored copy opens only for the same key and credential, and one that cannot be opened is 409 idempotency_key_reused. While the server cannot check a stored response (it cannot read the key that checks it, for one), the request is 503 unavailable with Retry-After: retry with the same key. An operation whose handler claims the key itself answers the same 503, before it runs, while the key-hash pepper cannot be read; any other request sent with a key runs meanwhile, and its response is not stored. |
model_id | path | yes | string | Model version id (mdl_ ULID, 002 §1). |
Idempotency
This operation accepts an Idempotency-Key header (003 §1): within
24 hours, keyed per project and caller, a retry of the same request
gets the first response, and the same key with another request is
409 already_exists (idempotency_key_reused). See the
Idempotency-Key parameter for the exceptions.
Responses
| Status | Schema | Description |
|---|---|---|
200 | ModelVersion | The model version after archival. |
default | ErrorEnvelope | Error response (003 §1). Every non-2xx response uses this envelope; the HTTP status matches error.code. Rate-limited requests get 429 resource_exhausted with retry_after_seconds and a Retry-After header; the X-RateLimit-* headers ride on EVERY response, success or error (003 §1; plan-driven per API key + endpoint class — WP-B2). A 503 unavailable carries retry_after_seconds too for a lock-wait timeout (SQLSTATE 55P03), a rule preview at capacity (preview_capacity_exhausted), or a label preview during which the erasure of every entity holding its censor clock began (label_preview_clock_entity_erased): how long to wait before retrying. |
Errors
Every non-2xx response uses the 003 §1 error envelope; error.request_id
matches the X-Request-Id response header, and the HTTP status always
matches error.code:
{
"error": {
"code": "invalid_argument",
"details": [
{
"type": "field_violation"
}
],
"message": "human-readable",
"request_id": "req_01hxample"
}
}
Closed error-code set (003 §1): invalid_argument, unauthenticated, permission_denied, not_found, already_exists, failed_precondition, resource_exhausted, internal, unavailable.
resource_exhausted responses (HTTP 429) additionally carry
retry_after_seconds. See ErrorEnvelope.