_Docs/
Get StartedModulesPlatformDeployCookbookChangelogReference
_Stack
_Modules
  • Ledger
  • Numscript
  • Connectivity
    • Capabilities
    • Operations
    • Accounts
    • Payments
    • Orders
    • Conversions
    • Payment Initiation
    • Account Pools
    • Payment Service Users
    • Connectors
      • Generic Connector
        • Getting Started
        • How it Works
      • PSP Connectors
        • Adyen
        • Atlar
        • Banking Circle
        • Column
        • Currencycloud
        • Increase
        • Mangopay
        • Modulr
        • Moneycorp
        • Qonto
        • Stripe
        • Wise
        • Banking BridgeEE
        • RoutableEE
      • Exchange Connectors
        • Coinbase PrimeEE
        • FireblocksEE
        • BitstampEE
      • Open Banking
        • Getting Started with Open Banking
        • Plaid
        • Tink
        • Powens
      • Build a connector
  • WalletsEE
  • FlowsEE
  • ReconciliationEE
  1. Modules
  2. Connectivity
  3. Connectors
  4. Exchange Connectors
  5. Bitstamp
Bitstamp
Exchange Connectors

Bitstamp

Connect a Bitstamp account to Formance Connectivity to sync wallets, balances, payments, orders, and conversions.

The Bitstamp connector polls a Bitstamp account and surfaces currency wallets, balances, payments, trading orders, and conversions. It is read-only and spot-only.

Bitstamp API keys scope to a single account — Main or one named sub-account — so install one connector instance per Bitstamp account you need to reconcile.

Prerequisites#

You need a Bitstamp account and an API key with the minimum permissions for the capabilities you use. Bitstamp uses HMAC-SHA256 v2 signing; the connector signs internally — you only supply the key and secret.

Make sure to create an API key dedicated to Formance. Doing so will improve your auditability and security and will allow you to revoke access to Formance at any time if needed.

Installation#

curl -X POST $FORMANCE_API_URL/api/payments/v3/connectors/install/bitstamp \
  -H "Content-Type: application/json" \
  -d @config.json
POST/api/payments/v3/connectors/install/bitstamp

With config.json containing:

JSON
{
  "apiKey": "string",
  "apiSecret": "string",
  "endpoint": "https://www.bitstamp.net",
  "name": "string",
  "pollingPeriod": "30m"
}

Configuration fields#

FieldRequiredDefaultDescription
apiKeyyes—Bitstamp API key. Sent in X-Auth as BITSTAMP <apiKey>.
apiSecretyes—HMAC-SHA256 signing secret. Never logged.
endpointnohttps://www.bitstamp.netAPI root. Override only for non-production.
nameyes—Unique name for this connector instance (e.g. bitstamp-main, bitstamp-treasury when running one per scope).
pollingPeriodno30mSync cadence (min 20m). Drives every capability.

The config is deliberately minimal — the API key scopes the connection, and Bitstamp's API exposes no portable way to fan out across scopes.

Capabilities#

  • FetchAccounts — currency wallets in scope via POST /api/v2/account_balances/.
  • FetchBalances — re-reads account_balances/ per cycle.
  • FetchPayments — user_transactions/ on a single since_id watermark.
  • FetchOrders — open-orders snapshot reconciled against order_status/ per tracked id.
  • FetchConversions — user_transactions/ rows with type=36 (instant buy/sell).

Payouts, transfers, webhooks, and bank-account creation are not implemented; Bitstamp's API surface for those flows is uneven.

Account model#

Every Connectivity internal account is one currency in the Bitstamp scope — one account per (connector install, currency). The reference is the currency ticker (USD, EUR, BTC); the connector-level name (e.g. bitstamp-main) disambiguates the scope. defaultAsset is TICKER/precision from the currencies cache. No EXTERNAL accounts are emitted. See Accounts for the cross-connector model.

Bitstamp returns every currency the account could hold. Rows with Available, Total, and Reserved all zero are skipped — emitting hundreds of empty accounts pollutes the catalogue without informing anyone.

Bitstamp doesn't expose per-currency creation dates, so CreatedAt defaults to BitstampGenesis = 2011-08-02 UTC (the platform's launch). The sentinel is stable across reinstalls.

Asset model#

The canonical asset is the uppercased currency ticker with precision suffix from the currencies cache — USD/2, EUR/2, BTC/8, USDT/6. The cache loads at install and refreshes on a TTL; assets not in the cache are logged and skipped rather than emitted with a guessed precision.

Workflow tree#

FetchAccounts (periodic)
  └── FetchOrders (periodic, derives tracked markets from accounts)

FetchBalances    (periodic root)
FetchPayments    (periodic root)
FetchConversions (periodic root)

FetchOrders nests under FetchAccounts because it derives tradeable markets from account metadata. Balances, Payments, and Conversions are independent roots — their Bitstamp endpoints are account-global at the API-key level, so no parent context is needed.

