FortisNet Intelligence API

Version 0.1.0 · OpenAPI 3.0.3 · Read-only reference

Read-side API for the FortisWallet self-custody client. The service enriches
public blockchain data with balances, history, NFTs, market data, charts and
exchange quotes while keeping provider credentials on the server.

Signing, key management, transaction construction/broadcast and keyless
current-balance fallback remain client-side and are deliberately not part of
this API. No wallet seed or private key may ever be sent here.

All `/v1` operations except enrollment and media require a short-lived device
bearer token. Obtain it through the attestation challenge and device enrollment
flow. This browser documentation is read-only; use a native client or `curl` for
authenticated requests. Production enrollment requires Apple App Attest or Play
Integrity; development enrollment is private-network only.

Cache-aware responses expose `cache.hit` and `cache.timestamp`. A response may
also set `cache.partial` when only part of a requested time window was already
cached. Historical-price responses include `availability` so clients can draw
the portion that exists when an upstream source has incomplete history.

System 12

GET/

Human-facing landing page

Public HTML landing page with current network/capability counts and operational status. Not consumed by the app.

Operation: landingPage · Security: public

Responses
{
  "200": {
    "description": "Landing page HTML.",
    "content": {
      "text/html": {
        "schema": {
          "type": "string"
        }
      }
    }
  }
}

GET/statusz

Public liveness detail for the landing page

Version, runtime, uptime, process CPU, process memory, storage-volume usage and live-traffic aggregates (request/error/distinct-client counts over 5m/10m windows plus a 15-minute per-minute series, from an in-memory ring). Polled by the landing page; discloses only aggregate counts and route templates — never device identifiers, addresses, data or keys. The TypeScript server reports its Node.js version in `node` and V8 process memory. The native C++ server (host build or Unikraft guest) reports a native runtime label in `node` (never a Node.js version) and reports memory values as null where they cannot be measured; it does not yet emit the optional coordinator and tokenRefresh blocks.

Operation: status · Security: public

Responses
{
  "200": {
    "description": "Process status snapshot.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "ok",
            "version",
            "node",
            "uptimeSec",
            "memory",
            "cpu",
            "disk",
            "stats"
          ],
          "properties": {
            "ok": {
              "type": "boolean"
            },
            "version": {
              "type": "string"
            },
            "node": {
              "type": "string",
              "description": "Runtime identifier: the Node.js version on the TypeScript server; a native runtime label (`native-cpp` on the host, `native-cpp-unikraft` in the guest) on the native server."
            },
            "uptimeSec": {
              "type": "integer"
            },
            "memory": {
              "type": "object",
              "required": [
                "rss",
                "heapUsed"
              ],
              "properties": {
                "rss": {
                  "type": "integer",
                  "nullable": true,
                  "description": "Resident set size in bytes. Always an integer on the TypeScript server; null on the native server when the platform exposes no /proc/self/statm (the Unikraft guest)."
                },
                "heapUsed": {
                  "type": "integer",
                  "nullable": true,
                  "description": "Heap bytes in use. TypeScript reports V8 heapUsed. The native guest reports bytes currently allocated through the Unikraft default allocator (allocations it tracks, not every library heap or thread stack); null where no allocator statistics exist (the native host build)."
                }
              }
            },
            "cpu": {
              "type": "object",
              "required": [
                "percent"
              ],
              "properties": {
                "percent": {
                  "type": "number",
                  "nullable": true,
                  "description": "Process CPU time as a percent of one logical core since the previous one-second sample. Null until a second sample is available or CPU time is unavailable. Multi-core work may exceed 100 percent."
                }
              }
            },
            "disk": {
              "type": "object",
              "nullable": true,
              "required": [
                "used",
                "total"
              ],
              "description": "Filesystem containing the persistent KV store. On a guest mount without capacity reporting, used is a bounded scan of persisted files and total is null. The scan refreshes at most once per 30 seconds. Null if measurement fails.",
              "properties": {
                "used": {
                  "type": "integer",
                  "format": "int64",
                  "description": "Allocated bytes on the filesystem."
                },
                "total": {
                  "type": "integer",
                  "format": "int64",
                  "nullable": true,
                  "description": "Total filesystem bytes when available."
                }
              }
            },
            "stats": {
              "type": "object",
              "required": [
                "window5m",
                "window10m",
                "topRoutes10m",
                "perMinute"
              ],
              "properties": {
                "window5m": {
                  "$ref": "#/components/schemas/TrafficWindow"
                },
                "window10m": {
                  "$ref": "#/components/schemas/TrafficWindow"
                },
                "topRoutes10m": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "required": [
                      "route",
                      "requests",
                      "errors"
                    ],
                    "properties": {
                      "route": {
                        "type": "string",
                        "description": "Route template, never a raw URL."
                      },
                      "requests": {
                        "type": "integer"
                      },
                      "errors": {
                        "type": "integer"
                      }
                    }
                  }
                },
                "perMinute": {
                  "type": "array",
                  "description": "One point per tracked minute, oldest first.",
                  "items": {
                    "type": "object",
                    "required": [
                      "startSec",
                      "requests",
                      "errors",
                      "clients"
                    ],
                    "properties": {
                      "startSec": {
                        "type": "integer",
                        "description": "Start of the minute, epoch seconds."
                      },
                      "requests": {
                        "type": "integer"
                      },
                      "errors": {
                        "type": "integer"
                      },
                      "clients": {
                        "type": "integer"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

GET/metrics

Protected Prometheus metrics

Operational counters with bounded route-template labels. Disabled with 404 unless METRICS_TOKEN is configured; then requires that dedicated bearer. Never emits addresses, device ids, cache keys or dynamic hosts.

Operation: prometheusMetrics · Security: public

Responses
{
  "200": {
    "description": "Prometheus text exposition.",
    "content": {
      "text/plain": {
        "schema": {
          "type": "string"
        }
      }
    }
  },
  "401": {
    "description": "Missing or invalid metrics bearer."
  },
  "404": {
    "description": "Metrics disabled for this deployment."
  }
}

GET/errors

Human-facing error board

Public HTML page listing server-side (5xx) responses per service over the tracked window, with a per-service threshold flag. Linked from the landing page's Service box. Not consumed by the app.

Operation: errorsPage · Security: public

Responses
{
  "200": {
    "description": "Error board HTML.",
    "content": {
      "text/html": {
        "schema": {
          "type": "string"
        }
      }
    }
  }
}

GET/errorsz

Live data for the error board

Aggregated 5xx counts per route template with service attribution and a bounded newest-first log of recent errors. Same disclosure level as /statusz — counts, templates and HTTP status codes only.

Operation: errorsFeed · Security: public

Responses
{
  "200": {
    "description": "Error aggregates snapshot.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "ok",
            "threshold",
            "windowMinutes",
            "totalErrors",
            "totalRequests",
            "routes",
            "recent"
          ],
          "properties": {
            "ok": {
              "type": "boolean"
            },
            "threshold": {
              "type": "integer",
              "description": "Errors per route in the window at which a service is flagged."
            },
            "windowMinutes": {
              "type": "integer"
            },
            "totalErrors": {
              "type": "integer"
            },
            "totalRequests": {
              "type": "integer"
            },
            "routes": {
              "type": "array",
              "items": {
                "type": "object",
                "required": [
                  "service",
                  "route",
                  "requests",
                  "errors",
                  "overThreshold"
                ],
                "properties": {
                  "service": {
                    "type": "string"
                  },
                  "route": {
                    "type": "string",
                    "description": "Route template, never a raw URL."
                  },
                  "requests": {
                    "type": "integer"
                  },
                  "errors": {
                    "type": "integer"
                  },
                  "overThreshold": {
                    "type": "boolean"
                  }
                }
              }
            },
            "recent": {
              "type": "array",
              "items": {
                "type": "object",
                "required": [
                  "sec",
                  "service",
                  "route",
                  "status"
                ],
                "properties": {
                  "sec": {
                    "type": "integer",
                    "description": "Epoch seconds of the response."
                  },
                  "service": {
                    "type": "string"
                  },
                  "route": {
                    "type": "string"
                  },
                  "status": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

GET/assets/background.jpg

Landing page hero background image

Static JPEG used by the landing page. Served with a one-day public cache.

Operation: landingBackground · Security: public

Responses
{
  "200": {
    "description": "JPEG bytes.",
    "content": {
      "image/jpeg": {
        "schema": {
          "type": "string",
          "format": "binary"
        }
      }
    }
  },
  "404": {
    "description": "The image asset was not shipped with this deployment."
  }
}

GET/healthz

Check server liveness

Lightweight process health check. It does not verify every upstream provider.

Operation: health · Security: public

Responses
{
  "200": {
    "description": "Server process is accepting requests.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "ok"
          ],
          "properties": {
            "ok": {
              "type": "boolean",
              "example": true
            }
          }
        }
      }
    }
  }
}

GET/v1/media/{key}

Read a verified cached image

Public content-addressed image proxy. Only URLs previously registered by the server can resolve; responses are raster-byte verified and immutable.

Operation: getMedia · Security: public

Parameters
[
  {
    "name": "key",
    "in": "path",
    "required": true,
    "description": "32-character lowercase hexadecimal media key.",
    "schema": {
      "type": "string",
      "pattern": "^[0-9a-f]{32}$"
    }
  }
]
Responses
{
  "200": {
    "description": "Verified image bytes. The exact raster media type is returned in Content-Type.",
    "headers": {
      "Cache-Control": {
        "schema": {
          "type": "string",
          "example": "public, max-age=31536000, immutable"
        }
      }
    },
    "content": {
      "image/png": {
        "schema": {
          "type": "string",
          "format": "binary"
        }
      },
      "image/jpeg": {
        "schema": {
          "type": "string",
          "format": "binary"
        }
      },
      "image/webp": {
        "schema": {
          "type": "string",
          "format": "binary"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  }
}

GET/doc

Interactive API reference (native C++ server)

Public HTML documentation generated from this OpenAPI specification. The native server also serves the same page at `/documentation` and the source at `/openapi.yaml`.

Operation: apiDocumentation · Security: public

Responses
{
  "200": {
    "description": "API reference HTML.",
    "content": {
      "text/html": {
        "schema": {
          "type": "string"
        }
      }
    }
  }
}

GET/documentation

Alias of /doc (native C++ server)

Public HTML reference with the same response as `/doc`.

Operation: apiDocumentationAlias · Security: public

Responses
{
  "200": {
    "description": "API reference HTML.",
    "content": {
      "text/html": {
        "schema": {
          "type": "string"
        }
      }
    }
  }
}

GET/openapi.yaml

Download the native OpenAPI specification

Public YAML contract used to generate `/doc`, including the native-only operations.

Operation: openApiSource · Security: public

Responses
{
  "200": {
    "description": "OpenAPI YAML document.",
    "content": {
      "application/yaml": {
        "schema": {
          "type": "string"
        }
      }
    }
  }
}

GET/status-history

Eight hours of aggregate process and API traffic history (native C++ server)

Public, process-local ring of exactly 480 chronological one-minute slots. A background thread samples CPU and memory every five seconds even with no page open. Request counts cover API-scope `/v1` responses; status and documentation reads are excluded. Missing measurements are null, quiet request minutes are zero, and a server restart starts a fresh ring. No device identifiers, addresses or request bodies appear.

Operation: statusHistory · Security: public

Responses
{
  "200": {
    "description": "Bounded aggregate history.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "slotDurationSec",
            "maxMinutes",
            "slots"
          ],
          "properties": {
            "slotDurationSec": {
              "type": "integer",
              "enum": [
                60
              ]
            },
            "maxMinutes": {
              "type": "integer",
              "enum": [
                480
              ]
            },
            "slots": {
              "type": "array",
              "minItems": 480,
              "maxItems": 480,
              "items": {
                "type": "object",
                "required": [
                  "created",
                  "duration",
                  "memoryMb",
                  "cpuPercent",
                  "requests"
                ],
                "properties": {
                  "created": {
                    "type": "integer",
                    "description": "UTC epoch seconds at the start of the slot."
                  },
                  "duration": {
                    "type": "integer",
                    "enum": [
                      60
                    ]
                  },
                  "memoryMb": {
                    "nullable": true,
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/ResourceRange"
                      }
                    ]
                  },
                  "cpuPercent": {
                    "nullable": true,
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/ResourceRange"
                      }
                    ]
                  },
                  "requests": {
                    "type": "object",
                    "required": [
                      "total",
                      "errors",
                      "avgDurationMs",
                      "maxDurationMs"
                    ],
                    "properties": {
                      "total": {
                        "type": "integer"
                      },
                      "errors": {
                        "type": "integer"
                      },
                      "avgDurationMs": {
                        "type": "number"
                      },
                      "maxDurationMs": {
                        "type": "number"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

Support 9

GET/admin/tickets

Operator ticket list (HTML)

HTML view of every help-desk ticket, with one thread expanded via `?open={ticketId}`. Root-scoped, so no device bearer applies. The route is disabled unless SUPPORT_ADMIN_TOKEN is configured and then requires that dedicated credential. STAGING ONLY until wallet-admin's staff identity, RBAC and audit trail move here.

Operation: ticketsPage · Security: supportAdminBasic

Parameters
[
  {
    "name": "open",
    "in": "query",
    "required": false,
    "schema": {
      "type": "string"
    },
    "description": "Ticket id whose thread to render above the list."
  }
]
Responses
{
  "200": {
    "description": "Ticket list HTML.",
    "content": {
      "text/html": {
        "schema": {
          "type": "string"
        }
      }
    }
  }
}

POST/admin/tickets/{id}/reply

Reply to a ticket from the staging console

Requires SUPPORT_ADMIN_TOKEN (HTTP Basic password or bearer). Staging bridge only; production must use the authenticated wallet-admin proxy. Outbound secret requests, unknown links and secret material are rejected before persistence.

Operation: replyToTicketFromStagingConsole · Security: supportAdminBasic

Parameters
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string"
    }
  }
]
Request body
{
  "required": true,
  "content": {
    "application/x-www-form-urlencoded": {
      "schema": {
        "type": "object",
        "required": [
          "body"
        ],
        "properties": {
          "body": {
            "type": "string",
            "minLength": 1,
            "maxLength": 8000
          }
        }
      }
    }
  }
}
Responses
{
  "303": {
    "description": "Reply stored; redirects back to the thread."
  },
  "422": {
    "description": "Outbound safety policy blocked the reply."
  }
}

POST/admin/tickets/{id}/status

Update a ticket's lifecycle state

Operation: setTicketStatusFromStagingConsole · Security: supportAdminBasic

Parameters
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string"
    }
  }
]
Request body
{
  "required": true,
  "content": {
    "application/x-www-form-urlencoded": {
      "schema": {
        "type": "object",
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "open",
              "pending",
              "resolved",
              "closed"
            ]
          }
        }
      }
    }
  }
}
Responses
{
  "303": {
    "description": "Status stored; redirects back to the thread."
  }
}

POST/v1/support/auth/verify

Verify a client-created wallet proof and start a support session

The client signs an EIP-191 message containing exactly Address, Nonce,
and Timestamp. The timestamp must be at most five minutes old and the
random 128-bit nonce is consumed atomically on successful verification.

Operation: verifySupportIdentity · Security: deviceBearer

Request body
{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "address",
          "nonce",
          "timestamp",
          "signature"
        ],
        "properties": {
          "address": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$"
          },
          "nonce": {
            "type": "string",
            "pattern": "^[0-9a-f]{32}$"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "signature": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{130}$"
          }
        }
      }
    },
    "application/protobuf": {
      "schema": {
        "type": "string",
        "format": "binary",
        "description": "fortisnet.intelligence.v1.SupportVerifyRequest"
      }
    }
  }
}
Responses
{
  "200": {
    "description": "A short-lived access token and rotating refresh token.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/SupportAuthSession"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.SupportAuthResponse"
        }
      }
    }
  },
  "401": {
    "description": "The proof is stale, already used, future-dated, or the signature does not match."
  }
}

POST/v1/support/auth/refresh

Rotate a support refresh token

Operation: refreshSupportIdentity · Security: deviceBearer

Request body
{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "refreshToken"
        ],
        "properties": {
          "refreshToken": {
            "type": "string",
            "minLength": 32,
            "maxLength": 512,
            "writeOnly": true
          }
        }
      }
    },
    "application/protobuf": {
      "schema": {
        "type": "string",
        "format": "binary",
        "description": "fortisnet.intelligence.v1.SupportRefreshRequest"
      }
    }
  }
}
Responses
{
  "200": {
    "description": "Replacement access and refresh tokens. The submitted refresh token is invalidated.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/SupportAuthSession"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.SupportAuthResponse"
        }
      }
    }
  },
  "401": {
    "description": "Refresh token missing, expired, already rotated, or issued to another device."
  }
}

POST/v1/support/tickets

Open a support ticket

Files a ticket for the authenticated wallet. Subject and body are
scanned for secret material and redacted **before persistence**; when a
rule fires, `secretRemoved` is true. Security warning text is compiled
into the app and is never supplied by this service.

Operation: openSupportTicket · Security: deviceBearer, supportSession

Request body
{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/OpenTicketRequest"
      }
    },
    "application/protobuf": {
      "schema": {
        "type": "string",
        "format": "binary",
        "description": "fortisnet.intelligence.v1.OpenSupportTicketRequest"
      }
    }
  }
}
Responses
{
  "201": {
    "description": "Ticket opened.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/TicketThread"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.SupportThreadResponse"
        }
      }
    }
  },
  "400": {
    "description": "Validation or policy failure."
  }
}

GET/v1/support/tickets

This verified wallet's tickets

Operation: listSupportTickets · Security: deviceBearer, supportSession

Responses
{
  "200": {
    "description": "Tickets, newest activity first.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "tickets"
          ],
          "properties": {
            "tickets": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/SupportTicket"
              }
            },
            "unreadCount": {
              "type": "integer",
              "minimum": 0
            }
          }
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.SupportTicketsResponse"
        }
      }
    }
  }
}

GET/v1/support/tickets/{id}

One thread this verified wallet owns

Returns 404 rather than 403 for another wallet's ticket: the distinction would confirm the id exists.

Operation: getSupportTicket · Security: deviceBearer, supportSession

Parameters
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string"
    }
  }
]
Responses
{
  "200": {
    "description": "The thread.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/TicketThread"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.SupportThreadResponse"
        }
      }
    }
  },
  "404": {
    "description": "No such ticket for this wallet."
  }
}

POST/v1/support/tickets/{id}/messages

Reply in a thread this verified wallet owns

Operation: postSupportMessage · Security: deviceBearer, supportSession

Parameters
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string"
    }
  }
]
Request body
{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "required": [
          "body"
        ],
        "properties": {
          "body": {
            "type": "string",
            "minLength": 1,
            "maxLength": 8000
          }
        }
      }
    },
    "application/protobuf": {
      "schema": {
        "type": "string",
        "format": "binary",
        "description": "fortisnet.intelligence.v1.PostSupportMessageRequest"
      }
    }
  }
}
Responses
{
  "201": {
    "description": "Message stored (post-redaction).",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "message",
            "secretRemoved"
          ],
          "properties": {
            "message": {
              "$ref": "#/components/schemas/SupportMessage"
            },
            "secretRemoved": {
              "type": "boolean",
              "description": "True when secret material was removed before persistence."
            }
          }
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.PostSupportMessageResponse"
        }
      }
    }
  },
  "400": {
    "description": "Empty, too long, or the conversation is closed."
  },
  "404": {
    "description": "No such ticket for this wallet."
  }
}

Device authentication 3

POST/v1/devices/challenge

Create a one-time attestation challenge

Operation: createAttestationChallenge · Security: public

Responses
{
  "200": {
    "description": "Challenge valid for five minutes.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/AttestationChallenge"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.AttestationChallengeResponse"
        }
      }
    }
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}

POST/v1/devices

Enroll an app installation

Returns the bearer token exactly once. Development enrollment is accepted only from loopback or a private network; production must validate platform attestation.

Operation: enrollDevice · Security: public

Request body
{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/DeviceEnrollment"
      }
    },
    "application/protobuf": {
      "schema": {
        "type": "string",
        "format": "binary",
        "description": "fortisnet.intelligence.v1.EnrollDeviceRequest"
      }
    }
  }
}
Responses
{
  "201": {
    "description": "Device enrolled; store `deviceToken` in platform secure storage.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "deviceId",
            "deviceToken",
            "expiresAt"
          ],
          "properties": {
            "deviceId": {
              "type": "string"
            },
            "deviceToken": {
              "type": "string",
              "writeOnly": true,
              "example": "fintel_dev_..."
            },
            "expiresAt": {
              "type": "string",
              "format": "date-time"
            }
          }
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.EnrollDeviceResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "403": {
    "$ref": "#/components/responses/Forbidden"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}

POST/v1/devices/rotate

Replace the current device bearer token

The old token becomes invalid immediately. This endpoint does not rotate third-party provider keys.

Operation: rotateDeviceToken · Security: deviceBearer

Responses
{
  "200": {
    "description": "Replacement token.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "deviceToken",
            "expiresAt"
          ],
          "properties": {
            "deviceToken": {
              "type": "string",
              "writeOnly": true
            },
            "expiresAt": {
              "type": "string",
              "format": "date-time"
            }
          }
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.RotateDeviceTokenResponse"
        }
      }
    }
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  }
}

