Skip to Content

Capture Payment

POST/payments/:paymentId/capture

Description

Capture an authorized payment transferring funds to your settlement address. This endpoint is only available for payments created with payment_method: "two_step".

Requires a signed request to authorize the fund movement.

🔐

Your signature is verified before the capture executes. Use signCapture() from the @exodus/checkout-signer SDK. Like every merchant signature, it is EIP-712 typed data.

Headers

HeaderDescriptionRequired
AuthorizationBearer token with your API keyyes
Content-Typeapplication/jsonyes
X-SignatureSignature from signCapture(), signed with your signing key.yes

Path Parameters

NameTypeDescriptionRequired
paymentIdstringThe unique identifier of the payment to capture.yes

Request Body

NameTypeDescriptionRequired
deadlinenumberUnix timestamp (seconds) the capture signature commits to. Must equal the `deadline` returned by `signCapture`. The token and amount are bound into the signature rather than sent here; the server derives them from the escrowed payment.yes

Example Request

import { CheckoutSigner } from '@exodus/checkout-signer'
 
const signer = new CheckoutSigner()
 
// paymentId: system identifier for the payment (used in the URL path)
const paymentId = 'tz4a98xxat96iws9zmbrgj3a'
 
// Fetch the payment (or use the payment.escrow_confirmed webhook object as-is)
const payment = await fetch(
  `https://checkout-api.exodus-int.com/payments/${paymentId}`,
  { headers: { Authorization: 'Bearer sk_live_xxxxxxxxxxxxxxxx' } },
).then((r) => r.json())
 
// Pass the payment straight in — chain, token, amount, and deadline are handled for you
const { signature, body } = signer.signCapture(payment)
 
const response = await fetch(
  `https://checkout-api.exodus-int.com/payments/${paymentId}/capture`,
  {
    method: 'POST',
    headers: {
      Authorization: 'Bearer sk_live_xxxxxxxxxxxxxxxx',
      'Content-Type': 'application/json',
      'X-Signature': signature,
    },
    body: JSON.stringify(body),
  },
)

Response

SUCCESSFUL CAPTURE
{
  "id": "tz4a98xxat96iws9zmbrgj3a",
  "on_chain_id": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b",
  "status": "settled",
  "tx_hash": "0x8a9c67b2d1e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9"
}

Error Responses

INVALID PAYMENT MODE
{
  "error": {
    "type": "invalid_request",
    "message": "Capture is only available for two-step payments"
  }
}
INVALID SIGNATURE
{
  "error": {
    "type": "authentication_error",
    "message": "Invalid signature"
  }
}
ALREADY CAPTURED
{
  "error": {
    "type": "invalid_request",
    "message": "Payment has already been fully captured"
  }
}
🛡️

Captures run a compliance screen before releasing funds. If the screen blocks the payment or the screening provider is temporarily unavailable, the API returns 422 with error.type: "cannot_process".

SCREENING BLOCKED (422)
{
  "error": {
    "type": "cannot_process",
    "message": "Payment cannot be processed",
    "code": "flagged",
    "data": { "payment_id": "tz4a98xxat96iws9zmbrgj3a" }
  }
}

error.code is flagged when the payment is permanently blocked, or screening_pending when the screening provider is temporarily unavailable (retry later).

Start building

XO

Request Demo

Schedule a call with our team

Select a product
Arrow right

Start building
Grateful

Contact Us

We're here to help