Home
Home/Developers/AI Attestations/Schema
Ed25519 signaturesSHA-256 hashes8 SDKs
Status
Sign inStart free
Home
Start here
Overview
Authentication
Errors
FAQ
API
Events
Canonical JSON and hashes
Documents
Anchoring a file
AI attestations
Attestation schema
Verifying attestations
Traces
Sealing a trace
Audit logs
Organizations
Streams
Portal
Public proof
Event schema
Exporting events
Integrations
Clerk
Auth0
Embeddable viewer
All endpoints
Reference
SDKs
Node.js
Python
Go
Java
Ruby
Rust
.NET
PHP
REST
Verification
Docs · AI attestations

Attestation schema

What you send, the bytes the server hashes, and the bytes the tenant key signs.

AI Attestations APIVerifying attestations
sha-256 · 3a352297…2592
sha-256 · 58cffc7b…f059

An attestation records one model call: the input text, the output text, the model provider, name and version, and an optional subject.

The server hashes the input, the output and the whole request. A worker then signs a short payload built from those hashes with the tenant's Ed25519 key.

The get endpoint returns the hashes, the signed bytes, the signature and the public key. The raw endpoint returns the request bytes. Both are stored as written, never rebuilt.

Signed recordGET /v1/ai/attestations/{attestation_id}
ai attestation · type outputSignature valid
input
What is the company refund policy?
output
Our refund policy allows returns within 30 days of delivery…
model
openai · gpt-4.1 · 2026-01-01
input_hash
2c8f05d1…a7e4409b
output_hash
f06b3a9e…11d27c58
subject
user_7b1c · sess_4f9a
Request fieldsIngest endpoint

Five top level fields. type, payload and context are required; subject and trace_id are optional.

Every field is part of the hashed bytes, including a null subject or trace_id.

type
outputThe record attests a model output.
decisionThe record attests a decision taken on a model output.
approvalThe record attests an approval of a model output.
Top level
typestring · required

What the record attests: a model output, a decision taken on it, or an approval of it. One of output, decision, approval.

trace_iduuid

An open trace owned by the tenant; the attestation is attached to it and included when the trace is sealed.

payload
payloadobject · required

The text that is hashed.

payload.inputstring · required · must not be empty or whitespace only

The prompt or input text; its SHA-256 becomes input_hash.

payload.outputstring · required · must not be empty or whitespace only

The model's output text; its SHA-256 becomes output_hash.

context
contextobject · required

Which model produced the output.

context.model_providerstring · required · must not be empty or whitespace only

Provider name, for example openai or anthropic; also a list filter.

context.model_namestring · required · must not be empty or whitespace only

Model name as the provider names it.

context.model_versionstring · required · must not be empty or whitespace only

Model version or snapshot date.

subject
subjectobject · up to 8 KB serialized, at most 20 keys besides user_id and session_id

Who or what triggered the call; every key becomes part of the hashed bytes.

subject.user_idstring

Your identifier for the end user.

subject.session_idstring

Your identifier for the session or conversation.

subject.*object · at most 20 keys

Any other keys with JSON values, kept as custom context and written in sorted key order.

Canonical bytes

The server does not hash the bytes you sent. It parses the request and writes it again as compact JSON. Those bytes are hashed, stored, and returned by the raw endpoint.

  • Fields in this order: type, payload, context, subject, trace_id.
  • No whitespace between tokens.
  • A missing subject or trace_id is written as null.
  • Inside subject: user_id, then session_id, each only when sent, then custom keys sorted by key.
  • Only quotes, backslashes and control characters are escaped. Other characters are written as UTF-8.
  • More than 1 MB of canonical bytes returns 413 payload_too_large.

To reproduce payload_hash, hash the raw endpoint's body as returned. Rebuilding the bytes yourself works only if every rule above holds.

{
  "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"
  }
}
HashesExample record

Three SHA-256 values are computed at ingest and returned in the 201 response. Each one covers a single byte string; nothing is concatenated.

Anyone holding the text or the raw bytes can recompute them with a standard SHA-256 and no Invoance tooling. The example values on this page check out against the get endpoint's record.

