{
  "openapi": "3.1.0",
  "info": {
    "title": "FractalAI — Post-Quantum Proof API",
    "version": "1.0.0",
    "description": "Pay-per-call cryptographic proofs signed with post-quantum ML-DSA-65 / Dilithium-3 (NIST FIPS-204), verifiable offline. USDC micropayments on Base via x402 v2 (gasless EIP-3009).",
    "x-guidance": "POST a JSON body to any /api/x402/* endpoint. An unpaid request returns an x402 v2 402 challenge; pay with USDC on Base (gasless EIP-3009) to receive an offline-verifiable ML-DSA-65 / Dilithium-3 proof. Each paid response echoes `signed_message` — verify with ml_dsa65.verify(signature, signed_message, public_key). Full machine catalog also at /.well-known/x402.json.",
    "contact": {
      "email": "helloinvestor@fractalai.net.co"
    }
  },
  "servers": [
    {
      "url": "https://fractalai.net.co"
    }
  ],
  "paths": {
    "/api/x402/sign": {
      "post": {
        "operationId": "x402Sign",
        "summary": "Post-quantum signature (ML-DSA-65 / Dilithium-3)",
        "tags": [
          "Signature"
        ],
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.020000"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "message": {
                    "type": "string",
                    "minLength": 1
                  },
                  "hash": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "message": "hello agent economy"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Paid: post-quantum proof (ML-DSA-65 / Dilithium-3), offline-verifiable via signed_message."
          },
          "402": {
            "description": "Payment Required — x402 v2 challenge (USDC on Base, gasless EIP-3009)."
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/XAgentAuthorization"
          }
        ]
      }
    },
    "/api/x402/verify-proof": {
      "post": {
        "operationId": "x402VerifyProof",
        "summary": "Verify a PQC signature, return a signed verdict",
        "tags": [
          "Verify"
        ],
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.010000"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "signature": {
                    "type": "string"
                  },
                  "public_key": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  },
                  "message_b64": {
                    "type": "string"
                  },
                  "algorithm": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "algorithm": "ml-dsa-65",
                "message": "hi",
                "signature": "AA==",
                "public_key": "AA=="
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Paid: post-quantum proof (ML-DSA-65 / Dilithium-3), offline-verifiable via signed_message."
          },
          "402": {
            "description": "Payment Required — x402 v2 challenge (USDC on Base, gasless EIP-3009)."
          }
        }
      }
    },
    "/api/x402/verify-agent": {
      "post": {
        "operationId": "x402VerifyAgent",
        "summary": "Know-Your-Agent identity / credential check",
        "tags": [
          "Identity"
        ],
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.030000"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "claim": {
                    "type": "string"
                  },
                  "signature": {
                    "type": "string"
                  },
                  "public_key": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "claim": "did:example:agent",
                "signature": "AA==",
                "public_key": "AA=="
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Paid: post-quantum proof (ML-DSA-65 / Dilithium-3), offline-verifiable via signed_message."
          },
          "402": {
            "description": "Payment Required — x402 v2 challenge (USDC on Base, gasless EIP-3009)."
          }
        }
      }
    },
    "/api/x402/attest-decision": {
      "post": {
        "operationId": "x402AttestDecision",
        "summary": "Notarize an AI decision (Black-Box + on-chain anchor)",
        "tags": [
          "Notary"
        ],
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.050000"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "agent_id": {
                    "type": "string"
                  },
                  "input": {
                    "type": "string"
                  },
                  "output": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "agent_id": "did:example:agent",
                "input": "prompt",
                "output": "response"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Paid: post-quantum proof (ML-DSA-65 / Dilithium-3), offline-verifiable via signed_message."
          },
          "402": {
            "description": "Payment Required — x402 v2 challenge (USDC on Base, gasless EIP-3009)."
          }
        }
      }
    },
    "/api/x402/seal": {
      "post": {
        "operationId": "x402Seal",
        "summary": "Timestamped provenance seal (C2PA / drand)",
        "tags": [
          "Provenance"
        ],
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.050000"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "content": {
                    "type": "string"
                  },
                  "content_hash": {
                    "type": "string",
                    "pattern": "^[0-9a-f]{64}$"
                  }
                }
              },
              "example": {
                "content_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Paid: post-quantum proof (ML-DSA-65 / Dilithium-3), offline-verifiable via signed_message."
          },
          "402": {
            "description": "Payment Required — x402 v2 challenge (USDC on Base, gasless EIP-3009)."
          }
        }
      }
    },
    "/api/x402/seal-cbom": {
      "post": {
        "operationId": "x402SealCbom",
        "summary": "Seal a CBOM (ECMA-424 crypto inventory)",
        "tags": [
          "CBOM"
        ],
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.050000"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "cbom": {
                    "type": "object"
                  }
                }
              },
              "example": {
                "cbom": {
                  "bomFormat": "CycloneDX",
                  "specVersion": "1.6"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Paid: post-quantum proof (ML-DSA-65 / Dilithium-3), offline-verifiable via signed_message."
          },
          "402": {
            "description": "Payment Required — x402 v2 challenge (USDC on Base, gasless EIP-3009)."
          }
        }
      }
    },
    "/api/x402/attest-training": {
      "post": {
        "operationId": "x402AttestTraining",
        "summary": "Training / dataset provenance (C2PA)",
        "tags": [
          "Provenance"
        ],
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.050000"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "model_id": {
                    "type": "string"
                  },
                  "dataset": {
                    "type": "string"
                  },
                  "dataset_hash": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "model_id": "model-1",
                "dataset": "training data"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Paid: post-quantum proof (ML-DSA-65 / Dilithium-3), offline-verifiable via signed_message."
          },
          "402": {
            "description": "Payment Required — x402 v2 challenge (USDC on Base, gasless EIP-3009)."
          }
        }
      }
    },
    "/api/x402/witness": {
      "post": {
        "operationId": "x402Witness",
        "summary": "Independent ML-DSA-65 notary attestation of an x402 settlement (on-chain re-derived)",
        "tags": [
          "Notary"
        ],
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.005000"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/XAgentAuthorization"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "seal_body"
                ],
                "properties": {
                  "seal_body": {
                    "type": "object",
                    "description": "Unsigned settlement-seal body (schema, resource, scheme, network, asset, payTo, amount, payer, transaction, success, response_sha256, sealed_at) — see @fractalai/x402-pqc-witness buildSealBody(). If `transaction` is present the transfer is re-derived from Base RPC and the seal is refused on any mismatch."
                  }
                }
              },
              "example": {
                "seal_body": {
                  "resource": "https://example.com/api/x402/thing",
                  "network": "eip155:8453",
                  "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
                  "payTo": "0x…",
                  "amount": "20000",
                  "payer": "0x…",
                  "transaction": "0x…"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Paid: independently FractalAI-signed ML-DSA-65 attestation of the settlement (offline-verifiable via signed_message)."
          },
          "402": {
            "description": "Payment Required — x402 v2 challenge (USDC on Base, gasless EIP-3009)."
          },
          "422": {
            "description": "Uncheckable or mismatching claim (network not Base mainnet, transfer not found, asset/payTo/amount differ on-chain)."
          }
        }
      }
    },
    "/api/passport/verify": {
      "get": {
        "operationId": "passportV2Spec",
        "summary": "Machine-readable specification of FractalAI Passport v2 (+ a live example token)",
        "tags": [
          "Identity"
        ],
        "responses": {
          "200": {
            "description": "JSON spec: format, signing domain, algorithm anchoring, header/payload fields, verification rules, endpoint contract and a fresh example minted from a published test key."
          }
        }
      },
      "post": {
        "operationId": "passportV2Verify",
        "summary": "Verify a FractalAI Passport v2 (ML-DSA-65 identity + attenuated org authority + agent delegation)",
        "description": "Free, public, 30 req/min per IP, body ≤ 256 KB. Verifies the ML-DSA-65 signature (alg anchored, never inferred), exact header/claims, aud+nonce binding, the 300 s lifetime cap, and — if the token carries an org credential chain — every link, attenuation, proof-of-possession and the org's on-chain status/revocation (FAIL-CLOSED: an unreachable node denies; allowUnregistered is rejected if a client sends it). Never echoes the token or chain.",
        "tags": [
          "Identity"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PassportV2VerifyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Verification verdict (valid may be false — read `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PassportV2VerifyResponse"
                }
              }
            }
          },
          "400": {
            "description": "Malformed body, or a client tried to send allowUnregistered."
          },
          "413": {
            "description": "Body over 256 KB (enforced against the byte stream)."
          },
          "429": {
            "description": "Rate limited (30/min per IP)."
          }
        }
      }
    },
    "/api/agents/registry": {
      "get": {
        "operationId": "agentsRegistryList",
        "summary": "Fractal Agent Commons: list registered agents (public, read-only)",
        "tags": [
          "Agent Registry"
        ],
        "description": "Agents whose latest fractalai-agent-manifest/1 is CURRENT by default: key equals the agent's current key on the node (fractal_agentPassport), fractal_verifyPassport valid, FRC-55 status Active, not expired. Prices are never listed; the binding price is each resource's live 402.",
        "parameters": [
          {
            "name": "capability",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Capability id or exact x402 resource URL."
          },
          {
            "name": "asset",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Asset symbol (USDC, COPM, ...)."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "current",
                "all",
                "Active",
                "Paused",
                "Destroyed",
                "Upgrading",
                "unknown"
              ],
              "default": "current"
            }
          },
          {
            "name": "region",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[A-Z]{2}$"
            },
            "description": "ISO 3166-1 alpha-2 (uppercase, must be an assigned code of the frozen list lib/agent-commons/iso-lists.json): only agents whose manifest.regions[] declares that country. Unlisted/ill-formed → 400 region_invalid."
          }
        ],
        "responses": {
          "200": {
            "description": "{ schema, filters, count, agents[] { id, name, did, manifest_url, manifest_id, version, kid, status, key_status, current, reasons[], capabilities[], regions[] (ISO 3166-1 codes) } }"
          },
          "400": {
            "description": "bad_status_filter | region_invalid"
          },
          "503": {
            "description": "Registry or node unavailable (fail-closed; nothing is listed as current without the node)."
          }
        }
      }
    },
    "/api/agents/registry/{id}": {
      "get": {
        "operationId": "agentsRegistryCurrent",
        "summary": "Current (vigente) signed manifest of one agent",
        "tags": [
          "Agent Registry"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "FRC-55 agent id (0x + 64 hex).",
            "schema": {
              "type": "string",
              "pattern": "^0x[0-9a-fA-F]{64}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ manifest (fractalai-agent-manifest/1, ML-DSA-65 signed by the agent), verification { key_status, agent_status, passport_valid, verified_with }, links }"
          },
          "404": {
            "description": "agent_not_registered, or no_current_manifest with reasons (key_rotated, agent_paused, expired, ...)."
          },
          "503": {
            "description": "Registry or node unavailable (fail-closed; nothing is listed as current without the node)."
          }
        }
      }
    },
    "/api/agents/registry/{id}/v/{n}": {
      "get": {
        "operationId": "agentsRegistryVersion",
        "summary": "One immutable manifest version, re-verified against the key frozen at publish time",
        "tags": [
          "Agent Registry"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "FRC-55 agent id (0x + 64 hex).",
            "schema": {
              "type": "string",
              "pattern": "^0x[0-9a-fA-F]{64}$"
            }
          },
          {
            "name": "n",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ manifest, verification { signature_ok, verified_with { kid, pub, identity_root, height }, key_status: current|rotated|revoked|unknown, agent_status, passport_valid, node_checked_at, ... }, immutable: true }. The manifest is immutable; verification.* is recomputed against the node on EVERY read (30 s node cache; Cache-Control max-age=30, never longer) — a rotation or revocation shows here as soon as the node reports it."
          },
          "404": {
            "description": "version_not_found"
          },
          "500": {
            "description": "stored_manifest_integrity_failed (never served as intact)."
          }
        }
      }
    },
    "/api/agents/registry/{id}/history": {
      "get": {
        "operationId": "agentsRegistryHistory",
        "summary": "Append-only version history with per-version key_status and chain check",
        "tags": [
          "Agent Registry"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "FRC-55 agent id (0x + 64 hex).",
            "schema": {
              "type": "string",
              "pattern": "^0x[0-9a-fA-F]{64}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ agent_id, agent_status, chain_ok, count, versions[] }"
          },
          "404": {
            "description": "agent_not_registered"
          }
        }
      }
    },
    "/api/agents/registry/{id}/passport": {
      "get": {
        "operationId": "agentsRegistryPassport",
        "summary": "Node-served Verifiable Agent Passport proof + node verdict",
        "tags": [
          "Agent Registry"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "FRC-55 agent id (0x + 64 hex).",
            "schema": {
              "type": "string",
              "pattern": "^0x[0-9a-fA-F]{64}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ agent_id, registered, agent_status, current_key { alg, kid, pub }, registry { identity_root, height, signer_kid }, valid, reasons[], continuity, first_binding, verdict (fractal_verifyPassport), proof (fractal_agentPassport, verbatim) }. valid = node verdict AND the registry's local checks (pinned fresh root, Merkle inclusion, node-signed agent_binding when served, continuity with the binding the registry holds) AND, for an id the registry holds NO binding for (registered:false or a prior without canonical_key), the first-binding evidence rule (R4-1: valid node-signed agent_binding, or the operator's trusted channel, or an independent corroborating channel) — otherwise valid:false with reason binding_unverified and first_binding { ok:false, reason, channels: '<n> channel(s): <base domains>' }. The served key is shown, never endorsed. Verifiers (MCP list/get/verify) treat an identity as current only when registered:true or continuity.ok:true. Only ML-DSA-65 is admitted: when the node serves a key under another algorithm or size (e.g. alg MlDsa44 / a 1312-byte key) the answer is 200 with valid:false, reasons:[\"key_algorithm_not_allowed\"], current_key { alg, kid:null, pub, key_bytes, allowed:false } and no verdict/proof endorsement."
          },
          "404": {
            "description": "agent has no PQC signing key bound on the node"
          },
          "503": {
            "description": "node_unavailable"
          }
        }
      }
    },
    "/api/agents/registry/{id}/agent-card.json": {
      "get": {
        "operationId": "agentsRegistryAgentCard",
        "summary": "A2A Agent Card (v0.3 shape) for one agent, derived from its current manifest",
        "tags": [
          "Agent Registry"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "FRC-55 agent id (0x + 64 hex).",
            "schema": {
              "type": "string",
              "pattern": "^0x[0-9a-fA-F]{64}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A2A AgentCard with x-fractalai { manifest_url, manifest_id, kid, key_status, agents_registry }."
          },
          "404": {
            "description": "agent_not_registered or no_current_manifest"
          }
        }
      }
    },
    "/api/agents/registry/{id}/manifest": {
      "post": {
        "operationId": "agentsRegistryPublish",
        "summary": "Publish a signed fractalai-agent-manifest/1 (authority = the agent's ML-DSA-65 signature)",
        "tags": [
          "Agent Registry"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "FRC-55 agent id (0x + 64 hex).",
            "schema": {
              "type": "string",
              "pattern": "^0x[0-9a-fA-F]{64}$"
            }
          }
        ],
        "description": "No admin token. Verified in order, fail-closed, signature BEFORE storage: (1) strict schema (unknown keys rejected, alg anchored to ML-DSA-65, lifetime <= 30 d); (2) signature against the agent's CURRENT key served by the node (fractal_agentPassport.current_pubkey, never the manifest's own key); (3) fractal_verifyPassport valid; (4) fractal_getAgent status Active; (5) every capabilities[].x402.resource exists in /.well-known/x402.json; (6) version == last+1 and prev_manifest_id chains. Limits: body <= 64 KB, 10/min per IP, 6/h per agent, 12 versions/day per agent.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentManifestV1"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "{ published: true, agent_id, version, manifest_id, prev_manifest_id, verified_with { kid, identity_root, height }, urls }"
          },
          "400": {
            "description": "invalid_manifest (strict schema; control characters; leading BOM; duplicate JSON key at any depth — the body is checked as raw text before JSON.parse) / expired / issued_in_future / agent_id_mismatch"
          },
          "403": {
            "description": "key_algorithm_not_allowed (the node serves a signing key that is not ML-DSA-65 — alg tag other than ml-dsa-65 or a key that is not 1952 bytes, e.g. ML-DSA-44; only ML-DSA-65 is admitted, founder decision 2026-10-12; on read the same agent is never current, reason key_algorithm_not_allowed) / key_not_current / signature_invalid / no_passport / passport_invalid / root_signature_untrusted / agent_not_active / status_mismatch / unknown_x402_resource / owner_mismatch / name_mismatch"
          },
          "409": {
            "description": "already_published / version_conflict / prev_manifest_mismatch (checked against the store before any node call)"
          },
          "413": {
            "description": "body exceeds 64 KB"
          },
          "422": {
            "description": "registry_mismatch (registry.identity_root/signer_kid/height differ from the verified passport) / origin_not_pinned (keys.jwks, keys.receipts_directory, endpoints.http/rpc/verify must be the official https://fractalai.net.co / https://api.fractalai.net.co locators) / region_rail_unknown (regions[].rails id not in the commerce-intent rail registry) / region_rail_not_implemented (rail declared but without a tested settlement adapter) / region_rail_currency_mismatch (a region's currencies must include the rail's currency or USD). regions[] itself (optional): country = assigned ISO 3166-1 alpha-2, currencies[] = current ISO 4217 alpha-3, both against the frozen list lib/agent-commons/iso-lists.json, no duplicates — violations are 400 invalid_manifest."
          },
          "429": {
            "description": "rate_limited / agent_rate_limited / daily_version_limit"
          },
          "503": {
            "description": "Registry or node unavailable (node_unavailable / node_error / agent_status_unknown / agent_identity_unknown when fractal_getAgent lacks a non-empty owner or name / no_trusted_root_signer / freshness_floor_unavailable) — fail-closed; nothing is listed as current without the node. binding_channel_untrusted: a FIRST binding (no prior row, or a prior row without canonical_key — R4-2) arrived over an untrusted channel with no valid node-signed agent_binding and no independent corroborating channel; detail names only the number of channels tried and their base domains, never URLs (R4-3). DEUDA R4-4: channel independence is decided by host base domain (no PSL, no DNS resolution): wildcard-DNS names (sslip.io, nip.io) or a proxy on another eTLD+1 pointing at the same node count as independent — FRACTAL_RPC_CORROBORATION_URLS is operator-controlled and must only name channels really run by someone else; the node's signed agent_binding makes corroboration unnecessary."
          }
        }
      },
      "get": {
        "operationId": "agentsRegistryPublishSpec",
        "summary": "Publication rules and limits (machine-readable)",
        "tags": [
          "Agent Registry"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "FRC-55 agent id (0x + 64 hex).",
            "schema": {
              "type": "string",
              "pattern": "^0x[0-9a-fA-F]{64}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "rules[], limits"
          }
        }
      }
    },
    "/api/agents/registry/{id}/rebind": {
      "post": {
        "operationId": "agentsRegistryRebind",
        "summary": "Operator re-binding of an agent_id to the identity the node serves now (append-only event; enrolled-admin ML-DSA-65 signature)",
        "tags": [
          "Agent Registry"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "FRC-55 agent id (0x + 64 lowercase hex; the path must be exact).",
            "schema": {
              "type": "string",
              "pattern": "^0x[0-9a-f]{64}$"
            }
          }
        ],
        "description": "Recovery path for a poisoned or stale TOFU binding (R3-1b). Headers x-admin-pubkey / x-admin-sig / x-admin-nonce: ML-DSA-65 signature by an ENROLLED admin key over \"fractalai-admin-action-v2|registry-rebind:<agent_id>:<prev_canonical|none>:<canonical_key>:<rotation_count>:<sha256hex(trim(reason))>|<floor(unix/3600)>|<nonce>\" (R4-6: the signature covers the previous and the new identity, the rotation_count and the reason — a captured request cannot be replayed with another reason/prior/rotation). Single-use nonce burned in Redis (replay → 409 admin_nonce_replayed; store down → 503 replay_store_unavailable). The cron secret is never accepted. The node passport must verify locally exactly as for a publication (pinned fresh root, Merkle inclusion, agent_binding when served, ML-DSA-65 only) and canonical_key must equal what the node serves; prev_canonical_key must equal the effective previous binding and rotation_count what the node serves. Not CORS-exposed.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "canonical_key",
                  "rotation_count",
                  "reason"
                ],
                "properties": {
                  "canonical_key": {
                    "type": "string",
                    "pattern": "^(0x)?[0-9a-fA-F]{64}$",
                    "description": "The canonical_key the node serves for the agent now."
                  },
                  "prev_canonical_key": {
                    "type": "string",
                    "description": "The effective previous binding (64 hex), or \"none\" / omitted when there is none. Covered by the signature."
                  },
                  "rotation_count": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "The rotation_count the node serves for the identity being bound. Covered by the signature."
                  },
                  "reason": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 500,
                    "description": "Operator reason (no control characters). sha256 of its trimmed form is covered by the signature; /history publishes exactly it."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "{ rebound: true, agent_id, event { seq, after_version, prev_canonical_key, new_canonical_key, new_rotation_count, identity_root, height, admin_kid, nonce, reason, created_at }, urls, note }"
          },
          "400": {
            "description": "bad_agent_id / bad_path / invalid_json / bad_canonical_key / bad_prev_canonical_key / bad_rotation_count / bad_reason / admin_nonce_required"
          },
          "401": {
            "description": "missing or invalid admin signature (invalid_admin_signature: any of agent, prev/new canonical, rotation_count or reason differs from what was signed)"
          },
          "403": {
            "description": "not_enrolled_admin / cron_not_allowed / no_passport / key_algorithm_not_allowed / root_signature_untrusted / root_signature_stale / passport_inclusion_invalid / agent_binding_invalid / passport_invalid"
          },
          "404": {
            "description": "agent_not_registered (a first binding is a publication)"
          },
          "409": {
            "description": "admin_nonce_replayed / canonical_key_not_served / prev_canonical_mismatch / rotation_count_not_served / binding_unchanged / rebind_conflict"
          },
          "503": {
            "description": "replay_store_unavailable / node_unavailable / node_error / no_trusted_root_signer / registry_store_unavailable"
          }
        }
      }
    },
    "/.well-known/agents.json": {
      "get": {
        "operationId": "agentsIndex",
        "summary": "Signed index of CURRENT agents (fractalai-agents-index/1)",
        "tags": [
          "Agent Registry"
        ],
        "description": "ML-DSA-65 signature by the FractalAI notary key (the x402-receipt-notary entry of /.well-known/jwks.json, same kid) over \"fractalai-agents-index/1\\n\" + sha256(JCS(index without signature)). Only agents whose latest manifest is current are listed. `scope` is INSIDE the signed canon: null for the global index; `{ region }` for the regional index requested with ?region=XX (only current agents whose manifest.regions[] declares that ISO 3166-1 country). A verifier that asked for a region must check scope.region equals it — a global index is never a fallback for a regional request (MCP list_agents reports index_scope_mismatch).",
        "parameters": [
          {
            "name": "region",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[A-Z]{2}$"
            },
            "description": "Optional ISO 3166-1 alpha-2 (uppercase, assigned code of the frozen list). Returns a SEPARATELY signed regional index with scope { region }. A valid region with no agents is a signed empty index (200). Ill-formed or unlisted → 400 region_invalid."
          }
        ],
        "responses": {
          "200": {
            "description": "{ schema, issued_at, expires_at, seq, registry { identity_root, height, signer_kid }, scope: null | { region }, agents[] { id, name, did, manifest_url, manifest_id, version, kid, status }, signature { alg: ML-DSA-65, kid, sig } }"
          },
          "400": {
            "description": "region_invalid — region is not one assigned ISO 3166-1 alpha-2 code (uppercase) of the frozen list."
          },
          "503": {
            "description": "index_unavailable"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "AgentAuthorization": {
        "type": "object",
        "description": "Self-contained post-quantum agent capability (lib/x402-agent-auth.ts + lib/agent-mandate.ts). An ENROLLED OPERATOR signs a mandate binding the agent's own ML-DSA-65 key to spend caps + a recipient allowlist + an expiry; the AGENT signs a per-intent proof over the EXACT payment. Both are verified and bound to THIS x402 payment (recipient == FractalAI treasury, currency USDC, chain base, amount == the resource price), the mandate policy is enforced (perTxMax, allowedRecipients, and dailyMax/totalMax against the shared per-agent spend ledger), and the intent nonce is burned AFTER settlement (single use). The verified capability is embedded in the ML-DSA-65-signed x402 receipt. Additive and fail-closed: header absent → plain x402; header present but invalid → request rejected (400/401/403/409/503).",
        "required": [
          "operatorPubkey",
          "operatorSignature",
          "mandate",
          "intent",
          "intentSignature"
        ],
        "properties": {
          "operatorPubkey": {
            "type": "string",
            "pattern": "^(0x)?[0-9a-fA-F]{3904}$",
            "description": "Enrolled operator ML-DSA-65 public key (hex, 1952 bytes). Must be enrolled (ADMIN_PUBKEYS / on-chain admin registry)."
          },
          "operatorSignature": {
            "type": "string",
            "pattern": "^(0x)?[0-9a-fA-F]{6618}$",
            "description": "Operator ML-DSA-65 signature (hex, 3309 bytes) over the canonical mandate message: \"fractalai-agent-mandate-v1\" + agent, perTxMax, dailyMax, totalMax, recipients (lowercased, sorted), agentPubkey, expiry."
          },
          "mandate": {
            "type": "object",
            "required": [
              "agentId",
              "perTxMax",
              "dailyMax",
              "totalMax",
              "allowedRecipients",
              "agentPubkey",
              "expiry"
            ],
            "properties": {
              "agentId": {
                "type": "string",
                "description": "Agent identifier; must equal intent.agentId."
              },
              "perTxMax": {
                "type": "number",
                "description": "Max USDC per intent (intent.amount must be ≤ this)."
              },
              "dailyMax": {
                "type": "number",
                "description": "Rolling-24h USDC cap, enforced against the shared per-agent spend ledger."
              },
              "totalMax": {
                "type": "number",
                "description": "Lifetime USDC cap, enforced against the shared per-agent spend ledger."
              },
              "allowedRecipients": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Recipient allowlist (EVM addresses, case-insensitive). Must contain the FractalAI treasury for this API."
              },
              "agentPubkey": {
                "type": "string",
                "pattern": "^(0x)?[0-9a-fA-F]{3904}$",
                "description": "The agent's ML-DSA-65 public key (hex). This key must produce intentSignature."
              },
              "expiry": {
                "type": "integer",
                "description": "Unix seconds; mandate must not be expired."
              }
            }
          },
          "intent": {
            "type": "object",
            "required": [
              "agentId",
              "amount",
              "recipientAddress",
              "recipientChain",
              "currency",
              "nonce",
              "expiry"
            ],
            "properties": {
              "agentId": {
                "type": "string"
              },
              "amount": {
                "type": "number",
                "description": "USDC amount; must equal the resource price exactly (e.g. 0.02 for /api/x402/sign, 0.005 for /api/x402/witness)."
              },
              "recipientAddress": {
                "type": "string",
                "description": "Must be the FractalAI treasury 0xC13789e82661635d9Cea38a53A0390CF9939ef4f."
              },
              "recipientChain": {
                "type": "string",
                "enum": [
                  "base"
                ]
              },
              "currency": {
                "type": "string",
                "enum": [
                  "USDC"
                ]
              },
              "nonce": {
                "type": "string",
                "description": "Single-use; burned after the payment settles (replay → 409 intent_nonce_replayed)."
              },
              "expiry": {
                "type": "integer",
                "description": "Unix seconds; must be in the future and at most 600 s ahead."
              }
            }
          },
          "intentSignature": {
            "type": "string",
            "pattern": "^(0x)?[0-9a-fA-F]{6618}$",
            "description": "Agent ML-DSA-65 signature (hex) over the canonical intent message \"fractalai-agent-pay-v1|…\" (lib/agent-mandate.intentMessage)."
          }
        }
      },
      "PassportV2VerifyRequest": {
        "type": "object",
        "required": [
          "token",
          "aud",
          "nonce"
        ],
        "properties": {
          "token": {
            "type": "string",
            "description": "FractalAI Passport v2: b64url(header).b64url(payload).b64url(ML-DSA-65 signature). Spec: GET /api/passport/verify."
          },
          "aud": {
            "type": "string",
            "maxLength": 256,
            "description": "The relying party's own identifier — compared against payload.aud."
          },
          "nonce": {
            "type": "string",
            "maxLength": 256,
            "description": "The relying party's single-use nonce — compared against payload.nonce."
          },
          "presentation": {
            "type": "object",
            "properties": {
              "requiredScope": {
                "type": "string",
                "description": "Scope the RP needs; answered from CHAIN-BACKED scopes only (self-asserted scopes never grant)."
              },
              "action": {
                "type": "object",
                "description": "FRC-55 signed agent action (AgentActionRecord: agentId, callId, output, tokensUsed, gasUsed, confidence, timestamp, signature) to bind to the passport's chain; requires requiredScope + popSignature."
              },
              "popSignature": {
                "type": "string",
                "description": "Hex ML-DSA-65 PoP by the passport subject over agentAuthzChallenge(aud, nonce) (lib/agent-authz)."
              },
              "cnfSignature": {
                "type": "string",
                "description": "Hex ML-DSA-65 holder-of-key proof by the cnf agent over \"fractalai-passport-v2-cnf|kid|aud|nonce\"."
              }
            }
          }
        },
        "additionalProperties": false
      },
      "PassportV2VerifyResponse": {
        "type": "object",
        "required": [
          "valid",
          "subject",
          "kid",
          "scopes",
          "chain_depth",
          "org_status",
          "errors"
        ],
        "properties": {
          "valid": {
            "type": "boolean",
            "description": "true iff the passport verifies AND every requested presentation check passed."
          },
          "subject": {
            "type": [
              "string",
              "null"
            ],
            "description": "Normalized ML-DSA-65 public key hex of the holder (null when invalid)."
          },
          "kid": {
            "type": [
              "string",
              "null"
            ]
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "EFFECTIVE scopes: attenuated, chain-backed. [] without a chain."
          },
          "chain_depth": {
            "type": "integer"
          },
          "org_status": {
            "type": "string",
            "enum": [
              "none",
              "active",
              "unanchored",
              "denied"
            ],
            "description": "none = identity-only token; active = org registered ACTIVE on-chain; denied = revoked/unknown (fail-closed). \"unanchored\" is never produced by this public endpoint (crypto-only mode is not a client option)."
          },
          "org_name": {
            "type": "string"
          },
          "cnf": {
            "type": "string",
            "description": "Delegated agent public key, only when the chain authorizes delegation."
          },
          "scope_granted": {
            "type": "boolean"
          },
          "holder_of_key": {
            "type": "boolean"
          },
          "action_authorized": {
            "type": "boolean"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "AgentManifestV1": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schema",
          "manifest_id",
          "version",
          "prev_manifest_id",
          "issued_at",
          "expires_at",
          "agent",
          "keys",
          "registry",
          "capabilities",
          "operations",
          "endpoints",
          "signature"
        ],
        "properties": {
          "schema": {
            "const": "fractalai-agent-manifest/1"
          },
          "manifest_id": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "sha256 hex of JCS(manifest without signature and manifest_id)"
          },
          "version": {
            "type": "integer",
            "minimum": 1,
            "description": "Monotonic per agent: exactly last + 1."
          },
          "prev_manifest_id": {
            "oneOf": [
              {
                "type": "string",
                "pattern": "^[0-9a-f]{64}$"
              },
              {
                "type": "null"
              }
            ],
            "description": "manifest_id of the previous version (null only for version 1)."
          },
          "issued_at": {
            "type": "integer",
            "description": "unix seconds"
          },
          "expires_at": {
            "type": "integer",
            "description": "unix seconds; expires_at - issued_at <= 30 days"
          },
          "agent": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "did",
              "name",
              "owner",
              "status",
              "passport_url"
            ],
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^0x[0-9a-f]{64}$"
              },
              "did": {
                "type": "string",
                "description": "did:web:fractalai.net.co:agents:<id>"
              },
              "name": {
                "type": "string"
              },
              "owner": {
                "type": "string",
                "pattern": "^fractal1_pq_[0-9a-f]{40}$"
              },
              "status": {
                "type": "string",
                "enum": [
                  "Active",
                  "Paused"
                ]
              },
              "passport_url": {
                "type": "string",
                "format": "uri"
              }
            }
          },
          "keys": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "signing",
              "receipts_directory",
              "jwks"
            ],
            "properties": {
              "signing": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "alg",
                  "kid",
                  "pub"
                ],
                "properties": {
                  "alg": {
                    "const": "ML-DSA-65"
                  },
                  "kid": {
                    "type": "string",
                    "pattern": "^[0-9a-f]{16}$",
                    "description": "sha256(pub bytes)[0:16]"
                  },
                  "pub": {
                    "type": "string",
                    "description": "base64url (no padding) ML-DSA-65 public key, 1952 bytes = the agent's current key on the node"
                  }
                }
              },
              "receipts_directory": {
                "type": "string",
                "format": "uri"
              },
              "jwks": {
                "type": "string",
                "format": "uri"
              }
            }
          },
          "registry": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "identity_root",
              "height",
              "signer_kid"
            ],
            "properties": {
              "identity_root": {
                "type": "string",
                "pattern": "^0x[0-9a-f]{64}$"
              },
              "height": {
                "type": "integer",
                "minimum": 0
              },
              "signer_kid": {
                "type": "string",
                "pattern": "^[0-9a-f]{16}$"
              }
            }
          },
          "capabilities": {
            "type": "array",
            "minItems": 1,
            "maxItems": 64,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "id",
                "description",
                "x402",
                "sandbox"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "pattern": "^[a-z0-9][a-z0-9-]{0,63}$"
                },
                "description": {
                  "type": "string",
                  "maxLength": 2048
                },
                "x402": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "resource"
                  ],
                  "properties": {
                    "resource": {
                      "type": "string",
                      "format": "uri",
                      "description": "Exact URL from /.well-known/x402.json (price never copied: the live 402 binds)."
                    },
                    "accepts_hint": {
                      "type": "string",
                      "example": "USDC/eip155:8453"
                    }
                  }
                },
                "input_schema": {
                  "type": "object"
                },
                "output_schema": {
                  "type": "object"
                },
                "limits": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "perTxMaxAtomic",
                    "dailyMaxAtomic",
                    "asset",
                    "decimals"
                  ],
                  "properties": {
                    "perTxMaxAtomic": {
                      "type": "string",
                      "pattern": "^(0|[1-9][0-9]*)$"
                    },
                    "dailyMaxAtomic": {
                      "type": "string",
                      "pattern": "^(0|[1-9][0-9]*)$"
                    },
                    "asset": {
                      "type": "string"
                    },
                    "decimals": {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 36
                    }
                  }
                },
                "sandbox": {
                  "type": "boolean"
                }
              }
            }
          },
          "operations": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "intent_schema",
              "receipt",
              "authorization"
            ],
            "properties": {
              "intent_schema": {
                "const": "commerce-intent/1"
              },
              "receipt": {
                "const": "delivery-receipt v0.2.1"
              },
              "authorization": {
                "const": "X-Agent-Authorization v2"
              }
            }
          },
          "endpoints": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "http",
              "rpc",
              "mcp",
              "verify"
            ],
            "properties": {
              "http": {
                "type": "string",
                "format": "uri"
              },
              "rpc": {
                "type": "string",
                "format": "uri"
              },
              "mcp": {
                "type": "string"
              },
              "verify": {
                "type": "string"
              }
            }
          },
          "regions": {
            "type": "array",
            "minItems": 1,
            "maxItems": 64,
            "description": "OPTIONAL (omit for no regional claim; never empty). Markets the agent serves. country: assigned ISO 3166-1 alpha-2; currencies[]: current ISO 4217 alpha-3 — both validated against the FROZEN list lib/agent-commons/iso-lists.json (generated once by scripts/gen-iso-lists.mjs); duplicate country or currency → 400. rails[]: ids of the commerce-intent rail registry (e.g. usdc-base, wcop-base, copm-polygon): unknown → 422 region_rail_unknown; no tested adapter → 422 region_rail_not_implemented; each region's currencies must include the rail's currency or USD → else 422 region_rail_currency_mismatch. Regional index: GET /.well-known/agents.json?region=CO.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "country",
                "currencies",
                "rails"
              ],
              "properties": {
                "country": {
                  "type": "string",
                  "pattern": "^[A-Z]{2}$"
                },
                "currencies": {
                  "type": "array",
                  "minItems": 1,
                  "maxItems": 32,
                  "items": {
                    "type": "string",
                    "pattern": "^[A-Z]{3}$"
                  }
                },
                "rails": {
                  "type": "array",
                  "minItems": 1,
                  "maxItems": 32,
                  "items": {
                    "type": "string",
                    "pattern": "^[a-z0-9][a-z0-9-]{0,63}$"
                  }
                }
              }
            }
          },
          "signature": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "alg",
              "kid",
              "sig"
            ],
            "properties": {
              "alg": {
                "const": "ML-DSA-65"
              },
              "kid": {
                "type": "string",
                "pattern": "^[0-9a-f]{16}$"
              },
              "sig": {
                "type": "string",
                "description": "base64url (no padding) ML-DSA-65 signature (3309 bytes) over utf8(\"fractalai-agent-manifest/1\\n\" + manifest_id)"
              }
            }
          }
        }
      }
    },
    "parameters": {
      "XAgentAuthorization": {
        "name": "X-Agent-Authorization",
        "in": "header",
        "required": false,
        "description": "OPTIONAL post-quantum agent capability proof: base64 of the JSON object described by AgentAuthorization (operator ML-DSA-65 mandate + agent ML-DSA-65 per-intent signature). When present it is verified, bound to THIS payment, policy-enforced and recorded in the signed receipt; when invalid the call is rejected (fails closed). Absent → the endpoint behaves as plain x402.",
        "schema": {
          "type": "string",
          "contentEncoding": "base64",
          "contentMediaType": "application/json",
          "contentSchema": {
            "$ref": "#/components/schemas/AgentAuthorization"
          }
        },
        "example": "eyJvcGVyYXRvclB1YmtleSI6IjxoZXg+Iiwib3BlcmF0b3JTaWduYXR1cmUiOiI8aGV4PiIsIm1hbmRhdGUiOnsiYWdlbnRJZCI6ImFnZW50LTEiLCJwZXJUeE1heCI6MSwiZGFpbHlNYXgiOjUsInRvdGFsTWF4Ijo1MCwiYWxsb3dlZFJlY2lwaWVudHMiOlsiMHhDMTM3ODllODI2NjE2MzVkOUNlYTM4YTUzQTAzOTBDRjk5MzllZjRmIl0sImFnZW50UHVia2V5IjoiPGhleD4iLCJleHBpcnkiOjE3OTAwMDAwMDB9LCJpbnRlbnQiOnsiYWdlbnRJZCI6ImFnZW50LTEiLCJhbW91bnQiOjAuMDIsInJlY2lwaWVudEFkZHJlc3MiOiIweEMxMzc4OWU4MjY2MTYzNWQ5Q2VhMzhhNTNBMDM5MENGOTkzOWVmNGYiLCJyZWNpcGllbnRDaGFpbiI6ImJhc2UiLCJjdXJyZW5jeSI6IlVTREMiLCJub25jZSI6Im4tMSIsImV4cGlyeSI6MTc5MDAwMDAwMH0sImludGVudFNpZ25hdHVyZSI6IjxoZXg+In0="
      }
    }
  }
}
