{
  "$id": "https://glide.co/schemas/agent-banking/draft/receipt.json",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Receipt",
  "description": "Tool-call receipt persisted in `activity_log` after a money-touching MCP call settles. Every write tool emits exactly one receipt per call. At v1 the receipt lives in DB (activityLog + agent columns). v1.5 promotes to signed + Merkle-leaf + EAS-anchored tree head. Money-safety: `on_chain_tx` is server-fetched from chain RPC — NEVER from a facilitator's receipt body (F1, x402.pay client-receipt-trust learning).",
  "channel": "v1",
  "examples": [
    {
      "receipt_id": "11111111-1111-4111-8111-111111111111",
      "principal_id": "22222222-2222-4222-8222-222222222222",
      "agent_principal_id": "33333333-3333-4333-8333-333333333333",
      "grant_id": "44444444-4444-4444-8444-444444444444",
      "policy_version": 1,
      "tool_call_id": "55555555-5555-4555-8555-555555555555",
      "idempotency_key": "idemp-abc-123",
      "action": "payments.initiate",
      "risk_verdict": "allow",
      "rail": "usdc-solana",
      "vendor_used": "squads",
      "amount_cents": 10000,
      "currency": "USDC",
      "counterparty_address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
      "counterparty_chain": "sol",
      "counterparty_token": "USDC",
      "on_chain_tx": "5xKf9q5tUJTuT9w8PNMo4WxFf2NAfx1YGv2XrdL5cHvCwJgGEN9pqRUxDvRJSwkV3bKpwSn4cyFFfh4SdVjg6Csa",
      "timestamp": "2026-04-25T00:00:00.000Z"
    },
    {
      "receipt_id": "11111111-1111-4111-8111-111111111111",
      "principal_id": "22222222-2222-4222-8222-222222222222",
      "agent_principal_id": "33333333-3333-4333-8333-333333333333",
      "grant_id": "44444444-4444-4444-8444-444444444444",
      "policy_version": 1,
      "tool_call_id": "55555555-5555-4555-8555-555555555555",
      "idempotency_key": null,
      "action": "accounts.list",
      "risk_verdict": "allow",
      "rail": "internal",
      "vendor_used": null,
      "amount_cents": 0,
      "currency": "USD",
      "counterparty_address": null,
      "counterparty_chain": null,
      "counterparty_token": null,
      "on_chain_tx": null,
      "timestamp": "2026-04-25T00:00:00.000Z"
    }
  ],
  "schema": {
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "object",
    "properties": {
      "receipt_id": {
        "$ref": "_types.json#/$defs/uuidV4",
        "description": "Primary key of this receipt row. Globally unique."
      },
      "principal_id": {
        "$ref": "_types.json#/$defs/uuidV4",
        "description": "Human principal whose vault the call hit."
      },
      "agent_principal_id": {
        "$ref": "_types.json#/$defs/uuidV4",
        "description": "Synthetic principal for the agent that made the call."
      },
      "grant_id": {
        "$ref": "_types.json#/$defs/uuidV4",
        "description": "JTI of the grant that authorized the call. Foreign key into the grants table; allows revocation forensics."
      },
      "policy_version": {
        "$ref": "_types.json#/$defs/nonNegativeInt",
        "description": "Policy version that gated this call. Pinned for audit so a later policy mutation doesn't rewrite history."
      },
      "tool_call_id": {
        "$ref": "_types.json#/$defs/uuidV4",
        "description": "ID of the upstream MCP tool call. Allows joining receipt-side audit with router-side metrics."
      },
      "idempotency_key": {
        "anyOf": [
          {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9._:\\-]+$"
          },
          { "type": "null" }
        ],
        "description": "Caller-supplied idempotency key. Replaying the same key on the same (vault_id, action) returns the original receipt rather than re-executing. Null if the call wasn't idempotency-keyed."
      },
      "action": {
        "type": "string",
        "minLength": 1,
        "maxLength": 64,
        "pattern": "^[a-z][a-z0-9_]*\\.[a-z][a-z0-9_]*$",
        "description": "Tool identifier in `<namespace>.<name>` form, e.g. `payments.initiate`, `accounts.list`. Lowercase ASCII; not reusing AgentScope because read tools don't 1:1 to write-scope strings."
      },
      "risk_verdict": {
        "$ref": "_types.json#/$defs/riskVerdict",
        "description": "Policy-engine verdict at the gate. `allow_with_step_up` here means step-up was completed before the tool ran (denied receipts shouldn't exist — denied calls don't settle)."
      },
      "rail": {
        "type": "string",
        "minLength": 1,
        "maxLength": 32,
        "pattern": "^[a-z][a-z0-9-]*$",
        "description": "Settlement rail used. Lowercase kebab-case. Examples: `usdc-base`, `usdc-solana`, `ach-bridge`, `wire-bridge`, `card-stripe`. Free-text vocabulary intentional (rail set grows faster than schema bumps); use the canonical name from the rail's connector manifest."
      },
      "vendor_used": {
        "anyOf": [
          {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "pattern": "^[a-z][a-z0-9-]*$"
          },
          { "type": "null" }
        ],
        "description": "Connector slug that ultimately settled the tx (matches ConnectorManifest.slug). Null for in-protocol settlement (e.g. direct on-chain transfer with no intermediary)."
      },
      "amount_cents": {
        "$ref": "_types.json#/$defs/amountCents",
        "description": "Settled amount in minor units (cents for fiat; 1e-2 USD-equivalent for stablecoins). Server-derived from chain receipt or facilitator confirmation; NEVER trust a client-supplied value."
      },
      "currency": {
        "$ref": "_types.json#/$defs/currencyCode",
        "description": "Currency identifier — ISO 4217 alpha-3 fiat (`USD`) or stablecoin symbol (`USDC`)."
      },
      "counterparty_address": {
        "anyOf": [
          {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          { "type": "null" }
        ],
        "description": "Counterparty address. EVM (0x-hex) or Solana (base58). Null for on-platform transfers where there's no chain-side counterparty (e.g. internal vault rebalance)."
      },
      "counterparty_chain": {
        "anyOf": [{ "$ref": "_types.json#/$defs/chainId" }, { "type": "null" }],
        "description": "Chain the counterparty address lives on. Null if and only if `counterparty_address` is null."
      },
      "counterparty_token": {
        "anyOf": [
          {
            "type": "string",
            "minLength": 1,
            "maxLength": 32
          },
          { "type": "null" }
        ],
        "description": "Token symbol or contract identifier the counterparty received (`USDC`, `USDT`). Null for fiat rails."
      },
      "on_chain_tx": {
        "anyOf": [
          {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          { "type": "null" }
        ],
        "description": "On-chain transaction hash. EVM: 0x-prefixed 32-byte hex. Solana: base58 signature. SERVER-FETCHED from chain RPC, never from a facilitator's receipt body. Null for fiat rails or pre-settlement receipts."
      },
      "timestamp": {
        "$ref": "_types.json#/$defs/isoDateTimeUtc",
        "description": "When the receipt was finalized (UTC instant). Server-stamped at write time."
      }
    },
    "required": [
      "receipt_id",
      "principal_id",
      "agent_principal_id",
      "grant_id",
      "policy_version",
      "tool_call_id",
      "idempotency_key",
      "action",
      "risk_verdict",
      "rail",
      "vendor_used",
      "amount_cents",
      "currency",
      "counterparty_address",
      "counterparty_chain",
      "counterparty_token",
      "on_chain_tx",
      "timestamp"
    ],
    "additionalProperties": false
  }
}
