InterposeInvest

Sandbox environment. Interpose is not a registered broker-dealer and holds no FINRA approval. Every API documented here is available in a simulated evaluation environment only — no client assets are held, no real trades are executed, and no custody is provided. The calculation engines are the production code paths; fills, custody, settlement and KYC decisioning are simulated. What is real and what is simulated →

From nothing to a real response.

Signing up provisions a real tenant — your own firm, branch, rep code and advisor login — not a shared demo account. Your data is isolated by the same rep-code scoping that runs for every firm on the platform, and the tenant arrives with a book already in it, so your first call returns rows rather than an empty list.

Three steps, about ten minutes: provision a tenant, exchange the login for a token, call the API. A fourth is there when you are ready to point a server at it — machine credentials are self-serve from the same login. If you decide to go ahead, the firm you explored becomes your live one — models, configuration and fee schedules intact.

1. Provision a tenant

POST/api/v1/auth/sandbox/signup
request
curl -X POST https://interposehq.com/api/v1/auth/sandbox/signup \
  -H 'Content-Type: application/json' \
  -d '{
        "email":   "you@yourfirm.com",
        "name":    "Your Name",
        "company": "Your Firm LLC"
      }'
FieldRequiredNotes
emailRequiredDisposable and temp-mail domains are rejected with a 422. Free-mail providers are accepted.
nameRequiredBecomes the advisor name on your rep code.
companyOptionalYour firm's display name. Defaults to name.

You get one of two successful 201 responses depending on whether email delivery is configured in the environment you hit. Either the tenant is provisioned immediately and the credentials are in the body, or the response carries status: "PENDING_VERIFICATION" and you confirm by clicking the emailed link or posting the token to /api/v1/auth/sandbox/verify.

provisioned immediately — 201
{
  "signup_id":       "sbx_01J8Z...",
  "status":          "ACTIVE",
  "firm_code":       "SBX7",
  "rep_code":        "SBX7R01",
  "expires_at":      "2026-09-21T18:04:11.402913+00:00",
  "portal_url":      "https://app.interposehq.com",
  "portal_email":    "you@yourfirm.com",
  "portal_password": "8Kd2m-QpvR7x",
  "docs_url":        "https://interposehq.com/developers/sandbox"
}

The password is shown once and is not recoverable

Only a bcrypt hash is stored. Signing up again with the same address returns 409 deliberately — otherwise anyone who knew your email address could take over your tenant.

Errors you may hit

StatusMeaningWhat to do
422Malformed email, or a blocked disposable domainUse a real, deliverable address
409A sandbox is already active for this addressSign in, or email support to reset
403This sandbox was suspendedEmail support
503Either the 25-tenant ceiling, or the edge rate limiter. A JSON body is the ceiling; an HTML body is the limiterBack off if HTML; email support if JSON

2. Exchange the login for a token

POST/api/v1/auth/login
request
curl -X POST https://interposehq.com/api/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"email": "you@yourfirm.com", "password": "8Kd2m-QpvR7x"}'

The response carries an access_token — an HS256 JWT valid for 8 hours — alongside a user object with your role, firm and rep code. The token carries those claims, and every subsequent read is scoped from them rather than from any resource id you pass.

Cache the token

