AI Attestations
Signed records of what a model was asked, what it answered, and which model answered.
An attestation is a record of one model call: the input text, the output text, the model provider, name and version, and an optional subject describing who or what triggered it. You post the record and the backend hashes the input, the output and the whole request, stores the request bytes, and returns the hashes with an attestation id.
A worker then signs a compact payload made of the record's id, tenant, type, the three hashes, the model context and the creation time with the tenant's Ed25519 key, and writes the row to an append-only table. The signed payload, signature and public key are returned by the get endpoint and can be checked offline; every SDK has a signature verification helper for it.
To check that a piece of text is the one that was recorded, hash it and post the hash to the verify endpoint, or fetch the raw payload and compare its SHA-256 with attestation_hash. Records past their plan's retention window are sealed: they leave the list and return 410 from get and raw, while verify still answers.
| Request body as canonical JSON | 1 MB (1,048,576 bytes); larger returns 413 payload_too_large |
|---|---|
| subject block | 8 KB serialized |
| subject custom keys | 20, not counting user_id and session_id |
| type | output, decision or approval |
| payload.input, payload.output and the three context fields | must not be empty or whitespace only |
| List page size | 1 to 500, default 50 |
| List cache | 30 seconds per tenant and query |
| Raw payload cache | 24 hours per tenant and attestation |
| Verify hash cache | 60 seconds per attestation |
content_hash | exactly 64 hex characters |
| Idempotency-Key replay window | 24 hours after the first response; 120 seconds while the first request is in flight |
| Monthly ingest quota | the plan's ai_attestations_per_month plus applied top-ups, counted per billing period |
| Monthly verify quota | the plan's api_verifications_per_month plus applied top-ups, counted per billing period |
| Rate limit | the plan's rate_limit_per_sec per tenant (default 10) and 60 times that per minute; exceeding either returns 429 rate_limited with Retry-After |
/v1/ai/attestationsIngest an attestation
Hashes a model input and output with its model context, queues the record for signing and returns the attestation id and hashes.
Content-TypeMust be application/json; any other value is rejected by the framework with 415 before the handler runs.
Idempotency-KeyReplays the first response for 24 hours when the same key is sent again with the same body; the value is trimmed and an empty value is ignored.
typeWhat the record attests: a model output, a decision taken on it, or an approval of it. One of output, decision, approval.
payloadThe text that is hashed.
payload.inputThe prompt or input text; its SHA-256 becomes input_hash.
payload.outputThe model's output text; its SHA-256 becomes output_hash.
contextWhich model produced the output.
context.model_providerProvider name, for example openai or anthropic; also a list filter.
context.model_nameModel name as the provider names it.
context.model_versionModel version or snapshot date.
subjectWho or what triggered the call; every key becomes part of the hashed bytes.
subject.user_idYour identifier for the end user.
subject.session_idYour identifier for the session or conversation.
subject.*Any other keys with JSON values, kept as custom context and written in sorted key order.
trace_idAn open trace owned by the tenant; the attestation is attached to it and included when the trace is sealed.
- The canonical bytes that are hashed are the request re-serialized by the server: compact JSON with the fields in the order type, payload, context, subject, trace_id; a missing subject or trace_id is written as null, user_id and session_id are omitted when absent, and custom subject keys are written in sorted order.
- The response is sent once the record is on the write queue; a worker signs it and writes the row, so GET can return 404 for a short time after the 201.
- The signature is not in this response; read it with GET /v1/ai/attestations/{attestation_id} once the worker has written the row.
- When the tenant already has a record with the same payload_hash, the call returns 200 with status duplicate and the existing attestation_id and created_at, and nothing new is written.
- With an Idempotency-Key, the first response is replayed for 24 hours; a retry while the first request is still in flight gets 202 with a body of {"status":"processing"}, and the same key with a different body gets 409.
- Idempotency keys are scoped to the tenant, the API key and this path in Redis and Postgres; the in-process cache keys on the tenant, the API key and the value only, so a key reused on another endpoint with a different body can get 409 from the same server process while that cache entry lives.
- A 200 duplicate response is not stored under the Idempotency-Key, so a retry with the same key inside 120 seconds gets 202 processing until the reservation expires, after which it runs again and gets the same 200.
- A body that is not valid JSON returns 400 and a body missing a required field returns 422, both with a plain-text message from the framework rather than the JSON error shape; a body over 2 MB is stopped by the framework with a plain-text 413 before the 1 MB canonical check runs.
- The monthly quota counter is incremented before validation and before the idempotency check, so a rejected request or a replayed one still counts toward the period's limit.
- The Ruby and PHP SDKs make model_provider, model_name and model_version optional, and the Ruby README shows a type of chat.completion with object input and output; the backend requires all three context fields, string input and output, and a type of output, decision or approval.
- SDK method names: Node client.attestations.ingest, Python client.attestations.ingest (the type is passed as attestation_type), Go client.Attestations.Ingest, Java client.attestations().ingest, Ruby client.attestations.ingest, Rust client.attestations().ingest, .NET client.Attestations.IngestAsync, PHP $client->attestations->ingest.
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const result = await client.attestations.ingest({
type: "output",
input: "Summarize the termination clause in contract CT-8472.",
output: "Either party may terminate with 30 days written notice. Early termination fees do not apply after month 12.",
modelProvider: "openai",
modelName: "gpt-4.1",
modelVersion: "2026-04-14",
subject: { userId: "u_4821", sessionId: "sess_9f3a", department: "legal" },
idempotencyKey: "ct-8472-summary-1",
});
console.log(result.attestation_id, result.payload_hash);
{
"attestation_id": "7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4",
"created_at": "2026-09-22T08:14:07Z",
"input_hash": "a46f0ad5dfb45fcbe2c303dcb34292af2f63fd79c6ff45b52dc40703a1cc5a70",
"output_hash": "a11ffee6d6ba93b478f557318a2b1a823f6e2816e0bd5d8178cc889a30e40fe5",
"payload_hash": "c4efe15781214a84046ad7e0592977c634a5cf45f4c1e06160daf760a295a8df",
"status": "accepted"
}
attestation_idServer-generated id of the record.
created_atWhen the server accepted the request; this value is part of the signed payload.
input_hashSHA-256 of the UTF-8 bytes of payload.input.
output_hashSHA-256 of the UTF-8 bytes of payload.output.
payload_hashSHA-256 of the canonical request bytes; stored as attestation_hash and returned by the raw endpoint.
statusaccepted when a new record was queued, duplicate when the tenant already has a record with this payload_hash. One of accepted, duplicate.
invalid_attestation_typetype is not output, decision or approval.
empty_payloadpayload.input or payload.output is empty or whitespace only.
invalid_contextcontext.model_provider, context.model_name or context.model_version is empty or whitespace only.
subject_too_many_keyssubject has more than 20 keys besides user_id and session_id.
subject_too_largesubject serializes to more than 8 KB.
trace_invalid_statusThe trace exists but is in a status other than open, sealing or sealed.
missing_api_keyNeither an Authorization header nor an X-API-Key header was sent.
invalid_authorization_schemeThe Authorization header is present but does not use the Bearer scheme.
invalid_api_key_formatThe key does not start with invoance_live_.
invalid_api_keyThe key does not match any API key.
api_key_revokedThe key has been revoked.
ip_not_allowedThe key has an IP allowlist and the caller's address is not on it.
api_key_lookup_failedThe key could not be looked up in the database.
insufficient_scopeThe key does not have the write scope.
payment_requiredThe subscription is past due on a paid plan.
feature_not_availableThe plan's monthly AI attestations limit is 0.
quota_exceededThe plan's monthly AI attestations limit for the current billing period is used up.
quota_check_failedThe quota counter could not be read; the request is rejected rather than allowed through.
trace_not_foundtrace_id was sent and no trace with that id belongs to the tenant.
trace_not_opentrace_id points at a trace that is sealing or sealed.
idempotency_key_reuse_mismatchThe Idempotency-Key was already used with a different body within the last 24 hours.
payload_too_largeThe canonical request bytes exceed 1 MB.
rate_limitedThe tenant used up its per-second or per-minute request budget; the Retry-After header says when to retry.
ingest_failedThe write queue did not acknowledge the record; any idempotency reservation is released so the same key can be retried.
serialization_failedThe request, the queue envelope or the response could not be serialized; a reservation made for an Idempotency-Key is released when the envelope fails.
hash_errorThe computed payload_hash could not be decoded before the duplicate check.
db_errorThe database read failed.
/v1/ai/attestationsList attestations
Returns a page of the tenant's active attestations, newest first, with optional date, type and provider filters.
page1-based page number; values below 1 are treated as 1.
limitRows per page; values outside the range are clamped.
date_fromOnly records with created_at at or after this instant (inclusive).
date_toOnly records with created_at before this instant (exclusive).
attestation_typeOnly records of this type; compared as text, so an unknown value returns an empty page. One of output, decision, approval.
model_providerOnly records whose context.model_provider equals this value exactly.
- Only records with access_tier active are returned; sealed records are left out of both the rows and total.
- Responses are cached for 30 seconds per tenant and query, so a new record can take up to 30 seconds to appear.
- A query value that does not parse, for example a date_from that is not RFC 3339 or a negative limit, returns 400 with a plain-text body from the framework rather than the JSON error shape.
- The scope check accepts a key with either the read scope or the write scope.
- The Node and PHP SDK READMEs show date-only values such as 2026-07-01 for date_from and date_to; the backend parses both as RFC 3339 timestamps with an offset, so a date without a time returns 400.
- The list rows do not carry model_version, hashes of input and output, or the signature; read one record for those.
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const page = await client.attestations.list({
limit: 50,
attestationType: "output",
modelProvider: "openai",
});
console.log(page.total, page.has_more, page.attestations.length);
{
"attestations": [
{
"attestation_id": "7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4",
"attestation_type": "output",
"attestation_hash": "c4efe15781214a84046ad7e0592977c634a5cf45f4c1e06160daf760a295a8df",
"model_provider": "openai",
"model_name": "gpt-4.1",
"retention_policy": "standard",
"created_at": "2026-09-22T08:14:07Z"
}
],
"page": 1,
"limit": 50,
"total": 1,
"has_more": false
}
attestationsThe page of records, ordered by created_at descending.
attestations[].attestation_idId of the record.
attestations[].attestation_typeThe type sent at ingest. One of output, decision, approval.
attestations[].attestation_hashSHA-256 of the canonical request bytes; the same value as payload_hash.
attestations[].model_providercontext.model_provider from the request; null when not stored.
attestations[].model_namecontext.model_name from the request; null when not stored.
attestations[].retention_policyRetention label derived from the plan's retention days when the row was written. One of short, standard, extended, regulatory, indefinite.
attestations[].created_atWhen the request was accepted.
pageThe page that was returned.
limitThe page size that was applied after clamping.
totalNumber of active records that match the filters.
has_moreTrue when page * limit is below total.
missing_api_keyNeither an Authorization header nor an X-API-Key header was sent.
invalid_authorization_schemeThe Authorization header is present but does not use the Bearer scheme.
invalid_api_key_formatThe key does not start with invoance_live_.
invalid_api_keyThe key does not match any API key.
api_key_revokedThe key has been revoked.
ip_not_allowedThe key has an IP allowlist and the caller's address is not on it.
api_key_lookup_failedThe key could not be looked up in the database.
insufficient_scopeThe key has neither the read scope nor the write scope.
rate_limitedThe tenant used up its per-second or per-minute request budget; the Retry-After header says when to retry.
db_errorThe database read failed.
/v1/ai/attestations/{attestation_id}Get an attestation
Returns one attestation with its hashes, the signed payload, the Ed25519 signature and public key, and the issuing organization.
attestation_idId returned by ingest.
- Decode signed_payload from hex to get compact JSON with the fields v, attestation_id, tenant_id, attestation_type, input_hash, output_hash, payload_hash, model_provider, model_name, model_version and created_at; the signature covers those bytes exactly, so verify the hex-decoded bytes, not a re-serialized copy.
- public_key is the raw 32-byte key; libraries that want DER need the SPKI prefix 302a300506032b6570032100 in front of it, which is what the Node SDK does in verifySignature.
- Every SDK checks the signature offline from this response: verifySignature in Node, Java and PHP, VerifySignatureAsync in .NET, VerifySignature in Go, verify_signature in Python, Ruby and Rust.
- created_at inside signed_payload is the string the worker signed at ingest time, while the created_at field is read back from the database, which stores microseconds; real values carry fractional seconds, so check the hex-decoded bytes rather than a payload rebuilt from the other fields.
- The response does not include the input, the output or the subject; read them with the raw endpoint and compare the SHA-256 of the returned bytes with attestation_hash.
- A sealed record returns 410 retention_expired rather than 404 so the caller can tell the two apart.
- The scope check accepts a key with either the read scope or the write scope.
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const att = await client.attestations.get("7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4");
console.log(att.attestation_hash, att.signature_alg, att.public_key);
{
"attestation_id": "7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4",
"tenant_id": "3b9d6f10-52c4-4a7e-9e1b-8d0c2f4a6e71",
"attestation_type": "output",
"attestation_hash": "c4efe15781214a84046ad7e0592977c634a5cf45f4c1e06160daf760a295a8df",
"input_hash": "a46f0ad5dfb45fcbe2c303dcb34292af2f63fd79c6ff45b52dc40703a1cc5a70",
"output_hash": "a11ffee6d6ba93b478f557318a2b1a823f6e2816e0bd5d8178cc889a30e40fe5",
"signed_payload": "7b2276223a312c226174746573746174696f6e5f6964223a2237633165346235322d396130662d346432652d623666332d326638613631633064396534222c2274656e616e745f6964223a2233623964366631302d353263342d346137652d396531622d386430633266346136653731222c226174746573746174696f6e5f74797065223a226f7574707574222c22696e7075745f68617368223a2261343666306164356466623435666362653263333033646362333432393261663266363366643739633666663435623532646334303730336131636335613730222c226f75747075745f68617368223a2261313166666565366436626139336234373866353537333138613262316138323366366532383136653062643564383137386363383839613330653430666535222c227061796c6f61645f68617368223a2263346566653135373831323134613834303436616437653035393239373763363334613563663435663463316530363136306461663736306132393561386466222c226d6f64656c5f70726f7669646572223a226f70656e6169222c226d6f64656c5f6e616d65223a226770742d342e31222c226d6f64656c5f76657273696f6e223a22323032362d30342d3134222c22637265617465645f6174223a22323032362d30392d32325430383a31343a30375a227d",
"signature": "d98f9274a8c40c70b659e16de85937a4ea24b102aab66c143530a33564a0a75dc89d1d46c017d0ccf48b6227d5c7be54a1927bb26985219b93bc82da62ebf10a",
"public_key": "1a3045e8b491a44ddc6b9c8a8680104945c65daff51e77e97d19ad6e9cba891f",
"signature_alg": "ed25519",
"model_provider": "openai",
"model_name": "gpt-4.1",
"model_version": "2026-04-14",
"retention_policy": "standard",
"created_at": "2026-09-22T08:14:07Z",
"organization": {
"name": "Northwind Legal",
"issuer_name": "Northwind Legal Ltd",
"primary_domain": "northwindlegal.example",
"domain_verified": true,
"domain_verified_at": "2026-05-03T10:22:41+00:00"
}
}
attestation_idId of the record.
tenant_idThe tenant that owns the record; also inside the signed payload.
attestation_typeThe type sent at ingest. One of output, decision, approval.
attestation_hashSHA-256 of the canonical request bytes; the same value as payload_hash.
input_hashSHA-256 of payload.input; null when not stored.
output_hashSHA-256 of payload.output; null when not stored.
signed_payloadThe exact bytes the tenant key signed: compact JSON with v, attestation_id, tenant_id, attestation_type, input_hash, output_hash, payload_hash, model_provider, model_name, model_version and created_at.
signatureEd25519 signature over signed_payload, 64 bytes.
public_keyThe tenant's raw 32-byte Ed25519 public key.
signature_algAlways ed25519. One of ed25519.
model_providercontext.model_provider from the request.
model_namecontext.model_name from the request.
model_versioncontext.model_version from the request.
retention_policyRetention label derived from the plan's retention days when the row was written. One of short, standard, extended, regulatory, indefinite.
created_atWhen the request was accepted.
organizationThe issuing organization, read from the tenant's organization row when the call is made.
organization.nameOrganization display name.
organization.issuer_nameIssuer name shown on proof pages.
organization.primary_domainThe organization's primary domain.
organization.domain_verifiedTrue once the primary domain passed DNS verification.
organization.domain_verified_atWhen the domain was verified, written with a +00:00 offset; omitted when the domain is not verified.
organization.logo_urlLogo URL set in the dashboard; omitted when not set.
invalid_path_parameterattestation_id is not a UUID.
missing_api_keyNeither an Authorization header nor an X-API-Key header was sent.
invalid_authorization_schemeThe Authorization header is present but does not use the Bearer scheme.
invalid_api_key_formatThe key does not start with invoance_live_.
invalid_api_keyThe key does not match any API key.
api_key_revokedThe key has been revoked.
ip_not_allowedThe key has an IP allowlist and the caller's address is not on it.
api_key_lookup_failedThe key could not be looked up in the database.
insufficient_scopeThe key has neither the read scope nor the write scope.
attestation_not_foundNo attestation with this id belongs to the tenant, or the record has not been written by the worker yet.
retention_expiredThe record is past its retention window and has been sealed; a plan upgrade can unseal it.
rate_limitedThe tenant used up its per-second or per-minute request budget; the Retry-After header says when to retry.
db_errorThe database read failed.
/v1/ai/attestations/{attestation_id}/rawGet the raw payload
Returns the stored canonical request bytes for an attestation, whose SHA-256 is the attestation_hash.
attestation_idId returned by ingest.
- The body is the exact bytes hashed at ingest: compact JSON with no whitespace, fields in the order type, payload, context, subject, trace_id, a missing subject or trace_id written as null, and custom subject keys in sorted order.
- SHA-256 over the response body as returned equals attestation_hash and payload_hash; hashing a pretty-printed or re-ordered copy gives a different value.
- The response carries Content-Type application/json and Content-Disposition inline; filename="ai-attestation-raw-{attestation_id}.json".
- The bytes are cached for 24 hours per tenant and attestation; the Node, Python and Go SDK doc comments say 5 minutes, the backend value is 86,400 seconds.
- SDK getRaw helpers (get_raw in Python, Ruby and Rust, GetRaw in Go, GetRawAsync in .NET) parse the body into a native object; the SDK verifyPayload helpers re-serialize it compactly in the original key order before hashing, and the Rust SDK asks for the raw string because its parsed values sort keys.
- A sealed record returns 410 retention_expired.
- The scope check accepts a key with either the read scope or the write scope.
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const raw = await client.attestations.getRaw("7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4");
console.log(raw.type, raw.context);
{
"type": "output",
"payload": {
"input": "Summarize the termination clause in contract CT-8472.",
"output": "Either party may terminate with 30 days written notice. Early termination fees do not apply after month 12."
},
"context": {
"model_provider": "openai",
"model_name": "gpt-4.1",
"model_version": "2026-04-14"
},
"subject": {
"user_id": "u_4821",
"session_id": "sess_9f3a",
"department": "legal"
},
"trace_id": null
}
typeThe type sent at ingest. One of output, decision, approval.
payload.inputThe input text exactly as sent.
payload.outputThe output text exactly as sent.
context.model_providerAs sent.
context.model_nameAs sent.
context.model_versionAs sent.
subjectuser_id and session_id when they were sent, then custom keys in sorted order; null when no subject was sent.
trace_idThe trace the record was attached to; null when none was sent.
invalid_path_parameterattestation_id is not a UUID.
missing_api_keyNeither an Authorization header nor an X-API-Key header was sent.
invalid_authorization_schemeThe Authorization header is present but does not use the Bearer scheme.
invalid_api_key_formatThe key does not start with invoance_live_.
invalid_api_keyThe key does not match any API key.
api_key_revokedThe key has been revoked.
ip_not_allowedThe key has an IP allowlist and the caller's address is not on it.
api_key_lookup_failedThe key could not be looked up in the database.
insufficient_scopeThe key has neither the read scope nor the write scope.
attestation_not_foundNo attestation with this id belongs to the tenant, or the record has not been written by the worker yet.
retention_expiredThe record is past its retention window and has been sealed; a plan upgrade can unseal it.
rate_limitedThe tenant used up its per-second or per-minute request budget; the Retry-After header says when to retry.
db_query_failedThe database read failed.
db_decode_failedThe row is missing its storage location.
r2_fetch_failedThe stored object could not be fetched from object storage.
r2_stream_failedThe stored object could not be read to the end.
/v1/ai/attestations/{attestation_id}/verifyVerify a hash
Compares a SHA-256 you computed against the stored attestation, input and output hashes and reports which one it matched.
Content-TypeMust be application/json; any other value is rejected by the framework with 415 before the handler runs.
attestation_idId returned by ingest.
content_hashThe SHA-256 to check: of the raw payload bytes, of payload.input or of payload.output.
- The hash is compared against attestation_hash, then input_hash, then output_hash, then payload_hash; attestation_hash and payload_hash hold the same value, so a hash of the raw payload is reported as attestation_hash.
- Each call consumes one API verification from the monthly quota, counted before the record is looked up, so a call for an unknown id still counts.
- The stored hashes are cached for 60 seconds per attestation.
- Verification does not check access_tier, so it works on sealed records that GET and the raw endpoint refuse with 410.
- Each call is recorded as a verification event with source api; it is not shown on public proof pages.
- There is no Idempotency-Key on this endpoint; repeated calls are separate verifications.
- A body that is not valid JSON returns 400 and a body without content_hash returns 422, both with a plain-text message from the framework rather than the JSON error shape.
- The SDKs compute the hash for you: verifyPayload (verify_payload in Python, Ruby and Rust, VerifyPayload in Go, VerifyPayloadAsync in .NET) takes the raw JSON and calls this endpoint, and every SDK checks that content_hash is 64 hex characters before sending.
- Checking the Ed25519 signature does not use this endpoint; the SDK verifySignature helpers do it offline from the GET response.
import { InvoanceClient } from "invoance";
// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();
const result = await client.attestations.verify("7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4", {
contentHash: "c4efe15781214a84046ad7e0592977c634a5cf45f4c1e06160daf760a295a8df",
});
console.log(result.match_result, result.matched_field);
{
"attestation_id": "7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4",
"match_result": true,
"matched_field": "attestation_hash",
"anchored_hash": "c4efe15781214a84046ad7e0592977c634a5cf45f4c1e06160daf760a295a8df",
"submitted_hash": "c4efe15781214a84046ad7e0592977c634a5cf45f4c1e06160daf760a295a8df",
"anchored_at": "2026-09-22T08:14:07Z",
"organization": {
"name": "Northwind Legal",
"issuer_name": "Northwind Legal Ltd",
"primary_domain": "northwindlegal.example",
"domain_verified": true,
"domain_verified_at": "2026-05-03T10:22:41+00:00"
}
}
attestation_idThe id from the path.
match_resultTrue when content_hash equals one of the stored hashes.
matched_fieldWhich stored hash matched, checked in this order; null when none did. One of attestation_hash, input_hash, output_hash, payload_hash.
anchored_hashThe stored attestation_hash, whatever was matched.
submitted_hashcontent_hash as received, decoded and re-encoded.
anchored_atThe record's created_at.
organizationThe issuing organization, read from the tenant's organization row when the call is made.
organization.nameOrganization display name.
organization.issuer_nameIssuer name shown on proof pages.
organization.primary_domainThe organization's primary domain.
organization.domain_verifiedTrue once the primary domain passed DNS verification.
organization.domain_verified_atWhen the domain was verified, written with a +00:00 offset; omitted when the domain is not verified.
organization.logo_urlLogo URL set in the dashboard; omitted when not set.
bad_requestcontent_hash is not exactly 64 hex characters.
invalid_path_parameterattestation_id is not a UUID.
missing_api_keyNeither an Authorization header nor an X-API-Key header was sent.
invalid_authorization_schemeThe Authorization header is present but does not use the Bearer scheme.
invalid_api_key_formatThe key does not start with invoance_live_.
invalid_api_keyThe key does not match any API key.
api_key_revokedThe key has been revoked.
ip_not_allowedThe key has an IP allowlist and the caller's address is not on it.
api_key_lookup_failedThe key could not be looked up in the database.
insufficient_scopeThe key has neither the read scope nor the write scope.
payment_requiredThe subscription is past due on a paid plan.
feature_not_availableThe plan's monthly API verifications limit is 0.
quota_exceededThe plan's monthly API verifications limit for the current billing period is used up.
quota_check_failedThe quota counter could not be read; the request is rejected rather than allowed through.
attestation_not_foundNo attestation with this id belongs to the tenant, or the record has not been written by the worker yet.
rate_limitedThe tenant used up its per-second or per-minute request budget; the Retry-After header says when to retry.
db_errorThe database read failed.
input_hash | SHA-256 of the UTF-8 bytes of payload.input, hex encoded. |
|---|---|
output_hash | SHA-256 of the UTF-8 bytes of payload.output, hex encoded. |
payload_hash | SHA-256 of the canonical request bytes, which are the request re-serialized by the server; returned by ingest and stored as attestation_hash. |
attestation_hash | The record's key hash, equal to payload_hash and unique per tenant; a second request with the same bytes returns the existing record. |
signed_payload | Compact JSON with v, attestation_id, tenant_id, attestation_type, input_hash, output_hash, payload_hash, model_provider, model_name, model_version and created_at; the bytes the tenant key signed. |
| signature | Ed25519 signature over signed_payload, 64 bytes, hex encoded. |
public_key | The tenant's raw 32-byte Ed25519 public key, hex encoded, the same for every record of the tenant. |
| subject | Optional context about who or what triggered the call: user_id, session_id and up to 20 custom keys, all part of the hashed bytes. |
retention_policy | Label derived from the plan's retention days when the row was written: short (up to 90), standard (up to 365), extended (up to 1825), regulatory (up to 2555), indefinite otherwise. |
| sealed | A record past its retention window; it is left out of the list, get and raw return 410 retention_expired, verify still works, and a plan upgrade can unseal it. |
| canonical bytes | The request as the server re-serializes it: compact JSON, fields in the order type, payload, context, subject, trace_id, nulls for absent subject and trace_id, custom subject keys sorted. |