# Execution Stream Source: https://docs.glider.fi/api-reference/endpoints/executions-stream GET /v1/executions/:operationId/stream Replayable server-sent event stream for canonical execution updates. Use this endpoint when you want direct HTTP streaming for a submitted execution. It emits canonical execution events over standard SSE for CLIs, API integrators, and future chat surfaces. If you want AG-UI-compatible execution streaming for TanStack AI consumers, use `GET /v1/ai/executions/:operationId/ag-ui-stream` instead. ## Auth * Wallet session cookie * or `X-API-KEY` ## Resume Semantics * Send `Last-Event-ID` with the opaque stream cursor from the SSE `id` field (the same value is also exposed as `cursor` in each event payload). * The server replays any missed canonical events before switching to live tail. ## Event Envelope Each SSE message uses a canonical event envelope: ```json theme={null} { "streamId": "execution:op_123", "eventId": "1743163201000-0", "sequence": 17, "ts": "2026-03-28T15:10:00.000Z", "kind": "execution.timeline_appended", "operationId": "op_123", "cursor": "1743163201000-0", "payload": { "message": "Swap submitted onchain" } } ``` Current execution event kinds: * `execution.snapshot` * `execution.state_changed` * `execution.timeline_appended` * `execution.progress` * `execution.result` * `execution.error` ## Example Call ```bash theme={null} curl -N \ --request GET \ --url 'https://api.glider.fi/v1/executions/op_123/stream' \ --header 'X-API-KEY: ' ``` Resume from a previously consumed event: ```bash theme={null} curl -N \ --request GET \ --url 'https://api.glider.fi/v1/executions/op_123/stream' \ --header 'X-API-KEY: ' \ --header 'Last-Event-ID: 1743163201000-0' ``` Example SSE frame: ```text theme={null} id: 1743163201000-0 event: execution.timeline_appended data: {"streamId":"execution:op_123","eventId":"1743163201000-0","sequence":17,"ts":"2026-03-28T15:10:00.000Z","kind":"execution.timeline_appended","operationId":"op_123","cursor":"1743163201000-0","payload":{"message":"Swap submitted onchain"}} ``` # Activate Chains on a Portfolio (Stage 2) Source: https://docs.glider.fi/api-reference/endpoints/v2-activate-chains POST /v2/portfolios/{portfolioId}/chains Stage 2 of the two-stage chain-activation flow — submits the owner's signature and creates smart accounts on the new chains. Stage 2 of the two-stage chain-activation flow. Verifies the portfolio owner's signature over the message returned by `POST /v2/portfolios/{portfolioId}/chains/signature`, then creates one smart account per requested chain inside a single transaction. On success the portfolio can receive deposits and operate on the new chains immediately. The signature is verified according to the portfolio's account type: ECDSA portfolios submit an EIP-191 `personal_sign` signature that may cover several chains at once; ERC-1271 portfolios submit their smart-contract wallet's signature over the stage-1 EIP-712 typed data, verified against the owner contract on the requested chain — exactly one chain per request. Smart accounts use deterministic CREATE2 addresses — the **same address** as the portfolio's existing smart accounts, now live on the new chains. The response mirrors the `smartAccounts` shape returned by `POST /v2/enroll` and `GET /v2/portfolios/{portfolioId}`. * Auth: `x-api-key` header (required) * Scope: `portfolios:write` `chainIds` must **exactly match** the set passed to the signature stage — the owner's signature covers that specific chain set, and any drift fails verification with `400`. The operation is **idempotent on the signed payload**: retrying with the same `chainIds` + `signature` replays the original response instead of failing on the already-active chains. A concurrent identical request returns `409` while the first is still in flight. Common error responses: * `400` when the body is invalid, the signature does not verify, a chain is already active, the portfolio is Solana, or more than one chain is requested on an ERC-1271 portfolio * `401` when `x-api-key` is missing or invalid * `403` when the API key lacks the `portfolios:write` scope * `404` when the portfolio does not exist or belongs to another tenant * `409` when an identical activation request is still in flight ```bash cURL theme={null} curl --request POST \ --url 'https://api.glider.fi/v2/portfolios/pf_01JWZEE2MF30KVRMRX53N88VA4/chains' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "chainIds": [42161], "signature": "0x8b2c...signed-by-portfolio-owner...1c" }' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/portfolios/pf_01JWZEE2MF30KVRMRX53N88VA4/chains", { method: "POST", headers: { "x-api-key": "gldr_sk_your_api_key", "Content-Type": "application/json", }, body: JSON.stringify({ chainIds: [42161], signature: "0x8b2c...signed-by-portfolio-owner...1c", }), }, ); ``` ```json 201 theme={null} { "success": true, "data": { "portfolioId": "pf_01JWZEE2MF30KVRMRX53N88VA4", "smartAccounts": [ { "accountId": "eip155:42161:0xe3a2d1f49aee887e42655b56371d4d76bbf58058" } ] } } ``` ```json 400 (invalid signature) theme={null} { "success": false, "error": { "code": "API_400", "message": "Signature does not match the chain-activation session-key message for this portfolio's owner" } } ``` ```json 404 theme={null} { "success": false, "error": { "code": "API_200", "message": "Portfolio with ID pf_01JWZEE2MF30KVRMRX53N88VA4 not found" } } ``` ```json 409 theme={null} { "success": false, "error": { "code": "API_007", "message": "An identical chain-activation request is still in progress" } } ``` # Get Chain Activation Signable Message (Stage 1) Source: https://docs.glider.fi/api-reference/endpoints/v2-activate-chains-signature POST /v2/portfolios/{portfolioId}/chains/signature Stage 1 of the two-stage chain-activation flow — returns the signable message for adding new chains to an existing portfolio. Stage 1 of the two-stage chain-activation flow. A portfolio's chains are chosen at enrollment (`chainIds` on `POST /v2/enroll`); this flow adds smart accounts on **additional** chains to an already-enrolled portfolio. The endpoint recomputes the session-key message over the requested (new) chains from the portfolio's existing owner wallet, operating agent, and account index, and returns it for the **portfolio owner** to sign — the same signature ceremony as enrollment. The message shape follows the portfolio's account type (determined server-side from how it was enrolled): * **ECDSA portfolios** get `{ "kind": "ecdsa", "raw": ... }` — a 32-byte digest covering all requested chains, signed via EIP-191 `personal_sign`. * **ERC-1271 portfolios** get `{ "kind": "typed-data", "typedData": ... }` — an EIP-712 payload signed with the owner's smart-contract wallet. One signature covers exactly one chain, so `chainIds` must contain exactly one entry. Pass the signature to `POST /v2/portfolios/{portfolioId}/chains` to activate. * Auth: `x-api-key` header (required) * Scope: `portfolios:write` The call is **stateless and deterministic**: nothing is reserved, and repeating it with the same `chainIds` returns the same message. There is no expiry — the signature stays valid until submitted. Current constraints: * **EVM portfolios only.** Solana portfolios return `400`. * ERC-1271 portfolios activate one chain per request; passing more than one `chainIds` entry returns `400`. To activate several chains, repeat the two-stage flow once per chain. * `chainIds` must not contain duplicates and must not include chains the portfolio already has a smart account on — check `smartAccounts` on `GET /v2/portfolios/{portfolioId}` first. * Unknown/unconfigured chains are rejected with `400`; the allowed list is in the error `details`. Common error responses: * `400` when the body is invalid, a chain is already active, a chain is unsupported, the portfolio is Solana, or more than one chain is requested on an ERC-1271 portfolio * `401` when `x-api-key` is missing or invalid * `403` when the API key lacks the `portfolios:write` scope * `404` when the portfolio does not exist or belongs to another tenant ```bash cURL theme={null} curl --request POST \ --url 'https://api.glider.fi/v2/portfolios/pf_01JWZEE2MF30KVRMRX53N88VA4/chains/signature' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "chainIds": [42161] }' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/portfolios/pf_01JWZEE2MF30KVRMRX53N88VA4/chains/signature", { method: "POST", headers: { "x-api-key": "gldr_sk_your_api_key", "Content-Type": "application/json", }, body: JSON.stringify({ chainIds: [42161] }), }, ); ``` ```json 200 (ECDSA portfolio) theme={null} { "success": true, "data": { "message": { "kind": "ecdsa", "raw": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef" } } } ``` ```json 200 (ERC-1271 portfolio) theme={null} { "success": true, "data": { "message": { "kind": "typed-data", "typedData": { "domain": { "name": "ERC1271Validator", "version": "0.0.1", "chainId": 42161, "verifyingContract": "0x9999999999999999999999999999999999999999" }, "types": { "MessageHash": [{ "name": "hash", "type": "bytes32" }] }, "primaryType": "MessageHash", "message": { "hash": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef" } } } } } ``` ```json 400 (chain already active) theme={null} { "success": false, "error": { "code": "API_400", "message": "Portfolio already has a vault on chain(s): [42161]" } } ``` ```json 404 theme={null} { "success": false, "error": { "code": "API_200", "message": "Portfolio with ID pf_01JWZEE2MF30KVRMRX53N88VA4 not found" } } ``` # Get Asset Allocation Breakdown Source: https://docs.glider.fi/api-reference/endpoints/v2-asset-allocation-breakdown POST /v2/assets/allocation-breakdown Aggregates canonical or CAIP assets into sector, industry, theme, or category buckets. Returns deterministic category makeup for an arbitrary list of holdings. Each holding must identify either a canonical asset (`assetCanonicalId`) or a chain-scoped CAIP-19 asset (`caipAssetId`). CAIP assets are resolved upward to their canonical subject before sector, industry, theme, or crypto-category membership is loaded. * Auth: `x-api-key` header (required) * Scope: `portfolios:read` Supported dimensions: * `sector` * `industryGroup` * `industry` * `subIndustry` * `gliderSector` * `gliderTheme` * `cryptoCategory` For multi-valued category dimensions, set `multiCategoryMode`: * `primary` selects the highest-confidence category for each holding. * `apportioned` splits each holding's weight across all matching categories. * `overlap` assigns the full holding weight to every matching category, so totals can exceed 100%. ```bash cURL theme={null} curl --request POST \ --url 'https://api.glider.fi/v2/assets/allocation-breakdown' \ --header 'content-type: application/json' \ --header 'x-api-key: gldr_sk_your_api_key' \ --data '{ "dimensions": ["sector", "industry", "gliderTheme"], "multiCategoryMode": "primary", "holdings": [ { "assetCanonicalId": "11111111-1111-4111-8111-111111111111", "symbol": "NOW", "weight": "25" }, { "caipAssetId": "eip155:56/erc20:0x2222222222222222222222222222222222222222", "symbol": "NVDAON", "weight": "75" } ] }' ``` ```json 200 theme={null} { "success": true, "data": { "normalization": { "inputMode": "weights", "normalizedTotal": "1", "sourceTotal": "100" }, "holdings": [ { "inputIndex": 0, "assetCanonicalId": "11111111-1111-4111-8111-111111111111", "caipAssetId": null, "symbol": "NOW", "name": null, "normalizedWeight": "0.25", "sourceValue": "25" } ], "breakdowns": [ { "dimension": "sector", "aggregationMode": "single", "totalWeight": "1", "absoluteTotalWeight": "1", "totalMayExceedOne": false, "buckets": [ { "key": "technology", "label": "Technology", "weight": "1", "absoluteWeight": "1", "holdingCount": 2, "holdings": [] } ] } ], "diagnostics": { "unresolved": [], "unsupported": [], "unclassified": [], "lowConfidence": [] } } } ``` # Create Strategy Source: https://docs.glider.fi/api-reference/endpoints/v2-create-strategy POST /v2/strategies Creates a new strategy with an initial allocation version. Creates a strategy from a flat list of asset allocations with a rebalance schedule and optional swap preferences. Allocations are validated before the strategy is persisted. * Auth: `x-api-key` header (required) * Scope: `strategies:write` Asset IDs use CAIP-19 format (e.g., `eip155:1/erc20:0xa0b8...`). Allocation weights are string percentages that must sum to 100. Schedule and preferences are stored separately and can be updated independently via `PUT /v2/strategies/:id/schedule` and `PATCH /v2/strategies/:id/preferences`. The `maxApy` field returned on strategy reads is managed by Glider. Common error responses: * `400` when the request body or allocation is invalid * `401` when `x-api-key` header is missing or the key is invalid * `403` when the API key lacks the `strategies:write` scope * `500` on unexpected server errors ```bash cURL theme={null} curl --request POST \ --url 'https://api.glider.fi/v2/strategies' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "name": "Balanced Growth", "description": "Multi-chain balanced allocation strategy", "allocation": { "assets": [ { "assetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "weight": "60" }, { "assetId": "eip155:1/erc20:0xdac17f958d2ee523a2206206994597c13d831ec7", "weight": "40" } ] }, "schedule": { "type": "interval", "frequency": "daily" }, "preferences": { "swap": { "slippageBps": 300, "priceImpactBps": 300, "thresholdUsd": "5.00" } } }' ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.glider.fi/v2/strategies", { method: "POST", headers: { "x-api-key": "gldr_sk_your_api_key", "Content-Type": "application/json", }, body: JSON.stringify({ name: "Balanced Growth", allocation: { assets: [ { assetId: "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", weight: "60" }, { assetId: "eip155:1/erc20:0xdac17f958d2ee523a2206206994597c13d831ec7", weight: "40" }, ], }, schedule: { type: "interval", frequency: "daily" }, preferences: { swap: { slippageBps: 300, thresholdUsd: "5.00" } }, }), }); ``` ```json 201 theme={null} { "success": true, "data": { "strategyId": "01JWZEE2MF30KVRMRX53N88VA4", "name": "Balanced Growth", "description": "Multi-chain balanced allocation strategy", "allocation": { "assets": [ { "assetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "weight": "60" }, { "assetId": "eip155:1/erc20:0xdac17f958d2ee523a2206206994597c13d831ec7", "weight": "40" } ] }, "schedule": { "type": "interval", "frequency": "daily" }, "preferences": { "swap": { "slippageBps": 300, "priceImpactBps": null, "thresholdUsd": "5.00" } }, "isPublic": false, "version": 1, "createdAt": "2026-04-17T12:00:00.000Z" } } ``` ```json 400 theme={null} { "success": false, "error": { "code": "API_400", "message": "Strategy validation failed: ...", "details": ["Allocation weights must sum to 100"] } } ``` # Discover Strategies Source: https://docs.glider.fi/api-reference/endpoints/v2-discovery-strategies GET /v2/discovery/strategies Browse Glider's public, mirrorable strategies — curated picks or top performers. Returns Glider's public, mirrorable strategies so you can surface them to your users and mirror them via the enrollment flow. Every result is public and mirrorable — use the returned `strategyId` with `POST /v2/enroll`. * Auth: `x-api-key` header (required). No additional scope is needed. Choose a `collection`: * `curated` — a hand-picked set of strategies, in editorial order. * `top_performing` — strategies ranked by live analytics. Use `sort` to choose the ranking. Both collections are cursor-paginated (keyset): pass the `nextCursor` from the previous response as the `cursor` query parameter to fetch the next page. `nextCursor` is `null` when there are no more results. Keep `collection` stable across pages. For `top_performing` the cursor already encodes its `sort`, so you may follow `nextCursor` without re-sending `sort`; if you do send it, it must match the cursor's. A cursor from one collection (or one `sort`) is rejected if replayed against another. Which catalog to return: `curated` or `top_performing`. Ranking for `top_performing`. One of `tvl.desc`, `inflows7d.desc`, or `users.desc`. Ignored for `curated`. Max strategies per page. Min 1, max 50. Applies to both collections; follow `nextCursor` to page beyond the first set. Opaque pagination cursor from a previous response. Must be paired with the same `collection` that produced it. For `top_performing` the cursor carries its `sort`, so `sort` need not be re-sent; if sent, it must match. Each item carries display metadata — including `maxApy`, the Glider-managed advertised maximum APY as a decimal percentage string (e.g. `"10.00"` = 10%; absent when not advertised) — its current `allocation` (asset weights, in the same shape as `GET /v2/strategies`), and a `metrics` object: * `metrics.tvlUsd` — total value locked across the strategy's active portfolios, as a USD decimal string rounded to 2 decimal places. `null` when the value is temporarily unavailable (never conflated with a real `"0.00"`). * `metrics.portfolioCount` — number of portfolios created from the strategy. `null` when temporarily unavailable. * `metrics.performance.summary` — target-allocation performance over standard lookback windows (1d…12m, plus `all` spanning the curve), in the same shape as `GET /v2/strategies/{strategyId}/performance`. A fixed window is omitted when the strategy's price history is shorter than that lookback; `all` is present whenever the summary has any windows. When performance can't be computed for an item (unresolvable allocation, insufficient history), `windows` degrades to an empty array rather than dropping the item. For the full chartable curve behind a summary, pass the item's `strategyId` to `GET /v2/strategies/{strategyId}/performance`. A discovery `strategyId` also works with the read endpoints `GET /v2/strategies/{strategyId}`, `GET /v2/strategies/{strategyId}/schedule`, `GET /v2/strategies/{strategyId}/versions`, and `GET /v2/strategies/{strategyId}/preferences` — all returning the creator's stored configuration where present (schedule and preference fields are `null` when the creator has none on record; only the owner can change them). Common error responses: * `400` (`API_400`) when `collection` is missing/invalid or `sort`/`limit` is invalid * `400` (`API_400`) when an explicit `sort` conflicts with the one the `cursor` was issued for * `400` (`API_002`) when `cursor` is malformed or was issued for a different `collection` * `401` when `x-api-key` header is missing or the key is invalid * `500` on unexpected server errors ```bash cURL theme={null} curl --request GET \ --url 'https://api.glider.fi/v2/discovery/strategies?collection=top_performing&sort=tvl.desc&limit=20' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```javascript JavaScript theme={null} // Both collections paginate — follow `nextCursor`, keeping collection (and sort, // for top_performing) stable across pages. const response = await fetch( "https://api.glider.fi/v2/discovery/strategies?collection=top_performing&sort=tvl.desc&limit=20", { headers: { "x-api-key": "gldr_sk_your_api_key" } }, ); const data = await response.json(); // Fetch next page if present if (data.nextCursor) { const next = await fetch( `https://api.glider.fi/v2/discovery/strategies?collection=top_performing&sort=tvl.desc&cursor=${encodeURIComponent(data.nextCursor)}`, { headers: { "x-api-key": "gldr_sk_your_api_key" } }, ); } ``` ```json 200 theme={null} { "success": true, "data": { "strategies": [ { "strategyId": "01JWZEE2MF30KVRMRX53N88VA4", "name": "Balanced Growth", "description": "Multi-chain balanced allocation strategy", "maxApy": "10.00", "allocation": { "assets": [ { "assetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "weight": "60" }, { "assetId": "eip155:1/erc20:0xdac17f958d2ee523a2206206994597c13d831ec7", "weight": "40" } ] }, "createdAt": "2026-01-01T00:00:00.000Z", "metrics": { "tvlUsd": "1250342.18", "portfolioCount": 421, "performance": { "summary": { "windows": [ { "window": "1d", "percentChange": "0.4521", "since": "2026-04-29" }, { "window": "1w", "percentChange": "1.7812", "since": "2026-04-23" }, { "window": "1m", "percentChange": "3.4910", "since": "2026-03-30" }, { "window": "3m", "percentChange": "8.1004", "since": "2026-01-30" }, { "window": "6m", "percentChange": "10.7240", "since": "2025-10-30" }, { "window": "12m", "percentChange": "12.4187", "since": "2025-04-30" }, { "window": "all", "percentChange": "12.4187", "since": "2025-04-30" } ] } } }, "canMirror": true } ] }, "nextCursor": null } ``` ```json 400 theme={null} { "success": false, "error": { "code": "API_400", "message": "Invalid request" } } ``` # Enroll User (Stage 2) Source: https://docs.glider.fi/api-reference/endpoints/v2-enroll POST /v2/enroll Stage 2 of the two-stage enrollment flow — commits the user's signed session-key and creates the portfolio. Stage 2 of the two-stage enrollment flow. Takes the `flowId`, `accountIndex`, and `agentAccountId` returned by `POST /v2/enroll/signature`, plus the user's `signature`, and provisions the portfolio: one smart account per requested chain, plus the rebalance schedule derived from your tenant's execution config. The whole operation succeeds atomically — a failure anywhere rolls back cleanly. Chains can be added to the portfolio later via the [chain-activation flow](/api-reference/endpoints/v2-activate-chains-signature). * Auth: `x-api-key` header (required) * Scope: `enroll:write` The endpoint is idempotent on `flowId` (scoped to your API key) with a 24-hour TTL: * Replaying the same `flowId` with the same body returns the cached response. * Replaying with a different body returns `409 IDEMPOTENCY_KEY_CONFLICT`. * Replaying while the original is still in-flight returns `409 IDEMPOTENCY_IN_PROGRESS`. * Replaying after a typed error (e.g., signature mismatch) returns the same typed error so retries don't get stuck. For EVM, the `signature` is computed by signing `message.raw` from the stage-1 response with the user's wallet, using viem's `signMessage({ message: { raw } })` or the equivalent wallet primitive. For EVM smart-contract wallets, pass `accountType: "ERC1271"` in both stages and have the user sign the stage-1 `message.typedData` EIP-712 payload. The server verifies the signature with ERC-1271 on the requested chain and configures the Kernel vault with the SmartAccount sudo validator. ERC-1271 enrollment currently supports exactly one EVM `chainId` per signature. Wallet addresses are exchanged as [CAIP-10](https://chainagnostic.org/CAIPs/caip-10) account identifiers. EVM `ownerAccountId` / `agentAccountId` use the chain-agnostic form `eip155:0:
`, and each element of the response `smartAccounts` array uses the chain-bound form `eip155::
`. ### Solana (SVM) enrollment For a **Solana-rooted** user, omit `signature` and instead send `signedSolanaTransaction` — the base64 `solanaTransaction` from stage 1 after the user signs it with their wallet. The server re-derives the expected instructions, verifies the user signed, co-signs with the pooled agent, and submits it (the paymaster pays). For an **EVM-rooted** user (Model A subaccount, client-driven single submit), send `userSecp256k1PublicKey` (the user's secp256k1 key — its derived address must equal `ownerAccountId`'s), `signature` (the user's slot-bound secp256k1 authorization over the Swig add-authority payload), `currentSlot` (the slot it was bound to), and optionally `ownerSignatureScheme` (`eip191` default / `raw`). The server replays the signature to add the agent + create the sub-account in one paymaster-sponsored transaction. Capture the slot and submit promptly — Swig enforces a slot window. The response `smartAccounts[]` entry's `accountId` is the **sub-account** the user funds; it is also surfaced as `depositAccountId`, with `swigRoleId` identifying the owning Swig role. The parent Swig PDA is internal. A second enroll for the same (tenant, user) reuses the same Swig and returns a new sub-account at a higher role. `portfolioName` is an optional display name for the portfolio. It is trimmed server-side, returned by portfolio read endpoints, and mutable via `PATCH /v2/portfolios/{portfolioId}`. Names may be up to 64 characters and may include Unicode letters, digits, spaces, and `- _ . ' : ( ) & /`. Common error responses: * `400` for an invalid request body, expired flow, mismatched signature, or invalid strategy ownership * `401` when `x-api-key` header is missing or the key is invalid * `403` when the API key lacks the `enroll:write` scope * `409` `IDEMPOTENCY_IN_PROGRESS` when an earlier request with the same `flowId` is still running * `409` `IDEMPOTENCY_KEY_CONFLICT` when the same `flowId` is reused with a different request body * `409` `PORTFOLIO_ALREADY_EXISTS` when the user is already enrolled at the given `accountIndex` * `500` on unexpected server errors ```bash cURL theme={null} curl --request POST \ --url 'https://api.glider.fi/v2/enroll' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "ownerAccountId": "eip155:0:0xabcdef0000000000000000000000000000000001", "strategyId": "01JWZEE2MF30KVRMRX53N88VA4", "chainIds": [1, 8453, 56], "accountType": "ECDSA", "accountIndex": "7", "agentAccountId": "eip155:0:0x1111111111111111111111111111111111111111", "signature": "0x9412d70d539f889ecec2d3152b68af758d689a4325a43b07811ee708814527c72256fc749d7fd3745a07f0aaa84813955050c461392d9b9ee1f80e106fac39e01b", "flowId": "flow_abc123", "portfolioName": "Alice Portfolio" }' ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.glider.fi/v2/enroll", { method: "POST", headers: { "x-api-key": "gldr_sk_your_api_key", "Content-Type": "application/json", }, body: JSON.stringify({ ownerAccountId: "eip155:0:0xabcdef0000000000000000000000000000000001", strategyId: "01JWZEE2MF30KVRMRX53N88VA4", chainIds: [1, 8453, 56], accountType: "ECDSA", accountIndex: "7", agentAccountId: "eip155:0:0x1111111111111111111111111111111111111111", signature: "0x9412d70d539f889ecec2d3152b68af758d689a4325a43b07811ee708814527c72256fc749d7fd3745a07f0aaa84813955050c461392d9b9ee1f80e106fac39e01b", flowId: "flow_abc123", portfolioName: "Alice's Portfolio", }), }); ``` ```json 201 theme={null} { "success": true, "data": { "portfolioId": "a1b2c3d4", "portfolioName": "Alice Portfolio", "strategyId": "01JWZEE2MF30KVRMRX53N88VA4", "smartAccounts": [ { "accountId": "eip155:1:0x2222222222222222222222222222222222222222" }, { "accountId": "eip155:8453:0x3333333333333333333333333333333333333333" } ] } } ``` ```json 201 (Solana / SVM subaccount) theme={null} { "success": true, "data": { "portfolioId": "a1b2c3d4", "portfolioName": "Alice Portfolio", "strategyId": "01JWZEE2MF30KVRMRX53N88VA4", "smartAccounts": [ { "accountId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:Dep0sit...PdA", "depositAccountId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:Dep0sit...PdA", "swigRoleId": 1 } ] } } ``` ```json 400 theme={null} { "success": false, "error": { "code": "API_400", "message": "Signature does not match the session-key message for this user" } } ``` ```json 409 theme={null} { "success": false, "error": { "code": "API_202", "message": "User 0xabc... is already enrolled at account index 7" } } ``` ```json 409 theme={null} { "success": false, "error": { "code": "API_007", "message": "Another enrollment request with the same flowId is still in progress" } } ``` # Prepare Enrollment Authorization (Stage 1) Source: https://docs.glider.fi/api-reference/endpoints/v2-enroll-signature POST /v2/enroll/signature Stage 1 of the two-stage enrollment flow — returns the wallet authorization payload and round-trip fields. Stage 1 of the two-stage enrollment flow. It verifies that your tenant can enroll users in the `strategyId` and returns the wallet authorization payload plus the round-trip values (`flowId`, `accountIndex`, `agentAccountId`) that stage 2 (`POST /v2/enroll`) needs. EVM enrollment reads a candidate account index. SVM enrollment returns `accountIndex: "0"` as a compatibility placeholder. Wallet addresses are exchanged as [CAIP-10](https://chainagnostic.org/CAIPs/caip-10) account identifiers. For EVM, `ownerAccountId` and `agentAccountId` use the chain-agnostic form `eip155:0:
` — a single wallet works on every EIP-155 chain. For EVM smart-contract wallets, pass `accountType: "ERC1271"` in both stages. ERC-1271 enrollment returns an EIP-712 `typed-data` message and currently requires exactly one EVM `chainId` per enrollment signature. Omit `accountType` or pass `"ECDSA"` for the existing EOA/multichain path. ### Solana (SVM) enrollment A Solana-native user enrolls with `ownerAccountId` set to a `solana:` CAIP-10 (`solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:`) and `chainIds: [1399811149]`. The SVM flow mirrors how a Solana user joins the platform: the first portfolio for a given **(tenant, user)** pair creates a **Swig smart wallet** (the user's wallet key is its root authority — a Solana key for a `solana:` owner, an EVM key for an `eip155:` owner; see below); subsequent portfolios **reuse the same Swig**. Each portfolio is a **sub-account** of that Swig, and the sub-account address is the **deposit address** the user funds. The pooled agent is attached at create, so the portfolio is operable immediately. #### Solana-rooted users (Model B) Because creating the sub-account is an on-chain action, stage 1 returns a **serialized Solana transaction** (`solanaTransaction`) for the user to sign with their wallet — instead of signing `message`. The response also includes: * `solanaTransaction` — base64 transaction; the user signs it and round-trips the signed bytes to `POST /v2/enroll` as `signedSolanaTransaction`. It carries a recent blockhash, so sign and submit promptly. * `depositAccountId` — CAIP-10 of the sub-account the user sends funds to. * `swigRoleId` — the Swig role that owns the sub-account (`1` for the first portfolio, higher when reusing an existing Swig). * `reusedSwig` — `true` when an existing Swig PDA for this (tenant, user) is reused, `false` when a new one is created. #### EVM-rooted users (Model A) A user whose primary wallet is **EVM** enrolls into the same Solana sub-account model by setting `ownerAccountId` to `eip155:0:
` with `chainIds: [1399811149]`. Their secp256k1 (EVM) key becomes the Swig root. Stage 1 returns the targets the client needs to build the authorization: * `swigAccountId` — CAIP-10 of the parent Swig PDA. * `depositAccountId` / `swigRoleId` / `reusedSwig` — as above. * `agentAccountId`, `accountIndex`, `flowId` — round-tripped to stage 2. The `message` is an `ecdsa` digest that is **informational only** — the user does **not** sign it. The client builds a **slot-bound** Swig add-authority payload (from `swigAccountId` + `agentAccountId` + a freshly captured slot), `personal_sign`s its hash, then sends `userSecp256k1PublicKey` + `signature` + `currentSlot` to `POST /v2/enroll`. Capture the slot and submit promptly — Swig enforces a slot window. **There is no create-only fallback**; these fields are required. * Auth: `x-api-key` header (required) * Scope: `enroll:write` The returned `flowId` is the idempotency anchor for the matching `POST /v2/enroll` call — it is valid for 24 hours. Re-submitting the same `flowId` on stage 2 replays the cached response. For EVM, the `accountIndex` is **peeked, not reserved**: repeated stage-1 calls for the same user return the same candidate index, and the index only advances when stage 2 commits successfully. Retries, abandoned flows, and tests do not burn account indices. For SVM, `accountIndex` is always `"0"`. It is a compatibility placeholder, not an account reservation or derivation input. Unknown chains in `chainIds` are rejected with `400` before any enrollment state is created — the allowed list is in the error `details`. Common error responses: * `400` when the request body is invalid, the `strategyId` doesn't exist or your API key cannot access it, or `chainIds` contains an unsupported chain * `401` when `x-api-key` header is missing or the key is invalid * `403` when the API key lacks the `enroll:write` scope * `500` on unexpected server errors ```bash cURL theme={null} curl --request POST \ --url 'https://api.glider.fi/v2/enroll/signature' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "ownerAccountId": "eip155:0:0xabcdef0000000000000000000000000000000001", "strategyId": "01JWZEE2MF30KVRMRX53N88VA4", "chainIds": [1, 8453, 56], "accountType": "ECDSA" }' ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.glider.fi/v2/enroll/signature", { method: "POST", headers: { "x-api-key": "gldr_sk_your_api_key", "Content-Type": "application/json", }, body: JSON.stringify({ ownerAccountId: "eip155:0:0xabcdef0000000000000000000000000000000001", strategyId: "01JWZEE2MF30KVRMRX53N88VA4", chainIds: [1, 8453, 56], accountType: "ECDSA", }), }); ``` ```json 200 theme={null} { "success": true, "data": { "message": { "kind": "ecdsa", "raw": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef" }, "agentAccountId": "eip155:0:0x1111111111111111111111111111111111111111", "accountIndex": "7", "accountType": "ECDSA", "flowId": "flow_abc123" } } ``` ```json 200 (EVM / ERC-1271 smart-contract wallet) theme={null} { "success": true, "data": { "message": { "kind": "typed-data", "typedData": { "domain": { "name": "ERC1271Validator", "version": "0.0.1", "chainId": 1, "verifyingContract": "0x9999999999999999999999999999999999999999" }, "types": { "MessageHash": [{ "name": "hash", "type": "bytes32" }] }, "primaryType": "MessageHash", "message": { "hash": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef" } } }, "agentAccountId": "eip155:0:0x1111111111111111111111111111111111111111", "accountIndex": "7", "accountType": "ERC1271", "flowId": "flow_abc123" } } ``` ```json 200 (Solana / SVM subaccount) theme={null} { "success": true, "data": { "message": { "kind": "solana-message", "text": "Glider — enroll Solana portfolio\n..." }, "agentAccountId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:Ag3nt...PdA", "accountIndex": "0", "flowId": "flow_abc123", "solanaTransaction": "AgAB... (base64; the user signs THIS)", "depositAccountId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:Dep0sit...PdA", "swigRoleId": 1, "reusedSwig": false } } ``` ```json 200 (EVM-rooted / SVM subaccount) theme={null} { "success": true, "data": { "message": { "kind": "ecdsa", "raw": "0xdeadbeef... (informational — the user does NOT sign this)" }, "agentAccountId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:Ag3nt...PdA", "accountIndex": "0", "flowId": "flow_abc123", "swigAccountId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:Sw1g...PdA", "depositAccountId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:Dep0sit...PdA", "swigRoleId": 1, "reusedSwig": false } } ``` ```json 400 theme={null} { "success": false, "error": { "code": "API_400", "message": "Request validation failed", "details": [ "chainIds: Unsupported chainId: [999]. Supported: [1, 8453, 42161, 56, 4663]" ] } } ``` ```json 401 theme={null} { "success": false, "error": { "code": "API_101", "message": "Missing API key" } } ``` ```json 403 theme={null} { "success": false, "error": { "code": "API_104", "message": "API key is missing required scope: enroll:write" } } ``` # Get Operation Status Source: https://docs.glider.fi/api-reference/endpoints/v2-get-operation GET /v2/portfolios/{portfolioId}/operations/{operationId} Poll the execution state of an async operation (withdrawal, rebalance, etc.) dispatched on a portfolio. Returns the current lifecycle state of an operation previously dispatched via a write endpoint such as `POST /v2/portfolios/{portfolioId}/withdraw` or `POST /v2/portfolios/{portfolioId}/rebalance`. Use to poll for onchain confirmation. Operations transition through: ``` accepted → running → (completed | failed | cancelled) ↕ retrying / awaiting_user ``` `completed`, `failed`, and `cancelled` are terminal — stop polling once you see one. * Auth: `x-api-key` header (required) * Scope: `portfolios:read` ### Polling cadence Poll at 2–5 second intervals. There is no SLA on when a dispatched withdrawal confirms onchain — it depends on the target chain's block time, bundler throughput, and paymaster availability. For withdraws, typical end-to-end latency is under a minute on Base / Arbitrum / Polygon and under \~30 seconds on L1 in most conditions; but pathological conditions (RPC congestion, paymaster rebalancing) may push this higher. ### Response fields | Field | Type | Notes | | ------------------------- | ---------------- | ------------------------------------------------------------------------------ | | `operationId` | string | Echo of the URL param. | | `portfolioId` | string | Echo of the URL param. | | `kind` | string | `withdraw`, `rebalance`, `bridge`, `swap`, `deposit`, `transfer`, `execution`. | | `state` | enum | Current lifecycle state. | | `createdAt` / `updatedAt` | ISO 8601 | Operation timestamps. | | `finishedAt` | ISO 8601 or null | Set once the operation reaches a terminal state. | | `error` | string or null | Set on `failed`/`cancelled`. Free-form message. | Common error responses: * `401` when `x-api-key` header is missing or the key is invalid * `403` when the API key lacks the `portfolios:read` scope * `404` when the portfolio doesn't exist, your API key cannot access it, or the `operationId` doesn't correspond to any operation on that portfolio * `500` on unexpected server errors ```bash cURL theme={null} curl --request GET \ --url 'https://api.glider.fi/v2/portfolios/a1b2c3d4/operations/op_01J_ABCDEF' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```json 200 — running theme={null} { "success": true, "data": { "operationId": "op_01J_ABCDEF", "portfolioId": "a1b2c3d4", "kind": "withdraw", "state": "running", "createdAt": "2026-04-17T12:05:00.000Z", "updatedAt": "2026-04-17T12:05:10.000Z", "finishedAt": null, "error": null } } ``` ```json 200 — completed theme={null} { "success": true, "data": { "operationId": "op_01J_ABCDEF", "portfolioId": "a1b2c3d4", "kind": "withdraw", "state": "completed", "createdAt": "2026-04-17T12:05:00.000Z", "updatedAt": "2026-04-17T12:05:28.000Z", "finishedAt": "2026-04-17T12:05:28.000Z", "error": null } } ``` ```json 404 theme={null} { "success": false, "error": { "code": "API_200", "message": "Portfolio or operation not found" } } ``` # Get Portfolio Source: https://docs.glider.fi/api-reference/endpoints/v2-get-portfolio GET /v2/portfolios/{portfolioId} Returns the authenticated tenant's portfolio detail by ID. Returns a single portfolio owned by the authenticated tenant — identity, portfolio display name, strategy metadata, per-chain smart accounts, and rebalance schedule state. For real-time balances and USD values call `GET /v2/portfolios/{portfolioId}/positions`. The `schedule` block is the canonical source for rebalance visibility: | Field | Use | | -------------------------- | --------------------------------------------------------------------------------------- | | `status` | `active` (scheduler will tick) vs `paused` (integrator stopped automation). | | `frequency` / `intervalMs` | Resolved cadence of the strategy. | | `nextDueAt` | When the next automated rebalance will fire. Render "next rebalance in …" UI from this. | | `lastRebalanceAt` | When the most recent run (manual or scheduled) completed. | * Auth: `x-api-key` header (required) * Scope: `portfolios:read` All on-chain identifiers are [CAIP](https://github.com/ChainAgnostic/CAIPs)-shaped. End-user wallets use the chain-agnostic CAIP-10 form `eip155:0:
`; smart accounts use the chain-bound form `eip155::
`. Owners are EVM EOAs today. Returns `404` for portfolios your API key does not have access to — same response shape as a portfolio that doesn't exist. Portfolio identifier returned by `POST /v2/enroll` or `GET /v2/portfolios`. Common error responses: * `400` when the path parameter is invalid * `401` when `x-api-key` header is missing or the key is invalid * `403` when the API key lacks the `portfolios:read` scope * `404` when the portfolio does not exist or does not belong to the tenant * `500` on unexpected server errors ```bash cURL theme={null} curl --request GET \ --url 'https://api.glider.fi/v2/portfolios/a1b2c3d4' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/portfolios/a1b2c3d4", { headers: { "x-api-key": "gldr_sk_your_api_key" } }, ); const data = await response.json(); ``` ```json 200 theme={null} { "success": true, "data": { "portfolioId": "a1b2c3d4", "portfolioName": "Alice Portfolio", "ownerAccountId": "eip155:0:0xabcdef0000000000000000000000000000000001", "strategyId": "01JWZEE2MF30KVRMRX53N88VA4", "strategyName": "Conservative Yield", "strategyDescription": "Multi-chain balanced allocation strategy", "smartAccounts": [ { "accountId": "eip155:8453:0xe3a2d1f49aee887e42655b56371d4d76bbf58058" } ], "schedule": { "status": "active", "frequency": "daily", "intervalMs": 86400000, "nextDueAt": "2026-04-16T12:00:00.000Z", "lastRebalanceAt": "2026-04-15T12:00:00.000Z" }, "createdAt": "2026-04-10T08:30:00.000Z", "strategyVersion": 4, "updatedAt": "2026-04-15T12:00:00.000Z" } } ``` ```json 404 theme={null} { "success": false, "error": { "code": "API_006", "message": "Portfolio with ID a1b2c3d4 not found" } } ``` # Get Portfolio Performance Source: https://docs.glider.fi/api-reference/endpoints/v2-get-portfolio-performance GET /v2/portfolios/{portfolioId}/performance Returns portfolio performance for an enrolled portfolio. Returns a daily performance curve for the authenticated tenant's portfolio. By default, returns are reported as money-weighted return (MWR), which reflects the user's actual money outcome after deposits and withdrawals. B2B clients can pass `returnMethod=TWR` when they need the strategy-style time-weighted view. * Auth: `x-api-key` header (required) * Scope: `portfolios:read` Portfolio identifier returned by `POST /v2/enroll` or `GET /v2/portfolios`. Return methodology for the curve. Defaults to `MWR`. Common error responses: * `400` when the path parameter is invalid * `401` when `x-api-key` header is missing or the key is invalid * `403` when the API key lacks the `portfolios:read` scope * `404` when the portfolio does not exist or does not belong to the tenant * `500` on unexpected server errors ```bash cURL theme={null} curl --request GET \ --url 'https://api.glider.fi/v2/portfolios/a1b2c3d4/performance' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/portfolios/a1b2c3d4/performance", { headers: { "x-api-key": "gldr_sk_your_api_key" } }, ); const data = await response.json(); ``` The response carries a `meta` block describing the calculation method and a `summary` block with available lookback returns. `summary` is omitted when there are not enough points to calculate a lookback. * `meta.method` is `MWR` by default, or `TWR` when requested. * `meta.resolution` is `1d`; `points[]` has at most one entry per UTC date. * `points[].percentChange` is the cumulative return at that point under `meta.method`, or `null` when a return is unavailable for that point. `TWR` is compounded from the first returned point; `MWR` is your money-weighted return, whose basis shifts as deposits and withdrawals change. For `MWR`, use `summary.windows[]` for lookback returns rather than differencing two `percentChange` values. * `points[].valueUsd` is the portfolio value for that UTC day. * `points[].cashFlowUsd` is the signed net cash flow for that UTC day: positive on net-deposit days, negative on net-withdrawal days, `0` otherwise. * `summary.windows[]` carries lookbacks ordered shortest to longest (`1d`, `1w`, `1m`, `3m`, `6m`, `12m`, `all`). Fixed windows are included when enough history is available; `all` anchors on the curve's first point and equals the cumulative return under `meta.method`. ```json 200 theme={null} { "success": true, "data": { "portfolioId": "a1b2c3d4", "strategyId": "01JWZEE2MF30KVRMRX53N88VA4", "meta": { "method": "MWR", "currency": "USD", "resolution": "1d", "asOf": "2026-04-30T12:00:00.000Z" }, "points": [ { "date": "2026-04-28", "percentChange": "0.0000", "valueUsd": "1000", "cashFlowUsd": "0" }, { "date": "2026-04-29", "percentChange": "1.2000", "valueUsd": "912", "cashFlowUsd": "-100" }, { "date": "2026-04-30", "percentChange": "2.5000", "valueUsd": "1025", "cashFlowUsd": "100", "isLive": true } ], "summary": { "windows": [ { "window": "1d", "percentChange": "1.2846", "since": "2026-04-29" }, { "window": "all", "percentChange": "2.5000", "since": "2026-04-28" } ] } } } ``` # Get Portfolio Positions Source: https://docs.glider.fi/api-reference/endpoints/v2-get-portfolio-positions GET /v2/portfolios/{portfolioId}/positions Returns real-time per-asset token balances and USD values for a portfolio. Returns the portfolio's live positions — per-asset balances, unit prices, and USD values — plus an aggregate `totalValueUsd`. During an open bridge, `totalValueUsd` includes the `inTransit.totalUsd` component so portfolio value does not dip while funds move between chains; use `inTransit.items[]` to reconcile the component that is not yet represented as a settled destination asset row. Responses are cached for up to 30 seconds, so rapid polling from a single caller is cheap. For schedule state (`nextDueAt`, `lastRebalanceAt`, `status`), strategy metadata, and smart account addresses without live balances, use `GET /v2/portfolios/{portfolioId}` instead. **One shape, every asset class.** The `assets[]` array uses the same row shape (`assetId`, `symbol`, `decimals`, `balance`, `balanceRaw`, `priceUsd`, `valueUsd`) for ERC-20 tokens, SPL tokens, and tokenized real-world assets (equities, treasuries) when the underlying allocation includes them. Symbol, decimals, and price come from Glider's upstream data providers and are passed through unchanged. * Auth: `x-api-key` header (required) * Scope: `portfolios:read` **Partial failures are surfaced, not raised as errors.** When a chain is temporarily unavailable or a price is missing, the endpoint still returns `200` with whatever loaded and a structured `warnings[]` array describing the gap. A full failure returns `totalValueUsd: "0"`, `assets: []`, and populated `warnings` — never a `5xx` for transient issues. **Multi-chain by construction.** Every asset entry carries a full CAIP-19 `assetId` and a CAIP-10 `smartAccountId`, so EVM and non-EVM holdings share one wire shape. Portfolio identifier returned by `POST /v2/enroll` or `GET /v2/portfolios`. Common error responses: * `400` when the path parameter is invalid * `401` when `x-api-key` header is missing or the key is invalid * `403` when the API key lacks the `portfolios:read` scope * `404` when the portfolio does not exist or does not belong to the tenant * `500` on unexpected server errors Warning kinds (in `warnings[]`): * `MISSING_SMART_ACCOUNT` — no smart account is known for the requested chain * `RPC_ERROR` — chain RPC timed out or errored * `MISSING_PRICE` — no USD price found for an asset * `BLOCKLISTED_ASSET` — asset is blocked and was skipped * `MISSING_ASSET` — asset metadata is unavailable * `IN_TRANSIT_READ_FAILED` — bridge in-transit value could not be read ```bash cURL theme={null} curl --request GET \ --url 'https://api.glider.fi/v2/portfolios/a1b2c3d4/positions' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/portfolios/a1b2c3d4/positions", { headers: { "x-api-key": "gldr_sk_your_api_key" } }, ); const data = await response.json(); if (data.data.warnings.length > 0) { // Show a stale-data banner; the response is still usable for whatever loaded. } ``` ```json 200 theme={null} { "success": true, "data": { "portfolioId": "a1b2c3d4", "totalValueUsd": "1525.500000", "inTransit": { "totalUsd": "25.000000", "items": [ { "sourceAssetId": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913:8453", "destinationChainId": 42161, "amountRaw": "25000000", "valueUsd": "25.000000" } ] }, "fetchedAt": "2026-04-15T12:34:56.000Z", "smartAccounts": [ { "accountId": "eip155:8453:0xe3a2d1f49aee887e42655b56371d4d76bbf58058" } ], "assets": [ { "assetId": "eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", "smartAccountId": "eip155:8453:0xe3a2d1f49aee887e42655b56371d4d76bbf58058", "symbol": "USDC", "decimals": 6, "balance": "1500.500000", "balanceRaw": "1500500000", "priceUsd": "1.000000", "valueUsd": "1500.500000" } ], "warnings": [] } } ``` ```json 200 (partial) theme={null} { "success": true, "data": { "portfolioId": "a1b2c3d4", "totalValueUsd": "1000.000000", "inTransit": { "totalUsd": "0", "items": [] }, "fetchedAt": "2026-04-15T12:34:56.000Z", "smartAccounts": [ { "accountId": "eip155:8453:0xe3a2d1f49aee887e42655b56371d4d76bbf58058" } ], "assets": [ { "assetId": "eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", "smartAccountId": "eip155:8453:0xe3a2d1f49aee887e42655b56371d4d76bbf58058", "symbol": "USDC", "decimals": 6, "balance": "1000.000000", "balanceRaw": "1000000000", "priceUsd": "1.000000", "valueUsd": "1000.000000" } ], "warnings": [ { "kind": "RPC_ERROR", "message": "chain 1 RPC timeout" } ] } } ``` ```json 404 theme={null} { "success": false, "error": { "code": "API_006", "message": "Portfolio with ID a1b2c3d4 not found" } } ``` # Get Portfolio Sector Exposure Source: https://docs.glider.fi/api-reference/endpoints/v2-get-portfolio-sector-exposure GET /v2/portfolios/{portfolioId}/sector-exposure Returns eligible equity exposure grouped by Glider's canonical sector taxonomy. Returns the portfolio's current eligible equity exposure grouped by Glider's canonical display sectors. Tokenized equities are resolved to their canonical underlying equity before classification. * Auth: `x-api-key` header (required) * Scope: `portfolios:read` * The requested portfolio must belong to the authenticated tenant. Unsupported non-equity positions are excluded from the exposure denominator and returned under `diagnostics.unsupportedPositions`. Equity positions that cannot be classified remain in the denominator and appear in the `Unclassified` row and `diagnostics.unclassifiedPositions`. All monetary values and weights are decimal strings. Do not parse and re-serialize them through floating-point arithmetic. ```bash cURL theme={null} curl --request GET \ --url 'https://api.glider.fi/v2/portfolios/01JWZEE2MF30KVRMRX53N88VA4/sector-exposure' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```json 200 theme={null} { "success": true, "data": { "portfolioId": "01JWZEE2MF30KVRMRX53N88VA4", "asOfDate": "2026-07-23T16:00:00.000Z", "taxonomy": "internal_display_sector_v1", "totalMarketValueUsd": "1000.000000", "classifiedMarketValueUsd": "800.000000", "unclassifiedMarketValueUsd": "200.000000", "rows": [ { "displaySector": "Technology", "marketValueUsd": "800.000000", "weight": "0.800000", "classifiedPositionCount": 2, "totalPositionCount": 2, "primarySources": [ { "source": "financedatabase_symbol_exchange", "count": 2, "marketValueUsd": "800.000000" } ] }, { "displaySector": "Unclassified", "marketValueUsd": "200.000000", "weight": "0.200000", "classifiedPositionCount": 0, "totalPositionCount": 1, "primarySources": [ { "source": "unclassified", "count": 1, "marketValueUsd": "200.000000" } ] } ], "diagnostics": { "unsupportedPositions": [], "unclassifiedPositions": [ { "assetId": "eip155:1/erc20:0x1111111111111111111111111111111111111111", "symbol": "EXAMPLE", "assetCanonicalId": "equity:example", "reason": "No canonical sector classification", "marketValueUsd": "200.000000" } ], "lowConfidencePositions": [] } } } ``` ```json 404 theme={null} { "success": false, "error": { "code": "API_200", "message": "Portfolio not found" } } ``` The endpoint returns `500 API_600` when live portfolio valuation is temporarily unavailable. Treat that as a transient server error and retry with backoff. # Get Strategy Source: https://docs.glider.fi/api-reference/endpoints/v2-get-strategy GET /v2/strategies/{strategyId} Returns strategy detail by ID. Returns a single strategy, including its allocation, rebalance schedule, swap preferences, and version number. You can read your own strategies, or any public strategy listed by [List Discovery Strategies](/api-reference/endpoints/v2-discovery-strategies). * Auth: `x-api-key` header (required) * Scope: `strategies:read` Allocation asset IDs are returned in CAIP-19 format. For your own strategies `schedule` is always populated; for public strategies you don't own it is the creator's cadence, or `null` when the creator has none configured. `preferences` shows the creator's overrides — inner fields are `null` when no override is set. `maxApy` is the strategy's advertised maximum APY as a decimal percentage string (e.g. `"10.00"` = 10%). It is managed by Glider and read-only through the API — display metadata, not a computed return. Read it instead of parsing APY figures out of the strategy name or description. The field is absent when no APY is advertised. Strategy identifier. Common error responses: * `400` when the path parameter is invalid * `401` when `x-api-key` header is missing or the key is invalid * `403` when the API key lacks the `strategies:read` scope * `404` when the strategy does not exist, or is neither owned by the tenant nor listed as a public strategy in discovery * `500` on unexpected server errors ```bash cURL theme={null} curl --request GET \ --url 'https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4", { headers: { "x-api-key": "gldr_sk_your_api_key" } }, ); const data = await response.json(); ``` ```json 200 theme={null} { "success": true, "data": { "strategyId": "01JWZEE2MF30KVRMRX53N88VA4", "name": "Balanced Growth", "description": "Multi-chain balanced allocation strategy", "maxApy": "10.00", "allocation": { "assets": [ { "assetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "weight": "60" }, { "assetId": "eip155:1/erc20:0xdac17f958d2ee523a2206206994597c13d831ec7", "weight": "40" } ] }, "schedule": { "type": "interval", "frequency": "daily" }, "preferences": { "swap": { "slippageBps": 300, "priceImpactBps": null, "thresholdUsd": "5.00" } }, "isPublic": false, "createdAt": "2026-03-31T12:00:00.000Z", "version": 3 } } ``` ```json 404 theme={null} { "success": false, "error": { "code": "API_006", "message": "Strategy not found: 01JWZEE2MF30KVRMRX53N88VA4" } } ``` # Get strategy fees Source: https://docs.glider.fi/api-reference/endpoints/v2-get-strategy-fees GET /v2/strategies/{strategyId}/fees Returns the strategy's integrator-fee override. Returns the strategy's stored integrator-fee override. `swapBps` is `null` when no per-strategy override is configured. The tenant-wide default lives at [`GET /v2/tenant/fees`](/api-reference/endpoints/v2-get-tenant-fees). * Auth: `x-api-key` header (required) * Required scope: `fees:read` Cross-tenant access returns 404. ```bash cURL theme={null} curl --request GET \ --url 'https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4/fees' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4/fees", { headers: { "x-api-key": "gldr_sk_your_api_key" } }, ); const data = await response.json(); ``` ```json Override set theme={null} { "success": true, "data": { "swapBps": 45 } } ``` ```json No override theme={null} { "success": true, "data": { "swapBps": null } } ``` # Get Strategy Performance Source: https://docs.glider.fi/api-reference/endpoints/v2-get-strategy-performance GET /v2/strategies/{strategyId}/performance Returns chartable target-allocation TWR performance for a strategy. Returns a daily performance curve for one of your strategies, or for any public strategy listed by [List Discovery Strategies](/api-reference/endpoints/v2-discovery-strategies). The curve covers the shorter of 12 months or the shared asset price-history window. Your own strategies use their configured rebalance cadence; public strategies use the creator's rebalance cadence when available, defaulting to a daily rebalance assumption. The `schedule` in the response states which cadence the curve was computed with. Note the discovery listing's `metrics.performance.summary` is always computed at the daily assumption, so it can differ from this endpoint's summary when a creator cadence applies. Strategy performance is always reported as TWR (time-weighted return). * Auth: `x-api-key` header (required) * Scope: `strategies:read` Strategy identifier. Common error responses: * `400` when the path parameter is invalid, or when the strategy's stored spec references an unknown asset (`caip_asset_id` not registered) or an invalid allocation weight. The error body's `message` field carries the offending CAIP id(s) when an unknown asset is the cause. * `401` when `x-api-key` header is missing or the key is invalid * `403` when the API key lacks the `strategies:read` scope * `404` when the strategy does not exist, or is neither yours nor public * `500` on unexpected server errors ```bash cURL theme={null} curl --request GET \ --url 'https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4/performance' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4/performance", { headers: { "x-api-key": "gldr_sk_your_api_key" } }, ); const data = await response.json(); ``` The response carries a `meta` block with the calculation provenance and a `summary` block derived from the points (omitted when the curve has fewer than two points). * `meta.method` is always `TWR` (time-weighted return). * `meta.resolution` is `1d`; `points[]` has at most one entry per UTC date. * `meta.asOf` is the wall-clock the curve was computed at — useful for caching and for displaying "as of X" labels. * The curve's window is implicit in `points`: the first/last point dates are the start/end. `points.at(-1).percentChange` is the cumulative return since `points[0].date`. * `summary.windows[]` carries one entry per lookback that has a usable anchor in the curve, ordered shortest → longest (`1d`, `1w`, `1m`, `3m`, `6m`, `12m`, `all`). Each entry is `{ window, percentChange, since }` where `since` is the anchor date used. Fixed windows whose lookback predates the curve are omitted (`12m` is omitted unless the strategy has the full 12-month history). `all` anchors on the curve's first point and equals the cumulative return, so it is present whenever `summary` is. ```json 200 theme={null} { "success": true, "data": { "strategyId": "01JWZEE2MF30KVRMRX53N88VA4", "schedule": { "type": "interval", "frequency": "monthly" }, "meta": { "method": "TWR", "currency": "USD", "resolution": "1d", "asOf": "2026-04-30T12:00:00.000Z" }, "points": [ { "date": "2025-04-30", "percentChange": "0.0000" }, { "date": "2025-05-31", "percentChange": "1.8742" }, { "date": "2026-04-30", "percentChange": "12.4187" } ], "summary": { "windows": [ { "window": "1d", "percentChange": "0.4521", "since": "2026-04-29" }, { "window": "1w", "percentChange": "1.7812", "since": "2026-04-23" }, { "window": "1m", "percentChange": "3.4910", "since": "2026-03-30" }, { "window": "3m", "percentChange": "8.1004", "since": "2026-01-30" }, { "window": "6m", "percentChange": "10.7240", "since": "2025-10-30" }, { "window": "12m", "percentChange": "12.4187", "since": "2025-04-30" }, { "window": "all", "percentChange": "12.4187", "since": "2025-04-30" } ] } } } ``` # Get strategy preferences Source: https://docs.glider.fi/api-reference/endpoints/v2-get-strategy-preferences GET /v2/strategies/{strategyId}/preferences Returns the strategy's stored preference overrides. Returns the strategy's stored preference overrides. Swap settings live under the `swap` namespace: `slippageBps`, `priceImpactBps`, and `thresholdUsd`. Each field is `null` when no override is set on this strategy. The strategy's schedule is mutated through `PUT /v2/strategies/{strategyId}/schedule` and is **not** returned by this endpoint. * Auth: `x-api-key` header (required) * Required scope: `strategies:read` Public strategies listed by [List Discovery Strategies](/api-reference/endpoints/v2-discovery-strategies) are also readable and return the creator's stored per-strategy overrides, when they have any. Note this is a config-only view: many public strategies carry no per-strategy overrides (all fields `null`), and a mirrored portfolio's effective swap settings are resolved at execution time on each rebalance — drawing on the creator's account-level configuration when no per-strategy override exists — so all-`null` here does not mean the strategy executes without swap preferences, and later changes by the creator carry through to mirrored portfolios. Only the strategy's owner can change these overrides (a `PATCH` on a strategy you don't own returns `404`). Anything else returns `404` — the API does not leak the existence of strategies you don't own. ```bash cURL theme={null} curl --request GET \ --url 'https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4/preferences' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4/preferences", { headers: { "x-api-key": "gldr_sk_your_api_key" } }, ); const data = await response.json(); ``` ```json Override set theme={null} { "success": true, "data": { "swap": { "slippageBps": 250, "priceImpactBps": null, "thresholdUsd": "5.00" } } } ``` ```json No override theme={null} { "success": true, "data": { "swap": { "slippageBps": null, "priceImpactBps": null, "thresholdUsd": null } } } ``` # Get strategy schedule Source: https://docs.glider.fi/api-reference/endpoints/v2-get-strategy-schedule GET /v2/strategies/{strategyId}/schedule Returns the strategy's rebalance schedule. Returns the *configured* rebalance cadence for this strategy. Every strategy you create has a schedule (set at creation; updated via `PUT /v2/strategies/{strategyId}/schedule`). This endpoint returns config only (`type` + `frequency`). For runtime state per portfolio — `nextDueAt`, `lastRebalanceAt`, and active/paused `status` — read the `schedule` block on `GET /v2/portfolios/{portfolioId}`. * Auth: `x-api-key` header (required) * Required scope: `strategies:read` Public strategies listed by [List Discovery Strategies](/api-reference/endpoints/v2-discovery-strategies) are also readable: `data` is the creator's cadence, or `null` when the creator has no schedule configured. Anything else returns `404` — the API does not leak the existence of strategies you don't own. ```bash cURL theme={null} curl --request GET \ --url 'https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4/schedule' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```json Schedule theme={null} { "success": true, "data": { "type": "interval", "frequency": "daily" } } ``` ```json Public strategy without a schedule theme={null} { "success": true, "data": null } ``` # Get tenant fees Source: https://docs.glider.fi/api-reference/endpoints/v2-get-tenant-fees GET /v2/tenant/fees Returns the tenant's integrator-fee defaults. Returns the tenant's stored integrator swap-fee default. `swapBps` is the basis-point fee charged on the output side of every swap; `null` when no default is configured. * Auth: `x-api-key` header (required) * Required scope: `fees:read` Per-strategy overrides live at [`GET /v2/strategies/{strategyId}/fees`](/api-reference/endpoints/v2-get-strategy-fees). ```bash cURL theme={null} curl --request GET \ --url 'https://api.glider.fi/v2/tenant/fees' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.glider.fi/v2/tenant/fees", { headers: { "x-api-key": "gldr_sk_your_api_key" }, }); const data = await response.json(); ``` ```json Configured theme={null} { "success": true, "data": { "swapBps": 50 } } ``` ```json Not configured theme={null} { "success": true, "data": { "swapBps": null } } ``` # Get tenant preferences Source: https://docs.glider.fi/api-reference/endpoints/v2-get-tenant-preferences GET /v2/tenant/preferences Returns the tenant's stored preference defaults. Returns the tenant's stored preference defaults. Swap settings live under the `swap` namespace: `slippageBps`, `priceImpactBps`, and `thresholdUsd` (minimum swap size in USD). Each field is returned as `null` when not configured for this tenant. * Auth: `x-api-key` header (required) * Required scope: `tenant:read` Schedule is per-strategy — see [`GET /v2/strategies/{strategyId}/schedule`](/api-reference/endpoints/v2-get-strategy-schedule). ```bash cURL theme={null} curl --request GET \ --url 'https://api.glider.fi/v2/tenant/preferences' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.glider.fi/v2/tenant/preferences", { headers: { "x-api-key": "gldr_sk_your_api_key" }, }); const data = await response.json(); ``` ```json Configured theme={null} { "success": true, "data": { "swap": { "slippageBps": 300, "priceImpactBps": 250, "thresholdUsd": "5.00" } } } ``` ```json No preferences set theme={null} { "success": true, "data": { "swap": { "slippageBps": null, "priceImpactBps": null, "thresholdUsd": null } } } ``` # Submit Liquidate-All (Stage 2) Source: https://docs.glider.fi/api-reference/endpoints/v2-liquidate-all POST /v2/portfolios/{portfolioId}/liquidate-all Stage 2 of the two-stage liquidate-all flow — submits the user-signed authorization. Accepted operations dispatch async. Stage 2 of the two-stage liquidate-all flow. Accepts the `message` returned by [`POST .../liquidate-all/signature`](./v2-liquidate-all-signature) verbatim, plus the owner's `signature`. Verifies ownership, re-checks live balances, and dispatches the swap to the settlement asset + delivery to the recipient. The authorized assets, settlement asset, and recipient all remain on the same chain; this flow does not perform cross-chain delivery or bridging. This is functionally identical to [`POST .../withdraw`](./v2-withdraw) submitting a `liquidate: true` authorization — same verification, idempotency (keyed on `message.nonce`), and polling. Echo the stage-1 `message` back as `body.message` (`typedData.message` for EVM portfolios, `authorization.message` for Solana); the `liquidate: true` flag and the `settlementAssetId` settlement asset must be present and are part of the signed bytes. * Auth: `x-api-key` header (required) * Scope: `portfolios:withdraw` * Chains: **EVM** and **Solana** (Solana requires the tenant's Solana B2B API access — without it, a Solana recipient returns `403`). ### Polling for onchain status The response returns `operationId`. Poll [`GET /v2/portfolios/{portfolioId}/operations/{operationId}`](./v2-get-operation) until the operation reaches a terminal (`completed` / `failed` / `cancelled`) state. The operation tracks the swap that produces and delivers the settlement asset. ### Solana portfolios A Solana liquidation runs as **one engine operation**: every authorized asset is swapped to USDC inside the portfolio's smart account (Jupiter routing), and the combined proceeds — the swap outputs plus any USDC the portfolio already held — are then transferred to the recipient in a final leg. Nothing leaves the smart account until every swap has settled, so a failed liquidation keeps all value inside the portfolio and a fresh authorization retries cleanly (already-swapped USDC simply becomes part of the next liquidation's direct transfer). Native SOL is liquidated too, minus a small rent reserve that keeps the account rent-exempt. ### Portfolios holding Ondo tokenized stocks Ondo Global Markets tokens (tokenized stocks such as `TSLAon`, `SPCXon`) are liquidated through Ondo's direct redemption — not on-market swaps — for materially better pricing. All Ondo holdings are redeemed in a single batched settlement and the proceeds are delivered to the recipient in the settlement asset. Two consequences: * **Settlement asset constraint.** Ondo redemption settles in the chain's Ondo cash asset: **USDT on BNB Chain (56)**, **USDC on Ethereum (1)**. A liquidation of a portfolio holding Ondo tokens with any other `settlementAssetId` is rejected at stage 1 with `400 API_221 UNSUPPORTED_SETTLEMENT_ASSET`, before anything is signed. To exit to a different stable, withdraw the Ondo tokens in-kind via [`POST .../withdraw`](./v2-withdraw) instead. * **Market hours.** Redemption requires an open US equities session. Submissions outside market hours (or during a halt) are accepted (`202`) but the redemption operation can fail after retries with an Ondo market-closed error — resubmit during regular trading hours. When Ondo assets are present, the returned `operationId` tracks the redemption operation (the slowest leg). Any same-chain transfer of an existing settlement-asset balance and any swap of non-Ondo assets dispatch as separate engine operations under the same authorization. ### Common error responses Identical to [`POST .../withdraw`](./v2-withdraw#common-error-responses), including `400 API_219 WITHDRAW_AS_USDC_UNSUPPORTED_CHAIN` when the default USDC settlement asset is unavailable on the recipient's chain and `400 API_221 UNSUPPORTED_SETTLEMENT_ASSET` when `message.settlementAssetId` is not an allowed settlement asset on the chain (USDC or USDT on EVM; USDC on Solana), plus the shared expiry / signature / idempotency errors. ```bash cURL theme={null} curl --request POST \ --url 'https://api.glider.fi/v2/portfolios/a1b2c3d4/liquidate-all' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "message": { "portfolioId": "a1b2c3d4", "recipientAccountId": "eip155:1:0x4444444444444444444444444444444444444444", "assets": [ { "assetId": "eip155:1/erc20:0x6b175474e89094c44da98b954eedeac495271d0f", "amountRaw": "5000000000000000000" } ], "nonce": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", "expiresAt": 1744898400, "liquidate": true, "settlementAssetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" }, "signature": "0xabc..." }' ``` ```json 202 theme={null} { "success": true, "data": { "operationId": "op_01JWZEE2MF30KVRMRX53N88VA4", "submittedAt": "2026-04-17T12:05:00.000Z" } } ``` # Prepare Liquidate-All (Stage 1) Source: https://docs.glider.fi/api-reference/endpoints/v2-liquidate-all-signature POST /v2/portfolios/{portfolioId}/liquidate-all/signature Stage 1 of the two-stage liquidate-all flow — enumerates every holding on the recipient's chain above the swap threshold and returns the authorization to swap them all to the chosen settlement asset (USDC, or USDT on EVM). Stage 1 of the two-stage liquidate-all flow. Reads the portfolio's live positions on the recipient's chain, selects every holding whose value is above the tenant's swap threshold, and returns a signable authorization that swaps the assets to one settlement asset and delivers it to `recipientAccountId`. Stage 2 (`POST /v2/portfolios/{portfolioId}/liquidate-all`) submits the signed authorization. This is the whole-portfolio counterpart of [`POST .../withdraw/signature`](./v2-withdraw-signature) with `liquidate: true` — you don't list assets; the server enumerates them. The signed message it returns is a standard withdrawal authorization with `liquidate: true`, so it behaves identically at stage 2. * Auth: `x-api-key` header (required) * Scope: `portfolios:withdraw` * Chains: **EVM** and **Solana** (Solana requires the tenant's Solana B2B API access — without it, a Solana recipient returns `403`). `recipientAccountId` is a chain-bound [CAIP-10](https://chainagnostic.org/CAIPs/caip-10) identifier — `eip155::
` (EVM) or `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:` (Solana). Liquidation is scoped to that one chain — holdings on other chains are untouched. To liquidate a multi-chain portfolio fully, call this once per chain. `recipientAccountId` selects the portfolio vault and chain to liquidate. If the portfolio has no vault on that chain, stage 1 returns `400 API_215 PORTFOLIO_HAS_NO_VAULT_ON_CHAIN`. For a multi-chain portfolio, only holdings on the selected chain are liquidated; holdings on other chains remain untouched. The settlement asset and recipient must be on the selected chain, and this endpoint does not bridge proceeds between chains. The shape of the returned authorization matches the withdraw flow: EVM portfolios receive an EIP-712 typed-data object under `data.typedData`; Solana portfolios receive an off-chain authorization under `data.authorization` (`kind: "ecdsa"` — EVM-rooted owner, sign `raw` via EIP-191 `personal_sign`; `kind: "solana-message"` — Solana-rooted owner, sign `text` via ed25519). See [Prepare Withdrawal](./v2-withdraw-signature) for the signing details. ### Settlement asset By default every holding is swapped to **USDC** on the recipient's chain. Pass an optional `settlementAssetId` whose CAIP-19 chain matches `recipientAccountId`: configured **USDC** or **USDT** for an EVM liquidation, or **USDC** for a Solana liquidation. Any asset that isn't an allowed settlement asset on the chain returns `400 API_221 UNSUPPORTED_SETTLEMENT_ASSET`. The chosen asset is bound into the signed message. On Solana the proceeds are swapped inside the portfolio's smart account and delivered to the recipient in one engine operation; native SOL positions are liquidated too (a small rent reserve stays behind so the account remains rent-exempt). **Ondo tokenized stocks** (`TSLAon`, `SPCXon`, …) redeem through Ondo directly and settle only in the chain's Ondo cash asset — **USDT on BNB Chain (56)**, **USDC on Ethereum (1)**. If the portfolio holds Ondo tokens and the requested settlement asset doesn't match, this endpoint rejects with `400 API_221 UNSUPPORTED_SETTLEMENT_ASSET` before anything is signed. See the [stage-2 notes](./v2-liquidate-all#portfolios-holding-ondo-tokenized-stocks) for market-hours behavior. ### Best-effort empty Holdings whose value is below the tenant swap threshold (or that have no swap route) are **left in the vault** — forcing uneconomical swaps would lose more to gas than they recover. The portfolio is emptied of everything worth swapping, not necessarily down to zero. If nothing clears the threshold, the call returns `400 API_220 NOTHING_TO_LIQUIDATE`. The authorization is valid for **10 minutes** and, like all withdrawals, binds the portfolio, recipient, the enumerated assets + amounts, the nonce, and the expiry into the user's signature. Common error responses: * `400 API_211 INVALID_RECIPIENT` — zero address or a self-transfer to the smart account being debited. * `400 API_215 PORTFOLIO_HAS_NO_VAULT_ON_CHAIN` — portfolio has no smart account on the recipient's chain. * `400 API_219 WITHDRAW_AS_USDC_UNSUPPORTED_CHAIN` — the recipient's chain has no canonical USDC to swap into (the default settlement asset). * `400 API_221 UNSUPPORTED_SETTLEMENT_ASSET` — `settlementAssetId` is not an allowed settlement asset on the chain (configured USDC or USDT for an EVM liquidation, or USDC for a Solana liquidation). * `400 API_220 NOTHING_TO_LIQUIDATE` — no holding on the chain clears the swap threshold. * `401` when `x-api-key` is missing or invalid. * `403` when the API key lacks the `portfolios:withdraw` scope, or the recipient is Solana and the tenant's Solana B2B API access is not enabled. * `404 API_200 PORTFOLIO_NOT_FOUND` — `portfolioId` doesn't exist or belongs to a different tenant. * `500` on unexpected server errors. ```bash cURL theme={null} curl --request POST \ --url 'https://api.glider.fi/v2/portfolios/a1b2c3d4/liquidate-all/signature' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "recipientAccountId": "eip155:1:0x4444444444444444444444444444444444444444" }' ``` ```bash cURL (settle to USDT on BNB) theme={null} curl --request POST \ --url 'https://api.glider.fi/v2/portfolios/a1b2c3d4/liquidate-all/signature' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "recipientAccountId": "eip155:56:0x4444444444444444444444444444444444444444", "settlementAssetId": "eip155:56/erc20:0x55d398326f99059fF775485246999027B3197955" }' ``` ```bash cURL (Solana) theme={null} curl --request POST \ --url 'https://api.glider.fi/v2/portfolios/a1b2c3d4/liquidate-all/signature' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "recipientAccountId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj" }' ``` ```json 200 theme={null} { "success": true, "data": { "authorizationId": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", "expiresAt": "2026-04-17T12:10:00.000Z", "typedData": { "primaryType": "Withdrawal", "domain": { "name": "Glider Withdrawal Authorization", "version": "2", "chainId": 1, "verifyingContract": "0x2222222222222222222222222222222222222222" }, "types": { "Withdrawal": [ { "name": "portfolioId", "type": "string" }, { "name": "recipientAccountId", "type": "string" }, { "name": "assets", "type": "WithdrawalAsset[]" }, { "name": "nonce", "type": "bytes32" }, { "name": "expiresAt", "type": "uint256" }, { "name": "liquidate", "type": "bool" }, { "name": "settlementAssetId", "type": "string" } ], "WithdrawalAsset": [ { "name": "assetId", "type": "string" }, { "name": "amountRaw", "type": "string" } ] }, "message": { "portfolioId": "a1b2c3d4", "recipientAccountId": "eip155:1:0x4444444444444444444444444444444444444444", "assets": [ { "assetId": "eip155:1/erc20:0x6b175474e89094c44da98b954eedeac495271d0f", "amountRaw": "5000000000000000000" }, { "assetId": "eip155:1/erc20:0x514910771af9ca656af840dff83e8264ecf986ca", "amountRaw": "12000000000000000000" } ], "nonce": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", "expiresAt": 1744898400, "liquidate": true, "settlementAssetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" } } } } ``` ```json 200 — Solana (Model B owner) theme={null} { "success": true, "data": { "authorizationId": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", "expiresAt": "2026-04-17T12:10:00.000Z", "authorization": { "kind": "solana-message", "text": "Glider — authorize Solana liquidation to a settlement asset\n\nPortfolio: a1b2c3d4\n…\nDomain: glider:svm-withdraw-liquidate:solana:v1", "message": { "portfolioId": "a1b2c3d4", "recipientAccountId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj", "assets": [ { "assetId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/spl:JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN", "amountRaw": "2500000000" } ], "nonce": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", "expiresAt": 1744898400, "liquidate": true, "settlementAssetId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/spl:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" } } } } ``` ```json 400 — nothing to liquidate theme={null} { "success": false, "error": { "code": "API_220", "message": "No assets above the liquidation threshold (1 USD) on the recipient's chain" } } ``` # List Portfolios Source: https://docs.glider.fi/api-reference/endpoints/v2-list-portfolios GET /v2/portfolios Returns the authenticated tenant's user portfolios with cursor-based pagination. Returns all portfolios owned by the authenticated tenant — identity, portfolio display name, strategy, per-chain smart accounts, and rebalance schedule state. Real-time balances and USD values are served by `GET /v2/portfolios/{portfolioId}/positions`; keep them separate so list polling stays cheap. * Auth: `x-api-key` header (required) * Scope: `portfolios:read` All on-chain identifiers are [CAIP](https://github.com/ChainAgnostic/CAIPs)-shaped. End-user wallets use the chain-agnostic CAIP-10 form `eip155:0:
` (the same EOA works on every EIP-155 chain). Smart accounts are chain-bound (`eip155::
`) because a smart account exists only at that (chain, address) tuple. Raw addresses and numeric chain IDs never appear on the wire. Owners are EVM EOAs today. Results are cursor-paginated in `createdAt` descending order. Pass the `nextCursor` value from the previous response as the `cursor` query parameter to fetch the next page. `nextCursor` is `null` when there are no more results. Archived portfolios are excluded. Filter to a single end-user's portfolios under this tenant. CAIP-10 account identifier. For EVM EOAs, use the chain-agnostic form `eip155:0:0x
`. Mixed-case input is accepted; the address part is normalized to lowercase. Filter to portfolios mirroring a specific strategy (ULID returned by `POST /v2/strategies`). Filter by rebalance schedule status. One of `active`, `paused`. When set, portfolios without a schedule are excluded. Max portfolios per page. Min 1, max 200. Opaque pagination cursor from a previous response. Common error responses: * `400` when a filter value is malformed (invalid CAIP-10, unknown status enum, bad cursor) or `limit` is out of range * `401` when `x-api-key` header is missing or the key is invalid * `403` when the API key lacks the `portfolios:read` scope * `500` on unexpected server errors ```bash cURL theme={null} curl --request GET \ --url 'https://api.glider.fi/v2/portfolios?limit=50' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```bash cURL (filter by user) theme={null} curl --request GET \ --url 'https://api.glider.fi/v2/portfolios?ownerAccountId=eip155:0:0xabcdef0000000000000000000000000000000001&status=active' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/portfolios?limit=50", { headers: { "x-api-key": "gldr_sk_your_api_key" } }, ); const data = await response.json(); // Fetch next page if present if (data.nextCursor) { const next = await fetch( `https://api.glider.fi/v2/portfolios?limit=50&cursor=${encodeURIComponent(data.nextCursor)}`, { headers: { "x-api-key": "gldr_sk_your_api_key" } }, ); } ``` ```json 200 theme={null} { "success": true, "data": { "portfolios": [ { "portfolioId": "a1b2c3d4", "portfolioName": "Alice Portfolio", "ownerAccountId": "eip155:0:0xabcdef0000000000000000000000000000000001", "strategyId": "01JWZEE2MF30KVRMRX53N88VA4", "strategyName": "Conservative Yield", "strategyDescription": "Multi-chain balanced allocation strategy", "smartAccounts": [ { "accountId": "eip155:8453:0xe3a2d1f49aee887e42655b56371d4d76bbf58058" } ], "schedule": { "status": "active", "frequency": "daily", "intervalMs": 86400000, "nextDueAt": "2026-04-16T12:00:00.000Z", "lastRebalanceAt": "2026-04-15T12:00:00.000Z" }, "createdAt": "2026-04-10T08:30:00.000Z" } ] }, "nextCursor": "eyJjIjoiMjAyNi0wNC0xMFQwODozMDowMC4wMDBaIiwiaSI6InBmXzAxSldaRUUyTUYzMEtWUk1SWDUzTjg4VkE0In0" } ``` ```json 400 theme={null} { "success": false, "error": { "code": "API_400", "message": "Request validation failed", "details": [ "ownerAccountId: Must be a valid CAIP-10 account identifier (chainNamespace:chainReference:address)" ] } } ``` # List Scopes Source: https://docs.glider.fi/api-reference/endpoints/v2-list-scopes GET /v2/scopes Returns all API scopes with descriptions and granting tiers. Returns every scope defined in the system. Use this to discover what permissions are available and how they are granted. * Auth: none (public endpoint) * Rate limit: global only Each scope has a **tier** that determines how it can be granted: | Tier | Meaning | | ------------ | ------------------------------------------------ | | `default` | Auto-granted on every new API key | | `standard` | Self-service via dashboard (if tenant is active) | | `restricted` | Admin review and manual grant only | ```json theme={null} { "success": true, "data": { "scopes": [ { "name": "strategies:read", "description": "List and view strategies", "tier": "default" }, { "name": "strategies:write", "description": "Create strategies and publish versions", "tier": "standard" }, { "name": "portfolios:read", "description": "List and view user portfolios", "tier": "default" }, { "name": "portfolios:write", "description": "Pause, resume, trigger rebalances, and update portfolio metadata", "tier": "standard" }, { "name": "enroll:write", "description": "Enroll new users into a strategy (creates smart accounts and a portfolio)", "tier": "standard" } ] } } ``` # List Strategies Source: https://docs.glider.fi/api-reference/endpoints/v2-list-strategies GET /v2/strategies Returns the authenticated tenant's strategies with cursor-based pagination. Returns all strategies belonging to the authenticated tenant. Each item includes the full strategy detail (allocation, schedule, preferences, advertised max APY, visibility, version, and creation timestamp). * Auth: `x-api-key` header (required) * Scope: `strategies:read` Results are paginated using cursor-based (keyset) pagination ordered by `createdAt` descending. Pass the `nextCursor` value from the previous response as the `cursor` query parameter to fetch the next page. `nextCursor` is `null` when there are no more results. Max strategies per page. Min 1, max 200. Opaque pagination cursor from a previous response. Common error responses: * `400` when `limit` is out of range or `cursor` is malformed * `401` when `x-api-key` header is missing or the key is invalid * `403` when the API key lacks the `strategies:read` scope * `500` on unexpected server errors ```bash cURL theme={null} curl --request GET \ --url 'https://api.glider.fi/v2/strategies?limit=50' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.glider.fi/v2/strategies?limit=50", { headers: { "x-api-key": "gldr_sk_your_api_key" }, }); const data = await response.json(); // Fetch next page if present if (data.nextCursor) { const next = await fetch( `https://api.glider.fi/v2/strategies?limit=50&cursor=${encodeURIComponent(data.nextCursor)}`, { headers: { "x-api-key": "gldr_sk_your_api_key" } }, ); } ``` ```json 200 theme={null} { "success": true, "data": { "strategies": [ { "strategyId": "01JWZEE2MF30KVRMRX53N88VA4", "name": "Balanced Growth", "description": "Multi-chain balanced allocation strategy", "maxApy": "10.00", "allocation": { "assets": [ { "assetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "weight": "60" }, { "assetId": "eip155:1/erc20:0xdac17f958d2ee523a2206206994597c13d831ec7", "weight": "40" } ] }, "schedule": { "type": "interval", "frequency": "daily" }, "preferences": { "swap": { "slippageBps": 300, "priceImpactBps": null, "thresholdUsd": "5.00" } }, "isPublic": false, "version": 3, "createdAt": "2026-04-17T12:00:00.000Z" } ] }, "nextCursor": "eyJjIjoiMjAyNi0wMy0zMVQxMjowMDowMC4wMDBaIiwiaSI6IjAxSldaRUUyTUYzMEtWUk1SWDUzTjg4VkE0In0" } ``` ```json 400 theme={null} { "success": false, "error": { "code": "API_400", "message": "Invalid cursor" } } ``` # List strategy versions Source: https://docs.glider.fi/api-reference/endpoints/v2-list-strategy-versions GET /v2/strategies/{strategyId}/versions Returns the strategy's allocation version history, newest first. Returns the strategy's allocation version history. Versions are immutable — once published, a version's allocation never changes. Use this endpoint to inspect history and identify the active version. * Auth: `x-api-key` header (required) * Required scope: `strategies:read` ### Response Each entry contains: * `version` — sequential integer, server-assigned. `1` at create; each publish increments by 1. * `allocation` — assets and weights for that version. * `changeLog` — optional note from the publish call; `null` when not provided. * `isHead` — `true` for the active version. Exactly one per strategy. * `createdAt` — ISO 8601 timestamp. ### Pagination Cursor-paginated, newest first. Pass the previous response's `nextCursor` verbatim as the next request's `?cursor=`. Default `limit=50`, max `200`. ### Cross-tenant access Public strategies listed by [List Discovery Strategies](/api-reference/endpoints/v2-discovery-strategies) are also readable. Anything else returns `404` — strategies you don't own are not exposed. ```bash cURL — first page theme={null} curl --request GET \ --url 'https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4/versions' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```bash cURL — next page theme={null} curl --request GET \ --url 'https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4/versions?cursor=' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4/versions", { headers: { "x-api-key": "gldr_sk_your_api_key" } }, ); const { data, nextCursor } = await response.json(); const head = data.versions.find((v) => v.isHead); ``` ```json 200 theme={null} { "success": true, "data": { "versions": [ { "version": 3, "allocation": { "assets": [ { "assetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "weight": "100" } ] }, "changeLog": "Switched to USDC-only", "isHead": true, "createdAt": "2026-04-29T12:00:00.000Z" }, { "version": 2, "allocation": { "assets": [ { "assetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "weight": "50" }, { "assetId": "eip155:1/erc20:0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "weight": "50" } ] }, "changeLog": null, "isHead": false, "createdAt": "2026-03-01T12:00:00.000Z" } ] }, "nextCursor": null } ``` # Patch Portfolio Source: https://docs.glider.fi/api-reference/endpoints/v2-patch-portfolio PATCH /v2/portfolios/{portfolioId} Updates the authenticated tenant's portfolio metadata by ID. Updates metadata for a portfolio owned by the authenticated tenant. Today only `portfolioName` is mutable. * Auth: `x-api-key` header (required) * Scope: `portfolios:write` The body uses JSON Merge Patch semantics: omit a field to preserve it, send `null` to clear it, or send a string to set it. Empty body `{}` is a 200 no-op. Unknown keys return `400`. `portfolioName` is trimmed server-side. Names may be up to 64 characters and may include Unicode letters, digits, spaces, and `- _ . ' : ( ) & /`. Portfolio identifier returned by `POST /v2/enroll` or `GET /v2/portfolios`. Common error responses: * `400` when the body is invalid * `401` when `x-api-key` header is missing or the key is invalid * `403` when the API key lacks the `portfolios:write` scope * `404` when the portfolio does not exist or does not belong to the tenant * `500` on unexpected server errors ```bash cURL theme={null} curl --request PATCH \ --url 'https://api.glider.fi/v2/portfolios/a1b2c3d4' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "portfolioName": "Alice Conservative Yield" }' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/portfolios/a1b2c3d4", { method: "PATCH", headers: { "x-api-key": "gldr_sk_your_api_key", "Content-Type": "application/json", }, body: JSON.stringify({ portfolioName: "Alice Conservative Yield" }), }, ); const data = await response.json(); ``` ```json 200 theme={null} { "success": true, "data": { "portfolioId": "a1b2c3d4", "portfolioName": "Alice Conservative Yield", "ownerAccountId": "eip155:0:0xabcdef0000000000000000000000000000000001", "strategyId": "01JWZEE2MF30KVRMRX53N88VA4", "strategyName": "Conservative Yield", "strategyDescription": "Multi-chain balanced allocation strategy", "smartAccounts": [ { "accountId": "eip155:8453:0xe3a2d1f49aee887e42655b56371d4d76bbf58058" } ], "schedule": { "status": "active", "frequency": "daily", "intervalMs": 86400000, "nextDueAt": "2026-04-16T12:00:00.000Z", "lastRebalanceAt": "2026-04-15T12:00:00.000Z" }, "createdAt": "2026-04-10T08:30:00.000Z", "strategyVersion": 4, "updatedAt": "2026-04-15T12:00:00.000Z" } } ``` ```json 400 theme={null} { "success": false, "error": { "code": "API_400", "message": "Request validation failed" } } ``` # Patch strategy Source: https://docs.glider.fi/api-reference/endpoints/v2-patch-strategy PATCH /v2/strategies/{strategyId} Patches the strategy's mutable display + discovery metadata. Patches the strategy's display metadata — name, description, visibility. Doesn't change the allocation, schedule, or preferences (each has its own endpoint). * Auth: `x-api-key` header (required) * Required scope: `strategies:write` ### Mutable fields | Field | Type | Nullable | Notes | | ------------- | -------------- | -------- | -------------------------------- | | `name` | string (1–256) | no | Display name. Cannot be cleared. | | `description` | string (≤2000) | yes | Send `null` to clear. | | `isPublic` | boolean | no | Discoverability flag. | Only the three metadata fields above are accepted here. Allocation, schedule, preferences, and fees each use their dedicated route. `maxApy` is managed by Glider and is read-only through the API. ### Patch semantics JSON Merge Patch (RFC 7396): * **Omit a field** — preserve the current value. * **Send `null`** — clear (only on nullable fields above). * **Send a value** — set. Empty body `{}` is a 200 no-op. Body is strict; unknown keys return `400`. ### Where to make other changes * Allocation: [`POST /v2/strategies/{strategyId}/versions`](/api-reference/endpoints/v2-publish-strategy-version) * Schedule: [`PUT /v2/strategies/{strategyId}/schedule`](/api-reference/endpoints/v2-set-strategy-schedule) * Preferences: [`PATCH /v2/strategies/{strategyId}/preferences`](/api-reference/endpoints/v2-patch-strategy-preferences) ### Cross-tenant access Returns `404` — strategies you don't own are not exposed. ### Response Returns the full `Strategy` shape (same as `GET /v2/strategies/{strategyId}`). ```bash cURL — rename and toggle visibility theme={null} curl --request PATCH \ --url 'https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'content-type: application/json' \ --data '{ "name": "Conservative Yield v2", "isPublic": true }' ``` ```bash cURL — clear the description theme={null} curl --request PATCH \ --url 'https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'content-type: application/json' \ --data '{ "description": null }' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4", { method: "PATCH", headers: { "x-api-key": "gldr_sk_your_api_key", "content-type": "application/json", }, body: JSON.stringify({ name: "Conservative Yield v2" }), }, ); const { data } = await response.json(); ``` ```json 200 theme={null} { "success": true, "data": { "strategyId": "01JWZEE2MF30KVRMRX53N88VA4", "name": "Conservative Yield v2", "description": "Multi-chain balanced allocation strategy", "allocation": { "assets": [ { "assetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "weight": "100" } ] }, "schedule": { "type": "interval", "frequency": "daily" }, "preferences": { "swap": { "slippageBps": null, "priceImpactBps": null, "thresholdUsd": null } }, "isPublic": true, "version": 3, "createdAt": "2026-04-01T12:00:00.000Z" } } ``` # Patch strategy fees Source: https://docs.glider.fi/api-reference/endpoints/v2-patch-strategy-fees PATCH /v2/strategies/{strategyId}/fees Patches the strategy's integrator-fee override. Patches the strategy's integrator-fee override. `swapBps` is the basis-point fee charged on the output side of every swap for this specific strategy. Range: `30` (0.3%)–`300` (3%). * Auth: `x-api-key` header (required) * Required scope: `fees:write` Sending `null` clears the per-strategy override. The tenant-wide default lives at [`PATCH /v2/tenant/fees`](/api-reference/endpoints/v2-patch-tenant-fees). Cross-tenant access returns 404. ### Patch semantics * **Omitted field** — preserve the current override value * **Explicit `null`** — clear the per-strategy override * **Explicit value** — set the override Empty body `{}` is a 200 no-op. ```bash cURL — set the override theme={null} curl --request PATCH \ --url 'https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4/fees' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'content-type: application/json' \ --data '{ "swapBps": 45 }' ``` ```bash cURL — clear the override theme={null} curl --request PATCH \ --url 'https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4/fees' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'content-type: application/json' \ --data '{ "swapBps": null }' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4/fees", { method: "PATCH", headers: { "x-api-key": "gldr_sk_your_api_key", "content-type": "application/json", }, body: JSON.stringify({ swapBps: 45 }), }, ); const data = await response.json(); ``` ```json After patch theme={null} { "success": true, "data": { "swapBps": 45 } } ``` # Patch strategy preferences Source: https://docs.glider.fi/api-reference/endpoints/v2-patch-strategy-preferences PATCH /v2/strategies/{strategyId}/preferences Patches the strategy's stored preference overrides. Patches the strategy's stored preference overrides. Swap settings live under the `swap` namespace: `slippageBps`, `priceImpactBps`, and `thresholdUsd`. * Auth: `x-api-key` header (required) * Required scope: `strategies:write` ### Patch semantics Patches are applied per inner field within `swap`: * **Omitted field** — preserve the current value * **Explicit `null`** — clear the field * **Explicit value** — set the field Empty body `{}` and `{ "swap": {} }` are both 200 no-ops. ### Clearing all preferences To remove every per-strategy override and fall back to the tenant defaults, send a PATCH with all swap fields set to `null`: ```jsonc theme={null} { "swap": { "slippageBps": null, "priceImpactBps": null, "thresholdUsd": null } } ``` The strategy's schedule is unaffected. There is no dedicated `DELETE` endpoint. ### Schedule is rejected This endpoint patches **preferences only**. To change the schedule, call `PUT /v2/strategies/{strategyId}/schedule`. Sending a `schedule` field here returns `400`. ### Cross-tenant access Returns `404` — strategies you don't own are not exposed. ```bash cURL — set slippage and threshold theme={null} curl --request PATCH \ --url 'https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4/preferences' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'content-type: application/json' \ --data '{ "swap": { "slippageBps": 300, "thresholdUsd": "5.00" } }' ``` ```bash cURL — clear a single field theme={null} curl --request PATCH \ --url 'https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4/preferences' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'content-type: application/json' \ --data '{ "swap": { "thresholdUsd": null } }' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4/preferences", { method: "PATCH", headers: { "x-api-key": "gldr_sk_your_api_key", "content-type": "application/json", }, body: JSON.stringify({ swap: { slippageBps: 300 } }), }, ); const data = await response.json(); ``` ```json After patch theme={null} { "success": true, "data": { "swap": { "slippageBps": 300, "priceImpactBps": null, "thresholdUsd": "5.00" } } } ``` # Patch tenant fees Source: https://docs.glider.fi/api-reference/endpoints/v2-patch-tenant-fees PATCH /v2/tenant/fees Patches the tenant's integrator-fee defaults. Patches the tenant's stored integrator swap-fee default. `swapBps` is the basis-point fee charged on the output side of every swap. Range: `30` (0.3%)–`300` (3%). * Auth: `x-api-key` header (required) * Required scope: `fees:write` Per-strategy overrides go to [`PATCH /v2/strategies/{strategyId}/fees`](/api-reference/endpoints/v2-patch-strategy-fees). ### Patch semantics * **Omitted field** — preserve the current value * **Explicit `null`** — clear the fee (no integrator fee applied) * **Explicit value** — set the fee Empty body `{}` is a 200 no-op. ```bash cURL — set the fee theme={null} curl --request PATCH \ --url 'https://api.glider.fi/v2/tenant/fees' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'content-type: application/json' \ --data '{ "swapBps": 50 }' ``` ```bash cURL — clear the fee theme={null} curl --request PATCH \ --url 'https://api.glider.fi/v2/tenant/fees' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'content-type: application/json' \ --data '{ "swapBps": null }' ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.glider.fi/v2/tenant/fees", { method: "PATCH", headers: { "x-api-key": "gldr_sk_your_api_key", "content-type": "application/json", }, body: JSON.stringify({ swapBps: 50 }), }); const data = await response.json(); ``` ```json After patch theme={null} { "success": true, "data": { "swapBps": 50 } } ``` # Patch tenant preferences Source: https://docs.glider.fi/api-reference/endpoints/v2-patch-tenant-preferences PATCH /v2/tenant/preferences Patches the tenant's stored preference defaults. Patches the tenant's stored preference defaults. Swap settings live under the `swap` namespace: `slippageBps`, `priceImpactBps`, and `thresholdUsd` (minimum swap size in USD). * Auth: `x-api-key` header (required) * Required scope: `tenant:write` Schedule is per-strategy — set it via [`PUT /v2/strategies/{strategyId}/schedule`](/api-reference/endpoints/v2-set-strategy-schedule). ### Patch semantics Patches are applied per inner field within `swap`: * **Omitted field** — preserve the current value * **Explicit `null`** — clear the field (revert to "not configured") * **Explicit value** — set the field Empty body `{}` and `{ "swap": {} }` are both 200 no-ops. Sending `{ "swap": { "slippageBps": null } }` clears `slippageBps` while leaving `priceImpactBps` and `thresholdUsd` untouched. ```bash cURL — set slippage and threshold theme={null} curl --request PATCH \ --url 'https://api.glider.fi/v2/tenant/preferences' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'content-type: application/json' \ --data '{ "swap": { "slippageBps": 300, "thresholdUsd": "5.00" } }' ``` ```bash cURL — clear a single field theme={null} curl --request PATCH \ --url 'https://api.glider.fi/v2/tenant/preferences' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'content-type: application/json' \ --data '{ "swap": { "thresholdUsd": null } }' ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.glider.fi/v2/tenant/preferences", { method: "PATCH", headers: { "x-api-key": "gldr_sk_your_api_key", "content-type": "application/json", }, body: JSON.stringify({ swap: { slippageBps: 300 } }), }); const data = await response.json(); ``` ```json After patch theme={null} { "success": true, "data": { "swap": { "slippageBps": 300, "priceImpactBps": null, "thresholdUsd": "5.00" } } } ``` # Publish strategy version Source: https://docs.glider.fi/api-reference/endpoints/v2-publish-strategy-version POST /v2/strategies/{strategyId}/versions Publishes a new allocation version and makes it the active one. Publishes a new allocation version. The new version becomes the strategy's active version. Previous versions stay readable via [`GET /v2/strategies/{strategyId}/versions`](/api-reference/endpoints/v2-list-strategy-versions). * Auth: `x-api-key` header (required) * Required scope: `strategies:write` **Affects every enrolled portfolio.** The new allocation is what every portfolio enrolled in this strategy will rebalance to on its next scheduled run. Pause portfolios via [`POST /v2/portfolios/{portfolioId}/stop`](/api-reference/endpoints/v2-stop-portfolio) first if you need a staged rollout. Don't grant `strategies:write` to keys exposed to end-user surfaces. ### Request body * `allocation` — required. Same validation as [`POST /v2/strategies`](/api-reference/endpoints/v2-create-strategy): weights sum to 100, CAIP-19 asset IDs, ≤50 assets. * `changeLog` — optional, ≤500 chars. Free-form note shown in the version history. Body is strict; unknown keys (including a client-supplied `version`) return `400`. Version numbers are server-assigned. ### Concurrent publishes If two publishes for the same strategy arrive at the same time, each gets a distinct sequential version number and the last one becomes the active version. Serialize on your side if you need strict ordering. ### Cross-tenant access Returns `404` — strategies you don't own are not exposed. ```bash cURL theme={null} curl --request POST \ --url 'https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4/versions' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'content-type: application/json' \ --data '{ "allocation": { "assets": [ { "assetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "weight": "70" }, { "assetId": "eip155:1/erc20:0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "weight": "30" } ] }, "changeLog": "Increased USDC weight from 60% to 70%" }' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4/versions", { method: "POST", headers: { "x-api-key": "gldr_sk_your_api_key", "content-type": "application/json", }, body: JSON.stringify({ allocation: { assets: [ { assetId: "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", weight: "70" }, { assetId: "eip155:1/erc20:0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", weight: "30" }, ], }, changeLog: "Increased USDC weight from 60% to 70%", }), }, ); const { data } = await response.json(); console.log(`Published v${data.version}`); ``` ```json 201 theme={null} { "success": true, "data": { "version": 4, "allocation": { "assets": [ { "assetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "weight": "70" }, { "assetId": "eip155:1/erc20:0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "weight": "30" } ] }, "changeLog": "Increased USDC weight from 60% to 70%", "isHead": true, "createdAt": "2026-04-29T12:00:00.000Z" } } ``` # Set strategy schedule Source: https://docs.glider.fi/api-reference/endpoints/v2-set-strategy-schedule PUT /v2/strategies/{strategyId}/schedule Change the strategy's rebalance cadence — fans out to every enrolled portfolio. Sets the strategy's rebalance schedule. The new cadence takes effect on the next rebalance cycle for every enrolled portfolio of this strategy, including paused portfolios — they will run on the new cadence the moment they resume. * Auth: `x-api-key` header (required) * Required scope: `strategies:write` **Fleet-wide side effect.** This call updates the rebalance cadence for every enrolled portfolio of this strategy. Flipping `weekly → hourly` on a 1,000-portfolio fleet will cause all of those portfolios to rebalance more frequently from the next cycle onward. **Do not grant `strategies:write` to API keys shipped to client-side or end-user-facing applications (mobile apps, browsers, embedded SDKs)** — keep that scope on server-side operator/dashboard keys only. ### Cross-tenant access Returns `404` — no existence leak. ```bash cURL theme={null} curl --request PUT \ --url 'https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4/schedule' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'content-type: application/json' \ --data '{ "type": "interval", "frequency": "hourly" }' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/strategies/01JWZEE2MF30KVRMRX53N88VA4/schedule", { method: "PUT", headers: { "x-api-key": "gldr_sk_your_api_key", "content-type": "application/json", }, body: JSON.stringify({ type: "interval", frequency: "hourly" }), }, ); const data = await response.json(); ``` ```json theme={null} { "success": true, "data": { "type": "interval", "frequency": "hourly" } } ``` # Start Portfolio Source: https://docs.glider.fi/api-reference/endpoints/v2-start-portfolio POST /v2/portfolios/{portfolioId}/start Resumes automation on a paused portfolio. Resumes automation for the portfolio. Idempotent — calling `start` on an already-active portfolio returns the current detail without side effects. On transition, `nextDueAt` resets to the current time so the next rebalance happens promptly rather than honouring the stale due time captured when automation was stopped. * Auth: `x-api-key` header (required) * Scope: `portfolios:write` Concurrent `start` and `stop` calls on the same portfolio are last-writer wins. Both requests return `200` with the state each observed, so a client that issued the losing transition may briefly see stale status — re-fetch the portfolio detail if you need the post-race state. Returns `404` for portfolios your API key does not have access to — same response shape as a portfolio that doesn't exist. Portfolio identifier returned by `POST /v2/enroll` or `GET /v2/portfolios`. Common error responses: * `400` when the portfolio has no rebalance schedule to transition * `401` when `x-api-key` header is missing or the key is invalid * `403` when the API key lacks the `portfolios:write` scope * `404` when the portfolio does not exist or does not belong to the tenant * `500` on unexpected server errors ```bash cURL theme={null} curl --request POST \ --url 'https://api.glider.fi/v2/portfolios/a1b2c3d4/start' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/portfolios/a1b2c3d4/start", { method: "POST", headers: { "x-api-key": "gldr_sk_your_api_key" }, }, ); const data = await response.json(); ``` ```json 200 theme={null} { "success": true, "data": { "portfolioId": "a1b2c3d4", "portfolioName": "Alice Portfolio", "ownerAccountId": "eip155:0:0xabcdef0000000000000000000000000000000001", "strategyId": "01JWZEE2MF30KVRMRX53N88VA4", "strategyName": "Conservative Yield", "strategyDescription": "Multi-chain balanced allocation strategy", "smartAccounts": [ { "accountId": "eip155:8453:0xe3a2d1f49aee887e42655b56371d4d76bbf58058" } ], "schedule": { "status": "active", "frequency": "daily", "intervalMs": 86400000, "nextDueAt": "2026-04-16T12:00:00.000Z", "lastRebalanceAt": "2026-04-15T12:00:00.000Z" }, "createdAt": "2026-04-10T08:30:00.000Z", "strategyVersion": 4, "updatedAt": "2026-04-16T12:00:00.000Z" } } ``` ```json 400 theme={null} { "success": false, "error": { "code": "API_400", "message": "Portfolio a1b2c3d4 has no rebalance schedule" } } ``` ```json 404 theme={null} { "success": false, "error": { "code": "API_006", "message": "Portfolio with ID a1b2c3d4 not found" } } ``` # Stop Portfolio Source: https://docs.glider.fi/api-reference/endpoints/v2-stop-portfolio POST /v2/portfolios/{portfolioId}/stop Pauses automation on an active portfolio. Pauses automation for the portfolio. Idempotent — calling `stop` on an already-paused portfolio returns the current detail without side effects. Rebalance history, on-chain balances, and smart account positions are preserved while paused; call `/start` to resume automation. * Auth: `x-api-key` header (required) * Scope: `portfolios:write` Concurrent `start` and `stop` calls on the same portfolio are last-writer wins. Both requests return `200` with the state each observed, so a client that issued the losing transition may briefly see stale status — re-fetch the portfolio detail if you need the post-race state. Returns `404` for portfolios your API key does not have access to — same response shape as a portfolio that doesn't exist. Portfolio identifier returned by `POST /v2/enroll` or `GET /v2/portfolios`. Common error responses: * `400` when the portfolio has no rebalance schedule to transition * `401` when `x-api-key` header is missing or the key is invalid * `403` when the API key lacks the `portfolios:write` scope * `404` when the portfolio does not exist or does not belong to the tenant * `500` on unexpected server errors ```bash cURL theme={null} curl --request POST \ --url 'https://api.glider.fi/v2/portfolios/a1b2c3d4/stop' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/portfolios/a1b2c3d4/stop", { method: "POST", headers: { "x-api-key": "gldr_sk_your_api_key" }, }, ); const data = await response.json(); ``` ```json 200 theme={null} { "success": true, "data": { "portfolioId": "a1b2c3d4", "portfolioName": "Alice Portfolio", "ownerAccountId": "eip155:0:0xabcdef0000000000000000000000000000000001", "strategyId": "01JWZEE2MF30KVRMRX53N88VA4", "strategyName": "Conservative Yield", "strategyDescription": "Multi-chain balanced allocation strategy", "smartAccounts": [ { "accountId": "eip155:8453:0xe3a2d1f49aee887e42655b56371d4d76bbf58058" } ], "schedule": { "status": "paused", "frequency": "daily", "intervalMs": 86400000, "nextDueAt": "2026-04-16T12:00:00.000Z", "lastRebalanceAt": "2026-04-15T12:00:00.000Z" }, "createdAt": "2026-04-10T08:30:00.000Z", "strategyVersion": 4, "updatedAt": "2026-04-16T12:00:00.000Z" } } ``` ```json 400 theme={null} { "success": false, "error": { "code": "API_400", "message": "Portfolio a1b2c3d4 has no rebalance schedule" } } ``` ```json 404 theme={null} { "success": false, "error": { "code": "API_006", "message": "Portfolio with ID a1b2c3d4 not found" } } ``` # Trigger Manual Rebalance Source: https://docs.glider.fi/api-reference/endpoints/v2-trigger-rebalance POST /v2/portfolios/{portfolioId}/rebalance Dispatches a one-off rebalance for a portfolio your API key has access to. Dispatches a one-off rebalance run outside the portfolio's schedule. Typical use cases: an end-user wants to re-target immediately to capture or hedge market volatility, or your UI exposes a "rebalance now" control. The endpoint returns `202` immediately with an `operationId`; poll `GET /v2/portfolios/{portfolioId}/operations/{operationId}` until the operation completes (`state`: `completed`, `failed`, or `cancelled`). To decide when (or whether) to surface a manual trigger, read `nextDueAt` and `lastRebalanceAt` from `GET /v2/portfolios/{portfolioId}` first. * Auth: `x-api-key` header (required) * Scope: `portfolios:write` If a rebalance is still in progress for the portfolio, the response returns the same `operationId` and original `submittedAt` from the in-flight run. Treat `operationId` as an opaque token. Each portfolio has a cooldown between manual triggers. Calls that arrive too soon after the previous rebalance return `429` with a `Retry-After` header (in seconds) indicating when to retry. Returns `404` for portfolios your API key does not have access to — same response shape as a portfolio that doesn't exist. Portfolio identifier returned by `POST /v2/enroll` or `GET /v2/portfolios`. Common error responses: * `401` when `x-api-key` header is missing or the key is invalid * `403` when the API key lacks the `portfolios:write` scope * `404` when the portfolio does not exist or your API key cannot access it * `429` when triggered too soon after the previous rebalance — see the `Retry-After` header * `500` on unexpected server errors ```bash cURL theme={null} curl --request POST \ --url 'https://api.glider.fi/v2/portfolios/a1b2c3d4/rebalance' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/portfolios/a1b2c3d4/rebalance", { method: "POST", headers: { "x-api-key": "gldr_sk_your_api_key" }, }, ); const { data } = await response.json(); const { operationId } = data; ``` ```json 202 theme={null} { "success": true, "data": { "operationId": "rebalance:a1b2c3d4:a1b2c3d4%3Amanual%3Areq_01JWZEE2MF30KVRMRX53N88VA4", "submittedAt": "2026-04-17T12:00:00.000Z" } } ``` ```json 404 theme={null} { "success": false, "error": { "code": "API_006", "message": "Portfolio with ID a1b2c3d4 not found" } } ``` ```json 429 theme={null} { "success": false, "error": { "code": "API_004", "message": "Rebalance for this portfolio finished too recently; see the Retry-After header" } } ``` # Validate Strategy Source: https://docs.glider.fi/api-reference/endpoints/v2-validate-strategy POST /v2/strategies/validate Runs the same validation gates as Create Strategy without persisting anything. A side-effect-free dry-run of `POST /v2/strategies`. Useful for pre-flight checks before submitting a create request — for example, to enable or disable a submit button, surface inline form errors, or preview which assets in an allocation would be rejected. The endpoint accepts the **same request body** as Create Strategy and runs the **same validation gates** in the same order: 1. **Asset validation** — every `assetId` must be recognized and not blocked. 2. **Structural validation** — CAIP-19 parsing, weight sum (must total 100\), no duplicate assets, and allocation shape. A 200 response is a faithful predictor of a Create Strategy success at the moment the call is made. Asset availability can change between the validate and create calls, so a passing dry-run is not a guarantee — clients should still handle a 400 from the actual create. * Auth: `x-api-key` header (required) * Scope: `strategies:write` Common error responses: * `400` when the asset gate or allocation validation fails * `401` when `x-api-key` header is missing or the key is invalid * `403` when the API key lacks the `strategies:write` scope * `500` on unexpected server errors ```bash cURL theme={null} curl --request POST \ --url 'https://api.glider.fi/v2/strategies/validate' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "name": "Balanced Growth", "description": "Multi-chain balanced allocation strategy", "allocation": { "assets": [ { "assetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "weight": "60" }, { "assetId": "eip155:1/erc20:0xdac17f958d2ee523a2206206994597c13d831ec7", "weight": "40" } ] }, "schedule": { "type": "interval", "frequency": "daily" }, "preferences": { "swap": { "slippageBps": 300, "priceImpactBps": 300, "thresholdUsd": "5.00" } } }' ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.glider.fi/v2/strategies/validate", { method: "POST", headers: { "x-api-key": "gldr_sk_your_api_key", "Content-Type": "application/json", }, body: JSON.stringify({ name: "Balanced Growth", allocation: { assets: [ { assetId: "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", weight: "60" }, { assetId: "eip155:1/erc20:0xdac17f958d2ee523a2206206994597c13d831ec7", weight: "40" }, ], }, schedule: { type: "interval", frequency: "daily" }, preferences: { swap: { slippageBps: 300, thresholdUsd: "5.00" } }, }), }); ``` ```json 200 theme={null} { "success": true, "data": { "valid": true } } ``` ```json 400 Asset blocklisted theme={null} { "success": false, "error": { "code": "API_400", "message": "Asset(s) are blocklisted and cannot be used in a strategy: eip155:1/erc20:0xaaa", "details": ["Asset(s) are blocklisted and cannot be used in a strategy: eip155:1/erc20:0xaaa"] } } ``` ```json 400 Asset not recognized theme={null} { "success": false, "error": { "code": "API_400", "message": "Asset(s) are not recognized as valid tokens: eip155:1/erc20:0xdead", "details": ["Asset(s) are not recognized as valid tokens: eip155:1/erc20:0xdead"] } } ``` ```json 400 Allocation weights theme={null} { "success": false, "error": { "code": "API_400", "message": "Strategy validation failed: Allocation weights must sum to 100", "details": ["Allocation weights must sum to 100"] } } ``` # Whoami Source: https://docs.glider.fi/api-reference/endpoints/v2-whoami GET /v2/whoami Returns the identity and permissions of the current API key. Validates the API key and returns the tenant identity, granted scopes, and key metadata. Use this to verify your key works and see which scopes are granted. * Auth: `x-api-key` header (required) * Rate limit: global only Call `/v2/scopes` to see all available scopes (with tiers), then `/v2/whoami` to see what your key has. The gap tells you what to request. Common error responses: * `401` when `x-api-key` header is missing or the key is invalid * `403` when using a legacy static key (no tenant identity) ```bash cURL theme={null} curl --request GET \ --url 'https://api.glider.fi/v2/whoami' \ --header 'x-api-key: gldr_sk_your_api_key' ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.glider.fi/v2/whoami", { headers: { "x-api-key": "gldr_sk_your_api_key" }, }); const data = await response.json(); ``` ```json theme={null} { "success": true, "data": { "tenantName": "Integrator", "tenantEmail": "dev@integrator.example", "apiKeyId": "key_01JWZEE2MF30KVRMRX53N88VA4", "scopes": [ "strategies:read", "strategies:write", "portfolios:read", "enroll:write" ] } } ``` # Submit Signed Withdrawal (Stage 2) Source: https://docs.glider.fi/api-reference/endpoints/v2-withdraw POST /v2/portfolios/{portfolioId}/withdraw Stage 2 of the two-stage withdrawal flow — submits the user-signed authorization. Accepted operations dispatch async. Stage 2 of the two-stage withdrawal flow. Accepts the `message` object returned by stage 1 verbatim, plus the user's `signature` over the authorization. Verifies the signature against the portfolio owner, re-checks live onchain balances, and dispatches the transfer onchain. `body.message` is the structured message from stage 1 — `typedData.message` for EVM portfolios, `authorization.message` for Solana portfolios (the shape is identical), including the `liquidate` flag. Echo it back **verbatim**: the signature was computed over these exact bytes. `body.signature` encoding depends on how the owner signed: * **EVM** (and Solana **Model A**, EVM-rooted): `0x…` hex (EIP-712 / EIP-191 ECDSA; EVM also accepts ERC-1271 smart-contract-wallet signatures). * **Solana Model B** (Solana-rooted): base58 ed25519. For Solana, Glider's pooled agent executes the transfer out of the Swig sub-account and the paymaster sponsors the fee; the verified owner signature is the authorization. See [stage 1 → Solana withdrawals](./v2-withdraw-signature#solana-withdrawals). * Auth: `x-api-key` header (required) * Scope: `portfolios:withdraw` **Recipient:** `recipientAccountId` may be any address the owner signs for (e.g. a user's smart wallet), on both EVM and Solana — the recipient is bound into the owner-signed authorization, so any payout is explicitly authorized by the owner. A self-transfer to the smart account / Swig sub-account being debited is always rejected (`400 API_211 INVALID_RECIPIENT`). ### Idempotency Keyed on `nonce` (scoped to your API key). Retries with the **same** `message` + `signature` replay the cached 202 response. Retries with the same `nonce` but a **different** body return `409 IDEMPOTENCY_KEY_CONFLICT` — fix the client, don't retry. Two parallel requests with the same body: one proceeds, the other sees `409 IDEMPOTENCY_IN_PROGRESS` — retry the identical body after 2–3 seconds. If stage 2 fails after accepting the request (e.g., a transient signature verification error, or balance drops below the requested amount between stages), the idempotency lock is released so the integrator can re-submit the same message + signature once the transient issue resolves. ### Liquidation When the signed `message.liquidate` is `true`, the authorized assets are swapped to the signed `message.settlementAssetId` settlement asset (USDC by default; USDT also allowed on EVM) and delivered to the recipient instead of being transferred as-is. The assets, settlement asset, and recipient must all reference the same chain; this flow does not perform cross-chain delivery or bridging. On Solana the swaps and the final delivery run as one engine operation inside the portfolio's smart account. The returned `operationId` tracks the swap. Both `liquidate` and `settlementAssetId` are part of the signed message and cannot be changed here. A `400 API_219 WITHDRAW_AS_USDC_UNSUPPORTED_CHAIN` is returned when the default USDC settlement asset is unavailable on the chain; a `400 API_221 UNSUPPORTED_SETTLEMENT_ASSET` is returned when `settlementAssetId` is not an allowed settlement asset on the chain. ### Polling for onchain status The response returns `operationId`. Poll [`GET /v2/portfolios/{portfolioId}/operations/{operationId}`](./v2-get-operation) until the operation reaches a terminal (`completed` / `failed` / `cancelled`) state. ### Common error responses * `400 API_216 WITHDRAWAL_AUTHORIZATION_EXPIRED` — signed authorization's `expiresAt` is in the past. Restart the flow via stage 1. * `400 API_217 INVALID_WITHDRAWAL_SIGNATURE` — signature does not recover to the portfolio owner. Almost always a client bug. * `400 API_210 INSUFFICIENT_BALANCE` — balance dropped below `amountRaw` between stage 1 and stage 2. Restart stage 1 to issue a fresh authorization. * `400 API_214 WITHDRAWAL_PORTFOLIO_MISMATCH` — `message.portfolioId` doesn't match the URL path. * `400 API_213 WITHDRAWAL_CHAIN_MISMATCH` — at least one asset is on a different chain than `recipientAccountId`. * `400 API_215 PORTFOLIO_HAS_NO_VAULT_ON_CHAIN` — portfolio has no smart account deployed on the recipient's chain. * `400 API_219 WITHDRAW_AS_USDC_UNSUPPORTED_CHAIN` — `message.liquidate` is `true` (with the default USDC settlement asset) but the recipient's chain has no canonical USDC. * `400 API_221 UNSUPPORTED_SETTLEMENT_ASSET` — `message.settlementAssetId` is not an allowed settlement asset on the recipient's chain (configured USDC or USDT for an EVM withdrawal, or USDC for a Solana withdrawal). * `400 API_212 DUPLICATE_WITHDRAW_ASSET` — two or more assets in `assets` share the same `assetId`. * `400 API_218 UNSUPPORTED_WITHDRAW_ASSET` — the requested asset cannot use the direct-transfer withdrawal path. For a redemption-required yield share, redeem through its registered yield source and then withdraw the underlying asset. * `400 API_211 INVALID_RECIPIENT` — zero address, or a self-transfer to the smart account / Swig sub-account being debited. * `403 API_104 PERMISSION_DENIED` — Solana withdrawal but the `svm_b2b_api` feature is not enabled for the tenant. * `404 API_200 PORTFOLIO_NOT_FOUND` — `portfolioId` doesn't exist or your API key cannot access it (can surface at stage 2 if the portfolio was archived/reassigned between stages). * `409 API_007 IDEMPOTENCY_IN_PROGRESS` — another request with the same nonce is still running. Retry the identical body. * `409 API_008 IDEMPOTENCY_KEY_CONFLICT` — nonce reused with a different body. Don't retry; restart stage 1. * `503 API_506 SIGNATURE_VERIFIER_UNAVAILABLE` — signature verification is temporarily unavailable. Safe to retry; the idempotency lock has been released. ```bash cURL theme={null} curl --request POST \ --url 'https://api.glider.fi/v2/portfolios/a1b2c3d4/withdraw' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "message": { "portfolioId": "a1b2c3d4", "recipientAccountId": "eip155:1:0x4444444444444444444444444444444444444444", "assets": [ { "assetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "amountRaw": "1000500000" } ], "nonce": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", "expiresAt": 1744898400 }, "signature": "0xabc..." }' ``` ```javascript JavaScript theme={null} // Typical flow: stage 1 → user signs with wagmi/viem → stage 2. import { useSignTypedData } from "wagmi"; const stage1 = await fetch(`${API}/v2/portfolios/${portfolioId}/withdraw/signature`, { method: "POST", headers: { "x-api-key": KEY, "Content-Type": "application/json" }, body: JSON.stringify({ recipientAccountId: `eip155:1:${recipient}`, assets: [{ assetId, amountRaw }], }), }).then((r) => r.json()); const signature = await signTypedDataAsync(stage1.data.typedData); // Stage 2 echoes `typedData.message` back as `body.message`. const stage2 = await fetch(`${API}/v2/portfolios/${portfolioId}/withdraw`, { method: "POST", headers: { "x-api-key": KEY, "Content-Type": "application/json" }, body: JSON.stringify({ message: stage1.data.typedData.message, signature }), }).then((r) => r.json()); // Poll execution status via GET /v2/portfolios/:id/operations/:operationId // until a terminal state is reached. const { operationId } = stage2.data; ``` ```javascript JavaScript (Solana, Model B) theme={null} import bs58 from "bs58"; // Stage 1 returns `data.authorization` for Solana portfolios. const stage1 = await fetch(`${API}/v2/portfolios/${portfolioId}/withdraw/signature`, { method: "POST", headers: { "x-api-key": KEY, "Content-Type": "application/json" }, body: JSON.stringify({ recipientAccountId: `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:${recipient}`, assets: [{ assetId, amountRaw }], }), }).then((r) => r.json()); const auth = stage1.data.authorization; // { kind: "solana-message", text, message } // The Solana wallet renders `auth.text` and signs its UTF-8 bytes (ed25519). const sigBytes = await wallet.signMessage(new TextEncoder().encode(auth.text)); const signature = bs58.encode(sigBytes); // Stage 2 echoes `authorization.message` back as `body.message`. const stage2 = await fetch(`${API}/v2/portfolios/${portfolioId}/withdraw`, { method: "POST", headers: { "x-api-key": KEY, "Content-Type": "application/json" }, body: JSON.stringify({ message: auth.message, signature }), }).then((r) => r.json()); const { operationId } = stage2.data; ``` ```json 202 theme={null} { "success": true, "data": { "operationId": "op_01JWZEE2MF30KVRMRX53N88VA4", "submittedAt": "2026-04-17T12:05:00.000Z" } } ``` ```json 400 — expired theme={null} { "success": false, "error": { "code": "API_216", "message": "Withdrawal authorization expired at 2026-04-17T11:50:00.000Z" } } ``` ```json 400 — bad signature theme={null} { "success": false, "error": { "code": "API_217", "message": "Withdrawal signature does not match the portfolio owner" } } ``` ```json 409 — in flight theme={null} { "success": false, "error": { "code": "API_007", "message": "Another withdrawal with the same nonce is still in progress" } } ``` ```json 503 — verifier down theme={null} { "success": false, "error": { "code": "API_506", "message": "Signature verifier is temporarily unavailable" } } ``` # Prepare Withdrawal Authorization (Stage 1) Source: https://docs.glider.fi/api-reference/endpoints/v2-withdraw-signature POST /v2/portfolios/{portfolioId}/withdraw/signature Stage 1 of the two-stage withdrawal flow — returns the authorization (EIP-712 for EVM, an off-chain message for Solana) for the end-user to sign in their wallet. Stage 1 of the two-stage withdrawal flow. Validates the request, checks live onchain balances, and returns the authorization message the end-user signs with their portfolio owner wallet. Stage 2 (`POST /v2/portfolios/{portfolioId}/withdraw`) accepts the signed authorization and dispatches the onchain transfer. Wallet addresses are exchanged as [CAIP-10](https://chainagnostic.org/CAIPs/caip-10) account identifiers. `recipientAccountId` is always chain-bound — a withdrawal goes to one address on one specific chain: * **EVM:** `eip155::
` * **Solana:** `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:` * Auth: `x-api-key` header (required) * Scope: `portfolios:withdraw` The returned `authorizationId` (= the signed `message.nonce`) is the idempotency anchor for the matching `POST .../withdraw` call. The **shape of the authorization depends on the withdrawal chain selected by `recipientAccountId` and the listed assets**: * **EVM withdrawals** — stage 1 returns the full EIP-712 typed-data object at `data.typedData`. Pass it directly to `signTypedData(data.typedData)` in the user's wallet, then echo `typedData.message` back as `body.message` on stage 2. * **Solana withdrawals** — stage 1 returns `data.authorization` instead. Sign it per its `kind` (see [Solana withdrawals](#solana-withdrawals)) and echo `authorization.message` back as `body.message` on stage 2. Stage 2's `body.message` shape is identical for both chains — only the signature encoding and where you read the message from (`typedData.message` vs `authorization.message`) differ. The authorization is valid for **10 minutes**. If the user takes longer to sign — or a balance check fails at stage 2 — restart the flow by calling this endpoint again. ### Chain-equivalence rule Every asset's `assetId` must be on the same chain as `recipientAccountId`. A single authorization withdraws a set of assets from one chain to one recipient on that chain. Multi-chain withdrawals require separate authorizations per chain. ```⚠️ Warning theme={null} At the moment, withdrawals and liquidations do not perform cross-chain settlement or bridging. `recipientAccountId`, every `assets[].assetId`, and `settlementAssetId` must reference the same chain. For example, Base assets cannot be liquidated directly to Solana USDC. Use a Base recipient and Base settlement asset, then bridge the proceeds separately if they need to arrive on Solana. ``` Some yield-share assets must be redeemed through their registered yield source before the underlying asset can leave a portfolio. A direct request for such a share returns `UNSUPPORTED_WITHDRAW_ASSET`; redeem it first, then authorize the underlying asset withdrawal. ### Liquidate to a settlement asset Set `liquidate: true` (default `false`) to have the listed `assets` swapped to a single settlement asset and delivered to `recipientAccountId`, instead of transferring each asset as-is. Both the flag and the settlement asset are part of the signed authorization (the EIP-712 typed-data on EVM, the off-chain digest/message on Solana), so the swap intent is bound to the user's signature and cannot be changed at stage 2. Use `settlementAssetId` to pick the settlement asset. Its CAIP-19 chain must match both `recipientAccountId` and every listed asset: * **EVM withdrawal** — USDC by default; configured USDT may be selected explicitly on the same EVM chain. * **Solana withdrawal** — Solana USDC only. Any asset that isn't an allowed settlement asset on the chain returns `400 API_221 UNSUPPORTED_SETTLEMENT_ASSET`. `settlementAssetId` is part of the signed message only when `liquidate: true`; plain transfers don't carry it. Assets whose value is below the tenant's configured swap threshold are **skipped** (left in the vault) — the prepared authorization covers only the assets that clear the threshold. If none clear it, stage 1 returns `400 API_220 NOTHING_TO_LIQUIDATE`. Assets already denominated in the settlement asset are delivered directly without a swap. To liquidate an entire portfolio without listing assets, use [`POST .../liquidate-all/signature`](./v2-liquidate-all-signature). ### Signed EIP-712 domain The returned `domain` binds the signature to two things: * `chainId` — the EVM chain the assets and recipient live on. Required for ERC-1271 smart-wallet verification, which re-hashes the typed-data using the wallet's own `chainId`. * `verifyingContract` — the smart account being debited on that chain. Scopes the signature to a specific smart account deployment. Integrators should pass `domain`, `types`, `primaryType`, and `message` verbatim to `signTypedData` in the user's wallet. ### Solana withdrawals For Solana portfolios, stage 1 returns `data.authorization` (not `data.typedData`). Glider's pooled agent executes the SPL/SOL transfer out of the portfolio's Swig sub-account and the Glider paymaster sponsors the fee — the user's signature is the **off-chain authorization** proving the funds' owner approved this exact withdrawal. The authorization binds the portfolio, the smart account being debited, the recipient, every asset and amount, the nonce, and the expiry, so a captured signature authorizes one withdrawal and nothing else. `authorization` is a discriminated union keyed on `kind`, matching the portfolio's owner model (the same two models as enrollment): * **`kind: "ecdsa"` — EVM-rooted (Model A).** The portfolio owner is an EVM key (the Swig root authority). Sign `authorization.raw` (a 32-byte hash) with the owner's EVM wallet via EIP-191 `personal_sign`. Submit the resulting `0x…` hex signature on stage 2. * **`kind: "solana-message"` — Solana-rooted (Model B).** The portfolio owner is a Solana key. The wallet (Phantom, Backpack, Solflare, …) renders `authorization.text` verbatim; sign its UTF-8 bytes via the wallet's `signMessage` (ed25519). Submit the resulting base58 signature on stage 2. In both cases, echo `authorization.message` back verbatim as `body.message` on stage 2. `recipientAccountId` and every asset's `assetId` must be on Solana (`solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/…`). Native SOL uses the `slip44:501` asset reference; SPL tokens use `spl:`. ### What the user sees in their wallet When rendering the signature prompt, the wallet displays each field of the signed struct. Users see `recipientAccountId` as a CAIP-10 string like `eip155:1:0x4444…` — chain and address are visible together. They also see `portfolioId`, `assets` (CAIP-19 asset ids + raw atomic amounts), `nonce`, `expiresAt`, and `liquidate` — plus `settlementAssetId` (the settlement asset) on a liquidation. Integrators SHOULD surface a decoded summary in their own UI (asset symbols, human-readable amounts) so users can verify the recipient and amounts before approving. Common error responses: * `400 API_210 INSUFFICIENT_BALANCE` — live balance below `amountRaw` for at least one asset. * `400 API_211 INVALID_RECIPIENT` — zero address, or a self-transfer to the smart account / Swig sub-account being debited. The recipient may otherwise be any address the owner signs for — see the recipient note on [stage 2](./v2-withdraw). * `403 API_104 PERMISSION_DENIED` — Solana withdrawal requested but the `svm_b2b_api` feature is not enabled for the tenant. * `400 API_212 DUPLICATE_WITHDRAW_ASSET` — two or more assets in `assets` share the same `assetId`. * `400 API_213 WITHDRAWAL_CHAIN_MISMATCH` — at least one asset is on a different chain than `recipientAccountId`. * `400 API_215 PORTFOLIO_HAS_NO_VAULT_ON_CHAIN` — portfolio has no smart account deployed on the recipient's chain. * `400 API_219 WITHDRAW_AS_USDC_UNSUPPORTED_CHAIN` — `liquidate: true` (with the default USDC settlement asset) but the recipient's chain has no canonical USDC to swap into. * `400 API_220 NOTHING_TO_LIQUIDATE` — `liquidate: true` but no listed asset's value clears the tenant swap threshold. * `400 API_221 UNSUPPORTED_SETTLEMENT_ASSET` — `settlementAssetId` is not an allowed settlement asset on the recipient's chain (configured USDC or USDT for an EVM withdrawal, or USDC for a Solana withdrawal). * `400` when the request body is otherwise invalid (schema errors). * `401` when `x-api-key` header is missing or the key is invalid. * `403` when the API key lacks the `portfolios:withdraw` scope. * `404 API_200 PORTFOLIO_NOT_FOUND` — `portfolioId` doesn't exist or belongs to a different tenant. * `500` on unexpected server errors. ```bash cURL theme={null} curl --request POST \ --url 'https://api.glider.fi/v2/portfolios/a1b2c3d4/withdraw/signature' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "recipientAccountId": "eip155:1:0x4444444444444444444444444444444444444444", "assets": [ { "assetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "amountRaw": "1000500000" } ] }' ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.glider.fi/v2/portfolios/a1b2c3d4/withdraw/signature", { method: "POST", headers: { "x-api-key": "gldr_sk_your_api_key", "Content-Type": "application/json", }, body: JSON.stringify({ recipientAccountId: "eip155:1:0x4444444444444444444444444444444444444444", assets: [ { assetId: "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", amountRaw: "1000500000", }, ], }), }, ); ``` ```bash cURL (liquidate to USDC) theme={null} curl --request POST \ --url 'https://api.glider.fi/v2/portfolios/a1b2c3d4/withdraw/signature' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "recipientAccountId": "eip155:1:0x4444444444444444444444444444444444444444", "liquidate": true, "settlementAssetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "assets": [ { "assetId": "eip155:1/erc20:0x6b175474e89094c44da98b954eedeac495271d0f", "amountRaw": "5000000000000000000" } ] }' ``` ```bash cURL (liquidate to USDT on BNB) theme={null} curl --request POST \ --url 'https://api.glider.fi/v2/portfolios/a1b2c3d4/withdraw/signature' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "recipientAccountId": "eip155:56:0x4444444444444444444444444444444444444444", "liquidate": true, "settlementAssetId": "eip155:56/erc20:0x55d398326f99059fF775485246999027B3197955", "assets": [ { "assetId": "eip155:56/erc20:0x2170ed0880ac9a755fd29b2688956bd959f933f8", "amountRaw": "1000000000000000000" } ] }' ``` ```bash cURL (Solana) theme={null} curl --request POST \ --url 'https://api.glider.fi/v2/portfolios/a1b2c3d4/withdraw/signature' \ --header 'x-api-key: gldr_sk_your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "recipientAccountId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU", "assets": [ { "assetId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/spl:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "amountRaw": "1000000" } ] }' ``` ```json 200 theme={null} { "success": true, "data": { "authorizationId": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", "expiresAt": "2026-04-17T12:10:00.000Z", "typedData": { "primaryType": "Withdrawal", "domain": { "name": "Glider Withdrawal Authorization", "version": "1", "chainId": 1, "verifyingContract": "0x2222222222222222222222222222222222222222" }, "types": { "Withdrawal": [ { "name": "portfolioId", "type": "string" }, { "name": "recipientAccountId", "type": "string" }, { "name": "assets", "type": "WithdrawalAsset[]" }, { "name": "nonce", "type": "bytes32" }, { "name": "expiresAt", "type": "uint256" } ], "WithdrawalAsset": [ { "name": "assetId", "type": "string" }, { "name": "amountRaw", "type": "string" } ] }, "message": { "portfolioId": "a1b2c3d4", "recipientAccountId": "eip155:1:0x4444444444444444444444444444444444444444", "assets": [ { "assetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "amountRaw": "1000500000" } ], "nonce": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", "expiresAt": 1744898400 } } } } ``` ```json 200 (liquidate, EVM) theme={null} { "success": true, "data": { "authorizationId": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", "expiresAt": "2026-04-17T12:10:00.000Z", "typedData": { "primaryType": "Withdrawal", "domain": { "name": "Glider Withdrawal Authorization", "version": "2", "chainId": 1, "verifyingContract": "0x2222222222222222222222222222222222222222" }, "types": { "Withdrawal": [ { "name": "portfolioId", "type": "string" }, { "name": "recipientAccountId", "type": "string" }, { "name": "assets", "type": "WithdrawalAsset[]" }, { "name": "nonce", "type": "bytes32" }, { "name": "expiresAt", "type": "uint256" }, { "name": "liquidate", "type": "bool" }, { "name": "settlementAssetId", "type": "string" } ], "WithdrawalAsset": [ { "name": "assetId", "type": "string" }, { "name": "amountRaw", "type": "string" } ] }, "message": { "portfolioId": "a1b2c3d4", "recipientAccountId": "eip155:1:0x4444444444444444444444444444444444444444", "assets": [ { "assetId": "eip155:1/erc20:0x6b175474e89094c44da98b954eedeac495271d0f", "amountRaw": "5000000000000000000" } ], "nonce": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", "expiresAt": 1744898400, "liquidate": true, "settlementAssetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" } } } } ``` ```json 200 (Solana, Model B) theme={null} { "success": true, "data": { "authorizationId": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", "expiresAt": "2026-04-17T12:10:00.000Z", "authorization": { "kind": "solana-message", "text": "Glider — authorize Solana withdrawal\n\nPortfolio: a1b2c3d4\nSmart account: solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:5tzFkiKscXHK5ZXCGbXZxdw7gTjjD1mBwuoFbhUvuAi9\nRecipient: solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU\nChain: 5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp\nAsset 1: 1000000 solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/spl:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v\nNonce: 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef\nExpires at (unix): 1744898400\n\nDomain: glider:svm-withdraw:solana:v1", "message": { "portfolioId": "a1b2c3d4", "recipientAccountId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU", "assets": [ { "assetId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/spl:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "amountRaw": "1000000" } ], "nonce": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", "expiresAt": 1744898400 } } } } ``` ```json 400 theme={null} { "success": false, "error": { "code": "API_213", "message": "Asset eip155:137/erc20:0x... is not on the recipient's chain (eip155:1)" } } ``` ```json 404 theme={null} { "success": false, "error": { "code": "API_200", "message": "Portfolio not found or not owned by tenant: a1b2c3d4" } } ``` # B2B API Overview Source: https://docs.glider.fi/api-reference/v2-overview Learn the authentication, response, scope, identifier, and retry rules for the B2B API. The B2B API is the primary REST API for external integrators. You can define strategies, enroll users, monitor portfolios, and withdraw assets. Two LLM-ready files cover this API. Give your AI model the docs [llms.txt](https://docs.glider.fi/llms.txt) for guides plus API pages, or the API [llms.txt](https://api.glider.fi/v2/llms.txt) for a compact endpoint-only reference generated from the live OpenAPI contract. ## Mental model A **strategy** is a reusable template. It contains an allocation, a schedule, and swap preferences. A **portfolio** connects one user to a strategy. It has one smart account for each chain. All enrolled portfolios use the allocation and schedule of the strategy. A new strategy version changes their target for the next rebalance. There are two rebalance triggers: * **Scheduled**: Uses the strategy `frequency`. Read `nextDueAt` and `lastRebalanceAt` from the portfolio `schedule`. * **Manual**: `POST /v2/portfolios/{id}/rebalance` starts one rebalance outside the schedule. A short cooldown applies to each portfolio. ## Asset coverage Strategy allocations and portfolio positions accept each supported CAIP-19 asset. Supported assets include ERC-20 tokens, SPL tokens, and tokenized real-world assets. `GET /v2/portfolios/{id}/positions` uses one row format for all asset types. ## Chain abstraction The Glider wallet infrastructure routes cross-chain deposits. It also moves assets between supported chains when required. Your client does not need to plan these migrations. ## Machine-readable specifications Use these files for coding agents and SDK generators. These files are the source of truth for the API. | Artifact | URL | Purpose | | ------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------- | | OpenAPI 3.1 JSON | `https://api.glider.fi/v2/openapi.json` | Generate SDKs, typed clients, Postman collections | | API llms.txt | `https://api.glider.fi/v2/llms.txt` | Endpoint-only reference from the OpenAPI contract, for coding agents (Claude, Cursor, etc.) | | Docs llms.txt | `https://docs.glider.fi/llms.txt` | Index of every guide and API page as raw markdown (`llms-full.txt` for full content) | | Interactive docs (Scalar) | `https://api.glider.fi/v2/docs` | Try-it-out UI with live request builder | ## Base URL ``` https://api.glider.fi/v2 ``` Use `https://staging-api.glider.fi/v2` for integration tests. Contact `developers@glider.fi` for a staging API key. ## Authentication Each business route except `GET /v2/scopes` requires an API key. Send the key in the `x-api-key` header. The OpenAPI file, interactive reference, and LLM-friendly reference are public. HTTP header names are not case-sensitive. The B2B API documentation uses lowercase for `x-api-key`. ```bash theme={null} curl -H "x-api-key: gldr_sk_your_api_key" https://api.glider.fi/v2/whoami ``` `GET /v2/whoami` returns the tenant identity and the assigned scopes. Call this route first to test the key. ## Response envelope The B2B API uses one response envelope. **Tracing identifiers are in the response headers, not in the JSON body.** ### Success ```json theme={null} { "success": true, "data": { /* endpoint-specific */ } } ``` ### Paginated success ```json theme={null} { "success": true, "data": { /* endpoint-specific */ }, "nextCursor": "eyJjIjoi..." // null on the last page } ``` `nextCursor` is next to `data`. It is not inside `data`. Each collection in `data` has a named key, such as `data.portfolios`. ### Error ```json theme={null} { "success": false, "error": { "code": "API_400", "message": "Request validation failed", "details": ["allocation.assets: Allocation weights must sum to 100"] } } ``` The `details` field is optional. The API omits it when `message` contains all applicable information. ### Tracing headers Each response includes these headers: | Header | Purpose | | ------------------ | --------------------------------------------------------- | | `X-Correlation-Id` | Cross-service trace id. Quote this when reporting issues. | | `X-Request-Id` | Unique per request. | The B2B API does not put `correlationId`, `requestId`, or `timestamp` in the body. Read these values from the headers. ## Scopes Each authenticated route requires one scope. If the key does not have this scope, the API returns `403 API_104`. | Scope | Tier | Grants | | --------------------- | -------- | ------------------------------------------------------------------------- | | `strategies:read` | default | List and view strategies | | `strategies:write` | standard | Create strategies and publish versions | | `portfolios:read` | default | List and view user portfolios, poll operations | | `portfolios:write` | standard | Pause, resume, trigger rebalances, and update portfolio metadata | | `portfolios:withdraw` | standard | Prepare and submit user-signed withdrawal authorizations | | `enroll:write` | standard | Enroll new users into a strategy (creates smart accounts and a portfolio) | | `tenant:read` | standard | Read tenant-wide execution and schedule defaults | | `tenant:write` | standard | Update tenant-wide execution and schedule defaults | | `fees:read` | standard | Read tenant and per-strategy swap-fee configuration | | `fees:write` | standard | Update tenant and per-strategy swap-fee configuration | Tiers: * **default**: Assigned to each new key. * **standard**: Assigned to an integrator on request. Contact `developers@glider.fi` to upgrade. * **restricted**: Requires a commercial agreement. Call `GET /v2/scopes` to read the current scope list. This route does not require authentication. Call `GET /v2/whoami` to read the scopes for your key. ## Identifiers (CAIP) All onchain identifiers use [CAIP](https://github.com/ChainAgnostic/CAIPs). Thus, the same request format works for each supported chain. Do not send bare hex addresses. | Identifier | Form | When to use | | -------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | End-user wallet (EVM EOA) | **Chain-agnostic** CAIP-10: `eip155:0:0x` | Owner addresses in EVM enrollment and portfolio reads. The same EOA works on every EIP-155 chain, so the chain reference is `0` per CAIP-10 §Abstract Account Addresses. | | End-user wallet (Solana) | CAIP-10: `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:` | Owner address for Solana-rooted (Model B) enrollment. Solana has no chain-agnostic `0` form, so the mainnet CAIP-2 reference is used. | | Smart account / smart-contract account | **Chain-bound** CAIP-10: `eip155::0x` (EVM) or `solana::` (Solana Swig sub-account) | Smart accounts, session-key agents, withdrawal recipients. The account exists only at that (chain, address) tuple. | | Asset (ERC-20) | CAIP-19: `eip155:/erc20:0x` | Strategy allocations, withdrawal assets. | | Asset (SPL, Solana) | CAIP-19: `solana:/spl:` | Same surface, Solana-native assets. | Withdrawal recipients must be chain-bound. The recipient chain must match each asset chain. One withdrawal can use only one chain. The recipient can be any address that the owner authorizes. This rule applies to EVM and Solana. The API rejects a transfer to the source smart account. See [CAIP identifiers](/guides/caip-identifiers) for a worked walk-through. ## Supported Chains Each deployment has a configured set of enrollment chains. An EVM chain requires a nonempty `JSON_RPC_URL_` value. Solana (`1399811149`) requires `SOLANA_RPC_URL` and the applicable tenant entitlement. Do not use a fixed global chain list. Ask Glider which chains are enabled in the applicable environment. An unsupported chain returns `400 API_400`. The `error.details` field identifies the rejected `chainIds`. You select portfolio chains during enrollment. An EVM portfolio can add chains later with the two-stage [chain-activation flow](/api-reference/endpoints/v2-activate-chains-signature) (`POST /v2/portfolios/{portfolioId}/chains/signature` + `POST /v2/portfolios/{portfolioId}/chains`). ### Solana (SVM) enrollment The `svm_b2b_api` entitlement controls Solana enrollment for each tenant. Contact `developers@glider.fi` to enable it. Without it, a Solana enrollment request returns `403 API_104`. Each enrollment request uses one namespace. Send Solana (`1399811149`) chain IDs or EVM chain IDs. Do not send both. Both owner models create a Swig smart wallet. Its sub-account is the deposit address. * **Solana-rooted (Model B)**: `ownerAccountId` is a `solana:` CAIP-10, and the user signs a Solana transaction returned by stage 1. * **EVM-rooted (Model A)**: `ownerAccountId` stays `eip155:0:` with `chainIds: [1399811149]`. The secp256k1 key of the user becomes the Swig root and authorizes the sub-account with a slot-bound signature. See [Two-stage enrollment](/guides/two-stage-enrollment#solana-svm-enrollment) for the request and response formats. Solana withdrawals use the same owner models. Stage 1 returns `data.authorization` instead of `data.typedData`. Stage 2 accepts a hex or base58 signature. See [Prepare Withdrawal Authorization → Solana withdrawals](/api-reference/endpoints/v2-withdraw-signature#solana-withdrawals). ## Monetary Values The API sends monetary values as decimal strings. This format prevents IEEE 754 precision loss. | Field | Format | Example | | ---------------------------- | -------------------------------------------------- | ------------------------------ | | `totalValueUsd`, `valueUsd` | 6 decimal places | `"1500.500000"` | | `balance` | Full precision, trailing zeros trimmed | `"14562044.3028598485"` | | `balanceRaw`, `amountRaw` | Integer string, no decimal, no scientific notation | `"14562044302859848500000000"` | | `priceUsd` | 6 decimal places | `"1.000000"` | | Strategy allocation `weight` | Percent with up to 2 decimal places | `"60"` or `"33.33"` | | Exposure `weight` | Fraction from 0 to 1 | `"0.6"` or `"0.3333"` | | `decimals` | Integer (the only numeric financial field) | `6` | For a full withdrawal, read `balanceRaw` from `GET /v2/portfolios/{portfolioId}/positions`. Send this value as `amountRaw`. Do not calculate it from `balance`. ## Pagination Pagination uses a cursor based on `(createdAt, id)`. * `limit`: Minimum 1, maximum 200, and default 50. * `cursor`: An opaque base64url string. * `nextCursor`: `null` when there are no more pages. Do not decode or create cursors. Glider owns the cursor format and can change it. Send the previous `nextCursor` unchanged. ## Idempotency Write routes with permanent effects use an idempotency anchor. An identical replay returns the original response. A replay with a different body returns `409 API_008`. | Route | Anchor | Sourced from | | ----------------------------------- | --------------- | ------------------------------------------------------ | | `POST /v2/enroll` | `flowId` | `POST /v2/enroll/signature` response | | `POST /v2/portfolios/{id}/withdraw` | `message.nonce` | `POST /v2/portfolios/{id}/withdraw/signature` response | See [Idempotency](/guides/idempotency) for retry rules and the three 409 sub-codes you may see. ## Asynchronous operations Write routes that start onchain work return `202` and an `operationId`. Poll the operation every 2–5 seconds. Stop when the state is `completed`, `failed`, or `cancelled`. ## Error codes All B2B API error codes use the `API_XXX` format. The number range identifies the category. The following table contains common codes. | Code | HTTP | Meaning | | --------- | ---- | ---------------------------------------------------------------------------------------- | | `API_006` | 404 | Resource not found | | `API_007` | 409 | Idempotency replay in progress — retry after a short wait | | `API_008` | 409 | Idempotency key conflict — same anchor, different body | | `API_101` | 401 | `x-api-key` header missing | | `API_102` | 401 | API key invalid | | `API_104` | 403 | Missing required scope, or Solana enrollment not enabled for your tenant (`svm_b2b_api`) | | `API_200` | 404 | Portfolio not found or not owned by tenant | | `API_202` | 409 | Portfolio already exists for this `(strategyId, ownerAccountId)` | | `API_210` | 400 | Insufficient balance for withdrawal | | `API_211` | 400 | Invalid recipient | | `API_212` | 400 | Duplicate `assetId` in withdrawal | | `API_213` | 400 | Withdrawal chain mismatch (recipient vs asset chain) | | `API_214` | 400 | Withdrawal `message.portfolioId` mismatches the path param | | `API_215` | 400 | Portfolio has no smart account on the recipient's chain | | `API_216` | 400 | Withdrawal authorization expired (past `message.expiresAt`) | | `API_217` | 400 | Withdrawal signature does not recover to the portfolio owner | | `API_400` | 400 | Request validation failed — see `details[]` | | `API_506` | 503 | Signature verifier temporarily unavailable — safe to retry | | `API_600` | 500 | Internal server error | For the full list, see [Error codes](/guides/error-codes). ## Tracing and support Include the `X-Correlation-Id` header when you report an issue. Glider uses this value to find related logs. ## Next steps * [Glider's Security Architecture](/guides/security-architecture) * [Two-stage enrollment](/guides/two-stage-enrollment) * [Two-stage withdrawal](/guides/two-stage-withdrawal) * [Idempotency](/guides/idempotency) * [CAIP identifiers](/guides/caip-identifiers) # B2B API Overview Source: https://docs.glider.fi/guides/b2b-overview Learn how the B2B API separates integrator access, user authority, automation, and onchain custody. The B2B API separates four types of authority: * **Integrator API keys identify the tenant.** They let your backend create strategies, enroll users, and read portfolios. Each key can use only its assigned scopes. * **Users control their wallets and portfolios.** Enrollment and withdrawal flows require user signatures. An API key cannot replace a user signature. * **Glider manages permitted automation.** Glider uses session keys or delegated agents to run scheduled and manual rebalances. * **Assets stay onchain.** User-specific smart accounts hold portfolio assets. Glider does not use an omnibus offchain ledger for these assets. For more information, read [Glider's Security Architecture](/guides/security-architecture). Contact the Glider team if you have questions about your wallet provider or production rollout.