Exchange your API application's client ID and secret for a bearer token, then use it to call any PerspioTalk API.
PerspioTalk APIs use the OAuth 2.0 client credentials grant. Your integration sends its client_id and client_secret to the token endpoint and gets back a short-lived access token. There is no interactive login and no refresh token: when a token expires, you request a new one the same way.
Before you startYou need an API application in your Perspio tenant. Create Application gives you the four values used on this page:
- Client ID (
client_id) and client secret (client_secret), to request a token- Tenant ID (
tid) and subscription key (Ocp-Apim-Subscription-Key), to call the APIs with it
Request a token
Send a POST to the token endpoint for the region your tenant is hosted in. It's the same host you use for every other PerspioTalk API.
| Region | Token endpoint |
|---|---|
| AU | https://talk.perspio.io/auth/v3/token |
| US | https://us-talk.perspio.io/auth/v3/token |
Send the body form-encoded (Content-Type: application/x-www-form-urlencoded), not as JSON. No other headers are needed: the token request doesn't take your tenant ID or subscription key.
Body parameters
| Parameter | Value |
|---|---|
grant_type | Always client_credentials |
client_id | The client ID of your API application |
client_secret | The client secret of your API application |
POST /auth/v3/token HTTP/1.1
Host: talk.perspio.io
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id={client_id}&client_secret={client_secret}curl --request POST 'https://talk.perspio.io/auth/v3/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_id={client_id}' \
--data-urlencode 'client_secret={client_secret}'$body = @{
grant_type = 'client_credentials'
client_id = '{client_id}'
client_secret = '{client_secret}'
}
$token = Invoke-RestMethod -Method Post -Uri 'https://talk.perspio.io/auth/v3/token' `
-ContentType 'application/x-www-form-urlencoded' -Body $body
$token.access_tokenimport requests
response = requests.post(
"https://talk.perspio.io/auth/v3/token",
data={
"grant_type": "client_credentials",
"client_id": "{client_id}",
"client_secret": "{client_secret}",
},
timeout=30,
)
response.raise_for_status()
access_token = response.json()["access_token"]const response = await fetch("https://talk.perspio.io/auth/v3/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: "{client_id}",
client_secret: "{client_secret}",
}),
});
if (!response.ok) throw new Error(`Token request failed: ${response.status}`);
const { access_token } = await response.json();using var http = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://talk.perspio.io/auth/v3/token")
{
Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "client_credentials",
["client_id"] = "{client_id}",
["client_secret"] = "{client_secret}",
}),
};
using var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadFromJsonAsync<JsonElement>();
var accessToken = json.GetProperty("access_token").GetString();Token response
A successful request returns 200 OK with the token in JSON.
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"expires_in": 3600,
"token_type": "bearer"
}| Property | Description |
|---|---|
access_token | The token to send on every API call, as Authorization: Bearer {access_token} |
expires_in | Seconds until the token expires. Tokens last one hour (3600) |
token_type | Always bearer |
Call an API with the token
Every PerspioTalk request needs three headers. The access token proves who is calling, the tenant ID says which tenant, and the subscription key identifies your tenant's API subscription.
| Header | Value |
|---|---|
Authorization | Bearer {access_token} |
tid | Your tenant ID |
Ocp-Apim-Subscription-Key | Your subscription key |
The tenant ID must be the tenant your API application belongs to. A token used with any other tenant ID returns 401 Unauthorized.
For example, Get Assets lists the assets in your tenant:
curl --request GET 'https://talk.perspio.io/assets/v3/' \
--header 'Authorization: Bearer {access_token}' \
--header 'tid: {tid}' \
--header 'Ocp-Apim-Subscription-Key: {subscription_key}' \
--header 'Accept: application/json'$headers = @{
Authorization = "Bearer $($token.access_token)"
tid = '{tid}'
'Ocp-Apim-Subscription-Key' = '{subscription_key}'
}
Invoke-RestMethod -Uri 'https://talk.perspio.io/assets/v3/' -Headers $headers
Try it in these docsEvery endpoint page has a Credentials panel. Enter your access token, tenant ID and subscription key there, on a page such as Get Assets, to send live requests from your browser.
When the token expires
Tokens last one hour.
- Reuse the token. Cache it and send it on every call until it's close to expiry. Don't request a new token per API call.
- Renew early. Request a new token a few minutes before the hour is up, so in-flight calls don't fail.
- Handle 401. An expired or invalid token returns
401 Unauthorized. Request a new token and retry the call once.
There are no refresh tokens with the client credentials grant. A new token request with the same client_id and client_secret is how you renew.
Errors
Token errors use the standard OAuth 2.0 format: an error code, usually with an error_description.
{
"error": "invalid_client",
"error_description": "Application not found."
}| Status | error | Meaning |
|---|---|---|
400 | invalid_request | client_id or client_secret is missing |
400 | unsupported_grant_type | grant_type isn't client_credentials, or the body was sent as JSON instead of form-encoded |
401 | invalid_client | The client ID or client secret is wrong |
429 | Too many requests. Wait and retry with backoff | |
5xx | The sign-in service is briefly unavailable. Retry with backoff |
Keep these values server-sideYour client secret and subscription key grant access to your Perspio tenant. Never put them in browser or mobile app code, and never commit them to source control. If a secret is exposed, generate a new one from the API application in Perspio.

