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

# tRPC Asset Search API

> Public asset search procedure with geo-restriction context

The asset search procedure is available on the tRPC base endpoint:

* Base endpoint: `POST /v1/trpc`
* Procedure: `assets.searchForTokens`
* Auth: public
* Rate tier: `Tier2`

The command-palette asset search procedure shares the same endpoint:

* Procedure: `assets.searchForCommandPalette`
* Auth: public
* Rate tier: `Tier2`
* Optional input: `includeAiSearch`
* Optional input: `aiSearchLimit`

When `includeAiSearch` is true and platform-api is configured with Cloudflare AI
Search credentials, the response includes an additive `aiSearchResults` array.
These results are normalized command targets from Cloudflare AI Search chunks:

* `kind`: `asset`, `stock`, `portfolio`, `strategy`, `action`, `document`, or
  `unknown`
* `title`, `subtitle`, `description`
* `href`: internal Glider path when the result is directly navigable
* `assetId`, `chainId`, `tokenAddress`, `symbol` when present in indexed metadata
* `sourceInstanceId`, `sourceKey`, and `score` for debugging/ranking visibility

The AI Search output is treated as optional recall/ranking metadata. Callers must
continue using the authoritative database-backed result fields for trading,
valuation, restrictions, and portfolio access control.

The initial public index is populated by the internal `asset-data-sync` worker:

* `POST /api/command-palette/ai-search/enqueue`
* `POST /api/command-palette/ai-search/sync`

Those routes load public RWA/stock command documents from Postgres, embed
structured command metadata into the document body, and upload the documents to
Cloudflare AI Search built-in storage. The upload path does not index private
portfolio holdings, user memory, balances, or activity.

## Response Notes

`assets.searchForTokens` returns additive restriction metadata alongside the search
results:

* optional input: `manualCountryCode`
  * intended source: `system.ipDetection`
  * behavior: evaluated in addition to the Cloudflare request country
* `restrictedAssetsMap`: geoblocking result by `assetId`
* `restrictionContext.countryCode`: normalized country code used for restriction
  evaluation
* `restrictionContext.regionCode`: normalized region code used for restriction
  evaluation
* `restrictionContext.manualCountryCode`: normalized manual country code used for
  additive restriction checks
* `restrictionContext.cloudflare`: normalized Cloudflare geo metadata used to derive
  the country and region context
* `restrictionContext.ipDebug`: raw IP-related request headers plus the derived
  client IP, country code, and region code used by the backend

Cloudflare is the only geo source of truth for this procedure. `restrictionContext.ipDebug`
is returned specifically for support and debugging so callers can compare the raw
forwarded headers against the derived values the backend used. If `manualCountryCode`
is provided, the backend evaluates both countries and keeps the stricter restriction
result.

## Cloudflare Geo Fields

`restrictionContext.cloudflare` includes:

* `city`
* `continent`
* `countryCode`
* `latitude`
* `longitude`
* `metroCode`
* `postalCode`
* `region`
* `regionCode`
* `timezone`

## IP Debug Fields

`restrictionContext.ipDebug` includes:

* `rawHeaders.cf-connecting-ip`
* `rawHeaders.x-forwarded-for`
* `rawHeaders.x-real-ip`
* `rawHeaders.cf-ipcountry`
* `rawHeaders.cf-region-code`
* `rawHeaders.cf-ipcity`
* `rawHeaders.cf-ipcontinent`
* `rawHeaders.cf-iplatitude`
* `rawHeaders.cf-iplongitude`
* `rawHeaders.cf-region`
* `rawHeaders.cf-metro-code`
* `rawHeaders.cf-postal-code`
* `rawHeaders.cf-timezone`
* `derivedClientIp`
* `derivedCountryCode`
* `derivedRegionCode`

## Example Response Shape

```json theme={null}
{
  "tokenSearchResults": [
    {
      "assetId": "eip155:1/erc20:0x...",
      "token": {
        "address": "0x...",
        "decimals": 18,
        "name": "Example",
        "networkId": 1,
        "symbol": "EXAMPLE"
      }
    }
  ],
  "restrictedAssetsMap": {
    "eip155:1/erc20:0x...": {
      "isRestricted": true,
      "reason": "country",
      "countryCode": "RU",
      "jurisdiction": "RU",
      "message": "This tokenized equity is not available in Russia."
    }
  },
  "restrictionContext": {
    "countryCode": "RU",
    "regionCode": "MOW",
    "manualCountryCode": "RU",
    "cloudflare": {
      "city": "Moscow",
      "continent": "EU",
      "countryCode": "RU",
      "latitude": "55.7558",
      "longitude": "37.6173",
      "metroCode": "0",
      "postalCode": "101000",
      "region": "Moscow",
      "regionCode": "MOW",
      "timezone": "Europe/Moscow"
    },
    "ipDebug": {
      "rawHeaders": {
        "cf-connecting-ip": "203.0.113.7",
        "x-forwarded-for": "203.0.113.7, 10.0.0.4",
        "x-real-ip": "203.0.113.7",
        "cf-ipcountry": "RU",
        "cf-region-code": "MOW",
        "cf-ipcity": "Moscow",
        "cf-ipcontinent": "EU",
        "cf-iplatitude": "55.7558",
        "cf-iplongitude": "37.6173",
        "cf-region": "Moscow",
        "cf-metro-code": "0",
        "cf-postal-code": "101000",
        "cf-timezone": "Europe/Moscow"
      },
      "derivedClientIp": "203.0.113.7",
      "derivedCountryCode": "RU",
      "derivedRegionCode": "MOW"
    }
  }
}
```
