Authentication
There are two ways to authenticate against the Open API (/v1/open-api/**). Both end with the
same thing — a token sent in the X-Open-Api-Token header on every request. What differs
is who obtains it, and for whom.
| PAT — personal access token | OAuth 2.0 — authorization code | |
|---|---|---|
| Built for | One company integrating with itself | An app used by many companies |
| Who obtains it | A company employee, from the UYSOT UI | Each company grants your app consent |
| Token prefix | uysot_pat_… | uysot_at_… (access), uysotrt_… (refresh) |
| Lifetime | Chosen when issued, up to 365 days | Access — 1 hour, refresh — 30 days |
| Renewal | None — issue a new one before it expires | grant_type=refresh_token |
| Permissions | Chosen when the application is set up | Whatever the company approved on the consent screen |
:::info Which one do I need? Writing a script or integration for your own company — use a PAT, described below. Distributing your app to other companies — use OAuth 2.0. :::
On this page:
- Using the token
- Grant format
- PAT — personal access tokens
- OAuth 2.0 in one paragraph
- Inspecting a token
- Errors
- Security recommendations
- FAQ
1. Using the token
Whatever its kind — PAT or OAuth access token — the header is the same:
GET https://api.service.app.uysot.uz/v1/open-api/contract/1234
X-Open-Api-Token: uysot_pat_436767a14e657166b7f455ee8b8dbe5f...
- An unknown, revoked or expired token returns
401, and so does a token whose company connection was revoked. - A valid token without the required grant returns
403. - The budget is 60 requests per minute per token; exceeding it returns
429(Rate limits). - If the company's UYSOT account is blocked, requests return
401even with a healthy token.
:::note Every 401 looks identical
The 401 body is a fixed English message with no error code, and it is the same whether the
header was missing, the token was revoked, it expired, the connection was disconnected or the
company is blocked:
{ "message": "Invalid or expired Open API token", "accept": false, "errors": null, "data": null }
GET /v1/open-api/token/info narrows it down: a 200 there means the token, the connection and
the company are all healthy, so the failing call had a different problem; a 401 there confirms
the credential itself is dead and needs refreshing or reissuing. Details in
Errors § 1.1.
:::
:::warning Authorization: Bearer does not work
OAuth access tokens are also sent in X-Open-Api-Token when calling API endpoints.
Authorization: Bearer … belongs to UYSOT's own product API — the Open API ignores it.
:::
2. Grant format
A grant is a (permission, scope) pair. In an OAuth scope string they are written as
PERMISSION:SCOPE, separated by spaces:
PERMISSION_OPEN_API_LEAD:READ PERMISSION_OPEN_API_CONTRACT:READ
Permissions:
| Value | Domain |
|---|---|
PERMISSION_OPEN_API_LEAD | Leads |
PERMISSION_OPEN_API_LEAD_NOTE | Lead notes |
PERMISSION_OPEN_API_LEAD_TASK | Lead tasks |
PERMISSION_OPEN_API_CONTRACT | Contracts |
PERMISSION_OPEN_API_CONTRACT_PAYMENT | Contract payments |
PERMISSION_OPEN_API_CALL | Calls |
Scopes: READ, SAVE (create/update), DELETE.
Each endpoint's reference page states the grant it requires.
:::note Permissions are attached to the connection, not to the token The token itself carries no permissions. They belong to the (application ↔ company) connection, so when a company widens or narrows what your integration may do, your existing token reflects the change immediately — no re-issue needed. :::
3. PAT — personal access tokens
A PAT is for an integration a company builds for its own data: a sync script, a report job, an internal service.
3.1 How to get one
- In UYSOT, an employee with Open API configuration rights creates an application for the integration and selects the grants it needs.
- They issue a token for it and choose an expiry date.
- The token is displayed in clear text exactly once:
uysot_pat_436767a14e657166b7f455ee8b8dbe5ff1d54bafd24259c8600a02aa8ccd8420
:::danger The token is shown once It is never displayed again. Copy it straight into a secret manager or an environment variable — if you lose it, issue a new one. :::
Hand the value to your integration through configuration; it does not belong in source control.
3.2 Rules to plan around
- Maximum lifetime: 365 days.
- Rotation: issuing a new PAT for an application revokes the previously active one — an application has exactly one working PAT at a time. Plan the swap together with a deploy.
- Shortly before a PAT expires the API starts flagging it in every response (see
5.1), so monitoring can pick it up and you can rotate ahead of time.
expiresInDaysintoken/infois the value to watch. - A PAT can be revoked at any time from the UYSOT UI; requests then return
401.
4. OAuth 2.0 in one paragraph
A third-party app registers with UYSOT, receives a client_id and client_secret, and sends each
company's user to a consent page. After approval your backend exchanges the returned authorization
code for an access token (1 hour) and a refresh token (30 days), and calls the API with the
access token in X-Open-Api-Token.
→ Full walkthrough: OAuth 2.0 — authorization code flow
5. Inspecting a token
To find out who a token belongs to, when it expires and what it may do — no grant required:
GET https://api.service.app.uysot.uz/v1/open-api/token/info
X-Open-Api-Token: uysot_pat_…
{
"accept": true,
"message": null,
"errors": null,
"data": {
"application": { "id": 12, "name": "Acme CRM sync", "type": "PRIVATE" },
"company": { "id": 34, "name": "Uysot Demo" },
"kind": "PAT",
"expiresAt": 1798070399,
"expiresInDays": 118,
"permissions": [
{ "permission": "PERMISSION_OPEN_API_CONTRACT", "scope": "READ" },
{ "permission": "PERMISSION_OPEN_API_LEAD", "scope": "READ" }
]
}
}
kind—PATorACCESS(an OAuth access token).expiresAt— epoch seconds;nullmeans no expiry date (only revocation ends it).- The token value is never echoed back.
This makes a convenient deployment-time health check ("is the integration configured correctly?").
5.1 The expiry warning
Once a token is within 7 days of expiring, the platform starts nudging you through the
responses themselves: on every successful call, the envelope's message is replaced with an
English warning instead of "Success".
{
"data": { },
"message": "Token will expire in 5 days. Please renew it.",
"accept": true,
"errors": []
}
- Wording:
"Token will expire in N days. Please renew it.","Token will expire in 1 day. …", or"Token expires today. Please renew it."on the last day. - It is not an error:
acceptstaystrue, the HTTP status stays200, anddatais untouched. - It appears only on tokens that have an expiry, and only on successful responses.
- There is no header for this — it lives in the body, so a client that logs
messagewill see it, and a client that comparesmessage == "Success"will break on it.
Treat it as a free monitoring signal: if your integration logs a non-"Success" message on a
200, that is your cue to rotate the credential (a PAT) or check the refresh flow (OAuth).
6. Errors
The full list of status codes and message codes is in Errors; OAuth-flow errors are in OAuth § errors.
| Status | Cause |
|---|---|
401 | Missing header, unknown/expired/revoked token, revoked connection, or a blocked company |
403 | Valid token, but the required (permission, scope) is missing |
429 | More than 60 requests per minute on this token |
7. Security recommendations
- Keep PATs and
client_secreton the backend. Never ship them in a frontend bundle, a mobile app or a repository. - Never log tokens. If you must, log the last 4 characters only.
- HTTPS only.
- If a credential leaks, revoke it and issue a new one — a leaked token grants everything its connection allows until it is revoked.
- Least privilege: request only the grants you actually use.
- Rotate PATs on a schedule you control, not on the day they expire.
8. FAQ
Can I send the token as Authorization: Bearer?
No. The Open API only reads X-Open-Api-Token (section 1).
Can a PAT be refreshed? No, PATs have no refresh flow. Issue a new one before the old expires — note that issuing revokes the previous PAT.
I issued a new PAT and the old token stopped working. That is by design: an application has one active PAT at a time (3.2).
Permissions changed — do I need a new token?
No. They live on the connection, so the change takes effect immediately. Check the current state
with GET /v1/open-api/token/info.
How do I tell 401 and 403 apart?
401 is a token problem (missing, expired, revoked, blocked company); 403 means the token is
fine but lacks a grant. On 403, check permissions in token/info.
One company uses two of my integrations — do the tokens collide? No. Each (application, company) pair is its own connection with its own tokens.
How is the rate limit counted? Per token — 60 requests per minute. Several instances sharing one token share the budget.