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

# Build a compatible indexer

> The three endpoints, response schemas, pagination, and CORS your service needs.

<Card title="Download the OpenAPI contract" icon="download" href="/ctoken/indexer.openapi.json">OpenAPI 3.0 for assets, wallet transactions, and prices.</Card>

Implement these **three GET routes** relative to `indexer.url`. Additional [hosted endpoints](/ctoken/indexer-api) are optional.

| Route                             | Query parameters                                                            | Successful JSON body                                                                     |
| --------------------------------- | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `/wallets/{address}/assets`       | None                                                                        | Bare array of Asset records, including `[]` for no holdings.                             |
| `/wallets/{address}/transactions` | `page` defaults 1; `limit` defaults 10, range 1–100                         | TxPage object with `items`, `total`, `page`, `pages`, `limit`.                           |
| `/prices`                         | Optional `tokens`: comma-separated underlying ERC-20 addresses, at most 200 | Prices object for the client's chain. Omitted tokens requests your available priced set. |

The SDK appends paths to your base URL and lowercases addresses. Each URL must serve one chain: `chainId` 84532 for Base Sepolia or 8453 for Base. No chain query parameter is sent.

Return HTTP 200 JSON in the shapes below, without a `{ data: ... }` envelope. Unknown wallets return empty results. Use 400 for invalid requests, 429 for rate limits, and 503 for temporary failures. Allow your app’s origin through CORS for GET/OPTIONS.

Custom headers/fetch are unsupported. For authenticated backends, use a same-origin proxy that supplies server credentials.

Asset response example:

```json theme={null}
[
  {
    "address": "0x3333333333333333333333333333333333333333",
    "base_erc20": "0x036cbd53842c5426634e7929541ec2318f3dcf7e",
    "name": "Confidential USDC",
    "symbol": "cUSDC",
    "decimals": 6,
    "balance_handle": null,
    "handle_block": null,
    "last_activity_block": "123456"
  }
]
```

Transaction response example (one public wrap):

```json theme={null}
{
  "items": [
    {
      "type": "flow",
      "token": "0x3333333333333333333333333333333333333333",
      "symbol": "cUSDC",
      "decimals": 6,
      "kind": "wrap",
      "from_addr": "0x1111111111111111111111111111111111111111",
      "to_addr": "0x1111111111111111111111111111111111111111",
      "handle": null,
      "amount": "1000000",
      "block_number": "123456",
      "block_time": "1788825600",
      "log_index": 2,
      "tx_hash": "0x4444444444444444444444444444444444444444444444444444444444444444"
    }
  ],
  "total": 1,
  "page": 1,
  "pages": 1,
  "limit": 10
}
```

Confidential transfers use `type: "transfer"`, `kind: "transfer"`, a 32-byte `handle`, sender/recipient addresses, and `amount: null`. Public `flow` rows use `wrap`/`unwrap`, base-unit amount strings, and `handle: null`. Exclude zero-address confidential mint/burn legs and public `burn` rows. Sort by descending block/log index, count after filtering, and return `pages = Math.ceil(total / limit)`.

Price response example:

```json theme={null}
{
  "chainId": 84532,
  "ttl": 60,
  "count": 1,
  "prices": {
    "0x036cbd53842c5426634e7929541ec2318f3dcf7e": {
      "usd": 1,
      "confidence": null,
      "source": "your-price-source",
      "updated_at": 1788825600,
      "stale": false
    }
  }
}
```

Empty transactions: `{ "items": [], "total": 0, "page": 1, "pages": 0, "limit": 10 }`. Empty assets: `[]`. Empty prices: `{ "chainId": 84532, "ttl": 60, "count": 0, "prices": {} }`. Keep missing quotes absent from `prices`; zero is a real quote. `count` should equal the number of returned entries.

Follow the OpenAPI field types and nullability. Only Tx `block_time` and `log_index` may be omitted. Use 20-byte hex addresses, 32-byte hashes/handles, decimals 0–255, and decimal strings for amounts, blocks, and transaction timestamps. Return native JSON numbers for pagination and price values. Extra keys are ignored.

Validate all three SDK calls against your service, including empty results, pagination, missing quotes, 429 retries, aborts, malformed payloads, and chain mismatch.

## HTTP failure policy

Network errors, 429, and 5xx receive up to three attempts. Other 4xx and invalid payloads fail immediately. `Retry-After` waits are capped at five seconds within the total timeout. Aborts preserve the caller’s reason; timeouts raise `INDEXER_UNAVAILABLE`.
