Authentication Guide
This guide explains step by step how to obtain an access token to use the CorpX PIX API.
Overview
The CorpX API uses OAuth 2.0 with the Client Credentials flow for authentication. You will need your credentials (client_id and client_secret) to obtain a valid access token.
Prerequisites
Before you begin, make sure you have:
- Client ID - Your unique client identifier
- Client Secret - Secret key for authentication
- X-Tenant-Id - Your tenant identifier (e.g.,
tenant-suaempresa)
If you don't have credentials yet, contact our support team.
Environments
| Environment | Authentication URL | API URL | Notes |
|---|---|---|---|
| Sandbox (dev) | — | — | Temporarily unavailable. |
| Production | https://auth.api.corpx.com/oauth2/token | https://tenant.api.corpx.com |
Step 1: Request an Access Token
Request
curl -X POST "https://auth.api.corpx.com/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
grant_type | string | Yes | Always client_credentials |
client_id | string | Yes | Your client identifier |
client_secret | string | Yes | Your secret key |
scope | string | No | Space-separated subset of the credential's scopes. Omitting it returns a token with all of them |
Success Response (200 OK)
{
"access_token": "eyJraWQiOiJ0emwzZWVYWGx1eVlDWHFwQXdBTzJWYWJNQ0llMFMyMXVRWGV2Y281N2RRPSIsImFsZyI6IlJTMjU2In0...",
"expires_in": 300,
"token_type": "Bearer"
}
| Field | Description |
|---|---|
access_token | JWT token for authentication in API calls |
expires_in | Validity time in seconds |
token_type | Token type (always Bearer) |
expires_inThe TTL is not the same for every credential: the ones created in the
backoffice panel last 5 minutes (expires_in: 300), while older, manually
provisioned credentials still last an hour. Do not hardcode 3600 in your
client — read expires_in from the response and renew comfortably ahead of it.
Error Response (401 Unauthorized)
{
"error": "invalid_client",
"error_description": "Client authentication failed"
}
Step 2: Use the Token in Requests
With the obtained token, include it in the Authorization header of all API requests.
Example: Check Balance
curl -X GET "https://tenant.api.corpx.com/v1/accounts/{accountId}/balance" \
-H "Authorization: Bearer eyJraWQiOiJ0emwzZWVYWGx1eVlDWHFwQXdBTzJWYWJNQ0llMFMyMXVRWGV2Y281N2RRPSIsImFsZyI6IlJTMjU2In0..." \
-H "X-Tenant-Id: tenant-suaempresa" \
-H "Content-Type: application/json"
Required Headers
| Header | Description |
|---|---|
Authorization | Access token in Bearer {access_token} format |
X-Tenant-Id | Your tenant identifier |
Content-Type | application/json for requests with a body |
Step 3: Renew the Token
The token expires after the time indicated in expires_in. We recommend:
- Store the token in cache with the
expires_inthat came in the response - Renew before expiration — with a 5-minute TTL, renewing in the last minute is already tight
- Handle the 403 for expired tokens — the API answers
403 Forbidden, not401; when you get it, obtain a new token and retry the call
Automatic Renewal Example (Bash)
#!/bin/bash
# Variables
CLIENT_ID="your_client_id"
CLIENT_SECRET="your_client_secret"
AUTH_URL="https://auth.api.corpx.com/oauth2/token"
# Function to get token
get_token() {
response=$(curl -s -X POST "$AUTH_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET")
echo "$response" | jq -r '.access_token'
}
# Get token
TOKEN=$(get_token)
# Use the token
curl -X GET "https://tenant.api.corpx.com/v1/accounts/{accountId}/balance" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Tenant-Id: tenant-suaempresa"
Full Examples in Different Languages
Python
import requests
# Credentials
CLIENT_ID = "your_client_id"
CLIENT_SECRET = "your_client_secret"
TENANT_ID = "tenant-suaempresa"
# Get token
auth_response = requests.post(
"https://auth.api.corpx.com/oauth2/token",
data={
"grant_type": "client_credentials",
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET
}
)
token = auth_response.json()["access_token"]
# Use the token
headers = {
"Authorization": f"Bearer {token}",
"X-Tenant-Id": TENANT_ID,
"Content-Type": "application/json"
}
response = requests.get(
"https://tenant.api.corpx.com/v1/accounts/{accountId}/balance",
headers=headers
)
print(response.json())
Node.js
const axios = require('axios');
const CLIENT_ID = 'your_client_id';
const CLIENT_SECRET = 'your_client_secret';
const TENANT_ID = 'tenant-suaempresa';
async function getToken() {
const response = await axios.post(
'https://auth.api.corpx.com/oauth2/token',
new URLSearchParams({
grant_type: 'client_credentials',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET
}),
{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
);
return response.data.access_token;
}
async function getBalance(accountId) {
const token = await getToken();
const response = await axios.get(
`https://tenant.api.corpx.com/v1/accounts/${accountId}/balance`,
{
headers: {
'Authorization': `Bearer ${token}`,
'X-Tenant-Id': TENANT_ID,
'Content-Type': 'application/json'
}
}
);
return response.data;
}
Credential scopes
Every credential (client_id/client_secret) carries a set of scopes that
defines exactly what it can do. You pick the scopes when creating the credential
in the backoffice panel (API Credentials, available to users with the tenant
manager profile).
Scopes you grant yourself:
| Scope | Unlocks |
|---|---|
read | Account-wide queries: balance, statement, timeline, entries |
qrcode.manage | Static and dynamic QR code: create, cancel and check the payment of your own QR |
pix_keys.manage | PIX keys: list, create and delete |
webhooks.manage | Webhook subscriptions, deliveries and retries |
exports.create | Statement exports: create, track and download |
med.defend | MED: query, answer and attach evidence (does not decide the refund) |
Every scope already queries its own domain. That is what makes an
inflow-only credential possible: grant qrcode.manage alone and it issues QR
codes and sees whether they were paid without access to the balance and the
statement. Only check read when the credential genuinely needs the
account-wide queries.
Scopes that move money are issued by CorpX, never from the panel:
| Scope | Unlocks |
|---|---|
pix_out.create | PIX out in every variant (/pix/out/*), with the status of its own payments |
refund.create | Refund of a received PIX, with the status of its own refunds |
internal_transfer.create | Internal transfer, with the destination lookup |
ted.create | TED, with the status of its own transfers |
boleto_payment.create | Boleto preview, payment and status |
med.decide | Decide a MED refund |
Requesting one of these from the panel is refused with
403 scope_not_self_service — contact support and we issue the credential.
Per-account restriction
A credential can be restricted to specific accounts of your tenant. That is
how you give a subsystem a credential that only issues QR codes for one account.
Calls to accounts outside the set answer 403 forbidden — the generic
permission error, not insufficient_scope: the scope is there, the account is
not. The message deliberately does not distinguish a nonexistent account from
one outside the set.
Combining both dimensions: qrcode.manage plus a single account yields a
credential that charges by QR code on that account and sees nothing else —
neither its balance nor the tenant's other accounts.
When a scope is missing
If the credential lacks the scope for the route, the API answers 403 Forbidden naming what is missing:
{ "errorCode": "insufficient_scope", "message": "esta credencial não tem escopo para esta operação; é necessário o escopo qrcode.manage" }
Credentials issued before granular scopes keep working unchanged: they retain
the coarse pair (api2/read api2/write) and full access.
Access suspended by a pendency
When there is an open pendency with CorpX, a tenant's access can be suspended
temporarily. Suspension does not invalidate your credential: the same
client_id/client_secret keeps issuing tokens normally, and the refusal comes
on the API call.
| State | What keeps working | Response on other calls |
|---|---|---|
| Suspended | Reads (GET) — balance, statement, operation status | 403 tenant_suspended on writes |
| Disabled | Nothing | 403 tenant_disabled on every route |
{ "errorCode": "tenant_suspended", "message": "há uma pendência em aberto: operações de escrita estão suspensas até a regularização, consultas seguem disponíveis" }
Two things worth knowing:
- Money does not stop. Incoming PIX is still credited and you still receive the webhooks for those events. What suspension blocks is starting new operations.
- Reactivation is immediate. Once the pendency is cleared, access returns on the next call — no new credential and no new token needed.
The reason for the pendency is not in the error body: it shows up in the backoffice panel, for your team's operators.
Common Errors
| Error | Cause | Solution |
|---|---|---|
invalid_client | Incorrect Client ID or Secret | Check your credentials |
invalid_grant | Invalid grant type | Use client_credentials |
401 Unauthorized | Missing Authorization header | Send Authorization: Bearer <token> |
403 Forbidden (invalid/expired token) | Expired token, invalid token, or bad signature | Obtain a new token. Note that expiry is 403, not 401 |
403 insufficient_scope | Credential without the route's scope | Create a credential with the scope named in the message |
403 tenant_suspended | Open pendency: writes are suspended | Clear it with CorpX support; reads keep working |
403 tenant_disabled | Tenant disabled | Contact CorpX support to restore access |
403 forbidden | Valid token, but no permission on that tenant or account | Check X-Tenant-Id and whether the credential is restricted to other accounts |
Best Practices
- Never expose the
client_secretin client-side code (frontend) - Use environment variables to store credentials
- Implement token caching to avoid unnecessary requests
- Monitor expiration and renew tokens proactively
- Use HTTPS for all communications
Next Steps
Now that you know how to authenticate, explore the other guides:
- Dynamic QR Code Guide - Generate PIX charges
- Cash Out Guide - Make PIX transfers
- Refund Guide - Request reversals