Diagnostics 1

POST/v1/crash-reports

Submit a redacted client crash report

Accepts a bounded Flutter or root-isolate error report for the authenticated
installation. The client excludes wallet state and request data; both client
and server redact bearer tokens, long address-like values and user-directory
names. Reports expire after 30 days. There is intentionally no client read API.

Operation: submitCrashReport · Security: deviceBearer

Request body
{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/CrashReportRequest"
      }
    },
    "application/protobuf": {
      "schema": {
        "type": "string",
        "format": "binary",
        "description": "fortisnet.intelligence.v1.CrashReportRequest"
      }
    }
  }
}
Responses
{
  "202": {
    "description": "Report accepted for operational diagnosis.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "accepted",
            "id"
          ],
          "properties": {
            "accepted": {
              "type": "boolean",
              "example": true
            },
            "id": {
              "type": "string"
            }
          }
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.CrashReportResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  }
}

AI 3

POST/v1/ai/conversations

Open a wallet-signed conversation with its first message

The client proves control of an EVM address by signing (EIP-191) the
text `FortisNet Intelligence conversation v1` followed by the address,
device id, nonce, timestamp, snapshot SHA-256 and session-token SHA-256,
one per line. The portfolio snapshot is stored with the conversation.
The user turn is committed before the server generates and commits an
assistant turn. The model API key remains on the server.
Retrying with the same `clientMessageId` returns the existing
conversation. Limited to 20 opens per device per hour; conversations
expire after 7 days.

Operation: openIntelligenceConversation · Security: deviceBearer

Request body
{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "address",
          "nonce",
          "timestamp",
          "snapshotHash",
          "accessToken",
          "accessTokenHash",
          "snapshotJson",
          "signature",
          "firstMessage",
          "clientMessageId"
        ],
        "properties": {
          "address": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$"
          },
          "nonce": {
            "type": "string",
            "pattern": "^[0-9a-f]{32}$"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "snapshotHash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "accessToken": {
            "type": "string",
            "pattern": "^fintel_chat_[A-Za-z0-9_-]{43}$"
          },
          "accessTokenHash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "snapshotJson": {
            "type": "string",
            "minLength": 2,
            "maxLength": 300000
          },
          "signature": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{130}$"
          },
          "firstMessage": {
            "type": "string",
            "minLength": 1,
            "maxLength": 8000
          },
          "clientMessageId": {
            "type": "string",
            "pattern": "^[0-9a-f]{32}$"
          }
        }
      }
    },
    "application/protobuf": {
      "schema": {
        "type": "string",
        "format": "binary",
        "description": "fortisnet.intelligence.v1.OpenConversationRequest"
      }
    }
  }
}
Responses
{
  "201": {
    "description": "Conversation opened (or the existing one for this first message).",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "id",
            "accessToken",
            "expiresAt",
            "message",
            "assistant"
          ],
          "properties": {
            "id": {
              "type": "string"
            },
            "accessToken": {
              "type": "string"
            },
            "expiresAt": {
              "type": "string",
              "format": "date-time"
            },
            "message": {
              "type": "object",
              "required": [
                "id",
                "body",
                "createdAt"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "body": {
                  "type": "string"
                },
                "createdAt": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            },
            "assistant": {
              "$ref": "#/components/schemas/IntelligenceMessage"
            }
          }
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.OpenConversationResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "description": "Missing device token, or the signature/snapshot proof is invalid (`PROOF_INVALID`)."
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  },
  "503": {
    "description": "The user turn is saved, but the model reply is unavailable. Retry the same proof and client message ID."
  }
}

GET/v1/ai/conversations/{id}/messages

Messages of a conversation this device owns

Operation: listIntelligenceMessages · Security: deviceBearer, intelligenceConversation

Responses
{
  "200": {
    "description": "Messages, oldest first.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "messages"
          ],
          "properties": {
            "messages": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/IntelligenceMessage"
              }
            }
          }
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.ConversationMessagesResponse"
        }
      }
    }
  },
  "404": {
    "description": "Unknown, expired, or not this device's conversation (never distinguished)."
  }
}

POST/v1/ai/conversations/{id}/messages

Save a user turn and generate a reply in this device's conversation

Idempotent per `clientMessageId`. Supply both `snapshotJson` and
`snapshotHash` when refreshing the portfolio context; otherwise the
server reuses the conversation's last snapshot. The snapshot used for
this turn is saved with it before generating a reply. The mobile app
refreshes local context on the first message after ten minutes.
Limited to 120 messages per device per hour.

Operation: postIntelligenceMessage · Security: deviceBearer, intelligenceConversation

Request body
{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "clientMessageId",
          "body"
        ],
        "properties": {
          "clientMessageId": {
            "type": "string",
            "pattern": "^[0-9a-f]{32}$"
          },
          "body": {
            "type": "string",
            "minLength": 1,
            "maxLength": 8000
          },
          "snapshotJson": {
            "type": "string",
            "minLength": 2,
            "maxLength": 300000
          },
          "snapshotHash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          }
        }
      }
    },
    "application/protobuf": {
      "schema": {
        "type": "string",
        "format": "binary",
        "description": "fortisnet.intelligence.v1.PostConversationMessageRequest"
      }
    }
  }
}
Responses
{
  "201": {
    "description": "Message stored (or the existing one for this `clientMessageId`).",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "message",
            "assistant"
          ],
          "properties": {
            "message": {
              "$ref": "#/components/schemas/IntelligenceMessage"
            },
            "assistant": {
              "$ref": "#/components/schemas/IntelligenceMessage"
            }
          }
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.PostConversationMessageResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "404": {
    "description": "Unknown, expired, or not this device's conversation (never distinguished)."
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  },
  "503": {
    "description": "The user turn is saved, but the model reply is unavailable. Retry with the same client message ID."
  }
}

Capabilities 2

GET/v1/networks

List networks and available server features

Use this capability document instead of hardcoding which analytics each chain supports.

Operation: listNetworks · Security: deviceBearer

Responses
{
  "200": {
    "description": "Network, balance, transfer, NFT, trustline, transaction-detail and price capabilities.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "networks",
            "prices"
          ],
          "properties": {
            "networks": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/NetworkCapability"
              }
            },
            "prices": {
              "type": "object",
              "additionalProperties": true
            }
          }
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.NetworksResponse"
        }
      }
    }
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  }
}

GET/v1/networks/{network}/overview

Read network-specific news and popular tokens

Returns editorial news matched to explicit network aliases and a
provider-ranked token list. `holderCount`, when present, is an on-chain
holder count, not a count of Fortis users. Sections report availability
and freshness independently so one upstream outage does not blank the
other card.

Operation: getNetworkOverview · Security: deviceBearer

Parameters
[
  {
    "name": "network",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "minLength": 1,
      "maxLength": 64
    }
  }
]
Responses
{
  "200": {
    "description": "Network-level discovery data.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/NetworkOverview"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.NetworkOverviewResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  },
  "503": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

DashPay 1

GET/v1/dashpay/names/resolve

Resolve a DPNS username to its current Platform identity

Uses the official Evo SDK in trusted mode to verify the Platform proof.
The returned identity is not a Dash Core payment address; clients must
require an established encrypted DashPay contact channel before paying.

Operation: resolveDashPayName · Security: deviceBearer

Parameters
[
  {
    "name": "name",
    "in": "query",
    "required": true,
    "schema": {
      "type": "string",
      "minLength": 1,
      "maxLength": 69
    },
    "examples": {
      "label": {
        "value": "alice"
      },
      "fullyQualified": {
        "value": "alice.dash"
      }
    }
  }
]
Responses
{
  "200": {
    "description": "Canonical name and current identity, or null when the name is unregistered.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "name",
            "identityId"
          ],
          "properties": {
            "name": {
              "type": "string",
              "example": "alice.dash"
            },
            "identityId": {
              "type": "string",
              "nullable": true
            }
          }
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.DashPayNameResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  },
  "503": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

Prices 3

GET/v1/prices

Read spot prices for up to 100 assets

A quote is per displayed unit, as markets quote it. For a Solana mint that scales the amount it displays (ScaledUiAmount, InterestBearing), that is the on-chain amount times the mint's multiplier; the balances endpoint reports it as a token's `displayUnits`.

Operation: getSpotPrices · Security: deviceBearer

Parameters
[
  {
    "name": "ids",
    "in": "query",
    "required": true,
    "description": "Comma-separated price IDs such as `ethereum`, `spl:<mint>`, `asa:<id>`, `<evm-network>:erc20:<address>`, `tron:trc20:<address>` or `tron:trc10:<numeric-id>`.",
    "schema": {
      "type": "string",
      "maxLength": 4000
    }
  },
  {
    "$ref": "#/components/parameters/Fiat"
  }
]
Responses
{
  "200": {
    "description": "Price map with cache provenance on each returned quote, plus missing and invalid ID lists.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/SpotPriceResponse"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.SpotPriceResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

GET/v1/prices/history

Read one token price series

Fills missing portions from upstream and persists fetched candles. Hourly windows are capped at 90 days and daily windows at 10 years.

Operation: getPriceHistory · Security: deviceBearer

Parameters
[
  {
    "name": "id",
    "in": "query",
    "required": true,
    "schema": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    }
  },
  {
    "$ref": "#/components/parameters/From"
  },
  {
    "$ref": "#/components/parameters/To"
  },
  {
    "$ref": "#/components/parameters/Interval"
  }
]
Responses
{
  "200": {
    "description": "Available points, requested bounds, cache provenance and actual availability.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/PriceHistory"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.PriceHistory"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

POST/v1/prices/history

Read comparison series for up to 50 tokens

Fetches every series independently, so one unavailable token does not fail the comparison. Existing cached days are reused and only missing time gaps are fetched.

Operation: getPriceHistoryBatch · Security: deviceBearer

Request body
{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "required": [
          "ids",
          "from",
          "to"
        ],
        "properties": {
          "ids": {
            "type": "array",
            "minItems": 1,
            "maxItems": 50,
            "uniqueItems": true,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          },
          "from": {
            "type": "integer",
            "format": "int64",
            "minimum": 1,
            "description": "Inclusive Unix time in seconds."
          },
          "to": {
            "type": "integer",
            "format": "int64",
            "minimum": 1,
            "description": "Exclusive Unix time in seconds."
          },
          "interval": {
            "$ref": "#/components/schemas/Interval"
          }
        }
      }
    },
    "application/protobuf": {
      "schema": {
        "type": "string",
        "format": "binary",
        "description": "fortisnet.intelligence.v1.PriceHistoryBatchRequest"
      }
    }
  }
}
Responses
{
  "200": {
    "description": "Per-ID series or per-ID error, plus aggregate cache provenance.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/PriceHistoryBatch"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.PriceHistoryBatchResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  }
}

Accounts 5

GET/v1/networks/{network}/address/{address}/token-holdings

Read Tron fungible token holdings through the keyed server transport

Tron mainnet only. Requires a device bearer token; the TronScan API key stays on the server.

Operation: getTronTokenHoldings · Security: deviceBearer

Parameters
[
  {
    "$ref": "#/components/parameters/Network"
  },
  {
    "$ref": "#/components/parameters/Address"
  }
]
Responses
{
  "200": {
    "description": "TRC-10 and TRC-20 holdings in TronScan wallet row format.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "data"
          ],
          "properties": {
            "data": {
              "type": "array",
              "items": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.StructuredReadResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "401": {
    "$ref": "#/components/responses/Unauthorized"
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  },
  "503": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

GET/v1/networks/{network}/address/{address}/balances

Read native and tracked-token balances

Operation: getAddressBalances · Security: deviceBearer

Parameters
[
  {
    "$ref": "#/components/parameters/Network"
  },
  {
    "$ref": "#/components/parameters/Address"
  },
  {
    "name": "tokens",
    "in": "query",
    "description": "Optional comma-separated tracked token IDs; at most 200.",
    "schema": {
      "type": "string",
      "maxLength": 8000
    }
  }
]
Responses
{
  "200": {
    "description": "Balance payload with `asOf`, `stale` and cache provenance.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/AccountBalances"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.StructuredReadResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

GET/v1/networks/{network}/address/{address}/transfers

Read durable paginated transfer history

Operation: getAddressTransfers · Security: deviceBearer

Parameters
[
  {
    "$ref": "#/components/parameters/Network"
  },
  {
    "$ref": "#/components/parameters/Address"
  },
  {
    "name": "since",
    "in": "query",
    "description": "Opaque delta-sync cursor returned by an earlier request.",
    "schema": {
      "type": "string",
      "maxLength": 40
    }
  },
  {
    "name": "before",
    "in": "query",
    "description": "Opaque newest-first display cursor. Mutually exclusive with `since`.",
    "schema": {
      "type": "string",
      "maxLength": 600
    }
  },
  {
    "name": "refresh",
    "in": "query",
    "description": "Set to `true` for an explicit user refresh that bypasses the normal short freshness window.",
    "schema": {
      "type": "string",
      "enum": [
        "true"
      ]
    }
  },
  {
    "name": "limit",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1,
      "maximum": 500,
      "default": 100
    }
  },
  {
    "name": "token",
    "in": "query",
    "description": "Restrict results to one token ID.",
    "schema": {
      "type": "string",
      "maxLength": 120
    }
  },
  {
    "name": "priceId",
    "in": "query",
    "description": "Canonical market-data identity for bounded receipt-value repair. Registry identity takes precedence.",
    "schema": {
      "type": "string",
      "pattern": "^[a-z0-9][a-z0-9-]{0,99}$"
    }
  },
  {
    "name": "from",
    "in": "query",
    "description": "Inclusive UTC epoch-second chart boundary.",
    "schema": {
      "type": "integer",
      "format": "int64",
      "minimum": 0
    }
  },
  {
    "name": "to",
    "in": "query",
    "description": "Inclusive UTC epoch-second chart boundary.",
    "schema": {
      "type": "integer",
      "format": "int64",
      "minimum": 0
    }
  },
  {
    "name": "flow",
    "in": "query",
    "description": "Token-leg direction; requires `token`.",
    "schema": {
      "type": "string",
      "enum": [
        "in",
        "out",
        "neutral"
      ]
    }
  }
]
Responses
{
  "200": {
    "description": "Durable transfer page and delta-sync cursor.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/TransfersPage"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.StructuredReadResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

GET/v1/networks/{network}/address/{address}/nfts

Read NFT holdings and display metadata

Operation: getAddressNfts · Security: deviceBearer

Parameters
[
  {
    "$ref": "#/components/parameters/Network"
  },
  {
    "$ref": "#/components/parameters/Address"
  }
]
Responses
{
  "200": {
    "description": "Reconciled NFT inventory with proxied and source media locations.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/NftInventory"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.StructuredReadResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

GET/v1/networks/{network}/address/{address}/insight

Read condensed address analytics

Operation: getAddressInsight · Security: deviceBearer

Parameters
[
  {
    "$ref": "#/components/parameters/Network"
  },
  {
    "$ref": "#/components/parameters/Address"
  }
]
Responses
{
  "200": {
    "description": "Address existence/type/activity insight with SWR provenance.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/AddressInsight"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.StructuredReadResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

Transactions 2

GET/v1/networks/{network}/tx/{hash}

Read normalized transaction details

Operation: getTransaction · Security: deviceBearer

Parameters
[
  {
    "$ref": "#/components/parameters/Network"
  },
  {
    "$ref": "#/components/parameters/Hash"
  }
]
Responses
{
  "200": {
    "description": "Normalized transaction state and chain detail.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/TransactionDetail"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.StructuredReadResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

GET/v1/networks/{network}/tx/{hash}/view

Read Flutter-compatible chain-native transaction JSON

Returns the app's existing family-specific detail shape verbatim. Unsupported families return 400 and unknown transactions return 404 so the client can fall back to direct RPC.

Operation: getTransactionView · Security: deviceBearer

Parameters
[
  {
    "$ref": "#/components/parameters/Network"
  },
  {
    "$ref": "#/components/parameters/Hash"
  }
]
Responses
{
  "200": {
    "description": "Flutter-compatible chain-native transaction view.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "additionalProperties": true
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.StructuredReadResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

Tokens 12

GET/v1/networks/{network}/token/{tokenId}/page

Read everything one token screen shows

One request for a token page: identity, the resolved quote, market
figures, profile, liquidity, holder count, the safety verdict and
(opt-in) a price series — all in the app's own vocabulary rather than an
upstream provider's field names.

`tokenId` accepts the full ref grammar: `native`, a bare or prefixed
contract / mint / asset id / issuer pair (`erc20:0x…`, `trc20:T…`, `trc10:1001305`, `spl:…`,
`asa:…`, `cardano:…`, `stellar:CODE-ISSUER`, `hts:0.0.x`,
`xrpl:CODE.issuer`), `symbol:BTC`, or `cg:{coinId}`. A bare identifier is
resolved against the network's family, so callers can pass exactly what
the chain calls the token.

Every section is independently cached, fails soft and reports its own
provenance in `cache`; a section that could not be read is `null` with an
entry in `errors`. `null` always means unknown, never zero.

Operation: getTokenPage · Security: deviceBearer

Parameters
[
  {
    "$ref": "#/components/parameters/Network"
  },
  {
    "$ref": "#/components/parameters/TokenId"
  },
  {
    "$ref": "#/components/parameters/TokenPageInclude"
  },
  {
    "$ref": "#/components/parameters/TokenPageDays"
  },
  {
    "$ref": "#/components/parameters/TokenPageInterval"
  },
  {
    "$ref": "#/components/parameters/TokenPageCoingeckoId"
  }
]
Responses
{
  "200": {
    "description": "The composed token page, with per-section cache provenance.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/TokenPage"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.StructuredReadResponse"
        }
      }
    }
  },
  "304": {
    "description": "The exact page representation is unchanged."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

POST/v1/tokens/compare

Compare trader metrics for up to 20 tokens

Performs one bounded database read over the current market and liquidity
projection. It never fans out to providers. Inputs are canonicalized by
the network token grammar; invalid and duplicate inputs fail soft in
`errors`, while valid tokens with no observations return null metrics.

Market price, market cap and 24-hour volume prefer the market provider
and fall back independently to DEX observations. Liquidity is DEX-only.
Every metric carries its own source, timestamp and freshness status;
null means unknown, never zero. Ranks are competition ranks within this
request (`1, 1, 3`) and price is deliberately not ranked.

Operation: compareTokens · Security: deviceBearer

Request body
{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/TokenComparisonRequest"
      }
    },
    "application/protobuf": {
      "schema": {
        "type": "string",
        "format": "binary",
        "description": "fortisnet.intelligence.v1.TokenComparisonRequest"
      }
    }
  }
}
Responses
{
  "200": {
    "description": "A request-ordered comparison with per-metric provenance.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/TokenComparisonResponse"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.TokenComparisonResponse"
        }
      }
    }
  },
  "304": {
    "description": "The comparison values and freshness states are unchanged."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  }
}

GET/v1/networks/{network}/token/{tokenId}

Read token metadata, market and risk details

The raw facet document: `market` and `profile` are relayed in the
upstream provider's own shape. Superseded by
`GET /v1/networks/{network}/token/{tokenId}/page`, which returns the
same facts normalized to the app's vocabulary plus identity, the
resolved quote and an optional price series. Retained because shipped
clients parse the provider field names directly.

Operation: getToken360 · Security: deviceBearer

Parameters
[
  {
    "$ref": "#/components/parameters/Network"
  },
  {
    "$ref": "#/components/parameters/TokenId"
  }
]
Responses
{
  "200": {
    "description": "Independently cached and fail-soft token intelligence facets.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/Token360"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.StructuredReadResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

GET/v1/networks/{network}/token/{tokenId}/holders/history

Read holder-count and ranked-distribution history

The daily holder counts this deployment has stored, newest first, plus
the 24h/7d/30d/90d/365d growth computed from them. The distribution
block separately returns daily concentration summaries and standard-
window address diffs: entries, exits, rank movement, and percentage-
point share changes.

Every observation is returned, including the ones a trend calculation
must discard: a lower-bound count (`atLeast`) can never anchor a
percentage, and observations are only compared within a single
`source`, because two indexers count different populations. A window
with no usable baseline is `null` — never `0`, which would claim the
holder base is flat.

Operation: getTokenHolderHistory · Security: deviceBearer

Parameters
[
  {
    "$ref": "#/components/parameters/Network"
  },
  {
    "$ref": "#/components/parameters/TokenId"
  },
  {
    "name": "days",
    "in": "query",
    "schema": {
      "type": "integer",
      "minimum": 1,
      "maximum": 400,
      "default": 30
    },
    "description": "How far back to return observations. The maximum matches the\nobservation retention, so a client explaining the 365d window can\nask for the history that window was computed from.\n"
  },
  {
    "name": "source",
    "in": "query",
    "schema": {
      "type": "string",
      "minLength": 1,
      "maxLength": 80
    },
    "description": "Provider selected by the canonical token model. When supplied,\ntrend percentages use only that provider's series; observations\nstill include every provider for diagnostics.\n"
  }
]
Responses
{
  "200": {
    "description": "Count evidence, count trends, and ranked-holder comparisons.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "network",
            "tokenId",
            "trendSource",
            "observations",
            "trends",
            "distribution"
          ],
          "properties": {
            "network": {
              "type": "string"
            },
            "tokenId": {
              "type": "string"
            },
            "trendSource": {
              "type": [
                "string",
                "null"
              ]
            },
            "observations": {
              "type": "array",
              "items": {
                "type": "object",
                "required": [
                  "at",
                  "count",
                  "atLeast",
                  "source"
                ],
                "properties": {
                  "at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "count": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "atLeast": {
                    "type": "boolean",
                    "description": "The count is a lower bound, so it anchors no percentage."
                  },
                  "source": {
                    "type": "string",
                    "minLength": 1
                  }
                }
              }
            },
            "trends": {
              "type": [
                "object",
                "null"
              ],
              "required": [
                "change24hPct",
                "change7dPct",
                "change30dPct",
                "change90dPct",
                "change365dPct",
                "latestAt"
              ],
              "description": "There is no 1h or 6h window and there cannot be: a holder\ncount is recorded once per UTC day, so the number does not\nmove within a day. Sub-daily growth would be invented.\n",
              "properties": {
                "change24hPct": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "change7dPct": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "change30dPct": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "change90dPct": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "change365dPct": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "latestAt": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            },
            "distribution": {
              "type": "object",
              "required": [
                "snapshots",
                "comparisons"
              ],
              "properties": {
                "snapshots": {
                  "type": "array",
                  "description": "Daily concentration summaries, newest first.",
                  "items": {
                    "type": "object",
                    "required": [
                      "at",
                      "source",
                      "positionCount",
                      "topHoldersPercent",
                      "topHoldersPercentExcludingLocked",
                      "burnedPercent"
                    ],
                    "properties": {
                      "at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "source": {
                        "type": "string"
                      },
                      "positionCount": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "topHoldersPercent": {
                        "type": [
                          "number",
                          "null"
                        ]
                      },
                      "topHoldersPercentExcludingLocked": {
                        "type": [
                          "number",
                          "null"
                        ]
                      },
                      "burnedPercent": {
                        "type": [
                          "number",
                          "null"
                        ]
                      }
                    }
                  }
                },
                "comparisons": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "required": [
                      "window",
                      "latestAt",
                      "baselineAt",
                      "topHoldersPercentPointChange",
                      "liveWalletPercentPointChange",
                      "burnedPercentPointChange",
                      "positions"
                    ],
                    "properties": {
                      "window": {
                        "type": "string",
                        "enum": [
                          "24h",
                          "7d",
                          "30d",
                          "90d",
                          "12m"
                        ]
                      },
                      "latestAt": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "baselineAt": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "format": "date-time"
                      },
                      "topHoldersPercentPointChange": {
                        "type": [
                          "number",
                          "null"
                        ]
                      },
                      "liveWalletPercentPointChange": {
                        "type": [
                          "number",
                          "null"
                        ]
                      },
                      "burnedPercentPointChange": {
                        "type": [
                          "number",
                          "null"
                        ]
                      },
                      "positions": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "required": [
                            "address",
                            "tag",
                            "kind",
                            "currentRank",
                            "previousRank",
                            "rankChange",
                            "currentPercent",
                            "previousPercent",
                            "percentagePointChange",
                            "currentBalance",
                            "previousBalance",
                            "balanceChange",
                            "currentIsContract",
                            "previousIsContract",
                            "currentIsLocked",
                            "previousIsLocked"
                          ],
                          "properties": {
                            "address": {
                              "type": "string"
                            },
                            "tag": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "kind": {
                              "type": "string",
                              "enum": [
                                "entered",
                                "exited",
                                "changed",
                                "unchanged"
                              ]
                            },
                            "currentRank": {
                              "type": [
                                "integer",
                                "null"
                              ]
                            },
                            "previousRank": {
                              "type": [
                                "integer",
                                "null"
                              ]
                            },
                            "rankChange": {
                              "type": [
                                "integer",
                                "null"
                              ],
                              "description": "Positive means movement toward rank 1."
                            },
                            "currentPercent": {
                              "type": [
                                "number",
                                "null"
                              ]
                            },
                            "previousPercent": {
                              "type": [
                                "number",
                                "null"
                              ]
                            },
                            "percentagePointChange": {
                              "type": [
                                "number",
                                "null"
                              ]
                            },
                            "currentBalance": {
                              "type": [
                                "number",
                                "null"
                              ]
                            },
                            "previousBalance": {
                              "type": [
                                "number",
                                "null"
                              ]
                            },
                            "balanceChange": {
                              "type": [
                                "number",
                                "null"
                              ]
                            },
                            "currentIsContract": {
                              "type": [
                                "boolean",
                                "null"
                              ]
                            },
                            "previousIsContract": {
                              "type": [
                                "boolean",
                                "null"
                              ]
                            },
                            "currentIsLocked": {
                              "type": [
                                "boolean",
                                "null"
                              ]
                            },
                            "previousIsLocked": {
                              "type": [
                                "boolean",
                                "null"
                              ]
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.StructuredReadResponse"
        }
      }
    }
  },
  "304": {
    "description": "The exact history representation is unchanged."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

GET/v1/networks/{network}/token/{tokenId}/liquidity/history

Read token liquidity, execution-quality, and pool-health history

Returns token-level liquidity and price OHLC, spread, pool
concentration, rolling 24-hour activity gauges, point-in-time changes,
and sample coverage. Rolling gauges are closing values and are never
summed across observation timestamps.

Operation: getTokenLiquidityHistory · Security: deviceBearer

Parameters
[
  {
    "$ref": "#/components/parameters/Network"
  },
  {
    "$ref": "#/components/parameters/TokenId"
  },
  {
    "name": "range",
    "in": "query",
    "schema": {
      "type": "string",
      "enum": [
        "1d",
        "7d",
        "30d",
        "90d",
        "1y"
      ],
      "default": "30d"
    }
  },
  {
    "name": "interval",
    "in": "query",
    "schema": {
      "type": "string",
      "enum": [
        "1h",
        "1d",
        "1w"
      ],
      "default": "1h"
    }
  }
]
Responses
{
  "200": {
    "description": "Liquidity history with explicit availability and coverage.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "network",
            "tokenId",
            "range",
            "interval",
            "availability",
            "changes",
            "series",
            "coverage"
          ],
          "properties": {
            "network": {
              "type": "string"
            },
            "tokenId": {
              "type": "string"
            },
            "range": {
              "type": "string",
              "enum": [
                "1d",
                "7d",
                "30d",
                "90d",
                "1y"
              ]
            },
            "interval": {
              "type": "string",
              "enum": [
                "1h",
                "1d",
                "1w"
              ]
            },
            "availability": {
              "type": "object",
              "required": [
                "status",
                "checkedAt",
                "lastSuccessfulAt",
                "source",
                "stale",
                "latestAgeSeconds"
              ],
              "properties": {
                "status": {
                  "type": "string",
                  "enum": [
                    "ok",
                    "not_found",
                    "unsupported",
                    "error",
                    "unknown"
                  ]
                },
                "checkedAt": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "lastSuccessfulAt": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "source": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "stale": {
                  "type": "boolean"
                },
                "latestAgeSeconds": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "minimum": 0
                }
              }
            },
            "latest": {
              "type": [
                "object",
                "null"
              ],
              "additionalProperties": true
            },
            "changes": {
              "type": "object",
              "additionalProperties": true
            },
            "series": {
              "type": "array",
              "items": {
                "type": "object",
                "additionalProperties": true
              }
            },
            "coverage": {
              "type": "object",
              "additionalProperties": true
            }
          }
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.StructuredReadResponse"
        }
      }
    }
  },
  "304": {
    "description": "The exact history representation is unchanged."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  }
}

GET/v1/networks/{network}/token/{tokenId}/liquidity/pools

Read every current liquidity pool for a token

Opt-in detail projection used when a user expands the Liquidity card.
Unlike the compact token-page projection, this response does not cap
the pool list. It is cached independently so a compact response can
never satisfy a complete-list caller.

Operation: getTokenLiquidityPools · Security: deviceBearer

Parameters
[
  {
    "$ref": "#/components/parameters/Network"
  },
  {
    "$ref": "#/components/parameters/TokenId"
  }
]
Responses
{
  "200": {
    "description": "Complete provider-normalized liquidity snapshot and cache provenance.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "network",
            "tokenId",
            "liquidity",
            "errors",
            "cache"
          ],
          "properties": {
            "network": {
              "type": "string"
            },
            "tokenId": {
              "type": "string"
            },
            "liquidity": {
              "type": [
                "object",
                "null"
              ],
              "additionalProperties": true
            },
            "errors": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            },
            "cache": {
              "$ref": "#/components/schemas/CacheInfo"
            }
          }
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.StructuredReadResponse"
        }
      }
    }
  },
  "304": {
    "description": "The complete pool representation is unchanged."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

GET/v1/networks/{network}/token/{tokenId}/liquidity/summary

Read the persisted 24-hour liquidity change

Compact card-oriented projection over durable liquidity snapshots.
A missing or stale baseline returns a null change instead of zero.

Operation: getTokenLiquiditySummary · Security: deviceBearer

Parameters
[
  {
    "$ref": "#/components/parameters/Network"
  },
  {
    "$ref": "#/components/parameters/TokenId"
  }
]
Responses
{
  "200": {
    "description": "Latest availability and the closest valid 24-hour comparison.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "network",
            "tokenId",
            "availability",
            "latestAt",
            "change24h"
          ],
          "properties": {
            "network": {
              "type": "string"
            },
            "tokenId": {
              "type": "string"
            },
            "availability": {
              "type": "object",
              "required": [
                "status",
                "checkedAt",
                "lastSuccessfulAt",
                "source",
                "stale",
                "latestAgeSeconds"
              ],
              "properties": {
                "status": {
                  "type": "string",
                  "enum": [
                    "ok",
                    "not_found",
                    "unsupported",
                    "error",
                    "unknown"
                  ]
                },
                "checkedAt": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "lastSuccessfulAt": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "source": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "stale": {
                  "type": "boolean"
                },
                "latestAgeSeconds": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "minimum": 0
                }
              }
            },
            "latestAt": {
              "type": [
                "integer",
                "null"
              ]
            },
            "change24h": {
              "type": [
                "object",
                "null"
              ],
              "properties": {
                "baselineAt": {
                  "type": "integer"
                },
                "liquidityPct": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "pricePct": {
                  "type": [
                    "number",
                    "null"
                  ]
                }
              }
            }
          }
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.StructuredReadResponse"
        }
      }
    }
  },
  "304": {
    "description": "The exact summary representation is unchanged."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  }
}

