Integrations
The integration API is the machine-facing side of your workspace: access to live queues, tickets, locations, services, configuration templates, and branding, reached with a credential you issue in the management console. It supports both reads and writes; each operation documents its required authority. It is the supported way to put queue data on your own intranet dashboard, into a CRM, or in front of a reporting job — no screen scraping and no console session involved.
Every identifier and every secret on this page is a fixed placeholder. None of them names a real workspace, branch, ticket, user or credential.
New to the API? Make your first API call, then return here for credential lifecycle, versioning, and error handling.
Getting a credential
Credentials live in the management console, under API credentials. An organisation admin or a location manager may issue them; an operator may not.
- Issue. Give the credential a label that says what will use it — labels are unique among your workspace's live credentials — and an expiry date, then choose the scopes this integration needs. Nothing is preselected. Scopes narrow the issuer's authority; they never grant extra permissions. Expiry is required and can be at most a year ahead. The console shows the secret exactly once, at the moment it is created: copy it into your integration's configuration before you close that dialog, because no later screen and no later call will show it again. What every later read shows is the display prefix, the secret's first twelve characters, which is enough to tell two credentials apart by eye.
- Rotate. Rotating mints a second secret and shows it once, the same way. The previous secret keeps working for 24 hours, so you can deploy the new one without an outage; after that overlap it is refused.
- Revoke. Revoking takes effect on the next call and cannot be undone — a secret still inside a rotation overlap dies with it. The credential stays listed for audit; issue a new one to resume.
A credential never reaches further than the person who issued it. It acts with that user's management level and over the branches that user manages, resolved on every single call: withdraw a branch from them and the credential stops seeing it immediately, and if the user is deactivated or loses their management level the credential stops authenticating at all.
Scopes and authority
Choose only the scopes your integration needs when issuing the credential. The set is fixed for that credential; rotation changes the secret, not its scopes.
| Scope | Operations it permits, within the issuer's authority |
|---|---|
queue:read | Read live queues and individual tickets. |
bookings:read | Read the bookable appointment slots of a service at a branch, and the appointments of a branch for one day. |
locations:read | Read branches, branch configuration, branch languages, branch service order and names, branch visit reasons, branch readiness, and location templates. |
locations:write | Create, edit, archive, reactivate, or configure branches; add, reorder, change the surfaces of and remove a branch's languages; reorder a branch's services and set their names per language; replace and delete a branch's visit reasons; manage and apply location templates. |
services:read | Read the service catalogue. |
services:write | Create, edit, archive, and reactivate services. |
branding:read | Read workspace branding and the customer homepage settings. |
branding:write | Replace the customer homepage settings. |
users:read | Read a branch's operator profiles. |
users:write | Invite, edit, deactivate, reactivate and remove operator profiles. |
devices:read | Read a branch's paired TV displays with their layout and colour set. |
devices:write | Set the layout and colour set of a paired TV display. |
settings:read | Read the customer authentication policy, the identity providers and the plan list; run an identity provider's connection check. |
settings:write | Set the customer authentication policy; create, reconfigure, enable, disable, make primary, reorder and remove identity providers, and set their client authentication. |
api | All integration operations, including future additions. |
whoami and the OpenAPI document are reachable with every valid credential. Scopes never override the issuer's role: creating or editing a location, all service and template operations, applying a template, reading branding, reading or replacing the homepage settings, the customer authentication policy, every identity-provider operation and the plan list require an organisation-admin issuer. A location manager can read, configure, archive, and reactivate their assigned branches, manage their operators, and set their TV displays' layout and colour set, with the corresponding scope.
Identity providers and entry policy
The identity provider your clients sign in with, and whether they must sign in, are set up in the console — see Connect your identity provider. Over the API an organisation-admin credential can manage the same providers:
- Read the providers —
GET /api/integration/v1/settings/identity-providerslists them in display order;GET /api/integration/v1/settings/identity-providers/{providerId}reads one. Each carries its id, display name, kind,enabledandprimaryflags, issuer URL, client id,clientAuthMethod,clientSecretConfigured,clientSecretUpdatedAt,verifiedAt(when a test sign-in last succeeded for its current configuration) andactivatedWithoutTest(it was activated, or its connection changed while active, without a test sign-in). The client secret is never returned, in any form. - Create a provider —
POST /api/integration/v1/settings/identity-providerswithkind,displayName,issuerUrl,clientIdand, for a secret-basedclientAuthMethod, itsclientSecret. The provider is created inactive, not primary and untested. - Reconfigure a provider —
PATCH /api/integration/v1/settings/identity-providers/{providerId}changes the members you send and keeps the rest;kindcannot change. - Set client authentication —
PUT /api/integration/v1/settings/identity-providers/{providerId}/client-authenticationtakesclientAuthMethod(none,client_secret_basicorclient_secret_post) and an optionalclientSecret, with the console's rules: a sent secret replaces the stored one, no secret keeps it, andnoneerases it. - Activate or deactivate —
POST /api/integration/v1/settings/identity-providers/{providerId}/enableandPOST /api/integration/v1/settings/identity-providers/{providerId}/disable. A provider becomes primary when it is activated and no active primary exists. The primary cannot be deactivated. - Make primary —
POST /api/integration/v1/settings/identity-providers/{providerId}/primary; only an active provider can be primary. - Reorder —
POST /api/integration/v1/settings/identity-providers/reorderwithorderedIdsnaming every provider exactly once. - Remove —
DELETE /api/integration/v1/settings/identity-providers/{providerId}. The primary cannot be removed while other providers remain, and the last provider cannot be removed while the entry policy is notoff. - Check a connection —
POST /api/integration/v1/settings/identity-providers/{providerId}/connection-checkruns the console's discovery check against the provider's stored issuer URL. It always answers 200 and reports a failure throughoutcomeandcode, with the same codes the console shows. It changes nothing. - Read or set the entry policy —
GETandPUT /api/integration/v1/settings/customer-authenticationwithpolicyoff,optionalorrequired.optionalandrequiredare refused with 422idp_not_configureduntil an active provider exists.
Activating without a test sign-in
The test sign-in runs in a browser, so the API cannot perform it. Activating an untested provider, or changing the connection (issuer URL, client id, authentication method, secret, scopes or claim mappings) of an active one, is refused with 409 registration_not_verified unless the request sets acceptUnverified to true. With it, QMS first runs the connection check against the issuer URL the provider would hold: a failure is refused with 422 connection_check_failed, naming the reason in checkCode, and nothing changes; a pass applies the change and marks the provider activatedWithoutTest. Changes that need no test — a display name, an inactive provider's connection — apply as usual, and on an inactive provider a connection change clears verifiedAt.
The console shows such a provider as Active without a test with a warning, and the audit log marks the change as applied without a test. A successful test sign-in in the console, or deactivating the provider, clears the mark.
Under the required entry policy, clients cannot get a ticket without signing in, so the primary must have passed a test sign-in: setting the policy to required while the primary is activatedWithoutTest, or a request that would leave an untested primary under required, is refused with 409 primary_not_verified.
Presenting the credential
Send the secret in the Authorization header, prefixed with the literal Api-Key — the scheme name is part of the header value, not a placeholder to replace:
GET /api/integration/v1/whoami
Host: your-tenant.example.com
Authorization: Api-Key qms_EXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAM0That secret is an example, not a working credential; issue your own in the console. Your workspace is reached on its own subdomain — the one you sign in to the console with. A generated client sends the header verbatim and does not prepend the prefix for you.
whoami is the call to start with. It answers with the credential's own id and label, the workspace it belongs to, the user who issued it, when it expires, and the branches it currently reaches; if that call works, the credential is configured correctly.
Two things are worth knowing before you debug an authentication problem. A console session authenticates nothing on this surface, and this header authenticates nothing on the console's own API — they are separate doors. And every authentication failure looks identical from outside: absent, malformed, unknown, expired, revoked, or past a rotation overlap all answer HTTP 401 with one and the same body, deliberately.
Versions and deprecation
The major version in the path — the v1 in /api/integration/v1/ — is the only place a breaking change is ever expressed. Nothing under a served major ever changes incompatibly, so a client generated today keeps working for as long as that major is served. A breaking change ships instead as a new major, /api/integration/v2/, served alongside the one before it.
These may appear under the current major at any time, so write your client to tolerate them: a new field in a response, a new endpoint, a new optional request parameter, and a new code value for a failure that previously had none.
These are breaking, and therefore only ever arrive as a new major: removing or renaming a field, endpoint or parameter; changing a field's type, format or meaning; adding a required request field; tightening validation on an existing one; changing the status code of an existing outcome; and adding a value to an enumeration a response emits — a client that branches on a ticket's state must never meet a value it was never told about.
When something is deprecated, every response that still carries it carries the Deprecation and Sunset headers, and it keeps working unchanged for at least six months from the first response that did. Sunset names the date it may be removed, never earlier than that, and removal happens only after it. Watch those two headers from your own monitoring: they are the notice.
When something goes wrong
Every non-2xx answer is a problem document sent as application/problem+json, carrying these common members (validation failures can also include field errors):
| Member | What it holds |
|---|---|
type | A URN identifying the problem kind, one per code. |
title | A fixed English summary of that kind. |
status | The HTTP status, repeated in the body. |
code | The stable, machine-readable slug. Branch on this one. |
detail | An English explanation of this occurrence, for a developer reading a log. |
{
"type": "urn:qms:problem:location_not_found",
"title": "Location Not Found",
"status": 404,
"code": "location_not_found",
"detail": "No location was found for this identifier."
}Common code values include:
code | Status | When |
|---|---|---|
unauthenticated | 401 | No valid credential was presented, whatever the cause. |
unsupported_api_version | 404 | The path names a major this build does not serve. |
not_found | 404 | The path names no endpoint at all. |
method_not_allowed | 405 | The path is served, but not with that method. |
location_not_found | 404 | No branch was found for that identifier. |
ticket_not_found | 404 | No ticket was found for that identifier. |
insufficient_scope | 403 | The credential does not carry a scope required by the operation. |
insufficient_authority | 403 | The issuer does not have the management level required by the operation. |
internal_error | 500 | The request could not be processed. |
Three of them — unsupported_api_version, method_not_allowed and not_found — are decided before any operation is reached, which is why no operation declares them. Be ready for the problem shape on a response your client has no declared status for.
location_not_found and ticket_not_found are deliberately indistinguishable from "you may not see this": outside the credential's reach, belonging to another workspace, and never having existed are one single answer, so nothing can be probed for its existence.
Writes and responses
A create answers 201 with the created resource in the body and no Location header. No response carries an ETag and no request takes If-Match: when two writes to one resource race, the later one wins (last-write-wins).
Where the API stops
Provisioning a branch end to end over the API still leaves a few steps to people, and a few settings to the console. Plan for them before you automate an onboarding run.
Steps that need a person or a device. A credential is not a person and cannot stand in front of a TV, so these stay outside the API by design:
- Pairing the display and the kiosk. Pairing is typed on the device itself — see Set up displays and kiosks.
- Accepting an operator invitation. An operator you create over the API is invited by email and stays
INVITEDuntil they accept. Until then, every write to their name, employee id, windows, languages or services is refused with 409operator_invite_pending— see Invite your team. - Testing an identity provider. The test sign-in runs in a browser — see Activating without a test sign-in.
Settings that are in the console only, for now. These have no API operation yet; set them in the console:
- operator weekly schedule and absences;
- CSV operator import;
- location-manager users and their role assignment;
- holiday closures;
- visit reasons;
- service groups;
- branding logo upload;
- notification settings;
- finishing an invited operator's profile before they accept;
- reading your own plan and trial end.
The reference
Browse the API reference by resource. Each operation has its own page with its method, path, description, request example, parameters, response examples, schemas, and authentication requirement.
- Authentication: verify a credential with
whoami. - Queues: read a branch's live queue or an individual ticket.
- Locations: list, create, edit, archive, reactivate, and configure branches, manage a branch's visit reasons, read a branch's readiness, and read and reorder a branch's services and set their names per language.
- Services: manage the workspace service catalogue.
- Templates: manage and apply reusable location configurations.
- Branding: read the workspace branding; read and replace the customer homepage settings.
- Languages: manage the workspace language catalogue and each branch's languages; see Configure languages. An operator's
languagescan name only languages already in the catalogue; any other code is refused with 400language_not_in_catalogue. - Contract: retrieve the OpenAPI document for client generation.
The reference is generated from the same committed OpenAPI document the API serves. Check the operation description for required scopes and issuer authority.