#!/usr/bin/env bash
# Capture a prior authorization (must equal the authorized amount in v1). Any
# 2xx is a response — branch on data.status. On a timeout/5xx, retry with the
# SAME operation_id; never mint a new one for the same capture attempt.
set -euo pipefail
API_BASE="${RADIUMONE_API_BASE:-https://api-sandbox.radiumone.io/gateway}"
: "${RADIUMONE_ACCESS_TOKEN:?set RADIUMONE_ACCESS_TOKEN to a Bearer access token}"
: "${RADIUMONE_TRANSACTION_ID:?set RADIUMONE_TRANSACTION_ID to the id of the AUTHORIZED transaction}"
curl -sS -X POST "$API_BASE/v1/transactions/$RADIUMONE_TRANSACTION_ID/capture" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $RADIUMONE_ACCESS_TOKEN" \
-d @request.json#!/usr/bin/env python3
"""Capture a prior authorization (must equal the authorized amount in v1).
Shared result pattern: any 2xx is a response you branch on ``status``. On a
network timeout or 5xx, retry with the SAME operation_id — never mint a new
one for the same capture attempt.
"""
import json
import os
import random
import time
from pathlib import Path
import requests
API_BASE = os.environ.get("RADIUMONE_API_BASE", "https://api-sandbox.radiumone.io/gateway")
def backoff_seconds(attempt: int) -> float:
"""Exponential backoff with jitter: attempt 1 waits ~0.25-0.5s, doubling
each attempt, capped at 4s -- avoids hammering the gateway in a loop."""
base = min(0.25 * 2 ** (attempt - 1), 4.0)
return base + random.random() * base
def capture_authorization(max_attempts: int = 3) -> dict:
body = json.loads((Path(__file__).parent / "request.json").read_text())
transaction_id = os.environ["RADIUMONE_TRANSACTION_ID"]
headers = {"Authorization": f"Bearer {os.environ.get('RADIUMONE_ACCESS_TOKEN', '')}"}
for attempt in range(1, max_attempts + 1):
try:
resp = requests.post(
f"{API_BASE}/v1/transactions/{transaction_id}/capture", json=body, headers=headers, timeout=30
)
except requests.exceptions.Timeout:
if attempt == max_attempts:
raise
time.sleep(backoff_seconds(attempt))
continue
if resp.status_code >= 500:
if attempt == max_attempts:
raise RuntimeError(f"server error {resp.status_code} after {attempt} attempts")
time.sleep(backoff_seconds(attempt))
continue
payload = resp.json()
if not resp.ok:
# 422 tx:capture-window-expired if the capture window has passed.
code = payload.get("type") or payload.get("code")
raise RuntimeError(f"capture failed: {code} ({resp.status_code})")
return payload # branch on data.status
raise RuntimeError("unreachable")
if __name__ == "__main__":
print(json.dumps(capture_authorization(), indent=2))
{
"status": "ok",
"request_id": "req_ord1002cap1a",
"data": {
"id": "1b2c3d4e-5f60-4718-9a2b-3c4d5e6f7081",
"request_id": "ord-1002-auth-1",
"type": "AUTHORIZE",
"status": "CAPTURED",
"amount": 10000,
"currency": "SGD",
"payment_method_type": "card",
"order_reference": "ORD-1002",
"response_code": "00",
"parent_transaction_id": null,
"switch_request_id": "sw-ord-1002-cap-1",
"created_at": "2026-09-14T10:00:00.000Z",
"updated_at": "2026-09-14T10:05:01.000Z"
}
}Capture - Payments API
Capture funds from an authorized transaction within its capture window. Retrying with the same operation ID returns the original result.
#!/usr/bin/env bash
# Capture a prior authorization (must equal the authorized amount in v1). Any
# 2xx is a response — branch on data.status. On a timeout/5xx, retry with the
# SAME operation_id; never mint a new one for the same capture attempt.
set -euo pipefail
API_BASE="${RADIUMONE_API_BASE:-https://api-sandbox.radiumone.io/gateway}"
: "${RADIUMONE_ACCESS_TOKEN:?set RADIUMONE_ACCESS_TOKEN to a Bearer access token}"
: "${RADIUMONE_TRANSACTION_ID:?set RADIUMONE_TRANSACTION_ID to the id of the AUTHORIZED transaction}"
curl -sS -X POST "$API_BASE/v1/transactions/$RADIUMONE_TRANSACTION_ID/capture" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $RADIUMONE_ACCESS_TOKEN" \
-d @request.json#!/usr/bin/env python3
"""Capture a prior authorization (must equal the authorized amount in v1).
Shared result pattern: any 2xx is a response you branch on ``status``. On a
network timeout or 5xx, retry with the SAME operation_id — never mint a new
one for the same capture attempt.
"""
import json
import os
import random
import time
from pathlib import Path
import requests
API_BASE = os.environ.get("RADIUMONE_API_BASE", "https://api-sandbox.radiumone.io/gateway")
def backoff_seconds(attempt: int) -> float:
"""Exponential backoff with jitter: attempt 1 waits ~0.25-0.5s, doubling
each attempt, capped at 4s -- avoids hammering the gateway in a loop."""
base = min(0.25 * 2 ** (attempt - 1), 4.0)
return base + random.random() * base
def capture_authorization(max_attempts: int = 3) -> dict:
body = json.loads((Path(__file__).parent / "request.json").read_text())
transaction_id = os.environ["RADIUMONE_TRANSACTION_ID"]
headers = {"Authorization": f"Bearer {os.environ.get('RADIUMONE_ACCESS_TOKEN', '')}"}
for attempt in range(1, max_attempts + 1):
try:
resp = requests.post(
f"{API_BASE}/v1/transactions/{transaction_id}/capture", json=body, headers=headers, timeout=30
)
except requests.exceptions.Timeout:
if attempt == max_attempts:
raise
time.sleep(backoff_seconds(attempt))
continue
if resp.status_code >= 500:
if attempt == max_attempts:
raise RuntimeError(f"server error {resp.status_code} after {attempt} attempts")
time.sleep(backoff_seconds(attempt))
continue
payload = resp.json()
if not resp.ok:
# 422 tx:capture-window-expired if the capture window has passed.
code = payload.get("type") or payload.get("code")
raise RuntimeError(f"capture failed: {code} ({resp.status_code})")
return payload # branch on data.status
raise RuntimeError("unreachable")
if __name__ == "__main__":
print(json.dumps(capture_authorization(), indent=2))
{
"status": "ok",
"request_id": "req_ord1002cap1a",
"data": {
"id": "1b2c3d4e-5f60-4718-9a2b-3c4d5e6f7081",
"request_id": "ord-1002-auth-1",
"type": "AUTHORIZE",
"status": "CAPTURED",
"amount": 10000,
"currency": "SGD",
"payment_method_type": "card",
"order_reference": "ORD-1002",
"response_code": "00",
"parent_transaction_id": null,
"switch_request_id": "sw-ord-1002-cap-1",
"created_at": "2026-09-14T10:00:00.000Z",
"updated_at": "2026-09-14T10:05:01.000Z"
}
}Authorizations
Bearer access token from POST /v1/auth/token. Treat it as an opaque string — do not depend on its internal encoding, which has changed before and isn't part of the contract.
Path Parameters
UUID of the AUTHORIZED parent transaction.
Body
Request body for POST /v1/transactions/{id}/capture.
Full capture only in v1. amount must equal the authorised amount. Idempotent via operation_id scoped to the parent transaction.
Capture amount as a {currency, value} money object. Must equal the authorised amount in v1, and currency must match the authorisation.
Show child attributes
Show child attributes
Your idempotency key for this capture. Retrying with the same key returns the original result instead of capturing again.
8 - 64Optional operation metadata.
Response
Successful Response
Standard success envelope. Every successful response has this shape, with the operation's own payload under data.
The operation's result. Its shape is documented per operation; omitted on responses that carry no payload.
Show child attributes
Show child attributes
Optional human-readable note. Omitted from the response when not set, which is the case for every payment operation today. Never parse it.
Correlation ID for this HTTP request, for logs and support. Send your own in the X-Request-Id header (letters, digits and hyphens, up to 36 characters -- other characters are stripped) or the gateway generates one. This is NOT the request_id idempotency key you send in a transaction body; the two are unrelated.
Always ok on a successful (2xx) response. Errors use a different body shape entirely (RFC 9457 problem details), so branch on the HTTP status code, not on this field.