# Managing linked accounts

When a payer links an account on the payment page, Pluggy stores an **enrollment**: the link between their account at one institution and the device and browser where they created the passkey. Enrollments are created only by the payer, during a payment. Through the API you can list them, read them, see the payments made with them and revoke them.

Every endpoint on this page only returns your own client's enrollments.

## The enrollment

```json title="Enrollment"
{
  "id": "c91a5e2b-7d0f-4f3e-9a51-2f6b8e1d4c77",
  "status": "AUTHORIZED",
  "connector": {
    "id": 608,
    "name": "Santander",
    "imageUrl": "https://cdn.pluggy.ai/assets/connector-icons/208.svg"
  },
  "debtor": {
    "name": "John Doe",
    "taxNumber": "***.456.789-**",
    "bankAccount": {
      "agency": "1234",
      "account": "****4821",
      "name": "Santander"
    }
  },
  "cpf": "***.456.789-**",
  "cnpj": null,
  "customerId": "a2d4c1e8-5b7f-4c3a-9e2d-1f6b8c0d7e55",
  "limits": {
    "transaction": 1000,
    "daily": 3000,
    "dailyUsed": 149.9
  },
  "expiresAt": "2027-10-28T13:05:00.000Z",
  "dataConsent": null,
  "revokedBy": null,
  "errorDetail": null,
  "createdAt": "2026-10-28T13:02:00.000Z",
  "updatedAt": "2026-10-28T13:05:00.000Z"
}
```

