{
  "openapi": "3.1.0",
  "info": {
    "title": "Tellr — Agent ATM",
    "version": "1.0.0",
    "description": "Six decision endpoints. Each call is 0.05 USDC on Base via x402. An unpaid POST returns HTTP 402 Payment Required with a PAYMENT-REQUIRED header. A 200 arrives after settlement. Decision logic only: no keys, no wallet access, moves nothing."
  },
  "servers": [
    {
      "url": "https://x402.bankr.bot/0xcdc493fb6f0fa17b213ac2195bc454b39c1228ec"
    }
  ],
  "tags": [
    {
      "name": "checkout"
    },
    {
      "name": "funding"
    },
    {
      "name": "payment"
    },
    {
      "name": "liquidation"
    },
    {
      "name": "agent-payments"
    },
    {
      "name": "treasury"
    },
    {
      "name": "stablecoin"
    },
    {
      "name": "spending"
    },
    {
      "name": "policy"
    },
    {
      "name": "limits"
    },
    {
      "name": "risk"
    },
    {
      "name": "guardrail"
    },
    {
      "name": "portfolio"
    },
    {
      "name": "sell-plan"
    },
    {
      "name": "rebalance"
    },
    {
      "name": "security"
    },
    {
      "name": "privilege"
    },
    {
      "name": "airdrop"
    },
    {
      "name": "nft"
    },
    {
      "name": "escalation"
    },
    {
      "name": "classification"
    },
    {
      "name": "fail-closed"
    },
    {
      "name": "top-off"
    },
    {
      "name": "card"
    },
    {
      "name": "payments"
    },
    {
      "name": "recurring"
    }
  ],
  "paths": {
    "/tellr-fund-checkout": {
      "post": {
        "operationId": "tellr-fund-checkout",
        "summary": "Decide how to fund a checkout before paying it.",
        "description": "Given an exact settlement amount and the holdings you are willing to sell, returns whether it can be covered, from where (stablecoin, liquidation, or mixed), the sell legs under a 75% per-position cap, and what selling would realise as a taxable disposal. Use before an agent makes a purchase to avoid draining stablecoin. Pure decision logic: no keys, no wallet access, moves nothing.",
        "tags": [
          "checkout",
          "funding",
          "payment",
          "liquidation",
          "agent-payments",
          "treasury",
          "stablecoin"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "settlementUsd": {
                    "type": "number",
                    "description": "Exact amount the checkout must receive, in USD. Required."
                  },
                  "stableUsd": {
                    "type": "number",
                    "description": "Stablecoin you already hold and are willing to spend. Default 0. The cheapest route — it realises nothing."
                  },
                  "holdings": {
                    "type": "array",
                    "description": "Holdings available to sell. Each needs 'symbol', 'balanceUsd', and 'pool' ('liquidity' or 'growth'). A holding with no pool is treated as growth and will not be sold without confirmation.",
                    "items": {
                      "type": "object",
                      "properties": {
                        "symbol": {
                          "type": "string",
                          "description": "Token symbol, e.g. BAG"
                        },
                        "balanceUsd": {
                          "type": "number",
                          "description": "Position value in USD"
                        },
                        "pool": {
                          "type": "string",
                          "description": "liquidity or growth",
                          "enum": [
                            "liquidity",
                            "growth"
                          ]
                        },
                        "change7dPct": {
                          "type": "number",
                          "description": "7-day move in percent, used for ranking"
                        }
                      }
                    }
                  },
                  "deadlineMs": {
                    "type": "integer",
                    "description": "Milliseconds you have before the checkout times out. Echoed back with a quote-risk flag."
                  },
                  "sourceOrder": {
                    "type": "string",
                    "description": "stablecoin_first (default) spends stablecoin before selling. liquidity_first sells holdings even when stablecoin is available — for deliberate rebalancing.",
                    "enum": [
                      "stablecoin_first",
                      "liquidity_first"
                    ]
                  },
                  "maxLiquidationUsd": {
                    "type": "number",
                    "description": "Per-purchase cap on how much may be sold to cover one checkout. Default 250. Above this the call is denied outright."
                  },
                  "minTransferUsd": {
                    "type": "number",
                    "description": "Dust floor. Default 1. A shortfall below this is too small to be worth moving and is skipped."
                  },
                  "txCeilingUsd": {
                    "type": "number",
                    "description": "Per-transaction ceiling to evaluate against. Default 150."
                  },
                  "dailyCapUsd": {
                    "type": "number",
                    "description": "Daily cap to evaluate against. Default 500."
                  },
                  "voiceConfirmed": {
                    "type": "boolean",
                    "description": "Whether a human has explicitly confirmed. Default false."
                  }
                },
                "required": [
                  "settlementUsd"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Decision or description. Returned only after the x402 payment settles.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Transport-level success"
                    },
                    "action": {
                      "type": "string",
                      "description": "fund (coverable) | confirm_required (needs a human) | deny (refused) | skip (nothing to do)",
                      "enum": [
                        "fund",
                        "confirm_required",
                        "deny",
                        "skip"
                      ]
                    },
                    "reason": {
                      "type": "string",
                      "description": "Rule that produced the answer, e.g. within_limits, liquidation_over_per_purchase_cap, growth_requires_confirm, below_min_transfer"
                    },
                    "settlementUsd": {
                      "type": "number",
                      "description": "The amount the checkout needed"
                    },
                    "funding": {
                      "type": "object",
                      "description": "How the checkout would be covered",
                      "properties": {
                        "source": {
                          "type": "string",
                          "description": "stablecoin | liquidation | mixed",
                          "enum": [
                            "stablecoin",
                            "liquidation",
                            "mixed"
                          ]
                        },
                        "raisedUsd": {
                          "type": "number",
                          "description": "Total raised by selling. 0 means nothing was sold."
                        },
                        "coveredUsd": {
                          "type": "number",
                          "description": "Total value the routes cover"
                        },
                        "coversCheckout": {
                          "type": "boolean",
                          "description": "True when covered >= settlement"
                        },
                        "legCount": {
                          "type": "integer",
                          "description": "Number of sell legs"
                        },
                        "realisesPnl": {
                          "type": "boolean",
                          "description": "True when selling crystallises a gain or loss"
                        },
                        "netRealizedPnlUsd": {
                          "type": "number",
                          "description": "Signed net realised P&L. Negative is a loss. Null when cost basis is unknown."
                        },
                        "disclosesRealizedLoss": {
                          "type": "boolean",
                          "description": "True when this funding would crystallise a loss"
                        }
                      }
                    },
                    "plan": {
                      "type": "object",
                      "description": "Sell plan when liquidation is needed, else null. legs[] each carry symbol, sellUsd, balanceUsd, pool and note.",
                      "properties": {
                        "legs": {
                          "type": "array",
                          "items": {
                            "type": "object"
                          },
                          "description": "One entry per position to sell"
                        },
                        "totalSellUsd": {
                          "type": "number",
                          "description": "Sum of all legs"
                        },
                        "perPositionCapFraction": {
                          "type": "number",
                          "description": "75% cap applied per position"
                        }
                      }
                    },
                    "deadlineRisk": {
                      "type": "string",
                      "description": "quote_required_at_execution when a deadline was supplied — the plan must be re-quoted inside the window"
                    },
                    "language": {
                      "type": "string",
                      "description": "Always 'descriptive_only'. This is a description of an amount, not an authorisation to move funds."
                    },
                    "disclaimer": {
                      "type": "string",
                      "description": "States explicitly that this is not an authorisation and that the caller applies its own limits and confirmation."
                    },
                    "stableUsd": {
                      "type": "number",
                      "description": "Stablecoin the caller said was available."
                    },
                    "stableAppliedUsd": {
                      "type": "number",
                      "description": "Stablecoin actually applied to this checkout."
                    },
                    "liquidationRequiredUsd": {
                      "type": "number",
                      "description": "Portion that would have to come from selling."
                    },
                    "sourceOrder": {
                      "type": "string",
                      "description": "Order used: stablecoin_first (default) or liquidity_first.",
                      "enum": [
                        "stablecoin_first",
                        "liquidity_first"
                      ]
                    },
                    "deadlineMs": {
                      "type": "integer",
                      "description": "The caller's deadline, echoed back."
                    }
                  }
                }
              }
            }
          },
          "402": {
            "description": "Payment Required. The request was not paid. Settle the x402 challenge, then retry the same POST.",
            "headers": {
              "PAYMENT-REQUIRED": {
                "description": "x402 payment challenge for this call.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/tellr-check": {
      "post": {
        "operationId": "tellr-check",
        "summary": "Decide whether a spend can auto-execute, needs a human, or must be refused, against configured per-transaction ceilings, a daily cap and pool rules.",
        "description": "Returns one of three fixed verdicts with the rule that produced it. Pure decision logic: no keys, no wallet access, moves nothing.",
        "tags": [
          "spending",
          "policy",
          "limits",
          "risk",
          "agent-payments",
          "guardrail"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "amountUsd": {
                    "type": "number",
                    "description": "The spend being evaluated. Required."
                  },
                  "dailySpentUsd": {
                    "type": "number",
                    "description": "Already spent today. Default 0."
                  },
                  "pool": {
                    "type": "string",
                    "description": "liquidity or growth. Growth always requires confirmation.",
                    "enum": [
                      "liquidity",
                      "growth"
                    ]
                  },
                  "voiceConfirmed": {
                    "type": "boolean",
                    "description": "Whether a human has confirmed. Default false."
                  },
                  "txCeilingUsd": {
                    "type": "number",
                    "description": "Per-transaction ceiling. Default 150."
                  },
                  "dailyCapUsd": {
                    "type": "number",
                    "description": "Daily cap. Default 500."
                  },
                  "faceMatchFailed": {
                    "type": "boolean",
                    "description": "Forces confirmation even under the ceiling."
                  }
                },
                "required": [
                  "amountUsd"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Decision or description. Returned only after the x402 payment settles.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "decision": {
                      "type": "string",
                      "description": "allow | confirm_required | deny",
                      "enum": [
                        "allow",
                        "confirm_required",
                        "deny"
                      ]
                    },
                    "reason": {
                      "type": "string",
                      "description": "e.g. within_limits, over_tx_ceiling, over_daily_cap, growth_pool_requires_confirm"
                    },
                    "details": {
                      "type": "object",
                      "description": "The inputs used, so the answer is auditable"
                    }
                  }
                }
              }
            }
          },
          "402": {
            "description": "Payment Required. The request was not paid. Settle the x402 challenge, then retry the same POST.",
            "headers": {
              "PAYMENT-REQUIRED": {
                "description": "x402 payment challenge for this call.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/tellr-sell-plan": {
      "post": {
        "operationId": "tellr-sell-plan",
        "summary": "Build a descriptive sell plan that raises a target amount from a set of holdings.",
        "description": "Sells stablecoins first, then smaller liquidity positions, and caps every position at 75% so no single holding is emptied. Returns legs and reasoning only — it does not execute and does not predict prices. Pure decision logic: no keys, no wallet access, moves nothing.",
        "tags": [
          "liquidation",
          "portfolio",
          "sell-plan",
          "rebalance",
          "treasury"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "targetUsd": {
                    "type": "number",
                    "description": "Amount to raise. Required."
                  },
                  "holdings": {
                    "type": "array",
                    "description": "Positions available. Each needs symbol, balanceUsd and pool.",
                    "items": {
                      "type": "object",
                      "properties": {
                        "symbol": {
                          "type": "string"
                        },
                        "balanceUsd": {
                          "type": "number"
                        },
                        "pool": {
                          "type": "string",
                          "enum": [
                            "liquidity",
                            "growth"
                          ]
                        },
                        "change7dPct": {
                          "type": "number",
                          "description": "7-day move, used for ranking"
                        }
                      }
                    }
                  },
                  "rankBy": {
                    "type": "string",
                    "description": "drawdown7d (default) or balance",
                    "enum": [
                      "drawdown7d",
                      "balance"
                    ]
                  },
                  "tagMap": {
                    "type": "object",
                    "description": "Optional map of symbol to pool when holdings carry no pool field"
                  },
                  "voiceConfirmed": {
                    "type": "boolean",
                    "description": "Required to include growth-pool holdings. Default false."
                  },
                  "txCeilingUsd": {
                    "type": "number"
                  },
                  "dailyCapUsd": {
                    "type": "number"
                  }
                },
                "required": [
                  "targetUsd"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Decision or description. Returned only after the x402 payment settles.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "legs": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "One leg per position: symbol, sellUsd, balanceUsd, pool, note"
                    },
                    "totalSellUsd": {
                      "type": "number"
                    },
                    "perPositionCapFraction": {
                      "type": "number",
                      "description": "0.75"
                    },
                    "needsGrowthConfirm": {
                      "type": "boolean",
                      "description": "True when growth holdings are required and unconfirmed"
                    },
                    "language": {
                      "type": "string",
                      "description": "Always 'descriptive_only'"
                    },
                    "disclaimer": {
                      "type": "string",
                      "description": "Figures describe recent market data only. Not a prediction or recommendation."
                    },
                    "shortfallUsd": {
                      "type": "number",
                      "description": "Amount the plan could NOT raise. Non-zero means the target is unreachable under the 75% cap."
                    },
                    "rankNote": {
                      "type": "string",
                      "description": "One-line statement of the ordering rule used, so a caller never has to infer it."
                    },
                    "rankingConfidence": {
                      "type": "string",
                      "description": "Truth contract for the ordering. Currently 'balance_tiebreak_only' — the order is a defined liquidity waterfall, NOT a performance ranking."
                    },
                    "reservedAvailable": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "reserved-pool bags present but never sellable, reported so they are not silently dropped."
                    }
                  }
                }
              }
            }
          },
          "402": {
            "description": "Payment Required. The request was not paid. Settle the x402 challenge, then retry the same POST.",
            "headers": {
              "PAYMENT-REQUIRED": {
                "description": "x402 payment challenge for this call.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/tellr-privilege-audit": {
      "post": {
        "operationId": "tellr-privilege-audit",
        "summary": "Audit a privilege-affecting event before honouring it.",
        "description": "Blocks privilege escalation that arrives as a gift, airdrop or unsolicited NFT — possession alone is never authority, an explicit user action is required. Pure decision logic: no keys, no wallet access, moves nothing.",
        "tags": [
          "security",
          "privilege",
          "airdrop",
          "nft",
          "escalation"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "description": "Event type, e.g. session_key_expand. Required."
                  },
                  "source": {
                    "type": "string",
                    "description": "Where it came from, e.g. unsolicited_nft_gift"
                  },
                  "explicitUserAction": {
                    "type": "boolean",
                    "description": "Whether the user explicitly acted. Default false."
                  }
                },
                "required": [
                  "type"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Decision or description. Returned only after the x402 payment settles.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "allowed": {
                      "type": "boolean",
                      "description": "Whether the event may be honoured"
                    },
                    "decision": {
                      "type": "string"
                    },
                    "reason": {
                      "type": "string",
                      "description": "e.g. privilege_change_from_gift_blocked"
                    },
                    "rule": {
                      "type": "string",
                      "description": "e.g. explicit_user_action_required"
                    },
                    "type": {
                      "type": "string",
                      "description": "The privilege event type that was audited, echoed back."
                    },
                    "source": {
                      "type": "string",
                      "description": "Where the event came from, e.g. unsolicited_nft_gift."
                    }
                  }
                }
              }
            }
          },
          "402": {
            "description": "Payment Required. The request was not paid. Settle the x402 challenge, then retry the same POST.",
            "headers": {
              "PAYMENT-REQUIRED": {
                "description": "x402 payment challenge for this call.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/tellr-classify": {
      "post": {
        "operationId": "tellr-classify",
        "summary": "Classify a holding as liquidity or growth for policy purposes.",
        "description": "Untagged holdings FAIL CLOSED to growth, the pool that requires confirmation, so missing information never becomes a permissive default. Pure decision logic: no keys, no wallet access, moves nothing.",
        "tags": [
          "classification",
          "portfolio",
          "policy",
          "fail-closed"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "symbol": {
                    "type": "string",
                    "description": "Token symbol. Required."
                  },
                  "declaredPool": {
                    "type": "string",
                    "description": "Caller's declaration, honoured if present. Three pools: liquidity (spendable, 75% cap), growth (sellable only with an explicit confirm), reserved (committed to a job - never sellable at any amount). An unrecognised value is refused, never coerced to liquidity.",
                    "enum": [
                      "liquidity",
                      "growth",
                      "reserved"
                    ]
                  },
                  "tagMap": {
                    "type": "object",
                    "description": "Optional symbol-to-pool map"
                  },
                  "liquidityWhitelist": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Optional symbols to treat as liquidity"
                  }
                },
                "required": [
                  "symbol"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Decision or description. Returned only after the x402 payment settles.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "symbol": {
                      "type": "string"
                    },
                    "pool": {
                      "type": "string",
                      "description": "liquidity or growth",
                      "enum": [
                        "liquidity",
                        "growth",
                        "reserved"
                      ]
                    },
                    "reason": {
                      "type": "string",
                      "description": "e.g. declared, manual_tag, default_growth_fail_closed"
                    }
                  }
                }
              }
            }
          },
          "402": {
            "description": "Payment Required. The request was not paid. Settle the x402 challenge, then retry the same POST.",
            "headers": {
              "PAYMENT-REQUIRED": {
                "description": "x402 payment challenge for this call.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/tellr-top-off": {
      "post": {
        "operationId": "tellr-top-off",
        "summary": "Decide whether a card top-off is worth making toward a target balance, and whether limits allow it.",
        "description": "Once the card is at or above target it returns skip, so it is idempotent and self-limiting. Shortfalls below the dust floor are skipped rather than requested. Describes an amount — it is not an authorisation and moves nothing.",
        "tags": [
          "top-off",
          "card",
          "funding",
          "payments",
          "treasury",
          "recurring"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "cardBalanceUsd": {
                    "type": "number",
                    "description": "Current card balance. Required."
                  },
                  "targetUsd": {
                    "type": "number",
                    "description": "Balance to top up toward. Default 50."
                  },
                  "enabled": {
                    "type": "boolean",
                    "description": "Opt-in flag. Default false; without it the call returns skip."
                  },
                  "minTransferUsd": {
                    "type": "number",
                    "description": "Dust floor. Default 1."
                  },
                  "dailySpentUsd": {
                    "type": "number"
                  },
                  "txCeilingUsd": {
                    "type": "number",
                    "description": "Default 150"
                  },
                  "dailyCapUsd": {
                    "type": "number",
                    "description": "Default 500"
                  },
                  "voiceConfirmed": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "cardBalanceUsd"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Decision or description. Returned only after the x402 payment settles.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "action": {
                      "type": "string",
                      "description": "request | confirm_required | deny | skip",
                      "enum": [
                        "request",
                        "confirm_required",
                        "deny",
                        "skip"
                      ]
                    },
                    "reason": {
                      "type": "string",
                      "description": "e.g. within_limits, at_or_above_target, top_off_disabled, below_min_transfer"
                    },
                    "amountUsd": {
                      "type": "number",
                      "description": "The top-off amount, or 0 when skipped"
                    },
                    "cardBalanceUsd": {
                      "type": "number"
                    },
                    "targetUsd": {
                      "type": "number"
                    },
                    "language": {
                      "type": "string",
                      "description": "Always 'descriptive_only'"
                    },
                    "disclaimer": {
                      "type": "string",
                      "description": "States that this is not an authorisation to move funds."
                    },
                    "enabled": {
                      "type": "boolean",
                      "description": "The opt-in flag as the engine read it. False yields action 'skip'."
                    },
                    "targetSource": {
                      "type": "string",
                      "description": "Where the target came from: owner (caller-supplied targetUsd always wins), derived (computed from a supplied report), or default (package floor).",
                      "enum": [
                        "owner",
                        "derived",
                        "default"
                      ]
                    },
                    "note": {
                      "type": "string",
                      "description": "Short human-readable note on the decision."
                    }
                  }
                }
              }
            }
          },
          "402": {
            "description": "Payment Required. The request was not paid. Settle the x402 challenge, then retry the same POST.",
            "headers": {
              "PAYMENT-REQUIRED": {
                "description": "x402 payment challenge for this call.",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  }
}
