1. Docs
Pix2DePix API
  • ⚡ Quickstart
  • 📝 Changelog
  • 📖 Glossary
  • Docs
    • 🔑 Authentication
    • 🪝 Webhooks
    • ⚠️ Errors & the response envelope
    • ✅ Possible Statuses
      • 📥💰 Deposit Statuses
      • 📤💸 Withdraw Statuses
    • 🧩 Advanced
      • 🔀 Synchronous requests & safe retries
    • 🛡️ Security & Limits
      • ✅ Best Practices
      • 🪲 Bug Bounty
      • 🧱 Firewall
      • 🚦 API Limits
    • 🚀 Features
      • ⏱️ QR Delay
  • API Endpoints
    • Ping
      GET
    • Deposit (PIX ➔ DePix)
      POST
    • Deposit Status
      GET
    • Deposits
      GET
    • User Info
      GET
    • Withdraw
      POST
    • Withdraw Status
      GET
  • Webhooks
    • Deposit Webhook
    • Withdraw Webhook
    • MED Webhook
  • Schemas
    • AllPossibleObjResponses
    • StandardResponse
    • PingObjOrError
    • JWTClaims
    • ObjResponse
    • ErrorObj
    • PingObj
    • ErrorResponse
    • DepositRequest
    • DepositObjOrError
    • DepositObj
    • PingResponse
    • DepositResponse
    • DepositStatusObj
    • DepositStatusResponse
    • DepositWebhookBody
    • DepositsResponse
    • DepositStatus
    • UserInfoResponse
    • WithdrawStatusResponse
    • WithdrawStatusObj
    • WithdrawResponse
    • WithdrawObj
    • WithdrawStatus
    • WithdrawWebhookBody
    • MEDWebhookBody
    • RejectionReasons
  1. Docs

🪝 Webhooks

Webhooks are still in beta
Breaking changes to the payloads and events on this page may still happen. When one does, we announce it in advance — on the Changelog and in Eulen's partner announcements group on Telegram — so you have time to adapt before it takes effect.

Overview#

A webhook is a way for our system to send real-time events to your server via HTTP requests, without your server needing to ask for the event. You register a URL that will receive events right after they happen — a deposit clears, a withdrawal completes, or a MED is filed against a past deposit.
See also: Deposit Statuses & Withdraw Statuses

Registering a webhook#

Registration is self-service. On your Telegram channel with Eulen:
/registerwebhook <type> <url> <secret>
Type: What kind of event data will be sent. Currently, webhook type can be:
deposit
withdraw
med
URL: The URL that events will be sent to. It must start with https:// — plain http://, localhost, and private address ranges (10.*, 172.*, 192.168.*) are rejected, so a tunnel with a public HTTPS hostname is the way to test against a local server.
Secret: You should create a secret of at least 16 characters. For maximum security, you can generate 32 random bytes as hex to use as a secret. You can do this by running this command on your terminal: openssl rand -hex 32.
Example Telegram command:
/registerwebhook deposit https://example.com/webhooks/deposit b4b8de163b9582b8281efa0be7360caba585b1c5240504a24c6dae43f504f922
Each type is registered separately, so a deposit webhook and a withdraw webhook can point at different URLs and use different secrets.

Changing or removing a webhook#

There is no update command. To point a type at a new URL, delete it first and register again:
/deletewebhooks deposit
/registerwebhook deposit https://example.com/webhooks/deposit-v2 <new-secret>
/deletewebhooks <type> removes every registration of that type for your account and answers There are no webhooks registered for this type. when there was nothing to remove. Use it on its own to turn a webhook off.
Never leave two URLs registered for the same type
Registering a second URL for a type you already registered is accepted, and the reply still says ✅ Webhook registered successfully — but it is not fan-out. Only one of the two endpoints receives the events, which one is not defined, and it can change between deliveries.
Always /deletewebhooks <type> before registering the replacement. During a migration, run the old and the new endpoint behind a single URL on your side.

Authorization Header#