GET/v1/networks/{network}/tokens/search

Search tokens on one network

Operation: searchTokens · Security: deviceBearer

Parameters
[
  {
    "$ref": "#/components/parameters/Network"
  },
  {
    "name": "q",
    "in": "query",
    "required": true,
    "schema": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    }
  }
]
Responses
{
  "200": {
    "description": "Up to 20 normalized token matches with cache provenance.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/TokenSearchResponse"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.TokenSearchResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

GET/v1/networks/robinhood/stock-tokens

List issuer-registered Robinhood Stock Token contracts

Exact contract identities from the Robinhood Assets issuer registry. This read is server-cached and never treats an arbitrary ticker as a Stock Token.

Operation: listRobinhoodStockTokens · Security: deviceBearer

Responses
{
  "200": {
    "description": "Validated issuer registry plus source provenance.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "assets",
            "source"
          ],
          "properties": {
            "assets": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/RobinhoodStockAsset"
              }
            },
            "source": {
              "type": "string",
              "enum": [
                "robinhood-assets"
              ]
            }
          }
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.RobinhoodStockCatalogResponse"
        }
      }
    }
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

GET/v1/networks/robinhood/stock-tokens/{address}

Read one Stock Token's reference market and corporate actions

Bid, ask and midpoint are multiplier-adjusted issuer reference prices, not executable DEX quotes. Historical candles are collected prospectively through the generic price protocol.

Operation: getRobinhoodStockToken · Security: deviceBearer

Parameters
[
  {
    "name": "address",
    "in": "path",
    "required": true,
    "schema": {
      "type": "string",
      "pattern": "^0x[0-9a-fA-F]{40}$"
    }
  }
]
Responses
{
  "200": {
    "description": "Issuer identity, reference quote, lifecycle events and disclosures.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "asset",
            "quote",
            "corporateActions",
            "source",
            "disclosures"
          ],
          "properties": {
            "asset": {
              "$ref": "#/components/schemas/RobinhoodStockAsset"
            },
            "quote": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/RobinhoodReferenceQuote"
                },
                {
                  "type": "null"
                }
              ]
            },
            "corporateActions": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/RobinhoodCorporateAction"
              }
            },
            "source": {
              "type": "string",
              "enum": [
                "robinhood-assets"
              ]
            },
            "disclosures": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.RobinhoodStockInstrumentResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

POST/v1/prices/markets

Read condensed market data for up to 100 tokens

Rows are cached per coin and kept fresh by a scheduled warmer for every
recently requested coin; uncached coins from concurrent requests share
one provider call. Coins the provider does not list are omitted. A coin
with no cached row whose fetch failed (provider busy or down) is listed
in `pending` instead of failing the request: keep the previous value
for those ids and ask again later.

Operation: getTokenMarkets · Security: deviceBearer

Request body
{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "required": [
          "ids"
        ],
        "properties": {
          "ids": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 100
            }
          },
          "fiat": {
            "type": "string",
            "enum": [
              "usd"
            ],
            "default": "usd"
          }
        }
      }
    },
    "application/protobuf": {
      "schema": {
        "type": "string",
        "format": "binary",
        "description": "fortisnet.intelligence.v1.MarketBatchRequest"
      }
    }
  }
}
Responses
{
  "200": {
    "description": "CoinGecko market rows and cache provenance.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/TokenMarketsResponse"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.MarketBatchResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "429": {
    "$ref": "#/components/responses/RateLimited"
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

GET/v1/prices/ohlc

Read OHLC candles for a generic price id

CoinGecko ids are backfilled on demand. Provider-native contract ids, including verified Robinhood Stock Tokens, return only honestly observed prospective candles.

Operation: getTokenOhlc · Security: deviceBearer

Parameters
[
  {
    "name": "id",
    "in": "query",
    "required": true,
    "schema": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    }
  },
  {
    "$ref": "#/components/parameters/Fiat"
  },
  {
    "name": "days",
    "in": "query",
    "required": true,
    "schema": {
      "type": "integer",
      "enum": [
        1,
        7,
        14,
        30,
        90,
        180,
        365
      ]
    }
  }
]
Responses
{
  "200": {
    "description": "OHLC tuples and cache provenance.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/OhlcResponse"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.OhlcResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

Exchange 7

GET/v1/networks/robinhood/sponsored-trades/capabilities

Discover gasless batched Stock Token execution

Enabled only when this deployment has both an Alchemy key and Gas Manager policy. No provider credential or policy id is returned.

Operation: getRobinhoodSponsoredTradeCapabilities · Security: deviceBearer

Responses
{
  "200": {
    "description": "Deployment capability and bounded batch policy.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/RobinhoodSponsoredTradeCapabilities"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.RobinhoodSponsoredCapabilitiesResponse"
        }
      }
    }
  }
}

POST/v1/networks/robinhood/transactions/diagnose

Simulate and explain a selected Stock Token trade before signing

Runs the exact reviewed calls sequentially with Alchemy `eth_simulateV1`.
Balance changes, reverts, token receipts and fees come from simulation;
internal calls are best-effort because Alchemy Debug/Trace availability
depends on the deployment plan. The endpoint never signs, mutates or
broadcasts calldata.

Operation: diagnoseRobinhoodTransaction · Security: deviceBearer

Request body
{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "from",
          "calls",
          "sponsored"
        ],
        "properties": {
          "from": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$"
          },
          "sponsored": {
            "type": "boolean"
          },
          "calls": {
            "type": "array",
            "minItems": 1,
            "maxItems": 2,
            "items": {
              "$ref": "#/components/schemas/DiagnosticEvmCall"
            }
          }
        }
      }
    },
    "application/protobuf": {
      "schema": {
        "type": "string",
        "format": "binary",
        "description": "fortisnet.intelligence.v1.RobinhoodDiagnoseRequest"
      }
    }
  }
}
Responses
{
  "200": {
    "description": "Normalized pre-sign diagnostic report.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/RobinhoodTransactionDiagnostics"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.RobinhoodDynamicResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "503": {
    "$ref": "#/components/responses/ProviderNotConfigured"
  }
}

POST/v1/networks/robinhood/sponsored-trades/prepare

Prepare a policy-approved EIP-7702 stock trade batch

Accepts one swap, optionally preceded by one exact ERC-20 approval. Targets and calldata are ABI-decoded against verified Stock Tokens, USDG/WETH, and canonical Robinhood Uniswap routers. The prepared operation remains server-side behind a short-lived device-bound ticket.

Operation: prepareRobinhoodSponsoredTrade · Security: deviceBearer

Request body
{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "required": [
          "from",
          "calls"
        ],
        "properties": {
          "from": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$"
          },
          "calls": {
            "type": "array",
            "minItems": 1,
            "maxItems": 2,
            "items": {
              "$ref": "#/components/schemas/SponsoredEvmCall"
            }
          }
        }
      }
    },
    "application/protobuf": {
      "schema": {
        "type": "string",
        "format": "binary",
        "description": "fortisnet.intelligence.v1.RobinhoodPrepareRequest"
      }
    }
  }
}
Responses
{
  "200": {
    "description": "Opaque ticket plus signature requests; private keys remain in the wallet.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/RobinhoodSponsoredTradePreparation"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.RobinhoodDynamicResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "503": {
    "$ref": "#/components/responses/ProviderNotConfigured"
  }
}

POST/v1/networks/robinhood/sponsored-trades/submit

Submit signatures for a server-held prepared batch

Operation: submitRobinhoodSponsoredTrade · Security: deviceBearer

Request body
{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "required": [
          "ticket",
          "signatures"
        ],
        "properties": {
          "ticket": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{32}$"
          },
          "signatures": {
            "type": "array",
            "minItems": 1,
            "maxItems": 2,
            "items": {
              "type": "string",
              "pattern": "^0x[0-9a-fA-F]{130}$"
            }
          }
        }
      }
    },
    "application/protobuf": {
      "schema": {
        "type": "string",
        "format": "binary",
        "description": "fortisnet.intelligence.v1.RobinhoodSubmitRequest"
      }
    }
  }
}
Responses
{
  "200": {
    "description": "Alchemy Wallet call id and UserOperation details.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "additionalProperties": true
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.RobinhoodDynamicResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "404": {
    "$ref": "#/components/responses/NotFound"
  },
  "503": {
    "$ref": "#/components/responses/ProviderNotConfigured"
  }
}

GET/v1/exchange/cross-chain/providers

List supported cross-chain routing services

Returns the stable Fortis provider catalogue and whether each provider is configured in this deployment. Credentials are never returned.

Operation: getCrossChainProviders · Security: deviceBearer

Responses
{
  "200": {
    "description": "Supported provider registry.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "providers",
            "cache"
          ],
          "properties": {
            "providers": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/CrossChainProvider"
              }
            },
            "cache": {
              "$ref": "#/components/schemas/CacheInfo"
            }
          }
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.CrossChainProvidersResponse"
        }
      }
    }
  }
}

POST/v1/exchange/cross-chain/quotes

Compare normalized cross-chain routes

Queries configured providers concurrently and returns up to five normalized routes,
ranked by guaranteed minimum output. Provider failures are isolated and reported in
`attempts`; a partial result remains successful. Returned transaction data is unsigned
and must be independently validated, reviewed and signed by the wallet.

Operation: getCrossChainQuotes · Security: deviceBearer

Request body
{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/CrossChainQuoteRequest"
      }
    },
    "application/protobuf": {
      "schema": {
        "type": "string",
        "format": "binary",
        "description": "fortisnet.intelligence.v1.CrossChainQuoteRequest"
      }
    }
  }
}
Responses
{
  "200": {
    "description": "Ranked routes and per-provider attempt outcomes.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/CrossChainQuoteResponse"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.CrossChainQuoteResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

POST/v1/exchange/cross-chain/status

Track destination settlement for a cross-chain transfer

Operation: getCrossChainStatus · Security: deviceBearer

Request body
{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/CrossChainStatusRequest"
      }
    },
    "application/protobuf": {
      "schema": {
        "type": "string",
        "format": "binary",
        "description": "fortisnet.intelligence.v1.CrossChainStatusRequest"
      }
    }
  }
}
Responses
{
  "200": {
    "description": "Normalized provider settlement state.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/CrossChainStatus"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.CrossChainStatusResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  },
  "503": {
    "$ref": "#/components/responses/ProviderNotConfigured"
  }
}

Bittensor 1

POST/v1/bittensor/analytics

Read an allow-listed Taostats analytics resource

Compatibility shim for the app's existing Taostats parser. The server supplies the Taostats key; arbitrary upstream hosts and paths are not accepted. Replace with domain-specific Bittensor resources before removing this operation.

Operation: getBittensorAnalytics · Security: deviceBearer

