The integrations API
Integrations give other systems an endpoint to call, and the desk a connection to call them.
Calling an endpoint
POST /api/v1/in/ie_Zq2xL9…
Authorization: Bearer iks_…
Content-Type: application/json
{ "title": "Disk almost full on files-01", "severity": "major", "host": "files-01", "id": "alert-81" }
- The body is JSON, up to 256 KB.
- The call is checked, logged and answered right away. It is handled on the queue a moment later.
| Answer | When |
|---|---|
202 { "id": "…" } | Accepted. The id is the call in the log. |
200 { "id": "…", "duplicate": true } | The same duplicate key (or Idempotency-Key header) was seen within 24 hours. The id is the earlier call. |
200 { "id": "…", "captured": true } | The endpoint was capturing its next call as a sample. |
400 invalid_json | The body isn't JSON. |
401 unauthorized | The secret, or the signature, is wrong or missing. |
403 ip_not_allowed | The caller's address isn't on the endpoint's IP allowlist. |
404 not_found | No such endpoint, or the endpoint or its integration is turned off. |
429 rate_limited | More calls in a minute than the endpoint allows (120 by default), or more than 600 a minute from one address. |
Refused calls are logged without their body.
Signing in
The endpoint's How the caller signs in decides which of these it expects:
- Bearer:
Authorization: Bearer <secret>. - Basic:
Authorization: Basic base64(<anything>:<secret>). The user name is ignored. - Signed body (HMAC): two headers, the same as the desk's webhooks:
X-Desk-Timestamp: Unix seconds, within 5 minutes of the desk's clock.X-Desk-Signature:sha256=followed by the hex HMAC-SHA256 of<timestamp>.<raw body>, keyed with the secret.
import { createHmac } from 'node:crypto';
const body = JSON.stringify(alert);
const ts = Math.floor(Date.now() / 1000).toString();
const signature = 'sha256=' + createHmac('sha256', secret).update(`${ts}.${body}`).digest('hex');
await fetch(address, { method: 'POST', body, headers: { 'content-type': 'application/json', 'x-desk-timestamp': ts, 'x-desk-signature': signature } });
After a new secret is given with the old one kept, both secrets work for 24 hours.
Managing integrations
These need a token with integration.read to read, and integration.settings to change. Neither comes with
admin:all: name them on the token. Secrets and credentials are never in an answer, except a new endpoint's secret,
once.
| Method and path | What it does |
|---|---|
GET /api/v1/integrations | The integrations, with their endpoint count, last call and failed calls today. |
GET /api/v1/integrations/meta | The connectors, the targets an endpoint can feed (with their fields), and the sign-in kinds. |
POST /api/v1/integrations | { orgId, name, description?, connector? }. Creates the integration with its first endpoint, and answers with { id, endpoint: { id, address, secret } }. |
GET /api/v1/integrations/{id} | One integration with its endpoints (addresses, today's counts), connection and webhooks. |
PATCH /api/v1/integrations/{id} | name, description, enabled. |
DELETE /api/v1/integrations/{id} | Deletes it, its endpoints and its log. Linked webhooks stay, unlinked. |
POST /api/v1/integrations/{id}/endpoints | A new endpoint (the fields below). Answers with its address and secret. |
PATCH /api/v1/integrations/endpoints/{id} | Changes only the fields given. |
DELETE /api/v1/integrations/endpoints/{id} | Deletes the endpoint. Its calls stay in the log. |
POST /api/v1/integrations/endpoints/{id}/secret | { immediately?: boolean }. A new secret. The old one keeps working for 24 hours unless immediately is true. |
POST /api/v1/integrations/endpoints/{id}/test | { body? }. What the body (the sample when none is given) would do, changing nothing: { results, paths }. |
POST /api/v1/integrations/paths | { sample, itemsPath? }. The placeholders a sample offers, each with an example value. |
PUT /api/v1/integrations/{id}/connection | { baseUrl, auth, credentials?, headers?, timeoutMs? }. Leave out credentials to keep the stored ones (when auth stays the same). |
DELETE /api/v1/integrations/{id}/connection | Removes the connection. |
POST /api/v1/integrations/{id}/connection/test | { path? }. A GET through the connection. The call is logged. |
GET /api/v1/integrations/{id}/calls | The log, paged, filtered by direction, outcome and endpointId. |
GET /api/v1/integrations/calls/{callId} | One call, with its body. |
POST /api/v1/integrations/calls/{callId}/replay | Handles an accepted incoming call again, as a new call. |
GET /api/v1/integrations/{id}/calls.export | The log of a period (from, to, format = csv, xlsx or pdf). |
An endpoint's fields:
| Field | Type |
|---|---|
name | text |
auth | bearer, basic or hmac |
target | case, or a plugin's target such as events.event or incidents.incident (from meta) |
unitId | a unit of the organization, or null |
fields | { fieldId: template } |
itemsPath, idempotencyPath | paths, or null |
rateLimit | calls a minute, 1 to 6000 |
ipListId | one of the organization's IP allowlists, or null |
sample | any JSON |
captureNext, enabled | true or false |
Assistants reach all of this through the MCP server's call_endpoint.