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: 1–12 hours)

│ ② 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 role bundles that determine which API operations you can perform (see scopes below)

Role bundles and scopes

Role BundleScopesDescription
Study ManagementSTUDY_READ, STUDY_WRITE, COHORT_READ, COHORT_WRITE, METADATA_READ, METADATA_WRITE, IMAGE_READFull read/write on studies, cohorts, metadata, images
Study ReportingSTUDY_READ, SCORE_READ, ANNOTATION_READ, REPORT_READ, REPORT_EXPORTRead access to studies, scores, and exports
Image IngestionIMAGE_WRITE, STUDY_READ, COHORT_READWrite images, read studies and cohorts

Your JWT token will contain the scopes for the role bundles assigned to your client. Calling an endpoint that requires a scope you do not have results in 403 Forbidden.

Getting a token

Exchange your credentials for a JWT access token:

curl -X POST https://api.account.dev.patholytix.com/oauth/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: 1–12 hours)

Using the token

Include the access token in the Authorization header on every API request:

curl https://api.dev.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 1 and 12 hours — the TTL is set per client at registration time.

Best practice: Request a new token proactively before the current one expires. Check the expires_in field and refresh when ~5 minutes remain.

Python example

import requests
import time

TOKEN_URL = "https://api.account.dev.patholytix.com/oauth/token"
API_BASE = "https://api.dev.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.