# Authentication (/en/docs/conta-digital/authentication)

<QuickLinks>
  <QuickLink href="/docs/conta-digital/endpoints/authentication/post_oauth_token" title="Get access token" method="POST" path="/oauth/token" />

  <QuickLink href="/docs/conta-digital/security" title="Security" />

  <QuickLink href="/docs/conta-digital/error-codes" title="Error codes" />
</QuickLinks>

The credential is a `client_id` and `client_secret` pair, created by the account holder in the dashboard with the operation PIN. It operates a single account and only the routes of the scopes it was given, and it can have a list of allowed IPs.

<Mermaid
  chart="`
flowchart LR
  C[&#x22;client_id + client_secret&#x22;] -->|&#x22;POST /oauth/token&#x22;| T[&#x22;15-minute token&#x22;]
  T -->|&#x22;Authorization: Bearer&#x22;| API[&#x22;/transactions routes&#x22;]
  C -.->|&#x22;Authorization: Basic&#x22;| API

  click T &#x22;/docs/conta-digital/endpoints/authentication/post_oauth_token&#x22; &#x22;Get access token&#x22;

  style T fill:#14ce71,stroke:#0eb464,color:#ffffff
`"
/>

## Get the token [#get-the-token]

Send the credential in `Authorization: Basic` to [`POST /oauth/token`](/docs/conta-digital/endpoints/authentication/post_oauth_token):

```http
POST /api/v1/oauth/token
Authorization: Basic base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
```

```json
{
  "access_token": "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..mQ3Zy1hbVhpbXBsZQ.ZXhlbXBsbw.c2lnbmF0dXJl",
  "token_type": "Bearer",
  "expires_in": 900,
  "scope": "PAYMENT_WRITE PAYMENT_READ STATEMENT_READ"
}
```

* The token is valid for 15 minutes (`expires_in: 900`). Once it expires, the API responds `401` with `TOKEN_INVALID`: request another one from the same route.
* `scope` lists the credential's scopes, separated by spaces.
* `client_id` and `client_secret` can also go in the body, as `application/x-www-form-urlencoded` or JSON.
* The route limits exchanges per `client_id` and IP. Above the limit, it responds `429` with `Retry-After`.

Examples in curl and Node.js are in [Getting started](/docs/conta-digital/getting-started#exchange-the-credential-for-a-token).

## Other ways to authenticate [#other-ways-to-authenticate]

The API also accepts the credential directly on each call. The three ways give access to the same account, with the same scopes.

| Way                 | Header                                                 | The secret travels                                                            |
| ------------------- | ------------------------------------------------------ | ----------------------------------------------------------------------------- |
| Access token        | `Authorization: Bearer <access_token>`                 | Only in the exchange for the token.                                           |
| Credential in Basic | `Authorization: Basic base64(client_id:client_secret)` | On every call.                                                                |
| Credential token    | `Authorization: Bearer pzu_<prefix>_<secret>`          | On every call. The dashboard shows this token when the credential is created. |

The dashboard login does not work on the API routes, and the credential does not work on the dashboard.

## Scopes [#scopes]

Each route requires a scope, and no scope includes another: `PAYMENT_WRITE` does not grant reading, and reading does not grant writing.

| Scope                    | Allows                                                                                 |
| ------------------------ | -------------------------------------------------------------------------------------- |
| `PAYMENT_WRITE`          | Create a charge.                                                                       |
| `PAYMENT_READ`           | Get and list charges and download the receipt.                                         |
| `REFUND`                 | Refund a charge and return a deposit.                                                  |
| `WITHDRAW`               | Withdraw to a Pix key and pay a Pix copy-and-paste code.                               |
| `WITHDRAW_READ`          | Get and list withdrawals and download the receipt.                                     |
| `INTERNAL_TRANSFER`      | Transfer to another PayZu account.                                                     |
| `INTERNAL_TRANSFER_READ` | Get and list transfers, sent and received, and download the receipt.                   |
| `DEPOSIT_READ`           | Get a Pix received without a charge and download the receipt.                          |
| `STATEMENT_READ`         | Get balance, statement, limits and metrics.                                            |
| `PIX_KEY_READ`           | List the account's Pix keys.                                                           |
| `PIX_KEY_WRITE`          | Create and delete a Pix key and set the default key.                                   |
| `PIX_DICT_READ`          | Decode a Pix copy-and-paste code and look up the recipient.                            |
| `INFRACTION_READ`        | Get MED disputes.                                                                      |
| `WEBHOOK_READ`           | List webhook endpoints and see whether the account has a callback secret.              |
| `WEBHOOK_WRITE`          | Register, update and delete webhook endpoints and issue or rotate the callback secret. |

`WITHDRAW`, `INTERNAL_TRANSFER`, `REFUND`, `PIX_KEY_WRITE` and `WEBHOOK_WRITE` are never selected by default in a new credential.

## IP list [#ip-list]

When the credential's IP list is filled in on the dashboard, a call from another IP receives `403` with `TOKEN_IP_NOT_ALLOWED`. The rule also applies to `POST /oauth/token` and to using the token: a token obtained from an allowed IP is refused if it comes from another one. With an empty list, any IP is accepted.

## Rotation and revocation [#rotation-and-revocation]

* The `client_secret` appears once, at creation. No route returns it afterwards.
* Rotating creates a new credential, with another `client_id`, another `client_secret` and the same scopes. The previous one stays valid for **24 hours**. The IP list does not carry over to the new one.
* Revoking invalidates the credential and the tokens it issued right away.
* Creating, rotating and revoking are done in the dashboard. The API has no route for them.

## Credential rejections [#credential-rejections]

They apply to every API route:

| Status | `code`                      | When                                                                                                                                           |
| ------ | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| 401    | `TOKEN_INVALID`             | Wrong, nonexistent or revoked credential, or expired or tampered token. Request a new token; if the rejection continues, check the credential. |
| 401    | `TOKEN_EXPIRED`             | The credential had an expiration date, and it has passed.                                                                                      |
| 401    | `JWT_INVALID_AUTH_FORMAT`   | The `Authorization` header is missing, or the scheme is neither `Bearer` nor `Basic`.                                                          |
| 401    | `TOKEN_INVALID_AUTH_FORMAT` | The `Basic` value does not decode to `client_id:client_secret`.                                                                                |
| 403    | `TOKEN_MISSING_SCOPE`       | The credential does not have the route's scope. `details.scope` says which one is missing.                                                     |
| 403    | `TOKEN_IP_NOT_ALLOWED`      | The call came from an IP outside the credential's list.                                                                                        |
| 403    | `TOKEN_HOLDER_BLOCKED`      | The account holder is blocked. The credential works again when the block is lifted.                                                            |

```json
{
  "message": "Esta credencial não tem permissão para esta operação.",
  "code": "TOKEN_MISSING_SCOPE",
  "details": { "scope": "WITHDRAW" }
}
```

On `POST /oauth/token`, a missing credential or a `Basic` value that does not decode responds `401` with `TOKEN_INVALID`. The route has two more rejections:

| Status | `code`                         | When                                                                                |
| ------ | ------------------------------ | ----------------------------------------------------------------------------------- |
| 400    | `TOKEN_UNSUPPORTED_GRANT_TYPE` | `grant_type` is missing or different from `client_credentials`.                     |
| 429    | `AUTH_TOO_MANY_REQUESTS`       | The exchange limit per `client_id` and IP was exceeded. Wait for the `Retry-After`. |