---
updatedAt: 2026-07-08T10:02:37.000Z
agentTools:
  projectIndex: https://doc.fintecture.com/llms.txt
---

# Request a PIS access token

A PIS access token authenticates every call you make to Fintecture's payment (PIS) endpoints. You obtain it with the OAuth 2.0 **client credentials** flow: exchange your application credentials for a short-lived Bearer token — no user interaction involved.

## How it works

1. [Create a Fintecture account](/docs/console-create-an-account)
2. [Register a client application](/docs/console-register-a-client-application) and get the credentials (i.e. the `app_id` and `app_secret`) associated to it
3. Encode your credentials using Base64 (`base64({app_id}:{app_secret})`) to build the `Authorization: Basic` header
4. [Request an access token](https://doc.fintecture.com/reference/createaccesstoken) with the `PIS` scope
5. Use the returned `access_token` as a `Bearer` token on every payment endpoint
6. Track `expires_in` and request a new token before it expires

> ❗️ **Keep your `app_secret` secret**
>
> Request tokens from your backend only. Never embed the `app_secret` (or a valid access token) in a browser, mobile app, or public repository.

## 1. Build the Authorization header

Concatenate your credentials with a colon and Base64-encode the result:

```shell
echo -n "your_app_id:your_app_secret" | base64
```

The header then looks like:

```http
Authorization: Basic eW91cl9hcHBfaWQ6eW91cl9hcHBfc2VjcmV0
```

## 2. Request the token

Call [`POST /oauth/accesstoken`](https://doc.fintecture.com/reference/createaccesstoken) with a form-encoded body (`grant_type`, `app_id`, `scope`):

```curl cURL
curl -X POST "https://api.sandbox.fintecture.com/oauth/accesstoken" \
  -H "Authorization: Basic $(echo -n "$APP_ID:$APP_SECRET" | base64 | tr -d '\n')" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "app_id=$APP_ID" \
  -d "scope=PIS"
```
```javascript Node.js
const APP_ID = process.env.FINTECTURE_APP_ID;
const APP_SECRET = process.env.FINTECTURE_APP_SECRET;

const basic = Buffer.from(`${APP_ID}:${APP_SECRET}`).toString('base64');

const response = await fetch('https://api.sandbox.fintecture.com/oauth/accesstoken', {
  method: 'POST',
  headers: {
    'Authorization': `Basic ${basic}`,
    'Content-Type': 'application/x-www-form-urlencoded',
  },
  body: new URLSearchParams({
    grant_type: 'client_credentials',
    app_id: APP_ID,
    scope: 'PIS',
  }),
});

const { access_token, expires_in } = await response.json();
```
```python Python
import base64
import os

import requests

app_id = os.environ["FINTECTURE_APP_ID"]
app_secret = os.environ["FINTECTURE_APP_SECRET"]

basic = base64.b64encode(f"{app_id}:{app_secret}".encode()).decode()

response = requests.post(
    "https://api.sandbox.fintecture.com/oauth/accesstoken",
    headers={"Authorization": f"Basic {basic}"},
    data={"grant_type": "client_credentials", "app_id": app_id, "scope": "PIS"},
)
response.raise_for_status()
token = response.json()["access_token"]
```

> 📘 **Sandbox vs production**
>
> The examples above target the sandbox. For production, use `https://api.fintecture.com` — and remember that each environment has its own `app_id` / `app_secret`: sandbox credentials return `401` in production, and vice versa.

## 3. Read the response

```json
{
  "token_type": "Bearer",
  "access_token": "eyJhbGciOiJub25lIn0.eyJleHAiOjE1MTQwODA0MjQsI",
  "expires_in": 599
}
```

| Field          | Description                         |
| :------------- | :---------------------------------- |
| `token_type`   | Always `Bearer`                     |
| `access_token` | The token to send on every PIS call |
| `expires_in`   | Remaining validity, in **seconds**  |

## 4. Use the token

Send the token in the `Authorization` header of every payment endpoint:

```http
Authorization: Bearer eyJhbGciOiJub25lIn0.eyJleHAiOjE1MTQwODA0MjQsI
```

> 📘 Most PIS endpoints also require a request **signature** on top of the Bearer token. See [HTTP Signature](/docs/api-http-signature).

## Token lifetime & renewal

PIS tokens are short-lived and have **no refresh token** — renewing simply means requesting a new token with the same call as above. In practice:

* Cache the token and reuse it for all calls until it expires — don't request a new token per API call.
* Track expiry from `expires_in` and renew slightly before (e.g. 60 seconds of safety margin).
* On a `401` response from a PIS endpoint, request a fresh token and retry once.

## Troubleshooting

| Status             | Most likely cause                                                                   | Fix                                                                                |
| :----------------- | :---------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------- |
| `400 bad_request`  | Missing/invalid `grant_type`, `app_id` or `scope`, or body not form-encoded         | Send `Content-Type: application/x-www-form-urlencoded` with the three fields above |
| `401 unauthorized` | Wrong `app_id:app_secret`, broken Base64, or credentials from the other environment | Re-encode the header; check you're using the credentials of the target environment |
| `403 forbidden`    | Application not allowed to use the `PIS` scope                                      | Check your application's products in the Console, or contact support               |

Every error response contains a `log_id` — include it when you contact support so we can trace the exact call.

<a href="/recipes/request-a-pis-access-token" target="_blank" class="thirdbutton">Recipe<i class="fa-solid fa-arrow-up-right-from-square" style={{ margin: "16px", marginRight: "-15px" }}></i></a>