Payments#

A Payment is one row from user_transactions/, polled on a single inclusive since_id watermark. Trade rows (type=2) feed Orders; instant-buy/sell rows (type=36) feed Conversions; everything else (deposits, withdrawals, settled activity, sub-account transfer legs of types 14 / 33 / 35) lands as a Payment.

The watermark is inclusive — the last row of cycle N reappears as the first of N+1, deduped downstream by PSPPayment.Reference. End-of-pagination keeps the watermark; we never reset.

Sub-account transfer rows (types 14 / 33 / 35) are mapped defensively — signed PAY-IN / PAYOUT legs sharing a transfer_pair_id. A Main-account API key does not actually surface them on user_transactions/. Customers needing transfer reconciliation install one connector per sub-account; the pair-id correlation works once both legs' keys are integrated.

Orders#

Bitstamp doesn't expose an "orders since X" endpoint. The connector reconciles a live snapshot every cycle:

  1. GetOpenOrders returns currently-open orders.
  2. New IDs are seeded into trackedOrders state with their first-sight LimitPrice.
  3. GetOrderStatus is called per id (snapshot ∪ tracked) for fills, fees, datetime, and market.
  4. The order maps to a PSPOrder with adjustments aggregating each observed state change.
  5. Tracked entries drop on terminal status (FILLED / CANCELLED).
  6. Tracked entries also drop after FirstSeenAt + 25d, emitting com.bitstamp.spec/retention_expired = true. Bitstamp retains order_status/ rows for 30 days; the 5-day margin avoids losing the terminal state.

Trade primitives in user_transactions/ (type=2 rows with a parent order_id) aggregate under their parent rather than being emitted as standalone Orders — one PSPOrder per Bitstamp order, fills attached.

Conversions#

user_transactions/ returns two primitives that both look like "buys" and "sells" in the web UI:

WireHas order_id?LifecycleFormance model
type=2 (Trade — order fill)yesorder-book — In Queue → Open → Finished / CancelledPSPOrder
type=36 (Instant buy/sell)noatomic — settled in one round-tripPSPConversion

Conversions share the user_transactions/ stream with payments but hold their own watermark — the two cursors advance independently. Asset class plays no role in classification: Bitstamp tags every crypto (BTC, USDC, EURC, …) as currency.type = "crypto" with no stablecoin tag. A type=36 BTC↔EUR row and a type=36 USDC↔EUR row are the same primitive; consumers wanting "market exposure" vs "stable-value swap" semantics apply their own allow-list against SourceAsset / DestinationAsset.

Install-time enrichment#

Four reference datasets load in parallel at install, refreshed via TTL cache:

  • markets — every trading pair, used to resolve order quote/base currencies.
  • my_markets — pairs the key has actually traded (gates Order details).
  • fees/trading — per-market trading fees, surfaced on Order metadata.
  • fees/withdrawal — per-currency withdrawal fees, surfaced on withdrawal-request payments.

Permission-gated endpoints feed a process-lifetime derivSkip cache: the first 403-style response for a key without my_markets scope logs once at Info, then subsequent attempts go silent. Keeps logs readable on read-only keys without trading scope.

Metadata keys#

Under com.bitstamp.spec/. Full list in the connector's MAPPINGS.md; highlights:

  • Account: currency_type, currency_decimals, withdrawal_fee?, is_crypto?.
  • Payment: tx_type, bank_transaction_id?, transfer_pair_id?, transfer_direction? (set on the defensive sub-account transfer legs).
  • Order: order_subtype (LIMIT / MARKET / INSTANT / STOP_LIMIT), order_status_datetime, client_order_id?, historical?, retention_expired?.
  • Conversion: from_amount_raw, to_amount_raw, fee_market.

Pagination and recovery#

FetchPayments and FetchConversions each persist a LastTransactionID watermark and advance only after the cycle completes — a mid-cycle worker crash replays the same page on restart, with downstream dedupe absorbing the overlap. FetchOrders checkpoints LastSeenEventIDPerMarket plus HasMoreCurrentMarket so a partial paginated walk resumes from the same market on the next cycle.

Known gaps#

  • Historical orders — orders placed and filled before install, or older than 30 days, fall outside Bitstamp's order_status/ retention and aren't back-filled. Per-fill rows exist in user_transactions/ as type=2 with order_id; MAPPINGS.md §9 documents the aggregation approach.
  • No programmatic sub-account discovery — Bitstamp's API doesn't expose a "list my scopes" call. Deploy one connector per account scope, named via name.
FireblocksOpen Banking
On This Page
  • Prerequisites
  • Installation
  • Configuration fields
  • Capabilities
  • Account model
  • Asset model
  • Workflow tree
  • Payments
  • Orders
  • Conversions
  • Install-time enrichment
  • Metadata keys
  • Pagination and recovery
  • Known gaps