SAQ exposes its ticket, time, and billing services to other systems through an HTTP API and an MCP server. Both use the same permission model as the app: a token can only see and do what the person or agent account behind it can see and do in the browser. This page tells you what exists, what does not, and how to make your first call.
What you can integrate
| Surface | Where | Used for |
|---|---|---|
| REST API | https://saq.no/api/w/{slug}/… |
Tickets, comments, attachments, links, boards, projects, time entries, client timesheets |
| MCP server | POST https://saq.no/mcp |
AI assistants and agents (Claude Code, Cursor, and any Streamable HTTP MCP client) |
| SCIM 2.0 | https://saq.no/scim/v2 |
User and group provisioning from an identity provider |
| OIDC single sign-on | Configured by an Owner or Admin under Settings → Authentication | Login through Microsoft Entra ID or a generic OpenID Connect provider; no developer work beyond registering the redirect URI |
| GitHub links | POST /api/w/{slug}/tickets/{ref}/github and the MCP tool link_github |
Storing a reference from a ticket to a commit or pull request |
{slug} is the workspace address, for example acme in https://saq.no/w/acme. Every example in this documentation uses the workspace acme and the placeholder token saq_YOUR_TOKEN.
What does not exist
- No outbound webhooks. SAQ never pushes events to your system. Poll the API or use MCP instead; see Webhooks (not available).
- No OAuth for third parties. Access is by bearer token minted in the app. There is no authorization-code flow for external applications.
- No API versioning path. The API lives under
/api, not/api/v1. - No sandbox environment. One production origin serves each deployment. Test against a workspace you control, with a token that has only the scopes you need.
- No SAML. Single sign-on is OpenID Connect only.
The OpenAPI document
The complete list of routes and request and response shapes is published as an OpenAPI 3.1 document at GET https://saq.no/api/openapi.json. It is public: fetch it with curl, commit it, or generate a client from it in CI, with no credentials. It lists every operation, including file downloads, the MCP endpoint, and SCIM, and every operation declares which credential it accepts, so a generated client wires up the Authorization header by itself. There is no hosted Swagger UI. The API reference describes the same routes with examples and error tables, and SAQ's build fails if the two ever disagree.
Permissions
A token acts as its owner:
- A personal token acts on behalf of the person who minted it. If that person can see a ticket in the app, the token can read it; if they cannot, the API answers
not_found. - An agent account key acts as a token-only workspace user with the Member role and membership of every project.
Scopes narrow a token further (read, write, triage) and a token can be limited to named projects. Period close, billing runs, workspace settings, and client management are never available to tokens. See Authenticate with API tokens.
Quick start
- Log in to SAQ, open Account → API tokens, and create a token for your workspace with the Read scope. Copy the secret; it is shown once.
- List the open tickets you can see:
curl "https://saq.no/api/w/acme/tickets?stateCategory=open&limit=2" \
-H "Authorization: Bearer saq_YOUR_TOKEN"
- Read the response. Every list has
itemsand anextCursorthat isnullon the last page:
{
"items": [
{
"id": "01924c3e-6b2a-7d11-9d0e-3a1f4b5c6d7e",
"humanId": "ACME-184",
"title": "Login page shows a blank screen",
"kind": {
"id": "01924c3e-0000-7000-8000-000000000001",
"key": "bug",
"name": "Bug"
},
"state": {
"id": "01924c3e-0000-7000-8000-000000000002",
"key": "in_progress",
"name": "In progress",
"category": "open",
"position": 2
},
"priority": {
"id": "01924c3e-0000-7000-8000-000000000003",
"key": "high",
"name": "High",
"position": 3
},
"project": {
"id": "01924c3e-0000-7000-8000-000000000004",
"key": "website",
"name": "Website"
},
"queue": null,
"assignee": {
"id": "01924c3e-0000-7000-8000-000000000005",
"name": "Kari Nordmann",
"clientName": null
},
"reporter": {
"id": "01924c3e-0000-7000-8000-000000000006",
"name": "Ola Hansen",
"clientName": "Acme AS"
},
"billingClient": {
"id": "01924c3e-0000-7000-8000-000000000007",
"name": "Acme AS"
},
"labels": [],
"dueDate": "2026-09-30",
"startDate": null,
"estimateMinutes": 120,
"parentId": null,
"commentCount": 3,
"createdAt": "2026-09-12T08:41:10.000Z",
"updatedAt": "2026-09-19T10:15:00.000Z"
}
],
"nextCursor": "MjAyNi0wOS0xOVQxMDoxNTowMC4wMDBafDAxOTI0YzNl"
}
Next, read the API conventions for identifiers, dates, paging, and errors.
Developer pages
- Authenticate with API tokens: token kinds, scopes, project scoping, address restrictions, expiry, revocation, and the routes a token may call.
- API conventions: paths, JSON, identifiers, dates, paging, filtering, errors, rate limits, idempotency, and size limits.
- Connect an MCP client: the MCP endpoint, setup snippets, and every tool.
- SCIM 2.0 reference: provisioning users and groups from an identity provider.
- Webhooks (not available): what to do instead of webhooks.
- GitHub links: store-only references to commits and pull requests.
Recipes
- Create a ticket from another system
- Keep an external system in sync
- Import time entries
- Read project status
- Download client timesheets
- Connect an AI agent
API reference
API reference · Public endpoints · Tickets · Comments and attachments · Viewers, links, and GitHub · Boards and My work · Projects · Time entries · Periods and reports · Documents · Workflow · API tokens and agents · Session-only operations