Paying Pix
Send payments to Brazilian bank accounts via Pix
This endpoint allows you to make payments from USDT to any given Pix key, which will reflect the balance in seconds into any bank account in Brazil.
Payments are processed through our prepaid system with direct bank settlement for optimal speed and reliability.
Request Body
| Name | Type | Description |
|---|---|---|
| pix_key | string | Required. The Pix key of the recipient. Can be a CPF/CNPJ, email, phone number, or random key. |
| currency | string | Required. Either 'BRL' or 'USDT'. Determines how the amount is interpreted. |
| amount | float | Required. Amount to be sent. Minimum value is 0.5 (validated as USDT equivalent regardless of the currency field). |
| from_address | string | Required. The sender wallet address. Must be a whitelisted address registered in your merchant wallets. |
| external_id | string | Recommended. Your internal reference ID for this transaction. Must be unique for each payment attempt. Used to prevent duplicates and track transactions. If omitted, idempotency and deduplication via external_id will not be available. |
| transfer_description | string | Optional. Free-text note appended to the recipient's Pix receipt. Composed together with the other fields (see Receipt Description); the final receipt is truncated to 140 characters if needed. |
| payer_document | string | Optional. The payer's document number. Included in the receipt when present. |
| payer_name | string | Optional. The payer's full name. Included in the receipt when present. |
| payer_id_type | string | Optional. The payer's document type, free text (e.g. DNI, CPF, CNPJ, CI). Included in the receipt when present. |
| payer_country | string | Optional. The payer's country code, free text (e.g. BRA, ARG, BOL). Included in the receipt when present. |
Currency Handling: If the payout currency is set to BRL, kamiPay sends exactly the BRL amount requested. If the payout currency is set to USDT, kamiPay calculates the BRL amount at payout creation time using the current exchange rate and sends that resulting BRL amount.
The minimum payment amount is 0.5 (validated as USDT equivalent). Sending an amount lower than 0.5 will result in a 400 error.
Receipt Description
Every Pix payment carries a short description that the recipient sees on their Pix receipt. kamiPay composes this message from the fields you send, concatenating every field that is present in a fixed order:
Pagamento via {your company name} {payer_name} {payer_id_type} {payer_document} {payer_country} {transfer_description}The message always begins with Pagamento via {your company name} (your registered company name). Each additional field you send is appended in the order above. Fields you don't send — or send blank — are simply skipped, with no extra spaces.
Examples
Sending all payer fields plus a note:
{
"payer_country": "ARG",
"payer_id_type": "DNI",
"payer_document": "12345678",
"payer_name": "Juan Perez",
"transfer_description": "Order #4821"
}produces:
Pagamento via {your company name} Juan Perez DNI 12345678 ARG Order #4821Sending only payer_document + payer_name:
Pagamento via {your company name} Juan Perez 12345678Sending only transfer_description:
Pagamento via {your company name} Order #4821Sending none of the optional fields falls back to just your company name:
Pagamento via {your company name}Length handling
The composed message is hard-truncated to 140 characters (the Pix protocol limit for this field). There is no error for long input — the receipt is silently cut to fit.
Because the fields are concatenated in a fixed order, the leading fields are the most protected from truncation: your company name is never lost, while a long transfer_description (the last field) is the first to be cut. Keep your most important information in the earlier fields.
The message is in Portuguese because the Pix recipient is always in Brazil. All description fields are optional; sending none of them simply falls back to Pagamento via {your company name}, so the receipt is never left blank and existing integrations stay backward-compatible.
Example Request
const url = `${baseURL}/v1/payments/payToPixKey`;
const body = {
"currency": "BRL",
"amount": 4.10,
"pix_key": "user@example.com", // CPF/CNPJ/email/Phone/pixkey
"from_address": "0x1c0aCF853xxxxx9488fEdce1ab4",
"external_id": "payment-unique-id-001", // Must be unique for each attempt
// Optional receipt description fields (see "Receipt Description"):
// all are composed into the receipt; the final message is truncated to 140 chars
// "payer_name": "Juan Perez",
// "payer_id_type": "DNI",
// "payer_document": "12345678",
// "payer_country": "ARG",
// "transfer_description": "Order #4821"
};
const response = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${access_token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body)
});import requests
import json
url = f"{base_url}/v1/payments/payToPixKey"
body = {
"currency": "BRL",
"amount": 4.10,
"pix_key": "user@example.com", # CPF/CNPJ/email/Phone/pixkey
"from_address": "0x1c0aCF853xxxxx9488fEdce1ab4",
"external_id": "payment-unique-id-001", # Must be unique for each attempt
# Optional receipt description fields (see "Receipt Description"):
# all are composed into the receipt; the final message is truncated to 140 chars
# "payer_name": "Juan Perez",
# "payer_id_type": "DNI",
# "payer_document": "12345678",
# "payer_country": "ARG",
# "transfer_description": "Order #4821"
}
headers = {
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json',
}
response = requests.post(url, json=body, headers=headers)package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
func main() {
url := fmt.Sprintf("%s/v1/payments/payToPixKey", baseURL)
// Create request body
requestBody, _ := json.Marshal(map[string]interface{}{
"currency": "BRL",
"amount": 4.10,
"pix_key": "user@example.com", // CPF/CNPJ/email/Phone/pixkey
"from_address": "0x1c0aCF853xxxxx9488fEdce1ab4",
"external_id": "payment-unique-id-001", // Must be unique for each attempt
// Optional receipt description fields (see "Receipt Description"):
// all are composed into the receipt; the final message is truncated to 140 chars
// "payer_name": "Juan Perez",
// "payer_id_type": "DNI",
// "payer_document": "12345678",
// "payer_country": "ARG",
// "transfer_description": "Order #4821"
})
// Create request
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(requestBody))
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", access_token))
req.Header.Add("Content-Type", "application/json")
// Make the request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return
}
defer resp.Body.Close()
}Response
Payment Successfully Processed
{
"status": "ok",
"kamipay_id": "txc_01jrazd7a9fqpp0bym8km1dbd6",
"data": {
"status": "broadcasted",
"usdt_amount": 0.875934
},
"external_id": "testing123"
}Always use kamipay_id and external_id for transaction tracking. The external_id field is only present in the response if it was provided in the request.
Payment Created (Processing)
Returned when the external_id matches an existing transaction that is still being processed or has already been completed.
{
"status": "created",
"kamipay_id": "txc_01jkg5vktaer5abc123kjgrsn2",
"data": null,
"external_id": "idempotency-1"
}Bad Request
The response format for 400 errors varies depending on the validation that failed.
Invalid PIX key format:
{
"detail": {
"msg": "Description of the format error",
"reformated_key": null,
"name": null
}
}Invalid amount (below minimum):
{
"detail": "Wrong amount USDt:0.3. Minimum USDt:0.5"
}Invalid currency:
{
"detail": "Currency should be BRL, USDT"
}Invalid or non-whitelisted from_address:
{
"detail": "Incorrect Address: 0xInvalidAddress123"
}Missing pix_key:
{
"detail": "pix_key is required"
}Unauthorized
Invalid or expired token:
{
"detail": "Incorrect Credentials"
}Valid token but insufficient permissions:
{
"detail": "Token Profile Not Authorized for this Endpoint"
}Conflict (Previous Transaction Cancelled)
Returned when the external_id matches an existing transaction that was previously cancelled. To retry, you must use a new external_id, since re-sending the same one will return this same 409 response.
{
"status": "canceled",
"kamipay_id": "txc_01jkg5vktaer5abc123kjgrsn2",
"data": null,
"external_id": "idempotency-1"
}Payment Failed (Transaction Cancelled)
The payment was attempted but failed at the gateway level. The transaction is permanently cancelled.
{
"status": "canceled",
"kamipay_id": "txc_01jrazd7a9fqpp0bym8km1dbd6",
"data": null,
"external_id": "testing123"
}Critical: This status indicates the transaction could NOT be processed and should be considered failed. If you want to retry the payment, you must use a new external_id.
Internal Server Error
{
"detail": "An unexpected error occurred."
}Service Unavailable
Returned when the payment gateway is under maintenance.
{
"detail": "Service is temporarily unavailable due to maintenance"
}Transaction Identifiers
| Identifier | Description |
|---|---|
kamipay_id | Primary identifier provided by kamiPay to monitor a specific transaction until completion. Always use this for transaction tracking. |
external_id | Your transaction identifier. Must be unique for each payment attempt. Essential for preventing duplicates, tracking in webhooks, and status endpoints. Only present in responses if it was provided in the request. |
Important: Always use kamipay_id and external_id as your primary tracking mechanisms.
Response Statuses Returned by This Endpoint
This endpoint can return the following status values in the response body:
| Status | HTTP Code | Description |
|---|---|---|
ok | 200 | Payment was successfully processed. The data object contains the transaction details. |
created | 201 | A transaction with this external_id already exists and is either in progress or completed. No new transaction is created. |
canceled | 409 | A transaction with this external_id was previously cancelled. You must use a new external_id to retry. |
canceled | 424 | The payment was attempted but failed at the gateway level. The transaction is permanently cancelled. You must use a new external_id to retry. |
For detailed transaction status tracking (e.g., intermediate processing states, final settlement confirmation), use the status endpoint or webhooks. This endpoint only returns the initial synchronous result of the payment attempt.
Payment Processing
Payments are processed through our prepaid system, which provides:
- Fast settlement: Direct bank transfers settled in batches
- Consistent identifiers: Same tracking system (
kamipay_idandexternal_id) regardless of underlying processing method
Error Handling & Retries
Status Code 424 - Critical Failure: When receiving a 424 status code, the transaction has failed permanently. To retry the payment, you must generate a new external_id. Using the same external_id will result in duplicate prevention mechanisms blocking the request.
Retry Guidelines
- Successful payments (200): No retry needed
- Processing payments (201): Do not retry. Monitor status via webhooks or status endpoint
- Failed payments (424): Generate a new
external_idbefore retrying - Conflicts (409): The previous transaction with this
external_idwas cancelled. You must use a newexternal_idto retry, as re-sending the same one will return409again - Server errors (500/503): Safe to retry with the same
external_idafter a short delay
Handling Parallel Requests
Status: Created (201)
When making requests while a previous transaction with the same external_id is still processing:
- Returns 201 status code
- The existing transaction is still being processed; no new transaction is created
- The
datafield will benull - Monitor via status endpoint or webhooks
Status: Cancelled (409/424)
- 409: A previous transaction with this
external_idwas cancelled. Use a newexternal_idto retry - 424: The current transaction failed to process at the gateway level. Use a new
external_idto retry