Payments

Retry a transfer batch's failed recipients

Changed on

Sends the batch's failed recipients again, from the same source wallet with the batch's options, and no other recipient. A failed recipient was not paid: its transaction never reached the network or the network rejected it. Recipients still pending or processing may land and are never sent again. Each resent recipient keeps its row and links to its new chunk transfer. Takes no body and requires an Idempotency-Key: a retry with the same key sends nothing again: while its response is stored, it returns that saved response, and after a server error or once the response has expired, the batch as it stands. Read the batch's current state with GET /v1/payments/transfer-batches/{batchId}, and use a new key for each retry you mean to send. Any credential that may send from the batch's wallet can retry it. Each recipient is paid the address the batch recorded for it. Returns 409, sending nothing: TRANSFER_BATCH_NOTHING_TO_RETRY when the batch has no failed recipients (while it is processing, its unsent recipients become failed only once its request can no longer send them), TRANSFER_BATCH_RETRY_CLAIMED when a concurrent retry claimed them first, and TRANSFER_BATCH_DESTINATION_CHANGED (details.recipientIds) when a failed recipient's counterparty account has changed its address since (send that recipient in a new batch). A native SOL retry is refused with 409 AMOUNT_BELOW_RENT_MINIMUM, as create is, when a failed recipient would still be paid less than the rent-exempt minimum at an address with no account; its recipients stay failed. Only recipients that are still failed are ever sent, so a retry under a new key never pays anyone twice.

post/v1/payments/transfer-batches/{batchId}/retry

Request

  • Base URL
    http://localhost:8787
  • URL
    http://localhost:8787/v1/payments/transfer-batches/{batchId}/retry
  • Auth
    HTTP bearer

Path parameters

batchIdstring required

Transfer batch identifier.

Example:xbatch_example

Headers

x-project-idstring

Required for dashboard sessions. API keys use their assigned project.

Example:prj_example
Idempotency-Keystring required

Required: a request without one returns 400 IDEMPOTENCY_KEY_REQUIRED. 1-255 printable ASCII characters; send a UUID per logical operation and reuse it on every retry. Within 24 hours, a retry with the same key and the same request returns the stored status and body with the response header Idempotent-Replayed: true. Keys are scoped to the credential: within 24 hours, the same key with a different request, or from a different credential, returns 422 IDEMPOTENCY_KEY_REUSED; after that, the same key from a different credential runs as a new request. A rotated API key is a new credential, so after any rotation read the resource with GET to see whether the original request ran, and never resend a money request under the new credential without checking first. A key whose original request is still running returns 409 IDEMPOTENCY_KEY_IN_FLIGHT with Retry-After. A 400, 401, 403, 409 or 429 refusal is not stored, so the same key runs again once the cause is fixed. Access is checked before the key on every request, so a refused request never claims the key, and a credential that has since lost access to the named resource gets 403 instead of the stored response. A replay is a snapshot of the original response: read the current state with GET instead of sending the request again. If the original request never recorded its outcome (it crashed), a same-key retry gets 409 IDEMPOTENCY_KEY_IN_FLIGHT until 15 minutes plus the route's lease have passed since the original was sent (about 16 minutes, or about 20 for transfer batches), then takes over and is answered from the record it wrote. Once the request has written its record, any answer below 500 is stored: a transfer that fails after that (such as 400 TRANSACTION_FAILED or a 409 signer mismatch) answers a same-key retry with the same error and Idempotent-Replayed: true, and after 24 hours the record answers with the same status and code. A 5xx is not stored: a same-key retry runs again and is answered from the record. To try again after a failure, send the request with a new key. After a 5xx, a retry with the same key runs again and answers from the original record instead of acting twice; that answer also carries Idempotent-Replayed: true.

Example:idempotency_example_12345

Response

Failed recipients resent

Example response

{
  "data": {
    "batch": {
      "id": "xbatch_example",
      "organizationId": "org_example",
      "projectId": "prj_example",
      "externalId": "payroll_2026_06_30",
      "sourceCustodyWalletId": "cwlt_example",
      "sourceProviderWalletId": "privy_wallet_123",
      "sourceAddress": "So11111111111111111111111111111111111111112",
      "token": "SOL",
      "status": "processing",
      "totalAmount": "1000.00",
      "recipientCount": 20,
      "transactionCount": 1,
      "createdAt": "2025-01-01T00:00:00.000Z",
      "updatedAt": "2025-01-01T00:00:00.000Z"
    },
    "recipients": [
      {
        "transferId": "xfr_example",
        "externalId": "payroll_row_001",
        "counterpartyId": "cpty_example",
        "counterpartyAccountId": "cpacct_example",
        "destination": "So11111111111111111111111111111111111111112",
        "amount": "100.00",
        "status": "pending",
        "createdAt": "2025-01-01T00:00:00.000Z",
        "updatedAt": "2025-01-01T00:00:00.000Z"
      }
    ],
    "transfers": [
      {
        "id": "xfr_example",
        "organizationId": "org_example",
        "custodyWalletId": "cwlt_example",
        "providerWalletId": "privy_wallet_123",
        "projectId": "prj_example",
        "type": "transfer",
        "direction": "outbound",
        "status": "confirmed",
        "signature": "sig_example",
        "serializedTx": "base64_tx_example",
        "slot": 123456,
        "blockTime": "2025-01-01T00:00:00.000Z",
        "confirmedAt": "2025-01-01T00:00:00.000Z",
        "fee": 5000,
        "error": "Signature failed",
        "initiatedBy": {
          "type": "api_key"
        },
        "source": "So11111111111111111111111111111111111111112",
        "destination": "So11111111111111111111111111111111111111112",
        "rampsMemo": {
          "invoice": "INV-123",
          "po": "PO-9"
        },
        "amount": "100.00",
        "counterpartyId": "cpty_example",
        "counterpartyDisplayName": "Acme Studio",
        "providerReference": "ramp_quote_example",
        "deliveryMode": "session_widget",
        "fiatCurrency": "USD",
        "fiatAmount": "100.00",
        "fiatPayout": {
          "amount": "20.82",
          "currency": "USD",
          "reportedAt": "2025-01-02T00:00:00.000Z"
        },
        "deliveryMismatch": {
          "reason": "amount_mismatch",
          "checkedAt": "2025-01-02T00:00:00.000Z",
          "reportedAmount": "20",
          "observedAmount": "0.5"
        },
        "risk": {
          "provider": "trm",
          "score": "0.12",
          "level": "low",
          "evaluatedAt": "2025-01-01T00:00:00.000Z"
        },
        "createdAt": "2025-01-01T00:00:00.000Z",
        "updatedAt": "2025-01-02T00:00:00.000Z"
      }
    ]
  },
  "meta": {
    "requestId": "req_example",
    "timestamp": "2025-01-01T00:00:00.000Z"
  }
}

Changes

    • warning

      added the new CUSTODY_POLICY_REFUSED enum value to the / response property for the response status

    • warning

      added the new CUSTODY_POLICY_REFUSED enum value to the / response property for the response status

    • warning

      added the new CUSTODY_POLICY_REFUSED enum value to the / response property for the response status

    • warning

      added the new CUSTODY_POLICY_REFUSED enum value to the / response property for the response status

    • warning

      added the new CUSTODY_POLICY_REFUSED enum value to the / response property for the response status

    • warning

      added the new CUSTODY_POLICY_REFUSED enum value to the / response property for the response status

    • warning

      added the new CUSTODY_POLICY_REFUSED enum value to the / response property for the response status

    • info

      endpoint added