> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useotto.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# X Layer API

> Use Otto X's data, recipes and managed execution accounts with X Layer payments.

**Base URL: [https://xlayer.ottoai.services](https://xlayer.ottoai.services)**

Otto X accepts x402 service payments on **X Layer, chain 196**, through the OKX Facilitator. It offers data reads, composed research recipes, unsigned DEX calldata and execution through a payer-bound sub-wallet. These are different operations: check the route before signing.

## Discover and pay

The [OpenAPI document](https://xlayer.ottoai.services/openapi.json) publishes methods, inputs and indicative prices. The [agent catalog](https://xlayer.ottoai.services/llm.txt), [x402 discovery](https://xlayer.ottoai.services/.well-known/x402), [token registry](https://xlayer.ottoai.services/tokens) and [supported protocols](https://xlayer.ottoai.services/supported-protocols) are free reads.

For licensed equity data (vendor-resolved tickers; no market-coverage claim), read each route's vendor, available fields and coverage limits in the agent catalog before paying. [API & agent coverage](/acp-swarm/storefront-parity) compares the interfaces; it does not establish coverage for a particular ticker.

```bash theme={null}
curl -i https://xlayer.ottoai.services/crypto-news
```

A paid route returns `402` with its `PAYMENT-REQUIRED` challenge. Use an OKX-compatible x402 signer to select the advertised token and scheme, sign, then retry the same request with `PAYMENT-SIGNATURE`. The service advertises USDT0, USDC and USDG options; read the token address, amount, network and EIP-712 domain from the chosen option. Do not assume `accepts[0]` is USDC. `exact` and `aggr_deferred` are distinct payment schemes; use only one supported by your client.

Service fees settle on X Layer even when an execution action uses another chain. The fee does not fund the principal you invest or trade. [API schemas and examples](/acp-swarm/x402-examples) shows how to inspect each request without paying.

## Choose an operation

| Route | Purpose and account |
| - | - |
| `POST /swap` | Quote and unsigned X Layer calldata for `userWalletAddress`; your wallet signs and broadcasts |
| `POST /auto-swap` | Execute from your Otto X sub-wallet; `payer`, `fromToken`, `toToken`, `amount`, `chain` |
| `POST /auto-bridge` | Bridge sub-wallet funds; source and destination chains must differ |
| `POST /auto-defi-invest` | Deposit into a specified allowlisted protocol/chain/token |
| `POST /auto-defi-withdraw` | Redeem a position into the sub-wallet; specify `ratio` or `amount` |
| `POST /auto-withdraw` | Transfer a specified token amount from the sub-wallet to `toAddress` |

Every `/auto-*` body names the paying wallet as `payer`, the address that signs the x402 payment. Otto checks that wallet's sub-wallet before it asks for payment; see [Fund before you call](#fund-before-you-call). This is a task index; the linked OpenAPI contains the complete body schemas. Most execution amounts are human-readable decimal strings. `principalAuth.value` and Yield Watch's `maxPrincipalRaw` use **atomic** token units. Slippage units differ by route: inspect the schema rather than reusing a swap percentage on a DeFi request.

**Yield Copilot is retired.** `POST /yield-copilot` was retired on 1 October 2026. It now answers `410 Gone` before any payment challenge, so nothing is charged. To deposit, name the venue with `POST /auto-defi-invest`. A deposit that Yield Copilot made is an ordinary lending position in your sub-wallet; exit it with the steps under [Yield deposits and withdrawals](#yield-deposits-and-withdrawals).

## Sub-wallet custody and funding

Otto X sub-wallets are **TEE-custodial**. Keys are held in OKX's Trusted Execution Environment and Otto's operator credential authorizes signing. You do not receive a seed phrase or directly control the execution key. This account is separate from the Otto app's connected wallet, Coinbase account and legacy Safe accounts.

`GET /sub-wallet` is a paid resolve-or-provision call bound to the verified x402 payer. An `address` query cannot select another payer. `GET /sub-wallet/preview?address=0x...` is a free lookup that never provisions; use its returned `sub_wallet.evm_address` and verify it before funding.

### Fund with a signed authorization

Where supported, include an EIP-3009 `principalAuth` alongside the separate x402 fee. The principal travels from your payment wallet directly into its sub-wallet on the action's chain. Supported principal tokens are USDC on Base, Arbitrum, Optimism, Ethereum and Polygon, and USDT0 on X Layer.

Example `/auto-swap` body shape; replace the addresses, future expiry, nonce and signature with your own signed values:

```json theme={null}
{
  "payer": "0xYOUR_PAYMENT_WALLET",
  "fromToken": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  "toToken": "0x4200000000000000000000000000000000000006",
  "amount": "10",
  "chain": "base",
  "principalAuth": {
    "from": "0xYOUR_PAYMENT_WALLET",
    "to": "0xYOUR_SUB_WALLET",
    "value": "10000000",
    "validAfter": 0,
    "validBefore": 0,
    "nonce": "0xYOUR_UNUSED_NONCE",
    "signature": "0xYOUR_SIGNATURE"
  }
}
```

`validBefore: 0` is a placeholder and must be replaced with a future Unix timestamp before signing. The sender must equal the verified payer; the recipient must equal its sub-wallet. The authorization is time-limited and nonce-protected. For auto-swap, auto-bridge and auto-defi-invest, `value` must cover the operation amount.

### Fund before you call

Before the payment challenge, Otto reads the sub-wallet of the `payer` you name. Create it first with `GET /sub-wallet` (\$0.01, paid by the payer). It must hold the operation's amount, or the request must carry a `principalAuth` signed by the payer for at least that amount, addressed to the sub-wallet, unexpired and backed by the payer's balance. A redeem needs a position, at least as large as a named amount. A spend of the chain's native coin (`"native"` or `0xeeee…eeee`) must also leave the chain's minimum gas balance in the sub-wallet, for example about 0.000002 ETH on Base or 0.00005 OKB on X Layer. Otherwise Otto answers before any payment: `409` with `NO_SUB_WALLET`, `FUNDS_REQUIRED`, `INSUFFICIENT_BALANCE`, `GAS_RESERVE_REQUIRED` (naming the reserve), `NOTHING_TO_REDEEM`, `POSITION_TOO_SMALL` or `PRINCIPAL_AUTH_UNFUNDED`, or `400` for a malformed or expired `principalAuth`, a zero amount or an address with no token contract. Nothing is signed or charged. `GET /sub-wallet/preview?address=<payer>` gives the deposit address for free. Deposit, wait about a minute for the deposit to be indexed, then send the same request again.

After payment, a payment signed by a wallet other than `payer` is refused with `403 PAYER_MISMATCH` and not settled.

### Fund after a balance request

If the balance changes between that check and execution, a supported operation can still return `409 FUNDS_REQUIRED`, with a deposit address, chain, token, required amount and `idempotency_key`. That response does not settle the x402 service fee. Check those details, deposit the requested principal, wait about a minute, then retry the original intent with the returned `idempotency-key` header; the operation then runs. The same operation sent without the header replaces the waiting one. If the response says `indexing_pending`, the deposit is already on-chain: wait about a minute and retry.

A retry under the `idempotency-key` of an operation that already succeeded gets its stored result before any payment challenge, free. If such a retry is answered after payment instead, it gets `409 ALREADY_SUCCEEDED` with the same `data`, and the payment is not settled.

Preserve returned transaction hashes and operation identifiers. A timeout after funding or broadcast is not proof that no funds moved; reconcile the original operation before creating another.

## Yield deposits and withdrawals

`POST /auto-defi-invest` deposits a stablecoin into the allowlisted protocol you name on the chain you choose. Read `/supported-protocols` for the current protocol × chain × token combinations. A venue that is temporarily disabled, currently Arbitrum lending deposits, is refused before payment. `GET /yield-recommendations` ranks the venues Otto deposits into and, for your own sub-wallet, suggests the exact `/auto-defi-invest` call for your largest idle stablecoin balance. X Layer may return `apy: null`. Ethereum execution can be declined during a base-fee spike.

The result includes the protocol, chain, token amount, sub-wallet and transaction hashes. A quoted APY is variable and projected yield is not realized profit. Lido's ETH staking is distinct from stablecoin lending.

`GET /yield-farming-active` returns current positions and rates; its rebalance suggestions do not execute a move. `GET /yield-farming-historical` returns recorded operations, fees and attributable realized results. A `null` realized value with a reason means attribution is incomplete, including some partial or externally funded positions.

To exit, including a position that Yield Copilot opened before its retirement:

1. Revoke any active Yield Watch policy as described below and confirm its paused/revoked state.
2. Redeem the position with `POST /auto-defi-withdraw`, for example `{"payer":"0xYOUR_PAYMENT_WALLET","protocol":"aave_v3","chain":"base","token":"USDC","ratio":"1"}` for a full position. On X Layer the token is `"USDT"` (or `0x779ded0c9e1022225f8e0630b35a9b54be713736`), not `"USDT0"`. Wait for confirmation and check the sub-wallet balance.
3. Transfer the resulting balance with `POST /auto-withdraw`, supplying `payer`, `chain`, token contract (or `"native"`), human-readable `amount` and your `toAddress`. Repeat for other tokens or chains as needed.

Revocation alone does not redeem a position. Redemption alone does not transfer funds to your external wallet. Protocol liquidity and service availability can delay an exit. A two-leg rebalance incurs the withdrawal and deposit fees; if the second leg fails, inspect the first leg and the idle sub-wallet balance before retrying.

## Yield Watch

`POST /yield-watch` registers **notify** or **auto** mode. Notify sends Telegram alerts without trading. Auto grants a signed permission for at most 30 days to rebalance between Aave V3 and Compound V3; Morpho is not accepted in auto mode. Read the current schema for supported chains, tokens and policy floors.

The signed policy binds wallet, sub-wallet, scope, expiry and nonce. `maxMoves` caps the number of moves; positive `maxPrincipalRaw` caps live position size per move. **`"0"` means no principal cap.** Hysteresis, minimum hold, throttle, fresh APY data, TVL checks and confirmed transaction receipts constrain execution. They do not guarantee returns or prevent market loss. Registering a policy is not evidence that a rebalance completed.

Use paid `GET /yield-watch` for state and `GET /yield-watch/audit?limit=50` for events and transaction hashes. Read failed phases to determine whether capital remains in the old venue or idle in the sub-wallet. Bookkeeping calls currently cost \$0.001; execution legs carry their own fees. Check each challenge.

**Revoke with `DELETE /yield-watch`:** both modes require the verified payer and a signed `YieldWatchRevocation` body. Use the current next nonce from the status response. Confirm the returned `mode`, `paused` and advanced nonce. A stale signature cannot revoke or reactivate a later policy.

<Accordion title="Exact Yield Watch signing contract">
  Use EIP-712 typed data, with domain:

  ```json theme={null}
  {
    "name": "Otto X Yield Watch",
    "version": "1",
    "chainId": 1,
    "verifyingContract": "0x0000000000000000000000000000000000000000"
  }
  ```

  This is an off-chain authority domain; its chain ID is not the execution chain. Registration's primary type is `YieldWatchDelegation`, with fields in this order:

  | Field | EIP-712 type |
  | - | - |
  | `wallet`, `subWallet` | `address`, `address` |
  | `mode` | `string` (`auto`) |
  | `chainIds`, `tokens`, `protocols` | `uint256[]`, `string[]`, `string[]` |
  | `hysteresisPctBps`, `hysteresisHours`, `throttleHours`, `minHoldDays` | `uint256` each |
  | `maxMoves`, `maxPrincipalRaw`, `nonce`, `issuedAt`, `expiry` | `uint256` each |

  Arrays must be sorted and unique. POST the signed `message` and `signature` with `mode: "auto"` and `telegramChatId`. Notify registration uses the schema's flat fields and verified payer, without a delegation signature.

  Revocation's primary type is **`YieldWatchRevocation`**, with ordered fields `wallet: address`, `subWallet: address`, `nonce: uint256`, `issuedAt: uint256`. DELETE its `{ "message": {...}, "signature": "0x..." }` body while paying from that same wallet. Use a fresh Unix `issuedAt`. A registration signature is not a revocation signature. The verifier uses EIP-712 even if a discovery description calls the signature EIP-191.
</Accordion>

## Research recipes

Recipes combine several data sources into one result and one payment. Use the free [recipe index](https://xlayer.ottoai.services/api/recipes) for current inputs and prices:

| Recipe | Result |
| - | - |
| `/vet-token` | X Layer token security, concentration and tradeability |
| `/token-deep-dive` | Token metrics, security, derivatives and social context |
| `/rwa-pulse` | Real-world-asset market context |
| `/mega-report` | Daily market briefing |

A missing core result causes a recipe error without settling the service fee. Unavailable best-effort sources are identified in the result. A token screen is evidence to inspect, not a safety guarantee or instruction to trade.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.