---
source: https://developers.qashio.com/v2/authentication/
title: Authentication
description: Obtain, use, refresh and validate a Qashio OAuth 2.0 access token.
---

Every Qashio API 2.0 request is authenticated with an OAuth 2.0 **Bearer access token**. The token is
bound to a single company, so you never send a `companyId` or an `x-api-key` header: the token is the
company context.

```http
Authorization: Bearer <access_token>
```

> Access to the API is enabled by Qashio. If you do not yet have a Client ID and Client Secret,
> contact your Qashio Customer Success Manager.

## 1. Get an authorization code

Send the user to Qashio's authorization screen. After they approve, Qashio redirects back to your
`redirect_uri` with a short-lived `code` in the query string.

```http
https://app.qashio.com/oauth2/authorize
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https://app.example.com/callback
  &scope=wallet:read cards:read cards:write transactions:read
  &state=opaque-csrf-value
```

Validate `state` on the way back before you use the code.

## 2. Exchange the code for tokens

Reference: [Exchange code for tokens](/v2/reference/post-oauth2-token/).

```shell
curl -X POST 'https://api.qashio.com/oauth2/token' \
  -H 'Content-Type: application/json' \
  -d '{
    "grant_type": "authorization_code",
    "code": "AUTHORIZATION_CODE",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET",
    "redirect_uri": "https://app.example.com/callback"
  }'
```

```json
{
  "access_token": "<JWT_TOKEN>",
  "token_type": "Bearer",
  "refresh_token": "rt_8f3a1c9e2b7d4a6c...",
  "scope": "wallet:read cards:read cards:write transactions:read"
}
```

Authorization codes are single-use and expire quickly. Exchange one immediately.

## 3. Call the API

```shell
curl -X GET 'https://api.qashio.com/public/v1/erp/company/summary' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer <access_token>'
```

## 4. Refresh before expiry

When the access token expires, exchange the refresh token for a new pair. Refresh tokens are rotated: store the new one and discard the old.

Reference: [Refresh access token](/v2/reference/post-oauth2-token-refresh-token/).

```shell
curl -X POST 'https://api.qashio.com/oauth2/token' \
  -H 'Content-Type: application/json' \
  -d '{
    "grant_type": "refresh_token",
    "refresh_token": "rt_8f3a1c9e2b7d4a6c...",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET"
  }'
```

## 5. Validate a token

`GET /oauth2/validate` tells you whether a token is still good without spending a business call.

```shell
curl -X GET 'https://api.qashio.com/oauth2/validate' \
  -H 'Authorization: Bearer <access_token>'
```

## Handling 401 and 403

| Status | What it means | What to do |
| --- | --- | --- |
| `401` | The token is missing, malformed or expired | Refresh once, then retry the request |
| `403` | The token is valid but the user may not perform this action | Do not retry: the grant needs wider scope |

## Storing credentials

- Keep the Client Secret and refresh tokens server-side. Never ship them to a browser or mobile app.
- Scope tokens to the least you need: request `cards:write` only for the integrations that issue cards.
- Rotate the Client Secret if it is ever exposed, and treat every token minted with it as compromised.
