{
  "openapi": "3.1.0",
  "info": {
    "title": "Quitanza API",
    "version": "0.3.0",
    "description": "The settlement assurance layer for the agent economy. Escrows lock simulated funds, deliveries are verified against machine-readable terms, and disputes resolve only by co-signature, arbiter signature or timeout default. Every closed matter issues a quitanza, a signed, independently verifiable settlement proof. Simulated rails: no real funds. Hosted deployments require `Authorization: Bearer <api key>` on every /v1 route (401 otherwise) and rate-limit /v1 per client (429 over budget); a local sandbox without QUITANZA_API_KEY runs open."
  },
  "servers": [
    {
      "url": "http://localhost:4280",
      "description": "Local sandbox"
    }
  ],
  "tags": [
    {
      "name": "agents",
      "description": "Sandbox-only key registration. The sandbox holds these keys server-side; production agents sign locally and never transmit private keys."
    },
    {
      "name": "mandates",
      "description": "Principal-signed caps binding an agent key: per-escrow max, cumulative max, allowed assets, optional counterparty allowlist, expiry. Optional in the sandbox; when present, every cap is enforced at escrow creation."
    },
    {
      "name": "escrows",
      "description": "The escrow lifecycle: created → funded → delivered → settled, with refund, dispute and timeout branches. Every state change lands on a hash-chained evidence trail."
    },
    {
      "name": "disputes",
      "description": "Structured challenges. A ruling is accepted only when co-signed by both parties or signed by the named arbiter; otherwise the timeout default applies. The platform never rules."
    },
    {
      "name": "quitanzas",
      "description": "Terminal settlement proofs: Ed25519-signed over canonical JSON, hash-chained to the evidence trail, verifiable offline."
    },
    {
      "name": "webhooks",
      "description": "Durable signed event delivery: Ed25519 signature over the canonical JSON body, retries with backoff, inspectable dead-letter list."
    },
    {
      "name": "x402",
      "description": "The x402 payment handshake settled through escrow (simulated rails)."
    },
    {
      "name": "meta",
      "description": "Service metadata."
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "operationId": "health",
        "summary": "Service health and issuer key",
        "tags": [
          "meta"
        ],
        "responses": {
          "200": {
            "description": "Service is up.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "service": {
                      "type": "string"
                    },
                    "issuer": {
                      "type": "string",
                      "pattern": "^[0-9a-f]{64}$"
                    }
                  }
                },
                "example": {
                  "ok": true,
                  "service": "quitanza-api",
                  "issuer": "9a…f1"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/quitanza-issuer.json": {
      "get": {
        "operationId": "getIssuerDocument",
        "summary": "The issuer keys this deployment signs quitanzas with",
        "description": "Public, no authentication. Serves the Ed25519 issuer keys as { keys: [{ keyId, alg, publicKey, validFrom }] }. A quitanza verifies against a domain when its signing key appears here and the signature checks out offline; the list form supports key rotation.",
        "tags": [
          "meta"
        ],
        "responses": {
          "200": {
            "description": "The issuer key set.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "keyId": {
                            "type": "string"
                          },
                          "alg": {
                            "type": "string",
                            "enum": [
                              "ed25519"
                            ]
                          },
                          "publicKey": {
                            "type": "string",
                            "pattern": "^[0-9a-f]{64}$"
                          },
                          "validFrom": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/openapi.json": {
      "get": {
        "operationId": "getOpenApi",
        "summary": "This document",
        "description": "The OpenAPI 3.1 description of every route, including the error model.",
        "tags": [
          "meta"
        ],
        "responses": {
          "200": {
            "description": "The OpenAPI 3.1 document."
          }
        }
      }
    },
    "/v1/agents": {
      "post": {
        "operationId": "createAgent",
        "summary": "Register a sandbox agent",
        "description": "Generates an Ed25519 keypair held server-side. Sandbox convenience only: production agents keep keys client-side and sign locally.",
        "tags": [
          "agents"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "label": "buyer-agent"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Agent registered; only the public key is returned.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "publicKey": {
                      "type": "string",
                      "pattern": "^[0-9a-f]{64}$"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/mandates": {
      "post": {
        "operationId": "registerMandate",
        "summary": "Register a principal-signed mandate",
        "description": "The signature must be the principal's, over the canonical JSON of {principal, caps}. Registration rejects anything else; creation-time enforcement then holds every escrow under the mandate to its caps. Registration is idempotent by content hash: submitting an identical mandate again returns the existing record with its usage intact.",
        "tags": [
          "mandates"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "principal",
                  "caps",
                  "signature"
                ],
                "properties": {
                  "principal": {
                    "type": "string",
                    "pattern": "^[0-9a-f]{64}$"
                  },
                  "caps": {
                    "$ref": "#/components/schemas/MandateCaps"
                  },
                  "signature": {
                    "$ref": "#/components/schemas/Signature"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Mandate accepted. `hash` is the content hash referenced by trails and quitanzas.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Mandate"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "hash": {
                          "type": "string",
                          "pattern": "^[0-9a-f]{64}$"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "bad_request: missing fields.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "mandate_violation: the signature is not the principal's or does not verify.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/mandates/{id}": {
      "get": {
        "operationId": "getMandate",
        "summary": "Fetch a mandate with its usage",
        "tags": [
          "mandates"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Mandate id (mnd_…)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The mandate, its hash, and cumulative usage.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Mandate"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "hash": {
                          "type": "string",
                          "pattern": "^[0-9a-f]{64}$"
                        },
                        "usage": {
                          "type": "object",
                          "properties": {
                            "spent": {
                              "type": "string",
                              "pattern": "^\\d+(\\.\\d{1,6})?$",
                              "description": "Decimal amount string, at most 6 fraction digits."
                            },
                            "remaining": {
                              "type": "string",
                              "pattern": "^\\d+(\\.\\d{1,6})?$",
                              "description": "Decimal amount string, at most 6 fraction digits."
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/escrows": {
      "post": {
        "operationId": "createEscrow",
        "summary": "Create an escrow",
        "description": "Parties are sandbox agent ids (payerAgentId/payeeAgentId) or raw Ed25519 public keys (payer/payee). Optional: mandateId (caps enforced at creation), arbiter (whose signature alone can later rule on a dispute), timeouts (per-stage deadlines; defaults guarantee silence always resolves).",
        "tags": [
          "escrows"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Any unique string. The first successful response under a key is stored and replayed verbatim on retry, so a retried request can never create a duplicate.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount",
                  "asset",
                  "terms"
                ],
                "properties": {
                  "payerAgentId": {
                    "type": "string"
                  },
                  "payeeAgentId": {
                    "type": "string"
                  },
                  "payer": {
                    "$ref": "#/components/schemas/KeyRef"
                  },
                  "payee": {
                    "$ref": "#/components/schemas/KeyRef"
                  },
                  "amount": {
                    "type": "string",
                    "pattern": "^\\d+(\\.\\d{1,6})?$",
                    "description": "Decimal amount string, at most 6 fraction digits."
                  },
                  "asset": {
                    "type": "string"
                  },
                  "terms": {
                    "$ref": "#/components/schemas/Terms"
                  },
                  "timeouts": {
                    "$ref": "#/components/schemas/EscrowTimeouts"
                  },
                  "mandateId": {
                    "type": "string"
                  },
                  "arbiter": {
                    "$ref": "#/components/schemas/KeyRef"
                  },
                  "arbiterAgentId": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "payerAgentId": "agt_…",
                "payeeAgentId": "agt_…",
                "amount": "25.00",
                "asset": "USDC",
                "terms": {
                  "description": "the agreed report, exactly",
                  "checks": [
                    {
                      "kind": "shape",
                      "requiredFields": [
                        "report"
                      ]
                    }
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Escrow created; emits escrow.created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Escrow"
                }
              }
            }
          },
          "400": {
            "description": "bad_request: missing terms or malformed party.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "mandate_violation: a cap would be exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found: unknown agent or mandate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listEscrows",
        "summary": "List all escrows",
        "tags": [
          "escrows"
        ],
        "responses": {
          "200": {
            "description": "Every escrow, any state.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "escrows": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Escrow"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/escrows/{id}": {
      "get": {
        "operationId": "getEscrow",
        "summary": "Fetch one escrow",
        "tags": [
          "escrows"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Escrow id (esc_…)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The escrow.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Escrow"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/escrows/{id}/fund": {
      "post": {
        "operationId": "fundEscrow",
        "summary": "Mark funds locked (simulated)",
        "description": "Transitions created → funded, stamps the delivery deadline, emits escrow.funded. Local simulation: no real funds, ever.",
        "tags": [
          "escrows"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Escrow id (esc_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Any unique string. The first successful response under a key is stored and replayed verbatim on retry, so a retried request can never create a duplicate.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The funded escrow with its deadlineAt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Escrow"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "invalid_state: not in created state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/escrows/{id}/delivery": {
      "post": {
        "operationId": "submitDelivery",
        "summary": "Submit a delivery; verification runs synchronously",
        "description": "Two forms. Sandbox: { agentId, payload }, where the API signs with the named agent's held key. Client-signed: { payload, submittedAt, signature }, where signature is the payee's Ed25519 signature over canonical {escrowId, contentHash, submittedAt}. A passing verdict settles and the response includes the quitanza; a failing verdict leaves the escrow in delivered for dispute or refund.",
        "tags": [
          "escrows"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Escrow id (esc_…)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "agentId": {
                    "type": "string"
                  },
                  "payload": {},
                  "submittedAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "signature": {
                    "$ref": "#/components/schemas/Signature"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Delivery accepted and judged. Emits delivery.submitted, then verdict.passed (with quitanza.issued) or verdict.failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryResult"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "invalid_state: not in funded state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "unprocessable: wrong signer or bad signature.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/escrows/{id}/refund": {
      "post": {
        "operationId": "refundEscrow",
        "summary": "Refund a funded or failed-verdict escrow",
        "description": "Allowed from funded (no delivery yet) or delivered with a failed verdict. Issues a quitanza with outcome refunded: proof of how the matter closed.",
        "tags": [
          "escrows"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Escrow id (esc_…)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The refund quitanza.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quitanza"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "invalid_state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/escrows/{id}/trail": {
      "get": {
        "operationId": "getTrail",
        "summary": "The hash-chained evidence trail",
        "description": "Every state change as a chained entry: hash = sha256(canonical({index, prevHash, at, event, body})). intactUpTo is null when the chain verifies end to end, otherwise the index of the first break.",
        "tags": [
          "escrows"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Escrow id (esc_…)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Trail entries and chain status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TrailEntry"
                      }
                    },
                    "intactUpTo": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/escrows/{id}/dispute": {
      "post": {
        "operationId": "openDispute",
        "summary": "Open a dispute on a delivered escrow",
        "tags": [
          "disputes"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Escrow id (esc_…)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "agentId"
                ],
                "properties": {
                  "agentId": {
                    "type": "string",
                    "description": "Sandbox agent; must be a party."
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Dispute opened; the decide-by deadline starts. Emits dispute.opened.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Dispute"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "invalid_state: only delivered escrows can be disputed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "unprocessable: opener is not a party.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "getDispute",
        "summary": "Fetch the dispute on an escrow",
        "tags": [
          "disputes"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Escrow id (esc_…)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The dispute.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Dispute"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/escrows/{id}/dispute/evidence": {
      "post": {
        "operationId": "submitEvidence",
        "summary": "Attach content-addressed evidence",
        "tags": [
          "disputes"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Escrow id (esc_…)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "agentId"
                ],
                "properties": {
                  "agentId": {
                    "type": "string"
                  },
                  "note": {
                    "type": "string"
                  },
                  "payload": {}
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Evidence recorded by content hash. Emits dispute.evidence.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Dispute"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "invalid_state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/escrows/{id}/dispute/resolve": {
      "post": {
        "operationId": "resolveDispute",
        "summary": "Submit a ruling: co-signed or arbiter-signed only",
        "description": "Signers sign the canonical JSON of {escrowId, disputeId, outcome, rationale}. Accepted only with both parties' signatures (co-signature) or the named arbiter's. `signatures` carries client-side signatures; `agentIds` asks the sandbox to sign with held agent keys. Anything else is rejected: the platform never rules. Disputes nobody resolves fall to the timeout default.",
        "tags": [
          "disputes"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Escrow id (esc_…)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "outcome"
                ],
                "properties": {
                  "outcome": {
                    "type": "string",
                    "enum": [
                      "release",
                      "refund",
                      "split"
                    ]
                  },
                  "rationale": {
                    "type": "string"
                  },
                  "signatures": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Signature"
                    }
                  },
                  "agentIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ruling applied; the matter closes in a quitanza. Emits dispute.resolved and quitanza.issued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "dispute": {
                      "$ref": "#/components/schemas/Dispute"
                    },
                    "quitanza": {
                      "$ref": "#/components/schemas/Quitanza"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "unauthorized_ruling: signatures are missing, invalid, or not from the required signers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "invalid_state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/escrows/{id}/quitanza": {
      "get": {
        "operationId": "getQuitanzaForEscrow",
        "summary": "The quitanza that closed an escrow",
        "tags": [
          "quitanzas"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Escrow id (esc_…)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The quitanza.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quitanza"
                }
              }
            }
          },
          "404": {
            "description": "not_found: the matter has not closed yet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/quitanzas/{id}": {
      "get": {
        "operationId": "getQuitanza",
        "summary": "Fetch a quitanza by id",
        "tags": [
          "quitanzas"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Quitanza id (qtz_…)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The quitanza.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quitanza"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/quitanzas/{id}/verify": {
      "get": {
        "operationId": "verifyQuitanza",
        "summary": "Verify a quitanza against the live trail",
        "description": "Checks the issuer signature over the canonical body, the integrity of the full evidence trail, and that the quitanza's trailHead matches the trail at its recorded length. The same checks run offline with no Quitanza code. See the quitanza format spec.",
        "tags": [
          "quitanzas"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Quitanza id (qtz_…)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Verification verdicts.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "quitanzaId": {
                      "type": "string"
                    },
                    "signatureValid": {
                      "type": "boolean"
                    },
                    "trailIntact": {
                      "type": "boolean"
                    },
                    "trailHeadMatches": {
                      "type": "boolean"
                    },
                    "issuer": {
                      "type": "string",
                      "pattern": "^[0-9a-f]{64}$"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks": {
      "post": {
        "operationId": "registerWebhook",
        "summary": "Register a webhook endpoint",
        "description": "Every engine event is POSTed to each registered URL as canonical JSON, signed with the announced key: x-quitanza-signature carries the Ed25519 signature, x-quitanza-key-id the signing public key. Failures retry with backoff; exhausted deliveries land on the dead-letter list.",
        "tags": [
          "webhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registered. Pin signingKey to verify deliveries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "url": {
                      "type": "string"
                    },
                    "signingKey": {
                      "type": "string",
                      "pattern": "^[0-9a-f]{64}$"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "bad_request: url is required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listWebhooks",
        "summary": "List registered webhooks and the signing key",
        "tags": [
          "webhooks"
        ],
        "responses": {
          "200": {
            "description": "Registered URLs and the signing public key.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhooks": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "signingKey": {
                      "type": "string",
                      "pattern": "^[0-9a-f]{64}$"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/dead-letters": {
      "get": {
        "operationId": "listDeadLetters",
        "summary": "Deliveries that exhausted every retry",
        "tags": [
          "webhooks"
        ],
        "responses": {
          "200": {
            "description": "Dead letters, oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deadLetters": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookDeadLetter"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/x402/demo": {
      "get": {
        "operationId": "x402Demo",
        "summary": "A paid resource settled through escrow (x402, simulated rails)",
        "description": "Without an X-PAYMENT header: 402 with PaymentRequirements (scheme quitanza-sandbox, network quitanza-local). With a valid signed payment header: runs the full escrow lifecycle for the request and returns the resource plus an X-PAYMENT-RESPONSE header naming the quitanza that proves the matter closed.",
        "tags": [
          "x402"
        ],
        "parameters": [
          {
            "name": "X-PAYMENT",
            "in": "header",
            "required": false,
            "description": "Base64-encoded JSON payment payload built by @quitanza/x402 createPaymentHeader().",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The resource; X-PAYMENT-RESPONSE names the escrow and quitanza.",
            "headers": {
              "X-PAYMENT-RESPONSE": {
                "description": "Base64-encoded JSON: { escrowId, quitanzaId, outcome }.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "402": {
            "description": "Payment required: x402 PaymentRequirements offers.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerApiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "The deployment's QUITANZA_API_KEY. Required on every /v1 route when the deployment sets one; the open sandbox accepts requests without it."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "Every error response carries this envelope.",
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "bad_request",
                  "mandate_violation",
                  "unauthorized_ruling",
                  "not_found",
                  "invalid_state",
                  "unprocessable",
                  "payment_invalid"
                ]
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "KeyRef": {
        "type": "object",
        "required": [
          "publicKey"
        ],
        "properties": {
          "publicKey": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "Raw 32-byte Ed25519 public key, hex."
          },
          "label": {
            "type": "string"
          }
        }
      },
      "Signature": {
        "type": "object",
        "required": [
          "signer",
          "signature"
        ],
        "properties": {
          "signer": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "Ed25519 public key of the signer, hex."
          },
          "signature": {
            "type": "string",
            "description": "Ed25519 signature over the canonical JSON of the signed body, hex."
          }
        }
      },
      "TermCheck": {
        "description": "One acceptance check. Kinds: hash (exact content hash), maxBytes, deadline, shape (required fields), predicate (named server-side predicate).",
        "oneOf": [
          {
            "type": "object",
            "required": [
              "kind",
              "expected"
            ],
            "properties": {
              "kind": {
                "const": "hash"
              },
              "expected": {
                "type": "string",
                "pattern": "^[0-9a-f]{64}$"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "kind",
              "limit"
            ],
            "properties": {
              "kind": {
                "const": "maxBytes"
              },
              "limit": {
                "type": "integer"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "kind",
              "by"
            ],
            "properties": {
              "kind": {
                "const": "deadline"
              },
              "by": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "kind",
              "requiredFields"
            ],
            "properties": {
              "kind": {
                "const": "shape"
              },
              "requiredFields": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          {
            "type": "object",
            "required": [
              "kind",
              "name"
            ],
            "properties": {
              "kind": {
                "const": "predicate"
              },
              "name": {
                "type": "string"
              },
              "params": {
                "type": "object"
              }
            }
          }
        ]
      },
      "Terms": {
        "type": "object",
        "required": [
          "description",
          "checks"
        ],
        "properties": {
          "description": {
            "type": "string"
          },
          "checks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TermCheck"
            }
          }
        }
      },
      "EscrowTimeouts": {
        "type": "object",
        "description": "Per-stage deadlines. Silence always resolves: any stage left past its deadline is resolved by the sweep, ending in a quitanza.",
        "properties": {
          "deliverWithinSeconds": {
            "type": "integer",
            "default": 86400
          },
          "resolveWithinSeconds": {
            "type": "integer",
            "default": 86400
          },
          "decideWithinSeconds": {
            "type": "integer",
            "default": 259200
          },
          "timeoutRuling": {
            "type": "string",
            "enum": [
              "release",
              "refund",
              "split"
            ],
            "default": "refund"
          }
        }
      },
      "Escrow": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "created",
              "funded",
              "delivered",
              "settled",
              "disputed",
              "resolved",
              "refunded"
            ]
          },
          "payer": {
            "$ref": "#/components/schemas/KeyRef"
          },
          "payee": {
            "$ref": "#/components/schemas/KeyRef"
          },
          "arbiter": {
            "$ref": "#/components/schemas/KeyRef"
          },
          "amount": {
            "type": "string",
            "pattern": "^\\d+(\\.\\d{1,6})?$",
            "description": "Decimal amount string, at most 6 fraction digits."
          },
          "asset": {
            "type": "string"
          },
          "terms": {
            "$ref": "#/components/schemas/Terms"
          },
          "timeouts": {
            "$ref": "#/components/schemas/EscrowTimeouts"
          },
          "deadlineAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the current stage expires; absent on terminal states."
          },
          "mandateId": {
            "type": "string"
          },
          "mandateHash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Delivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "escrowId": {
            "type": "string"
          },
          "contentHash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "sha256 of the canonical JSON of the payload."
          },
          "payload": {},
          "submittedAt": {
            "type": "string",
            "format": "date-time"
          },
          "signature": {
            "$ref": "#/components/schemas/Signature"
          }
        }
      },
      "Verdict": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "escrowId": {
            "type": "string"
          },
          "deliveryId": {
            "type": "string"
          },
          "passed": {
            "type": "boolean"
          },
          "results": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "check": {
                  "$ref": "#/components/schemas/TermCheck"
                },
                "passed": {
                  "type": "boolean"
                },
                "detail": {
                  "type": "string"
                }
              }
            }
          },
          "decidedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "DeliveryResult": {
        "type": "object",
        "properties": {
          "delivery": {
            "$ref": "#/components/schemas/Delivery"
          },
          "verdict": {
            "$ref": "#/components/schemas/Verdict"
          },
          "quitanza": {
            "$ref": "#/components/schemas/Quitanza"
          }
        }
      },
      "Dispute": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "escrowId": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "open",
              "under-review",
              "resolved"
            ]
          },
          "openedBy": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "reason": {
            "type": "string"
          },
          "evidence": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "party": {
                  "type": "string",
                  "pattern": "^[0-9a-f]{64}$"
                },
                "contentHash": {
                  "type": "string",
                  "pattern": "^[0-9a-f]{64}$"
                },
                "note": {
                  "type": "string"
                },
                "submittedAt": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "ruling": {
            "type": "object",
            "properties": {
              "outcome": {
                "type": "string",
                "enum": [
                  "release",
                  "refund",
                  "split"
                ]
              },
              "rationale": {
                "type": "string"
              },
              "decidedAt": {
                "type": "string",
                "format": "date-time"
              },
              "decidedBy": {
                "type": "string",
                "enum": [
                  "co-signature",
                  "arbiter",
                  "timeout"
                ],
                "description": "Who carried the ruling. The platform itself never rules."
              }
            }
          },
          "openedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MandateCaps": {
        "type": "object",
        "required": [
          "agent",
          "perEscrowMax",
          "cumulativeMax",
          "allowedAssets",
          "expiresAt"
        ],
        "properties": {
          "agent": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "Agent public key the mandate binds; escrows must be paid from it."
          },
          "perEscrowMax": {
            "type": "string",
            "pattern": "^\\d+(\\.\\d{1,6})?$",
            "description": "Decimal amount string, at most 6 fraction digits."
          },
          "cumulativeMax": {
            "type": "string",
            "pattern": "^\\d+(\\.\\d{1,6})?$",
            "description": "Decimal amount string, at most 6 fraction digits."
          },
          "allowedAssets": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "allowedCounterparties": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            },
            "description": "Payee allowlist. Absent or empty = any counterparty."
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Mandate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "principal": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "caps": {
            "$ref": "#/components/schemas/MandateCaps"
          },
          "signature": {
            "$ref": "#/components/schemas/Signature"
          }
        }
      },
      "TrailEntry": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer"
          },
          "prevHash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "Hash of the previous entry; 64 zeros for genesis."
          },
          "hash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "sha256(canonical({index, prevHash, at, event, body}))."
          },
          "at": {
            "type": "string",
            "format": "date-time"
          },
          "event": {
            "type": "string"
          },
          "body": {}
        }
      },
      "Quitanza": {
        "type": "object",
        "description": "The terminal settlement proof: issuer-signed over the canonical JSON of every field except signature, chained to the evidence trail by trailHead.",
        "properties": {
          "id": {
            "type": "string"
          },
          "escrowId": {
            "type": "string"
          },
          "outcome": {
            "type": "string",
            "enum": [
              "released",
              "refunded",
              "split"
            ]
          },
          "payer": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "payee": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "amount": {
            "type": "string",
            "pattern": "^\\d+(\\.\\d{1,6})?$",
            "description": "Decimal amount string, at most 6 fraction digits."
          },
          "asset": {
            "type": "string"
          },
          "termsHash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "deliveryHash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "verdictId": {
            "type": "string"
          },
          "disputeId": {
            "type": "string"
          },
          "mandateHash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "trailHead": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "trailLength": {
            "type": "integer"
          },
          "issuedAt": {
            "type": "string",
            "format": "date-time"
          },
          "anchor": {
            "type": "object",
            "description": "Present when custody is chain-backed: where the settlement moved funds. Inside the signed body. Local development chains only in this release.",
            "properties": {
              "chain": {
                "type": "string",
                "description": "CAIP-2 chain id, e.g. eip155:31337."
              },
              "contract": {
                "type": "string"
              },
              "escrowRef": {
                "type": "string"
              },
              "txHash": {
                "type": "string"
              }
            }
          },
          "signature": {
            "$ref": "#/components/schemas/Signature"
          }
        }
      },
      "EngineEvent": {
        "type": "object",
        "description": "The webhook payload: signed as canonical JSON, signature in x-quitanza-signature, key id in x-quitanza-key-id.",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "escrow.created",
              "escrow.funded",
              "delivery.submitted",
              "verdict.passed",
              "verdict.failed",
              "dispute.opened",
              "dispute.evidence",
              "dispute.resolved",
              "escrow.refunded",
              "escrow.timeout",
              "quitanza.issued"
            ]
          },
          "escrowId": {
            "type": "string"
          },
          "at": {
            "type": "string",
            "format": "date-time"
          },
          "data": {}
        }
      },
      "WebhookDeadLetter": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "event": {
            "$ref": "#/components/schemas/EngineEvent"
          },
          "attempts": {
            "type": "integer"
          },
          "lastError": {
            "type": "string"
          },
          "firstAttemptAt": {
            "type": "string",
            "format": "date-time"
          },
          "deadAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    }
  }
}