Authentication
All requests require a Bearer token in theAuthorization header.
401:
Environments
| Environment | Base URL |
|---|---|
| Sandbox | https://sandbox.trybraid.xyz |
| Production | https://production.trybraid.xyz |
ethereum → Ethereum Sepolia, solana → Solana devnet. The API uses canonical chain names in both environments; sandbox maps them to testnets internally.
Testing in sandbox
- Get test USDC — use the Circle faucet for Sepolia USDC (Ethereum) or devnet USDC (select Solana devnet).
- Simulated positions —
usdzis simulated in sandbox since USDZ is not available on testnets. ThepositionKeyand request shapes are identical to production. - Same API shapes — request and response formats are identical between sandbox and production. Only base URLs and underlying networks differ.
- End-to-end flow — create a wallet, deposit test USDC, check balances, initiate a withdrawal, and track it to completion. The full lifecycle works in sandbox.
Versioning
All endpoints are prefixed with/v2/. This is the current and only supported version.
Breaking changes (removed fields, changed types, altered semantics) will result in a new version prefix. Additive changes (new fields, new endpoints, new enum values) are made in-place and do not increment the version. Build your client to tolerate unknown JSON fields.
Response format
- Field names are camelCase (
withdrawalAmount,positionKey). - Enum values are lower_snake_case (
stablecoin_yield,overcollateralized_lending). - USD amounts are fixed-precision decimal strings (
"65000.00","0.00"), never raw floats. - Position identifiers use
positionKey(camelCase):usdz,syrupUsdc,rlp,ustb. - Timestamps are ISO 8601 UTC (
"2026-02-05T08:15:00.000Z"). - Durations are ISO 8601 (
"PT2H","PT24H"). See Yield Sources for details.
Pagination
List endpoints use cursor-based pagination. Passlimit and cursor as query parameters:
nextCursor. Pass it as cursor to fetch the next page. When nextCursor is null, you’ve reached the end.
| Param | Default | Description |
|---|---|---|
limit | 25 | Items per page (1–100) |
cursor | — | Opaque cursor from nextCursor |
sort | createdAt | Sort field |
order | desc | asc or desc |
createdAtGte | — | ISO 8601 lower-bound filter |
status | — | Repeatable status filter (e.g. status=processing&status=completed) |
The activity feed uses a slightly different pagination shape: responses contain
hasMore (boolean) instead of nextCursor. The cursor query parameter still works the same way.Idempotency
Create and withdrawal endpoints accept arequestId (UUID v4). Retrying with the same requestId and payload returns the original response without creating duplicates.
If the requestId was already used with a different payload, the API returns 409 request_id_conflict.
409, fetch the existing resource rather than retrying the create.
Rate limits
Requests are rate-limited per organization:| Tier | Limit | Window |
|---|---|---|
Read (GET) | 200 requests | 60 seconds |
Write (POST, PATCH, DELETE) | 50 requests | 60 seconds |
429 with a Retry-After header:
| Header | Description |
|---|---|
X-RateLimit-Limit | Max requests for this tier |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Retry-After | Seconds to wait (only on 429) |
429. For high-throughput integrations, use webhooks instead of polling.
Error handling
Errors are JSON with a human-readableerror string and a machine-readable code:
code for programmatic branching. Use error for logging.
Error code reference
| Code | HTTP | When |
|---|---|---|
validation_error | 400 | Invalid request body or parameters |
unknown_parameters | 400 | Unexpected query or body parameters |
unsupported_chain | 400 | Chain not supported for the requested operation |
unsupported_token | 400 | Token not supported |
invalid_destination_address | 400 | Malformed destination address |
precision_overflow | 400 | Too many decimal places (max 6 for USDC) |
invalid_position_weight | 400 | Weight out of range or weights do not sum to 10,000 bps |
invalid_cursor | 400 | Malformed pagination cursor |
unauthenticated | 401 | Missing or invalid Bearer token |
wallet_not_found | 404 | Wallet ID does not exist or does not belong to your organization |
withdrawal_not_found | 404 | Withdrawal ID does not exist |
deposit_not_found | 404 | Deposit ID does not exist |
rate_limit_exceeded | 429 | Too many requests — back off and retry |
insufficient_funds | 409 | Not enough unreserved balance |
duplicate_request_id | 409 | requestId already used with the same payload |
request_id_conflict | 409 | requestId already used with a different payload |
withdrawal_not_cancellable | 409 | Withdrawal in a non-cancellable state |
withdrawal_already_cancelled | 409 | Withdrawal already cancelled |
withdrawal_payout_in_progress | 409 | Cannot cancel — payout already broadcast |
internal_error | 500 | Server error — retry with exponential backoff |
wallet_creation_failed | 502 | Upstream custody provider failure |
service_temporarily_unavailable | 503 | Temporary outage — retry after a short delay |
Support
If you encounter500, 502, or 503 errors, retry with exponential backoff. For persistent issues, contact your Braid account representative. Webhook delivery is best-effort — build your integration to tolerate missed events by polling as a backstop.
Next steps
- Quickstart — create a wallet and withdraw in 5 minutes
- Create a Wallet — wallet creation, lifecycle, and response shape
- Webhooks — real-time event notifications