Manual and delayed capture

Manual capture is the process of placing an authorization hold on a card, and then capturing the payment later, typically at the time of fulfillment.

The initial invocation of create_payment_session requires that the customer be present in the checkout flow. Additional calls for capture or voids don’t require customer-in-flow.

Authorization holds for manual capture are created and managed using the same create_payment_session endpoint outlined in Accept Your First Payment.

Manual capture relies on your system storing the id field from the payment session response and calling /payments/v1/payment_session/{business_id}/capture when the payment should be captured, or /payments/v1/payment_session/{business_id}/void to release the authorization hold.

Suggested implementation

Use manual capture when your order should be authorized at checkout, but charged later after a fulfillment decision.

1

Create the payment session as an authorization

Pass kind: "authorization" to create_payment_session. Store the returned payment session id on your order.

2

Wait for the authorization webhook

Listen for payment_session_complete. For manual capture, the key post-checkout webhook status is authorized. This means the authorization hold is ready to capture or void.

3

Capture when you are ready to fulfill

Call capture_payment_session with a unique idempotency_key, the total_amount to capture, and the item_details being captured.

4

Fulfill after capture succeeds

The authorization webhook tells you the funds are held, not captured. Fulfill after your capture request succeeds for the amount you intend to charge.

5

Void if you will not fulfill

If the order should not be fulfilled, call void_payment_session to release the authorization hold.

If you need to capture more than the original authorization amount, void the authorization and create a new payment session. Capture amounts must be less than or equal to the original authorization.

Flow diagram

Request body

The create_payment_session endpoint can be invoked with the additional fields described here.

FieldTypeDescription
kindstringOne of: one_time_payment (default) or authorization

Kind values

  • one_time_payment (DEFAULT): The charge is immediately captured. This is the default if this field is not set.
  • authorization: A one_time_payment authorization hold only. Must be manually captured or voided with a follow-up API call.

Response body

The response is the same as described in the create_payment_session API reference, with additional error states.

Bad request

HTTP Status: 400

If a field is missing or the request otherwise can’t be processed, we’ll return a 400.

Capture and void operations

Once an authorization hold has been created, you can either capture or void it:

Capture request

Use a unique idempotency_key for each capture attempt. Include the total amount being captured and the specific items included in the capture.

If you plan to capture by item, provide item_id on each order_items entry when creating the payment session so you can reference those same items in item_details.

curl https://dev-api.truemed.com/payments/v1/payment_session/c51f5d44-9f8d-4e9f-bc5f-929a6f9f2f51/capture \
-X POST \
-H "Content-Type: application/json" \
-H "x-truemed-api-key: $TRUEMED_API_KEY" \
-d '{
"total_amount": 12500,
"idempotency_key": "capture_order_12345",
"final": true,
"item_details": [
{
"item_id": "line_item_1",
"quantity": 1,
"total": 12500
}
]
}'

Set final to false only if you plan to make additional captures against the same authorization. This requires multi-capture, which is off by default — see Capturing in multiple parts.

Void request

Void the authorization when the order should not be fulfilled.

curl https://dev-api.truemed.com/payments/v1/payment_session/c51f5d44-9f8d-4e9f-bc5f-929a6f9f2f51/void \
-X POST \
-H "Content-Type: application/json" \
-H "x-truemed-api-key: $TRUEMED_API_KEY" \
-d '{
"idempotency_key": "void_order_12345"
}'

Capturing in multiple parts

By default an authorization gets exactly one capture. Multi-capture lets you draw against a single authorization more than once — the usual case being a multi-shipment order, where each shipment captures the value that physically left the warehouse and the remainder stays held for the next one.

Off by default

Multi-capture is disabled on every sales channel until Truemed enables it for yours, and it is not compatible with every sales channel configuration. Confirm with your Truemed representative that it can be turned on for your channel before you build against it.

Turning it on

Both sides have to line up:

  1. Truemed enables multi-capture on your sales channel. There is no self-serve toggle.
  2. You pass request_multicapture: true to create_payment_session, alongside kind: "authorization".

