Miracle Docs

Capture

Capture an authorized payment to charge the customer. By default, payments use auto-capture — authorization and capture happen in a single step. Use manual capture when you need to authorize first and charge later.


When to use manual capture

Manual capture is useful when the final charge amount may differ from the authorization, or when you want to delay the charge until fulfillment:

  • Hotels and rentals — authorize at booking, capture at checkout with the final amount.
  • Pre-orders — hold funds now, charge when the item ships.
  • Marketplace holds — authorize when the buyer commits, capture after the seller confirms.
  • Custom or made-to-order goods — authorize upfront, capture when production is complete.

How it works

  1. Set captureMethod to "manual" when creating the payment or checkout session.
  2. The payment reaches requires_capture status — funds are held on the customer's card.
  3. Call POST /v1/payments/{id}/capture to charge the held amount.
  4. On success the payment moves to captured. If the provider rejects the capture, the payment transitions to failed — check the error object for the reason.

If you decide not to charge, call POST /v1/payments/{id}/cancel instead. See Cancel / Void.


Create a payment with manual capture

When creating the payment, include captureMethod: "manual":

{
  "amount": { "currency": "USD", "valueMinor": 15000 },
  "merchantReference": "order-5678",
  "captureMethod": "manual",
  "paymentMethod": { ... }
}

The same field works on checkout sessions:

{
  "amount": { "currency": "USD", "valueMinor": 15000 },
  "lineItems": [ ... ],
  "merchantReference": "order-5678",
  "successUrl": "https://your-site.com/success",
  "cancelUrl": "https://your-site.com/cancel",
  "captureMethod": "manual"
}

Capture the payment

Once the payment is in requires_capture status, call capture to charge the customer.

Full capture

By default, capture charges the full authorized amount. Send an empty body:

curl -X POST https://api.miracle.com/v1/payments/pay_abc123/capture \
  -H "Authorization: Bearer sk_test_your_secret_key" \
  -H "Idempotency-Key: capture-order-5678"
const response = await fetch('https://api.miracle.com/v1/payments/pay_abc123/capture', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sk_test_your_secret_key',
    'Idempotency-Key': 'capture-order-5678',
  },
});

const result = await response.json();

if (!response.ok) {
  // Capture was rejected — inspect the error
  console.error(result.error.code, result.error.message);
} else {
  const { data: payment } = result;
  // payment.status === 'captured'
}

The response returns the updated Payment object with status: "captured".

Partial capture

To capture less than the authorized amount, pass an amount in the request body. The remaining held amount is released automatically.

curl -X POST https://api.miracle.com/v1/payments/pay_abc123/capture \
  -H "Authorization: Bearer sk_test_your_secret_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: capture-order-5678-partial" \
  -d '{
    "amount": {
      "currency": "USD",
      "valueMinor": 12000
    }
  }'
const response = await fetch('https://api.miracle.com/v1/payments/pay_abc123/capture', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sk_test_your_secret_key',
    'Content-Type': 'application/json',
    'Idempotency-Key': 'capture-order-5678-partial',
  },
  body: JSON.stringify({
    amount: {
      currency: 'USD',
      valueMinor: 12000,
    },
  }),
});

const { data: payment } = await response.json();
// payment.status === 'captured'
// Captured $120.00 out of $150.00 authorized. $30.00 released.

Only a single capture is supported per payment. After capture, any remaining authorized amount is released.

The capture amount must not exceed the original authorization amount. Requests that exceed it are rejected with a validation error.


Authorization validity

Card authorizations expire — typically 7 days, though the exact window varies by card network and issuing bank. If you do not capture or cancel within this window:

  • The authorization hold is released automatically by the issuing bank.
  • The payment transitions to canceled status.
  • You receive a payment.canceled webhook.

Do not rely on the authorization hold lasting the full 7 days. Some issuers release holds earlier. Capture as soon as you are ready to charge.


Webhook

A successful capture triggers the payment.captured webhook event:

{
  "id": "evt_xyz789",
  "type": "payment.captured",
  "data": {
    "id": "pay_abc123",
    "tenantId": "ten_...",
    "capturedAmount": {
      "currency": "USD",
      "valueMinor": 12000
    }
  },
  "occurredAt": "2026-04-09T14:30:00Z"
}

On this page