Operate

Errors, retries, and payment headers

Return payment requests correctly, understand SDK errors, and retry safely.

Payment headers

PAYMENT-REQUIRED comes with an HTTP 402 payment request. Copy it unchanged to the payer. PAYMENT-RESPONSE comes after payment succeeds. It is payment proof that the SDK checks before it returns the paid result.

Keep each payment-header value exactly as you received it.

PAYMENT-RESPONSE is encoded JSON. The SDK validates and attaches it for you. A direct HTTP integration returns the exact value from Recuut’s successful completion response. Merchants must never construct, re-encode, or edit this payment proof.

SDK errors

ErrorMeaningTypical response
RecuutConfigurationErrorAn app setting is missing or wrongFix the setting. Do not retry the request.
RecuutTransportErrorThe network failed before Recuut repliedRetry only when it is safe to run the paid work again.
RecuutResponseErrorRecuut returned an error responseRead code and message.
RecuutUnexpectedResponseErrorRecuut replied outside the generated contractInspect the retained status, body, and headers; update the SDK before depending on new fields.
PaymentRequiredErrorAccess requires paymentReturn content and payment_required to the payer unchanged.
PaymentRateLimitedErrorThe Merchant API rate-limited the requestWait for retry_after, then retry within a bound.

Import generic SDK errors from recuut. Import generated Merchant response errors and enums from recuut.merchant_api. For a known Merchant API error, read code and message.

Advanced error details

Raw content bytes and headers remain available when you need to return a payment request exactly or inspect logs. A future status, undocumented error code, or malformed payload raises RecuutUnexpectedResponseError with the original status, body, and headers.

import osfrom recuut import Recuutfrom recuut.merchant_api import (    AccessRequest,    AccessResource,    PaymentRequiredError,    SettlementAsset,)def relay_challenge(    *, body: bytes, payment_required: str) -> tuple[bytes, dict[str, str]]:    return body, {"PAYMENT-REQUIRED": payment_required}try:    with Recuut() as client:        client.payments.access(            access_request=AccessRequest(                revision_id=os.environ["RECUUT_REVISION_ID"],                settlement_asset=SettlementAsset.USDC,                resource=AccessResource(                    url="https://merchant.example/reports/quarterly",                    description="Quarterly report",                    mime_type="application/json",                ),            )        )except PaymentRequiredError as error:    if error.payment_required is not None:        response_body, response_headers = relay_challenge(            body=error.content,            payment_required=error.payment_required,        )    else:        print(error.status_code, error.code, error.message)        print(error.header("Retry-After"))

Retry discipline

ResponseMeaningSafe action
Access 402Payment is requiredRelay the body and PAYMENT-REQUIRED; let the payer retry.
Completion 202Settlement is pendingKeep the result private; honor Retry-After and use Get a transaction.
400, 401, 403, 404, or 422Request, key, permission, ID, or contract errorFix the input or configuration; do not retry unchanged.
409Stored payment state conflicts with this requestRead the error code and transaction before deciding whether to resume.
PaymentRateLimitedError (429)Rate limitedWait for error.retry_after, then retry within a bound.
500, 502, or 503Temporary server or payment-provider failure may existRetry only when protected side effects are idempotent.

Do not retry every failure automatically. When Recuut says payment is required, return its 402 response to the payer unchanged. If Retry-After is present, wait that long. For a temporary network or server error, retry a limited number of times with increasing waits.

Keep the paid result private until completion succeeds. A network interruption can cause a retry. Make important side effects safe to run more than once when possible. A successful transaction and receipt are the saved payment record. An app timeout alone does not prove payment failed.

Use payment_request_id to match a transaction to the original attempt in your logs. Do not derive payment status from that ID.

Inspect exact responses and schemas → · OpenAPI JSON →

On this page