Request body
{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "required": [
          "path"
        ],
        "properties": {
          "path": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "query": {
            "type": "object",
            "default": {},
            "additionalProperties": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                },
                {
                  "type": "boolean"
                }
              ]
            }
          }
        }
      }
    },
    "application/protobuf": {
      "schema": {
        "type": "string",
        "format": "binary",
        "description": "fortisnet.intelligence.v1.BittensorAnalyticsRequest"
      }
    }
  }
}
Responses
{
  "200": {
    "description": "Allow-listed provider payload with cache provenance.",
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/ProviderEnvelope"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.BittensorAnalyticsResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "502": {
    "$ref": "#/components/responses/UpstreamUnavailable"
  }
}

News 3

GET/v1/news

The aggregated news wire

One mixed feed of **stories**, newest first. A story is a cluster, not an
article: several publishers reporting the same thing collapse into one
item, with `sourceCount` reporting how many carried it and
`GET /v1/news/{id}` listing them.

Each item carries what an aggregator's card shows — a headline, a short
attributed excerpt, a lead image key, and a link back to the publisher.
Article bodies are never stored or served; always link out and always
show `publisher`.

The feed is deliberately **not personalised**: no holdings, no device
scoping, no lane parameter. A client's topic tabs are lanes over this
one response, which is what lets it fetch once and switch tabs for free
— and what lets every device share one cached answer.

Ingestion runs on a schedule, so this endpoint contacts no upstream: any
number of polling devices costs one cached query. Validated with an
ETag; an unchanged feed costs a 304.

Clients MUST ignore unknown `topics` and `impact` values rather than
dropping the item — that rule is what lets new ones ship inside `/v1`.

Operation: readNews · Security: deviceBearer

Parameters
[
  {
    "name": "topics",
    "in": "query",
    "required": false,
    "description": "Comma-separated topic filter. Unknown values are ignored. Omit for\nthe mixed wire, which is what a tabbed client wants.\n",
    "schema": {
      "type": "string",
      "example": "macro,policy"
    }
  },
  {
    "name": "limit",
    "in": "query",
    "required": false,
    "description": "Maximum stories returned.",
    "schema": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100,
      "default": 40
    }
  }
]
Responses
{
  "200": {
    "description": "The wire.",
    "headers": {
      "ETag": {
        "schema": {
          "type": "string"
        }
      },
      "Cache-Control": {
        "schema": {
          "type": "string",
          "example": "private, max-age=60"
        }
      }
    },
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/NewsFeed"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.NewsFeedResponse"
        }
      }
    }
  },
  "304": {
    "description": "The wire is unchanged."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  }
}

GET/v1/news/{id}

One story, with every publisher that carried it

The feed item plus its full member list, so a reader can open the story
from an outlet they trust rather than the one this server ranked first.
A story that has aged out of retention is `404` — gone, not empty.

Operation: readNewsItem · Security: deviceBearer

Parameters
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "description": "Story id from the feed (ULID).",
    "schema": {
      "type": "string",
      "example": "01K2C8ZR4G0000000000000000"
    }
  }
]
Responses
{
  "200": {
    "description": "The story.",
    "headers": {
      "ETag": {
        "schema": {
          "type": "string"
        }
      },
      "Cache-Control": {
        "schema": {
          "type": "string",
          "example": "private, max-age=300"
        }
      }
    },
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/NewsItemDetail"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.NewsItemDetailResponse"
        }
      }
    }
  },
  "304": {
    "description": "The story is unchanged."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "404": {
    "description": "Unknown story",
    "or one past retention.": null
  }
}

GET/v1/news/{id}/reactions

What the market did around a story, per asset

Price observations around a story's publish stamp, for every ticker the
story carries, plus the same measurement for BTC as a benchmark.

These are **receipts, not attribution**. The windows report what the
price did over a span containing the publish stamp; nothing here claims
the story caused it. `differencePp` subtracts the benchmark's move so a
reader can see whether an asset diverged from the market at all, which
is the closest honest thing to a story-specific signal.

No look-ahead and no interpolation: a window whose end has not arrived
yet is `pending`, and one whose endpoints are further from the target
than the tolerance is `unavailable`. Coarse or missing data stays
absent rather than being smoothed into a number. A window is only
`measured` when both endpoints were actually observed.

Samples are cached per asset and day, so several stories about the same
asset share one upstream read.

Operation: readNewsReactions · Security: deviceBearer

Parameters
[
  {
    "name": "id",
    "in": "path",
    "required": true,
    "description": "Story id from the feed (ULID).",
    "schema": {
      "type": "string",
      "example": "01K2C8ZR4G0000000000000000"
    }
  }
]
Responses
{
  "200": {
    "description": "The measurements.",
    "headers": {
      "ETag": {
        "schema": {
          "type": "string"
        }
      },
      "Cache-Control": {
        "schema": {
          "type": "string",
          "example": "private, max-age=60"
        }
      }
    },
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/NewsReactions"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.NewsReactionsResponse"
        }
      }
    }
  },
  "304": {
    "description": "The measurements are unchanged."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  },
  "404": {
    "description": "Unknown story",
    "or one past retention.": null
  }
}

Gems 1

GET/v1/gems

The New Gems discovery feed

Tokens the wallet does not hold, each carried as a **whole token page** —
the very document `GET /v1/networks/{network}/token/{tokenId}/page`
returns. A client merges it into the model it already uses for a held
token, so a discovery card and the token page read the same numbers
through the same parser and cannot drift apart.

Every section served here is one the token-page facet already caches, so
the feed contacts no upstream of its own.

**The picks are a placeholder.** The candidate discovery and ranking job
is not built yet, so the feed serves a short hand-written list of tokens
from networks this server already covers, in the order written, with no
ranking of any kind. `placeholder: true` says so; a client that promotes
the list as a recommendation is misreporting it. A pick whose page cannot
be composed is dropped rather than returned empty.

Operation: readGems · Security: deviceBearer

Parameters
[
  {
    "name": "limit",
    "in": "query",
    "required": false,
    "description": "Maximum picks returned.",
    "schema": {
      "type": "integer",
      "minimum": 1,
      "maximum": 20,
      "default": 10
    }
  }
]
Responses
{
  "200": {
    "description": "The feed.",
    "headers": {
      "ETag": {
        "schema": {
          "type": "string"
        }
      },
      "Cache-Control": {
        "schema": {
          "type": "string",
          "example": "private, max-age=60"
        }
      }
    },
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/GemsFeed"
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.StructuredReadResponse"
        }
      }
    }
  },
  "304": {
    "description": "The feed is unchanged."
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  }
}

Sync 1

POST/v1/sync

Fetch one device-scoped batch of field-level model changes

Returns canonical model patches for accounts already watched by this
device. Use `after` only for a first date-based bootstrap; persist the
opaque returned cursor and use `cursor` for all subsequent requests.
`cursor` and `after` are mutually exclusive. Explicit null field values
clear a value; `operation: delete` is an entity tombstone.

Operation: readSyncBatch · Security: deviceBearer

Request body
{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "cursor": {
            "type": "string",
            "maxLength": 64
          },
          "after": {
            "type": "string",
            "format": "date-time"
          },
          "models": {
            "type": "array",
            "minItems": 1,
            "maxItems": 1,
            "items": {
              "type": "string",
              "enum": [
                "transfer"
              ]
            }
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 500,
            "default": 200
          }
        }
      }
    },
    "application/protobuf": {
      "schema": {
        "type": "string",
        "format": "binary",
        "description": "fortisnet.intelligence.v1.SyncRequest"
      }
    }
  }
}
Responses
{
  "200": {
    "description": "A directly applicable, idempotent sync batch.",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "required": [
            "schema",
            "cursor",
            "hasMore",
            "reset",
            "asOf",
            "supportedModels",
            "changes"
          ],
          "properties": {
            "schema": {
              "type": "integer",
              "enum": [
                1
              ]
            },
            "cursor": {
              "type": "string"
            },
            "hasMore": {
              "type": "boolean"
            },
            "reset": {
              "type": "boolean"
            },
            "asOf": {
              "type": "string",
              "format": "date-time"
            },
            "supportedModels": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "transfer"
                ]
              }
            },
            "changes": {
              "type": "array",
              "items": {
                "type": "object",
                "required": [
                  "model",
                  "key",
                  "operation",
                  "version",
                  "changedAt",
                  "fields"
                ],
                "properties": {
                  "model": {
                    "type": "string",
                    "enum": [
                      "transfer"
                    ]
                  },
                  "key": {
                    "type": "object",
                    "additionalProperties": true
                  },
                  "operation": {
                    "type": "string",
                    "enum": [
                      "upsert",
                      "patch",
                      "delete"
                    ]
                  },
                  "version": {
                    "type": "string"
                  },
                  "changedAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "fields": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        }
      },
      "application/protobuf": {
        "schema": {
          "type": "string",
          "format": "binary",
          "description": "fortisnet.intelligence.v1.SyncResponse"
        }
      }
    }
  },
  "400": {
    "$ref": "#/components/responses/BadRequest"
  }
}

Schemas