Every delivery carries the secret you registered, verbatim, after Basic :
Authorization: Basic b4b8de163b9582b8281efa0be7360caba585b1c5240504a24c6dae43f504f922
The value is not re-encoded — whatever string you gave to /registerwebhook is exactly what arrives. That matters if your framework parses the header as standard HTTP Basic auth, because a hex secret is not valid Base64 credentials and the parse will fail.
If you want the standard form, register the Base64 of username:password as the secret. For example, partner:b4b8de163b9582b8281efa0be7360caba585b1c5240504a24c6dae43f504f922 becomes cGFydG5lcjpiNGI4ZGUxNjNiOTU4MmI4MjgxZWZhMGJlNzM2MGNhYmE1ODViMWM1MjQwNTA0YTI0YzZkYWU0M2Y1MDRmOTIy, and that is the string you register.
To encode the credentials in Base64, you can use this command on your terminal: echo -n 'username:password' | base64
Compare the header in constant time and reject anything that does not match. The secret is the only proof that a delivery came from us.

Responding to webhooks#

Only 200 and 201 count as delivered
A 202 Accepted, a 204 No Content, a redirect, or any other status is recorded as a failed delivery — even though your server processed the event fine. If you acknowledge before processing, answer 200, not 202.
Please remember to:
⚠️ Verify the Authorization header to ensure the request is coming from the correct source.
Return 200 OK (or 201) on successful processing.
Return a response within 15 seconds. After that the connection is dropped and the delivery counts as failed.
Use the deposits API as a fallback in case the webhook fails.
Keep the handler fast: do the heavy lifting (database writes, downstream calls) in a background job, and still answer 200.
Process idempotently. Use a stable identifier from the payload (qrId for deposits, id for withdrawals) as a dedupe key, and make a repeat delivery a no-op.
A failed delivery is not sent again
There is no automatic retry. If your endpoint is down, slow, or answers anything other than 200/201, that event is not redelivered — you get a warning message in your Telegram channel with Eulen, and nothing else.
This makes reconciliation mandatory rather than optional: a periodic sweep with GET /deposits is the only way to recover an event you missed. Do not build a design that waits for a second attempt.
Delivery order
Deposit webhooks for the same payment are delivered in order. Withdraw and MED webhooks are not ordered, and a status you already passed may still arrive after a newer one — compare against the state you have stored before overwriting it.
Discriminate on webhookType
Every webhook body carries a webhookType field. Switch on it to route the payload to the right handler. The three values are "deposit", "withdraw", and "med".

deposit event#

Sent when a deposit reaches a terminal or notable state. The status field carries the same values as GET /deposit-status, and the payload mirrors that response plus the webhookType discriminator. The full set of values is on Deposit Statuses.
DepositWebhookBody

FieldTypeDescription
webhookTypestringAlways "deposit".
bankTxIdstringBank transaction ID for the Pix payment.
blockchainTxIDstringLiquid Network transaction ID for the DePix transfer.
customerMessagestringHuman-readable message about the deposit.
payerNamestringName of the payer.
payerTaxNumberstringCPF/CNPJ of the payer.
pixKeystringPix key used for the payment.
qrIdstringID of the deposit (the id returned by POST /deposit).
statusstringCurrent deposit status. See deposit statuses.
valueInCentsintegerDeposit amount, in cents of BRL.
expirationstringQR expiry, ISO 8601.
payerEUIDstringEUID of the payer.
delayUntilstringWhen a delayed deposit will be processed, ISO 8601.
Recommended confirmation status
For crediting your user, confirm on approved — the funds have been received and cleared, and DePix will be sent shortly. This fires sooner than depix_sent.
{
  "webhookType": "deposit",
  "bankTxId": "processor_E12345678202601010000a1b2c3d4e5f",
  "blockchainTxID": "ce45cbc2f7bdbf9ddda5e7a9fac1e2135c3be2b342b4a93c9a06032672fbf8cc",
  "customerMessage": "Pedido 4821",
  "payerName": "Maria Silva Souza",
  "payerTaxNumber": "12345678909",
  "pixKey": "550e8400-e29b-41d4-a716-446655440000",
  "qrId": "019b76daa8007000000000000000000d",
  "status": "approved",
  "valueInCents": 15000,
  "expiration": "2026-01-01T00:20:00-03:00",
  "payerEUID": "EU011234567890128",
  "delayUntil": null
}

withdraw event#

Sent as a withdrawal (DePix → Pix payout) moves through its lifecycle. Mirrors the GET /withdraw-status response, plus the webhookType discriminator.
WithdrawWebhookBody

