# Create a payment contract Building with an AI agent? Start at https://docs.payme.io/agents.md - Sandbox: `POST https://sandbox.payme.io/api/payment-contracts` - Production: `POST https://live.payme.io/api/payment-contracts` ## Credentials - Header `PayMe-Merchant-Key` (API key) Creates a contract that sends a payment to another seller (`payment.type` `1`) or requests a payment from one (`payment.type` `2`). PayMe asks the recipient to approve it by SMS or email; once it is approved, the payment runs on `payment.due_date`. See [B2B payment contracts](/b2b-payments/contracts) for the setup, the statuses and the callbacks. - **Authentication:** send the initiator's `seller_payme_id` (MPL) in the `PayMe-Merchant-Key` header, or as `seller_payme_id` in the body. - **Amounts** are in minor units (`1000` = 10.00). - **Payment method:** for `credit-card` and `il-direct-debit`, send the payment method's `buyer_key` at the **top level** of the body, not inside `payment`. Without it, on a contract that sends a payment, PayMe charges the payer's attached card that is valid on the due date and expires last (or the last `Active` direct-debit authorization). - **Retries are safe:** a request that repeats the initiator, recipient, `amount`, `currency`, `payment.due_date` and `payment.type` of a contract created in the last 5 minutes (and not failed) returns that contract instead of creating a new one. ### Errors HTTP status | Error code | Message | When ---|---|---|--- `422` | `20` | (a validation message) | A required field is missing or a value is invalid. `status_additional_info` joins all the validation messages. `400` | `252` | Currency is not allowed for this seller | `currency` isn't enabled for the initiator or the recipient. `400` | `320` | Currency is not supported | `il-direct-debit` with a currency other than `ILS`. `400` | `25` | Please verify phone number | `recipient.phone` isn't a valid mobile or international number. `400` | `251` | Seller was not found | `recipient.payme_id` matches no seller. `400` | `381` | Not enough details for unique recipient identification | Only `recipient.business_code` was sent and it matches no PayMe seller. Add `email` or `phone`. `400` | `383` | Initiator and recipient are the same | The recipient is the initiator's own account. `400` | `384` | Recipient account settings problem - please contact Customer Support | The recipient's seller account is inactive. `400` | `389` | The payment method isn't attached to the payer's account, please attach it or use a different one | `credit-card` or `il-direct-debit`, and the payer has no such payment method attached. `400` | `390` | The credit card expiration date is lower than the payment's due date, please add another payment method | No attached card is valid on `payment.due_date`. `400` | `391` | We didn't find any bank debits we can use for that account, please create a new one or select a different payment method | No direct-debit authorization is `Active`. `400` | `392` | The selected payment method isn't matching the provided token | `buyer_key` isn't one of the payer's attached payment methods of this `payment.method`. `400` | `393` | The contract amount is over the maximum amount for the selected payment method | A `credit-card` or `il-direct-debit` contract over `5000000`. `400` | `394` | The selected payment method does not match the provided token | `buyer_key` sent with `funds-transfer`. `500` | `1` | Required parameter is missing | No seller key: neither the `PayMe-Merchant-Key` header nor `seller_payme_id` in the body. `status_additional_info` is `payme_client_key`. `500` | `251` | Seller was not found | The seller key is unknown. `500` | `163` | Account settings problem - please contact Customer Support | The initiator's seller account is inactive. `500` | `900` | Please provide all required information in JSON format. Make sure to include 'Content-Type: application/json' header | The request isn't JSON. ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `PayMe-Merchant-Key` | header | string | yes | The initiator's `seller_payme_id` (MPL). You can send `seller_payme_id` in the JSON body instead. | ## Request body Content type: `application/json` | Field | Type | Required | Description | | --- | --- | --- | --- | | `description` | string | yes | What the payment is for, for example an invoice number. The recipient sees it. | | `amount` | integer | yes | Amount in minor units (`1000` = 10.00). At least `1`. `credit-card` and `il-direct-debit` contracts: at most `5000000` (error `393`). | | `currency` | string | yes | The currency, as a 3-letter ISO 4217 code: ILS, USD, EUR, GBP, TRY, CHF, SEK, NOK, CAD, ZAR, JPY, AUD, AED, ARS, BHD, BRL, CNY, INR, KRW, KWD, KZT, MXN, NGN, PLN, RON, RUB, SAR, UAH, PHP, VND or HKD. It must be enabled for both sellers on the contract, otherwise the call returns error `252`. `il-direct-debit` supports only `ILS`. | | `buyer_key` | string | no | The payment method to charge: a `buyer_key` from `seller_credit_cards` or `seller_bank_authorizations` of [`get-sellers`](/payments/query-apis/post-get-sellers) for the payer. Send it at the top level, not inside `payment`. Only for `credit-card` and `il-direct-debit`; with `funds-transfer` it returns error `394`. If you omit it on a contract that sends a payment, PayMe charges the attached card that is valid on `payment.due_date` and expires last, or the last `Active` direct-debit authorization. | | `remote_id` | string | no | Your own reference for the contract: letters, digits, `-` and `_`. PayMe doesn't check that it is unique. | | `callback_url` | string | no | A valid URL that receives a callback on every status change. See [Callbacks](/b2b-payments/contracts#callbacks). | | `recipient` | object | yes | Who receives the payment (`payment.type` `1`) or is asked to pay (`payment.type` `2`). Send at least one of `payme_id`, `business_code`, `email` or `phone`; PayMe uses them to find the recipient's PayMe seller account. If no seller matches, PayMe still sends the approval request to the `email` or `phone`, and the recipient opens a PayMe account when approving. To target one specific seller, send `payme_id` alone. | | `recipient.payme_id` | string | no | The recipient's `seller_payme_id` (MPL), or the `seller_payme_id_checksum` from [List previous recipients](/b2b-payments/contracts/get-recipient). When you send it, send no other `recipient` field (validation error otherwise). | | `recipient.business_code` | integer | no | The recipient's business number. If it is the only identifier you send, it must match a PayMe seller (error `381` otherwise). | | `recipient.email` | string | no | The recipient's email address. PayMe sends the approval request here and uses it to find the recipient's seller account. | | `recipient.phone` | string | no | The recipient's mobile number: an Israeli mobile number (e.g. `0500000001`) or an international number with its country code. PayMe sends the approval request here by SMS and uses it to find the recipient's seller account. An invalid number returns error `25`. | | `recipient.name` | string | no | The recipient's name. Returned by [List previous recipients](/b2b-payments/contracts/get-recipient). | | `recipient.language` | string | no | The language of the notifications sent to the recipient: `he` or `en`. Default: `he`. If the recipient already has a PayMe seller account, that account's language is used instead. | | `payment` | object | yes | The payment details. | | `payment.type` | integer | yes | `1` – the initiator sends a payment to the recipient; `2` – the initiator requests a payment from the recipient. | | `payment.method` | string | yes | `funds-transfer` – from the payer's PayMe balance; `credit-card` – an attached card; `il-direct-debit` – an attached Israeli direct-debit authorization (`ILS` only). | | `payment.due_date` | string | yes | When the payment runs, `YYYY-MM-DD HH:MM:SS`. Must be in the future. | | `payment.recurring` | object | no | Send it to repeat the payment. The contract is recurring only when both fields are sent. | | `payment.recurring.frequency_type` | integer | no | How often the payment repeats: `1` day, `2` week, `3` month, `4` year, `5` quarter. | | `payment.recurring.iterations` | integer | no | Number of payments, `1` to `54`. Required when you send `frequency_type`. | ### Request example ```json { "description": "December payment", "amount": 500000, "currency": "ILS", "buyer_key": "BUYERDEMO-BUYERDEMO-BUYERDEMO-1234567", "remote_id": "INV-2026-12", "callback_url": "https://www.example.com/callback", "recipient": { "business_code": 515555555, "email": "supplier@example.com", "phone": "0500000001", "name": "Example Supplies Ltd", "language": "en" }, "payment": { "type": 1, "method": "credit-card", "due_date": "2026-12-22 10:00:00" } } ``` ## Code samples ### curl ```bash curl -X POST 'https://sandbox.payme.io/api/payment-contracts' \ -H 'Content-Type: application/json' \ -H 'PayMe-Merchant-Key: ' \ --data-raw '{ "description": "December payment", "amount": 500000, "currency": "ILS", "buyer_key": "BUYERDEMO-BUYERDEMO-BUYERDEMO-1234567", "remote_id": "INV-2026-12", "callback_url": "https://www.example.com/callback", "recipient": { "business_code": 515555555, "email": "supplier@example.com", "phone": "0500000001", "name": "Example Supplies Ltd", "language": "en" }, "payment": { "type": 1, "method": "credit-card", "due_date": "2026-12-22 10:00:00" } }' ``` ### Node.js ```javascript const response = await fetch("https://sandbox.payme.io/api/payment-contracts", { method: "POST", headers: { "Content-Type": "application/json", "PayMe-Merchant-Key": "", }, body: JSON.stringify({ "description": "December payment", "amount": 500000, "currency": "ILS", "buyer_key": "BUYERDEMO-BUYERDEMO-BUYERDEMO-1234567", "remote_id": "INV-2026-12", "callback_url": "https://www.example.com/callback", "recipient": { "business_code": 515555555, "email": "supplier@example.com", "phone": "0500000001", "name": "Example Supplies Ltd", "language": "en" }, "payment": { "type": 1, "method": "credit-card", "due_date": "2026-12-22 10:00:00" } }), }); console.log(await response.text()); ``` ### Python ```python import requests response = requests.post( "https://sandbox.payme.io/api/payment-contracts", headers={ "PayMe-Merchant-Key": "", }, json={ "description": "December payment", "amount": 500000, "currency": "ILS", "buyer_key": "BUYERDEMO-BUYERDEMO-BUYERDEMO-1234567", "remote_id": "INV-2026-12", "callback_url": "https://www.example.com/callback", "recipient": { "business_code": 515555555, "email": "supplier@example.com", "phone": "0500000001", "name": "Example Supplies Ltd", "language": "en", }, "payment": { "type": 1, "method": "credit-card", "due_date": "2026-12-22 10:00:00", }, }, ) print(response.text) ``` ### PHP ```php 'POST', CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'PayMe-Merchant-Key: ', ], CURLOPT_POSTFIELDS => $body, ]); $response = curl_exec($ch); curl_close($ch); echo $response; ``` ## Responses ### 201 — The contract was created (or, on a retry within 5 minutes, the existing contract is returned). The contract fields appear at the top level and again under `data`. (success) | Field | Type | Description | | --- | --- | --- | | `status_code` | integer | `0` on success. | | `session` | string | PayMe session ID. Quote it when you contact PayMe support. | | `id` | integer | PayMe's numeric ID of the contract. | | `guid` | string | The contract ID (`CONT…`). Use it in the contract URLs, e.g. `/payment-contracts/{guid}/invoices`. | | `description` | string | What the payment is for, as sent. | | `seller_payme_id` | string | The initiator's `seller_payme_id` (MPL). | | `remote_id` | string | Your reference, as sent. `null` if you didn't send one. | | `created_date` | string | When the contract was created, `YYYY-MM-DD HH:MM:SS`. | | `confirmed_by_recipient_date` | string | When the recipient approved, `YYYY-MM-DD HH:MM:SS`. `null` until then. | | `confirmed_by_initiator_date` | string | When the initiator side was confirmed, `YYYY-MM-DD HH:MM:SS`. `null` until then. | | `resolution_date` | string | Reserved. Currently always `null`. | | `status` | integer | Contract status: `1` initial, waiting for the recipient; `2` recipient confirmed (moves to `3` at once); `3` approved, waiting for the due date; `4` completed; `5` failed or cancelled (see `error_code`). | | `amount` | integer | Amount in minor units (`1000` = 10.00). | | `currency` | string | ISO 4217 currency code, e.g. `ILS`. | | `payme_signature` | string | Lowercase hex MD5 of the partner's client key, the partner's client password and the contract `guid`, joined with no separator. See [Callbacks](/b2b-payments/contracts#callbacks). | | `fast_reconciliation` | boolean | PayMe-internal processing flag. `false` for contracts created as described in these pages. | | `error_code` | integer | Why the contract failed: `11000`–`11004`, listed on [B2B payment contracts](/b2b-payments/contracts#errors). `null` while the contract hasn't failed. | | `error_note` | string | Free text about the failure, such as the cancellation note or the payment error message. `null` otherwise. | | `error_text` | string | The name of `error_code`, e.g. `Recipient Rejected`. `null` otherwise. | | `recipient` | object | The recipient details exactly as you sent them (only the keys you sent appear), plus `local_name` and `intl_name`. | | `recipient.payme_id` | string | As sent. | | `recipient.business_code` | integer | As sent: a number, or a string if you sent a string. | | `recipient.email` | string | As sent. | | `recipient.phone` | string | As sent. | | `recipient.name` | string | As sent. | | `recipient.language` | string | As sent. | | `recipient.local_name` | string | The recipient seller's registered business name. `""` while the recipient has no PayMe seller account. | | `recipient.intl_name` | string | The recipient seller's registered business name in English. `""` while the recipient has no PayMe seller account. | | `payment` | object | The payment details. | | `payment.type` | integer | `1` – the initiator sends a payment; `2` – the initiator requests a payment. | | `payment.method` | string | `funds-transfer`, `credit-card` or `il-direct-debit`. | | `payment.guarantee_type` | integer | Used by PayMe's financing services. Ignore it. | | `payment.due_date` | string | When the payment runs, `YYYY-MM-DD HH:MM:SS`. | | `payment.recurring` | object | Repetition of the payment. Both fields are `null` on a one-time contract. | | `payment.recurring.frequency_type` | integer | `1` day, `2` week, `3` month, `4` year, `5` quarter. | | `payment.recurring.iterations` | integer | Number of payments. | | `data` | object | The same contract fields as the top level, repeated. | ```json { "status_code": 0, "session": "FhT3aXe7", "id": 63, "guid": "CONT1700-000000AA-EXAMPLE1-CONT0001", "description": "December payment", "seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567", "remote_id": "INV-2026-12", "created_date": "2026-11-09 12:00:13", "confirmed_by_recipient_date": null, "confirmed_by_initiator_date": null, "resolution_date": null, "status": 1, "amount": 500000, "currency": "ILS", "payme_signature": "5dbf1885ce1d4e082757a2cd6086edd1", "fast_reconciliation": false, "error_code": null, "error_note": null, "error_text": null, "recipient": { "business_code": 515555555, "email": "supplier@example.com", "phone": "0500000001", "name": "Example Supplies Ltd", "language": "en", "local_name": "", "intl_name": "" }, "payment": { "type": 1, "method": "credit-card", "guarantee_type": null, "due_date": "2026-12-22 10:00:00", "recurring": { "frequency_type": null, "iterations": null } }, "data": { "id": 63, "guid": "CONT1700-000000AA-EXAMPLE1-CONT0001", "description": "December payment", "seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567", "remote_id": "INV-2026-12", "created_date": "2026-11-09 12:00:13", "confirmed_by_recipient_date": null, "confirmed_by_initiator_date": null, "resolution_date": null, "status": 1, "amount": 500000, "currency": "ILS", "payme_signature": "5dbf1885ce1d4e082757a2cd6086edd1", "fast_reconciliation": false, "error_code": null, "error_note": null, "error_text": null, "recipient": { "business_code": 515555555, "email": "supplier@example.com", "phone": "0500000001", "name": "Example Supplies Ltd", "language": "en", "local_name": "", "intl_name": "" }, "payment": { "type": 1, "method": "credit-card", "guarantee_type": null, "due_date": "2026-12-22 10:00:00", "recurring": { "frequency_type": null, "iterations": null } } } } ``` ### 422 — Validation error: a required field is missing or a value is invalid. (error) | Field | Type | Description | | --- | --- | --- | | `status_code` | integer | `1` on failure. | | `status_error_details` | string | The error message. | | `status_additional_info` | string | More detail, such as the field or value that failed. On `422`, every validation message, joined with spaces. | | `status_error_code` | integer | PayMe error code. See the error table above. | | `session` | string | PayMe session ID. Quote it when you contact PayMe support. | ```json { "status_code": 1, "status_error_details": "The payment.method field is required.", "status_additional_info": "The payment.method field is required.", "status_error_code": 20, "session": "FhT3aXe7" } ``` ### 400 — The request is valid but can't be carried out. See the error table above. (error) | Field | Type | Description | | --- | --- | --- | | `status_code` | integer | `1` on failure. | | `status_error_details` | string | The error message. | | `status_additional_info` | string | More detail, such as the field or value that failed. On `422`, every validation message, joined with spaces. | | `status_error_code` | integer | PayMe error code. See the error table above. | | `session` | string | PayMe session ID. Quote it when you contact PayMe support. | ```json { "status_code": 1, "status_error_details": "The contract amount is over the maximum amount for the selected payment method", "status_additional_info": "Max amount 5000000", "status_error_code": 393, "session": "FhT3aXe7" } ``` ### 500 — No seller key, an unknown or inactive seller, or a body that isn't JSON. (error) | Field | Type | Description | | --- | --- | --- | | `status_code` | integer | `1` on failure. | | `status_error_details` | string | The error message. | | `status_additional_info` | string | More detail, such as the field or value that failed. On `422`, every validation message, joined with spaces. | | `status_error_code` | integer | PayMe error code. See the error table above. | | `session` | string | PayMe session ID. Quote it when you contact PayMe support. | ```json { "status_code": 1, "status_error_details": "Required parameter is missing", "status_additional_info": "payme_client_key", "status_error_code": 1, "session": "FhT3aXe7" } ```