Pix on File Device Linking (Enrollment)
Pix on File (JSR) works by establishing a device linking using FIDO2/WebAuthn authentication.
- Merchant initiates enrollment with customer to elect a bank from authorized banks
- Customer elects the bank and authorize the enrollment
- Customer returns to the merchant app and completes FIDO2 credential registration
- Enrollment becomes active and ready for payments
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.ebanxpay.com - Production:
https://api-local-latam.ebanxpay.com
See the API endpoints reference for the complete tenant and environment list.
Device Linking Initiation (Enrollment)
Device Linking begins with an enrollment request towards customer's banking institution. A list of available banking institutions can be recovered using the Bank List request. The following parameters are used for Device Linking requests:
| Parameter | Requirement | Description |
|---|---|---|
integration_key | Required | Your integration key |
payment_type_code | Required | Must be pix-jsr |
operation | Required | Must be enrollment |
enrollment.country | Required | Must be br |
enrollment.merchant_enrollment_code | Required | Unique enrollment code |
enrollment.notification_url | Required | URL to callback after customer authorizes the device linking |
enrollment.intent_url | Required | Deeplink URL that will be used to trigger user redirection after confirming the device linking in the banking application (return to merchant app) |
enrollment.email | Required | Customer e-mail |
enrollment.identification_type | Required | Either cpf or cnpj |
enrollment.identification | Required | Customer document |
enrollment.account_details.bank_id | Required | A valid bank_id |
enrollment.fidoOptions.platform | Required | Platform where the enrollment is being initiated (ANDROID, IOS, BROWSER, CROSS_PLATFORM). |
enrollment.fidoOptions.risk_signals | Required | Refer to the Risk Signals section. |
Risk Signals are contextual data points collected during user authentication that help Relying Parties (banks) assess the trust level of an authentication attempt.
Sample Request
curl -X POST \
--location 'https://sandbox-local-latam.ebanxpay.com/ws/userenrollment' \
--header 'Content-Type: application/json' \
--data '{
"integration_key": "{your_ebanx_integration_key}",
"payment_type_code": "pix-jsr",
"operation": "enrollment",
"enrollment": {
"country": "br",
"merchant_enrollment_code": "{merchant_enrollment_code}",
"notification_url": "{notification_url_backend}",
"intent_url": "{intent_url_device}",
"email": "john.doe@example.com",
"identification_type": "{cpf/cnpj}",
"identification": "{document_number}",
"account_details": {
"bank_id": "{bank_id}"
},
"fidoOptions": {
"platform": "{ENUM}",
"risk_signals": {}
}
}
}'
A successful request will return a JSON response like the one below, containing a redirect URL that will lead the customer to their banking application.
{
"status": "SUCCESS",
"redirect_url": "{redirect_url}",
"enrollment": {
"enrollment_code": "{merchant_enrollment_code}",
"status": "pending"
}
}
The enrollment code is named differently depending on the surface. Map each one explicitly instead of reusing a single key:
- Request body:
enrollment.merchant_enrollment_code - API response:
enrollment.enrollment_code - Callback (webhook):
enrollment.merchant_enrollment_code
The FIDO2 registration options follow the same pattern: the callback sends registrationOptions (camelCase), while the enrollment query response returns registration_options (snake_case). The two are not guaranteed to be byte-identical — the query response may add authenticatorSelection.residentKey: "required" and authenticatorSelection.requireResidentKey: true if the bank did not send them. Pass whichever object you received straight to navigator.credentials.create() without merging the two.
Callback
Once the customer authorizes the device linking in their banking application, the banking application will redirect the user to the {intent_url} informed in the original request and a status update will be sent to the {notification_url} informed in the initiation step with additional information that will be required to complete the FIDO2/WebAuthn authentication in the device.
{
"enrollment": {
"merchant_enrollment_code": "{merchant_enrollment_code}",
"status": "pending",
"registrationOptions": {
"rp": {
"id": "string",
"name": "string"
},
"user": {
"id": "string",
"name": "string",
"displayName": "string"
},
"challenge": "string",
"pubKeyCredParams": [
{
"alg": -7,
"type": "public-key"
}
]
}
}
}
Device Linking Confirmation
The last stage for Device Linking is processing the results of the FIDO2/WebAuthn authentication performed by the customer after confirming the enrollment and returning to the merchant application. It requires the following parameters:
| Parameter | Requirement | Description |
|---|---|---|
integration_key | Required | Your integration key |
payment_type_code | Required | Must be pix-jsr |
operation | Required | Must be authentication |
enrollment.merchant_enrollment_code | Required | Unique enrollment code |
enrollment.authentication.credentialId | Required | Credential ID (Base64 encoded) |
enrollment.authentication.rawCredentialId | Required | Credential ID in ArrayBuffer format (Base64 encoded) |
enrollment.authentication.authenticatorAttachment | Required | Must be platform. |
enrollment.authentication.credentialType | Required | Must be public-key |
enrollment.authentication.response.clientDataJSON | Required | Client data in JSON, encoded in Base64url |
enrollment.authentication.response.attestationObject | Required | Authenticator attestation data, encoded in Base64url |
Sample Request
curl -X POST \
--location 'https://sandbox-local-latam.ebanxpay.com/ws/userenrollment/authentication' \
--header 'Content-Type: application/json' \
--data '{
"integration_key": "{your_ebanx_integration_key}",
"payment_type_code": "pix-jsr",
"operation": "authentication",
"enrollment": {
"merchant_enrollment_code": "{merchant_enrollment_code}",
"authentication": {
"credentialId": "{fido_id}",
"rawCredentialId": "{fido_raw_id}",
"authenticatorAttachment": "platform",
"credentialType": "public-key",
"response": {
"clientDataJSON": "{client_data_json}",
"attestationObject": "{attestation_object}"
}
}
}
}'
A successful request will return a JSON response like the one below, confirming that the enrollment is ready to be used for payment processing.
{
"status": "SUCCESS",
"payment_type": "pix-jsr",
"enrollment": {
"status": "accepted"
}
}
Device Linking confirmation is synchronous and conclusive. EBANX calls the bank during the request and only records the enrollment as accepted once the bank confirms it, so a SUCCESS response with accepted means the enrollment is genuinely active. You can proceed to request payments immediately — there is no notification to wait for, and no need to poll.
Do not carry this assumption over to payment authentication, on the Payment page. That call is also synchronous, but it is not conclusive: it returns the payment still in PE, and confirmation arrives later. The two flows differ deliberately.
An enrollment is only usable for payments once enrollment.status is accepted. The full set of enrollment status values is:
| Status | Description |
|---|---|
pending | One or more required parties have not yet accepted or rejected the enrollment agreement. |
accepted | All required parties accepted. The enrollment is active and usable for payments. |
not_accepted | A party did not accept the enrollment agreement. |
revoked | A party revoked the enrollment agreement. A provider rejection is also returned as revoked. |
cancelled | The merchant revoked the enrollment agreement. |
expired | A required party did not accept before the enrollment expired. |
paused | A party paused the enrollment agreement. It cannot be used for payments while paused. |
not_found | The enrollment agreement no longer exists, or could no longer be found. |
Match these values exactly as returned. Note cancelled is spelled with a double L, and there is no rejected status — a bank rejection surfaces as revoked.