Skip to main content
Unlisted page
This page is unlisted. Search engines will not index it, and only users having a direct link can access it.

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.
Endpoint host

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.

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.

  1. 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.

  2. Create an enrollment

    Create an enrollment for the customer using the /ws/userenrollment endpoint.

    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 surfaces

    You send enrollment.merchant_enrollment_code, but the API response returns the same value as enrollment.enrollment_code. The webhook sent to your notification_url uses enrollment.merchant_enrollment_code again. Map each surface explicitly rather than reusing one key.

    redirect_url is an EBANX-hosted page

    The 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_url you supplied. That one is your own backend endpoint, which EBANX calls with status updates.



    Treat redirect_url as 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.

  3. Monitor enrollment status

    Listen for webhook notifications on your notification_url. Once the enrollment is activated, you can proceed to request payments using the active merchant_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.

  1. Create a payment

    Initiate the payment citing the established merchant_enrollment_code in the metadata object.

    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_url

    The enrollment step returns its URL as a top-level redirect_url, but the payment step does not. The hosted payment URL is nested at payment["pix-jsr"].hosted_url_payment, and there is no redirect_url anywhere in the payment response. Read the nested field.

    PE is not a confirmed payment

    The payment stays in PE until the customer completes the biometric approval on the hosted page and the provider reports the payment completed, after which EBANX marks it CO. That transition arrives through the payment status-change notification. Release goods or services on CO — never on the SUCCESS above.

  2. 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.

Still need help?

Help Image

We hope this article was helpful. If you still have questions, you can explore the following options: