Pular para o conteúdo

Link an account

Linking creates a Pix enrollment (a wallet) that binds a payer's account at their bank to a passkey on their device. Every payment flow requires an AUTHORISED enrollment first, so this is the flow you integrate before any payment.

The enrollment is authorized without redirection: the payer authenticates once at their bank, then registers a FIDO2 credential on their device. From then on, payments are approved with that passkey.

Method sequence

  1. participants.listMostUsed(subTenantId) — list banks enabled for Pix, most-used first. Filter to Pix-capable servers with the pixEnabledServers helper and let the payer pick one. Use listRegistered() as a fallback.
  2. enrollments.listByUserJsr(cpf, { deviceId }) — check whether the payer already has an AUTHORISED enrollment (skip linking if so).
  3. enrollments.createJsr(req) — open the enrollment. Returns { id, redirectUrl }. Open redirectUrl on the device.
  4. [device] the payer authenticates at the bank; the bank redirects back with state, code, and idToken, which the device delivers to your backend.
  5. enrollments.getDeviceOptionsJsr(req) — exchange those callback params for FIDO2 registration options.
  6. [device] the device calls navigator.credentials.create() with those options and produces an attestation.
  7. enrollments.registerDeviceJsr(enrollmentId, attestation) — bind the credential. The enrollment becomes AUTHORISED.

Sequence diagram

Account linking — from bank selection to an AUTHORISED enrollment

Create the enrollment

Steps 1–3: list banks, check for an existing enrollment, and create a new one. createJsr requires riskSignals collected from the device (device id, timezone, language, screen size, account tenure), and the X-Client-IP header passed as clientIp.

import { pixEnabledServers } from '@linainfratech/embedded-payments';

const cpf = '39033521016';
const clientIp = '203.0.113.10';            // payer device IP -> X-Client-IP
const riskSignals = {
deviceId: 'device-abc',
userTimeZoneOffset: '-03:00',
language: 'pt-BR',
screenDimensions: { width: 390, height: 844 },
accountTenure: '2024-01-01',
};

// 1) Banks enabled for Pix (most-used first; fallback to listRegistered)
const participants = subTenantId
? await client.participants.listMostUsed(subTenantId)
: await client.participants.listRegistered();
const bank = pixEnabledServers(participants)[0];

// 2) Skip linking if an AUTHORISED enrollment already exists
const existing = await client.enrollments.listByUserJsr(cpf, { deviceId: riskSignals.deviceId });

// 3) Create the enrollment
const created = await client.enrollments.createJsr({
organisationId: bank.OrganisationId,
authorisationServerId: bank.AuthorisationServerId,
enrollment: { document: cpf, deviceName: 'My Backend' },
riskSignals,
redirectUri: 'https://app.example.com/authorization-success',
}, { clientIp });

// Open created.redirectUrl on the device.

Register the device

After the bank redirects back with state, code, and idToken, exchange them for FIDO2 options (step 5), let the device build the attestation (step 6), then bind it (step 7). The example below is Node; the other SDKs expose the same two methods — getDeviceOptionsJsr and registerDeviceJsr — with idiomatic names.

// 5) Exchange callback params for FIDO2 registration options
const options = await client.enrollments.getDeviceOptionsJsr({
state,            // from the bank redirect — must match what you persisted in step 3
code,
idToken,
tenantId: 'lina',
platform: 'ANDROID',   // ANDROID | IOS | WEB | BROWSER | CROSS_PLATFORM
}, { clientIp });

// 6) [device] navigator.credentials.create(options) -> attestation

// 7) Register the device with the attestation returned by the device
const enrollment = await client.enrollments.registerDeviceJsr(options.enrollmentId, {
id: attestation.id,
rawId: attestation.rawId,
response: {
  clientDataJSON: attestation.response.clientDataJSON,
  attestationObject: attestation.response.attestationObject,
},
}, { clientIp });

console.log(enrollment.status); // AUTHORISED

registerDeviceJsr returns the enrollment with one of six statuses:

StatusMeaningWhat to do
AWAITING_RISK_SIGNALSThe enrollment was created but risk signals haven't been accepted yet.Retry with valid riskSignals, or wait — this is transient.
AWAITING_ACCOUNT_HOLDER_VALIDATIONThe bank hasn't confirmed the payer's authorization yet.Poll or wait for the redirect callback; don't call registerDeviceJsr early.
AWAITING_ENROLLMENTAuthorized at the bank, but device registration (steps 5–7) isn't complete.Finish the device registration steps.
AUTHORISEDReady to use.Use this enrollment's enrollmentId to pay.
REJECTEDThe bank or the payer rejected the enrollment.Send the payer through account linking again from step 1.
REVOKEDRevoked via revokeJsr (or by the payer at their bank).Same as REJECTED — link again.

Revoke an enrollment

Revoking is a single call — a logical revocation with no body. Pass the same clientIp used elsewhere in JSR flows.

await client.enrollments.revokeJsr(enrollmentId, { clientIp });

Next steps