Skip to content

Integrating a third-party product

The platform exposes two integration modes. Both authenticate with OAuth2 client credentials and share the same endpoint surface; the difference is whether your code calls the HTTP API directly or goes through the official TypeScript SDK.

Acting on behalf of a clinician? See token-exchange.md. When a real human (signed in via AWS Cognito) triggered the request, you can swap their Cognito ID token for a short-lived delegated platform token whose audit entries record the human, not your service account.

High-trust integration that needs cert-based auth? See mtls.md. Optional opt-in to OAuth2 mTLS (RFC 8705) — present a registered X.509 client cert in place of client_secret. Issued tokens carry a cnf.x5t#S256 binding so resource servers can verify the same cert presented the request.

Granting broad access without enumerating every scope? Scopes may contain wildcards: cases:* covers every cases:<action> scope (read, write, archive, etc.) and * covers everything. See scopes.md.

Quick comparison

Direct API SDK
Transport You make HTTPS calls You call typed TS methods, SDK handles HTTPS
Token refresh You manage SDK caches + refreshes 30s before expiry
Language Any TypeScript / JavaScript (Node 20+)
Scope enforcement Server-side (403 on missing scope) Server-side (same; the SDK doesn't pre-filter)
Workflows endpoints Available with orchestrator:* scopes Same — client.workflows.* methods
Best for Existing products in any language, custom HTTP needs New TypeScript integrations, fastest path to working code

Both modes hit the same endpoints in the catalogue below. The admin Integration tab (/orgs/:orgId/products/:productId/integration) is the easiest place to copy the parameters for either mode — open it for the specific (organisation, product) you're integrating against.

Get credentials

Each integration is provisioned as an OAuth2 client (client_id + client_secret) scoped to a single organisation + product. Ask the platform operator for:

  • client_id
  • client_secret
  • The list of granted scopes (see catalogue)
  • The platform host (e.g. https://api.example.com) — the platform serves every service under a single host using per-service path prefixes (/v1/clinical-api/..., /v1/orchestrator/..., /v1/auth/...). The Direct API and SDK examples below show how the prefixes are composed.

Mode 1 — Direct API

See direct-api.md for the full walkthrough. Three steps:

  1. Exchange your client credentials for a short-lived access token (POST /v1/auth/oauth/token on the auth service).
  2. Call any endpoint in your scope set with Authorization: Bearer <access_token>.
  3. Re-issue the token on expiry (~1h).

Mode 2 — SDK

See sdk.md for the install + first-call walkthrough. One step:

npm install @sa-platform/client-sdk
import { SkinAnalyticsClient } from '@sa-platform/client-sdk';

const client = new SkinAnalyticsClient({
  baseUrl, // platform host root, e.g. https://platform.example.com
  clientId,
  clientSecret,
  scopes: ['cases:write', 'patients:write', 'images:write'],
});

const patient = await client.patients.create({
  given_name: 'Alice',
  family_name: 'Anderson',
  dob: '1985-06-15',
});
const c = await client.cases.create({ patient_id: patient.id as string });

Postman collection

The Integration tab offers two downloads side-by-side:

  • Download Postman collection — a Postman v2.1 collection scoped to the (org, product) and to the scopes you ticked. The first folder, 0. Auth — get an access token, is the OAuth2 client-credentials request; the remaining folders are grouped by category (Patients, Cases, Images, Workflows, AI Review, Webhooks).
  • Download environment — a paired Postman v2.1 environment that pre-fills baseUrl (your platform host root) and clientId. clientSecret and accessToken ship empty; Postman masks both as type: "secret" so they don't leak when you screen-share.

Workflow:

  1. Import the collection and the environment.
  2. Select the environment in Postman's top-right environment picker.
  3. Open the environment and paste your client_secret (from the credentials tab — we never ship secrets in the downloaded file).
  4. Run 0. Auth → POST /v1/auth/oauth/token and copy the access_token response field into the environment's accessToken variable.
  5. Call any request in the remaining folders — they all resolve against {{baseUrl}}<absolute-path>, where the path includes the per-service prefix (/v1/clinical-api/cases, /v1/orchestrator/workflow-instances/:id, etc.). Re-issue the token on expiry (~1 h).

Refreshing the download after routing changes

The collection is generated from services/admin-api/src/integrations/endpoint-catalogue.ts at request time. If endpoints have moved (e.g. the 2026-05-27 service-routing convention changed /v1/cases to /v1/clinical-api/cases), simply re-download — there is no file to refresh locally and no manual edit step. Old collections downloaded before the change continue to point at the old paths; throw them away and grab a fresh one from the Integration tab.

Catalogue

The full endpoint catalogue is rendered live in the Integration tab. As of 2026-06-02 it covers:

  • Assessments — create, submit, get a consolidated AI assessment (assessments:read / assessments:write). See consolidated-assessments.md for the full walkthrough including inline and slot image modes.
  • Patients — create, get, update (patients:read / patients:write)
  • Cases — create, get, update (cases:read / cases:write)
  • Images — upload, get (images:read / images:write)
  • Workflows — fetch definition, fetch + advance instance (orchestrator:read, orchestrator:read-instances, orchestrator:advance-instance)
  • AI Review — submit + poll DERM reviews (derm_review:read / derm_review:write)
  • Webhooks — subscribe to + list event subscriptions (webhooks:read / webhooks:write)

When new endpoints become integration-ready, they're added to services/admin-api/src/integrations/endpoint-catalogue.ts and surface immediately in the UI + Postman download.