API Spec
OpenAI-compatible chat completions with signed receipts and hardware-attestation evidence at api.confidential.ai.
The Confidential Inference API provides OpenAI-compatible chat completions at
https://api.confidential.ai. The gateway validates the caller key at the
public boundary. It does not forward that key to the inference node.
New here? Overview explains the attested inference flow before you make a request.
All endpoints use HTTPS. Every response contains an x-request-id header.
Save this value when a completion receipt is needed.
Quickstart
Set an API key and read the available model list. Use a listed model ID in a completion request.
export CONFIDENTIAL_API_KEY='<API_KEY>'
export CONFIDENTIAL_API_BASE='https://api.confidential.ai'
curl --fail --silent "$CONFIDENTIAL_API_BASE/v1/models" | jq .curl --fail --silent "$CONFIDENTIAL_API_BASE/v1/chat/completions" \
-H "Authorization: Bearer $CONFIDENTIAL_API_KEY" \
-H 'Content-Type: application/json' \
--data '{
"model": "<MODEL_ID_FROM_V1_MODELS>",
"messages": [{"role": "user", "content": "Reply with one word: ready."}],
"stream": false
}' | jq .Attestation
Attestation lets you verify the confidential path before you send a prompt. The public attestation endpoint needs no API key. It returns fresh evidence from both the gateway and the inference worker. It does not contain your API key, prompt, or completion.
Use this sequence for a verified request:
- Generate a new 32-byte nonce and call
GET /v1/aci/attestation. - Verify the gateway and worker evidence, the nonce bindings, and the advertised receipt-signing key.
- Use an API key provisioned for your application to call
POST /v1/chat/completions. - Save the completion response
x-request-idand fetch its receipt. - Verify the receipt signature against the receipt-signing key in the attestation response.
- Request fresh attestation when you need new proof. If you revoke an API
key, the gateway rejects later use of that key with
401 invalid_api_key.
What the evidence proves
The response contains two linked hardware evidence records.
- Gateway evidence. The public gateway uses native Intel TDX evidence with
profile
tdx-direct/v1. Its quote binds the caller nonce, the live gateway TLS identity, and the public key that verifies completion receipts. Verify the quote signature, the expected gateway measurement and TCB policy, and every report-data binding before you trust this claim. - Worker evidence. The gateway obtains fresh c8s Intel TDX evidence from the inference worker. The worker evidence binds the caller nonce to the worker TLS public key for the verified private worker channel. API clients do not connect to that channel directly. Verify the quote signature, expected worker measurement and TCB policy, and its TLS-key binding.
- Receipt evidence. A completed request has an Ed25519-signed receipt. The receipt contains request and response hashes, safe request metadata, and the signature. Verify it with the receipt public key that the gateway attestation advertises.
This proof does not prove that the model output is correct, that the service is available, or that an API key belongs to a particular person. It does not prevent denial of service. It proves only the verified software and TLS identities represented by the evidence, for the nonce and policy values you verify.
Call the API
Use these calls in order when you need an attested completion. Set the base URL once. Replace placeholders with values from your account and model catalog.
export CONFIDENTIAL_API_BASE='https://api.confidential.ai'
export CONFIDENTIAL_API_KEY='<API_KEY>'1. Get fresh attestation before you send a prompt
Generate a new 32-byte nonce. This endpoint is public. Do not send an API key or prompt with this request.
NONCE="$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=')"
curl --fail --silent \
"$CONFIDENTIAL_API_BASE/v1/aci/attestation?nonce=$NONCE" | jq .Verify the returned gateway and worker evidence before you send sensitive input. Check the nonce bindings, the expected measurements and TCB policies, the TLS-key bindings, and the advertised receipt-signing key. See Request and verify attestation for the full checks.
2. Send a non-streaming completion
Use a model ID returned by GET /v1/models. The completion endpoint requires
an API key. Save the x-request-id response header; you use it to read the
receipt.
curl --fail --silent --show-error \
--dump-header response-headers.txt \
"$CONFIDENTIAL_API_BASE/v1/chat/completions" \
-H "Authorization: Bearer $CONFIDENTIAL_API_KEY" \
-H 'Content-Type: application/json' \
--data '{
"model": "<MODEL_ID_FROM_V1_MODELS>",
"messages": [
{"role": "user", "content": "Explain why the sky appears blue in two sentences."}
],
"stream": false
}' | jq .
REQUEST_ID="$(awk 'BEGIN { IGNORECASE = 1 } /^x-request-id:/ { gsub("\\r", ""); print $2 }' response-headers.txt)"3. Read the signed receipt
Receipts are public and do not require an API key. They contain request and response hashes, safe metadata, and a signature. They do not contain the prompt or completion.
curl --fail --silent \
"$CONFIDENTIAL_API_BASE/v1/aci/receipts/$REQUEST_ID" | jq .Verify the Ed25519 signature with the receipt-signing key in the attestation response from step 1. See Read and verify a receipt.
4. Check health and list models
These public endpoints are useful before normal application traffic. Health is a readiness check, not hardware evidence. The model catalog is the allowed model list for completion requests.
curl --fail --silent "$CONFIDENTIAL_API_BASE/health" | jq .
curl --fail --silent "$CONFIDENTIAL_API_BASE/v1/models" | jq .Stream a completion
Use the same API key and model ID. The response is server-sent events and ends
with data: [DONE].
curl --no-buffer --fail --silent --show-error \
"$CONFIDENTIAL_API_BASE/v1/chat/completions" \
-H "Authorization: Bearer $CONFIDENTIAL_API_KEY" \
-H 'Content-Type: application/json' \
--data '{
"model": "<MODEL_ID_FROM_V1_MODELS>",
"messages": [
{"role": "user", "content": "Give three short facts about oceans."}
],
"stream": true
}'Optional compatibility and supporting routes
GET /v1/attestation/report is a compatibility representation of the same
attestation evidence. GET /v1/aci/sessions/{session_id} returns supporting
worker-attestation session data linked by the canonical attestation response.
Use the canonical GET /v1/aci/attestation response for new integrations.
Authentication
POST /v1/chat/completions requires this header:
Authorization: Bearer <API_KEY>The health, model, receipt, and attestation endpoints do not require a caller API key. Do not put an API key in an attestation request or browser code.
All JSON errors use this shape:
{"error":{"code":"machine_readable_code"}}Create a chat completion
POST /v1/chat/completions
Accepts an OpenAI-compatible chat-completions request. model is required and
must exactly match an ID from GET /v1/models. The gateway relays the
remaining valid request fields to the configured OpenAI-compatible inference
service.
| Field | Type | Required | Description |
|---|---|---|---|
model | string | yes | An exact model ID from GET /v1/models. |
messages | array | passed through | OpenAI-compatible messages. The gateway forwards this field unchanged. |
stream | boolean | no | false or omitted returns JSON. true returns server-sent events. |
A non-streaming success is an OpenAI-compatible completion object with HTTP
200 OK.
{
"id": "chatcmpl-example",
"object": "chat.completion",
"model": "model-id",
"choices": [{
"index": 0,
"message": {"role": "assistant", "content": "ready"},
"finish_reason": "stop"
}]
}For "stream": true, the response content type is text/event-stream. The
stream contains OpenAI-compatible chunks and ends with data: [DONE].
data: {"object":"chat.completion.chunk","choices":[{"delta":{"content":"ready"}}]}
data: [DONE]List models
GET /v1/models
Returns the public model catalog. This endpoint does not query an inference worker.
{
"object": "list",
"data": [
{"id": "model-id", "object": "model"}
]
}Request and verify attestation
GET /v1/aci/attestation?nonce={base64url}
Returns the canonical attestation document. It does not require an API key.
nonce must be unpadded base64url and decode to exactly 32 random bytes.
Each nonce is single use.
NONCE="$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=')"
curl --fail --silent \
"$CONFIDENTIAL_API_BASE/v1/aci/attestation?nonce=$NONCE" | jq .The response has this shape. Evidence values are abbreviated. Use exact returned values during verification.
{
"api_version": "aci/1",
"workload_id": "gateway-workload-id",
"workload_keyset_digest": "sha256:...",
"attestation": {
"vendor": "intel",
"tee_type": "tdx",
"workload_keyset": {"receipt_signing_keys": [{"algo": "ed25519", "public_key": "..."}]},
"report_data": "128-hex-character-value",
"aci_statement_digest": "64-hex-character-value",
"evidence": {
"gateway": {
"profile": "tdx-direct/v1",
"attestation_api": {"platform": "tdx", "evidence": {"quote": "..."}}
},
"upstream_session_id": "as_...",
"node": {"node_nonce": "...", "node_spki_sha256": "sha256:...", "c8s_evidence": {}}
}
}
}HTTP 200 does not prove attestation. Verify all bindings:
- Decode
attestation.report_dataas 64 bytes. Its final 32 bytes must equal the nonce. Its first 32 bytes must equalSHA-256(signing_address || tls_certificate_fingerprint). - Read the live TLS certificate and compute its fingerprint. The
signing_addressis the raw public key of the Ed25519 receipt-signing key published inattestation.workload_keyset.receipt_signing_keys. - Decode the Intel TDX quote in
attestation.evidence.gateway.attestation_api. Verify its signature and collateral, then evaluate it against your expected gateway measurement and TCB policy. Its 64-byte report-data field must equal the value from step 1. - Verify
attestation.evidence.node.c8s_evidencewith the expected TDX measurement and TCB policy. Its report data must bind the node TLS SPKI and nonce. Compare the verified node SPKI hash withnode_spki_sha256. - Fetch the linked session and verify its evidence digest and TLS channel binding.
This gateway uses the tdx-direct/v1 profile. Its Intel TDX quote binds the
full 64-byte report-data value directly.
Invalid nonce input returns 400 invalid_attestation_request. Reusing a nonce
returns 409 attestation_nonce_replayed. A full nonce guard returns 429 attestation_nonce_store_full. If the gateway or node cannot produce fresh
evidence, the endpoint returns 503 attestation_unavailable. Treat 503 as a
failed attestation result.
GET /v1/attestation/report?version=2&signing_algo=ecdsa&nonce={base64url}
Returns a compatibility representation of the same gateway and node evidence.
The parameters must be exactly version=2, signing_algo=ecdsa, and a new
32-byte base64url nonce. Do not reuse a nonce from the canonical endpoint.
NONCE="$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=')"
curl --fail --silent \
"$CONFIDENTIAL_API_BASE/v1/attestation/report?version=2&signing_algo=ecdsa&nonce=$NONCE" \
| jq .It contains signing_address, a base64-encoded 64-byte report_data,
gateway_attestation, and node_attestation. Apply the same TDX gateway and
node verification checks as for the canonical representation. This
representation does not include api_version: "aci/1" or
aci_statement_digest.
GET /v1/aci/sessions/{session_id}
Returns the short-lived node-attestation session linked by
upstream_session_id in an attestation response.
{
"api_version": "aci/1",
"session_id": "as_...",
"channel_binding": [{"type": "tls_spki_sha256", "spki_sha256": "sha256:..."}],
"claims": {},
"evidence": {
"digest": "sha256:...",
"data": "data:application/json;base64,..."
}
}Decode the evidence data URL and verify its SHA-256 digest before parsing it.
Compare the session TLS SPKI binding with the node binding in the attestation
response. The session is supporting evidence. It does not replace gateway or
node attestation verification. Unknown or expired sessions return 404 session_not_found.
Read and verify a receipt
GET /v1/aci/receipts/{request_id}
Returns a signed receipt for a recent completed request. Use the
x-request-id response header from POST /v1/chat/completions as
{request_id}.
curl --fail --silent \
"$CONFIDENTIAL_API_BASE/v1/aci/receipts/$REQUEST_ID" | jq .{
"draft": {
"request_id": "request-id",
"status": 200,
"request_sha256": "sha256:...",
"response_sha256": "sha256:...",
"streaming": false
},
"created_at_unix_ms": 0,
"algorithm": "ed25519",
"signing_address": "base64url-ed25519-public-key",
"signature": "base64url-ed25519-signature"
}The receipt contains hashes and safe request metadata only. It does not contain
the prompt, completion, API key, or receipt private key. Receipts expire after
15 minutes. An unknown, expired, or unavailable receipt returns 404 with
receipt_not_found.
Verify the Ed25519 signature with signing_address. The signed payload has
these exact fields:
{
"api_version": "aci/1",
"purpose": "inference.receipt.v1",
"request_id": "...",
"request_sha256": "sha256:...",
"response_sha256": "sha256:...",
"status": 200,
"streaming": false
}Use a verifier that serializes this receipt payload exactly as the API defines. Then verify that the receipt key is bound by attestation evidence that you trust.
Health
GET /health
Returns gateway readiness. It is not attestation evidence.
curl --fail --silent "$CONFIDENTIAL_API_BASE/health" | jq .{"status":"ok"}When the gateway cannot use its verified inference-node channel, it returns
503 Service Unavailable, Retry-After: 1, and:
{"status":"unavailable"}Errors and retry rules
| HTTP | Code or body | Meaning | Client action |
|---|---|---|---|
400 | invalid_request | Completion JSON is invalid or model is missing. | Correct the request. |
400 | invalid_attestation_request | Attestation parameters are invalid. | Generate a valid new nonce. |
401 | invalid_api_key | The caller key is missing or invalid. | Obtain a valid key. Do not retry unchanged. |
404 | model_not_found | The model is not in the catalog. | Use GET /v1/models. |
404 | receipt_not_found | The receipt is unavailable or expired. | Use the correct recent request ID. |
404 | session_not_found | The attestation session is unavailable or expired. | Request new attestation with a new nonce. |
409 | attestation_nonce_replayed | The attestation nonce was already used. | Generate a new nonce. |
429 | upstream_unavailable | The verified node path is temporarily unavailable. | Honor Retry-After and retry safely. |
429 | attestation_nonce_store_full | The nonce guard is full. | Back off and retry with a new nonce. |
503 | attestation_unavailable | Fresh gateway or node evidence is unavailable. | Fail closed. Do not accept cached or partial evidence. |
503 | {"status":"unavailable"} | The gateway is not ready. | Honor Retry-After and retry. |