Cadmus API

Cadmus collects intra-oral scan orders from IOS portals and exposes them through a single, normalised REST API. This guide walks you through authentication, retrieving orders, and downloading scan files.

Base URL

All endpoints are relative to the base URL for your environment. Use HTTPS in all environments.

Staging
https://api.staging.cadmuslabs.nl

Shared development environment. Requires a valid user account.

Production
https://api.cadmuslabs.nl

Live environment. Requires a valid user account.

All examples below use https://api.cadmuslabs.nl as the base URL. Replace it with https://api.staging.cadmuslabs.nl when working against the staging environment.

Step 1 — Authenticate

Every API call requires a JSON Web Token (JWT). Obtain one by posting your credentials to /auth/login. The token is valid for 8 hours.

POST https://api.cadmuslabs.nl/auth/login
Request body
{
  "email": "you@example.com",
  "password": "your-password"
}
curl
curl -s -X POST https://api.cadmuslabs.nl/auth/login \
     -H "Content-Type: application/json" \
     -d '{"email":"you@example.com","password":"your-password"}'
Response (200 OK)
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expiresAt": "2026-03-09T18:30:00Z"
}

Copy the token value. You will pass it as Authorization: Bearer <token> on every subsequent request.

Step 2 — Retrieve orders

Orders are exposed through the /unified-cases resource. Every order is mapped to a single consistent schema — patient name, scan files, dentist, and restorations always appear in the same place regardless of which IOS portal the order originated from.

GET https://api.cadmuslabs.nl/unified-cases
curl
curl -s https://api.cadmuslabs.nl/unified-cases \
     -H "Authorization: Bearer <token>"
Response (200 OK)
{
  "items": [
    {
      "id": 42,
      "organizationId": 3,
      "portalId": 1,
      "configuredPortalId": 7,
      "caseId": "16ad2e12-fb26-4931-ba16-321285d74550",
      "guid": "33b07a91-c6ae-4824-aab6-f4a02560b0a6",
      "createdAt": "2026-03-08T09:14:00Z",
      "patientFullname": null, /* may be null — use acceptedData.patient.fullName */
      "status": "new",
      "completenessStatus": "complete",
      "infoRequestStatus": "none",
      "acceptedData": { /* normalised case fields — see data model below */ }
    }
  ],
  "page": 1,
  "pageSize": 20,
  "total": 183
}

Supported query parameters: page, pageSize, from (YYYY-MM-DD), to (YYYY-MM-DD).

Step 3 — Download files

All files (scan files and the order form PDF) are stored in cloud object storage (Google Cloud Storage) — they are not served directly from the database. The fileLink fields in acceptedData are internal storage paths, not public URLs.

Request a pre-authenticated signed URL from the API. Signed URLs are valid for 15 minutes and can be used directly in a browser, a 3D viewer, or a download client — no extra headers needed.

Cloud scanFiles[] fields

  • filenameFile name, e.g. upper.stl
  • fileTypeFormat: stl, ply, …
  • jawPosition: upper, lower, bite
  • typeScan type
  • creationDateTimeWhen the scan was created at the clinic
  • fileLinkInternal storage path — use the signed-URL endpoint to get a download link
GET https://api.cadmuslabs.nl/unified-cases/{id}/files/{filename}?fileType=stl
curl
curl -s "https://api.cadmuslabs.nl/unified-cases/42/files/upper.stl?fileType=stl" \
     -H "Authorization: Bearer <token>"
Response (200 OK)
{
  "url": "https://storage.googleapis.com/cadmus_collector_bucket/...&X-Goog-Signature=...",
  "filename": "upper.stl",
  "fileType": "stl",
  "expiresAt": "2026-03-09T10:45:00Z"
}

The order form is a single PDF attached to the case (acceptedData.orderForm). Use the dedicated endpoint to get a signed URL for it — no fileType parameter needed.

GET https://api.cadmuslabs.nl/unified-cases/{id}/order-form
curl
curl -s "https://api.cadmuslabs.nl/unified-cases/42/order-form" \
     -H "Authorization: Bearer <token>"
Response (200 OK)
{
  "url": "https://storage.googleapis.com/cadmus_collector_bucket/...&X-Goog-Signature=...",
  "filename": "DS Core_orderform.pdf",
  "expiresAt": "2026-03-09T10:45:00Z"
}

Pass the url directly to a PDF viewer or trigger a download. Request a fresh URL if the link has expired.

Step 4 — Look up portal information

Each unified case carries portalId and configuredPortalId to identify which IOS portal and which configured connection the order came from. Use these endpoints to resolve those IDs to names.

IOS portals

GET /portals/ios

All active IOS portal types. Returns id, name, className, url, axisMaping, quantizationBits.

Configured portals

GET /portals/configured

Configured portal connections for your organisation. Returns id, displayName, username, lastFetch, portalName.

Reference — unified-cases data model

Top-level status fields on each case: status (new | accepted | rejected | in_production | complete | cancelled), completenessStatus (complete | incomplete), infoRequestStatus (none | requested | received). The acceptedData object contains the following normalised fields.

Identity & routing

  • uidUnique case identifier
  • sourceOriginating portal name
  • externalIdID in the source system
  • organizationIdOrganisation identifier
  • organizationTypeOrganisation type
  • sentToDestination lab / recipient

Dates

  • originalCreationDateCreated in the source portal
  • originalReceivedDateReceived in the source portal
  • receivedDateNormalised received date
  • deliveryDateRequested delivery date
  • deliveryTimeRequested delivery time

Customer

  • customer.clinic.nameClinic name
  • customer.clinic.fullAddressClinic address
  • customer.dentist.fullNamePrescribing dentist
  • customer.dentist.externalIdDentist ID in source

Patient

  • patient.fullNameFull name
  • patient.genderGender
  • patient.dateOfBirthDate of birth
  • patient.referenceNoPatient reference number

Clinical

  • isOrthoOrthodontic case flag
  • restorations[]Restoration items (type, subtype)
  • worktypes[]Work items with material & FDI tooth
  • remarkFree-text clinical remark

Administrative

  • administrative.priorityUrgent flag
  • administrative.dueDateDue date
  • administrative.remakeRemake flag
  • administrative.deliveryNotesDelivery notes
  • administrative.contactRequiredContact required flag

Order form

  • orderForm.filenameOrder form file name (always a PDF)
  • orderForm.fileLinkInternal storage path
  • orderForm.creationDateTimeWhen the order form was created

Use GET /unified-cases/{id}/order-form to get a 15-min signed download URL.

Explore all endpoints

The interactive API reference lets you authenticate and call every endpoint directly in the browser. The OpenAPI JSON spec can be imported into Postman, Insomnia, or any compatible tooling.