| Field | Description |
|---|---|
| `id` | The enrollment id. Payment intents reference it as `enrollmentId`. |
| `status` | See the table below. |
| `connector` | The institution where the account is held. |
| `debtor` | The linked account, as the institution reported it. The tax number and the account number are masked. |
| `cpf` / `cnpj` | The payer's tax number, masked. Use the unmasked value to [filter the list](#list-enrollments). |
| `customerId` | The Payment Customer of the payment request where the link was created, or `null` when the payer typed their CPF. |
| `limits` | The per-payment (`transaction`) and `daily` limits the payer set at the bank, and how much of today's limit is used (`dailyUsed`), in BRL. Any of them can be `null` when the institution does not report it. |
| `expiresAt` | When the link expires, as set by the institution. |
| `dataConsent` | The balance-sharing permission, or `null` when it was not requested. See [Sharing balance](#sharing-balance). |
| `revokedBy` | Who revoked the link: `BANK` (the payer or the institution, at the bank) or `CLIENT` (you, through the API). `null` unless `status` is `REVOKED`. |
| `errorDetail` | `{ code, description }` with the reason the institution gave when the link was rejected, for example because the payer did not complete it in time. Otherwise `null`. |

| `status` | Meaning |
|---|---|
| `CREATED` | The payer started linking and has not finished: they are at the bank or creating the passkey. |
| `AUTHORIZED` | The link is active. Payments on that device need only biometrics. |
| `REJECTED` | The link was not completed: the institution or the payer rejected it, or the payer did not finish in time. `errorDetail` says which. |
| `REVOKED` | The link was revoked at the bank or through the API. |
| `EXPIRED` | The link reached its expiration date or became inactive at the institution. |

`REJECTED`, `REVOKED` and `EXPIRED` are final. To pay with biometrics again, the payer links again, which creates a new enrollment.

## List enrollments

```
GET /pix-biometric/enrollments?customerId={customerId}&status=AUTHORIZED
```

| Query parameter | Description |
|---|---|
| `customerId` | Enrollments created in payment requests for this Payment Customer. |
| `cpf` | The payer's CPF, digits only. |
| `cnpj` | The payer's CNPJ, digits only. |
| `status` | One of the statuses above. |
| `page` / `pageSize` | Pagination. `pageSize` is at most `500`. |

The response uses the usual pagination:

```json title="GET /pix-biometric/enrollments (response)"
{
  "total": 1,
  "totalPages": 1,
  "page": 1,
  "results": [
    {
      "id": "c91a5e2b-7d0f-4f3e-9a51-2f6b8e1d4c77",
      "status": "AUTHORIZED"
    }
  ]
}
```

A payer can have several enrollments: one per device and browser, and one per institution they linked. See the [API reference](/reference/pix-por-biometria/pix-biometric-enrollments-list).

## Retrieve an enrollment

```
GET /pix-biometric/enrollments/{id}
```

Returns the enrollment, refreshed from the institution, so a link revoked at the bank shows up as `REVOKED` here even before the webhook arrives. An id that does not exist for your client answers `404` with `PIX_BIOMETRIC_ENROLLMENT_NOT_FOUND`. See the [API reference](/reference/pix-por-biometria/pix-biometric-enrollment-retrieve).

## Payments made with an enrollment

```
GET /payments/intents?enrollmentId={id}
```

Lists the payment intents made with that link, with the usual pagination. Each one carries its `paymentRequest`, so you can tell which purchases the payer made with it. See the [API reference](/reference/payment-intent/payment-intents-list).

## Revoke an enrollment

```
POST /pix-biometric/enrollments/{id}/revoke
```

No body. The link is revoked at the institution and the enrollment is returned with `status: "REVOKED"` and `revokedBy: "CLIENT"`. You also get a `pix_biometric_enrollment/revoked` webhook. If the payer was still completing their first payment with that link, that payment intent fails.

Revoking cannot be undone: the payer has to link again to pay with biometrics. Revoking an enrollment that is already `REJECTED`, `REVOKED` or `EXPIRED` returns it unchanged, so retrying is safe. See the [API reference](/reference/pix-por-biometria/pix-biometric-enrollment-revoke).

The payer can also undo the link at any time in their bank's app. You learn about it through the `pix_biometric_enrollment/revoked` webhook, and the enrollment then shows `revokedBy: "BANK"`.

## Sharing balance

When the payment request was created with `linkedJourney: true`, the payer can also share their account balance and overdraft limit with you, in the same approval at the bank. It is optional for the payer and lasts as long as the link. Read the balance right before charging, to avoid a payment rejected for insufficient funds.

The enrollment carries the permission in `dataConsent`:

```json
{
  "status": "AUTHORISED",
  "rejectionReason": null,
  "updatedAt": "2026-10-28T13:05:00.000Z"
}
```

| `dataConsent.status` | Meaning |
|---|---|
| `AWAITING_AUTHORISATION` | The payer has not approved yet. |
| `AUTHORISED` | The balance can be read. |
| `REJECTED` | The payer or the institution rejected it. `rejectionReason` says why. |
| `REVOKED` | The permission was cancelled. |

`dataConsent` is `null` when the request did not ask for it, or the payer chose not to share.

### Read the balance

```
GET /pix-biometric/enrollments/{id}/balance
```

Reads the balance in real time from the institution. Amounts are in BRL. See the [API reference](/reference/pix-por-biometria/pix-biometric-enrollment-balance-retrieve).

```json
{
  "balance": 5.48,
  "overdraft": {
    "contracted": 1000,
    "used": 0,
    "available": 1000
  }
}
```

`overdraft` is the account's overdraft limit: how much was contracted, how much is in use and how much is still available. It is `null` when the institution does not share it.

| Status | `codeDescription` | Meaning |
|---|---|---|
| `200` | | Balance read. |
| `403` | `PIX_BIOMETRIC_DATA_CONSENT_NOT_AVAILABLE` | The link is not `AUTHORIZED`, or the payer did not share the balance, or the permission was cancelled. |
| `429` | `PIX_BIOMETRIC_RATE_LIMITED` | The institution's rate limit is reached, for example the account's monthly balance quota. |

<Callout variant="warning" title="Every call counts toward the institution's quota">
Each call reads the balance from the institution, with no cache. Open Finance limits balance reads per account per month (see [Operational Rate Limits](/docs/open-finance/rate-limits)). Read the balance right before a payment, not on a schedule.
</Callout>

### Stop sharing the balance

```
DELETE /pix-biometric/enrollments/{id}/data-consent
```

Cancels only the balance permission: the link stays `AUTHORIZED` and payments keep working. It returns the enrollment with `dataConsent.status` set to `REVOKED`. Cancelling a permission that already ended returns the enrollment unchanged, so retrying is safe. A link created without the permission answers `403` with `PIX_BIOMETRIC_DATA_CONSENT_NOT_AVAILABLE`. See the [API reference](/reference/pix-por-biometria/pix-biometric-enrollment-data-consent-delete).

Revoking the link also ends the balance permission. A permission cannot be added to an existing link: the payer links again from a payment request created with `linkedJourney: true`.

## Webhooks

| Event | When |
|---|---|
| `pix_biometric_enrollment/authorized` | The payer completed a link. |
| `pix_biometric_enrollment/revoked` | The link was revoked, at the bank or through the API. |
| `pix_biometric_enrollment/error` | The link was not completed: it was rejected, or ended before the payer finished it. |

```json title="pix_biometric_enrollment/authorized"
{
  "event": "pix_biometric_enrollment/authorized",
  "eventId": "8013240b-7c0a-409e-bf00-7fc586cc9196",
  "enrollmentId": "c91a5e2b-7d0f-4f3e-9a51-2f6b8e1d4c77",
  "customerId": "a2d4c1e8-5b7f-4c3a-9e2d-1f6b8c0d7e55",
  "paymentRequestId": "5b7e2c1d-3a4f-4e8b-9c6d-7f0a1b2c3d4e"
}
```

`paymentRequestId` is the payment request where the link was created, and `customerId` is `null` when it had no customer. Each event is sent once, when the enrollment enters that status. Retrieve the enrollment for the details.

A link that was `AUTHORIZED` and later reaches its `expiresAt` sends no event: check `expiresAt`, or retrieve the enrollment, to know it ended.