Merchant API

Submit a payment receipt

Attach receipt evidence to a pending payment that accepts receipt submission.

Some bank-transfer payments require receipt evidence after the payer completes the transfer. The Merchant API accepts either a previously uploaded private payment_receipt asset or a direct multipart image upload.

Direct uploads must be JPEG or PNG and no larger than 5 MiB. The platform verifies the object size and file signature before it is sent to the Provider. An asset can be attached to one payment intent only.

Endpoint

POST /payment_intents/:id/receipt

Required headers:

  • Authorization: Basic <base64(publicKey:secretKey)>.
  • JSON requests use Content-Type: application/json and preserve the { "asset_id": "..." } contract.
  • Multipart requests use Content-Type: multipart/form-data and require a valid Idempotency-Key.

Request

For an existing private receipt asset:

curl -s -X POST "$API_BASE/payment_intents/dord_01HZYABCDXYZ/receipt" \
  -u "$API_PUBLIC_KEY:$API_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"asset_id":"asst_01HZYRECEIPT"}'

For a direct upload, send the image in the file field. The idempotency fingerprint uses the image digest, so retries with a new multipart boundary remain safe:

curl -s -X POST "$API_BASE/payment_intents/dord_01HZYABCDXYZ/receipt" \
  -u "$API_PUBLIC_KEY:$API_SECRET_KEY" \
  -H "Idempotency-Key: receipt-dord-01HZYABCDXYZ-v1" \
  -F "file=@receipt.jpg;type=image/jpeg"

The intent must still be pending and configured to accept receipt submission. Repeating the request with the same asset is safe. A rejected receipt may be replaced with a new receipt; a different receipt while one is processing or submitted is rejected.

Outcome and reconciliation

The response is the payment intent and includes a nested receipt projection:

  • { "status": "required" } — the payment requires a receipt and none is attached.
  • { "status": "processing" } — the receipt is attached and verification is in progress.
  • { "status": "submitted" } — the Provider accepted the receipt.
  • { "status": "rejected", "failure_code": "receipt_rejected", "failure_message": "Receipt was rejected" } — the Provider rejected it; the payment remains pending and can receive a replacement.

Continue to poll the payment intent or consume the merchant webhook.

The multipart response is idempotent for 24 hours. Reusing a key with a different image digest returns idempotency_conflict. Provider upload outcomes are reconciled durably; a Provider rejection does not change the Payment status.

On this page