SponsoredEvmCall
{
  "type": "object",
  "additionalProperties": false,
  "required": [
    "to",
    "data"
  ],
  "properties": {
    "to": {
      "type": "string",
      "pattern": "^0x[0-9a-fA-F]{40}$"
    },
    "data": {
      "type": "string",
      "pattern": "^0x(?:[0-9a-fA-F]{2})*$"
    },
    "value": {
      "type": "string",
      "pattern": "^0x(?:0|[1-9a-fA-F][0-9a-fA-F]*)$"
    }
  }
}
DiagnosticEvmCall
{
  "type": "object",
  "additionalProperties": false,
  "required": [
    "to",
    "data"
  ],
  "properties": {
    "to": {
      "type": "string",
      "pattern": "^0x[0-9a-fA-F]{40}$"
    },
    "data": {
      "type": "string",
      "pattern": "^0x(?:[0-9a-fA-F]{2})*$"
    },
    "value": {
      "type": "string",
      "pattern": "^0x(?:0|[1-9a-fA-F][0-9a-fA-F]*)$"
    },
    "gas": {
      "type": "string",
      "pattern": "^0x(?:0|[1-9a-fA-F][0-9a-fA-F]*)$"
    },
    "maxFeePerGas": {
      "type": "string",
      "pattern": "^0x(?:0|[1-9a-fA-F][0-9a-fA-F]*)$"
    },
    "maxPriorityFeePerGas": {
      "type": "string",
      "pattern": "^0x(?:0|[1-9a-fA-F][0-9a-fA-F]*)$"
    }
  }
}
RobinhoodTransactionDiagnostics
{
  "type": "object",
  "required": [
    "available",
    "network",
    "chainId",
    "provider",
    "simulatedAt",
    "wouldRevert",
    "canSign",
    "calls",
    "balanceChanges",
    "approvals",
    "estimatedStockTokensReceived",
    "warnings",
    "traceAvailable",
    "internalCalls",
    "fee"
  ],
  "properties": {
    "available": {
      "type": "boolean"
    },
    "network": {
      "type": "string",
      "enum": [
        "robinhood"
      ]
    },
    "chainId": {
      "type": "integer",
      "enum": [
        4663
      ]
    },
    "provider": {
      "type": "string",
      "enum": [
        "alchemy"
      ]
    },
    "simulatedAt": {
      "type": "string",
      "format": "date-time"
    },
    "blockNumber": {
      "type": [
        "string",
        "null"
      ]
    },
    "wouldRevert": {
      "type": "boolean"
    },
    "canSign": {
      "type": "boolean"
    },
    "revertReason": {
      "type": [
        "string",
        "null"
      ]
    },
    "calls": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": true
      }
    },
    "balanceChanges": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": true
      }
    },
    "approvals": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": true
      }
    },
    "estimatedStockTokensReceived": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": true
      }
    },
    "warnings": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": true
      }
    },
    "traceAvailable": {
      "type": "boolean"
    },
    "internalCalls": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": true
      }
    },
    "fee": {
      "type": "object",
      "required": [
        "gasUsed",
        "estimatedMaxFeeWei",
        "paidBy",
        "perCall"
      ],
      "properties": {
        "gasUsed": {
          "type": "string",
          "pattern": "^[0-9]+$"
        },
        "estimatedMaxFeeWei": {
          "type": "string",
          "pattern": "^[0-9]+$"
        },
        "paidBy": {
          "type": "string",
          "enum": [
            "wallet",
            "sponsor"
          ]
        },
        "perCall": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": true
          }
        }
      }
    }
  }
}
RobinhoodSponsoredTradeCapabilities
{
  "type": "object",
  "required": [
    "network",
    "chainId",
    "enabled",
    "gasless",
    "sponsoredGas",
    "batchedCalls",
    "maxCalls",
    "accountStandard"
  ],
  "properties": {
    "network": {
      "type": "string",
      "enum": [
        "robinhood"
      ]
    },
    "chainId": {
      "type": "integer",
      "enum": [
        4663
      ]
    },
    "enabled": {
      "type": "boolean"
    },
    "gasless": {
      "type": "boolean"
    },
    "sponsoredGas": {
      "type": "boolean"
    },
    "batchedCalls": {
      "type": "boolean"
    },
    "maxCalls": {
      "type": "integer",
      "enum": [
        2
      ]
    },
    "accountStandard": {
      "type": "string",
      "enum": [
        "eip-7702"
      ]
    },
    "reason": {
      "type": "string"
    }
  }
}
RobinhoodSponsoredTradePreparation
{
  "type": "object",
  "required": [
    "ticket",
    "expiresAt",
    "prepared",
    "signatureRequests"
  ],
  "properties": {
    "ticket": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{32}$"
    },
    "expiresAt": {
      "type": "string",
      "format": "date-time"
    },
    "prepared": {
      "type": "object",
      "additionalProperties": true
    },
    "signatureRequests": {
      "type": "array",
      "minItems": 1,
      "maxItems": 2,
      "items": {
        "type": "object",
        "additionalProperties": true
      }
    }
  }
}
RobinhoodStockAsset
{
  "type": "object",
  "required": [
    "id",
    "tokenSymbol",
    "tokenName",
    "deployments",
    "currentMultiplier",
    "pendingMultiplier",
    "status",
    "tradingCapabilities"
  ],
  "properties": {
    "id": {
      "type": "string",
      "pattern": "^0x[0-9a-f]{64}$"
    },
    "tokenSymbol": {
      "type": "string"
    },
    "tokenName": {
      "type": "string"
    },
    "deployments": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "chainId",
          "contractAddress"
        ],
        "properties": {
          "chainId": {
            "type": "integer",
            "enum": [
              4663
            ]
          },
          "contractAddress": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$"
          }
        }
      }
    },
    "currentMultiplier": {
      "type": "string"
    },
    "pendingMultiplier": {
      "type": "string"
    },
    "pendingMultiplierEffectiveTime": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time"
    },
    "logoUrl": {
      "type": [
        "string",
        "null"
      ],
      "format": "uri"
    },
    "issuerLogoUrl": {
      "type": [
        "string",
        "null"
      ],
      "format": "uri"
    },
    "logoMediaKey": {
      "type": [
        "string",
        "null"
      ],
      "pattern": "^[0-9a-f]{32}$"
    },
    "status": {
      "type": "string"
    },
    "tradingCapabilities": {
      "$ref": "#/components/schemas/RobinhoodTradingCapabilities"
    }
  }
}
RobinhoodTradingSessionCapabilities
{
  "type": "object",
  "required": [
    "whole",
    "fractional"
  ],
  "properties": {
    "whole": {
      "type": [
        "string",
        "null"
      ]
    },
    "fractional": {
      "type": [
        "string",
        "null"
      ]
    }
  }
}
RobinhoodTradingCapabilities
{
  "type": "object",
  "required": [
    "fractionalTradability",
    "allDayTradability",
    "extendedHoursFractionalTradability",
    "market",
    "extended",
    "overnight"
  ],
  "properties": {
    "fractionalTradability": {
      "type": [
        "string",
        "null"
      ]
    },
    "allDayTradability": {
      "type": [
        "string",
        "null"
      ]
    },
    "extendedHoursFractionalTradability": {
      "type": [
        "boolean",
        "null"
      ]
    },
    "market": {
      "$ref": "#/components/schemas/RobinhoodTradingSessionCapabilities"
    },
    "extended": {
      "$ref": "#/components/schemas/RobinhoodTradingSessionCapabilities"
    },
    "overnight": {
      "$ref": "#/components/schemas/RobinhoodTradingSessionCapabilities"
    }
  }
}
RobinhoodReferenceQuote
{
  "type": "object",
  "required": [
    "symbol",
    "contractAddress",
    "bidUsd",
    "askUsd",
    "midpointUsd",
    "rawBidUsd",
    "rawAskUsd",
    "multiplier",
    "dailyTradingVolume",
    "tradingHalted",
    "generatedAt",
    "currency"
  ],
  "properties": {
    "symbol": {
      "type": "string"
    },
    "contractAddress": {
      "type": "string",
      "pattern": "^0x[0-9a-fA-F]{40}$"
    },
    "bidUsd": {
      "type": "number",
      "exclusiveMinimum": 0
    },
    "askUsd": {
      "type": "number",
      "exclusiveMinimum": 0
    },
    "midpointUsd": {
      "type": "number",
      "exclusiveMinimum": 0
    },
    "rawBidUsd": {
      "type": "number",
      "exclusiveMinimum": 0
    },
    "rawAskUsd": {
      "type": "number",
      "exclusiveMinimum": 0
    },
    "multiplier": {
      "type": "string"
    },
    "dailyTradingVolume": {
      "type": [
        "number",
        "null"
      ],
      "minimum": 0
    },
    "tradingHalted": {
      "type": "boolean"
    },
    "generatedAt": {
      "type": "string",
      "format": "date-time"
    },
    "currency": {
      "type": "string",
      "enum": [
        "USD"
      ]
    }
  }
}
RobinhoodCorporateAction
{
  "type": "object",
  "required": [
    "id",
    "type",
    "status",
    "tokenSymbol",
    "contractAddress",
    "processDate",
    "details"
  ],
  "properties": {
    "id": {
      "type": "string"
    },
    "type": {
      "type": "string"
    },
    "status": {
      "type": "string"
    },
    "tokenSymbol": {
      "type": "string"
    },
    "contractAddress": {
      "type": "string",
      "pattern": "^0x[0-9a-fA-F]{40}$"
    },
    "processDate": {
      "oneOf": [
        {
          "type": "object",
          "required": [
            "year",
            "month",
            "day"
          ],
          "properties": {
            "year": {
              "type": "integer",
              "minimum": 1900,
              "maximum": 9999
            },
            "month": {
              "type": "integer",
              "minimum": 1,
              "maximum": 12
            },
            "day": {
              "type": "integer",
              "minimum": 1,
              "maximum": 31
            }
          }
        },
        {
          "type": "null"
        }
      ]
    },
    "details": {
      "type": "object",
      "additionalProperties": true
    }
  }
}
Updates
{
  "type": "object",
  "description": "A change delta for one device. `changes` is a discriminated union keyed on\n`type`; a client MUST ignore entries with an unknown type so new change\nkinds can ship without a new API version.\n",
  "required": [
    "cursor",
    "hasMore",
    "reset",
    "asOf",
    "changes"
  ],
  "properties": {
    "cursor": {
      "type": "string",
      "description": "Opaque. Store verbatim and send back as `since`.",
      "example": "u1_01K2C8ZR4G0000000000000000"
    },
    "hasMore": {
      "type": "boolean",
      "description": "The window was capped — poll again immediately."
    },
    "reset": {
      "type": "boolean",
      "description": "The supplied cursor was unusable; do a full refresh."
    },
    "asOf": {
      "type": "string",
      "format": "date-time"
    },
    "changes": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/UpdateChange"
      }
    }
  }
}
UpdateChange
{
  "type": "object",
  "description": "One account with new activity — an idempotent refresh hint.",
  "required": [
    "type",
    "network",
    "address",
    "records",
    "latestAt",
    "tokens"
  ],
  "properties": {
    "type": {
      "type": "string",
      "enum": [
        "transfers"
      ],
      "description": "Discriminator. Unknown values must be ignored."
    },
    "network": {
      "type": "string",
      "example": "eth-mainnet"
    },
    "address": {
      "type": "string"
    },
    "records": {
      "type": "integer",
      "description": "Rows new or re-statused in this window — not a running total."
    },
    "latestAt": {
      "type": "string",
      "format": "date-time",
      "description": "Newest transfer time among them."
    },
    "tokens": {
      "type": "array",
      "description": "Token ids that moved (capped); empty for native-only activity.",
      "items": {
        "type": "string"
      }
    }
  }
}
GemsFeed
{
  "type": "object",
  "description": "A page of discovery picks, each one a complete token page.",
  "required": [
    "items",
    "asOf",
    "placeholder"
  ],
  "properties": {
    "items": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/GemItem"
      }
    },
    "asOf": {
      "type": "string",
      "format": "date-time",
      "description": "When this feed was composed."
    },
    "placeholder": {
      "type": "boolean",
      "description": "True while the picks are the hand-written list rather than the output\nof the candidate job. Clients should say so rather than implying a\nranking exists.\n"
    }
  }
}
GemItem
{
  "type": "object",
  "description": "One pick — why it is in the list, and its token page.",
  "required": [
    "id",
    "network",
    "ref",
    "note",
    "page"
  ],
  "properties": {
    "id": {
      "type": "string",
      "description": "Stable id for the pick: {network}/{canonical token id}."
    },
    "network": {
      "type": "string"
    },
    "ref": {
      "type": "string",
      "description": "The ref as requested",
      "so a client can re-read the page itself.": null
    },
    "note": {
      "type": "string",
      "description": "Why this token is in the list",
      "in the feed's own words.": null
    },
    "page": {
      "$ref": "#/components/schemas/TokenPage"
    }
  }
}
NewsFeed
{
  "type": "object",
  "description": "A page of aggregated stories plus the wire's own freshness.",
  "required": [
    "items",
    "asOf",
    "stale"
  ],
  "properties": {
    "items": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/NewsItem"
      }
    },
    "asOf": {
      "type": "string",
      "format": "date-time",
      "nullable": true,
      "description": "When the wire was last ingested. Null on a deployment whose\ningester has never completed a run — reported as null rather than\nas \"now\".\n"
    },
    "stale": {
      "type": "boolean",
      "description": "No ingest completed recently: what follows may be behind. Render\nthis rather than implying freshness.\n"
    }
  }
}
NewsItem
{
  "type": "object",
  "description": "One story. `id` is stable across re-fetches as later pickups join it,\nso it is safe as a client-side dedupe or read-mark key.\n",
  "required": [
    "id",
    "headline",
    "summary",
    "topics",
    "impact",
    "tickers",
    "publisher",
    "url",
    "imageMediaKey",
    "publishedAt",
    "sourceCount"
  ],
  "properties": {
    "id": {
      "type": "string",
      "example": "01K2C8ZR4G0000000000000000"
    },
    "headline": {
      "type": "string"
    },
    "summary": {
      "type": "string",
      "description": "A short extractive excerpt from the publisher, with boilerplate\nremoved. Empty when the feed carried nothing usable — never\ngenerated text, and never the article body.\n"
    },
    "topics": {
      "type": "array",
      "description": "Ignore unknown values; do not drop the item.",
      "items": {
        "type": "string",
        "enum": [
          "trend",
          "macro",
          "chains",
          "policy",
          "security",
          "flows",
          "protocol"
        ]
      }
    },
    "impact": {
      "type": "string",
      "enum": [
        "breaking",
        "watch",
        "normal"
      ],
      "description": "`breaking` is corroborated (two or more publishers) and recent;\n`watch` is a security story or a dated event to act before.\n"
    },
    "tickers": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/NewsTicker"
      }
    },
    "publisher": {
      "type": "string",
      "description": "The representative member's publisher. Always display it."
    },
    "url": {
      "type": "string",
      "description": "Canonical article URL at that publisher."
    },
    "imageMediaKey": {
      "type": "string",
      "nullable": true,
      "description": "Key for `GET /v1/media/{key}` — the lead image, proxied. Null when\nthe story has no usable image. Clients must not be given, and\nshould not seek, the publisher's own CDN URL: fetching it directly\nwould tell that CDN which stories the user reads.\n"
    },
    "publishedAt": {
      "type": "string",
      "format": "date-time",
      "description": "When the story broke — the earliest member's stamp."
    },
    "sourceCount": {
      "type": "integer",
      "description": "How many publishers carried this story, this one included."
    }
  }
}
NewsItemDetail
{
  "allOf": [
    {
      "$ref": "#/components/schemas/NewsItem"
    },
    {
      "type": "object",
      "required": [
        "sources"
      ],
      "properties": {
        "sources": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/NewsSource"
          }
        }
      }
    }
  ]
}
NewsSource
{
  "type": "object",
  "description": "One publisher's article within a story.",
  "required": [
    "publisher",
    "url",
    "title",
    "publishedAt",
    "subtitle",
    "abstract",
    "body",
    "summary",
    "imageMediaKey"
  ],
  "properties": {
    "publisher": {
      "type": "string"
    },
    "url": {
      "type": "string"
    },
    "title": {
      "type": "string"
    },
    "publishedAt": {
      "type": "string",
      "format": "date-time"
    },
    "subtitle": {
      "type": "string",
      "nullable": true,
      "description": "Reserved for feeds that carry a deck. Always null today."
    },
    "abstract": {
      "type": "string",
      "nullable": true,
      "description": "The publisher's own excerpt, attributed to them. Null when the feed\ncarried nothing usable — never another outlet's text.\n"
    },
    "body": {
      "type": "string",
      "nullable": true,
      "description": "Always null. Article bodies are not licensed, so they are neither\nstored nor served; link out to `url` instead of rendering in-app.\n"
    },
    "summary": {
      "type": "string",
      "nullable": true,
      "description": "Reserved for a generated per-article summary. Always null today —\n`abstract` is the publisher's text, this would not be.\n"
    },
    "imageMediaKey": {
      "type": "string",
      "nullable": true,
      "description": "Key for `GET /v1/media/{key}`, set only when this article supplied\nthe story's lead image. Null otherwise, including when the article\nhas a picture the story did not adopt — the proxy holds one image\nper story, and pointing here at a key it does not hold would 404.\n"
    }
  }
}
NewsReactions
{
  "type": "object",
  "description": "Per-asset price observations around one story.",
  "required": [
    "eventId",
    "publishedAt",
    "asOf",
    "currency",
    "source",
    "benchmark",
    "assets"
  ],
  "properties": {
    "eventId": {
      "type": "string",
      "example": "01K2C8ZR4G0000000000000000"
    },
    "publishedAt": {
      "type": "string",
      "format": "date-time",
      "description": "The story stamp every window is measured from."
    },
    "asOf": {
      "type": "string",
      "format": "date-time",
      "description": "When the measurement ran; what makes `pending` readable."
    },
    "currency": {
      "type": "string",
      "example": "USD"
    },
    "source": {
      "type": "string",
      "example": "CoinGecko",
      "description": "Who supplied the observations. Display it."
    },
    "benchmark": {
      "type": "object",
      "description": "The asset every `benchmark*` field is measured against.",
      "required": [
        "coinId",
        "symbol"
      ],
      "properties": {
        "coinId": {
          "type": "string",
          "example": "bitcoin"
        },
        "symbol": {
          "type": "string",
          "example": "BTC"
        }
      }
    },
    "assets": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/NewsReactionAsset"
      }
    },
    "unavailable": {
      "type": "boolean",
      "description": "The deployment serving this has no price source wired, so `assets`\nis empty and says nothing about the market. Absent on a server that\ncan measure.\n"
    }
  }
}
NewsReactionAsset
{
  "type": "object",
  "description": "One ticker from the story, measured over every window.",
  "required": [
    "coinId",
    "symbol",
    "points",
    "windows"
  ],
  "properties": {
    "coinId": {
      "type": "string",
      "nullable": true,
      "description": "Same id space as `/v1/prices`. Null when the ticker resolved to no\ncoin — `points` and every window are then empty or `unavailable`,\nnever zero.\n"
    },
    "symbol": {
      "type": "string",
      "description": "Uppercase display ticker."
    },
    "points": {
      "type": "array",
      "description": "The observations the windows were computed from, oldest first, so a\nclient can draw the span rather than trust the numbers blind. Spans\nthe half hour before the stamp to at most 24 hours after it.\n",
      "items": {
        "$ref": "#/components/schemas/PriceSample"
      }
    },
    "windows": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/NewsReactionWindow"
      }
    }
  }
}
NewsReactionWindow
{
  "type": "object",
  "description": "One span starting at the publish stamp. Report `status` before any\nnumber: a null `returnPct` is unknown, never flat.\n",
  "required": [
    "seconds",
    "status",
    "returnPct",
    "baseline",
    "endpoint",
    "benchmarkReturnPct",
    "benchmarkBaseline",
    "benchmarkEndpoint",
    "differencePp"
  ],
  "properties": {
    "seconds": {
      "type": "integer",
      "enum": [
        300,
        900,
        3600,
        86400
      ],
      "description": "Window length after the publish stamp."
    },
    "status": {
      "type": "string",
      "enum": [
        "measured",
        "pending",
        "unavailable"
      ],
      "description": "`measured` — both endpoints observed within tolerance.\n`pending` — the window has not closed yet; ask again later.\n`unavailable` — no usable observation near an endpoint. Every\nnumber below is null unless this is `measured`.\n"
    },
    "returnPct": {
      "type": "number",
      "nullable": true,
      "description": "Move from `baseline` to `endpoint`, in percent."
    },
    "baseline": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PriceSample"
        }
      ],
      "nullable": true,
      "description": "The last observation at or before the publish stamp — the actual\none used, not the stamp itself, so the lag is auditable.\n"
    },
    "endpoint": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PriceSample"
        }
      ],
      "nullable": true,
      "description": "The last observation at or before the window's end."
    },
    "benchmarkReturnPct": {
      "type": "number",
      "nullable": true,
      "description": "The benchmark's move over the same window. Null when its endpoints\nare not within a minute of this asset's, because differencing\nspans that do not line up would invent a number. Also null when\nthe asset *is* the benchmark.\n"
    },
    "benchmarkBaseline": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PriceSample"
        }
      ],
      "nullable": true
    },
    "benchmarkEndpoint": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PriceSample"
        }
      ],
      "nullable": true
    },
    "differencePp": {
      "type": "number",
      "nullable": true,
      "description": "`returnPct - benchmarkReturnPct`, in percentage points: how far the\nasset moved against the market. The nearest honest thing to a\nstory-specific signal, and still not causation.\n"
    }
  }
}
PriceSample
{
  "type": "array",
  "description": "One observation — `[epochMilliseconds, priceUsd]`.",
  "minItems": 2,
  "maxItems": 2,
  "items": {
    "type": "number"
  },
  "example": [
    1789334074000,
    64250.12
  ]
}
NewsTicker
{
  "type": "object",
  "description": "An asset a story is about.",
  "required": [
    "symbol",
    "coinId",
    "change24hPct"
  ],
  "properties": {
    "symbol": {
      "type": "string",
      "description": "Uppercase display ticker. Not a join key — tickers collide."
    },
    "coinId": {
      "type": "string",
      "nullable": true,
      "description": "Canonical coin id, the same id space as `/v1/prices`. This is the\njoin value against a client's own assets.\n"
    },
    "change24hPct": {
      "type": "number",
      "nullable": true,
      "description": "The **asset's** 24-hour move, joined on `coinId`. Deliberately not\n\"the move this headline caused\": that needs a window around the\npublish stamp, and naming a 24-hour figure that way would be false.\nNull when no quote is on hand.\n"
    }
  }
}
TrafficWindow
{
  "type": "object",
  "description": "Aggregate traffic over a sliding window (landing page stats).",
  "required": [
    "clients",
    "requests",
    "errors"
  ],
  "properties": {
    "clients": {
      "type": "integer",
      "description": "Distinct authenticated devices seen in the window."
    },
    "requests": {
      "type": "integer"
    },
    "errors": {
      "type": "integer",
      "description": "Responses with status 500 or above."
    }
  }
}
Interval
{
  "type": "string",
  "enum": [
    "1h",
    "1d"
  ],
  "default": "1h"
}
Ticker
{
  "type": "string",
  "pattern": "^[A-Za-z0-9._-]{1,20}$"
}
DecimalAmount
{
  "type": "string",
  "pattern": "^(?:0|[1-9]\\d*)(?:\\.\\d{1,18})?$",
  "maxLength": 80,
  "example": "1.25"
}
ApiError
{
  "type": "object",
  "required": [
    "error"
  ],
  "properties": {
    "error": {
      "type": "string",
      "example": "BAD_REQUEST"
    },
    "detail": {
      "description": "Safe validation detail. Upstream response bodies and secrets are never exposed.",
      "nullable": true
    },
    "topics": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "additionalProperties": true
}
CacheInfo
{
  "type": "object",
  "required": [
    "hit",
    "timestamp"
  ],
  "description": "Provenance for the returned payload, never a promise that all upstream data exists.",
  "properties": {
    "hit": {
      "type": "boolean",
      "description": "True only when the complete response was served from cached data."
    },
    "timestamp": {
      "type": "string",
      "format": "date-time",
      "nullable": true,
      "description": "Timestamp of the newest cached material used, or null when no cache contributed."
    },
    "partial": {
      "type": "boolean",
      "description": "True when cached and newly fetched or unavailable portions were combined."
    }
  }
}
AttestationChallenge
{
  "type": "object",
  "required": [
    "challenge",
    "expiresIn"
  ],
  "properties": {
    "challenge": {
      "type": "string",
      "minLength": 16
    },
    "expiresIn": {
      "type": "integer",
      "example": 300,
      "description": "Lifetime in seconds."
    }
  }
}
DeviceEnrollment
{
  "type": "object",
  "required": [
    "platform"
  ],
  "additionalProperties": false,
  "properties": {
    "platform": {
      "type": "string",
      "enum": [
        "ios",
        "android",
        "macos",
        "windows",
        "linux"
      ]
    },
    "appVersion": {
      "type": "string",
      "maxLength": 50
    },
    "attestation": {
      "type": "object",
      "required": [
        "provider",
        "challenge"
      ],
      "additionalProperties": false,
      "properties": {
        "provider": {
          "type": "string",
          "enum": [
            "development",
            "apple-app-attest",
            "play-integrity"
          ]
        },
        "challenge": {
          "type": "string",
          "minLength": 16,
          "maxLength": 200
        },
        "token": {
          "type": "string",
          "maxLength": 32000,
          "writeOnly": true
        }
      }
    }
  }
}
SupportAuthSession
{
  "type": "object",
  "additionalProperties": false,
  "required": [
    "accessToken",
    "refreshToken",
    "accessExpiresAt",
    "refreshExpiresAt"
  ],
  "properties": {
    "accessToken": {
      "type": "string",
      "writeOnly": true
    },
    "refreshToken": {
      "type": "string",
      "writeOnly": true
    },
    "accessExpiresAt": {
      "type": "string",
      "format": "date-time"
    },
    "refreshExpiresAt": {
      "type": "string",
      "format": "date-time"
    },
    "walletAddress": {
      "type": "string",
      "pattern": "^0x[0-9a-fA-F]{40}$",
      "description": "Present after signature verification; omitted during refresh."
    }
  }
}
OpenTicketRequest
{
  "type": "object",
  "additionalProperties": false,
  "required": [
    "subject",
    "body"
  ],
  "properties": {
    "topic": {
      "type": "string",
      "enum": [
        "transaction",
        "wallet",
        "account",
        "security",
        "other"
      ],
      "default": "other"
    },
    "subject": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "body": {
      "type": "string",
      "minLength": 1,
      "maxLength": 8000
    },
    "osVersion": {
      "type": "string",
      "maxLength": 64
    },
    "locale": {
      "type": "string",
      "maxLength": 35
    },
    "buildMode": {
      "type": "string",
      "enum": [
        "debug",
        "profile",
        "release"
      ]
    },
    "appVersion": {
      "type": "string",
      "maxLength": 64,
      "description": "Fallback only — the device's enrolment row is the authority for its version when it has one."
    },
    "context": {
      "type": "object",
      "additionalProperties": true,
      "description": "Free-form client diagnostics. Sanitised server-side: capped, flattened to scalars, and redacted string by string (and array-wide, so a phrase split one-word-per-element is still caught)."
    },
    "attachments": {
      "type": "array",
      "maxItems": 3,
      "description": "User-approved evidence. Transaction attachments retain public addresses, contract metadata, calldata and raw signed/provider data; secret-key fields are removed server-side. Images are bounded JPEGs with metadata removed at both the client and service boundary.",
      "items": {
        "$ref": "#/components/schemas/SupportAttachmentInput"
      }
    }
  }
}
SupportAttachmentInput
{
  "type": "object",
  "additionalProperties": false,
  "required": [
    "kind",
    "name",
    "data"
  ],
  "properties": {
    "kind": {
      "type": "string",
      "enum": [
        "transaction",
        "diagnostic"
      ]
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 120
    },
    "contentType": {
      "type": "string",
      "enum": [
        "application/json",
        "image/jpeg"
      ]
    },
    "data": {
      "type": "object",
      "additionalProperties": true,
      "description": "Structured JSON, or for image attachments an object containing encoding=base64, JPEG bytes, width and height."
    }
  }
}
SupportAttachment
{
  "allOf": [
    {
      "$ref": "#/components/schemas/SupportAttachmentInput"
    },
    {
      "type": "object",
      "required": [
        "id",
        "contentType",
        "byteSize",
        "createdAt"
      ],
      "properties": {
        "id": {
          "type": "string"
        },
        "byteSize": {
          "type": "integer",
          "minimum": 0
        },
        "createdAt": {
          "type": "string",
          "format": "date-time"
        }
      }
    }
  ]
}
SupportTicket
{
  "type": "object",
  "required": [
    "id",
    "topic",
    "subject",
    "status",
    "hasAttachment",
    "hasUnreadReply",
    "lastMessageAt",
    "createdAt"
  ],
  "properties": {
    "id": {
      "type": "string"
    },
    "topic": {
      "type": "string",
      "enum": [
        "transaction",
        "wallet",
        "account",
        "security",
        "other"
      ]
    },
    "subject": {
      "type": "string",
      "description": "Post-redaction."
    },
    "status": {
      "type": "string",
      "enum": [
        "open",
        "pending",
        "resolved",
        "closed"
      ]
    },
    "hasAttachment": {
      "type": "boolean"
    },
    "hasUnreadReply": {
      "type": "boolean"
    },
    "lastMessageAt": {
      "type": "string",
      "format": "date-time"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    }
  }
}
SupportMessage
{
  "type": "object",
  "required": [
    "id",
    "author",
    "body",
    "createdAt"
  ],
  "properties": {
    "id": {
      "type": "string"
    },
    "author": {
      "type": "string",
      "enum": [
        "user",
        "staff",
        "system"
      ],
      "description": "The staff member's identity is deliberately not exposed. The app renders its built-in anti-phishing warning for every thread."
    },
    "body": {
      "type": "string",
      "description": "Post-redaction."
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    }
  }
}
TicketThread
{
  "type": "object",
  "required": [
    "ticket",
    "messages",
    "attachments"
  ],
  "properties": {
    "ticket": {
      "$ref": "#/components/schemas/SupportTicket"
    },
    "messages": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/SupportMessage"
      }
    },
    "attachments": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/SupportAttachment"
      }
    },
    "secretRemoved": {
      "type": "boolean",
      "description": "True when redaction fired on something the user just sent. The app owns and renders the corresponding warning text."
    }
  }
}
CrashReportRequest
{
  "type": "object",
  "additionalProperties": false,
  "required": [
    "platform",
    "buildMode",
    "source",
    "fatal",
    "errorType",
    "message",
    "stack",
    "occurredAt"
  ],
  "properties": {
    "platform": {
      "type": "string",
      "minLength": 1,
      "maxLength": 32,
      "example": "ios"
    },
    "appVersion": {
      "type": "string",
      "maxLength": 64,
      "example": "1.0.0"
    },
    "buildMode": {
      "type": "string",
      "enum": [
        "debug",
        "profile",
        "release"
      ]
    },
    "source": {
      "type": "string",
      "enum": [
        "flutter",
        "platform"
      ]
    },
    "fatal": {
      "type": "boolean"
    },
    "errorType": {
      "type": "string",
      "minLength": 1,
      "maxLength": 160
    },
    "message": {
      "type": "string",
      "maxLength": 4000
    },
    "stack": {
      "type": "string",
      "maxLength": 24000
    },
    "occurredAt": {
      "type": "string",
      "format": "date-time"
    }
  }
}
NetworkOverview
{
  "type": "object",
  "additionalProperties": false,
  "required": [
    "network",
    "news",
    "popularTokens"
  ],
  "properties": {
    "network": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "name",
        "description",
        "websiteUrl"
      ],
      "properties": {
        "id": {
          "type": "string",
          "example": "base"
        },
        "name": {
          "type": "string",
          "example": "Base"
        },
        "description": {
          "type": "string"
        },
        "websiteUrl": {
          "type": "string",
          "format": "uri",
          "nullable": true
        }
      }
    },
    "news": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "available",
        "items",
        "asOf",
        "stale"
      ],
      "properties": {
        "available": {
          "type": "boolean"
        },
        "items": {
          "type": "array",
          "maxItems": 4,
          "items": {
            "$ref": "#/components/schemas/NewsItem"
          }
        },
        "asOf": {
          "type": "string",
          "format": "date-time",
          "nullable": true
        },
        "stale": {
          "type": "boolean"
        }
      }
    },
    "popularTokens": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "available",
        "items",
        "asOf",
        "stale",
        "metric",
        "source"
      ],
      "properties": {
        "available": {
          "type": "boolean"
        },
        "items": {
          "type": "array",
          "maxItems": 5,
          "items": {
            "$ref": "#/components/schemas/PopularNetworkToken"
          }
        },
        "asOf": {
          "type": "string",
          "format": "date-time",
          "nullable": true
        },
        "stale": {
          "type": "boolean"
        },
        "metric": {
          "type": "string",
          "enum": [
            "on_chain_holders"
          ]
        },
        "source": {
          "type": "string",
          "enum": [
            "coingecko"
          ],
          "nullable": true
        }
      }
    }
  }
}
PopularNetworkToken
{
  "type": "object",
  "additionalProperties": false,
  "required": [
    "tokenId",
    "address",
    "symbol",
    "name",
    "decimals",
    "logoMediaKey",
    "logoUrl",
    "coingeckoId",
    "marketCapUsd",
    "holderCount",
    "holderCountAtLeast",
    "priceUsd",
    "change24hPct",
    "rank",
    "source"
  ],
  "properties": {
    "tokenId": {
      "type": "string"
    },
    "address": {
      "type": "string"
    },
    "symbol": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "decimals": {
      "type": "integer",
      "minimum": 0,
      "maximum": 255
    },
    "logoMediaKey": {
      "type": "string",
      "nullable": true
    },
    "logoUrl": {
      "type": "string",
      "format": "uri",
      "nullable": true
    },
    "coingeckoId": {
      "type": "string",
      "nullable": true
    },
    "marketCapUsd": {
      "type": "number",
      "format": "double",
      "minimum": 0
    },
    "holderCount": {
      "type": "integer",
      "minimum": 0,
      "nullable": true
    },
    "holderCountAtLeast": {
      "type": "boolean"
    },
    "priceUsd": {
      "type": "number",
      "format": "double",
      "nullable": true
    },
    "change24hPct": {
      "type": "number",
      "format": "double",
      "nullable": true
    },
    "rank": {
      "type": "integer",
      "minimum": 1
    },
    "source": {
      "type": "string",
      "enum": [
        "coingecko"
      ]
    }
  }
}
NetworkCapability
{
  "type": "object",
  "required": [
    "id",
    "family",
    "capabilities"
  ],
  "properties": {
    "id": {
      "type": "string",
      "example": "ethereum"
    },
    "family": {
      "type": "string",
      "example": "evm"
    },
    "name": {
      "type": "string"
    },
    "native": {
      "type": "object",
      "additionalProperties": true
    },
    "capabilities": {
      "type": "object",
      "description": "Feature states such as `ok`, `none`, `rpc` or degraded modes.",
      "additionalProperties": true
    }
  },
  "additionalProperties": true
}
SpotPriceResponse
{
  "type": "object",
  "required": [
    "fiat",
    "prices",
    "missing",
    "invalid"
  ],
  "properties": {
    "fiat": {
      "type": "string",
      "example": "usd"
    },
    "prices": {
      "type": "object",
      "additionalProperties": {
        "$ref": "#/components/schemas/SpotPrice"
      }
    },
    "missing": {
      "type": "array",
      "description": "Syntactically valid IDs for which no price was available.",
      "items": {
        "type": "string"
      }
    },
    "invalid": {
      "type": "array",
      "description": "IDs rejected by the server's price-ID grammar.",
      "items": {
        "type": "string"
      }
    }
  }
}
SpotPrice
{
  "type": "object",
  "required": [
    "sourceId",
    "price",
    "change24hPct",
    "asOf",
    "stale",
    "source",
    "cache"
  ],
  "properties": {
    "sourceId": {
      "type": "string"
    },
    "price": {
      "type": "number",
      "format": "double"
    },
    "change24hPct": {
      "type": "number",
      "format": "double",
      "nullable": true
    },
    "asOf": {
      "type": "string",
      "format": "date-time"
    },
    "stale": {
      "type": "boolean"
    },
    "source": {
      "type": "string"
    },
    "cache": {
      "$ref": "#/components/schemas/CacheInfo"
    }
  }
}
PricePoint
{
  "type": "array",
  "description": "Two-item tuple `[unixTimestampSeconds, value]`; used for both close price and volume series.",
  "minItems": 2,
  "maxItems": 2,
  "items": {
    "type": "number",
    "format": "double"
  },
  "example": [
    1753000000,
    3012.44
  ]
}
PriceResolutionAvailability
{
  "type": "object",
  "required": [
    "interval",
    "from",
    "to",
    "days"
  ],
  "properties": {
    "interval": {
      "$ref": "#/components/schemas/Interval"
    },
    "from": {
      "type": "integer",
      "format": "int64"
    },
    "to": {
      "type": "integer",
      "format": "int64"
    },
    "days": {
      "type": "number",
      "minimum": 0
    }
  }
}
PriceAvailability
{
  "type": "object",
  "required": [
    "from",
    "to",
    "days",
    "resolutions"
  ],
  "properties": {
    "from": {
      "type": "integer",
      "format": "int64",
      "nullable": true
    },
    "to": {
      "type": "integer",
      "format": "int64",
      "nullable": true
    },
    "days": {
      "type": "number",
      "minimum": 0,
      "description": "Available time span expressed in days."
    },
    "resolutions": {
      "type": "array",
      "description": "Exact time coverage available for each stored resolution.",
      "items": {
        "$ref": "#/components/schemas/PriceResolutionAvailability"
      }
    }
  }
}
PriceHistory
{
  "type": "object",
  "required": [
    "id",
    "interval",
    "points",
    "volumes",
    "cache",
    "coverage",
    "window",
    "availability"
  ],
  "properties": {
    "id": {
      "type": "string"
    },
    "interval": {
      "$ref": "#/components/schemas/Interval"
    },
    "points": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/PricePoint"
      }
    },
    "volumes": {
      "type": "array",
      "description": "Parallel 24-hour rolling-volume tuples where the provider supplied volume.",
      "items": {
        "$ref": "#/components/schemas/PricePoint"
      }
    },
    "coverage": {
      "type": "object",
      "required": [
        "cachedPoints",
        "fetchedPoints"
      ],
      "properties": {
        "cachedPoints": {
          "type": "integer",
          "minimum": 0
        },
        "fetchedPoints": {
          "type": "integer",
          "minimum": 0
        }
      }
    },
    "window": {
      "type": "object",
      "required": [
        "from",
        "to",
        "days"
      ],
      "properties": {
        "from": {
          "type": "integer",
          "format": "int64",
          "nullable": true
        },
        "to": {
          "type": "integer",
          "format": "int64",
          "nullable": true
        },
        "days": {
          "type": "number",
          "minimum": 0
        }
      }
    },
    "availability": {
      "$ref": "#/components/schemas/PriceAvailability"
    },
    "cache": {
      "$ref": "#/components/schemas/CacheInfo"
    }
  }
}
PriceHistoryFailure
{
  "type": "object",
  "required": [
    "error",
    "availability"
  ],
  "properties": {
    "error": {
      "type": "string",
      "enum": [
        "UNRECOGNIZED_ID",
        "UPSTREAM_UNAVAILABLE"
      ]
    },
    "availability": {
      "$ref": "#/components/schemas/PriceAvailability"
    }
  }
}
PriceHistoryBatch
{
  "type": "object",
  "required": [
    "from",
    "to",
    "interval",
    "series",
    "cache"
  ],
  "properties": {
    "from": {
      "type": "integer",
      "format": "int64"
    },
    "to": {
      "type": "integer",
      "format": "int64"
    },
    "interval": {
      "$ref": "#/components/schemas/Interval"
    },
    "series": {
      "type": "object",
      "additionalProperties": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/PriceHistory"
          },
          {
            "$ref": "#/components/schemas/PriceHistoryFailure"
          }
        ]
      }
    },
    "cache": {
      "$ref": "#/components/schemas/CacheInfo"
    }
  }
}
AccountReference
{
  "type": "object",
  "required": [
    "network",
    "address"
  ],
  "properties": {
    "network": {
      "type": "string",
      "minLength": 1,
      "maxLength": 64
    },
    "address": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128
    }
  }
}
PortfolioSnapshotRequest
{
  "type": "object",
  "required": [
    "accounts"
  ],
  "properties": {
    "accounts": {
      "type": "array",
      "minItems": 1,
      "maxItems": 200,
      "items": {
        "allOf": [
          {
            "$ref": "#/components/schemas/AccountReference"
          },
          {
            "type": "object",
            "properties": {
              "tokens": {
                "type": "array",
                "maxItems": 200,
                "default": [],
                "items": {
                  "type": "string",
                  "maxLength": 120
                }
              }
            }
          }
        ]
      }
    },
    "fiat": {
      "type": "string",
      "enum": [
        "usd"
      ],
      "default": "usd"
    },
    "refresh": {
      "type": "boolean",
      "default": false,
      "description": "Invalidates the requested balance envelopes before reading. Intended for an explicit foreground refresh."
    }
  }
}
PortfolioRangeRequest
{
  "type": "object",
  "required": [
    "accounts"
  ],
  "properties": {
    "accounts": {
      "type": "array",
      "minItems": 1,
      "maxItems": 200,
      "items": {
        "$ref": "#/components/schemas/AccountReference"
      }
    },
    "range": {
      "type": "string",
      "enum": [
        "7d",
        "30d",
        "90d",
        "1y"
      ],
      "default": "30d"
    }
  }
}
PortfolioSnapshot
{
  "type": "object",
  "required": [
    "totalFiat",
    "byNetwork",
    "accounts",
    "etag"
  ],
  "properties": {
    "totalFiat": {
      "type": "number",
      "format": "double"
    },
    "byNetwork": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "network",
          "fiat"
        ],
        "properties": {
          "network": {
            "type": "string"
          },
          "fiat": {
            "type": "number",
            "format": "double"
          }
        }
      }
    },
    "accounts": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": true
      }
    },
    "etag": {
      "type": "string"
    }
  }
}
AccountBalances
{
  "type": "object",
  "required": [
    "network",
    "address",
    "native",
    "tokens",
    "asOf",
    "stale",
    "cache"
  ],
  "properties": {
    "network": {
      "type": "string"
    },
    "address": {
      "type": "string"
    },
    "native": {
      "type": "object",
      "additionalProperties": true
    },
    "tokens": {
      "type": "array",
      "description": "Each token's `balanceRaw` is on-chain units. A token that scales the amount it displays (a Solana ScaledUiAmount or InterestBearing mint) also carries `displayUnits`, the amount as displayed; `price` is per displayed unit, so `fiat` is `displayUnits × price`.",
      "items": {
        "type": "object",
        "additionalProperties": true
      }
    },
    "asOf": {
      "type": "string",
      "format": "date-time"
    },
    "reference": {
      "$ref": "#/components/schemas/BlockchainReference"
    },
    "finality": {
      "type": "string",
      "enum": [
        "unknown",
        "latest",
        "confirmed",
        "safe",
        "finalized"
      ]
    },
    "stale": {
      "type": "boolean"
    },
    "cache": {
      "$ref": "#/components/schemas/CacheInfo"
    }
  },
  "additionalProperties": true
}
BlockchainReference
{
  "type": "object",
  "required": [
    "kind",
    "value"
  ],
  "description": "Optional chain-native position shared by all balances in the account response. Omitted unless the read was pinned to that exact state.",
  "properties": {
    "kind": {
      "type": "string",
      "enum": [
        "block",
        "slot",
        "round",
        "ledger",
        "ledgerVersion",
        "checkpoint",
        "consensusTimestamp",
        "certifiedHeight",
        "sequence"
      ]
    },
    "value": {
      "type": "string",
      "minLength": 1,
      "description": "Text rather than a JSON number so positions above 2^53 remain exact."
    },
    "hash": {
      "type": "string"
    }
  }
}
AnalyticsPayload
{
  "type": "object",
  "description": "Shape is endpoint-specific; consult the endpoint summary and examples in the Flutter integration guide.",
  "properties": {
    "asOf": {
      "type": "string",
      "format": "date-time"
    },
    "stale": {
      "type": "boolean"
    },
    "cache": {
      "$ref": "#/components/schemas/CacheInfo"
    }
  },
  "additionalProperties": true
}
AccountCoverage
{
  "type": "object",
  "required": [
    "covered",
    "excluded"
  ],
  "properties": {
    "covered": {
      "type": "integer",
      "minimum": 0
    },
    "excluded": {
      "type": "integer",
      "minimum": 0
    },
    "unknown": {
      "type": "integer",
      "minimum": 0
    }
  }
}
PortfolioValue
{
  "type": "object",
  "required": [
    "range",
    "from",
    "points",
    "accounts",
    "cache"
  ],
  "properties": {
    "range": {
      "type": "string",
      "enum": [
        "7d",
        "30d",
        "90d",
        "365d"
      ]
    },
    "from": {
      "type": "integer",
      "format": "int64",
      "nullable": true
    },
    "points": {
      "type": "array",
      "description": "Oldest-to-newest `[unixTimestampSeconds, totalUsd]` tuples.",
      "items": {
        "$ref": "#/components/schemas/PricePoint"
      }
    },
    "accounts": {
      "$ref": "#/components/schemas/AccountCoverage"
    },
    "cache": {
      "$ref": "#/components/schemas/CacheInfo"
    }
  }
}
FlowBucket
{
  "type": "object",
  "required": [
    "count",
    "fiat",
    "unpriced"
  ],
  "properties": {
    "count": {
      "type": "integer",
      "minimum": 0
    },
    "fiat": {
      "type": "number",
      "format": "double",
      "nullable": true,
      "description": "Null when no transfer in this direction could be priced; never a fabricated zero."
    },
    "unpriced": {
      "type": "integer",
      "minimum": 0
    }
  }
}
ActivityPoint
{
  "type": "object",
  "required": [
    "t",
    "in",
    "out"
  ],
  "properties": {
    "t": {
      "type": "integer",
      "format": "int64",
      "description": "UTC day in Unix seconds."
    },
    "in": {
      "type": "integer",
      "minimum": 0
    },
    "out": {
      "type": "integer",
      "minimum": 0
    }
  }
}
PortfolioFlows
{
  "type": "object",
  "required": [
    "range",
    "in",
    "out",
    "points",
    "accounts",
    "cache"
  ],
  "properties": {
    "range": {
      "type": "string",
      "enum": [
        "7d",
        "30d",
        "90d",
        "365d"
      ]
    },
    "in": {
      "$ref": "#/components/schemas/FlowBucket"
    },
    "out": {
      "$ref": "#/components/schemas/FlowBucket"
    },
    "points": {
      "type": "array",
      "description": "Days with no activity are omitted.",
      "items": {
        "$ref": "#/components/schemas/ActivityPoint"
      }
    },
    "accounts": {
      "$ref": "#/components/schemas/AccountCoverage"
    },
    "cache": {
      "$ref": "#/components/schemas/CacheInfo"
    }
  }
}
TransferAmount
{
  "type": "object",
  "nullable": true,
  "required": [
    "raw",
    "decimals"
  ],
  "properties": {
    "raw": {
      "type": "string",
      "description": "Integer base-unit amount."
    },
    "decimals": {
      "type": "integer",
      "minimum": 0
    }
  }
}
TransferAsset
{
  "type": "object",
  "nullable": true,
  "required": [
    "chainId",
    "symbol",
    "name",
    "decimals",
    "contractAddress"
  ],
  "properties": {
    "chainId": {
      "type": "string"
    },
    "symbol": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "decimals": {
      "type": "integer",
      "minimum": 0
    },
    "contractAddress": {
      "type": "string",
      "nullable": true
    }
  }
}
Transfer
{
  "type": "object",
  "required": [
    "id",
    "type",
    "status",
    "direction",
    "timestamp",
    "chainId",
    "family",
    "confirmations",
    "metadata"
  ],
  "properties": {
    "id": {
      "type": "string"
    },
    "type": {
      "type": "string",
      "enum": [
        "send",
        "receive",
        "swap",
        "bridge",
        "approve",
        "associate",
        "contractCall",
        "mint",
        "burn",
        "stake",
        "unstake",
        "reward",
        "fee",
        "unknown"
      ]
    },
    "status": {
      "type": "string",
      "enum": [
        "pending",
        "confirmed",
        "failed",
        "dropped"
      ]
    },
    "direction": {
      "type": "string",
      "enum": [
        "incoming",
        "outgoing",
        "selfTransfer"
      ]
    },
    "timestamp": {
      "type": "string",
      "format": "date-time"
    },
    "chainId": {
      "type": "string"
    },
    "family": {
      "type": "string"
    },
    "hash": {
      "type": "string",
      "nullable": true
    },
    "blockNumber": {
      "type": "integer",
      "format": "int64",
      "nullable": true
    },
    "confirmations": {
      "type": "integer",
      "minimum": 0
    },
    "fromAddress": {
      "type": "string",
      "nullable": true
    },
    "toAddress": {
      "type": "string",
      "nullable": true
    },
    "asset": {
      "$ref": "#/components/schemas/TransferAsset"
    },
    "amount": {
      "$ref": "#/components/schemas/TransferAmount"
    },
    "toAsset": {
      "$ref": "#/components/schemas/TransferAsset"
    },
    "toAmount": {
      "$ref": "#/components/schemas/TransferAmount"
    },
    "destinationChainId": {
      "type": "string",
      "nullable": true
    },
    "destinationHash": {
      "type": "string",
      "nullable": true
    },
    "fee": {
      "$ref": "#/components/schemas/TransferAmount"
    },
    "feeSymbol": {
      "type": "string",
      "nullable": true
    },
    "fiatValue": {
      "type": "number",
      "format": "double",
      "nullable": true
    },
    "fiatCurrency": {
      "type": "string",
      "nullable": true
    },
    "memo": {
      "type": "string",
      "nullable": true
    },
    "error": {
      "type": "string",
      "nullable": true
    },
    "metadata": {
      "type": "object",
      "additionalProperties": true
    }
  }
}
TransfersPage
{
  "type": "object",
  "required": [
    "network",
    "address",
    "items",
    "operations",
    "cursor",
    "hasMore",
    "nextBefore",
    "historyComplete",
    "window",
    "cache"
  ],
  "properties": {
    "network": {
      "type": "string"
    },
    "address": {
      "type": "string"
    },
    "items": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Transfer"
      }
    },
    "operations": {
      "type": "array",
      "description": "Page-aligned economic read models; independent of the stable transfer items.",
      "items": {
        "$ref": "#/components/schemas/EconomicOperation"
      }
    },
    "cursor": {
      "type": "string",
      "nullable": true,
      "description": "Account high-water cursor for subsequent delta sync."
    },
    "hasMore": {
      "type": "boolean"
    },
    "nextBefore": {
      "type": "string",
      "nullable": true,
      "description": "Cursor for the next display page."
    },
    "historyComplete": {
      "type": "boolean",
      "description": "Whether upstream history backfill reached its end."
    },
    "window": {
      "type": "object",
      "required": [
        "from",
        "to",
        "flow"
      ],
      "properties": {
        "from": {
          "type": "integer",
          "format": "int64",
          "nullable": true
        },
        "to": {
          "type": "integer",
          "format": "int64",
          "nullable": true
        },
        "flow": {
          "type": "string",
          "enum": [
            "all",
            "in",
            "out",
            "neutral"
          ]
        }
      }
    },
    "cache": {
      "$ref": "#/components/schemas/CacheInfo"
    }
  }
}
EconomicOperation
{
  "type": "object",
  "required": [
    "id",
    "recordId",
    "kind",
    "triggeredAt",
    "settledAt",
    "status",
    "hash",
    "venue",
    "confidence",
    "legs"
  ],
  "properties": {
    "id": {
      "type": "string"
    },
    "recordId": {
      "type": "string"
    },
    "kind": {
      "type": "string",
      "enum": [
        "buy",
        "sell",
        "swap",
        "transfer",
        "bridge",
        "mint",
        "burn",
        "reward",
        "fee",
        "unknown"
      ]
    },
    "triggeredAt": {
      "type": "string",
      "format": "date-time"
    },
    "settledAt": {
      "type": "string",
      "format": "date-time",
      "nullable": true
    },
    "status": {
      "type": "string",
      "enum": [
        "pending",
        "confirmed",
        "failed",
        "dropped"
      ]
    },
    "hash": {
      "type": "string",
      "nullable": true
    },
    "venue": {
      "type": "string",
      "nullable": true
    },
    "confidence": {
      "type": "string",
      "enum": [
        "source",
        "derived",
        "estimated"
      ]
    },
    "legs": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/EconomicOperationLeg"
      }
    }
  }
}
EconomicOperationLeg
{
  "type": "object",
  "required": [
    "index",
    "role",
    "tokenId",
    "asset",
    "amount",
    "direction",
    "unitPriceUsd",
    "totalUsd",
    "valuationSource",
    "valuationSourceId",
    "valuationAsOf"
  ],
  "properties": {
    "index": {
      "type": "integer",
      "minimum": 0
    },
    "role": {
      "type": "string",
      "enum": [
        "input",
        "output",
        "fee"
      ]
    },
    "tokenId": {
      "type": "string"
    },
    "asset": {
      "$ref": "#/components/schemas/TransferAsset"
    },
    "amount": {
      "$ref": "#/components/schemas/TransferAmount"
    },
    "direction": {
      "type": "string",
      "enum": [
        "in",
        "out",
        "neutral"
      ]
    },
    "unitPriceUsd": {
      "type": "number",
      "format": "double",
      "nullable": true
    },
    "totalUsd": {
      "type": "number",
      "format": "double",
      "nullable": true
    },
    "valuationSource": {
      "type": "string",
      "nullable": true,
      "enum": [
        "execution",
        "appSnapshot",
        "1h",
        "1d",
        "unknownEstimate",
        "manual"
      ]
    },
    "valuationSourceId": {
      "type": "string",
      "nullable": true
    },
    "valuationAsOf": {
      "type": "string",
      "format": "date-time",
      "nullable": true
    }
  }
}
OperationLegIdentity
{
  "type": "object",
  "additionalProperties": false,
  "required": [
    "operationId",
    "legIndex"
  ],
  "properties": {
    "operationId": {
      "type": "string",
      "minLength": 1,
      "maxLength": 600
    },
    "legIndex": {
      "type": "integer",
      "minimum": 0,
      "maximum": 32
    }
  }
}
OperationBasisWrite
{
  "allOf": [
    {
      "$ref": "#/components/schemas/OperationLegIdentity"
    },
    {
      "type": "object",
      "required": [
        "unitPriceUsd"
      ],
      "properties": {
        "unitPriceUsd": {
          "type": "number",
          "format": "double",
          "exclusiveMinimum": 0,
          "maximum": 1000000000000000000
        }
      }
    }
  ]
}
TransferSummary
{
  "type": "object",
  "required": [
    "range",
    "in",
    "out",
    "cache"
  ],
  "properties": {
    "range": {
      "type": "string"
    },
    "in": {
      "$ref": "#/components/schemas/FlowBucket"
    },
    "out": {
      "$ref": "#/components/schemas/FlowBucket"
    },
    "cache": {
      "$ref": "#/components/schemas/CacheInfo"
    }
  }
}
ActivityChart
{
  "type": "object",
  "required": [
    "range",
    "interval",
    "points",
    "cache"
  ],
  "properties": {
    "range": {
      "type": "string"
    },
    "interval": {
      "type": "string",
      "enum": [
        "1d"
      ]
    },
    "points": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/ActivityPoint"
      }
    },
    "cache": {
      "$ref": "#/components/schemas/CacheInfo"
    }
  }
}
ValueChart
{
  "type": "object",
  "required": [
    "range",
    "points",
    "cache"
  ],
  "properties": {
    "range": {
      "type": "string"
    },
    "points": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/PricePoint"
      }
    },
    "cache": {
      "$ref": "#/components/schemas/CacheInfo"
    }
  }
}
NftMedia
{
  "type": "object",
  "required": [
    "image",
    "imageProxy"
  ],
  "properties": {
    "image": {
      "type": "string",
      "nullable": true,
      "description": "Original provider media URL."
    },
    "imageProxy": {
      "type": "string",
      "nullable": true,
      "description": "Relative verified-media proxy path."
    }
  }
}
NftItem
{
  "type": "object",
  "required": [
    "id",
    "contractAddress",
    "tokenId",
    "standard",
    "name",
    "collectionName",
    "media",
    "flags",
    "firstSeenAt"
  ],
  "properties": {
    "id": {
      "type": "string"
    },
    "contractAddress": {
      "type": "string",
      "nullable": true
    },
    "tokenId": {
      "type": "string"
    },
    "standard": {
      "type": "string",
      "nullable": true
    },
    "name": {
      "type": "string",
      "nullable": true
    },
    "collectionName": {
      "type": "string",
      "nullable": true
    },
    "media": {
      "$ref": "#/components/schemas/NftMedia"
    },
    "flags": {
      "type": "object",
      "additionalProperties": true
    },
    "firstSeenAt": {
      "type": "string",
      "format": "date-time"
    }
  }
}
NftInventory
{
  "type": "object",
  "required": [
    "network",
    "address",
    "items",
    "stale",
    "cache"
  ],
  "properties": {
    "network": {
      "type": "string"
    },
    "address": {
      "type": "string"
    },
    "items": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/NftItem"
      }
    },
    "stale": {
      "type": "boolean",
      "description": "True when a failed rescan caused the durable inventory to be served."
    },
    "cache": {
      "$ref": "#/components/schemas/CacheInfo"
    }
  }
}
Trustline
{
  "type": "object",
  "required": [
    "tokenId",
    "symbol",
    "balanceRaw",
    "decimals",
    "limit"
  ],
  "properties": {
    "tokenId": {
      "type": "string"
    },
    "symbol": {
      "type": "string",
      "nullable": true
    },
    "balanceRaw": {
      "type": "string",
      "nullable": true
    },
    "decimals": {
      "type": "integer",
      "nullable": true,
      "minimum": 0
    },
    "limit": {
      "type": "string",
      "nullable": true
    }
  }
}
TrustlineInventory
{
  "type": "object",
  "required": [
    "network",
    "address",
    "trustlines"
  ],
  "properties": {
    "network": {
      "type": "string"
    },
    "address": {
      "type": "string"
    },
    "trustlines": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Trustline"
      }
    }
  }
}
AddressInsight
{
  "type": "object",
  "required": [
    "exists",
    "kind",
    "txCount",
    "txCountFloor",
    "asOf",
    "stale",
    "cache"
  ],
  "properties": {
    "exists": {
      "type": "boolean"
    },
    "kind": {
      "type": "string",
      "enum": [
        "eoa",
        "contract",
        "account",
        "unknown"
      ]
    },
    "txCount": {
      "type": "integer",
      "nullable": true,
      "minimum": 0
    },
    "txCountFloor": {
      "type": "boolean"
    },
    "asOf": {
      "type": "string",
      "format": "date-time"
    },
    "stale": {
      "type": "boolean"
    },
    "cache": {
      "$ref": "#/components/schemas/CacheInfo"
    }
  }
}
TransactionDetail
{
  "type": "object",
  "required": [
    "status",
    "blockNumber",
    "detail",
    "raw",
    "fetchedAt",
    "cache"
  ],
  "properties": {
    "status": {
      "type": "string",
      "enum": [
        "pending",
        "confirmed",
        "failed",
        "unknown"
      ]
    },
    "blockNumber": {
      "type": "integer",
      "format": "int64",
      "nullable": true
    },
    "detail": {
      "type": "object",
      "additionalProperties": true
    },
    "raw": {
      "nullable": true
    },
    "fetchedAt": {
      "type": "string",
      "format": "date-time"
    },
    "cache": {
      "$ref": "#/components/schemas/CacheInfo"
    }
  }
}
SafetySignal
{
  "type": "object",
  "required": [
    "label",
    "detail",
    "sentiment"
  ],
  "properties": {
    "label": {
      "type": "string"
    },
    "detail": {
      "type": "string"
    },
    "sentiment": {
      "type": "string",
      "enum": [
        "good",
        "caution",
        "bad",
        "neutral"
      ]
    }
  }
}
TokenSafety
{
  "type": "object",
  "required": [
    "score",
    "level",
    "signals"
  ],
  "properties": {
    "score": {
      "type": "integer",
      "minimum": 0,
      "maximum": 100
    },
    "level": {
      "type": "string",
      "enum": [
        "high",
        "medium",
        "low",
        "unknown"
      ]
    },
    "signals": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/SafetySignal"
      }
    }
  }
}
TokenPage
{
  "type": "object",
  "required": [
    "network",
    "ref",
    "input",
    "asOf",
    "included",
    "identity",
    "quote",
    "market",
    "profile",
    "liquidity",
    "holders",
    "safety",
    "comparison",
    "insight",
    "series",
    "errors",
    "cache"
  ],
  "properties": {
    "network": {
      "type": "string",
      "nullable": true
    },
    "ref": {
      "type": "string",
      "description": "Canonical spelling of the requested ref — cache under this, and ask with it next time."
    },
    "input": {
      "type": "string",
      "description": "What the caller wrote",
      "before normalization.": null
    },
    "asOf": {
      "type": "string",
      "format": "date-time",
      "description": "The oldest available timestamp among included cached sections; live sections contribute response time."
    },
    "included": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "identity",
          "quote",
          "market",
          "profile",
          "liquidity",
          "holders",
          "safety",
          "comparison",
          "insight",
          "series"
        ]
      }
    },
    "identity": {
      "$ref": "#/components/schemas/TokenIdentity"
    },
    "quote": {
      "nullable": true,
      "allOf": [
        {
          "$ref": "#/components/schemas/TokenQuote"
        }
      ]
    },
    "market": {
      "nullable": true,
      "allOf": [
        {
          "$ref": "#/components/schemas/TokenMarketView"
        }
      ]
    },
    "profile": {
      "nullable": true,
      "allOf": [
        {
          "$ref": "#/components/schemas/TokenProfileView"
        }
      ]
    },
    "liquidity": {
      "type": "object",
      "nullable": true,
      "additionalProperties": true
    },
    "holders": {
      "type": "object",
      "nullable": true,
      "required": [
        "count",
        "atLeast",
        "source"
      ],
      "description": "Each `change*Pct` window is OMITTED rather than null when the\nobservation history has no trustworthy baseline for it — a token\nwatched for two days has a 24h figure and no 30d one, and an absent\nkey is how a client tells that from \"flat\". There is deliberately\nno 1h/6h window: counts are recorded once per UTC day.\n",
      "properties": {
        "count": {
          "type": "integer",
          "minimum": 0
        },
        "atLeast": {
          "type": "boolean",
          "description": "True when the count is a floor (the source reported \"at least N\")."
        },
        "source": {
          "type": "string",
          "description": "Which counter produced this figure (`ethplorer`, `goplus`,\n`jupiter`, …). Not cosmetic: two indexers count different\npopulations, so a change here is why a growth window can\ndisappear.\n"
        },
        "change24hPct": {
          "type": "number"
        },
        "change7dPct": {
          "type": "number"
        },
        "change30dPct": {
          "type": "number"
        },
        "change90dPct": {
          "type": "number"
        },
        "change365dPct": {
          "type": "number"
        }
      }
    },
    "safety": {
      "nullable": true,
      "allOf": [
        {
          "$ref": "#/components/schemas/TokenSafety"
        }
      ]
    },
    "comparison": {
      "type": "object",
      "nullable": true,
      "description": "The viewed token followed by the fixed BTC and BNB benchmarks, de-duplicated when the viewed token is itself a benchmark.",
      "required": [
        "fiat",
        "items"
      ],
      "properties": {
        "fiat": {
          "type": "string",
          "enum": [
            "usd"
          ]
        },
        "items": {
          "type": "array",
          "maxItems": 3,
          "items": {
            "$ref": "#/components/schemas/TokenComparisonItem"
          }
        }
      }
    },
    "insight": {
      "type": "array",
      "description": "What the token's OWN chain says about it, as titled sections ready to\nrender in order. Empty — never absent — for a family with no\nchain-native token index, since \"the chain has no such record\" and\n\"we didn't ask\" must not look the same to a client.\n",
      "items": {
        "$ref": "#/components/schemas/TokenInsightSection"
      }
    },
    "series": {
      "nullable": true,
      "allOf": [
        {
          "$ref": "#/components/schemas/TokenSeries"
        }
      ]
    },
    "errors": {
      "type": "object",
      "description": "Section name → error code, for sections that could not be read.",
      "additionalProperties": {
        "type": "string"
      }
    },
    "cache": {
      "type": "object",
      "description": "Per-section provenance; only requested sections appear.",
      "additionalProperties": {
        "$ref": "#/components/schemas/CacheInfo"
      }
    }
  }
}
TokenIdentity
{
  "type": "object",
  "required": [
    "tokenId",
    "transferTokenId",
    "kind",
    "network",
    "address",
    "issuer",
    "symbol",
    "name",
    "decimals",
    "native",
    "logoMediaKey",
    "coingeckoId",
    "coingeckoIdSource",
    "ambiguous",
    "verified",
    "priceId",
    "input"
  ],
  "properties": {
    "tokenId": {
      "type": "string"
    },
    "transferTokenId": {
      "type": "string",
      "nullable": true,
      "description": "Canonical transfer-store identifier, when this ref uniquely identifies an asset."
    },
    "kind": {
      "type": "string",
      "enum": [
        "native",
        "erc20",
        "trc10",
        "trc20",
        "spl",
        "asa",
        "cardano",
        "stellar",
        "hts",
        "xrpl",
        "symbol",
        "coingecko"
      ]
    },
    "network": {
      "type": "string",
      "nullable": true
    },
    "address": {
      "type": "string",
      "nullable": true,
      "description": "The chain's own identifier; null for a native coin or a ticker ref."
    },
    "issuer": {
      "type": "string",
      "nullable": true
    },
    "symbol": {
      "type": "string",
      "nullable": true
    },
    "name": {
      "type": "string",
      "nullable": true
    },
    "decimals": {
      "type": "integer",
      "minimum": 0,
      "nullable": true
    },
    "native": {
      "type": "boolean"
    },
    "logoMediaKey": {
      "type": "string",
      "nullable": true,
      "pattern": "^[0-9a-f]{32}$"
    },
    "coingeckoId": {
      "type": "string",
      "nullable": true
    },
    "coingeckoIdSource": {
      "type": "string",
      "nullable": true,
      "enum": [
        "ref",
        "registry",
        "contract",
        "search",
        "hint"
      ],
      "description": "How the coin id was found; null when the token is not listed anywhere visible."
    },
    "ambiguous": {
      "type": "boolean",
      "description": "True when a ticker matched several listed coins and the first was taken."
    },
    "verified": {
      "type": "boolean",
      "nullable": true,
      "description": "The family's own verification flag",
      "where it has one.": null
    },
    "priceId": {
      "type": "string",
      "nullable": true,
      "description": "`GET /v1/prices?ids=` id, or null when the price plane has no grammar for this family."
    },
    "input": {
      "type": "string"
    }
  }
}
TokenComparisonRequest
{
  "type": "object",
  "additionalProperties": false,
  "required": [
    "tokens"
  ],
  "properties": {
    "tokens": {
      "type": "array",
      "minItems": 1,
      "maxItems": 20,
      "items": {
        "$ref": "#/components/schemas/TokenComparisonReference"
      }
    },
    "fiat": {
      "type": "string",
      "enum": [
        "usd"
      ],
      "default": "usd"
    }
  }
}
TokenComparisonReference
{
  "type": "object",
  "additionalProperties": false,
  "required": [
    "network",
    "tokenId"
  ],
  "properties": {
    "network": {
      "type": "string",
      "minLength": 1,
      "maxLength": 64
    },
    "tokenId": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    }
  }
}
TokenComparisonMetric
{
  "type": "object",
  "required": [
    "value",
    "asOf",
    "source",
    "status"
  ],
  "properties": {
    "value": {
      "type": "number",
      "format": "double",
      "nullable": true
    },
    "asOf": {
      "type": "string",
      "format": "date-time",
      "nullable": true
    },
    "source": {
      "type": "string",
      "nullable": true
    },
    "status": {
      "type": "string",
      "enum": [
        "available",
        "stale",
        "unavailable",
        "unsupported",
        "error"
      ]
    }
  }
}
TokenComparisonItem
{
  "type": "object",
  "required": [
    "network",
    "tokenId",
    "identity",
    "metrics",
    "changes",
    "liquidityQuality",
    "derived",
    "ranks",
    "warnings"
  ],
  "properties": {
    "network": {
      "type": "string"
    },
    "tokenId": {
      "type": "string",
      "description": "Canonical token identifier."
    },
    "identity": {
      "type": "object",
      "required": [
        "symbol",
        "name",
        "logoMediaKey",
        "marketId"
      ],
      "properties": {
        "symbol": {
          "type": "string",
          "nullable": true
        },
        "name": {
          "type": "string",
          "nullable": true
        },
        "logoMediaKey": {
          "type": "string",
          "nullable": true
        },
        "marketId": {
          "type": "string",
          "nullable": true
        }
      }
    },
    "metrics": {
      "type": "object",
      "required": [
        "priceUsd",
        "marketCapUsd",
        "volume24hUsd",
        "liquidityUsd"
      ],
      "properties": {
        "priceUsd": {
          "$ref": "#/components/schemas/TokenComparisonMetric"
        },
        "marketCapUsd": {
          "$ref": "#/components/schemas/TokenComparisonMetric"
        },
        "volume24hUsd": {
          "$ref": "#/components/schemas/TokenComparisonMetric"
        },
        "liquidityUsd": {
          "$ref": "#/components/schemas/TokenComparisonMetric"
        }
      }
    },
    "changes": {
      "type": "object",
      "required": [
        "price1hPct",
        "price24hPct",
        "price7dPct",
        "price30dPct"
      ],
      "properties": {
        "price1hPct": {
          "type": "number",
          "nullable": true
        },
        "price24hPct": {
          "type": "number",
          "nullable": true
        },
        "price7dPct": {
          "type": "number",
          "nullable": true
        },
        "price30dPct": {
          "type": "number",
          "nullable": true
        }
      }
    },
    "liquidityQuality": {
      "type": "object",
      "required": [
        "spreadPct",
        "topPoolShare",
        "poolCount"
      ],
      "properties": {
        "spreadPct": {
          "type": "number",
          "nullable": true
        },
        "topPoolShare": {
          "type": "number",
          "nullable": true
        },
        "poolCount": {
          "type": "integer",
          "nullable": true
        }
      }
    },
    "derived": {
      "type": "object",
      "required": [
        "turnoverPct",
        "liquidityToMarketCapPct",
        "volumeToLiquidityRatio"
      ],
      "properties": {
        "turnoverPct": {
          "type": "number",
          "nullable": true
        },
        "liquidityToMarketCapPct": {
          "type": "number",
          "nullable": true
        },
        "volumeToLiquidityRatio": {
          "type": "number",
          "nullable": true
        }
      }
    },
    "ranks": {
      "type": "object",
      "required": [
        "marketCap",
        "volume24h",
        "liquidity"
      ],
      "properties": {
        "marketCap": {
          "type": "integer",
          "nullable": true,
          "minimum": 1
        },
        "volume24h": {
          "type": "integer",
          "nullable": true,
          "minimum": 1
        },
        "liquidity": {
          "type": "integer",
          "nullable": true,
          "minimum": 1
        }
      }
    },
    "warnings": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  }
}
TokenComparisonError
{
  "type": "object",
  "required": [
    "inputIndex",
    "network",
    "tokenId",
    "code"
  ],
  "properties": {
    "inputIndex": {
      "type": "integer",
      "minimum": 0
    },
    "network": {
      "type": "string"
    },
    "tokenId": {
      "type": "string"
    },
    "code": {
      "type": "string",
      "enum": [
        "INVALID_TOKEN",
        "DUPLICATE_TOKEN"
      ]
    }
  }
}
TokenComparisonResponse
{
  "type": "object",
  "required": [
    "fiat",
    "computedAt",
    "items",
    "errors"
  ],
  "properties": {
    "fiat": {
      "type": "string",
      "enum": [
        "usd"
      ]
    },
    "computedAt": {
      "type": "string",
      "format": "date-time"
    },
    "items": {
      "type": "array",
      "maxItems": 20,
      "items": {
        "$ref": "#/components/schemas/TokenComparisonItem"
      }
    },
    "errors": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/TokenComparisonError"
      }
    }
  }
}
TokenQuote
{
  "type": "object",
  "required": [
    "priceUsd",
    "change24hPct",
    "source",
    "asOf"
  ],
  "description": "The one price a header should show, with the source that won.",
  "properties": {
    "priceUsd": {
      "type": "number"
    },
    "change24hPct": {
      "type": "number",
      "nullable": true
    },
    "source": {
      "type": "string",
      "enum": [
        "market",
        "spot",
        "dex"
      ]
    },
    "asOf": {
      "type": "string",
      "format": "date-time",
      "nullable": true
    }
  }
}
TokenMarketView
{
  "type": "object",
  "description": "Market figures in the app's vocabulary. Every field is nullable; null means unknown, never zero.",
  "required": [
    "symbol",
    "name",
    "priceUsd",
    "change1hPct",
    "change24hPct",
    "change7dPct",
    "change14dPct",
    "change30dPct",
    "change200dPct",
    "change1yPct",
    "marketCapUsd",
    "marketCapRank",
    "fdvUsd",
    "volume24hUsd",
    "high24hUsd",
    "low24hUsd",
    "circulatingSupply",
    "totalSupply",
    "maxSupply",
    "athUsd",
    "athAt",
    "athChangePct",
    "atlUsd",
    "atlAt",
    "atlChangePct",
    "sparkline7d"
  ],
  "properties": {
    "symbol": {
      "type": "string",
      "nullable": true
    },
    "name": {
      "type": "string",
      "nullable": true
    },
    "priceUsd": {
      "type": "number",
      "nullable": true
    },
    "change1hPct": {
      "type": "number",
      "nullable": true
    },
    "change24hPct": {
      "type": "number",
      "nullable": true
    },
    "change7dPct": {
      "type": "number",
      "nullable": true
    },
    "change14dPct": {
      "type": "number",
      "nullable": true
    },
    "change30dPct": {
      "type": "number",
      "nullable": true
    },
    "change200dPct": {
      "type": "number",
      "nullable": true
    },
    "change1yPct": {
      "type": "number",
      "nullable": true
    },
    "marketCapUsd": {
      "type": "number",
      "nullable": true
    },
    "marketCapRank": {
      "type": "integer",
      "nullable": true
    },
    "fdvUsd": {
      "type": "number",
      "nullable": true
    },
    "volume24hUsd": {
      "type": "number",
      "nullable": true
    },
    "high24hUsd": {
      "type": "number",
      "nullable": true
    },
    "low24hUsd": {
      "type": "number",
      "nullable": true
    },
    "circulatingSupply": {
      "type": "number",
      "nullable": true
    },
    "totalSupply": {
      "type": "number",
      "nullable": true
    },
    "maxSupply": {
      "type": "number",
      "nullable": true
    },
    "athUsd": {
      "type": "number",
      "nullable": true
    },
    "athAt": {
      "type": "string",
      "format": "date-time",
      "nullable": true
    },
    "athChangePct": {
      "type": "number",
      "nullable": true
    },
    "atlUsd": {
      "type": "number",
      "nullable": true
    },
    "atlAt": {
      "type": "string",
      "format": "date-time",
      "nullable": true
    },
    "atlChangePct": {
      "type": "number",
      "nullable": true
    },
    "sparkline7d": {
      "type": "array",
      "description": "Oldest → newest closes over the last 7 days; empty when unknown. Capped at 400 points.",
      "items": {
        "type": "number"
      }
    }
  }
}
TokenProfileView
{
  "type": "object",
  "required": [
    "description",
    "categories",
    "links",
    "marketsCount",
    "sentimentUpPct",
    "logoMediaKey"
  ],
  "properties": {
    "description": {
      "type": "string",
      "nullable": true
    },
    "categories": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "links": {
      "type": "array",
      "description": "Labelled pairs, so a new link kind needs no client change to render.",
      "items": {
        "type": "object",
        "required": [
          "label",
          "url"
        ],
        "properties": {
          "label": {
            "type": "string"
          },
          "url": {
            "type": "string"
          }
        }
      }
    },
    "marketsCount": {
      "type": "integer",
      "nullable": true
    },
    "sentimentUpPct": {
      "type": "number",
      "nullable": true
    },
    "logoMediaKey": {
      "type": "string",
      "nullable": true,
      "pattern": "^[0-9a-f]{32}$"
    }
  }
}
TokenInsightSection
{
  "type": "object",
  "description": "One chain-native panel. Deliberately presentational — label/value rows\nrather than typed fields — because each family knows different things\n(Stellar issuer flags, Solana list verification), and forcing them into a\ncommon shape would mean dropping whatever didn't fit.\n",
  "required": [
    "title",
    "summary",
    "ok",
    "rows"
  ],
  "properties": {
    "title": {
      "type": "string",
      "example": "Issuer controls"
    },
    "summary": {
      "type": "string",
      "description": "One line for the collapsed state.",
      "example": "clawback enabled · can be frozen · approval required"
    },
    "ok": {
      "type": "boolean",
      "description": "False when the chain was read but had no record of this token."
    },
    "rows": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "label",
          "value"
        ],
        "properties": {
          "label": {
            "type": "string",
            "example": "Can claw back"
          },
          "value": {
            "type": "string",
            "example": "Yes"
          },
          "sentiment": {
            "type": "string",
            "enum": [
              "good",
              "caution",
              "bad"
            ],
            "description": "How to colour the value. Absent means neutral — a fact with no verdict."
          }
        }
      }
    }
  }
}
TokenSeries
{
  "type": "object",
  "required": [
    "interval",
    "points",
    "volumes",
    "window"
  ],
  "properties": {
    "interval": {
      "type": "string",
      "enum": [
        "1h",
        "1d"
      ]
    },
    "points": {
      "type": "array",
      "description": "`[epochSeconds, close]`, oldest → newest.",
      "items": {
        "type": "array",
        "items": {
          "type": "number"
        },
        "minItems": 2,
        "maxItems": 2
      }
    },
    "volumes": {
      "type": "array",
      "description": "Parallel `[epochSeconds, volume]`; empty means unknown, not zero.",
      "items": {
        "type": "array",
        "items": {
          "type": "number"
        },
        "minItems": 2,
        "maxItems": 2
      }
    },
    "window": {
      "type": "object",
      "required": [
        "from",
        "to",
        "days"
      ],
      "properties": {
        "from": {
          "type": "integer",
          "nullable": true
        },
        "to": {
          "type": "integer",
          "nullable": true
        },
        "days": {
          "type": "integer"
        }
      }
    }
  }
}
Token360
{
  "type": "object",
  "required": [
    "network",
    "tokenId",
    "meta",
    "market",
    "profile",
    "liquidity",
    "holders",
    "safety",
    "errors",
    "cache"
  ],
  "properties": {
    "network": {
      "type": "string"
    },
    "tokenId": {
      "type": "string"
    },
    "meta": {
      "type": "object",
      "nullable": true,
      "additionalProperties": true
    },
    "market": {
      "type": "object",
      "nullable": true,
      "additionalProperties": true
    },
    "profile": {
      "type": "object",
      "nullable": true,
      "additionalProperties": true
    },
    "liquidity": {
      "type": "object",
      "nullable": true,
      "additionalProperties": true
    },
    "holders": {
      "type": "object",
      "nullable": true,
      "required": [
        "count",
        "floor"
      ],
      "properties": {
        "count": {
          "type": "integer",
          "minimum": 0
        },
        "floor": {
          "type": "boolean"
        }
      }
    },
    "safety": {
      "$ref": "#/components/schemas/TokenSafety"
    },
    "errors": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    },
    "cache": {
      "type": "object",
      "required": [
        "market",
        "profile",
        "liquidity",
        "holders"
      ],
      "properties": {
        "market": {
          "$ref": "#/components/schemas/CacheInfo"
        },
        "profile": {
          "$ref": "#/components/schemas/CacheInfo"
        },
        "liquidity": {
          "$ref": "#/components/schemas/CacheInfo"
        },
        "holders": {
          "$ref": "#/components/schemas/CacheInfo"
        }
      }
    }
  }
}
TokenSearchItem
{
  "type": "object",
  "required": [
    "network",
    "tokenId",
    "address",
    "symbol",
    "name",
    "decimals",
    "verified",
    "holderCount",
    "logoMediaKey"
  ],
  "properties": {
    "network": {
      "type": "string"
    },
    "tokenId": {
      "type": "string"
    },
    "address": {
      "type": "string"
    },
    "symbol": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "decimals": {
      "type": "integer",
      "minimum": 0
    },
    "verified": {
      "type": "boolean"
    },
    "holderCount": {
      "type": "integer",
      "minimum": 0,
      "nullable": true
    },
    "logoMediaKey": {
      "type": "string",
      "nullable": true,
      "pattern": "^[0-9a-f]{32}$"
    }
  }
}
TokenSearchResponse
{
  "type": "object",
  "required": [
    "items",
    "cache"
  ],
  "properties": {
    "items": {
      "type": "array",
      "maxItems": 20,
      "items": {
        "$ref": "#/components/schemas/TokenSearchItem"
      }
    },
    "cache": {
      "$ref": "#/components/schemas/CacheInfo"
    }
  }
}
IntelligenceMessage
{
  "type": "object",
  "required": [
    "id",
    "role",
    "body",
    "createdAt"
  ],
  "properties": {
    "id": {
      "type": "string"
    },
    "role": {
      "type": "string",
      "description": "Author of the message, e.g. `user`."
    },
    "body": {
      "type": "string"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    }
  }
}
TokenMarketsResponse
{
  "type": "object",
  "required": [
    "markets",
    "pending",
    "cache"
  ],
  "properties": {
    "pending": {
      "type": "array",
      "description": "Requested ids with no cached row whose fetch failed this time. Not \"unlisted\" — retry later.",
      "items": {
        "type": "string"
      }
    },
    "markets": {
      "type": "array",
      "items": {
        "allOf": [
          {
            "$ref": "#/components/schemas/TokenMarketView"
          },
          {
            "type": "object",
            "description": "Provider-independent market row enriched with a Fortis media-proxy key.",
            "required": [
              "id",
              "logoMediaKey"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "logoMediaKey": {
                "type": "string",
                "nullable": true,
                "pattern": "^[0-9a-f]{32}$"
              }
            }
          }
        ]
      }
    },
    "cache": {
      "$ref": "#/components/schemas/CacheInfo"
    }
  }
}
OhlcPoint
{
  "type": "array",
  "description": "`[unixTimestampMilliseconds, open, high, low, close]`.",
  "minItems": 5,
  "maxItems": 5,
  "items": {
    "type": "number",
    "format": "double"
  }
}
OhlcResponse
{
  "type": "object",
  "required": [
    "points",
    "cache"
  ],
  "properties": {
    "points": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/OhlcPoint"
      }
    },
    "cache": {
      "$ref": "#/components/schemas/CacheInfo"
    }
  }
}
ProviderEnvelope
{
  "type": "object",
  "required": [
    "payload",
    "cache"
  ],
  "properties": {
    "payload": {
      "description": "Provider-compatible JSON constrained and sanitized by the server adapter.",
      "nullable": true
    },
    "cache": {
      "$ref": "#/components/schemas/CacheInfo"
    }
  }
}
CrossChainProvider
{
  "type": "object",
  "required": [
    "id",
    "name",
    "websiteUrl",
    "configured",
    "custody",
    "capabilities"
  ],
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9-]{0,31}$"
    },
    "name": {
      "type": "string"
    },
    "websiteUrl": {
      "type": "string",
      "format": "uri"
    },
    "configured": {
      "type": "boolean"
    },
    "custody": {
      "type": "string",
      "enum": [
        "non-custodial",
        "custodial",
        "mixed",
        "unknown"
      ]
    },
    "capabilities": {
      "type": "object",
      "required": [
        "quote",
        "execution",
        "status"
      ],
      "additionalProperties": false,
      "properties": {
        "quote": {
          "type": "boolean"
        },
        "execution": {
          "type": "boolean"
        },
        "status": {
          "type": "boolean"
        }
      }
    }
  }
}
CrossChainEndpoint
{
  "type": "object",
  "required": [
    "chainId",
    "token",
    "decimals",
    "address"
  ],
  "additionalProperties": false,
  "properties": {
    "chainId": {
      "type": "string",
      "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$",
      "description": "Fortis routing key; decimal EIP-155 id on EVM networks. Provider adapters translate non-EVM keys."
    },
    "token": {
      "type": "string",
      "minLength": 1,
      "maxLength": 256
    },
    "decimals": {
      "type": "integer",
      "minimum": 0,
      "maximum": 36
    },
    "address": {
      "type": "string",
      "minLength": 1,
      "maxLength": 256
    }
  }
}
CrossChainQuoteRequest
{
  "type": "object",
  "required": [
    "from",
    "to",
    "amountIn"
  ],
  "additionalProperties": false,
  "properties": {
    "from": {
      "$ref": "#/components/schemas/CrossChainEndpoint"
    },
    "to": {
      "$ref": "#/components/schemas/CrossChainEndpoint"
    },
    "amountIn": {
      "type": "string",
      "pattern": "^[1-9]\\d{0,79}$",
      "description": "Exact source amount in the token's smallest unit."
    },
    "slippageBps": {
      "type": "integer",
      "minimum": 0,
      "maximum": 5000,
      "default": 50
    },
    "providers": {
      "type": "array",
      "minItems": 1,
      "maxItems": 10,
      "uniqueItems": true,
      "items": {
        "type": "string",
        "pattern": "^[a-z][a-z0-9-]{0,31}$"
      }
    }
  }
}
CrossChainStep
{
  "type": "object",
  "required": [
    "kind",
    "tool",
    "fromChainId",
    "toChainId",
    "fromToken",
    "toToken"
  ],
  "additionalProperties": false,
  "properties": {
    "kind": {
      "type": "string",
      "enum": [
        "swap",
        "bridge",
        "fee",
        "protocol",
        "other"
      ]
    },
    "tool": {
      "type": "string"
    },
    "fromChainId": {
      "type": "string",
      "nullable": true
    },
    "toChainId": {
      "type": "string",
      "nullable": true
    },
    "fromToken": {
      "type": "string",
      "nullable": true
    },
    "toToken": {
      "type": "string",
      "nullable": true
    }
  }
}
CrossChainExecution
{
  "type": "object",
  "required": [
    "kind",
    "to",
    "data",
    "value",
    "gasLimit",
    "approvalAddress"
  ],
  "additionalProperties": false,
  "properties": {
    "kind": {
      "type": "string",
      "enum": [
        "evm-calldata",
        "utxo-psbt",
        "serialized-transaction",
        "deposit",
        "unknown"
      ]
    },
    "to": {
      "type": "string",
      "nullable": true
    },
    "data": {
      "type": "string",
      "nullable": true
    },
    "value": {
      "type": "string",
      "nullable": true
    },
    "gasLimit": {
      "type": "string",
      "nullable": true
    },
    "approvalAddress": {
      "type": "string",
      "nullable": true
    }
  }
}
CrossChainRoute
{
  "type": "object",
  "required": [
    "id",
    "rank",
    "providerId",
    "providerName",
    "providerUrl",
    "providerQuoteId",
    "bridgeId",
    "bridge",
    "bridgeUrl",
    "riskKey",
    "custody",
    "amountIn",
    "amountOut",
    "amountOutMin",
    "decimalsIn",
    "decimalsOut",
    "feeUsd",
    "sourceNetworkFee",
    "etaSeconds",
    "quotedAt",
    "expiresAt",
    "steps",
    "execution"
  ],
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "string"
    },
    "rank": {
      "type": "integer",
      "minimum": 1
    },
    "providerId": {
      "type": "string"
    },
    "providerName": {
      "type": "string"
    },
    "providerUrl": {
      "type": "string",
      "format": "uri"
    },
    "providerQuoteId": {
      "type": "string",
      "nullable": true
    },
    "bridgeId": {
      "type": "string",
      "nullable": true,
      "description": "Exact provider bridge/tool identifier used for status lookup; distinct from the display name."
    },
    "bridge": {
      "type": "string"
    },
    "bridgeUrl": {
      "type": "string",
      "format": "uri",
      "nullable": true,
      "description": "Trusted official bridge website from the Fortis bridge directory; null when not verified."
    },
    "riskKey": {
      "type": "string",
      "description": "Settlement dependency key; equal values are not independent routes."
    },
    "custody": {
      "type": "string",
      "enum": [
        "non-custodial",
        "custodial",
        "mixed",
        "unknown"
      ]
    },
    "amountIn": {
      "type": "string",
      "pattern": "^\\d+$"
    },
    "amountOut": {
      "type": "string",
      "pattern": "^\\d+$"
    },
    "amountOutMin": {
      "type": "string",
      "pattern": "^\\d+$"
    },
    "decimalsIn": {
      "type": "integer",
      "minimum": 0,
      "maximum": 36
    },
    "decimalsOut": {
      "type": "integer",
      "minimum": 0,
      "maximum": 36
    },
    "feeUsd": {
      "type": "number",
      "format": "double",
      "nullable": true
    },
    "sourceNetworkFee": {
      "type": "string",
      "pattern": "^\\d+$",
      "nullable": true
    },
    "etaSeconds": {
      "type": "integer",
      "minimum": 0,
      "nullable": true
    },
    "quotedAt": {
      "type": "string",
      "format": "date-time"
    },
    "expiresAt": {
      "type": "string",
      "format": "date-time"
    },
    "steps": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/CrossChainStep"
      }
    },
    "execution": {
      "$ref": "#/components/schemas/CrossChainExecution"
    }
  }
}
CrossChainProviderAttempt
{
  "type": "object",
  "required": [
    "providerId",
    "status",
    "routeCount"
  ],
  "additionalProperties": false,
  "properties": {
    "providerId": {
      "type": "string"
    },
    "status": {
      "type": "string",
      "enum": [
        "quoted",
        "no-route",
        "not-configured",
        "failed"
      ]
    },
    "routeCount": {
      "type": "integer",
      "minimum": 0
    }
  }
}
CrossChainQuoteResponse
{
  "type": "object",
  "required": [
    "routes",
    "bestRouteId",
    "attempts",
    "cache"
  ],
  "additionalProperties": false,
  "properties": {
    "routes": {
      "type": "array",
      "maxItems": 5,
      "items": {
        "$ref": "#/components/schemas/CrossChainRoute"
      }
    },
    "bestRouteId": {
      "type": "string",
      "nullable": true
    },
    "attempts": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/CrossChainProviderAttempt"
      }
    },
    "cache": {
      "$ref": "#/components/schemas/CacheInfo"
    }
  }
}
CrossChainStatusRequest
{
  "type": "object",
  "required": [
    "providerId",
    "txHash",
    "fromChainId",
    "toChainId"
  ],
  "additionalProperties": false,
  "properties": {
    "providerId": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9-]{0,31}$"
    },
    "txHash": {
      "type": "string",
      "minLength": 1,
      "maxLength": 256
    },
    "fromChainId": {
      "type": "string",
      "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$"
    },
    "toChainId": {
      "type": "string",
      "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$"
    },
    "providerQuoteId": {
      "type": "string",
      "minLength": 1,
      "maxLength": 256
    },
    "bridgeId": {
      "type": "string",
      "minLength": 1,
      "maxLength": 256,
      "description": "Exact bridge/tool identifier returned with the selected route."
    },
    "bridge": {
      "type": "string",
      "minLength": 1,
      "maxLength": 256,
      "deprecated": true,
      "description": "Legacy display-name field; accepted and ignored for compatibility."
    }
  }
}
CrossChainStatus
{
  "type": "object",
  "required": [
    "providerId",
    "state",
    "substatus",
    "message",
    "sourceHash",
    "destinationHash",
    "providerExplorerUrl",
    "bridgeExplorerUrl",
    "elapsedSeconds",
    "actualReceived",
    "progress",
    "recovery",
    "providerData"
  ],
  "additionalProperties": false,
  "properties": {
    "providerId": {
      "type": "string"
    },
    "state": {
      "type": "string",
      "enum": [
        "not-found",
        "pending",
        "completed",
        "partial",
        "refunded",
        "failed",
        "needs-action"
      ]
    },
    "substatus": {
      "type": "string",
      "nullable": true
    },
    "message": {
      "type": "string",
      "nullable": true
    },
    "sourceHash": {
      "type": "string"
    },
    "destinationHash": {
      "type": "string",
      "nullable": true
    },
    "providerExplorerUrl": {
      "type": "string",
      "format": "uri",
      "nullable": true
    },
    "bridgeExplorerUrl": {
      "type": "string",
      "format": "uri",
      "nullable": true
    },
    "elapsedSeconds": {
      "type": "integer",
      "minimum": 0,
      "nullable": true
    },
    "actualReceived": {
      "nullable": true,
      "allOf": [
        {
          "$ref": "#/components/schemas/CrossChainReceivedAsset"
        }
      ]
    },
    "progress": {
      "type": "array",
      "maxItems": 32,
      "items": {
        "$ref": "#/components/schemas/CrossChainProgressStep"
      }
    },
    "recovery": {
      "nullable": true,
      "allOf": [
        {
          "$ref": "#/components/schemas/CrossChainRecovery"
        }
      ]
    },
    "providerData": {
      "type": "object",
      "description": "Bounded latest upstream status response retained for support diagnostics.",
      "additionalProperties": true
    }
  }
}
CrossChainReceivedAsset
{
  "type": "object",
  "required": [
    "chainId",
    "amount",
    "tokenAddress",
    "symbol",
    "decimals"
  ],
  "additionalProperties": false,
  "properties": {
    "chainId": {
      "type": "string",
      "nullable": true
    },
    "amount": {
      "type": "string",
      "pattern": "^\\d+$"
    },
    "tokenAddress": {
      "type": "string",
      "nullable": true
    },
    "symbol": {
      "type": "string",
      "nullable": true
    },
    "decimals": {
      "type": "integer",
      "minimum": 0,
      "maximum": 36,
      "nullable": true
    }
  }
}
CrossChainProgressStep
{
  "type": "object",
  "required": [
    "id",
    "kind",
    "label",
    "state",
    "chainId",
    "txHash",
    "explorerUrl",
    "startedAt",
    "completedAt",
    "durationSeconds",
    "timingSource",
    "detail",
    "errorCode",
    "errorMessage"
  ],
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "string"
    },
    "kind": {
      "type": "string",
      "enum": [
        "source",
        "bridge",
        "destination",
        "refund"
      ]
    },
    "label": {
      "type": "string"
    },
    "state": {
      "type": "string",
      "enum": [
        "not-started",
        "pending",
        "finalizing",
        "completed",
        "failed",
        "refunded",
        "needs-action"
      ]
    },
    "chainId": {
      "type": "string",
      "nullable": true
    },
    "txHash": {
      "type": "string",
      "nullable": true
    },
    "explorerUrl": {
      "type": "string",
      "format": "uri",
      "nullable": true
    },
    "startedAt": {
      "type": "string",
      "format": "date-time",
      "nullable": true
    },
    "completedAt": {
      "type": "string",
      "format": "date-time",
      "nullable": true
    },
    "durationSeconds": {
      "type": "integer",
      "minimum": 0,
      "nullable": true
    },
    "timingSource": {
      "type": "string",
      "enum": [
        "provider",
        "inferred",
        "wallet-observed"
      ],
      "nullable": true
    },
    "detail": {
      "type": "string",
      "nullable": true
    },
    "errorCode": {
      "type": "string",
      "nullable": true
    },
    "errorMessage": {
      "type": "string",
      "nullable": true
    }
  }
}
CrossChainRecovery
{
  "type": "object",
  "required": [
    "kind",
    "title",
    "description",
    "actionLabel",
    "actionUrl"
  ],
  "additionalProperties": false,
  "properties": {
    "kind": {
      "type": "string",
      "enum": [
        "wait",
        "add-gas",
        "track-refund",
        "swap-received-asset",
        "contact-support"
      ]
    },
    "title": {
      "type": "string"
    },
    "description": {
      "type": "string"
    },
    "actionLabel": {
      "type": "string",
      "nullable": true
    },
    "actionUrl": {
      "type": "string",
      "format": "uri",
      "nullable": true
    }
  }
}
ResourceRange
{
  "type": "object",
  "description": "Min, average and max of the samples taken within one minute.",
  "required": [
    "min",
    "avg",
    "max",
    "samples"
  ],
  "properties": {
    "min": {
      "type": "number"
    },
    "avg": {
      "type": "number"
    },
    "max": {
      "type": "number"
    },
    "samples": {
      "type": "integer"
    }
  }
}