Pix on File Hosted Flow
Pix on File (Jornada Sem Redirecionamento) is a Pix payment flow in Brazil that allows customers to authorize payments with a device linked to their bank account, without scanning a QR code for each purchase. The hosted flow keeps Risk Signals collection and the FIDO2/WebAuthn ceremony out of the merchant application: EBANX returns a redirect URL, runs device linking and customer authentication on its own page, and notifies the merchant by webhook once the enrollment and the payment are fully authenticated.
This guide outlines the key concepts and implementation details required to integrate the Pix on File hosted flow via API. It covers essential steps such as creating a hosted enrollment, redirecting the customer for authentication, requesting payments against an active enrollment, and handling the webhook notifications that confirm each step.
Requirements
- EBANX Account
- API Credentials - Ensure you have your EBANX integration Key. If not, complete the Merchant Signup Form.
Pix on File is processed by the EBANX Brazilian payment institution, so all requests must be sent to the local hosts below — not to the cross-border hosts used by other payment methods.
- Sandbox:
https://sandbox-local-latam.ebanx.com - Production:
https://api-local-latam.ebanx.com
How it works
The hosted flow significantly reduces integration complexity:
- You redirect customers to an EBANX-hosted URL.
- EBANX securely collects risk signals and processes WebAuthn.
- Once completed, the merchant is notified via Webhooks.
Enrollment Flow
The enrollment process establishes the authorization between merchant and customer. In this hosted flow, device linking confirmation is handled entirely by EBANX during the redirect.
- Provide the bank list (optional)
The merchant can optionally show a bank list and let the user select their bank. This step is optional because if no bank is selected, the same bank list dropdown will be shown during the EBANX redirect anyway.
Like the bank, the merchant can also send
enrollment.identification. When this field is provided by the merchant, it is prefilled on the Hosted page and cannot be modified by the customer. - Create an enrollment
Create an enrollment for the customer using the
/ws/userenrollmentendpoint.curl -X POST \
--location 'https://sandbox-local-latam.ebanx.com/ws/userenrollment' \
--header 'Content-Type: application/json' \
--data '{
"integration_key": "{{integration_key}}",
"payment_type_code": "pix-jsr",
"operation": "hosted_enrollment",
"enrollment": {
"country": "br",
"merchant_enrollment_code": "{{unique_enrollment_code}}",
"notification_url": "https://merchant.example.com/callback",
"intent_url": "{{intent_url}}",
"email": "john.doe@example.com",
"identification_type": "{{identification_type}}",
"identification": "{{tax_id}}",
"account_details": {
"bank_id": "{{bank_id}}"
}
}
}'The response will contain the
redirect_url— the page where your customer completes their WebAuthn registration and authorizes the bank.{
"status": "SUCCESS",
"redirect_url": "https://merchant.example.com/callback",
"enrollment": {
"enrollment_code": "{{unique_enrollment_code}}",
"status": "pending"
}
}Field naming across surfacesYou send
enrollment.merchant_enrollment_code, but the API response returns the same value asenrollment.enrollment_code. The webhook sent to yournotification_urlusesenrollment.merchant_enrollment_codeagain. Map each surface explicitly rather than reusing one key.redirect_url is an EBANX-hosted pageThe sample above uses this documentation's standard placeholder domain, but the value you receive is an EBANX-hosted page — it is not on your domain, and it is not the
notification_urlyou supplied. That one is your own backend endpoint, which EBANX calls with status updates.Treat
redirect_urlas opaque: send the customer to whatever value you receive, unchanged, and never rebuild it.Instead of performing device linking in your app, all the complex authentication is handled in this redirect. You should receive a webhook from EBANX once the enrollment is fully confirmed and authenticated.
- Monitor enrollment status
Listen for webhook notifications on your
notification_url. Once the enrollment is activated, you can proceed to request payments using the activemerchant_enrollment_code.
Payment Flow
Once an active enrollment is established, you can start requesting payments. The same idea applies: all customer presence authentication (WebAuthn) is handled by the EBANX app redirect, and the merchant only needs to receive a webhook when the payment reaches a confirmed status.
- Create a payment
Initiate the payment citing the established
merchant_enrollment_codein themetadataobject.curl -X POST \
--location 'https://sandbox-local-latam.ebanx.com/ws/direct' \
--header 'Content-Type: application/json' \
--data '{
"integration_key": "{{integration_key}}",
"type": "request",
"mode": "full",
"payment": {
"country": "br",
"currency_code": "BRL",
"amount_total": 99.85,
"merchant_payment_code": "{{unique_merchant_code}}",
"order_number": null,
"payment_type_code": "pix-jsr",
"name": "John Doe",
"email": "john.doe@example.com",
"document": "{{tax_id}}",
"metadata": {
"merchant_enrollment_code": "{{unique_enrollment_code}}"
}
}
}'You will receive a pending payment response containing
payment["pix-jsr"].hosted_url_payment. Redirect the customer to that URL to provide their biometric approval.{
"payment": {
"hash": "59acc5f00945fa382ab051651440826da7701533249b3a475",
"country": "br",
"merchant_payment_code": "{{unique_merchant_code}}",
"order_number": null,
"status": "PE",
"open_date": "{{YYYY-MM-DD HH:mm:ss}}",
"confirm_date": null,
"transfer_date": null,
"amount_br": "99.85",
"amount_ext": "99.85",
"amount_iof": "1",
"currency_rate": "1",
"currency_ext": "BRL",
"due_date": "{{YYYY-MM-DD}}",
"payment_type_code": "pix-jsr",
"pix-jsr": {
"hosted_url_payment": "https://payment.ebanx.com/pix-jsr-payment?hash=59acc5f00945fa382ab051651440826da7701533249b3a475&tenant=LOCAL_LATAM"
}
},
"status": "SUCCESS"
}The hosted payment URL is not a top-level redirect_urlThe enrollment step returns its URL as a top-level
redirect_url, but the payment step does not. The hosted payment URL is nested atpayment["pix-jsr"].hosted_url_payment, and there is noredirect_urlanywhere in the payment response. Read the nested field.PE is not a confirmed paymentThe payment stays in
PEuntil the customer completes the biometric approval on the hosted page and the provider reports the payment completed, after which EBANX marks itCO. That transition arrives through the payment status-change notification. Release goods or services onCO— never on theSUCCESSabove. - Wait for confirmation
You will receive a webhook notification when the payment is fully confirmed by the customer.
Resources
Use the following resources when testing in your sandbox environment.
API Reference
Click here to access detailed API documentation to integrate efficiently.
Mock Customer Data
Click here to view mock customer data for testing and validating user flows.
Error Codes
Click here to review common error codes to troubleshoot and resolve issues quickly.
Still need help?
We hope this article was helpful. If you still have questions, you can explore the following options:
- Merchant support: Contact our support team at sales.engineering@ebanx.com for assistance.
- Not a partner yet? Please complete the Merchant Signup Form, and our commercial team will reach out to you.