---
title: Authentication
description: OAuth2 JWT Bearer flow used by all IDCloud contracts. Learn how to obtain credentials, build the JWT assertion, and exchange it for an access token.
canonical: https://developer.unico.io/developers/start/authentication
locale: en
generated_by: markdown-export
---

- [/](/)
- [Start](/developers/start/)
- Authentication

**On this page# Authentication

All Unico API use **OAuth2 with JWT Bearer Grant Type** ([RFC 7523](https://datatracker.ietf.org/doc/html/rfc7523)). You generate a short-lived JWT assertion on your back-end, exchange it for a Bearer token, and use that token in every subsequent call.
Never do this on the client sideThe JWT assertion must be generated on your **back-end only**. Never expose your private key in front-end code, mobile apps, repositories, or logs.
### Obtaining credentials​

Before you can generate tokens, you need a **service account** provisioned by Unico. Contact Unico support and provide:

Service account name (max 12 characters)
Responsible person's name, email and phone (Brazil, US or Mexico numbers only)

You will receive:

Unique account name
Tenant ID
Base JWT payload
Private key file (`.pem` format)

One account per environmentKeep separate service accounts for **UAT** and **Production**.
### Building the JWT assertion​

The assertion is a JWT in compact JWS format: `{Base64url(Header)}.{Base64url(Payload)}.{Base64url(Signature)}`.
Header
```
{  "alg": "RS256",  "typ": "JWT"}
```

Payload
ClaimValueNotes`iss``<account_name>@<tenant_id>.iam.acesso.io`Provided with your credentials`aud``https://identityhomolog.acesso.io` (UAT) or `https://identity.acesso.io` (Production)Must match the token endpoint host`scope``*`Grants all permissions`iat`Unix timestamp (seconds)Time the JWT was issued`exp``iat` + max 3600Cannot exceed 1 hour from `iat`
```
{  "iss": "my-account@abc123.iam.acesso.io",  "aud": "https://identity.acesso.io",  "scope": "*",  "iat": 1738086000,  "exp": 1738089600}
```

Signature
Sign the header + payload using **RS256** (RSA + SHA-256) with the `.pem` private key provided by Unico.
Do not add extra claimsAny field not listed above (e.g. `sub`, `jti`, `nbf`) will cause a `1.2.22` error. Use only the claims shown.
### Requesting the token​

The token endpoint is the same for both contracts:
EnvironmentEndpoint**Production**`POST https://identity.acesso.io/oauth2/token`**UAT**`POST https://identityhomolog.acesso.io/oauth2/token`
Request
ParameterValue`Content-Type``application/x-www-form-urlencoded``grant_type``urn:ietf:params:oauth:grant-type:jwt-bearer``assertion`Your signed JWT
cURLNode.jsPython```
curl -X POST https://identity.acesso.io/oauth2/token \  -H "Content-Type: application/x-www-form-urlencoded" \  -d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \  -d "assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
```

```
import jwt from 'jsonwebtoken';import fs from 'fs';import qs from 'querystring';const privateKey = fs.readFileSync('./private-key.pem');const now = Math.floor(Date.now() / 1000);const assertion = jwt.sign(  {    iss: 'my-account@abc123.iam.acesso.io',    aud: 'https://identity.acesso.io',    scope: '*',    iat: now,    exp: now + 3600,  },  privateKey,  { algorithm: 'RS256' });const res = await fetch('https://identity.acesso.io/oauth2/token', {  method: 'POST',  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },  body: qs.stringify({    grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',    assertion,  }),});const { access_token, expires_in } = await res.json();
```

```
import timeimport jwt  # PyJWTimport requestswith open("private-key.pem", "rb") as f:    private_key = f.read()now = int(time.time())assertion = jwt.encode(    {        "iss": "my-account@abc123.iam.acesso.io",        "aud": "https://identity.acesso.io",        "scope": "*",        "iat": now,        "exp": now + 3600,    },    private_key,    algorithm="RS256",)response = requests.post(    "https://identity.acesso.io/oauth2/token",    data={        "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",        "assertion": assertion,    },)token_data = response.json()access_token = token_data["access_token"]
```

Response
```
{  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",  "token_type": "Bearer",  "expires_in": 3600}
```

FieldTypeDescription`access_token`stringJWT access token. Use in `Authorization: Bearer <token>` on all API calls.`expires_in`integerExpiration time in seconds. Example: `3600`.`token_type`stringAlways `Bearer`.
### Using the token​

Add the token to the `Authorization` header of every API request. The header is identical for both contracts — what differs is the **host and path** of the API you're calling:
ContractProduction hostUAT host**Unico API**`https://api.idcloud.unico.app``https://api.idcloud.uat.unico.app`
`Authorization` is the only auth header required:
```
curl -X POST https://api.idcloud.unico.app/client/v1/process \  -H "Authorization: Bearer $ACCESS_TOKEN" \  -H "Content-Type: application/json" \  -d '{ ... }'
```

### Token renewal​

Tokens expire after 3600 seconds. Implement proactive renewal in your back-end:

Track `expires_in` from the token response and store the expiry timestamp.
Request a new token **when 10 minutes or less remain** before expiration.
Never wait for a `401` in production to trigger a renewal.

### API reference​

The full OpenAPI specification for the token endpoint is available in [`authentication.yaml`](/assets/files/authentication-1d221f9a7230435e9c390ba98dac7321.yaml).
MethodPathDescription`POST``/oauth2/token`Exchange a signed JWT assertion for a Bearer access token
### Error codes​

CodeDescriptionAction`1.0.1`The ID provided in the formation of `iss` is incorrectVerify the `iss` field matches the tenant ID provided when the private key was generated`1.0.14`Application is not activeCheck with the project manager if the application being used is active`1.1.1`The `scope` parameter was not providedAdd `"scope": "*"` to your JWT payload`1.2.4`JWT assertion invalidThe JWT assertion is no longer valid. Two causes: (a) the current time is past `exp` (JWT truly expired — generate a fresh assertion for each token request); or (b) `exp` exceeds `iat + 3600` (lifetime too long — cap `exp` at `iat + 3600`).`1.2.5`JWT validation failedThe JWT cannot be validated. Verify the parameters and ensure it was signed with RS256 and the correct private key`1.2.6`Private key is no longer validThe private key used to sign the JWT is no longer acceptable. Request new credentials for the account`1.2.7`JWT already usedThe JWT is no longer acceptable as it has already been used. Generate a new assertion for each token request`1.2.11`Account is not activeThe account used is not active`1.2.14`Account lacks necessary permissionsThe account used does not have the necessary permissions`1.2.18`Account temporarily lockedThe account has been temporarily locked due to exceeding the number of invalid authentication attempts`1.2.19`Unauthorized user impersonationThe JWT contains a `sub` claim pointing to an account that is not authorized for impersonation. Remove the `sub` claim from the payload.`1.2.20`JWT decoding failedFailed to decode the JWT. Verify the token format and that it was signed with RS256.`1.2.21`Wrong private key / Authentication failedThe JWT signature could not be verified against any known key for this account. Check that you are using the correct `.pem` private key for this service account and environment.`1.2.22`Disallowed fields in payloadThe JWT contains extra payload fields that are not allowed. Remove any claims not listed in this guide (e.g. `sub`, `jti`, `nbf`). Note: if you included a `sub` claim and received `1.2.19` instead, that error takes precedence.`1.3.1`IP access restrictionYour IP is not in the allowlist for this account`1.3.2`Time-based access restrictionRequest is outside the permitted time window for this account
### What's next​

[Environments](/developers/start/environments) — sandbox vs production hosts
Unico API — Create Process — first call after authentication
[SSO / SAML](/developers/start/sso-saml) — authenticate your users into Unico's product portals with your own identity provider, instead of the server-to-server flow above
Last updated on Oct 8, 2026**