Skip to main content
Duplicate payout runs are the most expensive kind of retry bug. Pvium’s idempotency model is built on identifiers you supply, so a retried request references the same payout instead of creating a second one.

Supply your own payout ID

create accepts an optional id — a client-generated ID the new payout is created under. It must be a UUID (the API validates this); if omitted, the server generates one. For idempotency, don’t use a random UUID per attempt — derive a deterministic UUID (v5) from the thing that makes the payout unique in your system, such as an invoice ID or payroll period, so every retry produces the same id:
createFinalized requires an id for exactly this reason — a create-and-finalize call must be safely retryable.

Individual payments

Within a batch, a payment’s identity is (batch, receiver, memo) — enforced by a unique constraint.
  • The same receiver can appear on multiple rows in one batch as long as each row carries a distinct memo. Put your per-payment business key in the memo — an invoice code or installment identifier like INV-1042:install-3 — and each obligation becomes its own idempotent row.
  • Re-adding the same (receiver, memo) never creates a second payment: a direct add is rejected as a duplicate, and an invite-driven attach updates the existing row’s amount in place.
  • A retried add-payments call therefore cannot double-pay anyone — the repeated rows collide on (batch, receiver, memo) instead of duplicating.

Nonces

Every payout also carries a nonce. The SDK generates one automatically; pass your own only if you need the nonce to be deterministic across systems.

Retry pattern

  1. Compute the payout id from your business key before calling Pvium.
  2. On a timeout or network error, retry the same call with the same id.
  3. A retried create whose id already exists is rejected as a duplicate — it will not create a second payout, and it will not return the existing one. Catch the conflict, fetch the payout with pvium.payout.get(id), and continue from its current state — it may already be created or even finalized.
  4. Key webhook processing on payout and payment IDs, so re-deliveries are no-ops. See Webhooks.

What finalization adds

Finalization is naturally idempotent in effect: it signs the payout’s exact contents, and funding accepts only that signed payout. Even if your retry logic misfires at the create step, nothing can be paid twice from one funding — the checkout funds the signed payment set, once.