payout / developers
Guide

Withdrawal

On this page

Send money from your Payout balance to a bank account. Withdrawals use 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.

Before you begin

  • Approved QWAC and QSEAL certificates. Get them from a supported Qualified Trust Service Provider (currently I.CA and Disig), import both with POST /api/v1/mtls/certificates and wait until Payout approves them. Certificates describes the whole process.
  • An API key, as in Simple payment, step 1.
  • A Bearer token, as in Simple payment, step 2. Get it from the standard host (https://sandbox.payout.one), not from the mTLS host.

Steps

  1. Prepare and sign the request

    Prepare the body 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": "[email protected]"
        },
        "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>"
    

    amount is in cents: 300 is 3.00 EUR. The same applies to other currencies, so 30000 is 300 CZK.

    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. M2M Withdrawals shows the full header and a signing example.

    Note

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

  2. Create the withdrawal

    Send the request to the mTLS host. The connection presents your QWAC; the request carries the Bearer token and the QSEAL headers from step 1.

    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 'Accept: application/json' \
    --header 'Authorization: Bearer SFMyNTY.EXAMPLE-TOKEN.dGhpcy1pcy1hLWZha2Utc2lnbmF0dXJlLWV4YW1wbGU' \
    --header 'Idempotency-Key: 9df1a34b-0c34-4565-bde8-50dd17b336f3' \
    --header "Digest: $DIGEST" \
    --header "X-JWS-Signature: $JWS_SIGNATURE" \
    --data "$BODY"
    

    A 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": "9df1a34b-0c34-4565-bde8-50dd17b336f3",
        "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": null,
            "note": null
        },
        "signature": "54476b2fc64161ed45036ec6a35a8f8d6b07e13f4d93b24c6247cf6dbbacd68c"
    }
    

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

    Responses in API v2 are still signed as in API v1. To check that a response comes from Payout, compute the signature from the response values and compare it with signature. external_id is an empty string when it is null.

    TEXT
    Pattern: amount|currency|external_id|iban|nonce|client_secret
    Input:   300|EUR|PAYOUT-2026-0001|SK3112000000198742637541|ZUc0Mk9sVXZDOXNsdklzMQ|q3dpHpYtDrH-KmGD4HMn5OTEx6IsZPBokQ8CqMONWqMSEePWy9bXd3Ua3KvO7f6C
    SHA-256: 54476b2fc64161ed45036ec6a35a8f8d6b07e13f4d93b24c6247cf6dbbacd68c
    

    Note

    The hash is in lowercase hex. Some libraries return uppercase hex; convert it to lowercase before you compare it.

  3. Check the status

    Reading a withdrawal 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 'Accept: application/json' \
    --header 'Authorization: Bearer SFMyNTY.EXAMPLE-TOKEN.dGhpcy1pcy1hLWZha2Utc2lnbmF0dXJlLWV4YW1wbGU'
    
    Status Meaning
    pending Created, not processed yet
    in_transit Sent to the bank, not confirmed yet
    paid Paid out
    canceled Cancelled, not paid out
    failed Failed, not paid out
  4. Cancel the withdrawal if needed

    You can cancel only a withdrawal that is still pending and hasn't been passed to the bank yet. Check if withdrawal can be cancelled tells you in advance whether this is still possible.

    Sign the cancel request with QSEAL as in step 1. It has no body, so Digest is the SHA-256 of an empty body.

    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 'Accept: application/json' \
    --header 'Authorization: Bearer SFMyNTY.EXAMPLE-TOKEN.dGhpcy1pcy1hLWZha2Utc2lnbmF0dXJlLWV4YW1wbGU' \
    --header "Digest: $DIGEST" \
    --header "X-JWS-Signature: $JWS_SIGNATURE"
    

In production, use https://app.payout.one for the Bearer token and https://api-mtls.payout.one for the withdrawal calls.

Next steps

  • Create withdrawal and the other operations in the API reference list all parameters, responses and errors.
  • M2M Withdrawals covers Verification of Payee and the HTTP error codes.

Was this page helpful?