API overview
How the UnitX REST API works: addresses and versioning, authenticating with API keys or OAuth tokens, rate limits, errors and webhooks.
Everything you can do in UnitX goes through the same REST API that the app itself uses, so scripts and integrations can read and change your organization’s data under the same permission rules as people. This page covers what developers need to know first: addresses, authentication, limits, errors and webhooks.
What you can build#
- Scripts and scheduled jobs that create or update records, using an API key.
- Tools that act as the person using them, using an OAuth application.
- Systems that react to changes in UnitX, using outgoing webhooks.
- User provisioning from your identity provider, using SCIM.
- Apps for other organizations, published through the Marketplace.
Addresses and versioning#
API operations live under /api/v1/ on your UnitX API address — for example /api/v1/projects. The version is part of the path. Within a version, changes are additive; a breaking change would come with a new version.
A few addresses keep their own shape because outside systems depend on them:
/openapi.json- The published OpenAPI specification. Public, no sign-in needed.
/oauth/…- OAuth authorization and token endpoints.
/scim/v2/…- SCIM provisioning endpoints.
/integrations/webhooks/…- Addresses that other systems post incoming webhooks to.
Tip
/openapi.json.Authentication#
Send a credential in the Authorization header as a bearer token:
Authorization: Bearer <credential>
| Credential | Acts as | Organization |
|---|---|---|
API key (begins uxk_) | The person or service account it belongs to, limited to its scopes | Fixed to the organization the key was created in |
| OAuth access token | The person who approved the application, limited to the approved scopes | Fixed to the organization it was approved in |
With an API key or OAuth token you do not need to name the organization. If you send an X-Organization-Id header anyway, it must match the credential’s organization or the request is refused.
Permissions#
- Every operation needs a permission, such as
project.view. A request whose credential does not carry it is refused with403. - An API key or token can only use permissions listed in its scopes. A service account’s key is also limited by the service account’s role.
- Permissions narrowed by a scope such as “Their own records” describe a person, so they cannot be used by machine credentials.
- Fields your organization has restricted through data classification are left out of responses for credentials without the required permission.
See Service accounts and API keys and OAuth applications for how to obtain each credential.
Rate limits#
Requests are counted in one-minute windows. Two limits apply at the same time, and reaching either one limits the request:
| Counted per | Requests per minute |
|---|---|
| Organization (all credentials and people together) | 1,000 |
| API key | 300 |
Every response includes headers so a client can slow down before hitting the limit:
RateLimit-Limit- The limit that applies.
RateLimit-Remaining- Requests left in the current window.
RateLimit-Reset- Seconds until the window resets (a number of seconds, not a timestamp).
Over the limit, the API answers 429 with a Retry-After header giving the seconds to wait.
Requests and validation#
- Send and receive JSON.
- Unknown fields are rejected, not silently ignored. A misspelt field name fails with
400and says which field was wrong. - Lists that can grow large, such as the activity feed and the audit trail, are fetched a page at a time with
cursorandlimitparameters. The specification shows which operations are paged.
Errors#
Errors share one shape, with a stable code you can program against:
{ "error": { "code": "not_found", "message": "…", "details": null, "requestId": "…" } }
details lists the individual problems when validation fails. Quote requestId when you contact support. Common codes:
| Status | Code |
|---|---|
| 400 | bad_request |
| 401 | unauthorized |
| 403 | forbidden |
| 404 | not_found |
| 409 | conflict |
| 422 | unprocessable_entity |
| 429 | rate_limited |
| 5xx | internal_error or service_unavailable |
Deprecations#
If an operation is ever scheduled for removal, its responses carry a Deprecation header, a Sunset header with the removal date when one is set, and a Link header pointing to what to use instead. Watch for these in your logs.
Webhooks#
Outgoing webhooks#
Add an endpoint under Integrations → Outgoing webhooks and UnitX sends each event to it as an HTTP POST with a JSON body:
{ "id": "…", "type": "…", "occurredAt": "2026-09-16T10:00:00.000Z", "data": { … } }
x-unitx-event- The event type.
x-unitx-delivery- A unique id for this delivery.
x-unitx-signaturesha256=followed by the hex HMAC-SHA256 of the raw request body, using the endpoint’s signing secret. Compute it yourself and compare before trusting the request.
Respond with a 2xx status to acknowledge. Failed deliveries are retried with increasing delays, up to five attempts, and each attempt is visible under Deliveries.
Incoming webhooks#
For systems connected under Integrations, UnitX can receive webhooks at an endpoint you create on the connection. Each request must carry a valid x-unitx-signature computed with that endpoint’s secret.
Provisioning with SCIM#
User provisioning uses the SCIM 2.0 standard at /scim/v2/Users and /scim/v2/Groups, authenticated with a SCIM credential rather than an API key. See SCIM provisioning.
Why do I get 403 when my key has the right scope?
For a service account’s key, the account’s role must also hold the permission, and not only under a scope such as “Their own records”. Check the role on the Roles screen. The account may also be disabled.
My API key stopped working suddenly.
It may have expired, been revoked, belonged to someone who was suspended or removed, or belonged to a service account that was disabled. Keys revoked for any of these reasons do not come back; create a new one.
Is there an SDK?
Generate a client from /openapi.json with the OpenAPI tooling for your language.
Related pages#
Still stuck? Search the docs with ⌘K, open Help inside UnitX, or contact support.