← Topics

Wallet transfers with API providers: keep request queues clear

How chained transfer calls between a platform and its API providers flood request queues, and how a queue, fast replies and pushed results keep the platform stable when a provider is slow.

A platform holds its users' wallets; API providers run the servers behind the services the platform offers. Before a user can use a provider's service, funds move from the platform to the provider, and when the user is done they move back. Most integrations build these transfers as a chain of synchronous HTTP calls in both directions. Under load the chain fills request queues on both sides, and one slow provider can take the whole platform down with it. Your platform's stability should not depend on someone else's servers.

A transfer as a chain of requests

UserPlatformAPI providerStart the servicethe platform's worker waitsPOST /transfer-inwaits for the providerGET /wallet/verifythe provider's worker waits too200 OK200or a timeout, status unknownGET /transfer/123every 2 s until settledReady
Before: one transfer, done as a chain of HTTP calls

The platform's worker is held for the whole round trip, including the provider's call back into the platform. The provider's worker is held while it waits for that callback. Each side waits on the other, so when traffic rises both pools fill at the same time. A timeout leaves the transfer's status unknown, which brings status polls and retries, and without idempotency keys a retry can move the money twice.

How one slow provider becomes everyone's problem

Promotion startsmore transfers
Provider C slows150 ms → 5 s
Platform workers heldwaiting for C
Shared pool used upevery request queues
Requests time out
Retries and status pollsmore requests
Logins, deposits and other providers failnothing to do with C
A slow moment turns into a flood

A worked example, not a benchmark (written 2026-11-09): 500 transfers a second spread over three providers, and a pool of 200 workers on the platform. Little's law gives the workers in use: transfers per second × seconds each one holds a worker.

After: accept fast, settle in the background, push the result

UserPlatformAPI providerHibiraStart the servicePendingledger written, worker freedPOST /transfersidempotency key, from a queue202 Acceptedrecorded, worker freedThe provider settles from its own queue, at its own pacetransfer.settledprivate-partner-acmetransfer.settledid 123, amountbalance.updatedprivate-wallet-42balance.updated
After: each side answers fast and the result arrives as an event
  1. The platform answers the user first. It writes the transfer to its ledger as pending, puts it in a queue and replies. The user's page shows "pending" and updates by itself when the event arrives.
  2. A dispatcher sends transfers to each provider with an idempotency key, a short timeout and a fixed number of slots per provider. The provider replies 202 as soon as it has recorded the transfer, so a retry with the same key is always safe.
  3. No callbacks. The request carries everything the provider needs, signed, so the provider does not call the platform back to verify it.
  4. The result is pushed. The provider publishes transfer.settled to a private channel for that platform, such as private-partner-acme. The platform's backend keeps one WebSocket connection and subscribes, authorized by the provider's auth endpoint. It updates the ledger and publishes balance.updated to the user's wallet channel, as in balance sync between products.
  5. One check for anything missed. Every few minutes the platform asks each provider for the transfers still pending, in one batch request, instead of polling each transfer.

Keeping the platform stable when a provider is not

  • Separate slots for each provider. A provider that slows down fills only its own slots in the dispatcher. Logins, deposits and every other provider keep their workers.
  • Stop sending to a provider that stops answering. After a run of timeouts, pause that provider's queue and show its services as unavailable, then try again after a short wait. Transfers stay pending in your ledger, not in held connections.
  • A dropped WebSocket connection blocks nothing. Transfers keep being accepted. When the connection is back, the batch check settles whatever happened in between.
  • Your users never wait on a provider's response time. The page answers in the platform's own time, and the result appears when it is ready.

Common questions

Why does a slow API provider slow down the whole platform?

Because the platform's workers wait for it. Each synchronous call holds a worker for as long as the provider takes, and the workers are shared with everything else. When one provider slows down, those workers run out, and requests that never touch that provider queue behind it.

Does WebSocket replace the transfer API?

No. The transfer itself stays an HTTP request with an idempotency key, recorded in both ledgers. WebSocket replaces the waiting: the held connection, the status polls and the callbacks. The result arrives as an event instead.

What if the platform is disconnected when a transfer settles?

Nothing is lost, because both ledgers already hold the transfer. pusher-js and the other client libraries reconnect on their own, and the batch check of pending transfers settles anything that was missed. Use the transfer ID to ignore an event you have already applied.

How many connections does this need?

One WebSocket connection from the platform's backend to each provider's channel is enough for every transfer with that provider, however many users it serves. On Hibira each publish counts as one message; on paid plans deliveries to subscribers are not counted. See pricing.

Product names are trademarks of their owners.