input_hashSHA-256 of the UTF-8 bytes of payload.input, hex encoded.
output_hashSHA-256 of the UTF-8 bytes of payload.output, hex encoded.
payload_hashSHA-256 of the canonical bytes, hex encoded. Returned by ingest.
attestation_hashThe same value as payload_hash, stored as the row's key. It is unique per tenant, so the same bytes twice return the existing record.
Signed bytes

After the write queue accepts the record, a worker builds a second compact JSON object and signs it. That object is signed_payload.

  • The signature is plain Ed25519 over these bytes. No prehash, no domain tag.
  • The key is the tenant's signing key. public_key is its raw 32-byte public key, hex encoded.
  • The input, output and subject are not in the signed bytes. payload_hash binds them.
  • Verify the hex decoded bytes exactly as the get endpoint returns them.
  • created_at in the signed bytes is the string the worker signed. A payload rebuilt from the other fields can differ in it.
Fields, in order
vThe payload version. Always 1.
attestation_idThe id returned by ingest.
tenant_idThe tenant that owns the record.
attestation_typeThe type sent at ingest.
input_hashAs above.
output_hashAs above.
payload_hashAs above. This field binds the input, output and subject to the signature.
model_providercontext.model_provider as sent.
model_namecontext.model_name as sent.
model_versioncontext.model_version as sent.
created_atWhen the server accepted the request. RFC 3339 in UTC with a Z suffix; real values carry fractional seconds.
{
  "v": 1,
  "attestation_id": "7c1e4b52-9a0f-4d2e-b6f3-2f8a61c0d9e4",
  "tenant_id": "3b9d6f10-52c4-4a7e-9e1b-8d0c2f4a6e71",
  "attestation_type": "output",
  "input_hash": "a46f0ad5dfb45fcbe2c303dcb34292af2f63fd79c6ff45b52dc40703a1cc5a70",
  "output_hash": "a11ffee6d6ba93b478f557318a2b1a823f6e2816e0bd5d8178cc889a30e40fe5",
  "payload_hash": "c4efe15781214a84046ad7e0592977c634a5cf45f4c1e06160daf760a295a8df",
  "model_provider": "openai",
  "model_name": "gpt-4.1",
  "model_version": "2026-04-14",
  "created_at": "2026-09-22T08:14:07Z"
}
The same record twice

Identical canonical bytes give the same payload_hash, and one tenant holds one record per hash. An Idempotency-Key adds exact response replay on top.

To keep two identical calls apart, add a subject key such as a request id. It changes the canonical bytes and the hash with them.

Full replay rules are on the ingest endpoint.

Same body, with or without a key200 with status duplicate and the existing attestation_id. Nothing new is written.
Same Idempotency-Key, same bodyThe first response, replayed for 24 hours.
Same Idempotency-Key, different body409 idempotency_key_reuse_mismatch.
Retry while the first request is running202 with a body of {"status":"processing"}. Retry after a moment.
Questions
Are the input and output stored?

Yes. The canonical bytes are stored in object storage and returned by the raw endpoint. The get and list endpoints return only the hashes.

Why does my recomputed payload_hash differ?

You hashed different bytes. Pretty printing, another key order or an unsorted subject changes the hash. Hash the raw endpoint's body as returned.

What happens when retention ends?

The row is sealed, not deleted. It leaves the list, get and raw return 410 retention_expired, verify still answers, and a plan upgrade can unseal it.

Proof infrastructure. Records are hashed, signed with your organization's Ed25519 key, and stored append-only, so anyone can check them later.

Products

  • Audit Logs
  • Event Ledger
  • AI Attestation
  • Document Anchoring
  • Traces

Developers

  • Documentation
  • API reference
  • SDKs
  • How it works
  • How traces seal
  • System status

Verify

  • Audit Log
  • Event
  • AI Attestation
  • Document
  • Trace

Company

  • Company overview
  • What is Invoance
  • Pricing
  • Security
  • Compliance teams
  • Finance teams
  • Partners
  • Resources
  • Help center
  • Contact
© 2025 – 2026 Invoance, Inc. All rights reserved.© 2026 Invoance, Inc. All rights reserved.
PrivacyLegal noticeLegal FAQ