payout / developers
Guide

Withdrawal

On this page

A withdrawal sends money from your Payout balance to a bank account. Withdrawals go through the M2M Withdrawals API (API v2): a separate mTLS host, your QWAC certificate for the connection and a QSEAL signature on every request that creates or cancels a withdrawal.

  1. Get and import your certificates

    • Obtain a QWAC and a QSEAL certificate from a supported Qualified Trust Service Provider (currently I.CA and Disig).
    • Import both via POST /api/v1/mtls/certificates and wait until Payout approves them. The whole process is described in Certificates.
  2. Generate api key and secret

    • You can do it in backoffice (Sandbox or Production) after you have already created account.
    • It's required to add notify url before api key is generated. This url will be used for sending webhook notifications (POST HTTP Requests) about state of checkouts and other events.
  3. Make authorization call to get Bearer token. This call goes to the standard host, not the mTLS one.

    Command Line
    curl --location --request POST 'https://sandbox.payout.one/api/v1/authorize' \
    --header 'Content-Type: application/json' \
    --header 'Accept: application/json' \
    --data-raw '{
     "client_id": "DC995618-7ED8-4070-9DA0-48B6F86551C3",
     "client_secret": "q3dpHpYtDrH-KmGD4HMn5OTEx6IsZPBokQ8CqMONWqMSEePWy9bXd3Ua3KvO7f6C"
    }'
    

    Response of this call looks like this:

    JSON
    {
     "token": "SFMyNTY.g2gDYSFuBgCaSXELfgFiAAFRgWnBcvEfet1jIr9OPF984RGTKu-8HcHPQKJitk_kJKiU",
     "valid_for": 6000
    }
    
  4. Prepare the withdrawal and sign it with your QSEAL certificate

    Command Line
    BODY='{
        "amount": 300,
        "currency": "EUR",
        "external_id": "PAYOUT-2026-0001",
        "iban": "SK3112000000198742637541",
        "customer": {
            "first_name": "John",
            "last_name": "Doe",
            "email": "john.doe@payout.one"
        },
        "statement_descriptor": "Simple statement description"
    }'
    DIGEST="SHA-256=$(printf %s "$BODY" | openssl dgst -sha256 -binary | base64)"
    JWS_SIGNATURE="<detached JWS over $DIGEST, made with your QSEAL key>"
    

    Note

    Amount is in cents (EUR), so 3 EUR is 300. It's same for other currencies, for example 300 CZK is 30000.

    Digest is the SHA-256 of the exact body you send. X-JWS-Signature is a detached JWS over the Digest value, made with the private key of your QSEAL certificate. Its protected header carries the thumbprint of the QSEAL certificate (x5t#S256) and the signing time (sigT), which must be within 5 minutes of the server time. The full header and a signing example are in M2M Withdrawals.

    Note

    API v1 required a signature and a nonce in the body. With QSEAL they are no longer needed: the signature covers the whole body.

  5. Create the withdrawal on the mTLS host. The connection presents your QWAC, the call carries the Bearer token from step 3 and the QSEAL headers from step 4.

    Command Line
    curl --location --request POST 'https://api-mtls-sandbox.payout.one/api/v2/withdrawals' \
    --cert qwac.pem --key qwac.key \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer SFMyNTY.g2gDYSFuBgCaSXELfgFiAAFRgA.WnBcvEfet2jJr4OPF984RGTKu-8HcHPQKJitk_kJKiU' \
    --header 'Accept: application/json' \
    --header 'Idempotency-Key: 74775d02-745f-4198-cf3c-be9f1971dabe' \
    --header "Digest: $DIGEST" \
    --header "X-JWS-Signature: $JWS_SIGNATURE" \
    --data "$BODY"
    

    Successful call returns 201 Created with the withdrawal:

    JSON
    {
        "id": 52331,
        "object": "withdrawal",
        "amount": 300,
        "api_key_id": 42,
        "currency": "EUR",
        "external_id": "PAYOUT-2026-0001",
        "iban": "SK3112000000198742637541",
        "idempotency_key": "74775d02-745f-4198-cf3c-be9f1971dabe",
        "status": "pending",
        "metadata": {},
        "statement_descriptor": "Simple statement description",
        "created_at": 1759744800,
        "nonce": "ZUc0Mk9sVXZDOXNsdklzMQ",
        "customer": {
            "first_name": "John",
            "last_name": "Doe",
            "name": "John Doe",
            "email": "john.doe@payout.one",
            "phone": null
        },
        "signature": "0a6b7c2d9e1f4a3b5c8d7e6f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b"
    }
    

    Sending the same Idempotency-Key again returns the existing withdrawal instead of creating a new one.

  6. Check the status of the withdrawal. Reading needs the QWAC and the Bearer token, but no QSEAL signature.

    Command Line
    curl --location --request GET 'https://api-mtls-sandbox.payout.one/api/v2/withdrawals/52331' \
    --cert qwac.pem --key qwac.key \
    --header 'Authorization: Bearer SFMyNTY.g2gDYSFuBgCaSXELfgFiAAFRgA.WnBcvEfet2jJr4OPF984RGTKu-8HcHPQKJitk_kJKiU' \
    --header 'Accept: application/json'
    

    Withdrawal statuses:

    • pending - created, not processed yet
    • in_transit - being processed
    • paid - processed
    • canceled - cancelled
    • failed - failed
  7. Cancel the withdrawal if needed. Only a withdrawal that is still pending and has not been picked up for processing can be cancelled. Cancel is signed with QSEAL like step 4; the request has no body, so Digest is the SHA-256 of an empty body. Check cancel allowed tells you beforehand whether it is still possible.

    Command Line
    DIGEST="SHA-256=$(printf %s "" | openssl dgst -sha256 -binary | base64)"
    JWS_SIGNATURE="<detached JWS over $DIGEST, made with your QSEAL key>"
    curl --location --request POST 'https://api-mtls-sandbox.payout.one/api/v2/withdrawals/52331/cancel' \
    --cert qwac.pem --key qwac.key \
    --header 'Authorization: Bearer SFMyNTY.g2gDYSFuBgCaSXELfgFiAAFRgA.WnBcvEfet2jJr4OPF984RGTKu-8HcHPQKJitk_kJKiU' \
    --header 'Accept: application/json' \
    --header "Digest: $DIGEST" \
    --header "X-JWS-Signature: $JWS_SIGNATURE"
    

All parameters, responses and errors of these calls are in the API reference. For production use https://app.payout.one for the authorization call and https://api-mtls.payout.one for the withdrawal calls.

Was this page helpful?