Skip to main content
Read this once before you write any code. Everything in the integration journey assumes the conventions below.

Base URLs

EnvironmentBase URL
Sandboxhttps://sandbox.api.bluumfinance.com/v1
Productionhttps://api.bluumfinance.com/v1
All endpoints are under the /v1 prefix. You receive separate credentials for each environment.

Authentication

Every request uses HTTP Basic authentication. Your API Key is the username, your API Secret is the password.
  1. Concatenate them with a colon: API_KEY:API_SECRET
  2. Base64-encode the result
  3. Send it as Authorization: Basic <encoded>
curl "https://sandbox.api.bluumfinance.com/v1/assets" \
  -H "Authorization: Basic $(echo -n 'YOUR_API_KEY:YOUR_API_SECRET' | base64)"
The API Secret is shown only once when created. Store it in a secret manager immediately. If lost, generate a new key pair. Never commit credentials or log them in plaintext.
Get credentials in Create a sandbox account.

Object IDs

IDs are opaque, prefixed strings — not raw UUIDs. The prefix tells you the resource type: inv_ investor, fs_ funding source, dep_ deposit, wd_ withdrawal, ord_ order, pos_ position, doc_ document, wh_ webhook, apk_ API key. Pass IDs back exactly as returned; raw UUIDs are rejected on the external surface.

Response shape

Every resource carries a small envelope followed by domain fields:
{
  "id": "inv_01j9x8m2k7qpzwv3t5r6y8n0ab",
  "object": "investor",
  "created": 1718455800,
  "livemode": false,
  "metadata": {},
  "...": "domain fields"
}

Pagination

List endpoints accept limit and offset query parameters and return a list envelope:
{
  "object": "list",
  "url": "/v1/investors/inv_.../orders",
  "has_more": false,
  "data": [ /* resources */ ]
}
Page with offset. limit defaults per endpoint (commonly 50, max 200).

Idempotency

Retrying a request must not move money twice. Send an Idempotency-Key header on state-changing requests — required on deposits and withdrawals, recommended on orders.
curl -X POST "$BASE_URL/investors/$INVESTOR_ID/deposits" \
  -H "Authorization: Basic $AUTH" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: dep-$(uuidgen)" \
  -d '{ "amount": "5000.00", "currency": "USD", "method": "ach", "funding_source_id": "fs_01j9x8m2k7qpzwv3t5r6y8n0ef" }'
ScenarioResult
First request with a keyProcessed; response stored
Retry, same key + bodyOriginal response returned — not reprocessed
Retry, same key + different bodyError — the key is bound to the first request
Generate a fresh key per distinct operation (e.g. dep-<uuid>). Keys expire after 24 hours.

Rate limits

Rate limiting is a stated platform policy. Build for it now so you don’t have to retrofit later.
EnvironmentLimit
Sandbox10 requests/second per key pair
Production25 requests/second per key pair
On 429, back off and retry with jitter; honor the Retry-After header when present.

Errors

Errors return a consistent envelope with a structured BLUM-{HTTP_STATUS}-{SEQUENCE} code:
{
  "error": {
    "type": "invalid_request_error",
    "code": "BLUM-400-002",
    "message": "Required field 'symbol' is missing.",
    "param": "symbol"
  }
}
StatusRetryableMeaning
400 / 422NoValidation or business-rule failure — fix the request
401 / 403NoAuth or permission problem — check credentials and product entitlements
404NoResource doesn’t exist or isn’t yours
409NoConflict (e.g. duplicate)
429 / 5xxYesBack off with jitter and retry
Add-on products (Market Data, Cash Management, Wealth) return 403 with a PRODUCT_NOT_ENABLED code when not enabled for your tenant. See the full catalog in Error Codes.

Versioning

The API is versioned in the path (/v1). Backwards-compatible changes (new endpoints, new optional fields, new enum values) ship without a version bump. Breaking changes ship under a new prefix and are announced in the Changelog.

Request tracing

Every response includes an X-Request-Id header. Log it — include it in support requests to let Bluum trace a specific call. Next: Create a sandbox account.