Authentication
The Patholytix API uses OAuth 2.0 Client Credentials flow. This is a machine-to-machine authentication pattern — your integration authenticates as itself, not as a user.
How it works
Your Application
│
│ ① POST /oauth/token (client_id + client_secret)
▼
Authorization Server ──► Issues signed JWT (TTL: 5–15 minutes, default 10 min)
│
│ ② API Request + Authorization: Bearer <JWT>
▼
Patholytix API ──► Validates token, checks scopes, returns response
Requesting credentials
Contact your Deciphex account representative to request a set of integration credentials. Your representative will provision a client and share your credentials securely via Bitwarden — you will receive a Bitwarden Send link to retrieve them.
Your credentials consist of:
client_id— your client identifierclient_secret— your client secret (treat as a password — never expose in client-side code, logs, or version control)
Your client will be configured with:
- An organisation that determines which Patholytix instance you have access to
- One or more sites within that organisation that your client can read and write data for
- One or more scopes that determine which API operations your client can perform
Scopes
Each endpoint requires a specific scope. Your JWT token contains the scopes assigned to your client — calling an endpoint that requires a scope you do not have results in 403 Forbidden.
| Scope | Description |
|---|---|
Study:Read | Read-only access to studies, subjects, organs, codes, and findings |
Study:Write | Create and update studies, subjects, organs, and codes |
Study:Delete | Delete studies, subjects, and organs |
Finding:Read | Read pathology findings |
Your client will be provisioned with the scopes appropriate for your integration use case. Contact your Deciphex account representative if you need additional scopes.
Getting a token
Exchange your credentials for a JWT access token:
curl -X POST https://api.account.dev2.patholytix.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"
Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600
}
| Field | Description |
|---|---|
access_token | The JWT to include on API requests |
token_type | Always Bearer |
expires_in | Seconds until the token expires (configured per client: 5–15 minutes, default 10 minutes) |
Using the token
Include the access token in the Authorization header on every API request:
curl https://api.dev2.patholytix.com/api/v1/studies \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Token expiry and refresh
Tokens do not support refresh. When a token expires, request a new one using the same POST /oauth/token request. Tokens are valid for between 5 and 15 minutes — the TTL is set per client at registration time (default 10 minutes).
Best practice: Request a new token proactively before the current one expires. Check the expires_in field and schedule a refresh ~60 seconds before expiry.
Python example
import requests
import time
TOKEN_URL = "https://api.account.dev2.patholytix.com/oauth2/token"
API_BASE = "https://api.dev2.patholytix.com/api"
def get_token(client_id: str, client_secret: str) -> dict:
resp = requests.post(TOKEN_URL, data={
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret,
})
resp.raise_for_status()
return resp.json()
token_response = get_token("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET")
access_token = token_response["access_token"]
headers = {"Authorization": f"Bearer {access_token}"}
resp = requests.get(f"{API_BASE}/v1/studies", headers=headers)
print(resp.json())
Error responses
| Status | Meaning |
|---|---|
401 Unauthorized | Token is missing, malformed, expired, or has an invalid signature |
403 Forbidden | Token is valid but lacks the required scope for the requested operation |
See Error Codes for the full error response format.