Newbie? Grab some coffee, this'll take about an hour. Pro? You know the drill - 5 minutes

HUBHUD is a robust PIX payment API designed to simplify payment processing for businesses of all sizes. Our API provides a seamless integration for processing instant payments through Brazil's PIX system.

This documentation is intended for developers who want to integrate PIX payments into their applications.

Getting Started

To begin using the HUBHUD API, you'll need to:

  1. Contact our sales team to obtain your API credentials
  2. Set up your development environment
  3. Follow the authentication process described below

Postman Collection

Download our Postman collection to quickly test the API endpoints:

Download Postman Collection

Authentication

The HUBHUD API supports two authentication methods depending on the endpoint version.

Basic Authentication (v2 endpoints)

All /api/v2/* endpoints use HTTP Basic Authentication with your API Client credentials.

Encode your api_client_id:api_client_secret in Base64 and include it in the Authorization header:

Headers
{
    "Content-Type": "application/json",
    "Authorization": "Basic BASE64(api_client_id:api_client_secret)"
}
Generating Base64 (Node.js / Browser)
const credentials = btoa('YOUR_CLIENT_ID:YOUR_SECRET_ID');
// Use: Authorization: Basic ${credentials}

JWT Token (v1 / legacy endpoints)

Legacy endpoints, such as /api/withdrawals and /api/create-payment, use JWT Bearer Tokens.

POST
/api/login

Request Body (JWT)

Field Type Description Required
email String Your account's registered email. Yes
password String Your account password. Yes

Response (JWT)

JSON
{
    "success": true,
    "message": "Token generated successfully",
    "token": "YOUR_JWT_TOKEN",
    "token_type": "Bearer",
    "expires_at": "2025-07-01 00:21:56"
}

Create Pay-in (v2) NEW

Creates a new asynchronous pay-in request. This is the new, recommended method for creating deposits.

Asynchronous Flow

This endpoint is asynchronous. A successful request will return an HTTP 202 Accepted status, indicating the request was received and is being processed. The final payment details (including the QR Code) will be delivered to your registered deposit webhook.

POST
/api/v2/create-payment

Request Headers

This endpoint requires Basic Authentication. See the Authentication section.

Request Body

Field Type Description Required
externalId String A unique identifier for the transaction, generated by you. Yes
amount Number The deposit amount. E.g., 5 for R$ 5,00. Yes
document String The payer's document number (CPF/CNPJ). Yes
name String The full name of the payer. Yes
identification String Additional identification field. No
expire Number Expiration time for the payment in seconds. No
description String A brief description of the payment. Yes

Success Response (202 Accepted)

JSON
{
    "success": true,
    "message": "Pay-in request received and is being processed.",
    "payment_id": "62e2e461ac282e33d85",
    "status": "pending"
}

Code Examples

cURL
curl -X POST 'https://api.hubhud.io/api/v2/create-payment' \
-H 'Content-Type: application/json' \
-H 'Authorization: Basic YOUR_BASE64_CREDENTIALS' \
-d '{
    "externalId": "62e2e461ac282e33d85",
    "amount": 5,
    "document": "25689754895",
    "name": "Hubhud",
    "expire": 3600,
    "description": "description"
}'
JavaScript
const b64Auth = btoa('YOUR_CLIENT_ID:YOUR_SECRET_ID');

const response = await fetch('https://api.hubhud.io/api/v2/create-payment', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': `Basic ${b64Auth}`
    },
    body: JSON.stringify({
        externalId: '62e2e461ac282e33d85',
        amount: 5,
        document: '25689754895',
        name: 'Hubhud',
        expire: 3600,
        description: 'description'
    })
});

const data = await response.json();
PHP
$client = new \GuzzleHttp\Client();

$response = $client->post('https://api.hubhud.io/api/v2/create-payment', [
    'headers' => [
        'Content-Type' => 'application/json'
    ],
    'auth' => [
        'YOUR_CLIENT_ID', 
        'YOUR_SECRET_ID'
    ],
    'json' => [
        'externalId' => '62e2e461ac282e33d85',
        'amount' => 5,
        'document' => '25689754895',
        'name' => 'Hubhud',
        'expire' => 3600,
        'description' => 'description'
    ]
]);

$data = json_decode($response->getBody(), true);
Python
import requests

headers = {
    'Content-Type': 'application/json',
}

data = {
    'externalId': '62e2e461ac282e33d85',
    'amount': 5,
    'document': '25689754895',
    'name': 'Hubhud',
    'expire': 3600,
    'description': 'description'
}

response = requests.post(
    'https://api.hubhud.io/api/v2/create-payment',
    headers=headers,
    json=data,
    auth=('YOUR_CLIENT_ID', 'YOUR_SECRET_ID')
)

result = response.json()

Create Payment (v1)

Legacy endpoint — uses JWT Bearer Token authentication. For new integrations, prefer Create Pay-in (v2) or Create Payment (v2).

Creates a new PIX payment synchronously. This endpoint uses legacy JWT Auth.

POST
/api/create-payment

Request Headers

Headers
{
    "Content-Type": "application/json",
    "Authorization": "Bearer YOUR_JWT_TOKEN"
}

Request Body

Field Type Description Required
externalId String A unique identifier for the transaction. Yes
amount Number The deposit amount. Yes
document String The payer's document number (CPF/CNPJ). Yes
name String The full name of the payer. Yes
identification String Additional identification field. No
expire Number Expiration time in seconds. Yes

Success Response

JSON
{
    "success": true,
    "message": "Payment processed successfully",
    "data": {
        "pix": "00020126850014br.gov.bcb.pix2563pix.voluti.com.br/...",
        "uuid": "943c8da5-f771-4a10-b5a1-f4678d65ca4c",
        "externalId": "012553856872857548828558",
        "amount": "12.00",
        "createdAt": "2025-04-25T23:05:42.180Z",
        "expire": 3600
    }
}

Withdrawals (v1)

Legacy endpoint — uses JWT Bearer Token authentication. For new integrations, prefer Withdrawals (v2).

Create a new withdrawal request to transfer funds to a PIX key. (This endpoint uses legacy JWT Auth).

POST
/api/withdrawals

Request Headers

Headers
{
    "Content-Type": "application/json",
    "Authorization": "Bearer YOUR_JWT_TOKEN"
}

Request Body

Field Type Description Required
externalId String A unique identifier for the transaction. Yes
pixKey String The destination PIX key. Yes
pixKeyType String Enum: CPF, CNPJ, EMAIL, PHONE, EVP. Yes
documentNumber String The recipient's document number (CPF/CNPJ). Yes
name String The full name of the recipient. Yes
amount Number The withdrawal amount. Yes

Success Response

JSON
{
    "success": true,
    "message": "Withdrawal request created successfully",
    "data": {
        "id": "943c8da5-f771-4a10-b5a1-f4678d65ca4c",
        "externalId": "45656286d2",
        "amount": "20.00",
        "status": "pending",
        "createdAt": "2025-04-25T23:05:42.180Z"
    }
}

(Bankslip) Boleto NEW

Endpoints for validating, paying, and querying boleto (bank slip) transactions. These endpoints use Basic Authentication (api_client_id:api_client_secret).

Processes the payment of a bank slip (boleto). The barcode is read and validated internally — no prior call to a read endpoint is necessary. The payment is created asynchronously and processed in the background.

Authentication

Basic Auth — send an Authorization header with the base64 value of api_client_id:api_client_secret.

Request Body

Field Type Description Required
barcode String The barcode digits (numeric only, 44–60 characters). Also accepted as barCode.digitable (nested object). Yes
payer_document String CPF or CNPJ of the payer. Optional: auto-populated from barcode data via internal read if not provided. No

Success Response (200 OK)

JSON
{
    "status": "ok",
    "message": "Boleto payment created successfully",
    "data": {
        "id": "1234567890123",
        "boleto": {
            "createdAt": "2026-04-17T04:18:07.000000Z",
            "updatedAt": "2026-04-17T04:25:15.000000Z",
            "status": "PROCESSING",
            "isPaid": "N",
            "paidAt": null,
            "barcode": "07790001161242351436606752570454914270000001000",
            "type": 2,
            "amounts": {
                "original": "150.00",
                "paid": "150.00",
                "fee": "2.25",
                "total": "152.25"
            },
            "payer": {
                "document": "000.000.000-00"
            },
            "beneficiary": {
                "name": "Example Beneficiary LTDA",
                "document": "12.345.678/0001-90"
            },
            "fees": {
                "service": {
                    "percentage": "1.50",
                    "amount": "2.25",
                    "currency": "BRL"
                }
            }
        }
    }
}

Error Response (400 Bad Request)

JSON
{
    "status": "error",
    "message": "Validation failed",
    "errors": {
        "barcode": ["The barcode must be between 44 and 60 characters."],
        "tfa_code": ["The tfa_code field is required."],
        "payer_document": ["The payer_document is not a valid document."]
    }
}

Request Example (cURL)

cURL
curl -X POST 'https://api.hubhud.io/api/v2/transactions/bankslip-out/payment' \
-H 'Authorization: Basic YOUR_BASE64_CREDENTIALS' \
-H 'Content-Type: application/json' \
-d '{
    "barcode": "07790001161242351436606752570454914270000001000",
    "amount": 150.00,
    "tfa_code": "123456",
    "payer_document": "00000000000191",
    "beneficiary_name": "Example Beneficiary LTDA",
    "beneficiary_document": "12345678000190",
    "read_response": "{\"status\":\"ok\",\"data\":{\"value\":150.00,\"digitable\":\"07790001161242351436606752570454914270000001000\",\"type\":2,...}}"
}'

Important Notes

  • Internal Read: The endpoint automatically reads and validates the barcode internally. There is no need to call a separate read endpoint before calling pay.
  • 2FA via API: 2FA verification is always bypassed for this API endpoint, regardless of account settings. The tfa_code field is accepted but not validated.
  • Balance Check: The system verifies that the account has sufficient available balance to cover both the boleto amount and service fees before processing.
  • Duplicate Prevention: The same barcode cannot be processed twice unless the previous attempt has a failed or cancelled status.
  • Asynchronous Processing: The payment is queued for background processing. The response indicates the payment has been created with status PROCESSING, but settlement may take time.
  • Already Paid: If the barcode was already paid by the same account, the endpoint returns status: ok with the existing payment data (idempotent).

Returns the current status of a boleto payment. The lookup is scoped to the authenticated account and its allowed sub-accounts, so transactions from another account are never returned.

Authentication

Basic Auth - send an Authorization header with the base64 value of api_client_id:api_client_secret.

Smart Lookup

You can send any one of the fields below. The API searches in boleto history and payments using the provided identifier.

Field Type Description Required
id String Can be the boleto history ID (request_bankslips_out.id) or a payment ID (payments.id). One of these fields is required
transaction_id String Transaction identifier. It can match a boleto history ID, payments.external_payment_id, or payments.provider_transaction_id. One of these fields is required
barcode String Boleto barcode or digitable line. Punctuation is accepted and will be normalized before lookup. One of these fields is required

Request Examples

By boleto ID
{
    "id": "1717012345678123"
}
By payment ID
{
    "id": "12345"
}
By transaction_id
{
    "transaction_id": "1717012345678123"
}
By barcode
{
    "barcode": "34191.79001 01043.510047 91020.150008 2 91070026000"
}

Request Example (cURL)

cURL
curl -X POST 'https://api.hubhud.io/api/v2/transactions/bankslip-out/status' \
-H 'Authorization: Basic YOUR_BASE64_CREDENTIALS' \
-H 'Content-Type: application/json' \
-d '{
    "barcode": "34191.79001 01043.510047 91020.150008 2 91070026000"
}'

Response - Pending

JSON
{
    "status": "ok",
    "data": {
        "transactionId": "1717012345678123",
        "status": "PENDING",
        "payerName": "Cliente Exemplo",
        "payerCpf": "12345678900"
    }
}

Response - Paid

JSON
{
    "status": "ok",
    "data": {
        "transactionId": "1717012345678123",
        "status": "PAID",
        "payerName": "Cliente Exemplo",
        "payerCpf": "12345678900",
        "paymentDate": "2026-05-29T18:42:10.000000Z",
        "authentication": "ABC123456789XYZ"
    }
}

Error Responses

Not found or access denied
{
    "status": "error",
    "message": "Transaction not found or access denied."
}
Payment is not a boleto
{
    "status": "error",
    "message": "Bankslip transaction not found for this payment."
}

Create Payment (v2) NEW

Creates a new PIX payment synchronously using Basic Authentication. This is the v2 version of Create Payment, updated to use API Client credentials (no JWT required).

POST
/api/v2/create-payment

Request Headers

Requires Basic Authentication (api_client_id:api_client_secret in Base64). See the Authentication section.

Request Body

Field Type Description Required
externalId String A unique identifier for the transaction, generated by you. Yes
amount Number The deposit amount. E.g., 5 for R$ 5,00. Yes
document String The payer's document number (CPF/CNPJ). Yes
name String The full name of the payer. Yes
identification String Additional identification field. No
expire Number Expiration time for the QR Code in seconds. No
description String A brief description of the payment. Yes

Success Response (200 OK)

JSON
{
    "success": true,
    "message": "Payment processed successfully",
    "data": {
        "pix": "00020126850014br.gov.bcb.pix2563pix.example.com.br/...",
        "uuid": "943c8da5-f771-4a10-b5a1-f4678d65ca4c",
        "externalId": "012553856872857548828558",
        "amount": "12.00",
        "createdAt": "2025-04-25T23:05:42.180Z",
        "expire": 3600
    }
}

Code Example (cURL)

cURL
curl -X POST 'https://api.hubhud.io/api/v2/create-payment' \
-H 'Content-Type: application/json' \
-H 'Authorization: Basic YOUR_BASE64_CREDENTIALS' \
-d '{
    "externalId": "012553856872857548828558",
    "amount": 12,
    "document": "25689754895",
    "name": "Fulano de Tal",
    "expire": 3600
}'

Transaction Status (v2) NEW

Returns the locally recorded status of a specific IN (pay-in) or OUT (pay-out) transaction. The lookup accepts either external_payment_id or provider_transaction_id.

POST
/api/v2/transaction/status

Authentication

Requires HTTP Basic Authentication using api_client_id:api_client_secret. The authenticated account is determined exclusively from these credentials.

The lookup is always restricted by the authenticated account's internal ID and returns only client-visible transactions. Supplying an identifier that belongs to another account returns 404 Not Found.

Request Body

FieldTypeDescriptionRequired
idStringAn external_payment_id or provider_transaction_id.Yes

Success Response — IN example (200 OK)

JSON
{
    "success": true,
    "externalId": "payin-order-123",
    "providerTransactionId": "E00416968202511100200KYh0fOaevTK",
    "status": "paid",
    "type": "IN",
    "amount": 100.00
}

Success Response — OUT example (200 OK)

JSON
{
    "success": true,
    "externalId": "payout-order-456",
    "providerTransactionId": "E90400888202511100200ABC12345678",
    "status": "processing",
    "type": "OUT",
    "amount": 75.50
}

Error Responses

JSON — 401 Unauthorized
{ "message": "Invalid API credentials." }
JSON — 404 Not Found
{ "message": "Transaction not found in this account." }
JSON — 422 Unprocessable Content
{
    "message": "The id field is required.",
    "errors": { "id": ["The id field is required."] }
}

Code Example (cURL)

cURL
curl -X POST 'https://api.hubhud.io/api/v2/transaction/status' \
-u 'YOUR_CLIENT_ID:YOUR_CLIENT_SECRET' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{ "id": "YOUR_EXTERNAL_ID_OR_PROVIDER_ID" }'

Withdrawals (v2) NEW

Process a PIX withdrawal using Basic Authentication. This is the v2 version of Withdrawals, using API Client credentials instead of JWT tokens. 2FA is skipped for API requests.

POST
/api/v2/withdrawals

Request Headers

Requires Basic Authentication (api_client_id:api_client_secret in Base64). See the Authentication section.

Note on Sender Name

The sender's name is automatically taken from the authenticated API client's account user. You do not need to include it in the request body.

Request Body

Field Type Description Required
externalId String A unique identifier for the transaction. Yes
pixKey String The destination PIX key. Yes
pixKeyType String Enum: CPF, CNPJ, EMAIL, PHONE, EVP. Yes
documentNumber String The recipient's document number (CPF/CNPJ). Yes
amount Number The withdrawal amount in BRL. Yes
account_id Integer Sub-account ID to withdraw from. Mandatory if the authenticated account has sub-accounts. No

Success Response (200 OK)

JSON
{
    "success": true,
    "message": "Withdraw processed successfully",
    "data": {
        "id": "943c8da5-f771-4a10-b5a1-f4678d65ca4c",
        "externalId": "wd_abc123",
        "amount": "50.00",
        "status": "pending",
        "createdAt": "2025-04-25T23:05:42.180Z"
    },
    "payment_id": "wd_abc123"
}

Error Responses

JSON — 400 Bad Request
{
    "success": false,
    "message": "Insufficient balance.",
    "errors": null
}
JSON — 403 Forbidden
{
    "error": "Withdrawal via API is blocked for this account"
}

Code Example (cURL)

cURL
curl -X POST 'https://api.hubhud.io/api/v2/withdrawals' \
-H 'Content-Type: application/json' \
-H 'Authorization: Basic YOUR_BASE64_CREDENTIALS' \
-d '{
    "externalId": "wd_abc123",
    "pixKey": "[email protected]",
    "pixKeyType": "EMAIL",
    "documentNumber": "12345678901",
    "amount": 50.00
}'

Balance (v2) NEW

Returns the balance for the authenticated account using Basic Authentication. The API reads only the balance row for the account's current acquirer: balances.account_id = account.id and balances.acquirer_id = accounts.acquirer_id.

POST
/api/v2/balance

Request Headers

This endpoint requires Basic Authentication. See the Authentication section.

Request Body

None. The account is determined from the authenticated API Client credentials.

Success Response (200 OK)

JSON
{
    "success": true,
    "data": {
        "available_balance": "1250.50",
        "blocked_balance": "80.00"
    }
}

Error Responses

JSON - 401 Unauthorized
{
    "message": "API credentials missing."
}
JSON - 401 Unauthorized
{
    "message": "Invalid API credentials."
}
JSON - 403 Forbidden
{
    "success": false,
    "message": "Your account has been deactivated administratively.",
    "error": "account_inactive"
}

Code Example (cURL)

cURL
curl -X POST 'https://api.hubhud.io/api/v2/balance' \
-H 'Authorization: Basic YOUR_BASE64_CREDENTIALS' \
-H 'Accept: application/json'

Legacy Endpoints (JWT Auth)

These endpoints use JWT Bearer Token authentication (obtained via POST /api/login). All require the header Authorization: Bearer YOUR_JWT_TOKEN.

Returns the balance for the authenticated account using JWT Bearer Token authentication. The API reads only the balance row for the account's current acquirer: balances.account_id = account.id and balances.acquirer_id = accounts.acquirer_id.

Request Body

None. The account is determined from the authenticated JWT token.

Success Response (200 OK)

JSON
{
    "success": true,
    "data": {
        "available_balance": "1250.50",
        "blocked_balance": "80.00"
    }
}

Response Fields

FieldTypeDescription
available_balanceNumber/StringAvailable balance on the account's current acquirer.
blocked_balanceNumber/StringAmount currently blocked on the account's current acquirer.

Error Responses

JSON - 401 Unauthorized
{
    "message": "Unauthenticated."
}
JSON - 404 Not Found
{
    "message": "Account not found."
}
JSON - 403 Forbidden
{
    "message": "Unauthorized access to this account."
}

Code Example (cURL)

cURL
curl -X POST 'https://api.hubhud.io/api/balance' \
-H 'Authorization: Bearer YOUR_JWT_TOKEN' \
-H 'Accept: application/json'

Returns the status of a specific transaction. Searches by external_payment_id or provider_transaction_id.

Request Body

FieldTypeDescriptionRequired
idStringThe externalId or provider transaction ID to look up.Yes

Success Response (200 OK)

JSON
{
    "success": true,
    "externalId": "62e2e461ac282e33d85",
    "providerTransactionId": "E00416968202511100200KYh0fOaevTK",
    "status": "paid",
    "type": "IN",
    "amount": 5.00
}

Error Responses

JSON — 404
{ "message": "Transaction not found in this account." }

Code Example (cURL)

cURL
curl -X POST 'https://api.hubhud.io/api/transaction/status' \
-H 'Authorization: Bearer YOUR_JWT_TOKEN' \
-H 'Content-Type: application/json' \
-d '{ "id": "62e2e461ac282e33d85" }'

Returns a paginated list of transactions for the authenticated account. All filters are optional. Results are paginated 15 per page and ordered by newest first.

Request Body (all optional)

FieldTypeDescriptionRequired
statusStringFilter by status. Enum: paid, processing, pending, refunded, canceled.No
type_transactionStringFilter by type. Enum: IN (deposit), OUT (withdrawal).No
date_fromString (date)Start date filter (e.g. 2025-01-01). Used with date_to.No
date_toString (date)End date filter. Must be equal to or after date_from.No

Success Response (200 OK)

Returns a Laravel paginated resource collection. Each item is a PaymentResource object.

cURL
curl -X POST 'https://api.hubhud.io/api/transactions' \
-H 'Authorization: Bearer YOUR_JWT_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
    "status": "paid",
    "type_transaction": "IN",
    "date_from": "2025-01-01",
    "date_to": "2025-01-31"
}'

Returns the sum of amounts and fees for paid transactions within a given period. Defaults to type_transaction=IN if not specified.

Request Body (all optional)

FieldTypeDescriptionRequired
dateString (date)Filter for a single specific date.No
startDateString (date)Start of date range. Used with endDate.No
endDateString (date)End of date range. Must be equal to or after startDate.No
type_transactionStringEnum: IN or OUT. Default: IN.No

Success Response (200 OK)

JSON
{
    "total_amount": 1250.50,
    "total_fee": 18.75,
    "currency": "BRL"
}

Code Example (cURL)

cURL
curl -X POST 'https://api.hubhud.io/api/transactions/totals' \
-H 'Authorization: Bearer YOUR_JWT_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
    "startDate": "2025-01-01",
    "endDate": "2025-01-31",
    "type_transaction": "IN"
}'

Health Check

Returns the operational status of the API and its database connection. No authentication required.

GET
/api/health

Success Response (200 OK)

JSON
{ "status": "healthy" }

Error Response (503 Service Unavailable)

JSON
{ "status": "unhealthy", "database": "unreachable" }

Code Example (cURL)

cURL
curl -X GET 'https://api.hubhud.io/api/health'

Webhooks

Webhooks allow you to receive real-time notifications about payment status changes. Configure your webhook endpoint to receive these notifications automatically.

Webhook Configuration

To set up webhooks, you need to:

  1. Configure the webhook URL in your HUBHUD dashboard
  2. Implement the webhook handler to process incoming notifications

Webhook Events

You will receive notifications for the following events. We use a type field in the JSON payload to distinguish them.

Deposit Events (Pay-in)

  • PAYIN_CONFIRMED: Sent when a deposit (pay-in) is successfully received and confirmed.

Withdrawal Events (Payout)

  • PAYOUT_CONFIRMED: Sent when a withdrawal (payout) is successfully completed.
  • PAYOUT_CANCELED: Sent when a withdrawal (payout) fails or is canceled.

Webhook Payloads

Event: PAYIN_CONFIRMED (Deposit)

Sent when a deposit is successfully paid by the customer.

JSON
{
  "type": "PAYIN_CONFIRMED",
  "externalId": "1762740042198_cxweu8hxx",
  "amount": "2.25",
  "status": "paid",
  "fee_applied": 0,
  "endToEndId": null,
  "processed_at": "2025-11-09T23:01:12-03:00",
  "uuid": "42dbed01-b917-4dc1-a483-f8d91e180c0a",
  "metadata": {
    "authCode": "42dbed01-b917-4dc1-a483-f8d91e180c0a",
    "amount": "2.25",
    "paymentDateTime": "2025-11-10T02:01:08.422+00:00",
    "pixKey": "",
    "receiveName": "INTERMEDIACOES LTDA",
    "receiverName": "INTERMEDIACOES LTDA",
    "receiverBankName": "33053580",
    "receiverDocument": "46655005000153",
    "receiveAgency": "0000",
    "receiveAccount": "0000",
    "payerName": "FULANO DE TAL",
    "payerAgency": "0000",
    "payerAccount": "0000",
    "payerDocument": "58694521459",
    "createdAt": "2025-11-09T23:00:43-03:00",
    "endToEnd": "E00416968202511100200KYh0fOaevTK"
  }
}

Event: PAYOUT_CONFIRMED (Withdrawal)

Sent when a withdrawal request is successfully processed and the funds are sent.

JSON
{
  "type": "PAYOUT_CONFIRMED",
  "externalId": "123457778698758",
  "amount": "1.00",
  "status": "paid",
  "fee_applied": "0.30",
  "endToEndId": "E3305358020250721211fb18",
  "processed_at": "2025-07-21T21:13:13+00:00",
  "uuid": "943c8da5-f771-4a10-b5a1-f4678d65ca4c",
  "metadata": {
    "pixKey": "00000000000",
    "pixKeyType": "CPF",
    "receiverName": "FULANO DE TAL",
    "receiverBankName": "Banco Exemplo S.A.",
    "receiverDocument": "11122233345"
  }
}

Event: PAYOUT_CANCELED (Withdrawal)

Sent when a withdrawal request fails or is canceled (e.g., insufficient funds, invalid key).

JSON
{
  "type": "PAYOUT_CANCELED",
  "externalId": "9876543210",
  "amount": "150.75",
  "status": "canceled",
  "processed_at": "2025-11-09T23:10:00-03:00",
  "uuid": "a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8",
  "reason_cancelled": "Invalid PIX Key.",
  "metadata": []
}

Webhook Security

Signature Verification

Each webhook request includes a signature in the X-signature header. Verify this signature to ensure the request came from HUBHUD.

Example Verification (Node.js)

JavaScript
const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
    const hmac = crypto.createHmac('sha256', secret);
    hmac.update(payload);
    const expectedSignature = `sha256=${hmac.digest('hex')}`;
    return crypto.timingSafeEqual(
        Buffer.from(signature),
        Buffer.from(expectedSignature)
    );
}

// Usage
const isValid = verifyWebhook(
    request.body,
    request.headers['x-signature'],
    process.env.WEBHOOK_SECRET
);

Important Notes

QR Code Expiration

The generated QR code is valid for the time specified in the expire field (in seconds). After this period, the QR code becomes invalid and a new payment must be created.

Best Practices

  • Always validate the response status and handle errors appropriately.
  • Store the externalId for future reference and idempotency.
  • Implement proper error handling and retry mechanisms.
  • Secure your webhook endpoint by verifying the X-Signature header.
  • Prefer v2 endpoints (Basic Auth) for new integrations — JWT endpoints are legacy.