Parsware Platform
Get an access token
A registered application doesn't send its secret with every call. It trades it, once an hour, for an access token, and sends that. Here is the one request that gets one, what is inside it, how to use it, and what each refusal means.

The front-desk dashboard now has a client id and a secret. It doesn't send those with every call. It trades them for an access token, a short-lived pass that names the application, its organisation, its role and its scopes, and sends that instead. This is the developer's half of the story: the one request, and what comes back.
The client id is the one in the registration's row:

The request
One POST to Parsware's token endpoint, https://identity.<your domain>/connect/token, as a form:
POST /connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id=app-7e330620b9604bdfacd35a4407d7465a
&client_secret=<the secret>
&scope=reporting.read
grant_type=client_credentials: an application signing in as itself, with no person involved.scope: the scopes this token should carry, separated by spaces. Always ask by name. A token requested withoutscopecomes back holding none, and every call made with it is refused.
The answer:
{
"access_token": "eyJhbGciOiJSUzI1NiIs…",
"token_type": "Bearer",
"expires_in": 3600
}
What's inside it
The token is a signed JWT. You don't need to read it to use it, but it's worth knowing what it says. Decoded, ours carried:
{
"iss": "http://localhost:5001/",
"aud": "parsware-api",
"sub": "app-7e330620b9604bdfacd35a4407d7465a",
"client_id": "app-7e330620b9604bdfacd35a4407d7465a",
"tenant_id": "5c3f953a-164e-4329-9aae-b5acf8003dba",
"role": "nw_visitorreports",
"scope": "reporting.read",
"iat": 1791968760,
"exp": 1791972360
}
subis the application itself. That's how every service knows this caller is an application and not a person, and why it's held to its scopes.tenant_idis your organisation, taken from the registration, never from the request.roleis the role the registration holds. It decides which records the token reaches.expis one hour afteriat. There's no refresh token: when this one expires, the application asks for a new one the same way.
Each service checks the signature itself, against keys Identity publishes, without calling Identity back. That's why a token is quick to use, and also why revoking a registration doesn't stop a token that's already been issued (see Register an external app).
Using it
Send it in the Authorization header of every call:
GET /environments/e997c7a6-…-1b7238afe386/reporting/entities
Authorization: Bearer eyJhbGciOiJSUzI1NiIs…
200 OK
[{ "logicalName": "nw_visitor", "displayName": "Visitor", "displayCollectionName": "Visitors" }]
The environment is in the address because the token names your organisation, not one of its environments. One table came back because the role reads one table.
Get a token once, keep it, and use it until it's close to expiring. Asking for a fresh token before every call works, but it's a round trip each time for nothing.
When it says no
Every refusal we met while writing this, and what each means:
| Answer | What happened |
|---|---|
401 · invalid_client |
The client id or secret is wrong, or the registration has been revoked. |
400 · invalid_request, not allowed to use the specified scope |
You asked for a scope the registration wasn't given. Ask for fewer, or have it added to the registration. |
400 · invalid_scope |
The scope doesn't exist. Check the spelling. |
401 with no body |
The call had no token, or a token that has expired or been tampered with. Get a new one. |
403 · platform.scope_required |
The token is valid but doesn't carry the scope this call needs. The message names it. |
403 · modelapps.record.forbidden |
The scope is right but the role may not touch that table. |
The last two are the useful distinction. A scope refusal is fixed on the registration; a record refusal is fixed on the role.
Good to know
- Keep the secret on the server. A client-credentials token is for software that can keep a secret: a service, a scheduled job, a dashboard's back end. Never put the secret in a browser page or a mobile app, where anyone can read it.
- Ask for the scopes you need for this job. A registration with four scopes can still ask for a token holding one. A nightly export that only reads can request a read-only token, even if the same registration can write.
- The clock matters. A machine whose clock has drifted can think a fresh token has already expired. Keep it synchronised.
Try it
Get a token for one of your registrations with curl or any HTTP tool, paste it into a JWT
decoder and read the claims. Then ask for a scope the registration doesn't have and read the
refusal.