Fundamentals

API conventions

A handful of patterns hold across every endpoint in the API. Learn them once here and each individual resource in the reference will read the same way — the same auth, the same money format, the same timestamps, the same list shape.

Authentication

Every request carries an OAuth 2.0 bearer token in the Authorization header. Tokens are short-lived and workspace-scoped. See the Authentication guide for the token exchange and refresh flow.

every request
Authorization: Bearer $VB_TOKEN

Retrying a write

The create endpoints — POST /v1/orders, POST /v1/deposits, and POST /v1/withdrawalsdo not currently de-duplicate retries. Each accepted request creates a new resource, and its id is always generated by VirtuaBroker. There is no request header that changes this; a key you supply is ignored.

So a retry after a timeout or a dropped connection can create a second order, deposit or withdrawal — the first request may well have succeeded even though you never saw the response.

Do not blind-retry a write. If a create times out, reconcile before resending: list the resource filtered by userEmail plus a from/to window (and currency), and check whether your operation already exists. Resend only if it does not.

GET /v1/deposits
# after a timed-out create, check before resending
curl "https://api.virtuabroker.com/v1/deposits?userEmail=payer@example.com&from=1780000000&to=1780003600&currency=EUR" \
  -H "Authorization: Bearer $VB_TOKEN"

On POST /v1/orders only, set externalReference on the create to carry your own order number. Order reads return it as externalRefeference — note the spelling — so that is the field to match on when you reconcile an order.

POST /v1/deposits and POST /v1/withdrawals do not accept an externalReference; it is ignored if sent. Reconcile those on userEmail plus the time window, currency and amount.

A true idempotency contract — a key that makes a retry return the original result — is in development. It will be documented here when it ships. Until then, reconcile-before-resend is the supported pattern.

Amounts & currencies

Monetary amounts are JSON numbers, not strings — sending an amount as a string fails validation. Currencies are ISO 4217 alphabetic codes (EUR, BRL, USD, …). Every amount is paired with the currency it is denominated in.

example
{
  "originAmount": 1000,
  "originCurrency": "EUR",
  "destinationAmount": 6098.2,
  "destinationCurrency": "BRL"
}

Rails & methods

Payin methods, payout methods, and rails are identifiers you discover per currency rather than hard-code. Fetch the accepted shapes with GET /v1/currencies/{currency}/payin-schema and GET /v1/currencies/{currency}/destination-schema — the API reference documents both.

Public rail names such as SEPA, PIX, and SPEI appear in requests and responses where they identify the rail. The API never exposes the internal providers that clear a given rail.

SEPA PIX SPEI

Timestamps

Timestamps are numeric Unix epoch values, not ISO strings. A quote's expireAt is Unix seconds; resource timestamps such as createdAt and updatedAt are numeric as well, and the list filters from and to take Unix values. Parse them as instants; do not assume a local timezone.

example
{
  "expireAt": 1694291200
}

Pagination

List endpoints accept limit and page query parameters and return a bare JSON array — there is no wrapper object and no total in the response.

GET /v1/orders?limit=20&page=1
[
  { /* order */ },
  { /* … up to 20 orders … */ }
]

Totals come from separate count endpoints: GET /v1/orders/count and GET /v1/deposits/count, each returning { "count": … }. There is no count endpoint for withdrawals.

GET /v1/orders/count
{
  "count": 137
}

Lists also accept filters: currency, minAmount, maxAmount, from (Unix), to (Unix), userEmail, search, limit, and page — plus status on the order, deposit, and withdrawal list endpoints.

Rate limits

Read endpoints (GET) are throttled, not rejected: past a threshold within the window, responses gain added latency. No error is returned — there is no 429.

Write endpoints — POST /v1/orders, POST /v1/deposits, and POST /v1/withdrawals — are limited per workspace, at roughly 300 requests per minute. Exceeding the limit returns HTTP 503 with OperationError, and responses carry the standard RateLimit-* headers. There is no Retry-After header and no X-RateLimit-* headers.

The API never returns 429. Handle the write limit by treating a 503 OperationError as retryable — back off and resend. A rate-limited request was rejected before it created anything, so resending is safe; a request that timed out is a different case — see Retrying a write.

Response envelope

Orders, deposits, and withdrawals return the resource object directly — and a bare array for lists. Newer resources — currencies, pending payments, banking accounts — wrap their result in an envelope of the form { "ok": true, "value": … }. The two shapes coexist, so follow the documented response for each endpoint in the reference rather than assuming one form everywhere.

enveloped response
{
  "ok": true,
  "value": [ /* … currencies … */ ]
}

Check each endpoint's documented response in the API reference to know whether it is enveloped or returns the object directly.

← Prev
Statuses