Payout Payment API
Do you have question? Ask us here
If you want to use or implement our API, please contact us on [email protected].
Production environment
https://app.payout.one
Sandbox environment
https://sandbox.payout.one
Sandbox is for test purposes only.
Authentication
Bearer
For accessing the API a valid token must be passed in all the queries in the 'Authorization' header. A valid token is obtained from POST /api/v1/authorize with the client_id and client_secret of an API key generated in Admin section of your account. The token is valid for the number of seconds returned in valid_for (6000); after that, request a new one.
The following syntax must be used in the 'Authorization' header :
Bearer <<token>>
So Authorization header can be like:
Authorization: Bearer SFMyNTY.EXAMPLE-TOKEN.dGhpcy1pcy1hLWZha2Utc2lnbmF0dXJlLWV4YW1wbGU
mTLS + QSEAL (M2M Withdrawals)
Withdrawals run on the mTLS hosts api-mtls-sandbox.payout.one and api-mtls.payout.one. Besides the bearer token, they need an approved QWAC for the TLS connection and, for requests that create or cancel, a QSEAL signature in Digest and X-JWS-Signature. See M2M Withdrawals and Certificates.
Errors
Errors are returned as JSON with an errors key. Its value is either a message or an object (or list of objects) with messages per field. Send Accept: application/json with every request.
401
{
"errors": "Bad credentials. Check your credentials or contact support."
}
401 - missing, invalid or expired token
{
"errors": "Unauthorized access. Check your token."
}
429 - after 5 failed POST /api/v1/authorize attempts for the same client_id within 5 minutes; retry after the number of seconds in the Retry-After header
{
"errors": "Too many failed authentication attempts for this client. Try again in a few minutes."
}
Exchanges the client_id and client_secret of your API key for a Bearer token. Send the token in the Authorization: Bearer <token> header of all other requests. The token is valid for valid_for seconds.
After 5 failed attempts for the same client_id within 5 minutes, the endpoint responds with 429 and a Retry-After: 300 header.
Request body
8b0f3c52-6d1e-4a7b-9c2d-5e4f3a2b1c0dexample-client-secret-not-realResponse 200
Authorization header · e.g. SFMyNTY.EXAMPLE-TOKEN.dGhpcy1pcy1hLWZha2Utc2lnbmF0dXJlLWV4YW1wbGU6000Other responses
client_id or client_secret is missing)client_idcurl -X POST 'https://sandbox.payout.one/api/v1/authorize' \
-H "Content-Type: application/json" \
-d '{
"client_id": "8b0f3c52-6d1e-4a7b-9c2d-5e4f3a2b1c0d",
"client_secret": "example-client-secret-not-real"
}'const res = await fetch("https://sandbox.payout.one/api/v1/authorize", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
"client_id": "8b0f3c52-6d1e-4a7b-9c2d-5e4f3a2b1c0d",
"client_secret": "example-client-secret-not-real"
}),
});
const data = await res.json();import os, requests
res = requests.post(
"https://sandbox.payout.one/api/v1/authorize",
json={
"client_id": "8b0f3c52-6d1e-4a7b-9c2d-5e4f3a2b1c0d",
"client_secret": "example-client-secret-not-real",
},
)
data = res.json()<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://sandbox.payout.one/api/v1/authorize");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
"client_id" => "8b0f3c52-6d1e-4a7b-9c2d-5e4f3a2b1c0d",
"client_secret" => "example-client-secret-not-real"
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);{
"token": "SFMyNTY.EXAMPLE-TOKEN.dGhpcy1pcy1hLWZha2Utc2lnbmF0dXJlLWV4YW1wbGU",
"valid_for": 6000
}This request create checkout for payment where customer will authorize payment method for transaction.
To perform an idempotent request, provide an additional Idempotency-Key: <key> header to the request. An idempotency key is a unique value generated by the client which the server uses to recognize subsequent retries of the same request. How you create unique keys is up to you, but we suggest using v4 UUIDs, or another random string with enough entropy to avoid collisions. If a checkout with the same key already exists for your account, it is returned with status 200; if its amount differs, the request fails with 409.
Use mode for pre-authorization with later capture (pre_authorization), storing a card (store_card), payments with a stored card (card_on_file) and recurrent payments (recurrent). These modes must be enabled for your account.
How to create the signature
Signature is created by few steps. First step is to create string concatenated with character | by joining arguments in following order:
amount(exactly as sent in the request)currencyexternal_idnonceclient_secret(obtained from merchant's API key)
After this step we should have string that looks like this: amount|currency|external_id|nonce|client_secret
Now, we use SHA256 hashing algorithm to hash this string and encode it using Base16 in lowercase. The signature is now complete and you can send it with the request.
Checkout statuses:
processing- created payment formrequires_capture- additional authorization requested (if some gateway has additional authorization)succeeded- from checkout created transactionexpired- no transaction has been created. Expires after the checkout expiration time of your account (by default 10 days after creation)
Other statuses the API can return: requires_payment_method, requires_action, requires_authorization, requires_3ds, pisp_processing, awaiting_confirmation, cancelled, failed.
Parameters
31f0ac6a-9ea6-01a7-7998-720437afb34cRequest body
1050EURJohnDoefirst_name and last_name; responses always contain it. e.g. John Doe[email protected]+ are removed. e.g. +421900000000nullf0ac316a-9ea6-7998-01a7-720437afb34cIdempotency-Key header; when the header is sent, its value replaces this field. max 50 · e.g. 31f0ac6a-9ea6-01a7-7998-720437afb34c{"source": "eshop"}ZUc0Mk9sVXZDOXNsdklzMQhttps://eshop.example.com/payment/redirect5a940ff7f1698f5d334527951519c84fa104c77ecf6691936093835bcac14d52pre_authorization only authorizes the amount on the card, capture it later with the capture endpoint. store_card stores the card and sends its token in the payu_token.created webhook. card_on_file pays with a stored card (requires card_token). recurrent makes a recurrent payment (requires recurrent_token). one of standard, pre_authorization, store_card, card_on_file, recurrent · default standardmode store_card. true requires recurrent payments to be enabled for your account. default falsepayu_token.created webhook. Required when mode is recurrent.payu_token.created webhook. Required when mode is card_on_file.card, apple_pay, pisp, bank_transfer). If the value is not available for your account, the customer sees all available methods. e.g. cardSK3112000000198742637541John DoeMain Street 1Flat 281101BratislavaSKJohn DoeMain Street 1Flat 281101BratislavaSKProduct 135032026-10-20PREMIUMoffer_id (transaction splitting must be enabled for your account). The sum of unit_price * quantity of all products must equal amount. default falseResponse 201
checkout141447f0ac316a-9ea6-7998-01a7-720437afb34c1050EURhttps://eshop.example.com/payment/redirect31f0ac6a-9ea6-01a7-7998-720437afb34cJohnDoefirst_name and last_name; responses always contain it. e.g. John Doe[email protected]+ are removed. e.g. +421900000000nullhttps://sandbox.payout.one/checkouts/U0ZNeU5UWS5FWEFNUExFLUNIRUNLT1VULVRPS0VOLm5vdC1hLXJlYWwtc2lnbmF0dXJl/?account_id=R…{"source": "eshop"}processing, requires_payment_method, requires_action, requires_authorization, requires_capture, requires_3ds, pisp_processing, awaiting_confirmation, succeeded, expired, cancelled, failed · e.g. processingZUc0Mk9sVXZDOXNsdklzMQ5a940ff7f1698f5d334527951519c84fa104c77ecf6691936093835bcac14d52null if there is none. See Section with Payment Attributespayment, bank_transfer · e.g. paymentpending, in_transit, successful, failed, expired, refunded, partialy_refunded · e.g. successfulcard""1759744800pending, available, onhold, canceled · e.g. available301020bank_transfer objects. e.g. CZ6508000000192000145399name is encrypted with your API key (see the Checkout verification webhook guide).<encrypted>iban is encrypted with your API key.<encrypted>payment, bank_transfer · e.g. paymentpending, in_transit, successful, failed, expired, refunded, partialy_refunded · e.g. successfulcard""1759744800pending, available, onhold, canceled · e.g. available301020bank_transfer objects. e.g. CZ6508000000192000145399name is encrypted with your API key (see the Checkout verification webhook guide).<encrypted>iban is encrypted with your API key.<encrypted>null if not sentJohn DoeMain Street 1Flat 281101BratislavaSKnull if not sentJohn DoeMain Street 1Flat 281101BratislavaSKnull if not sentProduct 13350U0ZNeU5UWS5FWEFNUExFLUNIRUNLT1VULVRPS0VOLm5vdC1hLXJlYWwtc2lnbmF0dXJltrue for succeeded checkouts. e.g. falseOther responses
recurrent_token or card_tokenIdempotency-Key but a different amount already existscurl -X POST 'https://sandbox.payout.one/api/v1/checkouts' \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"amount": 1050,
"currency": "EUR",
"customer": {
"first_name": "John",
"last_name": "Doe",
"email": "[email protected]"
},
"external_id": "f0ac316a-9ea6-7998-01a7-720437afb34c",
"nonce": "ZUc0Mk9sVXZDOXNsdklzMQ",
"metadata": {
"note": "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been."
},
"redirect_url": "https://eshop.example.com/payment/redirect",
"signature": "5a940ff7f1698f5d334527951519c84fa104c77ecf6691936093835bcac14d52"
}'const res = await fetch("https://sandbox.payout.one/api/v1/checkouts", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PAYOUT_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"amount": 1050,
"currency": "EUR",
"customer": {
"first_name": "John",
"last_name": "Doe",
"email": "[email protected]"
},
"external_id": "f0ac316a-9ea6-7998-01a7-720437afb34c",
"nonce": "ZUc0Mk9sVXZDOXNsdklzMQ",
"metadata": {
"note": "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been."
},
"redirect_url": "https://eshop.example.com/payment/redirect",
"signature": "5a940ff7f1698f5d334527951519c84fa104c77ecf6691936093835bcac14d52"
}),
});
const data = await res.json();import os, requests
res = requests.post(
"https://sandbox.payout.one/api/v1/checkouts",
headers={"Authorization": f"Bearer {os.environ['PAYOUT_TOKEN']}"},
json={
"amount": 1050,
"currency": "EUR",
"customer": {
"first_name": "John",
"last_name": "Doe",
"email": "[email protected]",
},
"external_id": "f0ac316a-9ea6-7998-01a7-720437afb34c",
"nonce": "ZUc0Mk9sVXZDOXNsdklzMQ",
"metadata": {
"note": "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been.",
},
"redirect_url": "https://eshop.example.com/payment/redirect",
"signature": "5a940ff7f1698f5d334527951519c84fa104c77ecf6691936093835bcac14d52",
},
)
data = res.json()<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://sandbox.payout.one/api/v1/checkouts");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
"amount" => 1050,
"currency" => "EUR",
"customer" => [
"first_name" => "John",
"last_name" => "Doe",
"email" => "[email protected]"
],
"external_id" => "f0ac316a-9ea6-7998-01a7-720437afb34c",
"nonce" => "ZUc0Mk9sVXZDOXNsdklzMQ",
"metadata" => [
"note" => "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been."
],
"redirect_url" => "https://eshop.example.com/payment/redirect",
"signature" => "5a940ff7f1698f5d334527951519c84fa104c77ecf6691936093835bcac14d52"
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . getenv("PAYOUT_TOKEN"), "Content-Type: application/json"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);{
"object": "checkout",
"id": 141447,
"external_id": "f0ac316a-9ea6-7998-01a7-720437afb34c",
"amount": 1050,
"currency": "EUR",
"redirect_url": "https://eshop.example.com/payment/redirect",
"idempotency_key": "31f0ac6a-9ea6-01a7-7998-720437afb34c",
"customer": {
"first_name": "John",
"last_name": "Doe",
"name": "John Doe",
"email": "[email protected]",
"phone": null,
"note": null
},
"checkout_url": "https://sandbox.payout.one/checkouts/U0ZNeU5UWS5FWEFNUExFLUNIRUNLT1VULVRPS0VOLm5vdC1hLXJlYWwtc2lnbmF0dXJl/?account_id=RVhBTVBMRS1BQ0NPVU5ULVRPS0VOLTAwMDAwMDAwMDA",
"metadata": {
"note": "Lorem Ipsum is simply dummy text of the printing and typesetting industry."
},
"status": "processing",
"nonce": "WWdVaGk4d3ZqeHFOTjM4Qw",
"signature": "5a940ff7f1698f5d334527951519c84fa104c77ecf6691936093835bcac14d52",
"payment": null,
"all_payments": [],
"billing_address": null,
"shipping_address": null,
"products": null,
"payment_token": "U0ZNeU5UWS5FWEFNUExFLUNIRUNLT1VULVRPS0VOLm5vdC1hLXJlYWwtc2lnbmF0dXJl",
"is_status_final": false
}You can use this request to retrieve list of checkouts. The newest checkouts are returned first.
Parameters
100Response 200 (array)
checkout141447f0ac316a-9ea6-7998-01a7-720437afb34c1050EURhttps://eshop.example.com/payment/redirect31f0ac6a-9ea6-01a7-7998-720437afb34cJohnDoefirst_name and last_name; responses always contain it. e.g. John Doe[email protected]+ are removed. e.g. +421900000000nullhttps://sandbox.payout.one/checkouts/U0ZNeU5UWS5FWEFNUExFLUNIRUNLT1VULVRPS0VOLm5vdC1hLXJlYWwtc2lnbmF0dXJl/?account_id=R…{"source": "eshop"}processing, requires_payment_method, requires_action, requires_authorization, requires_capture, requires_3ds, pisp_processing, awaiting_confirmation, succeeded, expired, cancelled, failed · e.g. processingZUc0Mk9sVXZDOXNsdklzMQ5a940ff7f1698f5d334527951519c84fa104c77ecf6691936093835bcac14d52null if there is none. See Section with Payment Attributespayment, bank_transfer · e.g. paymentpending, in_transit, successful, failed, expired, refunded, partialy_refunded · e.g. successfulcard""1759744800pending, available, onhold, canceled · e.g. available301020bank_transfer objects. e.g. CZ6508000000192000145399name is encrypted with your API key (see the Checkout verification webhook guide).<encrypted>iban is encrypted with your API key.<encrypted>payment, bank_transfer · e.g. paymentpending, in_transit, successful, failed, expired, refunded, partialy_refunded · e.g. successfulcard""1759744800pending, available, onhold, canceled · e.g. available301020bank_transfer objects. e.g. CZ6508000000192000145399name is encrypted with your API key (see the Checkout verification webhook guide).<encrypted>iban is encrypted with your API key.<encrypted>null if not sentJohn DoeMain Street 1Flat 281101BratislavaSKnull if not sentJohn DoeMain Street 1Flat 281101BratislavaSKnull if not sentProduct 13350U0ZNeU5UWS5FWEFNUExFLUNIRUNLT1VULVRPS0VOLm5vdC1hLXJlYWwtc2lnbmF0dXJltrue for succeeded checkouts. e.g. falseOther responses
curl -X GET 'https://sandbox.payout.one/api/v1/checkouts' \
-H "Authorization: Bearer $TOKEN"const res = await fetch("https://sandbox.payout.one/api/v1/checkouts", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.PAYOUT_TOKEN}`,
},
});
const data = await res.json();import os, requests
res = requests.get(
"https://sandbox.payout.one/api/v1/checkouts",
headers={"Authorization": f"Bearer {os.environ['PAYOUT_TOKEN']}"},
)
data = res.json()<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://sandbox.payout.one/api/v1/checkouts");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . getenv("PAYOUT_TOKEN")]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);[
{
"object": "checkout",
"id": 141447,
"external_id": "f0ac316a-9ea6-7998-01a7-720437afb34c",
"amount": 1050,
"currency": "EUR",
"redirect_url": "https://eshop.example.com/payment/redirect",
"idempotency_key": "31f0ac6a-9ea6-01a7-7998-720437afb34c",
"customer": {
"first_name": "John",
"last_name": "Doe",
"name": "John Doe",
"email": "[email protected]",
"phone": "+421900000000"
},
"checkout_url": "https://sandbox.payout.one/checkouts/U0ZNeU5UWS5FWEFNUExFLUNIRUNLT1VULVRPS0VOLm5vdC1hLXJlYWwtc2lnbmF0dXJl/?account_id=RVhBTVBMRS1BQ0NPVU5ULVRPS0VOLTAwMDAwMDAwMDA",
"metadata": {
"source": "eshop"
},
"status": "processing",
"nonce": "ZUc0Mk9sVXZDOXNsdklzMQ",
"signature": "5a940ff7f1698f5d334527951519c84fa104c77ecf6691936093835bcac14d52",
"payment": {
"object": "payment",
"status": "successful",
"payment_method": "card",
"failure_reason": "",
"created_at": 1759744800,
"funds": "available",
"fee": 30,
"net": 1020,
"iban": "CZ6508000000192000145399",
"account_details": {
"name": "<encrypted>"
},
"customer": {
"iban": "<encrypted>"
}
},
"all_payments": [
{
"object": "payment",
"status": "successful",
"payment_method": "card",
"failure_reason": "",
"created_at": 1759744800,
"funds": "available",
"fee": 30,
"net": 1020,
"iban": "CZ6508000000192000145399",
"account_details": {
"name": "<encrypted>"
},
"customer": {
"iban": "<encrypted>"
}
}
],
"billing_address": {
"name": "John Doe",
"address_line_1": "Main Street 1",
"address_line_2": "Flat 2",
"postal_code": "81101",
"city": "Bratislava",
"country_code": "SK"
},
"shipping_address": {
"name": "John Doe",
"address_line_1": "Main Street 1",
"address_line_2": "Flat 2",
"postal_code": "81101",
"city": "Bratislava",
"country_code": "SK"
},
"products": [
{
"name": "Product 1",
"quantity": 3,
"unit_price": 350
}
],
"payment_token": "U0ZNeU5UWS5FWEFNUExFLUNIRUNLT1VULVRPS0VOLm5vdC1hLXJlYWwtc2lnbmF0dXJl",
"is_status_final": false
}
]You can use this request to retrieve information about specified checkout.
How to verify the signature
Signature is created by few steps. First step is to create string concatenated with character | by joining arguments in following order:
amountcurrencyexternal_idnonceclient_secret(obtained from merchant's API key)
After this step we should have string that looks like this: amount|currency|external_id|nonce|client_secret
Now, we use SHA256 hashing algorithm to hash this string and encode it using Base16 in lowercase. Compare the result with signature from the response.
Checkout statuses:
processing- created payment formrequires_capture- additional authorization requested (if some gateway has additional authorization)succeeded- from checkout created transactionexpired- no transaction has been created. Expires after the checkout expiration time of your account (by default 10 days after creation)
Other statuses the API can return: requires_payment_method, requires_action, requires_authorization, requires_3ds, pisp_processing, awaiting_confirmation, cancelled, failed.
Payment statuses:
pending- this status should not create because the payment card is verified onlinesuccessful- confirmation from acquirer that the transaction was successfulfailed- confirmation from acquirer that the transaction was failedrefunded- transaction refund (automatically via API)partialy_refunded- part of the payment was refunded
Bank transfer statuses:
in_transit- created bank transfersuccessful- after reconciliation of the bank statement. (automatic matching)refunded- transaction refund (manually via IS Payout)partialy_refunded- part of the transfer was refundedfailed,expired
Funds statuses:
pending- processed transaction, payment affecting pending balanceavailable- processed transaction, payment affecting available balanceonhold,canceled
Parameters
141447Response 200
checkout141447f0ac316a-9ea6-7998-01a7-720437afb34c1050EURhttps://eshop.example.com/payment/redirect31f0ac6a-9ea6-01a7-7998-720437afb34cJohnDoefirst_name and last_name; responses always contain it. e.g. John Doe[email protected]+ are removed. e.g. +421900000000nullhttps://sandbox.payout.one/checkouts/U0ZNeU5UWS5FWEFNUExFLUNIRUNLT1VULVRPS0VOLm5vdC1hLXJlYWwtc2lnbmF0dXJl/?account_id=R…{"source": "eshop"}processing, requires_payment_method, requires_action, requires_authorization, requires_capture, requires_3ds, pisp_processing, awaiting_confirmation, succeeded, expired, cancelled, failed · e.g. processingZUc0Mk9sVXZDOXNsdklzMQ5a940ff7f1698f5d334527951519c84fa104c77ecf6691936093835bcac14d52null if there is none. See Section with Payment Attributespayment, bank_transfer · e.g. paymentpending, in_transit, successful, failed, expired, refunded, partialy_refunded · e.g. successfulcard""1759744800pending, available, onhold, canceled · e.g. available301020bank_transfer objects. e.g. CZ6508000000192000145399name is encrypted with your API key (see the Checkout verification webhook guide).<encrypted>iban is encrypted with your API key.<encrypted>payment, bank_transfer · e.g. paymentpending, in_transit, successful, failed, expired, refunded, partialy_refunded · e.g. successfulcard""1759744800pending, available, onhold, canceled · e.g. available301020bank_transfer objects. e.g. CZ6508000000192000145399name is encrypted with your API key (see the Checkout verification webhook guide).<encrypted>iban is encrypted with your API key.<encrypted>null if not sentJohn DoeMain Street 1Flat 281101BratislavaSKnull if not sentJohn DoeMain Street 1Flat 281101BratislavaSKnull if not sentProduct 13350U0ZNeU5UWS5FWEFNUExFLUNIRUNLT1VULVRPS0VOLm5vdC1hLXJlYWwtc2lnbmF0dXJltrue for succeeded checkouts. e.g. falseOther responses
curl -X GET 'https://sandbox.payout.one/api/v1/checkouts/{checkout_id}' \
-H "Authorization: Bearer $TOKEN"const res = await fetch("https://sandbox.payout.one/api/v1/checkouts/{checkout_id}", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.PAYOUT_TOKEN}`,
},
});
const data = await res.json();import os, requests
res = requests.get(
"https://sandbox.payout.one/api/v1/checkouts/{checkout_id}",
headers={"Authorization": f"Bearer {os.environ['PAYOUT_TOKEN']}"},
)
data = res.json()<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://sandbox.payout.one/api/v1/checkouts/{checkout_id}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . getenv("PAYOUT_TOKEN")]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);{
"object": "checkout",
"id": 141447,
"external_id": "f0ac316a-9ea6-7998-01a7-720437afb34c",
"amount": 1050,
"currency": "EUR",
"redirect_url": "https://eshop.example.com/payment/redirect",
"idempotency_key": "31f0ac6a-9ea6-01a7-7998-720437afb34c",
"customer": {
"first_name": "John",
"last_name": "Doe",
"name": "John Doe",
"email": "[email protected]",
"phone": null,
"note": null
},
"checkout_url": "https://sandbox.payout.one/checkouts/U0ZNeU5UWS5FWEFNUExFLUNIRUNLT1VULVRPS0VOLm5vdC1hLXJlYWwtc2lnbmF0dXJl/?account_id=RVhBTVBMRS1BQ0NPVU5ULVRPS0VOLTAwMDAwMDAwMDA",
"metadata": {
"note": "Lorem Ipsum is simply dummy text of the printing and typesetting industry."
},
"status": "succeeded",
"nonce": "OVkzUFBFcFM0QnhzQmR4Uw",
"signature": "ceea2fdac8d191a5bc49f54447f2ab2d187c74bd5c75c544c7e176b34b0d4fbf",
"payment": {
"object": "payment",
"status": "successful",
"payment_method": "card",
"failure_reason": "",
"created_at": 1759744800,
"funds": "available",
"fee": 30,
"net": 1020
},
"all_payments": [
{
"object": "payment",
"status": "successful",
"payment_method": "card",
"failure_reason": "",
"created_at": 1759744800,
"funds": "available",
"fee": 30,
"net": 1020
}
],
"billing_address": null,
"shipping_address": null,
"products": null,
"payment_token": "U0ZNeU5UWS5FWEFNUExFLUNIRUNLT1VULVRPS0VOLm5vdC1hLXJlYWwtc2lnbmF0dXJl",
"is_status_final": true
}Cancels a checkout created with mode pre_authorization, for example if a product or service is not delivered. Only checkouts for which the checkout.captured webhook was not triggered can be cancelled.
On success the checkout status changes to failed and a checkout.canceled webhook is sent.
Parameters
141447Responses
mode pre_authorizationcurl -X DELETE 'https://sandbox.payout.one/api/v1/checkouts/{checkout_id}' \
-H "Authorization: Bearer $TOKEN"const res = await fetch("https://sandbox.payout.one/api/v1/checkouts/{checkout_id}", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.PAYOUT_TOKEN}`,
},
});
const data = await res.json();import os, requests
res = requests.delete(
"https://sandbox.payout.one/api/v1/checkouts/{checkout_id}",
headers={"Authorization": f"Bearer {os.environ['PAYOUT_TOKEN']}"},
)
data = res.json()<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://sandbox.payout.one/api/v1/checkouts/{checkout_id}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . getenv("PAYOUT_TOKEN")]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);"pre-auth canceled"In the case of card payments, it is possible to authorize a certain order amount (checkout created with mode pre_authorization) and capture full or only a portion of the funds deposited. This feature needs to be enabled for your account.
Send an empty body to capture the whole pre-authorized amount, or amount for a partial capture. After successful capture of funds the checkout.captured webhook is sent.
Parameters
141447Request body
150Responses
amount is larger than the checkout amountcurl -X POST 'https://sandbox.payout.one/api/v1/checkouts/{checkout_id}/capture' \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"amount": 150
}'const res = await fetch("https://sandbox.payout.one/api/v1/checkouts/{checkout_id}/capture", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PAYOUT_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"amount": 150
}),
});
const data = await res.json();import os, requests
res = requests.post(
"https://sandbox.payout.one/api/v1/checkouts/{checkout_id}/capture",
headers={"Authorization": f"Bearer {os.environ['PAYOUT_TOKEN']}"},
json={
"amount": 150,
},
)
data = res.json()<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://sandbox.payout.one/api/v1/checkouts/{checkout_id}/capture");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
"amount" => 150
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . getenv("PAYOUT_TOKEN"), "Content-Type: application/json"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);"captured"Retrieves the data your customer needs to pay a checkout manually via bank transfer — recipient name, IBAN, local account number (CZ/SK), variable symbol, amount, currency and a ready-to-render QR code. No email is sent by this endpoint; it is intended for cases where you want to render the instructions in your own UI.
Bank transfer must be enabled for your account in the checkout's currency. The QR code is cached, so repeated calls for the same checkout return the same image and are safe to retry.
Parameters
141447Response 200
Payout a.s.SK3112000000198742637541prefix-account/bank_code). Filled for Czech (CZ) and Slovak (SK) IBANs, otherwise null. e.g. 000019-8742637541/1200100012341110.5000EURiVBORw0KGgoAAAANSUhEUgAAAX8AAAHBCAYAAACBh...Other responses
curl -X GET 'https://sandbox.payout.one/api/v1/checkouts/{checkout_id}/payment_instructions' \
-H "Authorization: Bearer $TOKEN"const res = await fetch("https://sandbox.payout.one/api/v1/checkouts/{checkout_id}/payment_instructions", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.PAYOUT_TOKEN}`,
},
});
const data = await res.json();import os, requests
res = requests.get(
"https://sandbox.payout.one/api/v1/checkouts/{checkout_id}/payment_instructions",
headers={"Authorization": f"Bearer {os.environ['PAYOUT_TOKEN']}"},
)
data = res.json()<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://sandbox.payout.one/api/v1/checkouts/{checkout_id}/payment_instructions");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . getenv("PAYOUT_TOKEN")]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);{
"recipient_name": "Payout a.s.",
"iban": "SK3112000000198742637541",
"account_number": "000019-8742637541/1200",
"variable_symbol": "1000123411",
"amount": "10.5000",
"currency": "EUR",
"qr_code": "iVBORw0KGgoAAAANSUhEUgAAAX8AAAHBCAYAAACBh..."
}Sends money from your Payout balance to the given IBAN.
Call it on the mTLS host with an approved QWAC, and sign the request with your QSEAL certificate: Digest is the SHA-256 of the exact request body, X-JWS-Signature a detached JWS over that Digest value. How to obtain and import the certificates and how to build the signature is described in M2M Withdrawals and Certificates.
To perform an idempotent request, provide an additional Idempotency-Key: <key> header. If a withdrawal with the same key already exists for your account, it is returned instead of creating a new one.
Withdrawal statuses:
pending- manualy created withdrawalin_transit- process withdrawalpaid- processed withdrawalcanceled- cancelled withdrawalfailed- failed withdrawal
Parameters
31f0ac6a-9ea6-01a7-7998-720437afb34cSHA-256= followed by the Base64 SHA-256 of the exact request body (of an empty body when there is none) · e.g. SHA-256=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=<protected header>..<signature>) over the Digest value, made with your QSEAL key. The protected header carries x5t#S256 (QSEAL thumbprint) and sigT (signing time, at most 5 minutes off). e.g. eyJhbGciOiJQUzI1NiIsIng1dCNTMjU2IjoiLi4uIn0..c2lnbmF0dXJlRequest body
1050EURSK3112000000198742637541JohnDoefirst_name and last_name; responses always contain it. e.g. John Doe[email protected]+ are removed. e.g. +421900000000nullPAYOUT-2026-0001/-?:().,'+ are allowed. max 140 · e.g. Simple statement descriptionResponse 201
52331withdrawal105042EURPAYOUT-2026-0001SK3112000000198742637541Idempotency-Key header of the request that created the withdrawal · e.g. 7c9e6679-7425-40de-944b-e07fc1f90ae7pending, in_transit, paid, canceled, failed · e.g. pending{}Simple statement description1759744800ZUc0Mk9sVXZDOXNsdklzMQJohnDoefirst_name and last_name; responses always contain it. e.g. John Doe[email protected]+ are removed. e.g. +421900000000nullcee91937a98c89ea53440e84d367713ba6d196cbd0bf639f15f2f9c05b4762e9Other responses
iban missing, insufficient or zero balance, or invalid or not allowed currencyDigest that does not match the body, or sigT more than 5 minutes offBODY='{
"amount": 1050,
"currency": "EUR",
"external_id": "PAYOUT-2026-0001",
"iban": "SK3112000000198742637541",
"customer": {
"first_name": "John",
"last_name": "Doe",
"email": "[email protected]"
},
"statement_descriptor": "Simple statement description"
}'
DIGEST="SHA-256=$(printf %s "$BODY" | openssl dgst -sha256 -binary | base64)"
JWS_SIGNATURE="<detached JWS over $DIGEST>" # QSEAL key, see M2M Withdrawals: Signing payment instructions with QSEAL
curl -X POST 'https://api-mtls-sandbox.payout.one/api/v2/withdrawals' \
--cert qwac.pem --key qwac.key \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Digest: $DIGEST" \
-H "X-JWS-Signature: $JWS_SIGNATURE" \
-d "$BODY"import { readFileSync } from "node:fs";
import { createHash } from "node:crypto";
import { Agent } from "undici";
const dispatcher = new Agent({ connect: { cert: readFileSync("qwac.pem"), key: readFileSync("qwac.key") } });
const body = JSON.stringify({
"amount": 1050,
"currency": "EUR",
"external_id": "PAYOUT-2026-0001",
"iban": "SK3112000000198742637541",
"customer": {
"first_name": "John",
"last_name": "Doe",
"email": "[email protected]"
},
"statement_descriptor": "Simple statement description"
});
const digest = "SHA-256=" + createHash("sha256").update(body).digest("base64");
const jwsSignature = signDetachedJws(digest); // QSEAL key, see M2M Withdrawals: Signing payment instructions with QSEAL
const res = await fetch("https://api-mtls-sandbox.payout.one/api/v2/withdrawals", {
method: "POST",
dispatcher,
headers: {
Authorization: `Bearer ${process.env.PAYOUT_TOKEN}`,
"Content-Type": "application/json",
Digest: digest,
"X-JWS-Signature": jwsSignature,
},
body,
});
const data = await res.json();import base64, hashlib, json, os, requests
body = json.dumps({
"amount": 1050,
"currency": "EUR",
"external_id": "PAYOUT-2026-0001",
"iban": "SK3112000000198742637541",
"customer": {
"first_name": "John",
"last_name": "Doe",
"email": "[email protected]",
},
"statement_descriptor": "Simple statement description",
})
digest = "SHA-256=" + base64.b64encode(hashlib.sha256(body.encode()).digest()).decode()
jws_signature = sign_detached_jws(digest) # QSEAL key, see M2M Withdrawals: Signing payment instructions with QSEAL
res = requests.post(
"https://api-mtls-sandbox.payout.one/api/v2/withdrawals",
cert=("qwac.pem", "qwac.key"),
headers={
"Authorization": f"Bearer {os.environ['PAYOUT_TOKEN']}",
"Content-Type": "application/json",
"Digest": digest,
"X-JWS-Signature": jws_signature,
},
data=body,
)
data = res.json()<?php
$body = json_encode([
"amount" => 1050,
"currency" => "EUR",
"external_id" => "PAYOUT-2026-0001",
"iban" => "SK3112000000198742637541",
"customer" => [
"first_name" => "John",
"last_name" => "Doe",
"email" => "[email protected]"
],
"statement_descriptor" => "Simple statement description"
]);
$digest = "SHA-256=" . base64_encode(hash("sha256", $body, true));
$jwsSignature = sign_detached_jws($digest); // QSEAL key, see M2M Withdrawals: Signing payment instructions with QSEAL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api-mtls-sandbox.payout.one/api/v2/withdrawals");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_SSLCERT, "qwac.pem");
curl_setopt($ch, CURLOPT_SSLKEY, "qwac.key");
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . getenv("PAYOUT_TOKEN"), "Content-Type: application/json", "Digest: " . $digest, "X-JWS-Signature: " . $jwsSignature]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);{
"id": 52331,
"object": "withdrawal",
"amount": 1050,
"api_key_id": 42,
"currency": "EUR",
"external_id": "PAYOUT-2026-0001",
"iban": "SK3112000000198742637541",
"idempotency_key": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"status": "pending",
"metadata": {},
"statement_descriptor": "Simple statement description",
"created_at": 1759744800,
"nonce": "ZUc0Mk9sVXZDOXNsdklzMQ",
"customer": {
"first_name": "John",
"last_name": "Doe",
"name": "John Doe",
"email": "[email protected]",
"phone": "+421900000000"
},
"signature": "cee91937a98c89ea53440e84d367713ba6d196cbd0bf639f15f2f9c05b4762e9"
}Lists the withdrawals of your account. Read-only requests need the bearer token and the QWAC, no QSEAL signature.
Parameters
ASC, DESC · default DESCResponse 200 (array)
52331withdrawal105042EURPAYOUT-2026-0001SK3112000000198742637541Idempotency-Key header of the request that created the withdrawal · e.g. 7c9e6679-7425-40de-944b-e07fc1f90ae7pending, in_transit, paid, canceled, failed · e.g. pending{}Simple statement description1759744800ZUc0Mk9sVXZDOXNsdklzMQJohnDoefirst_name and last_name; responses always contain it. e.g. John Doe[email protected]+ are removed. e.g. +421900000000nullcee91937a98c89ea53440e84d367713ba6d196cbd0bf639f15f2f9c05b4762e9Other responses
curl -X GET 'https://api-mtls-sandbox.payout.one/api/v2/withdrawals' \
--cert qwac.pem --key qwac.key \
-H "Authorization: Bearer $TOKEN"import { readFileSync } from "node:fs";
import { Agent } from "undici";
const dispatcher = new Agent({ connect: { cert: readFileSync("qwac.pem"), key: readFileSync("qwac.key") } });
const res = await fetch("https://api-mtls-sandbox.payout.one/api/v2/withdrawals", {
method: "GET",
dispatcher,
headers: {
Authorization: `Bearer ${process.env.PAYOUT_TOKEN}`,
},
});
const data = await res.json();import os, requests
res = requests.get(
"https://api-mtls-sandbox.payout.one/api/v2/withdrawals",
cert=("qwac.pem", "qwac.key"),
headers={
"Authorization": f"Bearer {os.environ['PAYOUT_TOKEN']}",
},
)
data = res.json()<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api-mtls-sandbox.payout.one/api/v2/withdrawals");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_SSLCERT, "qwac.pem");
curl_setopt($ch, CURLOPT_SSLKEY, "qwac.key");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . getenv("PAYOUT_TOKEN")]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);[
{
"id": 52331,
"object": "withdrawal",
"amount": 1050,
"api_key_id": 42,
"currency": "EUR",
"external_id": "PAYOUT-2026-0001",
"iban": "SK3112000000198742637541",
"idempotency_key": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"status": "pending",
"metadata": {},
"statement_descriptor": "Simple statement description",
"created_at": 1759744800,
"nonce": "ZUc0Mk9sVXZDOXNsdklzMQ",
"customer": {
"first_name": "John",
"last_name": "Doe",
"name": "John Doe",
"email": "[email protected]",
"phone": "+421900000000"
},
"signature": "cee91937a98c89ea53440e84d367713ba6d196cbd0bf639f15f2f9c05b4762e9"
}
]Returns one withdrawal of your account. Read-only requests need the bearer token and the QWAC, no QSEAL signature.
How to verify the signature
The response carries a signature you can check. Join these values with |:
amountcurrencyexternal_idibannonceclient_secret(obtained from merchant's API key)
After this step we should have string that looks like this: amount|currency|external_id|iban|nonce|client_secret
Now, we use SHA256 hashing algorithm to hash this string and encode it using Base16 in lowercase. Compare the result with signature from the response.
Withdrawal statuses:
pending- manualy created withdrawalin_transit- process withdrawalpaid- processed withdrawalcanceled- cancelled withdrawalfailed- failed withdrawal
Parameters
52331Response 200
52331withdrawal105042EURPAYOUT-2026-0001SK3112000000198742637541Idempotency-Key header of the request that created the withdrawal · e.g. 7c9e6679-7425-40de-944b-e07fc1f90ae7pending, in_transit, paid, canceled, failed · e.g. pending{}Simple statement description1759744800ZUc0Mk9sVXZDOXNsdklzMQJohnDoefirst_name and last_name; responses always contain it. e.g. John Doe[email protected]+ are removed. e.g. +421900000000nullcee91937a98c89ea53440e84d367713ba6d196cbd0bf639f15f2f9c05b4762e9Other responses
curl -X GET 'https://api-mtls-sandbox.payout.one/api/v2/withdrawals/{withdrawal_id}' \
--cert qwac.pem --key qwac.key \
-H "Authorization: Bearer $TOKEN"import { readFileSync } from "node:fs";
import { Agent } from "undici";
const dispatcher = new Agent({ connect: { cert: readFileSync("qwac.pem"), key: readFileSync("qwac.key") } });
const res = await fetch("https://api-mtls-sandbox.payout.one/api/v2/withdrawals/{withdrawal_id}", {
method: "GET",
dispatcher,
headers: {
Authorization: `Bearer ${process.env.PAYOUT_TOKEN}`,
},
});
const data = await res.json();import os, requests
res = requests.get(
"https://api-mtls-sandbox.payout.one/api/v2/withdrawals/{withdrawal_id}",
cert=("qwac.pem", "qwac.key"),
headers={
"Authorization": f"Bearer {os.environ['PAYOUT_TOKEN']}",
},
)
data = res.json()<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api-mtls-sandbox.payout.one/api/v2/withdrawals/{withdrawal_id}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_SSLCERT, "qwac.pem");
curl_setopt($ch, CURLOPT_SSLKEY, "qwac.key");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . getenv("PAYOUT_TOKEN")]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);{
"id": 52331,
"object": "withdrawal",
"amount": 1050,
"api_key_id": 42,
"currency": "EUR",
"external_id": "PAYOUT-2026-0001",
"iban": "SK3112000000198742637541",
"idempotency_key": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"status": "pending",
"metadata": {},
"statement_descriptor": "Simple statement description",
"created_at": 1759744800,
"nonce": "ZUc0Mk9sVXZDOXNsdklzMQ",
"customer": {
"first_name": "John",
"last_name": "Doe",
"name": "John Doe",
"email": "[email protected]",
"phone": "+421900000000"
},
"signature": "cee91937a98c89ea53440e84d367713ba6d196cbd0bf639f15f2f9c05b4762e9"
}Cancels a withdrawal that has not been processed yet. Signed with QSEAL like Create withdrawal; the request has no body, so Digest is the SHA-256 of an empty body.
On success the response is the cancelled withdrawal. When it can no longer be cancelled, the response is {"allowed": false, "status": "<current status>"}, also with status 200. Use Check cancel allowed first if you need to know in advance.
Parameters
52331SHA-256= followed by the Base64 SHA-256 of the exact request body (of an empty body when there is none) · e.g. SHA-256=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=<protected header>..<signature>) over the Digest value, made with your QSEAL key. The protected header carries x5t#S256 (QSEAL thumbprint) and sigT (signing time, at most 5 minutes off). e.g. eyJhbGciOiJQUzI1NiIsIng1dCNTMjU2IjoiLi4uIn0..c2lnbmF0dXJlResponse 200
52331withdrawal105042EURPAYOUT-2026-0001SK3112000000198742637541Idempotency-Key header of the request that created the withdrawal · e.g. 7c9e6679-7425-40de-944b-e07fc1f90ae7pending, in_transit, paid, canceled, failed · e.g. pending{}Simple statement description1759744800ZUc0Mk9sVXZDOXNsdklzMQJohnDoefirst_name and last_name; responses always contain it. e.g. John Doe[email protected]+ are removed. e.g. +421900000000nullcee91937a98c89ea53440e84d367713ba6d196cbd0bf639f15f2f9c05b4762e9Other responses
Digest that does not match the body, or sigT more than 5 minutes off; or the withdrawal belongs to another accountBODY=''
DIGEST="SHA-256=$(printf %s "$BODY" | openssl dgst -sha256 -binary | base64)"
JWS_SIGNATURE="<detached JWS over $DIGEST>" # QSEAL key, see M2M Withdrawals: Signing payment instructions with QSEAL
curl -X POST 'https://api-mtls-sandbox.payout.one/api/v2/withdrawals/{withdrawal_id}/cancel' \
--cert qwac.pem --key qwac.key \
-H "Authorization: Bearer $TOKEN" \
-H "Digest: $DIGEST" \
-H "X-JWS-Signature: $JWS_SIGNATURE"import { readFileSync } from "node:fs";
import { createHash } from "node:crypto";
import { Agent } from "undici";
const dispatcher = new Agent({ connect: { cert: readFileSync("qwac.pem"), key: readFileSync("qwac.key") } });
const body = "";
const digest = "SHA-256=" + createHash("sha256").update(body).digest("base64");
const jwsSignature = signDetachedJws(digest); // QSEAL key, see M2M Withdrawals: Signing payment instructions with QSEAL
const res = await fetch("https://api-mtls-sandbox.payout.one/api/v2/withdrawals/{withdrawal_id}/cancel", {
method: "POST",
dispatcher,
headers: {
Authorization: `Bearer ${process.env.PAYOUT_TOKEN}`,
Digest: digest,
"X-JWS-Signature": jwsSignature,
},
});
const data = await res.json();import base64, hashlib, os, requests
body = ""
digest = "SHA-256=" + base64.b64encode(hashlib.sha256(body.encode()).digest()).decode()
jws_signature = sign_detached_jws(digest) # QSEAL key, see M2M Withdrawals: Signing payment instructions with QSEAL
res = requests.post(
"https://api-mtls-sandbox.payout.one/api/v2/withdrawals/{withdrawal_id}/cancel",
cert=("qwac.pem", "qwac.key"),
headers={
"Authorization": f"Bearer {os.environ['PAYOUT_TOKEN']}",
"Digest": digest,
"X-JWS-Signature": jws_signature,
},
)
data = res.json()<?php
$body = "";
$digest = "SHA-256=" . base64_encode(hash("sha256", $body, true));
$jwsSignature = sign_detached_jws($digest); // QSEAL key, see M2M Withdrawals: Signing payment instructions with QSEAL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api-mtls-sandbox.payout.one/api/v2/withdrawals/{withdrawal_id}/cancel");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_SSLCERT, "qwac.pem");
curl_setopt($ch, CURLOPT_SSLKEY, "qwac.key");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . getenv("PAYOUT_TOKEN"), "Digest: " . $digest, "X-JWS-Signature: " . $jwsSignature]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);{
"id": 52331,
"object": "withdrawal",
"amount": 1050,
"api_key_id": 42,
"currency": "EUR",
"external_id": "PAYOUT-2026-0001",
"iban": "SK3112000000198742637541",
"idempotency_key": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"status": "pending",
"metadata": {},
"statement_descriptor": "Simple statement description",
"created_at": 1759744800,
"nonce": "ZUc0Mk9sVXZDOXNsdklzMQ",
"customer": {
"first_name": "John",
"last_name": "Doe",
"name": "John Doe",
"email": "[email protected]",
"phone": "+421900000000"
},
"signature": "cee91937a98c89ea53440e84d367713ba6d196cbd0bf639f15f2f9c05b4762e9"
}Tells whether the withdrawal can still be cancelled. It is a POST and is signed with QSEAL like Cancel withdrawal (empty body).
Parameters
52331SHA-256= followed by the Base64 SHA-256 of the exact request body (of an empty body when there is none) · e.g. SHA-256=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=<protected header>..<signature>) over the Digest value, made with your QSEAL key. The protected header carries x5t#S256 (QSEAL thumbprint) and sigT (signing time, at most 5 minutes off). e.g. eyJhbGciOiJQUzI1NiIsIng1dCNTMjU2IjoiLi4uIn0..c2lnbmF0dXJlResponse 200
trueOther responses
Digest that does not match the body, or sigT more than 5 minutes off; or the withdrawal belongs to another accountBODY=''
DIGEST="SHA-256=$(printf %s "$BODY" | openssl dgst -sha256 -binary | base64)"
JWS_SIGNATURE="<detached JWS over $DIGEST>" # QSEAL key, see M2M Withdrawals: Signing payment instructions with QSEAL
curl -X POST 'https://api-mtls-sandbox.payout.one/api/v2/withdrawals/{withdrawal_id}/cancel_allowed' \
--cert qwac.pem --key qwac.key \
-H "Authorization: Bearer $TOKEN" \
-H "Digest: $DIGEST" \
-H "X-JWS-Signature: $JWS_SIGNATURE"import { readFileSync } from "node:fs";
import { createHash } from "node:crypto";
import { Agent } from "undici";
const dispatcher = new Agent({ connect: { cert: readFileSync("qwac.pem"), key: readFileSync("qwac.key") } });
const body = "";
const digest = "SHA-256=" + createHash("sha256").update(body).digest("base64");
const jwsSignature = signDetachedJws(digest); // QSEAL key, see M2M Withdrawals: Signing payment instructions with QSEAL
const res = await fetch("https://api-mtls-sandbox.payout.one/api/v2/withdrawals/{withdrawal_id}/cancel_allowed", {
method: "POST",
dispatcher,
headers: {
Authorization: `Bearer ${process.env.PAYOUT_TOKEN}`,
Digest: digest,
"X-JWS-Signature": jwsSignature,
},
});
const data = await res.json();import base64, hashlib, os, requests
body = ""
digest = "SHA-256=" + base64.b64encode(hashlib.sha256(body.encode()).digest()).decode()
jws_signature = sign_detached_jws(digest) # QSEAL key, see M2M Withdrawals: Signing payment instructions with QSEAL
res = requests.post(
"https://api-mtls-sandbox.payout.one/api/v2/withdrawals/{withdrawal_id}/cancel_allowed",
cert=("qwac.pem", "qwac.key"),
headers={
"Authorization": f"Bearer {os.environ['PAYOUT_TOKEN']}",
"Digest": digest,
"X-JWS-Signature": jws_signature,
},
)
data = res.json()<?php
$body = "";
$digest = "SHA-256=" . base64_encode(hash("sha256", $body, true));
$jwsSignature = sign_detached_jws($digest); // QSEAL key, see M2M Withdrawals: Signing payment instructions with QSEAL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api-mtls-sandbox.payout.one/api/v2/withdrawals/{withdrawal_id}/cancel_allowed");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_SSLCERT, "qwac.pem");
curl_setopt($ch, CURLOPT_SSLKEY, "qwac.key");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . getenv("PAYOUT_TOKEN"), "Digest: " . $digest, "X-JWS-Signature: " . $jwsSignature]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);{
"allowed": true
}You can initiate refund process with this request. This marks given payment identified by checkout_id as refunded and creates a refund to the original customer.
Send amount for a partial refund; without it, the whole remaining amount is refunded. Depending on the payment method, only a full refund may be possible. For checkouts created with should_split: true, offer_id is required and selects the split transaction to refund.
How to create the signature
Signature is created by few steps. First step is to create string concatenated with character | by joining arguments in following order:
amount(as sent in the request; if you omitamount, the checkout amount in cents)currency(of the checkout)external_id(of the checkout)iban(as sent in the request; empty if omitted)nonceclient_secret(obtained from merchant's API key)
After this step we should have string that looks like this: amount|currency|external_id|iban|nonce|client_secret
Now, we use SHA256 hashing algorithm to hash this string and encode it using Base16 in lowercase. The signature is now complete and you can send it with the request.
Refund statuses:
pending- manualy created refundin_transit- process refundpaid- processed refundcanceled- cancelled refundfailed- failed refund
Request body
141447500signature. e.g. SK3112000000198742637541/-?:().,'+ are allowed. max 140 · e.g. Refund for order 1001should_split: true. e.g. PREMIUMsignature. Max. 64 characters. e.g. cnd0aXJ0cnVuZXg2804703f1e9f40f709ac42c691eefddf207e0c16ffa16b9666cdd850d9565d75Response 200
52332refund500EURexternal_id of the refunded checkout · e.g. f0ac316a-9ea6-7998-01a7-720437afb34cnullJohnDoefirst_name and last_name; responses always contain it. e.g. John Doe[email protected]+ are removed. e.g. +421900000000nullpending{}Refund for order 10011759744800QjJqWEtiVDBNSmMyTm11dgf0269fef27c86d64f58276b75d169e9f65625fd1ae4f76f2e13db48a492cd4d4Other responses
offer_id or checkout_id)How to create the signatureoffer_id)curl -X POST 'https://sandbox.payout.one/api/v1/refunds' \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"checkout_id": 141447,
"amount": 500,
"iban": "SK3112000000198742637541",
"statement_descriptor": "Refund for order 1001",
"offer_id": "PREMIUM",
"nonce": "cnd0aXJ0cnVuZXg",
"signature": "2804703f1e9f40f709ac42c691eefddf207e0c16ffa16b9666cdd850d9565d75"
}'const res = await fetch("https://sandbox.payout.one/api/v1/refunds", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PAYOUT_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"checkout_id": 141447,
"amount": 500,
"iban": "SK3112000000198742637541",
"statement_descriptor": "Refund for order 1001",
"offer_id": "PREMIUM",
"nonce": "cnd0aXJ0cnVuZXg",
"signature": "2804703f1e9f40f709ac42c691eefddf207e0c16ffa16b9666cdd850d9565d75"
}),
});
const data = await res.json();import os, requests
res = requests.post(
"https://sandbox.payout.one/api/v1/refunds",
headers={"Authorization": f"Bearer {os.environ['PAYOUT_TOKEN']}"},
json={
"checkout_id": 141447,
"amount": 500,
"iban": "SK3112000000198742637541",
"statement_descriptor": "Refund for order 1001",
"offer_id": "PREMIUM",
"nonce": "cnd0aXJ0cnVuZXg",
"signature": "2804703f1e9f40f709ac42c691eefddf207e0c16ffa16b9666cdd850d9565d75",
},
)
data = res.json()<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://sandbox.payout.one/api/v1/refunds");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
"checkout_id" => 141447,
"amount" => 500,
"iban" => "SK3112000000198742637541",
"statement_descriptor" => "Refund for order 1001",
"offer_id" => "PREMIUM",
"nonce" => "cnd0aXJ0cnVuZXg",
"signature" => "2804703f1e9f40f709ac42c691eefddf207e0c16ffa16b9666cdd850d9565d75"
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . getenv("PAYOUT_TOKEN"), "Content-Type: application/json"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);{
"id": 52332,
"object": "refund",
"amount": 500,
"currency": "EUR",
"external_id": "f0ac316a-9ea6-7998-01a7-720437afb34c",
"customer": {
"first_name": "John",
"last_name": "Doe",
"name": "John Doe",
"email": "[email protected]",
"phone": "+421900000000"
},
"status": "pending",
"metadata": {},
"statement_descriptor": "Refund for order 1001",
"created_at": 1759744800,
"nonce": "QjJqWEtiVDBNSmMyTm11dg",
"signature": "f0269fef27c86d64f58276b75d169e9f65625fd1ae4f76f2e13db48a492cd4d4"
}You can use this request to retrieve list of payment methods enabled for your account. The identificator can be used as payment_method when creating a checkout.
Response 200 (array)
Card Paymentcard201.5Other responses
curl -X GET 'https://sandbox.payout.one/api/v1/payment_methods' \
-H "Authorization: Bearer $TOKEN"const res = await fetch("https://sandbox.payout.one/api/v1/payment_methods", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.PAYOUT_TOKEN}`,
},
});
const data = await res.json();import os, requests
res = requests.get(
"https://sandbox.payout.one/api/v1/payment_methods",
headers={"Authorization": f"Bearer {os.environ['PAYOUT_TOKEN']}"},
)
data = res.json()<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://sandbox.payout.one/api/v1/payment_methods");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . getenv("PAYOUT_TOKEN")]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);[
{
"name": "Card Payment",
"identificator": "card",
"fixed_fee": 20,
"percentual_fee": 1.5
},
{
"name": "Bank transfer",
"identificator": "bank_transfer",
"fixed_fee": 10,
"percentual_fee": 0.0
}
]Retrieves balance of current account. Keep in mind, that every API key belongs to a specific account.
Response 200 (array)
156724314768EUROther responses
curl -X GET 'https://sandbox.payout.one/api/v1/balance' \
-H "Authorization: Bearer $TOKEN"const res = await fetch("https://sandbox.payout.one/api/v1/balance", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.PAYOUT_TOKEN}`,
},
});
const data = await res.json();import os, requests
res = requests.get(
"https://sandbox.payout.one/api/v1/balance",
headers={"Authorization": f"Bearer {os.environ['PAYOUT_TOKEN']}"},
)
data = res.json()<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://sandbox.payout.one/api/v1/balance");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . getenv("PAYOUT_TOKEN")]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);[
{
"available": 25500,
"currency": "USD",
"pending": 0
},
{
"available": 1567243,
"currency": "EUR",
"pending": 14768
}
]Imports a QWAC or QSEAL certificate used by the server-to-server APIs (for example M2M withdrawals). Upload the PEM-encoded certificate — the public part only, never the private key. The certificate must be issued by a QTSP in Payout's trust store.
The certificate starts in status pending; Payout verifies it manually and changes the status to approved (or rejected). See the mTLS client certificates guide.
Request body
qwac, qseal · e.g. qwac-----BEGIN CERTIFICATE-----
MIIF...
-----END CERTIFICATE-----
Response 201
103a697b2fddcad32f63b842fbe48dd47eaf70340edb6df2e6677c7b70889a57qwac, qseal · e.g. qwacpending, approved, rejected · e.g. pendingC=SK,O=Example QTSP,CN=Example Qualified CAC=SK,O=Example s.r.o.,organizationIdentifier=NTRSK-12345678,CN=Example s.r.o.organizationIdentifier subject attribute · e.g. NTRSK-123456782026-06-09T06:23:06Z2027-06-09T06:23:06ZnullnullOther responses
type / pemcurl -X POST 'https://sandbox.payout.one/api/v1/mtls/certificates' \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "qwac",
"pem": "-----BEGIN CERTIFICATE-----\nMIIF...\n-----END CERTIFICATE-----\n"
}'const res = await fetch("https://sandbox.payout.one/api/v1/mtls/certificates", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PAYOUT_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"type": "qwac",
"pem": "-----BEGIN CERTIFICATE-----\nMIIF...\n-----END CERTIFICATE-----\n"
}),
});
const data = await res.json();import os, requests
res = requests.post(
"https://sandbox.payout.one/api/v1/mtls/certificates",
headers={"Authorization": f"Bearer {os.environ['PAYOUT_TOKEN']}"},
json={
"type": "qwac",
"pem": "-----BEGIN CERTIFICATE-----\nMIIF...\n-----END CERTIFICATE-----\n",
},
)
data = res.json()<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://sandbox.payout.one/api/v1/mtls/certificates");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
"type" => "qwac",
"pem" => "-----BEGIN CERTIFICATE-----\nMIIF...\n-----END CERTIFICATE-----\n"
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . getenv("PAYOUT_TOKEN"), "Content-Type: application/json"]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);{
"thumbprint": "103a697b2fddcad32f63b842fbe48dd47eaf70340edb6df2e6677c7b70889a57",
"type": "qwac",
"status": "pending",
"issuer_dn": "C=SK,O=Example QTSP,CN=Example Qualified CA",
"subject_dn": "C=SK,O=Example s.r.o.,organizationIdentifier=NTRSK-12345678,CN=Example s.r.o.",
"subject_org_id": "NTRSK-12345678",
"valid_from": "2026-06-09T06:23:06Z",
"valid_until": "2027-06-09T06:23:06Z"
}Lists the certificates imported for your account, newest first.
Response 200
103a697b2fddcad32f63b842fbe48dd47eaf70340edb6df2e6677c7b70889a57qwac, qseal · e.g. qwacpending, approved, rejected · e.g. approvedC=SK,O=Example s.r.o.,organizationIdentifier=NTRSK-12345678,CN=Example s.r.o.2027-06-09T06:23:06ZOther responses
curl -X GET 'https://sandbox.payout.one/api/v1/mtls/certificates' \
-H "Authorization: Bearer $TOKEN"const res = await fetch("https://sandbox.payout.one/api/v1/mtls/certificates", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.PAYOUT_TOKEN}`,
},
});
const data = await res.json();import os, requests
res = requests.get(
"https://sandbox.payout.one/api/v1/mtls/certificates",
headers={"Authorization": f"Bearer {os.environ['PAYOUT_TOKEN']}"},
)
data = res.json()<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://sandbox.payout.one/api/v1/mtls/certificates");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . getenv("PAYOUT_TOKEN")]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);{
"data": [
{
"thumbprint": "103a697b2fddcad32f63b842fbe48dd47eaf70340edb6df2e6677c7b70889a57",
"type": "qwac",
"status": "approved",
"subject_dn": "C=SK,O=Example s.r.o.,organizationIdentifier=NTRSK-12345678,CN=Example s.r.o.",
"valid_until": "2027-06-09T06:23:06Z"
}
]
}Returns the approval status of one of your certificates. After approval the status changes to approved and the certificate becomes usable.
Parameters
103a697b2fddcad32f63b842fbe48dd47eaf70340edb6df2e6677c7b70889a57Response 200
103a697b2fddcad32f63b842fbe48dd47eaf70340edb6df2e6677c7b70889a57pending, approved, rejected · e.g. pendingnullOther responses
curl -X GET 'https://sandbox.payout.one/api/v1/mtls/certificates/{thumbprint}/status' \
-H "Authorization: Bearer $TOKEN"const res = await fetch("https://sandbox.payout.one/api/v1/mtls/certificates/{thumbprint}/status", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.PAYOUT_TOKEN}`,
},
});
const data = await res.json();import os, requests
res = requests.get(
"https://sandbox.payout.one/api/v1/mtls/certificates/{thumbprint}/status",
headers={"Authorization": f"Bearer {os.environ['PAYOUT_TOKEN']}"},
)
data = res.json()<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://sandbox.payout.one/api/v1/mtls/certificates/{thumbprint}/status");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . getenv("PAYOUT_TOKEN")]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);{
"thumbprint": "103a697b2fddcad32f63b842fbe48dd47eaf70340edb6df2e6677c7b70889a57",
"status": "pending"
}Removes one of your certificates.
Parameters
103a697b2fddcad32f63b842fbe48dd47eaf70340edb6df2e6677c7b70889a57Responses
curl -X DELETE 'https://sandbox.payout.one/api/v1/mtls/certificates/{thumbprint}' \
-H "Authorization: Bearer $TOKEN"const res = await fetch("https://sandbox.payout.one/api/v1/mtls/certificates/{thumbprint}", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.PAYOUT_TOKEN}`,
},
});
const data = await res.json();import os, requests
res = requests.delete(
"https://sandbox.payout.one/api/v1/mtls/certificates/{thumbprint}",
headers={"Authorization": f"Bearer {os.environ['PAYOUT_TOKEN']}"},
)
data = res.json()<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://sandbox.payout.one/api/v1/mtls/certificates/{thumbprint}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . getenv("PAYOUT_TOKEN")]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);- Need help? Contact support.
- Questions? Contact sales.
- Service status? status.payout.one.
- LLM? Read llms.txt.