{
  "components": {
    "responses": {
      "Error": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "Every non-2xx answer. An unknown code is a generic refusal."
      }
    },
    "schemas": {
      "Error": {
        "properties": {
          "error": {
            "properties": {
              "code": {
                "enum": [
                  "acceptance_required",
                  "automation_not_allowed",
                  "awaiting_owner",
                  "bad_request",
                  "chain-unavailable",
                  "gateway_halted",
                  "idempotency_conflict",
                  "in_progress",
                  "internal",
                  "not_found",
                  "not_quotable",
                  "oracle",
                  "out_of_scope",
                  "payment_required",
                  "payment_unavailable",
                  "price",
                  "quote_expired",
                  "rate_limited",
                  "unauthenticated",
                  "would_revert"
                ],
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "retryable": {
                "type": "boolean"
              }
            },
            "required": [
              "code",
              "message",
              "retryable"
            ],
            "type": "object"
          }
        },
        "required": [
          "error"
        ],
        "type": "object"
      }
    },
    "securitySchemes": {
      "agentSession": {
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "title": "DefiLoops agent gateway",
    "version": "1"
  },
  "openapi": "3.0.3",
  "paths": {
    "/v1/accounts": {
      "get": {
        "operationId": "get_v1_accounts",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "The signed-in wallet's account on its sign-in chain, derived exactly as the console derives it from the wallet as owner, and whether it is deployed."
      },
      "post": {
        "operationId": "post_v1_accounts",
        "responses": {
          "200": {
            "description": "Success."
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Payment required (x402 v2, exact on eip155:8453): the PAYMENT-REQUIRED header holds the accepted payment, and a payment-identifier is required as the idempotency key."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "Create the signed-in wallet's own account through the console's activation workflow (the worker sends, through the relayer). Paid, because deployment costs gas: closed until priced. Refused out_of_scope if the account carries a hook or module that could make the relayed send revert; an account that already exists is returned with nothing sent, and asking again sends nothing twice."
      }
    },
    "/v1/accounts/{account}/balances": {
      "get": {
        "operationId": "get_v1_accounts_account_balances",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "The account's balances, read by the console's own balance reader. Another wallet's account is not_found."
      }
    },
    "/v1/accounts/{account}/deposit": {
      "get": {
        "operationId": "get_v1_accounts_account_deposit",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "Where to send money for one of your accounts: its address on its chain, the assets it holds, and the console's instructions. Another wallet's account is not_found."
      }
    },
    "/v1/accounts/{account}/positions": {
      "get": {
        "operationId": "get_v1_accounts_account_positions",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "The account's lending positions, read from each venue by the console's own position reader. Another wallet's account is not_found."
      }
    },
    "/v1/agents/{agentRef}/credential": {
      "get": {
        "operationId": "get_v1_agents_agentRef_credential",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [],
        "summary": "A DefiLoops-signed \"listed\" credential for an agent whose listing is effective and not suspended: EIP-712 typed data (agentRef, listing id, the reviewed identity's snapshot hash, issuedAt, expiresAt; at most 30 days), verifiable offline against the address of GET /v1/credential-signer. Any other agent, and a deployment without a credential key, is not_found."
      }
    },
    "/v1/agents/{agentRef}/listing": {
      "get": {
        "operationId": "get_v1_agents_agentRef_listing",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [],
        "summary": "An agent's guardian listing: its effective state now (pending inside the activation delay, effective, suspended or unlisted), the identity snapshot the guardian reviewed and the capability manifest it was listed with. An agent with no listing is not_found."
      }
    },
    "/v1/auth/challenge": {
      "post": {
        "operationId": "post_v1_auth_challenge",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [],
        "summary": "Agent sign-in, step 1: a single-use SIWE (EIP-4361) message for a wallet on a chain, optionally naming an ERC-8004 agentId."
      }
    },
    "/v1/auth/verify": {
      "post": {
        "operationId": "post_v1_auth_verify",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [],
        "summary": "Agent sign-in, step 2: the signed message (EOA, ERC-1271 or ERC-6492) for a short-lived agent token."
      }
    },
    "/v1/call": {
      "post": {
        "operationId": "post_v1_call",
        "responses": {
          "200": {
            "description": "Success."
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Payment required (x402 v2, exact on eip155:8453): the PAYMENT-REQUIRED header holds the accepted payment, and a payment-identifier is required as the idempotency key."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "One operation in one request: a quote, then the run of that quote, composed in process. You commit to maxCost (the most you will pay for gas plus fee, in wei) and minOut (the least a priced step may deliver, raw units), and the call runs only if the fresh preview is within them; the floor is the greatest of the compiled one, the owner's oracle floor and your minOut. Everything else is quote then run: consent, the capability manifest, the simulation (fail-closed), and the hold from credits before anything starts. Idempotency-Key is required; the same key and request answer the same run, and never start or pay twice. Answers 202 with the run id."
      }
    },
    "/v1/catalog": {
      "get": {
        "operationId": "get_v1_catalog",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [],
        "summary": "Every catalogue operation, its chains, and whether an agent can call it. With q (job words) and/or chain it is a search: the callable operations whose asked phrases or description match, ranked by measured quality (success rate, then realised slippage, then gas), each with its index figures and sample size."
      }
    },
    "/v1/credential-signer": {
      "get": {
        "operationId": "get_v1_credential_signer",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [],
        "summary": "The address that signs the \"listed\" credentials. not_found where none are issued."
      }
    },
    "/v1/credits": {
      "get": {
        "operationId": "get_v1_credits",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "The signed-in tenant's own credits, in credits (decimal string) and micro-credits (1 credit = 1,000,000 micro): spendable (net of open run holds), held (by open run holds), total, and pending: credits bought with a settled POST /v1/credits that are not minted yet (the mint waits for Base's safe block, a minute or more). Poll it after buying, and quote only once pending.count is 0."
      },
      "post": {
        "operationId": "post_v1_credits",
        "responses": {
          "200": {
            "description": "Success."
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Payment required (x402 v2, exact on eip155:8453): the PAYMENT-REQUIRED header holds the accepted payment, and a payment-identifier is required as the idempotency key."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "Top up credits with an x402 exact payment. Credits are minted once the settlement is final on chain, at 100 credits to the USDC; they are non-refundable."
      }
    },
    "/v1/grants": {
      "get": {
        "operationId": "get_v1_grants",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "The hire grants naming the signed-in agent (by its ERC-8004 agent or its SIWX tenant), newest first, each with its status: an owner records them on the console, never here."
      }
    },
    "/v1/me": {
      "get": {
        "operationId": "get_v1_me",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "The signed-in agent: its tenant, wallet, chain, the agent it proved, and when its token ends."
      }
    },
    "/v1/openapi.json": {
      "get": {
        "operationId": "get_v1_openapi_json",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [],
        "summary": "This document: every /v1 route, generated from the route table."
      }
    },
    "/v1/plan-schema": {
      "get": {
        "operationId": "get_v1_plan_schema",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [],
        "summary": "The plan document's JSON Schema (draft 2020-12, format v1): what a plan must look like to be quoted. Generated from the catalogue, so its operation and chain enums are this build's."
      }
    },
    "/v1/plans": {
      "post": {
        "operationId": "post_v1_plans",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "Save a plan document (format v1) for an account you have consent for, as the revision it names, through the console's own save: the shape, the adapters' declarations, the task's owner, no rewriting of what already ran, and your agent's capability manifest (a step outside it is refused out_of_scope). It stores the plan and runs nothing: to run it, quote it with planRef {taskId, revision}. Idempotency-Key is required; the same key and request answer the same save, and never store a second revision."
      }
    },
    "/v1/plans/{taskId}": {
      "get": {
        "operationId": "get_v1_plans_taskId",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "The revisions of a plan you saved through POST /v1/plans, oldest first, each with its hash and document. Another caller's plan is not_found."
      }
    },
    "/v1/quotes": {
      "post": {
        "operationId": "post_v1_quotes",
        "responses": {
          "200": {
            "description": "Success."
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Payment required (x402 v2, exact on eip155:8453): the PAYMENT-REQUIRED header holds the accepted payment, and a payment-identifier is required as the idempotency key."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "Quote an operation or a plan on the caller's own account: a private preview of the exact calls B will make (decoded with the published ABIs), their simulation, every floor (the least the keeper's price guard accepts at the owner's maximum slippage, or the caller's if tighter), the maximum cost, the plan hash and validUntil. previewHash is keccak256 of the preview's RFC 8785 canonical JSON. A lend.supply whose params are only {asset} names no venue: it is quoted at the highest supply rate, read on chain, among the venues inside the caller's policy, and the preview's routing section shows the venue chosen, every venue compared and why; a venue's own operation, or a plan, is quoted as named."
      }
    },
    "/v1/quotes/{id}": {
      "get": {
        "operationId": "get_v1_quotes_id",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "One of the caller's quotes, with its status (live until validUntil, then expired). Another caller's quote is not_found."
      }
    },
    "/v1/quotes/{id}/run": {
      "post": {
        "operationId": "post_v1_quotes_id_run",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "Run one of the caller's live quotes, accepting the preview named by previewHash. Idempotency-Key is required: the same key and request answer the same run, and never start or pay twice. Before anything starts the consent is checked again, the plan is compiled again and must make the calls the preview showed (only the deadline, and a swap's route bounded by the signed floor, may differ; floors only go up), and the quote's maximum cost is held. Answers 202 with the run id at once."
      }
    },
    "/v1/runs/{run_id}": {
      "get": {
        "operationId": "get_v1_runs_run_id",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "One of the caller's runs: its state, and its payment (the hold, and once settled what was captured and released). Another caller's run is not_found."
      }
    },
    "/v1/runs/{run_id}/events": {
      "get": {
        "operationId": "get_v1_runs_run_id_events",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "One of the caller's runs' timeline after the cursor `after` (an event's seq, exclusive), and the next cursor."
      }
    },
    "/v1/runs/{run_id}/stop": {
      "post": {
        "operationId": "post_v1_runs_run_id_stop",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "Stop one of the caller's runs, through the console's own stop. What is already on chain stays there. Never refused but for there being no such run."
      }
    },
    "/v1/session-certs": {
      "get": {
        "operationId": "get_v1_session_certs",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "The signed-in wallet's session certificates, newest first, each with its status."
      },
      "post": {
        "operationId": "post_v1_session_certs",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "Register a session certificate: an EIP-712 document the signed-in wallet (the root) signs to let a session key act for one purpose, on one chain, for at most 24 hours."
      }
    },
    "/v1/session-certs/{id}": {
      "delete": {
        "operationId": "delete_v1_session_certs_id",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "Revoke a session certificate. Revocation is a new record and is for ever."
      }
    },
    "/v1/templates": {
      "get": {
        "operationId": "get_v1_templates",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [],
        "summary": "The templates shelf: plans with typed slots (asset, amount, basis points) and constraints (min, max, allowed assets, chains), each with its author, risk notes and operations. With q (job words) and/or chain it is a search by the catalogue's own matcher, ranked by the measured quality of the template's weakest operation (success rate, then slippage, then gas); unmeasured last."
      }
    },
    "/v1/templates/{id}/quotes": {
      "post": {
        "operationId": "post_v1_templates_id_quotes",
        "responses": {
          "200": {
            "description": "Success."
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Payment required (x402 v2, exact on eip155:8453): the PAYMENT-REQUIRED header holds the accepted payment, and a payment-identifier is required as the idempotency key."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "Fill a template's slots with your values and quote the result on your own account, exactly as POST /v1/quotes quotes an inline plan. A value outside its slot's constraint, a missing or unknown slot, or a chain the template does not name is refused bad_request naming the slot."
      }
    },
    "/v1/webhook": {
      "delete": {
        "operationId": "delete_v1_webhook",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "Remove your webhook. Nothing more is posted; the events cursor still answers."
      },
      "get": {
        "operationId": "get_v1_webhook",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "Your registered webhook URL and when it was set (never its secret)."
      },
      "put": {
        "operationId": "put_v1_webhook",
        "responses": {
          "200": {
            "description": "Success."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "agentSession": []
          }
        ],
        "summary": "Register the one https URL your runs' completion is posted to (a new registration replaces the last). The answer carries the signing secret, shown once: each delivery is signed HMAC-SHA256 over `\u003ctimestamp\u003e.\u003cbody\u003e`. The URL must resolve only to public addresses; it is checked again on every delivery."
      }
    }
  }
}