Skip to main content

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 identifier
  • client_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.

ScopeDescription
Study:ReadRead-only access to studies, subjects, organs, codes, and findings
Study:WriteCreate and update studies, subjects, organs, and codes
Study:DeleteDelete studies, subjects, and organs
Finding:ReadRead 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
}
FieldDescription
access_tokenThe JWT to include on API requests
token_typeAlways Bearer
expires_inSeconds 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

StatusMeaning
401 UnauthorizedToken is missing, malformed, expired, or has an invalid signature
403 ForbiddenToken is valid but lacks the required scope for the requested operation

See Error Codes for the full error response format.