request_multicapture cannot be combined with tokenize: true or with a payment_token — a session that saves or reuses a card cannot also be multi-captured. If any of those conditions fails, create_payment_session returns:

{
"error": "FeatureNotEnabled",
"message": "Multi-capture is not enabled for this payment. Please contact support."
}

request_multicapture is only meaningful with kind: "authorization". Sessions created with kind: "one_time_payment" are captured automatically, in full, as a single final capture.

request_multicapture is not part of the idempotency comparison. Replaying a create_payment_session call with the same idempotency_key but a different request_multicapture value returns the original session unchanged — it does not switch multi-capture on or off. See create_payment_session for the full list of fields that are not compared.

Requesting it does not guarantee it

request_multicapture: true asks the card network for the capability. The network decides, per card, at authorization time. If it declines, the authorization behaves like an ordinary single-capture hold, and your first final: false capture fails with:

{
"error": "ValidationError",
"message": "Multicapture is not enabled, capture must be final."
}

There is no field on the payment session that reports the grant ahead of time, so treat a single-capture fallback as a live possibility and be ready to capture the order in one final call.

Making the captures

Call capture_payment_session once per shipment with a unique idempotency_key per capture, final: false on every capture but the last, and final: true on the last one.

1

Capture each shipment with final: false

Send only the items in that shipment in item_details, and set total_amount to the sum of their total values. The unshipped remainder stays authorized.

2

Close the authorization with final: true

The last capture releases whatever is still uncaptured back to the cardholder. To release the remainder without capturing anything more, send total_amount: 0 with final: true — allowed only once at least one capture has already succeeded.

# First shipment — 2 of the 3 units ship now
curl https://dev-api.truemed.com/payments/v1/payment_session/c51f5d44-9f8d-4e9f-bc5f-929a6f9f2f51/capture \
-X POST \
-H "Content-Type: application/json" \
-H "x-truemed-api-key: $TRUEMED_API_KEY" \
-d '{
"total_amount": 6000,
"idempotency_key": "capture_order_12345_shipment_1",
"final": false,
"item_details": [
{ "item_id": "line_item_1", "quantity": 2, "total": 6000 }
]
}'
# Second shipment — the last unit ships, closing the authorization
curl https://dev-api.truemed.com/payments/v1/payment_session/c51f5d44-9f8d-4e9f-bc5f-929a6f9f2f51/capture \
-X POST \
-H "Content-Type: application/json" \
-H "x-truemed-api-key: $TRUEMED_API_KEY" \
-d '{
"total_amount": 3000,
"idempotency_key": "capture_order_12345_shipment_2",
"final": true,
"item_details": [
{ "item_id": "line_item_1", "quantity": 1, "total": 3000 }
]
}'

Rules each capture must satisfy

RuleError if you break it
Every capture is fully itemized: total_amount equals the sum of item_details[*].total. Order-level shipping, fees, and discounts are only accepted on a single capture that covers the whole order, so under multi-capture they must be carried as line items.TotalAmountInvalid
The captures together never exceed the authorized amount.TotalAmountInvalid
A final: false capture has a non-zero total_amount.TotalAmountInvalid
A total_amount: 0 capture is final: true and follows an earlier capture.TotalAmountInvalid
Per line, quantity is positive and within the quantity still capturable, and total is non-negative and within the total still capturable.InvalidItemizedData
A line keeps the same effective per-unit cost (total / quantity) across every capture it appears in, within rounding. A single unit is never split across two captures, though different units of the same line can ship separately. If a line prices its units asymmetrically, send them as separate order_items.InvalidItemizedData

TotalAmountInvalid on a capture returns the fixed message Capture amount is invalid. — it does not name which of the four rules above was broken. InvalidItemizedData does: its message names the item and the value that was rejected.

The card network also caps how many times one authorization can be drawn against: at most 50 non-final captures plus the final one.

After capturing

Each capture shows up in the captures[] array on get_payment_session, carrying its own id, capture_amount, captured_at, amount_refundable, and item_details. The payment_session_complete webhook carries a captures[] array too, with id, capture_amount, and captured_at on each entry.

Pass a capture’s id as capture_id on create_refund to refund against that specific capture.