Wallgent
Guides

Idempotency

Safely retry failed requests without creating duplicate payments.

Overview

Network failures, timeouts, and retries are inevitable. Without idempotency keys, a retry after a timeout could create a duplicate payment. With idempotency keys, retrying the exact same request is safe — the server returns the cached result from the first attempt.

Idempotency keys apply to write operations that move money:

  • Sending a payment (POST /v1/payments)
  • Batch payments (POST /v1/payments/batch)
  • Funding a wallet (POST /v1/wallets/:id/fund)

How to Use Idempotency Keys

SDK

Pass an idempotencyKey field in the request body:

import Wallgent from '@wallgent/sdk'

const wg = new Wallgent({ apiKey: process.env.WALLGENT_API_KEY })

const payment = await wg.payments.send({
  from: 'wal_01J...',
  to: 'wal_01J...',
  amount: '75.00',
  description: 'Invoice #1042 payment',
  idempotencyKey: 'inv-1042-payment-2026-03-01',
})

REST API

Pass the key as an Idempotency-Key header:

const response = await fetch('https://api.wallgent.com/v1/payments', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.WALLGENT_API_KEY}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': 'inv-1042-payment-2026-03-01',
  },
  body: JSON.stringify({
    from: 'wal_01J...',
    to: 'wal_01J...',
    amount: '75.00',
  }),
})

Key Format Recommendations

Keys must be unique per operation. Good formats:

  • UUID v4: crypto.randomUUID() — simple and universally unique
  • Composite key: <userId>-<operationId>-<timestamp> — traceable to a specific operation
  • Hash of request: sha256(JSON.stringify(requestBody)) — deterministic for the same input
import { randomUUID } from 'node:crypto'

// Option 1: UUID (simple)
const key = randomUUID()

// Option 2: Composite (traceable)
const key = `pay-${userId}-${invoiceId}-${Date.now()}`

// Option 3: Deterministic (reproduce the same key for the same logical operation)
const key = `payroll-${payPeriod}-${employeeId}`

TTL and Behavior

ScenarioBehavior
Same key, same parameters, within 24 hoursReturns the cached response — no new operation is created
Same key, same parameters, after 24 hoursCreates a new operation (key has expired)
Same key, different parameters, any timeReturns IDEMPOTENCY_KEY_REUSE error (HTTP 409)

The 24-hour TTL (IDEMPOTENCY_TTL_HOURS = 24) means you can safely retry within a day of the original attempt without risk of duplication.


Handling IDEMPOTENCY_KEY_REUSE

If you send a request with the same idempotency key but different parameters, the API returns an error rather than silently using the wrong cached result:

// This will return IDEMPOTENCY_KEY_REUSE (409) because the amount is different
const payment = await wg.payments.send({
  from: 'wal_01J...',
  to: 'wal_01J...',
  amount: '100.00',          // Different from the original '75.00'
  idempotencyKey: 'inv-1042-payment-2026-03-01',  // Same key
})

Always generate a new key for a new logical operation.


Safe Retry Pattern

import { randomUUID } from 'node:crypto'

async function sendPaymentWithRetry(
  from: string,
  to: string,
  amount: string,
  description: string,
  maxAttempts = 3,
) {
  // Generate the idempotency key once — reuse it across all retry attempts
  const idempotencyKey = randomUUID()

  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      const result = await wg.payments.send({
        from,
        to,
        amount,
        description,
        idempotencyKey,
      })
      return result
    } catch (err) {
      const isRetryable =
        err instanceof Error &&
        (err.message.includes('TIMEOUT') || err.message.includes('network'))

      if (!isRetryable || attempt === maxAttempts) {
        throw err
      }

      // Exponential backoff before retry
      const delay = 1000 * 2 ** (attempt - 1)
      await new Promise((r) => setTimeout(r, delay))
    }
  }
}

The key is generated once before the loop. All retry attempts use the same key, so only one payment is ever created regardless of how many requests reach the server.

On this page