{
  "openapi": "3.1.1",
  "info": {
    "title": "Circles Pathfinder API",
    "description": "REST API for computing transitive transfer paths through the Circles trust network using Google OR-Tools.",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://rpc.prod3.aboutcircles.com/pathfinder"
    }
  ],
  "paths": {
    "/findMaxFlow": {
      "get": {
        "tags": [
          "Pathfinding"
        ],
        "summary": "Compute max transferable flow (GET)",
        "description": "Computes the maximum transferable flow between two addresses WITHOUT computing the full transfer path. Faster than /findPath when you only need to know the amount.\n\n**Required params**: `from` (sender address), `to` (receiver address), `amount` (target flow in CRC wei, use max uint256 for max possible).\n\n**Optional params**:\n- `fromTokens`/`toTokens`: Restrict which tokens can be used at source/sink (array of token-owner addresses)\n- `excludedFromTokens`/`excludedToTokens`: Exclude specific tokens\n- `withWrap`: Include ERC-20 wrapper paths (default: false)\n- `simulatedBalances`: JSON string of hypothetical balances for 'what-if' testing\n- `simulatedConsentedAvatars`: Addresses to treat as having consented\n- `maxTransfers`: Limit transfer step count (gas cost control)\n- `quantizedMode`: Enforce 96 CRC quantization for invitations\n- `debugShowIntermediateSteps`: Include all transformation stages\n\n**Address format**: 0x-prefixed, 40 hex chars, checksummed or lowercase.\n**Amount format**: uint256 as decimal string. Max uint256 = 115792089237316195423570985008687907853269984665640564039457584007913129639935.\n**Array size limit**: 1000 entries per array parameter.\n**Request body limit**: 1 MB.",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "Sender Ethereum address (0x-prefixed, 40 hex chars)",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Receiver Ethereum address (0x-prefixed, 40 hex chars)",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "amount",
            "in": "query",
            "description": "Target flow as uint256 decimal string. Use max uint256 for 'find maximum possible flow'",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "fromTokens",
            "in": "query",
            "description": "Restrict which token types source can send (array of token-owner addresses)",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "toTokens",
            "in": "query",
            "description": "Restrict which token types sink will receive (array of token-owner addresses)",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "excludedFromTokens",
            "in": "query",
            "description": "Token types source must NOT send (array of token-owner addresses)",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "excludedToTokens",
            "in": "query",
            "description": "Token types sink must NOT receive (array of token-owner addresses)",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "withWrap",
            "in": "query",
            "description": "Include ERC-20 wrapper tokens in path calculation",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "simulatedBalances",
            "in": "query",
            "description": "JSON-encoded array of SimulatedBalance objects for what-if analysis",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "simulatedConsentedAvatars",
            "in": "query",
            "description": "Avatars with consented flow enabled (array of 0x-prefixed addresses)",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "maxTransfers",
            "in": "query",
            "description": "Cap the number of transfer steps in result (gas cost control)",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "quantizedMode",
            "in": "query",
            "description": "Enforce 96 CRC quantization per sink-bound transfer (invitation module)",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "debugShowIntermediateSteps",
            "in": "query",
            "description": "Include debug pipeline stages (rawPaths, collapsed, routerInserted, sorted) in response",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MaxFlowResponse"
                }
              }
            }
          }
        }
      }
    },
    "/findPath": {
      "get": {
        "tags": [
          "Pathfinding"
        ],
        "summary": "Compute transfer path (GET)",
        "description": "Computes a multi-hop transitive transfer path between two addresses. Uses Google OR-Tools max-flow solver. The response contains the exact transfer steps to submit on-chain via Hub.sol operateFlowMatrix().\n\n**Required params**: `from` (sender address), `to` (receiver address), `amount` (target flow in CRC wei).\n\n**Optional params**: Same as GET /findMaxFlow, plus:\n- `maxTransfers`: Limit the number of transfer steps for gas cost control\n- `quantizedMode`: Enforce 96 CRC quantization per sink-bound transfer (invitation module)\n- `debugShowIntermediateSteps`: Include rawPaths, collapsed, routerInserted, sorted stages in response\n\n**Response**: `maxFlow` (actual achievable amount) + `transfers` (ordered list of from/to/tokenOwner/value steps).\n\n**Use POST /findPath** for complex requests with simulatedBalances/simulatedTrusts (JSON body).",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "Sender Ethereum address (0x-prefixed, 40 hex chars)",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Receiver Ethereum address (0x-prefixed, 40 hex chars)",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "amount",
            "in": "query",
            "description": "Target flow as uint256 decimal string. Use max uint256 for 'find maximum possible flow'",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "fromTokens",
            "in": "query",
            "description": "Restrict which token types source can send (array of token-owner addresses)",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "toTokens",
            "in": "query",
            "description": "Restrict which token types sink will receive (array of token-owner addresses)",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "excludedFromTokens",
            "in": "query",
            "description": "Token types source must NOT send (array of token-owner addresses)",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "excludedToTokens",
            "in": "query",
            "description": "Token types sink must NOT receive (array of token-owner addresses)",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "withWrap",
            "in": "query",
            "description": "Include ERC-20 wrapper tokens in path calculation",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "simulatedBalances",
            "in": "query",
            "description": "JSON-encoded array of SimulatedBalance objects for what-if analysis",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "simulatedConsentedAvatars",
            "in": "query",
            "description": "Avatars with consented flow enabled (array of 0x-prefixed addresses)",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "maxTransfers",
            "in": "query",
            "description": "Cap the number of transfer steps in result (gas cost control)",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "quantizedMode",
            "in": "query",
            "description": "Enforce 96 CRC quantization per sink-bound transfer (invitation module)",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "debugShowIntermediateSteps",
            "in": "query",
            "description": "Include debug pipeline stages (rawPaths, collapsed, routerInserted, sorted) in response",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MaxFlowResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Pathfinding"
        ],
        "summary": "Compute transfer path (POST)",
        "description": "Computes a multi-hop transitive transfer path (POST variant with JSON body). **Preferred for complex requests** with simulatedBalances, simulatedTrusts, or many token filters.\n\n**Request body**: FlowRequest JSON object with fields:\n- `source` (required): Sender address (0x-prefixed, 40 hex chars)\n- `sink` (required): Receiver address\n- `targetFlow` (required): Amount in CRC wei (uint256 string). Use max uint256 for 'send as much as possible'.\n- `fromTokens`/`toTokens`: Restrict tokens at source/sink (array of addresses)\n- `excludedFromTokens`/`excludedToTokens`: Exclude specific tokens\n- `withWrap`: Include ERC-20 wrapper paths (default: false)\n- `simulatedBalances`: Array of {holder, token, amount, isWrapped?, isStatic?} for 'what-if' testing\n- `simulatedTrusts`: Array of {truster, trustee} for hypothetical trust edges\n- `simulatedConsentedAvatars`: Addresses to treat as consented\n- `maxTransfers`: Limit transfer step count (gas cost control)\n- `quantizedMode`: Enforce 96 CRC quantization (invitation module)\n- `debugShowIntermediateSteps`: Include all transformation stages\n\n**Response**: MaxFlowResponse with `maxFlow`, `transfers[]`, and optional `debug` stages.\n\n**Restrictions**: Body limit 1 MB, array params max 1000 entries each.\n**Solver timeout**: 30 seconds (configurable via PATHFINDER_SOLVER_TIMEOUT_SECONDS).",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "null"
                  },
                  {
                    "$ref": "#/components/schemas/FlowRequest"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MaxFlowResponse"
                }
              }
            }
          }
        }
      }
    },
    "/snapshot": {
      "get": {
        "tags": [
          "Graph"
        ],
        "summary": "Get trust graph snapshot",
        "description": "Returns a snapshot of the full Circles trust graph including all avatars, trust edges, and token balances. **Large response** (can be several MB compressed).\n\n**ETag support**: Responses include an ETag header. Send `If-None-Match` with the previous ETag to get 304 Not Modified if the graph hasn't changed. The graph updates every ~5 seconds.\n\n**Cache-Control**: `public, max-age=5` — clients can cache briefly.\n\n**503**: Returned if the graph has not been built yet (service starting up).\n\n**Common use case**: Pre-loading the trust graph for client-side pathfinding or visualization.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": { }
              }
            }
          },
          "503": {
            "description": "Service Unavailable",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "DebugPipelineStages": {
        "type": "object",
        "properties": {
          "rawPaths": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/TransferPathStep"
            },
            "description": "Stage 1: Raw paths from MaxFlowSolver with token pools (tpool-0x...).\nShows Avatar → TokenPool → Avatar paths before collapsing."
          },
          "collapsed": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/TransferPathStep"
            },
            "description": "Stage 2: Token pools collapsed, showing Avatar → Avatar flows.\nIntermediate pool nodes removed, flows aggregated."
          },
          "routerInserted": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/TransferPathStep"
            },
            "description": "Stage 3: Router inserted for group mints.\nAvatar → Group becomes Avatar → Router → Group."
          },
          "sorted": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/TransferPathStep"
            },
            "description": "Stage 4: Final sorted order for contract execution.\nEnsures mint dependencies are satisfied (collateral before mints)."
          }
        },
        "description": "Debug information showing all transformation stages in the pathfinding pipeline.\nEach stage shows how edges are transformed as they progress through the system."
      },
      "FlowRequest": {
        "type": "object",
        "properties": {
          "source": {
            "type": [
              "null",
              "string"
            ],
            "description": "Sender address (0x-prefixed, 40 hex chars). Must be a registered Circles V2 avatar."
          },
          "sink": {
            "type": [
              "null",
              "string"
            ],
            "description": "Receiver address (0x-prefixed, 40 hex chars). Must be a registered Circles V2 avatar."
          },
          "targetFlow": {
            "type": [
              "null",
              "string"
            ],
            "description": "Amount to transfer in CRC wei (1 CRC = 10^18 wei). Use max uint256 (\"115792089237316195423570985008687907853269984665640564039457584007913129639935\") to discover maximum possible flow."
          },
          "toTokens": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "Restrict which tokens the sink can receive. Array of token-owner addresses. If omitted, all trusted tokens are accepted."
          },
          "fromTokens": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "Restrict which tokens the source can send. Array of token-owner addresses. If omitted, all held tokens are used."
          },
          "excludedFromTokens": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "Exclude specific tokens from the source side. Array of token-owner addresses."
          },
          "excludedToTokens": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "Exclude specific tokens from the sink side. Array of token-owner addresses."
          },
          "withWrap": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "When true, includes ERC-20 wrapper token paths in addition to native ERC-1155 paths."
          },
          "simulatedBalances": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/SimulatedBalance"
            },
            "description": "Hypothetical token balances to inject into the graph before path computation.\nUseful for testing \"what if\" scenarios without on-chain state changes."
          },
          "simulatedTrusts": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/SimulatedTrust"
            },
            "description": "Hypothetical trust relations to inject into the graph before path computation."
          },
          "simulatedConsentedAvatars": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "Addresses to treat as having consented to advanced usage (CRC-1155 operator approval).\nAffects which intermediate transfer paths are valid."
          },
          "maxTransfers": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Maximum number of transfer steps in the result. Limits path complexity for on-chain gas cost control.",
            "format": "int32"
          },
          "quantizedMode": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "When true, enforces 96 CRC quantization for sink-bound transfers (invitation module).\nEach sink-bound transfer will be exactly N × 96 CRC.\nThe number of invites is derived from targetFlow: invites = targetFlow / 96 CRC.\nUse max uint256 targetFlow to discover all possible invites."
          },
          "debugShowIntermediateSteps": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "When true, includes debug information showing all transformation stages:\nrawPaths (from solver), collapsed (pools removed), routerInserted (group mints), sorted (final order)."
          }
        },
        "description": "Request body for path computation through the Circles trust network.\nContains source/sink addresses, target amount, and optional filters for tokens, wrapping, quantization, simulation, and debugging."
      },
      "MaxFlowResponse": {
        "required": [
          "maxFlow",
          "transfers"
        ],
        "type": "object",
        "properties": {
          "maxFlow": {
            "type": "string",
            "description": "Maximum achievable flow in CRC wei (uint256 as decimal string). This is the actual amount that can be transferred, which may be less than targetFlow."
          },
          "transfers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TransferPathStep"
            },
            "description": "Ordered list of individual token transfer steps to submit on-chain via Hub.sol operateFlowMatrix()."
          },
          "debug": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Debug information showing transformation stages (only present if debugShowIntermediateSteps=true).",
                "$ref": "#/components/schemas/DebugPipelineStages"
              }
            ]
          },
          "graphBlock": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Block number of the graph snapshot used for this computation.\nLets callers detect staleness by comparing to the current chain head.",
            "format": "int64"
          }
        },
        "description": "Response from path computation. Contains the maximum achievable flow and the transfer steps to execute on-chain."
      },
      "ProblemDetails": {
        "type": "object",
        "properties": {
          "type": {
            "type": [
              "null",
              "string"
            ]
          },
          "title": {
            "type": [
              "null",
              "string"
            ]
          },
          "status": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "detail": {
            "type": [
              "null",
              "string"
            ]
          },
          "instance": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "SimulatedBalance": {
        "type": "object",
        "properties": {
          "holder": {
            "type": "string",
            "description": "Holder address — the avatar that holds the tokens (0x-prefixed, any case accepted)."
          },
          "token": {
            "type": "string",
            "description": "Token identifier — the token-owner avatar address, or ERC-20 wrapper address."
          },
          "amount": {
            "type": "string",
            "description": "Balance amount as uint256 string in CRC wei (1 CRC = 10^18 wei). Example: \"96000000000000000000\" = 96 CRC."
          },
          "isWrapped": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "When true, treat this as an ERC-20 wrapped token balance instead of native ERC-1155."
          },
          "isStatic": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "When true, this balance is not subject to demurrage decay — it remains constant regardless of time."
          }
        },
        "description": "A hypothetical token balance to inject into the trust graph for simulation."
      },
      "SimulatedTrust": {
        "type": "object",
        "properties": {
          "truster": {
            "type": "string",
            "description": "The address that grants trust (0x-prefixed, 40 hex chars)."
          },
          "trustee": {
            "type": "string",
            "description": "The address that receives trust (0x-prefixed, 40 hex chars)."
          }
        },
        "description": "A hypothetical trust relation to inject into the trust graph for simulation."
      },
      "TransferPathStep": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string",
            "description": "Sender address for this transfer step (0x-prefixed, lowercase)."
          },
          "to": {
            "type": "string",
            "description": "Receiver address for this transfer step (0x-prefixed, lowercase)."
          },
          "tokenOwner": {
            "type": "string",
            "description": "Token owner address identifying which Circles token is transferred (0x-prefixed, lowercase)."
          },
          "value": {
            "type": "string",
            "description": "Transfer amount in CRC wei (uint256 as decimal string). 1 CRC = 10^18 wei."
          }
        },
        "description": "A single step in a transitive transfer path. Each step represents one on-chain token transfer."
      }
    }
  },
  "tags": [
    {
      "name": "Pathfinding"
    },
    {
      "name": "Graph"
    }
  ]
}