FieldTypeDescription
webhookTypestringAlways "withdraw".
idstringWithdrawal ID (the withdrawalId returned by POST /withdraw).
pixKeystringDestination Pix key.
statusstringCurrent withdraw status. See withdraw statuses.
expirationstringExpiry of the withdrawal, ISO 8601.
depositAddressstringLiquid address to send DePix to.
depositAmountInCentsintegerAmount of DePix sent, in cents.
payoutAmountInCentsintegerAmount of BRL paid out, in cents.
blockchainTxIDstringLiquid Network transaction ID for the DePix transfer.
receiptUrlstringURL of the payout receipt.
receiverNamestringName of the Pix key owner.
receiverTaxNumberstringCPF/CNPJ of the receiver (masked).
transferDatestringWhen the Pix payout was made, ISO 8601.
centralBankIdstringCentral Bank Pix transaction ID.
{
  "webhookType": "withdraw",
  "id": "019b76daa800700000000000000000a1",
  "pixKey": "example@example.com",
  "status": "sent",
  "expiration": "2026-01-01T00:20:00-03:00",
  "depositAddress": "lq1qqtkuz7nwp3cymteg0px5pcltrm26uvsak32t04fawfsacafutjg22fhvc8xhj0ue96cevuatnmt757rf4z2r5cwntsemgngcr",
  "depositAmountInCents": 124647,
  "payoutAmountInCents": 123400,
  "blockchainTxID": "79ad78f779839d2cbbe59612c7f9eff0919892ae601888b40ab877607ea54217",
  "receiptUrl": "https://response.eulen.app/receipt/pix/ddb95453-25f8-57f7-8a89-2225cfb18540",
  "receiverName": "Maria Silva Souza",
  "receiverTaxNumber": "***.456.789-**",
  "transferDate": "2026-01-01T00:01:12-03:00",
  "centralBankId": "E12345678202601010000a1b2c3d4e5f"
}

med event#

Sent when a MED (Pix special refund / chargeback) is filed against a past deposit. Use it to reconcile the affected deposit on your side.
No enablement step
med is a webhook type like the other two: register it with /registerwebhook med <url> <secret> and the events start arriving. There is nothing to switch on, and nothing to ask for.
MEDWebhookBody

FieldTypeDescription
webhookTypestringAlways "med".
qrIdstringID of the deposit the MED was filed against.
bankTxIdstringBank transaction ID of the original Pix payment.
blockchainTxIDstringLiquid Network transaction ID for the original DePix transfer.
creationDateReportstringWhen the MED was filed, ISO 8601.
euidstringEUID associated with the deposit.
namestringName tied to the report.
partnerIdstringYour Partner ID.
principalValueInCentsintegerPrincipal amount of the MED, in cents of BRL.
taxNumberstringCPF/CNPJ tied to the report.
{
  "webhookType": "med",
  "qrId": "019b76daa8007000000000000000000d",
  "bankTxId": "processor_E12345678202601010000a1b2c3d4e5f",
  "blockchainTxID": "ce45cbc2f7bdbf9ddda5e7a9fac1e2135c3be2b342b4a93c9a06032672fbf8cc",
  "creationDateReport": "2026-01-02T00:00:00-03:00",
  "euid": "EU011234567890128",
  "name": "Maria Silva Souza",
  "partnerId": "acme",
  "principalValueInCents": 15000,
  "taxNumber": "12345678909"
}

Reconciliation / missed webhooks#

Webhook delivery is best-effort and single-attempt, so reconciliation is part of a correct integration rather than a safety net you add later. If a delivery is missed — your endpoint was down, a 200 never reached us, or the response took longer than 15 seconds — pull the state directly.
GET /deposits — list deposits over a date range, filtered by status. Returns up to 200 rows of { qrId, status, bankTxId }. This is the documented fallback for sweeping over deposits you may have missed.
GET /deposit-status?id=<id> — fetch the full current state of a single deposit.
GET /withdraw-status?id=<id> — fetch the full current state of a single withdrawal.
Reconcile on a schedule
A periodic sweep with GET /deposits (for example, over the last few hours) is a reliable safety net. Treat your reconciliation job and your webhook handler as two paths to the same idempotent state machine.
Modified at 2026-08-12 02:14:19
Previous
🔑 Authentication
Next
⚠️ Errors & the response envelope
Built with