# Void an authorization Building with an AI agent? Start at https://docs.payme.io/agents.md - Sandbox: `POST https://sandbox.payme.io/api/refund-sale` - Production: `POST https://live.payme.io/api/refund-sale` ## Credentials - Request body field `payme_client_key` (optional): Partners: your partner key. You can send it in the `PayMe-Partner-Key` header instead. A seller leaves it out. - Request body field `seller_payme_id` (optional): The seller's MPL. Required unless you send `seller_id` with `payme_client_key`. You can send it in the `PayMe-Merchant-Key` header instead. ## Overview Releases the hold of an `authorized` sale, in full or in part. It is the same call as [`refund-sale`](/payments/post-sale-actions/post-refund-sale): PayMe voids instead of refunding because the sale is `authorized`. ### Credentials The same as `refund-sale`: send `seller_payme_id`, or `seller_id` with your partner key (`payme_client_key`). A partner key on its own fails with error `1`. ### Rules - The sale's status must be `authorized`, or `partial-void` to release more of the hold. Otherwise the call returns error `305`. - Leave out `sale_refund_amount` to release the whole hold; the sale becomes `voided`. - Send an amount to release part of the hold; the sale becomes `partial-void` and can no longer be captured. To charge less than you authorized, capture with a lower `sale_price` instead. After a partial void, `sale_refund_buffer` shows the hold that remains. - An authorization that [multi-capture](/accepting-payments/capture-and-saved-cards/multi-capture) has already captured from can't be voided (HTTP 400, error `369`). - PayMe never voids an authorization on its own. If you don't void it, the card issuer releases the hold on its own schedule. PayMe sends the same `refund` callback as for a refund. See [Callbacks](/payments/getting-started/callbacks). ## Parameters This endpoint takes no path, query or header parameters. ## Request body Content type: `application/json` | Field | Type | Required | Description | | --- | --- | --- | --- | | `payme_client_key` | string | no | Partners: your partner key. You can send it in the `PayMe-Partner-Key` header instead. A seller leaves it out. | | `seller_payme_id` | string | no | The seller's MPL. Required unless you send `seller_id` with `payme_client_key`. You can send it in the `PayMe-Merchant-Key` header instead. | | `seller_id` | string | no | Partners: your own ID for the seller (the `seller_id` you set when you created it). Send it with `payme_client_key` instead of `seller_payme_id`. | | `payme_sale_id` | string | yes | The authorized sale. | | `sale_refund_amount` | integer | no | Amount of the hold to release, in minor units (`1000` = 10.00). Leave it out to release the whole hold. A partial amount must be at least the account's minimum sale value (500 by default). | | `verify_only` | boolean | no | `true` to check the call without making it: PayMe validates the request and returns the amounts, but changes nothing and sends no callback. Default `false`. | | `language` | string | no | Language of error messages: `en`, or `he` (the default). `es`, `ru` and `ar` are accepted, but messages then come back mostly in English. Any other value returns error `51`. | ### Request example ```json { "seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567", "payme_sale_id": "SALEDEMO-XXXXXXXX-XXXXXXXX-XXXXXXXX", "language": "en" } ``` ## Code samples ### curl ```bash curl -X POST 'https://sandbox.payme.io/api/refund-sale' \ -H 'Content-Type: application/json' \ --data-raw '{ "seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567", "payme_sale_id": "SALEDEMO-XXXXXXXX-XXXXXXXX-XXXXXXXX", "language": "en" }' ``` ### Node.js ```javascript const response = await fetch("https://sandbox.payme.io/api/refund-sale", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ "seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567", "payme_sale_id": "SALEDEMO-XXXXXXXX-XXXXXXXX-XXXXXXXX", "language": "en" }), }); console.log(await response.text()); ``` ### Python ```python import requests response = requests.post( "https://sandbox.payme.io/api/refund-sale", json={ "seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567", "payme_sale_id": "SALEDEMO-XXXXXXXX-XXXXXXXX-XXXXXXXX", "language": "en", }, ) print(response.text) ``` ### PHP ```php 'POST', CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => $body, ]); $response = curl_exec($ch); curl_close($ch); echo $response; ``` ## Responses ### 200 — The hold was released, in full or in part. (success) | Field | Type | Description | | --- | --- | --- | | `status_code` | integer | `0`: success. | | `status_error_code` | integer | `0` on success. | | `payme_status` | string | `success`. | | `refunded_from_creditcard` | boolean | Always `false` on a void. | | `sale_invoice_number` | string | Number of the credit document, when the invoicing service is active on the account; otherwise `null`. | | `sale_invoice_url` | string | Link to the credit document, when the invoicing service is active on the account; otherwise `null`. | | `sale_refund_buffer` | integer | The part of the hold that is still authorized, in minor units. | | `sale_status` | string | The sale's status after the call: `voided`, or `partial-void` after a partial void. | | `payme_transaction_total` | integer | Amount released from the hold, in minor units. | | `payme_transaction_total_aft_deduction` | integer | Same as `payme_transaction_total` on a void. | | `payme_transaction_id` | string | PayMe's ID of the void transaction. `null` when `verify_only` is `true`. | | `payme_transaction_card_brand` | string | Card brand. `null` when `verify_only` is `true`. | | `payme_transaction_auth_number` | string | Authorization number from the card issuer. `null` when `verify_only` is `true`. | | `payme_transaction_voucher` | string | Voucher number. `null` when `verify_only` is `true`. | | `payme_signature` | string | PayMe's signature for the transaction. `null` when `verify_only` is `true`. | | `payme_transaction_acquirer` | string | The acquirer, or `null`. | | `payme_transaction_arn` | string | Acquirer reference (EMV UID) when the acquirer returns one; usually `null`. Not returned when `verify_only` is `true`. | | `session` | string | PayMe's ID for this request. Quote it when you contact PayMe. | ```json { "status_code": 0, "status_error_code": 0, "payme_status": "success", "refunded_from_creditcard": false, "sale_invoice_number": null, "sale_invoice_url": null, "sale_refund_buffer": 0, "sale_status": "voided", "payme_transaction_total": 10000, "payme_transaction_total_aft_deduction": 10000, "payme_transaction_id": "TRANDEMO-XXXXXXXX-XXXXXXXX-XXXXXXXX", "payme_transaction_card_brand": "Mastercard", "payme_transaction_auth_number": "72****4", "payme_transaction_voucher": "24****9", "payme_signature": "b71e**************ca5a", "payme_transaction_acquirer": null, "payme_transaction_arn": null, "session": "Ab12Cd34" } ``` ### 400 — Error `1`: no seller named (a partner key on its own). Error `352`: `sale_refund_amount` isn't a positive number. Error `369`: multi-capture has already captured part of this authorization. (error) | Field | Type | Description | | --- | --- | --- | | `status_code` | integer | `1`: the request failed. | | `status_error_code` | integer | PayMe's error code. See [Errors](/payments/getting-started/errors). | | `status_error_details` | string | The error message, in the language you asked for. | | `status_additional_info` | string | Extra detail: usually the field at fault, the sale's current status, or the masked card number. | | `session` | string | PayMe's ID for this request. Quote it when you contact PayMe. | ```json { "status_code": 1, "status_error_details": "Authorization cannot be voided", "status_additional_info": "", "status_error_code": 369, "session": "Ab12Cd34" } ``` ### 403 — Error `303`: the sale belongs to another partner or another seller. (error) | Field | Type | Description | | --- | --- | --- | | `status_code` | integer | `1`: the request failed. | | `status_error_code` | integer | PayMe's error code. See [Errors](/payments/getting-started/errors). | | `status_error_details` | string | The error message, in the language you asked for. | | `status_additional_info` | string | Extra detail: usually the field at fault, the sale's current status, or the masked card number. | | `session` | string | PayMe's ID for this request. Quote it when you contact PayMe. | ```json { "status_code": 1, "status_error_details": "Could not access sale", "status_additional_info": "", "status_error_code": 303, "session": "Ab12Cd34" } ``` ### 500 — Request errors. `305`: the sale isn't `authorized` or `partial-void`. `357`: this sale type can't be voided. `519`: the amount is above the sale price. `352`: a partial amount outside the account's limits. `363`: already fully voided. `520`: more than the hold that remains. `251`: unknown seller. `350`: no sale has this ID. (error) | Field | Type | Description | | --- | --- | --- | | `status_code` | integer | `1`: the request failed. | | `status_error_code` | integer | PayMe's error code. See [Errors](/payments/getting-started/errors). | | `status_error_details` | string | The error message, in the language you asked for. | | `status_additional_info` | string | Extra detail: usually the field at fault, the sale's current status, or the masked card number. | | `session` | string | PayMe's ID for this request. Quote it when you contact PayMe. | ```json { "status_code": 1, "status_error_details": "Cannot perform action due to an incorrect status", "status_additional_info": "completed", "status_error_code": 305, "session": "Ab12Cd34" } ```