Skip to main content

What the receipt proves

Every Confidential AI response includes a signed receipt id in the x-receipt-id header. The receipt is an ordered transparency event log. It records request and response hashes, the selected route, upstream verification status, and the signature that binds the record to the attested gateway keyset. For a confidential response, the receipt’s upstream.verified event shows result: verified, required: true, and a session_id. That session records the verified upstream security context and channel binding.

Prerequisites

export API_KEY="<API_KEY>"
export BASE="https://inference.phala.com"
You need curl, jq, and a SHA-256 tool. On Linux use sha256sum; on macOS use shasum -a 256.

1. Fetch a fresh gateway attestation

export NONCE="$(openssl rand -hex 16)"

curl -s "$BASE/v1/aci/attestation?nonce=$NONCE" \
  -H "Authorization: Bearer $API_KEY" \
  -o report.json
Verify the report as described in Verify Attestation. The receipt checks below assume you trust the attested keyset from this report.

2. Make a request and capture the receipt id

curl -s -D headers.txt "$BASE/v1/chat/completions" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"phala/qwen3.5-27b",
       "messages":[{"role":"user","content":"Explain attestation in one sentence."}]}' \
  -o response.json

export RECEIPT_ID="$(grep -i '^x-receipt-id' headers.txt | tr -d '\r' | awk '{print $2}')"
echo "receipt: $RECEIPT_ID"
Keep response.json; the receipt commits to the exact response bytes.

3. Fetch the signed receipt

curl -s "$BASE/v1/aci/receipts/$RECEIPT_ID" \
  -H "Authorization: Bearer $API_KEY" \
  -o receipt.json

jq '{receipt_id, workload_id, workload_keyset_digest, endpoint}' receipt.json

4. Match the receipt to the attested gateway

test "$(jq -r .workload_id report.json)" = "$(jq -r .workload_id receipt.json)" \
  && test "$(jq -r .workload_keyset_digest report.json)" = "$(jq -r .workload_keyset_digest receipt.json)" \
  && echo "workload matches" || echo "MISMATCH"
If these values do not match, the receipt is not bound to the attestation report you verified.

5. Check the response hash

The response.returned.wire_hash event must match the bytes you received.
# Linux:
RESP_HASH="sha256:$(sha256sum response.json | awk '{print $1}')"

# macOS alternative:
# RESP_HASH="sha256:$(shasum -a 256 response.json | awk '{print $1}')"

RECEIPT_RESP_HASH="$(jq -r '.event_log[] | select(.type=="response.returned") | .wire_hash' receipt.json)"

echo "received: $RESP_HASH"
echo "receipt:  $RECEIPT_RESP_HASH"
test "$RESP_HASH" = "$RECEIPT_RESP_HASH" && echo "response hash matches"
The receipt also contains request.received.body_hash. In production, hash the exact request body bytes you sent and compare them to that event.

6. Verify the receipt signature

The receipt signature covers the canonical receipt bytes. Verify it under one of the receipt_signing_keys entries from attestation.workload_keyset.
# Conceptual:
# verifier check-receipt --attestation report.json --receipt receipt.json
A receipt signed by a key outside the attested keyset is not bound to the verified gateway and must fail verification.

7. Confirm the upstream was confidential

jq '.event_log[] | select(.type=="upstream.verified")
    | {provider, model_id, result, required, session_id}' receipt.json
Interpretation:
  • Confidential response: result is verified, required is true, and session_id is present.
  • Routed response: result is failed, required is false, and no session_id is present. The gateway was attested, but the upstream provider was not.
To inspect the immutable upstream security context:
SESSION_ID="$(jq -r '.event_log[] | select(.type=="upstream.verified") | .session_id' receipt.json)"

curl -s "$BASE/v1/aci/sessions/$SESSION_ID" \
  -H "Authorization: Bearer $API_KEY" | \
  jq '{session_id, provider, endpoint, channel_binding, claims}'

Legacy signature endpoint

Older clients may call GET /v1/signature/{id}. It returns the same receipt inside a compatibility envelope:
curl -s "$BASE/v1/signature/$RECEIPT_ID" \
  -H "Authorization: Bearer $API_KEY"
New clients should use GET /v1/aci/receipts/{id} directly.

What a passing verification proves

  • The gateway is a specific reviewed workload in a genuine TEE.
  • The receipt is signed by a key in the attested gateway keyset.
  • The response you received is exactly the response the gateway signed.
  • For a confidential model response, the upstream provider was verified and channel-bound before forwarding.