Skip to main content

Credentials

API key only, as everywhere in the Business API. Every recipient and destination is scoped to the key’s business; there is no cross-business lookup and no way to reference another business’s recipient.

Rate limits

All eleven operations share 120 requests per minute, counted once against your business and once against the key. The limit is declared on the controller, so a payout run competes with itself: creating a recipient, adding a detail, and resolving a destination all draw on the same allowance, as do transfers and FX quotes on the same controller. A payout batch therefore costs roughly: At 120 per minute, a run of 500 saved payees needs about eight and a half minutes of steady pacing. Build the pacing in; do not discover it as 429s.

What Sakawave stores

Two consequences for your side of the integration:
  1. You cannot read routing values back. If your UI must show a customer the account number they saved, keep your own copy under your own controls — Sakawave will not return it.
  2. Correcting a value means replacing it. There is no update endpoint. Add a new payment detail and delete the old one.

The 24-hour window

A destination expires 24 hours after creation and is consumed by the transfer that spends it. Both produce the same error:
An expired destination and an already-spent one are indistinguishable. If your retry logic re-sends a transfer with a stored destinationId, a genuine duplicate and a stale destination will look the same. Resolve a fresh destination for every attempt, and rely on the transfer’s own externalReference for idempotency.
The pattern that works:
The one that does not: storing a destinationId on your payout row and reusing it on retry.

Defaults

Every recipient with any payment details has exactly one default.
  • The first detail added becomes the default whatever isDefault says.
  • Setting isDefault on a later one moves the default.
  • Deleting the default promotes another automatically; you cannot end up with details and no default.
  • There is no way to clear a default.
Creating a destination without naming a paymentDetailId uses the default. If your recipients hold several details, name the one you mean rather than depending on which is current.

No events

Recipients, payment details, and destinations emit no webhooks. Nothing tells you a destination expired, and nothing tells you a detail became provider-locked. Both surface as errors at the point of use.

Legacy provider-locked details

A payment detail whose routing values live at the provider rather than with Sakawave carries providerLocked: true. Sakawave tries to resolve it when you use it; when it cannot, you get 409 payment_detail_provider_locked and the only fix through this API is to delete the detail and add it again with its routing fields. Check for it proactively if you hold long-lived recipients:

Configuration checklist

  • fields built from route descriptors at runtime, never hardcoded
  • A fresh destination resolved per transfer attempt, never reused
  • Transfer idempotency carried by externalReference, not by the destination
  • Your own copy kept of any routing value your UI must display
  • Payout batches paced under 120 calls per minute across all eleven operations
  • displayName from the destination shown for approval, since verification may have changed it
  • A check for providerLocked details if your recipients predate the current routing model