QAS TechDocs

    Cashout API

    Create and manage PIX and bank transfer cashout transactions

    Cashout API - Overview

    Welcome to the Cashout API documentation. This guide provides a comprehensive overview of how to use our PIX transfer system to send money from your accounts to recipients via the Brazilian instant payment network.

    Table of Contents


    What is Cashout?

    Cashout is the process of transferring money from your account to external recipients using the PIX instant payment system. PIX is Brazil's instant payment platform that enables real-time transfers 24/7, including weekends and holidays.

    With our Cashout API, you can:

    • Send money using PIX keys (email, phone, CPF/CNPJ, or random key)
    • Transfer funds using complete bank account details
    • Use PIX copy-and-paste codes (BR Codes) for payments
    • Implement single-step or two-step approval workflows
    • Ensure safe retries with idempotency keys

    Approval Workflows

    Our API supports two different approval workflows to suit your business needs:

    Two-Step Approval (Create + Approve)

    Use Case: Organizations that require manual review, compliance checks, or dual authorization before processing transfers.

    Flow:

    1. Create Transaction - Create a transaction with status NEW
    2. Review - Perform any necessary validation or approval processes
    3. Approve Transaction - Approve and initiate the transfer (status changes to PROCESSING)

    Benefits:

    • Clear audit trail with separate creation and approval steps
    • Time to review transactions before processing
    • Supports different user roles (creator vs approver)
    • Compliance-friendly for organizations with strict controls

    Available Methods:

    One-Step Approval (Auto-Approve)

    Use Case: Trusted operations where immediate processing is required without separate approval.

    Flow:

    1. Create and Approve - Single API call creates the transaction and immediately processes it (status goes from NEW to PROCESSING)
    2. Background Processing - Transfer is initiated automatically

    Benefits:

    • Faster processing for trusted operations
    • Simplified integration with fewer API calls
    • Ideal for automated systems or low-risk transfers

    Important: Always use idempotency keys with self-approve endpoints to prevent duplicate transfers!

    Available Methods:


    PIX Key Types and Formats

    PIX keys are identifiers that simplify transfers by eliminating the need to provide complete bank account details. Our API supports all PIX key types:

    1. Email

    • Format: Standard email format
    • Example: recipient@example.com
    • Validation: Must be a valid email address
    • Case Sensitivity: Case-insensitive

    2. Phone Number

    • Format: Must include country code with + prefix
    • Example: +5511999999999
    • Pattern: +55 (Brazil) + 11 (area code) + 9 (mobile) + XXXXXXXX (number)
    • Validation:
      • Must start with +55 for Brazilian numbers
      • Mobile numbers: 11 digits after country code (includes the digit 9)
      • Landline numbers: 10 digits after country code
    • Important: Always include the + prefix and country code

    3. CPF (Individual Tax ID)

    • Format: 11 digits, numbers only (no formatting)
    • Example: 11122233344
    • Validation: Must be a valid CPF number
    • Note: Do not include dots or dashes (. or -)

    4. CNPJ (Company Tax ID)

    • Format: 14 digits, numbers only (no formatting)
    • Example: 11222333000144
    • Validation: Must be a valid CNPJ number
    • Note: Do not include dots, dashes, or slashes

    5. EVP (Random Key)

    • Format: UUID v4 format
    • Example: 123e4567-e89b-12d3-a456-426614174000
    • Description: Randomly generated key created by the recipient's bank
    • Validation: Must be a valid UUID format with hyphens

    PIX Key Format Summary Table

    Key TypeFormatExampleNotes
    Emailname@domain.comuser@example.comCase-insensitive
    Phone+55AAXXXXXXXXX+5511999999999Must include +55 prefix
    CPF11 digits11122233344Numbers only, no formatting
    CNPJ14 digits11222333000144Numbers only, no formatting
    EVPUUID v4123e4567-e89b-12d3-a456-426614174000Random key with hyphens

    Account Types for Manual Transfers

    When creating cashout transactions using manual recipient account details (instead of PIX keys), you must specify the account type and account model:

    Account Type Values

    The recipient_account_type field accepts these values:

    ValueDescriptionUse Case
    CURRENT_ACCOUNTCurrent AccountStandard checking account for daily transactions
    PAYMENT_ACCOUNTPayment AccountDigital payment account (e.g., fintech or digital banks)
    SAVING_ACCOUNTSavings AccountSavings account with limited transaction frequency

    Account Model

    The recipient_account_model field must always be set to the fixed value:

    "recipient_account_model": "Movement"

    Important: This is a required field with a fixed value when using manual account details.

    Example

    {
      "recipient_account_type": "CURRENT_ACCOUNT",
      "recipient_account_model": "Movement"
    }

    Applies to these endpoints:


    Authentication

    All Cashout API endpoints require two authentication mechanisms:

    1. Bearer Token (Authorization Header)

    Authenticate your requests using a JWT access token:

    Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

    How to obtain:

    • Use the authentication endpoint to obtain an access token
    • Tokens have an expiration time and must be refreshed when expired

    2. HMAC Signature (hmac Header)

    Ensure request integrity using HMAC-SHA256 signature:

    hmac: 57373705c83bc5efe41001790c54642e670088c0c87d56bc8f990f2260c7740b...

    How it works:

    • HMAC (Hash-based Message Authentication Code) combines your secret key with the request body
    • Generates a cryptographic hash that verifies the request hasn't been tampered with
    • Both headers are required for all requests

    Important: Never share your HMAC secret key. Store it securely and use it only server-side.


    Idempotency

    Idempotency is a mechanism to safely retry API requests without risk of creating duplicate transactions. This is critically important for financial operations.

    What is Idempotency?

    When you include an idempotency-key header in your request, the API guarantees that retrying the same request (with the same key and body) will not create duplicate transactions.

    How It Works

    idempotency-key: 550e8400-e29b-41d4-a716-446655440000

    Scenarios:

    1. First Request (HTTP 200 - New Transaction Created):

      • Idempotency key is new (doesn't exist in database)
      • API creates a new transaction and returns details
      • Response includes a new transaction_id
      • Webhooks will be sent when transaction completes
    2. Retry with Same Key + Same Body (HTTP 200 - Existing Transaction Returned):

      • Idempotency key already exists in database with the same request body
      • API returns the existing transaction (no new transaction created)
      • Response has the same transaction_id as the first request
      • Status reflects current database state: Can be PROCESSING, SUCCESS, ERROR, CANCELLED, or REFUNDED
      • Important: No new webhooks are sent - this is a replay of the original request
      • Safe to retry without risk of duplicate transfers
      • Always check status field - Don't assume the transaction succeeded
    3. Same Key + Different Body (HTTP 409 Conflict):

      • Idempotency key already exists but with a different request body
      • API rejects the request to prevent data inconsistency
      • Returns error response with details
      • Action required: Generate a new idempotency key for the new transaction

    Example Error Response:

    {
      "worked": false,
      "detail": "Idempotency key already used with a different request",
      "message": "Idempotency key already used with a different request",
      "data": null
    }

    Key Format

    • Format: UUID version 4 (RFC 4122)
    • Example: 550e8400-e29b-41d4-a716-446655440000
    • Generation: Use a cryptographically secure UUID v4 generator
    • Scope: Unique per account (not globally unique)
    • Case: Case-insensitive, but lowercase is recommended

    When to Use

    Endpoint TypeIdempotency Key
    Two-Step (Create)Recommended - Prevents duplicate creation
    One-Step (Self-Approve)Strongly Recommended - Prevents duplicate transfers and financial losses
    ApproveNot needed - Status validation prevents duplicates

    Best Practices

    1. Generate on Client: Create the UUID before making the request
    2. Store with Transaction: Save both the idempotency key and transaction_id from the response
    3. Check Transaction ID on Retry: If you retry a request, compare the returned transaction_id with your stored value:
      • Same transaction_id = This is the existing transaction (no new transfer will be processed)
      • Different transaction_id = This is a new transaction (new transfer will be processed)
    4. One Key per Transaction: Never reuse idempotency keys for different transactions
    5. Retry with Same Key: Always use the same idempotency key when retrying the exact same request
    6. Handle 409 Conflicts: If you receive HTTP 409, it means you're trying to use an existing key with different data - generate a new idempotency key
    7. Permanent Storage: Idempotency keys are stored indefinitely in the system

    Practical Example: Handling Retries

    First Request - New Transaction Created

    POST /create-cashout-self-approve
    idempotency-key: 550e8400-e29b-41d4-a716-446655440000
    
    Request Body:
    {
      "source_account_branch_identifier": "0001",
      "source_account_number": "123456",
      "amount": 100.00,
      "key": "111*****999"
    }

    Response (HTTP 200):

    {
      "worked": true,
      "transaction_id": 1134,
      "status": "PROCESSING",
      "amount": 100.00,
      "fee": 0.0,
      "key": "111*****999",
      "from_accout": "123456",
      "code_transaction": null,
      "recipient_name": null,
      "recipient_account_id": null,
      "idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
      "erro_descriptor": ""
    }

    ✅ Action: Store transaction_id: 1134 and idempotency_key in your database for tracking.


    Retry Request - Existing Transaction Returned

    Network error occurs, you retry with the same idempotency key and same body:

    POST /create-cashout-self-approve
    idempotency-key: 550e8400-e29b-41d4-a716-446655440000
    
    Request Body: (exactly the same as before)
    {
      "source_account_branch_identifier": "0001",
      "source_account_number": "123456",
      "amount": 100.00,
      "key": "111*****999"
    }

    Response (HTTP 200) - Example 1: Successful Transaction

    {
      "worked": true,
      "transaction_id": 1134,
      "status": "SUCCESS",
      "amount": 100.00,
      "fee": 0.0,
      "key": "111*****999",
      "from_accout": "123456",
      "code_transaction": "E12345678901234567890123456789AB",
      "recipient_instution": "12345678",
      "recipient_account_id": "654321",
      "recipient_branch_id": "0001",
      "recipient_legal_id": "111*****999",
      "recipient_name": "RECIPIENT NAME",
      "recipient_account_type": "CURRENT_ACCOUNT",
      "idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
      "erro_descriptor": ""
    }

    Response (HTTP 200) - Example 2: Cancelled Transaction

    {
      "worked": true,
      "transaction_id": 1134,
      "status": "CANCELLED",
      "amount": 100.00,
      "fee": 0.0,
      "key": "111*****999",
      "from_accout": "123456",
      "code_transaction": null,
      "recipient_instution": null,
      "recipient_account_id": null,
      "recipient_branch_id": null,
      "recipient_legal_id": null,
      "recipient_name": null,
      "recipient_account_type": null,
      "idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
      "erro_descriptor": "Transaction cancelled - Account limit exceeded"
    }

    Response (HTTP 200) - Example 3: Error Transaction

    {
      "worked": true,
      "transaction_id": 1134,
      "status": "ERROR",
      "amount": 100.00,
      "fee": 0.0,
      "key": "111*****999",
      "from_accout": "123456",
      "code_transaction": null,
      "recipient_instution": null,
      "recipient_account_id": null,
      "recipient_branch_id": null,
      "recipient_legal_id": null,
      "recipient_name": null,
      "recipient_account_type": null,
      "idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
      "erro_descriptor": "Payment processing error - Insufficient funds"
    }

    🔍 How to Identify This is the Same Transaction:

    Compare the transaction_id from both responses:

    • First request: transaction_id: 1134
    • Retry request: transaction_id: 1134 ← SAME ID!

    ✅ This means: No new transaction was created. You are receiving the current status of the existing transaction from the database.

    Key Observations:

    1. Same transaction_id (1134) - This is your primary indicator that no duplicate was created
    2. Status reflects database state - Can be PROCESSING, SUCCESS, ERROR, CANCELLED, or REFUNDED depending on transaction outcome
    3. Always check the status field - Don't assume the transaction succeeded
    4. Check erro_descriptor - If status is ERROR, CANCELLED, or REFUNDED, this field contains the reason
    5. Fields populated on success - code_transaction, recipient_name, etc. only have values when status is SUCCESS
    6. Same idempotency_key - Confirms this is the replay of your original request

    Important: The response always reflects the real-time state of the transaction in the database, not a cached response.


    Conflict Request - Different Body with Same Key

    You try to create a different transaction using the same idempotency key:

    POST /create-cashout-self-approve
    idempotency-key: 550e8400-e29b-41d4-a716-446655440000
    
    Request Body: (DIFFERENT from original - different amount)
    {
      "source_account_branch_identifier": "0001",
      "source_account_number": "123456",
      "amount": 200.00,
      "key": "111*****999"
    }

    Response (HTTP 409 Conflict):

    {
      "worked": false,
      "detail": "Idempotency key already used with a different request",
      "message": "Idempotency key already used with a different request",
      "data": null
    }

    ❌ This means: The idempotency key is already associated with a different transaction (different request body).

    Action Required:

    • Generate a new idempotency key for this new transaction
    • Never reuse idempotency keys for different transactions
    • Each unique transaction must have its own unique idempotency key

    How Idempotency Works Internally

    Our API validates idempotency directly against the database:

    1. First Request:

      • Idempotency key doesn't exist in database
      • New transaction is created and stored
      • Response is returned with new transaction_id and initial status (NEW or PROCESSING)
    2. Retry with Same Key + Same Body:

      • System finds existing transaction with the idempotency key
      • Compares stored request body with incoming request body
      • Bodies match → Returns existing transaction with current database state
      • Important: Status reflects real-time transaction outcome:
        • PROCESSING - Still being processed
        • SUCCESS - Transfer completed successfully
        • ERROR - Payment processing error (check erro_descriptor)
        • CANCELLED - Transaction cancelled due to business rules or restrictions (check erro_descriptor)
        • REFUNDED - Transaction was completed but later refunded (check erro_descriptor)
    3. Same Key + Different Body:

      • System finds existing transaction with the idempotency key
      • Compares stored request body with incoming request body
      • Bodies don't match → Returns HTTP 409 Conflict

    Key Differences:

    • ✅ Permanent storage: Idempotency keys are stored indefinitely (not just 24 hours)
    • ✅ Real-time status: Retries return the current transaction state from database, not a cached response
    • ✅ Database-based: Direct comparison with stored transactions, not cache lookup
    • ✅ Live transaction state: Status can change between retries as the transaction progresses

    Critical Points for Clients

    1. Always compare transaction_id between requests to identify if you received a new or existing transaction
    2. Always check the status field - Don't assume success! The transaction may have been cancelled, encountered an error, or been refunded
    3. Status can vary - The same transaction can return different statuses depending on when you retry:
      • First call: PROCESSING
      • Retry 1 minute later: SUCCESS, ERROR, CANCELLED, or REFUNDED
    4. Check erro_descriptor on failures - If status is ERROR, CANCELLED, or REFUNDED, this field explains why
    5. Don't wait for duplicate webhooks - Retried requests don't trigger new webhook notifications
    6. Use the returned data - When you receive an existing transaction, its status reflects the real database state
    7. Store idempotency keys permanently - Keep them in your database for audit and reconciliation purposes

    Transaction Status Flow

    Understanding transaction statuses helps you track and manage transfers:

    ┌─────────────┐
    │     NEW     │  ← Transaction created (Two-Step only)
    └──────┬──────┘
           │
           │ Approval (manual or automatic)
           ↓
    ┌─────────────┐
    │ PROCESSING  │  ← Transfer being executed
    └──────┬──────┘
           │
           ├─── Success ──────────→ ┌─────────┐
           │                        │ SUCCESS │  ← Transfer completed successfully
           │                        └────┬────┘
           │                             │
           │                             │ Refund requested
           │                             ↓
           │                        ┌──────────┐
           │                        │ REFUNDED │  ← Money returned to sender
           │                        └──────────┘
           │
           ├─── Payment Error ─────→ ┌─────────┐
           │                         │  ERROR  │  ← Payment processing failed
           │                         └─────────┘
           │
           └─── Business Rules ────→ ┌──────────┐
                                     │ CANCELLED │  ← Cancelled due to restrictions
                                     └──────────┘

    Status Descriptions

    StatusDescriptionNext Steps
    NEWTransaction created, waiting for approvalCall /approve-cashout to process
    PROCESSINGTransfer is being executed by the payment systemWait for final status (async)
    SUCCESSTransfer completed successfullyNo action needed (money sent)
    ERRORPayment processing error occurredCheck erro_descriptor field for error details (e.g., insufficient funds, invalid recipient, payment system unavailable). Create a new transaction if needed
    CANCELLEDTransaction cancelled due to business rules or restrictionsCheck erro_descriptor field for cancellation reason (e.g., account limits, blocked operations, compliance restrictions). Create a new transaction if needed
    REFUNDEDTransaction was successful but later refundedCheck erro_descriptor field for refund reason. Money was returned to the sender account

    Available Endpoints

    Cashout Methods

    Explore different ways to create PIX cashouts based on your business needs:

    1. PIX Cashout Manual (Two-Step Approval)

    Create PIX transfers using complete bank account details (branch and account number).

    View PIX Cashout Manual Documentation →

    Endpoints:

    • Create cashout manual (NEW status)
    • Approve cashout manual
    • Legacy manual transfer

    2. PIX Cashout by Key (Two-Step Approval)

    Create PIX transfers using PIX keys (email, phone, CPF/CNPJ, or random key).

    View PIX Cashout by Key Documentation →

    Endpoints:

    • Create cashout by PIX key (NEW status)
    • Create copy and paste transfer

    3. Cashout Auto Approve (Single-Step)

    Create PIX transfers with automatic approval for immediate processing.

    View Cashout Auto Approve Documentation →

    Endpoints:

    • Auto-approve cashout by PIX key
    • Auto-approve cashout manual

    Status Consultation

    Query transaction status and retrieve receipts:


    Common Business Rules

    These rules apply to all Cashout endpoints:

    Amount Validation

    • Must be greater than zero
    • Maximum of 2 decimal places
    • Example valid amounts: 100.00, 50.50, 1000
    • Example invalid amounts: 0, -10.00, 100.999

    Account Identification

    • Branch Identifier: 4-digit code (e.g., 0001)
    • Account Number: Numeric string (e.g., 123456)
    • Both are required to identify the source account

    Recipient Account Details (Manual Transfers)

    When not using a PIX key, you must provide:

    • Recipient name
    • Recipient CPF or CNPJ
    • Recipient bank ISPB code (8 digits)
    • Recipient account number and digit
    • Recipient branch number
    • Account type (Current, Savings, or Payment)

    HMAC Validation

    • All request bodies must be validated with HMAC signature
    • Invalid HMAC results in 400 Bad Request with response: {"worked": false, "detail": "HMAC invalid", "message": "HMAC invalid", "data": null}

    Getting Started

    Quick Start Guide

    1. Choose Your Workflow:

      • Need approval workflow? Use /create-cashout + /approve-cashout
      • Need immediate processing? Use /create-cashout-self-approve
    2. Prepare Authentication:

      • Obtain your Bearer token
      • Generate HMAC signature for your request body
    3. Generate Idempotency Key:

      // JavaScript example
      const { v4: uuidv4 } = require('uuid');
      const idempotencyKey = uuidv4();
    4. Format Your PIX Key:

      • Phone: +5511999999999 (with + and country code)
      • Email: user@example.com
      • CPF: 11122233344 (numbers only)
    5. Make Your First Request:

      POST /create-cashout-self-approve
      Authorization: Bearer <your_token>
      hmac: <computed_hmac>
      idempotency-key: 550e8400-e29b-41d4-a716-446655440000
      Content-Type: application/json
      
      {
        "source_account_branch_identifier": "0001",
        "source_account_number": "123456",
        "amount": 100.00,
        "key": "+5511999999999"
      }
    6. Handle the Response:

      • Check worked: true for success
      • Store the transaction_id for tracking
      • Check status field (NEW or PROCESSING)

    Example Use Cases

    Use Case 1: Automated Payouts

    • Use self-approve endpoints for faster processing
    • Always include idempotency keys
    • Monitor transaction status asynchronously

    Use Case 2: Manual Review Required

    • Use two-step approval endpoints
    • Implement approval UI for authorized users
    • Maintain audit trail of who created vs who approved

    Use Case 3: Customer-Initiated Transfers

    • Use /create-manual-transfer for web applications
    • Require MFA validation for security
    • Allow customers to choose between PIX key or manual details

    Error Handling

    All endpoints return standard HTTP status codes:

    Status CodeMeaningAction
    200SuccessProcess response data
    400Bad RequestCheck request body, parameters, or HMAC signature
    401UnauthorizedVerify Bearer token
    409ConflictIdempotency key conflict - use new key

    Last Updated: May 2026
    API Version: v3