API Overview
Response Envelope
All API responses use a standard envelope. Successful responses have success: true with a data field. Error responses have success: false with an error field.
// Success{ "success": true, "data": { ... }}
// Error{ "success": false, "error": { "code": "VALIDATION_ERROR", "message": "Realm name is required" }}HTTP Status Codes
| Code | Meaning |
|---|---|
200 | Success |
201 | Created — resource successfully created |
400 | Bad Request — validation error in your input |
401 | Unauthorized — missing or invalid authentication |
404 | Not Found — resource doesn't exist or isn't accessible |
409 | Conflict — duplicate resource or invalid state transition |
500 | Internal Server Error — unexpected failure |
Auth Endpoints
Sign Up
POST/api/v1/auth/signup
Create a new builder account. Returns a JWT token on success.
Request Body
emailstringrequiredpasswordstringrequiredorgNamestringrequiredResponse
{ "success": true, "data": { "token": "eyJhbGciOiJIUzI1NiIs...", "builder": { "id": "54248205-1cc7-4437-a76c-95d2b7bf07e0", "email": "you@company.com", "orgName": "Acme Trading" } }}Sign In
POST/api/v1/auth/signin
Authenticate an existing builder. Returns a JWT token.
Request Body
emailstringrequiredpasswordstringrequiredResponse
{ "success": true, "data": { "token": "eyJhbGciOiJIUzI1NiIs...", "builder": { "id": "54248205-1cc7-4437-a76c-95d2b7bf07e0", "email": "you@company.com", "orgName": "Acme Trading" } }}Get Profile
GET/api/v1/auth/meJWT
Retrieve the authenticated builder's profile.
Response
{ "success": true, "data": { "id": "54248205-1cc7-4437-a76c-95d2b7bf07e0", "email": "you@company.com", "orgName": "Acme Trading" }}Realm Endpoints
All realm endpoints require JWT authentication. Builders can only access their own realms.
Create Realm
POST/api/v1/realmsJWT / API Key
Request Body
namestringrequiredtypestring"demo" (default) or "production".descriptionstringResponse 201 Created
{ "success": true, "data": { "id": "6d25623e-597e-4815-8bfa-6911b38c2079", "builderId": "54248205-1cc7-4437-a76c-95d2b7bf07e0", "name": "Development", "slug": "development", "type": "demo", "description": "My development environment", "createdAt": "2026-02-11T21:12:52.529Z", "updatedAt": "2026-02-11T21:12:52.529Z" }}List Realms
GET/api/v1/realmsJWT / API Key
Returns all realms belonging to the authenticated builder, ordered by creation date (newest first).
Response
{ "success": true, "data": { "realms": [ { "id": "6d25623e-597e-4815-8bfa-6911b38c2079", "builderId": "54248205-...", "name": "Development", "slug": "development", "type": "demo", "description": "My development environment", "createdAt": "2026-02-11T21:12:52.529Z", "updatedAt": "2026-02-11T21:12:52.529Z" } ], "total": 1 }}Get Realm
GET/api/v1/realms/:idJWT / API Key
Retrieve a specific realm by ID.
Path Parameters
idstringrequiredDelete Realm
DELETE/api/v1/realms/:idJWT / API Key
Permanently delete a realm. This cannot be undone.
Response
{ "success": true, "data": { "deleted": true }}Update Realm Settings
PATCH/api/v1/realms/:id/settingsJWT / API Key
Update settings for a realm. Settings are stored as a JSON object and can include configuration like a default builder fee for exchange orders.
Request Body
defaultBuilderFeeBpsnumber | nullnull to clear.Response
{ "success": true, "data": { "id": "6d25623e-...", "builderId": "54248205-...", "name": "Development", "slug": "development", "type": "demo", "description": "My development environment", "settings": { "defaultBuilderFeeBps": 5 }, "createdAt": "2026-02-11T...", "updatedAt": "2026-02-22T..." }}API Key Endpoints
All API key endpoints require JWT authentication. API keys are scoped to a builder and a specific realm.
Create API Key
POST/api/v1/api-keysJWT
Generate a new API key. The raw key is included in the response and is never returned again.
Request Body
namestringrequiredResponse 201 Created
{ "success": true, "data": { "apiKey": { "id": "388ffe67-5d95-4fc3-b4c2-0719b2ed756f", "builderId": "54248205-...", "name": "Backend Service", "keyPrefix": "78ae7276", "status": "active", "createdAt": "2026-02-11T21:12:52.529Z", "revokedAt": null, "updatedAt": "2026-02-11T21:12:52.529Z" }, "rawKey": "arca_78ae7276_178e3df83d9d51372ad0..." }}The rawKey is only included in the creation response. Store it immediately in a secure location. If lost, revoke the key and create a new one.
List API Keys
GET/api/v1/api-keysJWT
List all API keys for the authenticated builder.
Response
{ "success": true, "data": { "apiKeys": [ { "id": "388ffe67-...", "builderId": "54248205-...", "name": "Backend Service", "keyPrefix": "78ae7276", "status": "active", "createdAt": "2026-02-11T21:12:52.529Z", "revokedAt": null, "updatedAt": "2026-02-11T21:12:52.529Z" } ], "total": 1 }}Revoke API Key
DELETE/api/v1/api-keys/:idJWT
Revoke an API key. The key immediately becomes unusable. This cannot be undone.
Path Parameters
idstringrequiredResponse
{ "success": true, "data": { "revoked": true }}Arca Object Endpoints
All explorer endpoints require authentication (JWT or API key). Objects are scoped to a realm and a builder.
Create Arca Object
POST/api/v1/objectsJWT / API Key
Create an Arca object. This endpoint is idempotent: if an active object already exists at the path with matching type and denomination, it is returned without error. Atomically creates both the object and a create operation in a single transaction.
Idempotency
Idempotency is enforced at two levels. If operationPath is provided, the system first checks whether that operation already exists — if it does, the existing result is returned (retry safety). Second, if an active Arca object exists at path with matching type/denomination, it is returned as-is. A type or denomination mismatch returns an error.
Request Body
realmIdstringrequiredpathstringrequired/treasury/usd-reserve). Must be unique within the realm for active objects.typestring"denominated" (default), "exchange", "deposit", "withdrawal", "escrow".denominationstring"USD", "BTC"). Relevant for denominated objects.metadatastringoperationPathstring":" and prefix /op/create{arcaPath} to generate this (e.g., /op/create/wallets/main:1). Recommended for production use, especially when an Arca may be deleted and recreated at the same path.Response 200 OK
{ "success": true, "data": { "object": { "id": "a1b2c3d4-...", "realmId": "6d25623e-...", "path": "/treasury/usd-reserve", "type": "denominated", "denomination": "USD", "status": "active", "metadata": null, "createdAt": "2026-02-11T21:12:52.529Z", "updatedAt": "2026-02-11T21:12:52.529Z" }, "operation": { "id": "e5f6a7b8-...", "realmId": "6d25623e-...", "path": "/op/create/treasury/usd-reserve:1", "type": "create", "state": "completed", "sourceArcaPath": null, "targetArcaPath": "/treasury/usd-reserve", "input": "...", "outcome": "...", "actorType": "builder", "actorId": "b1234...", "createdAt": "2026-02-11T21:12:52.529Z", "updatedAt": "2026-02-11T21:12:52.529Z" } }}List Arca Objects
GET/api/v1/objectsJWT / API Key
List objects in a realm. Optionally filter by path prefix.
Query Parameters
realmIdstringrequiredprefixstring/treasury).includeDeletedbooleanfalse.Response
{ "success": true, "data": { "objects": [ { "id": "...", "path": "/treasury/usd-reserve", ... } ], "total": 1 }}Browse Objects
GET/api/v1/objects/browseJWT / API Key
S3-style hierarchical browsing. Returns folders (virtual path prefixes) and objects at the given level.
Query Parameters
realmIdstringrequiredprefixstring"/".includeDeletedbooleanfalse.Response
{ "success": true, "data": { "prefix": "/treasury/", "folders": ["reserves/"], "objects": [ { "id": "...", "path": "/treasury/usd-reserve", ... } ] }}Aggregate by Path
GET/api/v1/objects/aggregateJWT / API Key
Compute total equity in USD for all Arca objects under a path prefix, broken down by asset type (spot denominations and perp positions). Exchange objects use the canonical Model C (full notional) accounting: equity = cash + sum(position_qty × mid_price). Cash may be negative for leveraged positions.
Query Parameters
realmIdstringrequiredprefixstringrequired/users/u-123/).Reserved balances (in-flight amounts tied to pending operations) are included in totalEquityUsd since they represent value the user still owns. The totalReservedUsd field breaks out how much of the total is currently in-flight.
Response
{ "success": true, "data": { "prefix": "/users/u-123/", "totalEquityUsd": "15734.56", "totalReservedUsd": "500", "breakdown": [ { "asset": "USD", "category": "spot", "amount": "10000", "price": null, "valueUsd": "10000" }, { "asset": "USD-EXCHANGE", "category": "exchange", "amount": "4900", "price": null, "valueUsd": "4900" }, { "asset": "BTC", "category": "spot", "amount": "0.05", "price": "98000", "valueUsd": "4900" }, { "asset": "ETH-PERP", "category": "perp", "amount": "1.5", "price": "2800", "valueUsd": "334.56", "weightedAvgLeverage": "3.00" } ], "objects": [ { "objectId": "a1b2...", "path": "/users/u-123/usd", "type": "denominated", "denomination": "USD", "valueUsd": "10500", "balances": [{ "denomination": "USD", "amount": "10000", "price": null, "valueUsd": "10000" }], "reservedBalances": [ { "denomination": "USD", "amount": "500", "price": null, "valueUsd": "500", "operationId": "op-1234..." } ], "positions": null }, { "objectId": "c3d4...", "path": "/users/u-123/exchange", "type": "exchange", "denomination": null, "valueUsd": "5234.56", "balances": [{ "denomination": "USD", "amount": "4900", "price": null, "valueUsd": "4900" }], "reservedBalances": [], "positions": [ { "coin": "ETH", "side": "LONG", "size": "1.5", "entryPrice": "2750", "markPrice": "2800", "unrealizedPnl": "75", "valueUsd": "334.56" } ] } ] }}Aggregation Watches
POST/api/v1/aggregations/watchJWT / API Key
Watches provide real-time, targeted aggregation updates. Create a watch to subscribe to aggregated equity for a set of objects defined by prefix, glob pattern, explicit path list, or composition of other watches. The server emits aggregation.invalidated SSE events only when an object in the watch changes, enabling efficient targeted invalidation instead of polling.
Create Watch
/api/v1/aggregations/watchJWT / API KeyRequest Body
{ "realmId": "realm-123", "sources": [ { "type": "prefix", "value": "/users/u-123/" }, { "type": "pattern", "value": "/users/*/exchanges/hl/*" }, { "type": "paths", "value": "/treasury/usd,/treasury/btc" }, { "type": "watch", "value": "<other-watch-id>" } ]}realmIdstringrequiredsourcesarrayrequiredtype (prefix, pattern, paths, or watch) and a value string.paths accepts comma-separated Arca paths.pattern supports * as a single-segment wildcard. watch composes another watch by ID.Response
{ "success": true, "data": { "watchId": "a1b2c3d4-...", "aggregation": { "prefix": "(watch)", "totalEquityUsd": "15734.56", "totalReservedUsd": "500", "breakdown": [...], "objects": [...] } }}Get Watch Aggregation
/api/v1/aggregations/watch/:watchIdJWT / API KeyReturns the current aggregation for a watch (recomputed from Redis object leaves). The response shape is the same as the aggregate-by-path endpoint.
Destroy Watch
/api/v1/aggregations/watch/:watchIdJWT / API KeyRemoves the watch subscription. Watches are also automatically evicted after 5 minutes of inactivity.
Get Object Detail
GET/api/v1/objects/:idJWT / API Key
Retrieve an Arca object with its full history — operations, events, state deltas, and current balances.
Path Parameters
idstringrequiredResponse
{ "success": true, "data": { "object": { "id": "a1b2c3d4-...", "realmId": "6d25623e-...", "path": "/treasury/usd-reserve", "type": "denominated", "denomination": "USD", "status": "active", "metadata": null, "createdAt": "2026-02-11T21:12:52.529Z", "updatedAt": "2026-02-11T21:12:52.529Z" }, "operations": [ { "id": "...", "type": "create", ... } ], "events": [ { "id": "...", "type": "deposit.completed", ... } ], "deltas": [ { "id": "...", "deltaType": "balance_change", ... } ], "balances": [ { "id": "...", "denomination": "USD", "amount": "1000.00" } ] }}Get Object Balances
GET/api/v1/objects/:id/balancesJWT / API Key
Retrieve all current balances for a specific Arca object.
Path Parameters
idstringrequiredResponse
{ "success": true, "data": { "balances": [ { "id": "b1c2d3e4-...", "arcaId": "a1b2c3d4-...", "denomination": "USD", "amount": "1000.00" } ] }}Get Object Snapshot (As-Of)
GET/api/v1/objects/:id/snapshotJWT / API Key
Retrieve historical balances and canonical perp positions for an object at a specific timestamp using append-only ledger rows.
Path Parameters
idstringrequiredQuery Parameters
realmIdstringrequiredasOfstringrequiredResponse
{ "success": true, "data": { "realmId": "r1", "arcaId": "a1", "asOf": "2026-02-18T18:00:00Z", "balances": [{ "denomination": "USD", "amount": "1200.00" }], "positions": [{ "market": "BTC-PERP", "side": "LONG", "size": "0.25", "leverage": 3 }] }}Delete Arca Object
POST/api/v1/objects/deleteJWT / API Key
Delete an Arca object. If the object has remaining balances, provide a sweepToPath to transfer funds to another Arca before deletion. The delete executes as a Temporal workflow: first setting status to deleting (blocking all operations), then performing any necessary liquidation, fund withdrawal, sweep, and finalizing to deleted.
In-flight blocking: If the object has any in-flight operations (outbound or inbound holds in held status), the delete is rejected. Wait for pending operations to reach a terminal state before requesting deletion.
Exchange objects: For exchange-type Arca objects with open positions, set liquidatePositions: true to close all positions via market order before deletion. The resulting cash is automatically withdrawn to the platform balance and swept to the target. The workflow has a 5-minute timeout; on timeout, the object reverts to active status.
Request Body
realmIdstringrequiredobjectIdstringobjectId or path.pathstringobjectId.sweepToPathstringliquidatePositionsbooleantrue to liquidate all open exchange positions via market orders before deletion. Required for exchange objects with open positions. Defaults to false.Response 200 OK
{ "success": true, "data": { "object": { "id": "a1b2c3d4-...", "realmId": "6d25623e-...", "path": "/treasury/usd-reserve", "type": "denominated", "denomination": "USD", "status": "deleted", "deletedAt": "2026-02-12T10:30:00.000Z", "createdAt": "2026-02-11T21:12:52.529Z", "updatedAt": "2026-02-12T10:30:00.000Z" }, "operation": { "id": "e5f6a7b8-...", "type": "delete", "state": "completed", "sourceArcaPath": "/treasury/usd-reserve", "targetArcaPath": "/treasury/sweep-account" } }}Get Object Versions
GET/api/v1/objects/:id/versionsJWT / API Key
Retrieve all versions of an Arca object at the same path. When an Arca is deleted, a deletedAt timestamp is set and the path becomes available for reuse. If a new Arca is later created at the same path and also deleted, each becomes a separate version. This endpoint returns all of them sorted newest-first.
Path Parameters
idstringrequiredResponse
{ "success": true, "data": { "versions": [ { "id": "...", "path": "/treasury/usd-reserve", "status": "active", "deletedAt": null, ... }, { "id": "...", "path": "/treasury/usd-reserve", "status": "deleted", "deletedAt": "2026-02-10T15:00:00.000Z", ... } ] }}Operation Endpoints
Create Operation
POST/api/v1/operationsJWT / API Key
Create a new operation against Arca objects. Operations track intent and state transitions.
Request Body
realmIdstringrequiredpathstringrequiredtypestringrequired"transfer", "create", "delete", "deposit", "withdrawal", "swap", "order", "cancel".sourceArcaPathstringtargetArcaPathstringinputstringResponse 201 Created
{ "success": true, "data": { "id": "e5f6a7b8-...", "realmId": "6d25623e-...", "path": "/treasury/usd-reserve", "type": "deposit", "state": "pending", "sourceArcaPath": null, "targetArcaPath": "/treasury/usd-reserve", "input": null, "outcome": null, "actorType": "builder", "actorId": "b1234...", "createdAt": "2026-02-11T21:12:52.529Z", "updatedAt": "2026-02-11T21:12:52.529Z" }}List Operations
GET/api/v1/operationsJWT / API Key
List operations in a realm. Optionally filter by type.
Query Parameters
realmIdstringrequiredtypestring"deposit").Response
{ "success": true, "data": { "operations": [ { "id": "...", "type": "deposit", "state": "completed", ... } ], "total": 5 }}Get Operation Detail
GET/api/v1/operations/:idJWT / API Key
Retrieve an operation with its correlated events and state deltas. This is the primary view for understanding what an operation did.
Path Parameters
idstringrequiredResponse
{ "success": true, "data": { "operation": { "id": "e5f6a7b8-...", "type": "deposit", "state": "completed", "actorType": "api_key", "actorId": "k-9876...", ... }, "events": [ { "id": "...", "type": "deposit.completed", ... } ], "deltas": [ { "id": "...", "deltaType": "balance_change", "beforeValue": "0", "afterValue": "1000.00", ... } ] }}Event Endpoints
Create Event
POST/api/v1/eventsJWT / API Key
Create a new event in a realm.
Request Body
realmIdstringrequiredtypestringrequired"deposit.completed").operationIdstringarcaPathstringpathstringpayloadstringResponse 201 Created
{ "success": true, "data": { "id": "f1e2d3c4-...", "realmId": "6d25623e-...", "operationId": "e5f6a7b8-...", "arcaPath": "/treasury/usd-reserve", "type": "deposit.completed", "path": null, "payload": "{\"amount\":\"1000.00\"}", "createdAt": "2026-02-11T21:12:52.529Z" }}List Events
GET/api/v1/eventsJWT / API Key
List events in a realm.
Query Parameters
realmIdstringrequiredResponse
{ "success": true, "data": { "events": [ { "id": "...", "type": "deposit.completed", ... } ], "total": 12 }}Get Event Detail
GET/api/v1/events/:idJWT / API Key
Retrieve an event with its correlated operation and state deltas.
Path Parameters
idstringrequiredResponse
{ "success": true, "data": { "event": { "id": "...", "type": "deposit.completed", ... }, "operation": { "id": "...", "type": "deposit", "state": "completed", ... }, "deltas": [ { "id": "...", "deltaType": "balance_change", ... } ] }}Deposit Endpoints
Initiate Deposit
POST/api/v1/depositJWT / API Key
Initiate a deposit to a denominated Arca object. This creates a pending deposit operation, then starts a Temporal workflow with a durable timer. In demo realms the deposit simulates asynchronously and completes (or fails) after the specified duration. On completion, a deposit.completed event is emitted with the balance_change delta. The durable timer survives service restarts — deposits are guaranteed to settle.
Request Body
realmIdstringrequiredarcaPathstringrequiredamountstringrequired"1000.00").pathstringdurationSecondsnumber5.willSucceedbooleantrue.Response 201 Created
{ "success": true, "data": { "operation": { "id": "e5f6a7b8-...", "type": "deposit", "state": "pending", ... }, "event": { "id": "f1e2d3c4-...", "type": "deposit.completed", "payload": "{\"amount\":\"1000.00\",\"arcaPath\":\"/treasury/usd-reserve\"}", ... } }}On-Chain Deposits (Real Money)
When on-chain custody is active, the deposit response also includes pool address information. The user sends tokens (USDC) directly to the pool address on-chain. The platform detects the transfer via a background ChainMonitor and automatically credits the Arca object after sufficient block confirmations.
The deposit amount is determined by what arrives on-chain, not by what the user declares. The amount field in the request is advisory only — the actual credited amount equals the on-chain transfer amount.
On-Chain Response Fields
poolAddressstringtokenAddressstringchainstring"anvil" for demo, "arbitrum" for production).expiresAtstringOn-Chain Deposit Flow
- Call
POST /depositto create a deposit intent - Response includes
poolAddress— send USDC to this address on-chain - The platform monitors the chain for incoming transfers
- When the transfer is detected, a confirmation workflow waits for N block confirmations
- After confirmation, the Arca object is credited with the actual on-chain amount
- A
deposit.confirmedevent is emitted
Demo: Simulate On-Chain Send
In demo realms (using the Anvil local chain), you can simulate an on-chain USDC transfer without a real wallet. The portal Deposit modal includes a Simulate On-Chain Send button that calls the internal endpoint below.
/api/v1/internal/custody-pool/simulate-depositInternalRequest Body
realmIdstringamountstringResponse
poolAddressstringamountstringtxHashstringchainstring"anvil").Transfer Endpoints
Execute Transfer
POST/api/v1/transferJWT / API Key
Atomically transfer balance between two denominated Arca objects. Both objects must share the same denomination and the source must have sufficient funds. The entire operation — balance reads, debit, credit, operation creation, event, and state deltas — is executed in a single Spanner read-write transaction.
Request Body
realmIdstringrequiredpathstringrequired"/op/transfer/alice-to-bob-1"). Must be unique per realm. If a transfer already exists at this path, the existing result is returned (idempotent).sourceArcaPathstringrequired"/alice/wallet").targetArcaPathstringrequired"/bob/wallet").amountstringrequired"250.00").Idempotency
The path field serves as an idempotency key. If a transfer operation already exists at the given path within the realm, the endpoint returns the existing operation and event without executing a second transfer. The path is enforced as unique per realm by a database index.
Response 201 Created
{ "success": true, "data": { "operation": { "id": "a1b2c3d4-...", "path": "/op/transfer/alice-to-bob-1", "type": "transfer", "state": "completed", "sourceArcaPath": "/alice/wallet", "targetArcaPath": "/bob/wallet", "input": "{\"amount\":\"250.00\",\"denomination\":\"USD\",\"sourceArcaPath\":\"/alice/wallet\",\"targetArcaPath\":\"/bob/wallet\"}", "outcome": "{\"status\":\"completed\",\"amount\":\"250.00\",\"denomination\":\"USD\",\"sourceNewBalance\":\"750.00\",\"targetNewBalance\":\"1250.00\"}", ... } }}The operation's outcome field contains the transfer result including the new balances for both source and target. No separate event is created — the operation reaching "completed" state is the terminal signal.
Atomicity Guarantees
The transfer executes inside a single Spanner read-write transaction. If any step fails (insufficient balance, object not found, denomination mismatch), the entire transaction aborts with no side-effects — no partial debits, no orphaned operations.
Error Cases
400VALIDATION_ERROR404NOT_FOUNDExchange Arca
Exchange Arca objects represent simulated perpetual futures exchange accounts. They are currently supported in demo realms only and use the Hyperliquid exchange simulation backend.
Creating an Exchange Arca
Create an exchange Arca using the standard object creation endpoint withtype: "exchange" and metadata containing exchangeType. The denomination is automatically set to USD.
POST /api/v1/objects{ "realmId": "...", "path": "/exchanges/my-hl", "type": "exchange", "denomination": "USD", "metadata": "{\"exchangeType\": \"hyperliquid\"}"}The system automatically creates a sim-exchange account and stores the simAccountId in the object's metadata.
Transfer Protocol
All transfers use the same POST /api/v1/transfer endpoint regardless of source and target types. The backend resolves sender and receiver behaviors based on the object types involved:
- Immediate settlement (e.g. denominated → denominated): Both sides settle atomically in a single transaction. The operation is created with state
completed. - Async settlement (e.g. denominated → exchange): Funds are debited from the source and placed into a reserved balance. The operation starts as
pendingand a background workflow delivers the funds to the receiver when it is ready.
To transfer into an exchange, simply use arca:ReceiveTo on the exchange path. There is no separate "fund exchange" permission or operation type. Reserved balances appear in the object detail response while the transfer is in flight.
Reserved Balances
The reservedBalances field on the object detail response shows in-flight amounts:
{ "id": "...", "arcaId": "...", "operationId": "...", "denomination": "USD", "amount": "1000.00", "status": "held", "createdAt": "...", "updatedAt": "..."}Status values: held (in-flight), released (settled),cancelled (operation failed).
Exchange Account State
GET /api/v1/objects/{id}/exchange/stateReturns account state including balance, margin summary, positions, and open orders.
Update Leverage
POST /api/v1/objects/{id}/exchange/leverage{ "coin": "BTC", "leverage": 10}Set the leverage for a coin. Leverage is a per-coin setting (not per-order), matching Hyperliquid's model. Changing leverage re-margins any existing position at the new leverage. If the account can't afford the increased margin (when decreasing leverage), the request is rejected. Increasing leverage always succeeds as it releases margin. Defaults to 1x if not set.
Get Leverage
GET /api/v1/objects/{id}/exchange/leverage # All coinsGET /api/v1/objects/{id}/exchange/leverage?coin=BTC # Single coinPlace Order (Operation)
POST /api/v1/objects/{id}/exchange/orders{ "realmId": "...", "path": "/op/order/btc-buy-1", "coin": "BTC", "side": "BUY", "orderType": "MARKET", "size": "0.001"}Placing an order is now a full operation with idempotency. The path field is the idempotency key — same path with same inputs returns the prior result. The response contains the operation record. Orders use the leverage currently set for the coin (via the Update Leverage endpoint above).
Supported fields: coin, side (BUY/SELL),orderType (MARKET/LIMIT), size, price(required for LIMIT), reduceOnly,timeInForce (GTC/IOC/ALO),builderFeeBps (tenths of a basis point, e.g. 45 = 4.5 bps — additive to exchange and platform fees; see System-Owned Objects for fee routing).
List Orders
GET /api/v1/objects/{id}/exchange/orders?status=openCancel Order
DELETE /api/v1/objects/{id}/exchange/orders/{orderId}List Positions
GET /api/v1/objects/{id}/exchange/positionsMarket Data (Global)
GET /api/v1/exchange/market/meta # Market metadata (perps universe)GET /api/v1/exchange/market/mids # Current mid pricesGET /api/v1/exchange/market/book/{coin} # L2 order bookExchange Permissions
| Action | Description |
|---|---|
arca:PlaceOrder | Place orders on an exchange Arca |
arca:CancelOrder | Cancel orders on an exchange Arca |
arca:ReadExchange | Read exchange state, orders, positions |
arca:Exchange | Alias: PlaceOrder + CancelOrder + ReadExchange |
Nonce Endpoints
Get Next Nonce
POST/api/v1/nonceJWT / API Key
Return the next unused nonce for a given prefix within a realm. Every call atomically increments the counter, whether or not the caller uses the resulting path. This provides a safe, unique number for constructing idempotent operation or object paths.
Request Body
realmIdstringrequiredprefixstringrequiredseparator field (see below).separatorstring/ if the prefix ends with /, otherwise -. Use ":" for operation nonces (e.g., /op/create/wallets/main:1).Response 200 OK
{ "success": true, "data": { "nonce": 1, "path": "/op/create/wallets/main:1" }}Behavior
The counter is realm-scoped and prefix-scoped. Two different prefixes in the same realm maintain independent counters. The counter starts at 1 on first use and increments by 1 on every call. The increment is atomic (Spanner read-write transaction), so concurrent callers are guaranteed unique nonces.
Nonce Best Practices
Nonces are the building block for idempotent, auditable paths in Arca. Follow these conventions so every builder on the platform produces consistent, collision-free paths.
Separator Conventions
| Context | Separator | Example Prefix | Result |
|---|---|---|---|
| Operation nonces | : (colon) | /op/create/wallets/main | /op/create/wallets/main:1 |
| Object name nonces | - (hyphen, default) | /orders/order | /orders/order-47 |
| Child path nonces | (none) — prefix ends with / | /wallets/ | /wallets/3 |
When to Use Nonces
- Operations that may be retried — transfers, deposits, and any create call that should be safely re-submittable.
- Delete + recreate scenarios — because an Arca path can be reused after deletion, the create operation path must include a nonce to remain unique. Pass the resulting path as
operationPathonPOST /api/v1/objects. - Auto-named objects — use a trailing-slash prefix (e.g.,
/wallets/) to generate sequential child paths.
Recommended Pattern: Create with Nonce
// 1. Reserve a nonce for the create operation (colon separator)const { path: opPath } = await arca.nonce('/op/create/wallets/main', ':');// → { nonce: 1, path: "/op/create/wallets/main:1" }
// 2. Store opPath, then use it for the operationconst { object } = await arca.createDenominatedArca({ ref: '/wallets/main', denomination: 'USD', operationPath: opPath,});// Safe to retry — same operationPath returns the same result.Idempotency Guarantee
The operation path is the unique-per-realm idempotency key. Reusing the same operation path on a subsequent call is a safe retry and returns the original result. A new nonce produces a new, independent operation. The Arca object path alone also provides idempotency for the common case where no delete/recreate is expected — if an active object already exists at the path with matching type and denomination, it is returned without creating a duplicate.
Common Anti-Pattern: Inline Nonce
Every call to POST /api/v1/nonce atomically increments the counter and returns a new, unique path. If you call it inline — inside a retry loop or as part of the operation request itself — each attempt gets a different path, so every retry creates a distinct operation instead of safely replaying the original one.
Correct pattern: Reserve the nonce once, store the resulting path (in a variable, database, or message queue), then use that stored path for the operation. If the operation fails transiently, retry with the same path.
State Delta Endpoints
List State Deltas
GET/api/v1/deltasJWT / API Key
List state deltas for a specific Arca path. Deltas capture before/after values for every state change.
Query Parameters
realmIdstringrequiredarcaPathstringrequiredResponse
{ "success": true, "data": { "deltas": [ { "id": "d1e2f3a4-...", "realmId": "6d25623e-...", "operationId": "e5f6a7b8-...", "eventId": "f1e2d3c4-...", "arcaPath": "/treasury/usd-reserve", "deltaType": "balance_change", "beforeValue": "0", "afterValue": "1000.00", "createdAt": "2026-02-11T21:12:52.529Z" } ], "total": 1 }}Real-time Streaming
Event Stream (SSE)
GET/api/v1/streamToken (query param)
Subscribe to a real-time stream of realm events via Server-Sent Events. Because the browser EventSource API cannot send custom headers, the JWT token is passed as a query parameter instead of in the Authorization header.
Query Parameters
realmIdstringrequiredtokenstringrequiredConnection Example
const token = "eyJhbGciOiJIUzI1NiIs...";const realmId = "6d25623e-...";const url = `/api/v1/stream?realmId=${realmId}&token=${token}`;
const es = new EventSource(url);
es.addEventListener("connected", (e) => { console.log("Connected:", JSON.parse(e.data));});
// Listen for specific event typeses.addEventListener("deposit.completed", (e) => { const event = JSON.parse(e.data); console.log("Deposit completed:", event);});
es.addEventListener("transfer.completed", (e) => { const event = JSON.parse(e.data); console.log("Transfer completed:", event);});Event Format
On connection, a connected event is sent as a heartbeat. Subsequent events match the event types created in the realm (e.g., deposit.completed, transfer.completed, order.filled). Each SSE event's data field contains the full serialized event object.
Summary
Get Summary
GET/api/v1/summaryJWT / API Key
Get aggregate counts for objects, operations, and events in a realm. Useful for dashboard widgets.
Query Parameters
realmIdstringrequiredResponse
{ "success": true, "data": { "objectCount": 12, "operationCount": 47, "eventCount": 93 }}Get Reconciliation State
GET/api/v1/reconciliationJWT / API Key
Returns platform-expected versus venue-reported balances/positions for exchange objects in a realm.
Agent (AI)
The Agent endpoint provides a conversational AI assistant that can inspect your realm's current state (objects, balances, operations, exchange positions) and propose executable multi-step plans. The agent uses read-only tools to gather context and never mutates state directly — all proposed operations are returned as structured plans for the client to execute after user approval.
Agent Status
GET/api/v1/agent/statusJWT / API Key
Returns whether the agent feature is enabled (requires AGENT_ANTHROPIC_API_KEY environment variable).
{ "data": { "enabled": true } }Chat
POST/api/v1/agent/chatJWT / API Key
Send a conversation to the agent. Returns a text/event-stream (SSE) response with the following event types:
text— Streamed text content from the assistanttool_use— Shows which tool the agent is calling (e.g. browsing objects, checking balances)plan— A structured plan with executable stepsdone— Stream complete
Request Body
{ "realmId": "uuid", "messages": [ { "role": "user", "content": "Transfer 500 from user-2/main to their exchange and open a 10x BTC long" } ]}Plan Step Format
Each step in a proposed plan contains:
{ "id": "step-1", "description": "Transfer 500 USD from /user-2/main to /user-2/exchange", "operation": "transfer", "params": { "from": "/user-2/main", "to": "/user-2/exchange", "amount": "500" }, "dependsOn": []}Supported operations: transfer, deposit, createObject,deleteObject, placeOrder, cancelOrder, setLeverage.
Org & Team Endpoints
Manage your organization, team members, and invitations. All endpoints require JWT authentication and check account-level permissions.
Get Organization
GET/api/v1/orgJWT
Returns the current user's organization.
{ "success": true, "data": { "id": "org-uuid", "name": "Acme Inc", "slug": "acme-inc", "createdAt": "2026-02-22T00:00:00Z" }}List Members
GET/api/v1/org/membersJWT
Lists all members in the organization with their roles and realm type scopes.
{ "success": true, "data": { "members": [ { "id": "member-id", "userId": "user-uuid", "email": "alice@example.com", "memberType": "user", "role": "admin", "realmTypeScope": ["production", "staging"], "createdAt": "2026-02-22T00:00:00Z" } ], "total": 1 }}Invite Member
POST/api/v1/org/invitationsJWT
Send an invitation email. Requires account:ManageMembers permission. The inviter cannot assign a role higher than their own.
Request Body
{ "email": "bob@example.com", "role": "developer", "realmTypeScope": ["development", "testing"]}Update Member
PATCH/api/v1/org/membersJWT
Update a member's role or realm type scope. Privilege escalation protection applies.
Request Body
{ "memberId": "member-id", "role": "admin", "realmTypeScope": null}Remove Member
DELETE/api/v1/org/membersJWT
Remove a member from the organization. Cannot remove the owner.
Query Parameters
memberIdstringrequiredTransfer Ownership
POST/api/v1/org/transfer-ownershipJWT
Transfer organization ownership to another member. Only the current owner can do this.
Request Body
{ "newOwnerUserId": "user-uuid" }