Sandbox Environment
The Sarna sandbox is a mock API that returns static, deterministic responses for all documented endpoints. Use it to build and test integrations without touching production data.
Overview
- Deterministic responses: Same input always produces the same output
- Bearer token auth: Simple
Authorization: Bearer <token>— no HMAC signing - Two-tier access: Standard tokens for trading/market data, admin tokens for
/v1/admin/* - 148 endpoints: Golden workflow endpoints return full data; remaining return placeholder responses
- Rate limit: 100 requests/minute per token
Credentials
| Token | Access Level | Endpoints |
|---|---|---|
| sandbox_test_xxxx12345 | Standard | All /v1/* except /v1/admin/* |
| sandbox_admin_xxxx67890 | Admin | All endpoints including /v1/admin/* |
Standard tokens accessing admin endpoints receive a 403 Forbidden with upgrade guidance.
Getting Started
1. Set your base URL and token
BASE_URL=https://sarna-sandbox-api.sarna.workers.dev TOKEN=sandbox_test_xxxx12345
2. Make your first request
curl -H "Authorization: Bearer $TOKEN" $BASE_URL/v1/quotes/AAPL
3. Check the response
{
"Errors": [],
"Warnings": [],
"HasData": true,
"Quote": {
"Symbol": "AAPL",
"CompanyName": "Apple Inc.",
"LastPrice": 187.50,
"BidPrice": 187.48,
"AskPrice": 187.52,
"Volume": 52340000,
"Timestamp": "2026-03-28T15:59:59Z"
}
} Golden Workflow: Quote → Order → Fill → Positions
The canonical trading workflow in four steps:
Step 1: Get a Quote
curl -H "Authorization: Bearer $TOKEN" \ $BASE_URL/v1/quotes/AAPL
Returns current bid/ask/last price for the symbol.
Step 2: Place an Order
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"Symbol":"AAPL","Side":"Buy","Quantity":100,"AccountId":"500123"}' \
$BASE_URL/v1/orders
Returns a new order with status Pending.
Note: LimitPrice is negative for buys (debit = money out).
Step 3: Check Order Status
curl -H "Authorization: Bearer $TOKEN" \ $BASE_URL/v1/orders/ORD-001
Dynamic orders (from Step 2) return as Filled on subsequent GET.
Step 4: Verify Positions
curl -H "Authorization: Bearer $TOKEN" \ $BASE_URL/v1/accounts/500123/positions
Returns all positions for the account with market values.
Test Data
Accounts
| Account ID | Type | Description |
|---|---|---|
| 500123 | Individual | Primary test account (used in golden workflow) |
| 500124 | Joint | Joint account with TSLA and AMZN positions |
| 500125 | IRA | Retirement account (restricted trading) |
| 500199 | Institutional | Admin test account |
Available Symbols
| Symbol | Company | Last Price | Bid | Ask |
|---|---|---|---|---|
| AAPL | Apple Inc. | $187.50 | $187.48 | $187.52 |
| GOOGL | Alphabet Inc. | $141.80 | $141.78 | $141.82 |
| MSFT | Microsoft Corp. | $378.25 | $378.20 | $378.30 |
| TSLA | Tesla Inc. | $248.90 | $248.85 | $248.95 |
| AMZN | Amazon.com Inc. | $178.35 | $178.30 | $178.40 |
Pre-loaded Orders (Account 500123)
| Order ID | Symbol | Side | Qty | Status | Price |
|---|---|---|---|---|---|
| ORD-001 | AAPL | Buy | 100 | Filled | -$187.50 |
| ORD-002 | GOOGL | Buy | 50 | Filled | -$141.80 |
| ORD-003 | MSFT | Sell | 25 | Pending | $378.25 |
Prices follow sign conventions: negative = debit (buy), positive = credit (sell).
Behavior Notes
- Stateful orders: Orders created via POST return as
Filledon subsequent GET - State resets: Dynamically created orders expire after 24 hours. Pre-loaded data is always available.
- Rate limiting: 100 requests/minute per token. Returns
429withRetry-Afterheader. - Unknown accounts: Return
403 Forbidden(not 404) for security. - Placeholder endpoints: Unimplemented endpoints return
200withHasData: falseand a warning. - PascalCase fields: All response fields use PascalCase (
AccountId, notaccountId). - UTC timestamps: All timestamps are UTC in ISO 8601 format.
Common Errors
| Status | Code | Cause | Fix |
|---|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid token | Add Authorization: Bearer sandbox_test_xxxx12345 |
| 403 | FORBIDDEN | Standard token on admin endpoint | Use sandbox_admin_xxxx67890 |
| 404 | NOT_FOUND | Unknown symbol or order ID | Check available symbols/orders in test data above |
| 400 | INVALID_REQUEST | Missing required fields | Check error message for required fields |
| 429 | RATE_LIMITED | Too many requests | Wait 60 seconds, use exponential backoff |
Resources
- API Reference — Full endpoint documentation
- Agent Quickstart — Copy-paste snippets for AI coding assistants
- Human Quickstart — 5-minute getting started guide
- SDKs — Python, TypeScript, Go, Java client libraries
- Test Data (JSON) — Machine-readable test data catalog