All /api/v1/auth/* paths are limited to 10 requests per minute per IP. Logging in per request will rate-limit you within seconds — and the proxy answers with a 503, not a 429. An unknown email and a wrong password return the identical 401 body, deliberately.

3. Make your first call

Your tenant arrives with a book already in it: 16 clients, 32 accounts on native custody across six account types, and 4 portfolios. Accounts are funded on a ladder from $75,000 to $4.8M — deliberately not a flat figure, so AUM tiering and fee-schedule boundaries have something to bite on — and the cash is invested by a real rebalancing run, so you get positions, orders and tax lots rather than an all-cash book. Seeding runs behind the signup response, not inside it. If you call within a few seconds of signing up you may catch a partially built book; poll until the count settles.

list your accounts
TOKEN='eyJhbGciOiJIUzI1NiIs...'

curl -H "Authorization: Bearer $TOKEN" \
  'https://interposehq.com/api/v1/baas/accounts?page=1&page_size=25'
response (abridged — the full object carries about 30 fields)
{
  "items": [
    {
      "account_id":     "acct_01HV4Y2K3M5N8P9QR2ST4UVWXY",
      "account_type":   "INDIVIDUAL",
      "account_name":   "Amara Okonkwo — Individual",
      "status":         "ACTIVE",
      "cash_balance":   "18450.7300",
      "buying_power":   "18450.7300",
      "base_currency":  "USD",
      "custodian":      "NATIVE"
    }
  ],
  "total": 24,
  "page": 1,
  "page_size": 25
}

Two field names catch people out immediately: it is account_type, not type, and an account's currency is base_currency currency is the field on transactional objects like orders, not on the account. Note "18450.7300": a string, four decimal places. Every monetary value on the platform is a string, and that is not stylistic.

A call that needs no token at all

Market data is public, which makes it the fastest way to prove your networking works:

no Authorization header required
curl https://interposehq.com/api/v1/baas/market/quotes/AAPL

Portfolios and drift

Drift is computed per portfolio against its model. On a freshly seeded book it comes back near zero on every line, because the seeder invested the cash by running the real rebalancer against those same models. To watch drift open up, move a model's weights or trade against a portfolio and read it again.

GET/api/v1/pm/portfolios
GET/api/v1/pm/portfolios/{portfolio_id}/drift

Placing an order

Note the typed security identifier — there is no bare symbol field anywhere on the trading surface, and the field is type, not order_type:

request
curl -X POST https://interposehq.com/api/v1/baas/orders \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
        "account_id": "acct_01HV4Y2K3M5N8P9QR2ST4UVWXY",
        "security":   {"type": "ticker", "value": "AAPL"},
        "side":       "BUY",
        "type":       "MARKET",
        "quantity":   "10.00000000"
      }'

The account must be ACTIVE. A PENDING account returns a 422 whose detail is an object naming the failed check — a shape worth handling, because it is how every pre-trade compliance rule reports itself:

422 — a business-rule rejection
{
  "detail": {
    "message": "Order rejected by compliance checks.",
    "failed_checks": [
      { "check":  "account_active",
        "reason": "Account acct_01HV4Y... is PENDING; must be ACTIVE to accept orders." }
    ]
  }
}

In your sandbox this order routes to a local FIX 4.2 simulator. A sandbox account cannot dispatch to an external custodian — the gate lives in the order router and keys off the account's own flag, not an environment variable a deploy could flip.

4. Issue a key for your server

The portal token works everywhere, but it expires in eight hours and belongs to a person. For a server, mint a key from that login and let it fetch its own tokens. The key is scoped to the firm and rep code of the login that created it, so it can never see more than you can — supplying firm_id or allowed_rep_codes in the body is a 422, not a silent ignore.

POST/api/v1/baas/api-keys/self
issue a key
curl -X POST https://interposehq.com/api/v1/baas/api-keys/self \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"name": "evaluation-worker", "permission_group": "read_only"}'
exchange it — form-encoded, not JSON
curl -X POST https://interposehq.com/api/v1/baas/oauth/token \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=client_credentials' \
  -d 'client_id=key_01J8Z...' \
  -d 'client_secret=ik_live_...'

key_secret comes back exactly once and is not recoverable. Portfolio Management issues keys on the same path under /api/v1/pm/api-keys/self, and a key on one pillar is not a credential on the other. The permission groups, the ten-key ceiling per firm and the revoke path are under Authentication.

Current limits

what is not built yet

The path above works end to end today. Two things to know before you build on it.

  • A key's permission group is recorded, not enforced per route

    A self-serve key is scoped to your firm and rep codes, and that isolation is real. The permission_group you choose is not yet checked per route, so a read_only key is not prevented from writing. See Authentication.

  • Your book has positions, but no history behind them

    The seeder funds 32 accounts and invests them, so positions, orders and tax lots are all there from the start. What it does not do is backdate anything — every lot opens on your provisioning date. Performance, attribution and realised-gain reporting therefore have no series behind them until time passes or you generate activity. Ask us if you need a book with history for your evaluation.