Get started
Introduction
The dskr.ai API lets your tools and AI assistants work with your help desk: list and read tickets, open new ones, reply to customers, add internal notes, save drafts, change status and assign.
- Base URL:
https://app.dskr.ai/functions/agentApi - Format: every call is a
POSTwith a JSON body. Responses are JSON. - Plan: available on the Scale plan.
- MCP: the same actions are available to Claude, ChatGPT and other AI assistants through our MCP server (see AI Assistants).
https://app.dskr.ai/functions/agentApiAuthentication
Every request needs a connection key in the Authorization header.
To create one:
- 1Sign in to dskr.ai as an admin.
- 2Go to Settings → AI agents & automation and create a connection.
- 3Tick the actions this connection may use.
- 4Copy the key. It starts with
dskr_agent_and is shown only once.
Each key works for one company only and can only perform the actions you ticked. Keep it secret, never put it in browser code, and delete the connection if it leaks.
Authorization: Bearer dskr_agent_your_key_hereQuickstart
Using Zapier, n8n or Make? Add an HTTP step (Zapier: Webhooks by Zapier → POST, n8n: HTTP Request, Make: HTTP → Make a request) with the same address, header and JSON body.
curl -X POST \
https://app.dskr.ai/functions/agentApi \
-H "Authorization: Bearer $DSKR_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "read",
"view": "tickets",
"limit": 5
}'{
"tickets": [
{
"id": "6a1b2c3d4e5f60718293a4b5",
"ticket_number": 1042,
"customer_id": "6a0f1e2d3c4b5a6978877665",
"subject": "Can't log in",
"status": "Open",
"priority": "Normal",
"assigned_to_id": ""
}
],
"next_offset": 5
}Requests and responses
- Send only these fields in the body:
action,view,ticket_id,customer_id,requester_id,subject,body,status,assignee_id,request_id,limit,offset. Any other field is rejected. - The body can be up to 16,000 characters.
- A successful call returns HTTP 200 with the result object directly.
- Ids are 24-character text strings, for example
6a1b2c3d4e5f60718293a4b5.ticket_numberis a number. - Dates are ISO 8601 text, for example
2026-10-18T14:30:00.000Z. - Responses are never cached (
Cache-Control: no-store).
Pagination
List actions accept limit (1–50, default 20) and offset (0–10,000, default 0). Responses include next_offset: send it as offset to get the next page, and stop when it's null.
Idempotency
Every action that changes something (create, send, note, draft, status, assign) needs a request_id: a UUID you generate, such as 6f1c2b9e-3a47-4d2e-9b1f-8c0e5a7d2f41.
- If a call times out, retry with the same
request_id. If the first call already succeeded, you get the original response back and nothing happens twice. - Use a new
request_idfor every new action. - Reusing a
request_idfor a different action returns409.
REQUEST_ID=$(uuidgen)Errors
Errors return a 4xx or 5xx status and a body like the example.
| Status | Message | Meaning |
|---|---|---|
| 400 | The body must be JSON.The body must be a JSON object. | The body isn't a JSON object. |
| 400 | Action request is too large. | The body is over 16,000 characters. |
| 400 | Only the advertised ticket actions are allowed. | Unknown action, or a field that isn't allowed. |
| 400 | Mutating actions require a stable UUID request_id for safe retries. | Missing or invalid request_id. |
| 400 | Choose a limit of 1–50 and an offset of 0–10000. | limit or offset out of range. |
| 400 | Unsupported view. | Unknown view. |
| 400 | Invalid ticket. | ticket_id isn't a valid id. |
| 400 | Enter a message of 1–10000 characters. | body is empty or too long. |
| 400 | Choose Open, Waiting, Resolved or Closed. | Invalid status. |
| 400 | Choose a customer. | Missing or invalid customer_id. |
| 400 | Choose a customer and requester, and enter a subject of 1–200 characters. | Invalid create request. |
| 400 | Choose an active assignable support user in this workspace. | Invalid assignee_id. |
| 401 | Invalid key. | Missing, wrong or deleted key. |
| 403 | This connection is off. Turn it on in Settings → AI agents / automation. | The connection is turned off. |
| 403 | AI agents & automation is not available on your plan. Upgrade to Scale to use this connection. | Not on the Scale plan. |
| 403 | This connection is not allowed to perform this action. | The action isn't ticked for this connection. |
| 403 | Choose an active linked requester in this workspace. | The requester isn't active for that customer. |
| 403 | Internal ticket contents are not exposed to external agents. | The ticket is internal-only. |
| 403 | Internal tickets cannot send customer replies. | Send on an internal-only ticket. |
| 403 | Only customer-visible tickets can have reply drafts. | Draft on an internal-only ticket. |
| 404 | Ticket not found. | No such ticket in your company. |
| 404 | Customer not found. | No such customer in your company. |
| 405 | Send a POST request with a JSON body. | The method isn't POST. |
| 409 | This request id belongs to a different action. | request_id reused for another action. |
| 409 | Archived tickets are read-only. | Changing an archived ticket. |
| 409 | The support queue is unavailable. | Your company has no active Support queue. |
| 409 | Connection changed or is busy. Retry shortly. | Two calls collided. Retry. |
| 429 | This connection is busy. Retry shortly. | Another call on this connection is still running. |
| 429 | Connection limit reached: 60 actions per minute. | Rate limit reached. |
| 500 | Agent action could not complete. | Unexpected error. Retry with the same request_id. |
- 400The body must be JSON.The body must be a JSON object.The body isn't a JSON object.
- 400Action request is too large.The body is over 16,000 characters.
- 400Only the advertised ticket actions are allowed.Unknown action, or a field that isn't allowed.
- 400Mutating actions require a stable UUID request_id for safe retries.Missing or invalid
request_id. - 400Choose a limit of 1–50 and an offset of 0–10000.
limitoroffsetout of range. - 400Unsupported view.Unknown
view. - 400Invalid ticket.
ticket_idisn't a valid id. - 400Enter a message of 1–10000 characters.
bodyis empty or too long. - 400Choose Open, Waiting, Resolved or Closed.Invalid
status. - 400Choose a customer.Missing or invalid
customer_id. - 400Choose a customer and requester, and enter a subject of 1–200 characters.Invalid create request.
- 400Choose an active assignable support user in this workspace.Invalid
assignee_id. - 401Invalid key.Missing, wrong or deleted key.
- 403This connection is off. Turn it on in Settings → AI agents / automation.The connection is turned off.
- 403AI agents & automation is not available on your plan. Upgrade to Scale to use this connection.Not on the Scale plan.
- 403This connection is not allowed to perform this action.The action isn't ticked for this connection.
- 403Choose an active linked requester in this workspace.The requester isn't active for that customer.
- 403Internal ticket contents are not exposed to external agents.The ticket is internal-only.
- 403Internal tickets cannot send customer replies.Send on an internal-only ticket.
- 403Only customer-visible tickets can have reply drafts.Draft on an internal-only ticket.
- 404Ticket not found.No such ticket in your company.
- 404Customer not found.No such customer in your company.
- 405Send a POST request with a JSON body.The method isn't POST.
- 409This request id belongs to a different action.
request_idreused for another action. - 409Archived tickets are read-only.Changing an archived ticket.
- 409The support queue is unavailable.Your company has no active Support queue.
- 409Connection changed or is busy. Retry shortly.Two calls collided. Retry.
- 429This connection is busy. Retry shortly.Another call on this connection is still running.
- 429Connection limit reached: 60 actions per minute.Rate limit reached.
- 500Agent action could not complete.Unexpected error. Retry with the same
request_id.
{ "error": "Ticket not found." }Rate limits
- 60 actions per minute per connection. The count resets at the start of each minute (UTC).
- One action at a time per connection. Wait for each call to finish before sending the next.
- There's no Retry-After header. On a
429or409 … Retry shortly, wait a few seconds and retry with the samerequest_id. - For more throughput, create separate connections for separate workflows.
API reference
List tickets
https://app.dskr.ai/functions/agentApiLists customer-visible tickets that aren't archived, most recently updated first.
Body parameters
| Parameter | Type | Description |
|---|---|---|
actionrequired | string | "read". |
viewoptional | string | "tickets". This is the default. |
limitoptional | integer | 1–50, default 20. |
offsetoptional | integer | 0–10,000, default 0. |
actionrequiredstring"read".viewoptionalstring"tickets". This is the default.limitoptionalinteger1–50, default 20.offsetoptionalinteger0–10,000, default 0.
curl -X POST \
https://app.dskr.ai/functions/agentApi \
-H "Authorization: Bearer $DSKR_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "read",
"view": "tickets",
"limit": 20
}'{
"tickets": [
{
"id": "6a1b2c3d4e5f60718293a4b5",
"ticket_number": 1042,
"customer_id": "6a0f1e2d3c4b5a6978877665",
"subject": "Can't log in",
"status": "Open",
"priority": "Normal",
"assigned_to_id": ""
}
],
"next_offset": 20
}Get a ticket
https://app.dskr.ai/functions/agentApiReturns one ticket with its description and customer-visible messages, newest first.
Body parameters
| Parameter | Type | Description |
|---|---|---|
actionrequired | string | "read". |
viewoptional | string | "tickets". |
ticket_idrequired | string | The ticket's id. |
limitoptional | integer | Page through the messages. |
offsetoptional | integer | Page through the messages. |
actionrequiredstring"read".viewoptionalstring"tickets".ticket_idrequiredstringThe ticket's id.limitoptionalintegerPage through the messages.offsetoptionalintegerPage through the messages.
curl -X POST \
https://app.dskr.ai/functions/agentApi \
-H "Authorization: Bearer $DSKR_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "read",
"view": "tickets",
"ticket_id": "6a1b2c3d4e5f60718293a4b5"
}'{
"ticket": {
"id": "6a1b2c3d4e5f60718293a4b5",
"ticket_number": 1042,
"customer_id": "6a0f1e2d3c4b5a6978877665",
"subject": "Can't log in",
"status": "Open",
"priority": "Normal",
"assigned_to_id": "",
"description": "I get an error."
},
"messages": [
{
"id": "6a2c3d4e5f60718293a4b5c6",
"body": "We're on it.",
"author_name": "Ana",
"created_at": "2026-10-18T14:30:00.000Z"
}
],
"next_offset": null
}List customers
https://app.dskr.ai/functions/agentApiLists your customer companies, sorted by name.
Body parameters
| Parameter | Type | Description |
|---|---|---|
actionrequired | string | "read". |
viewrequired | string | "customers". |
limitoptional | integer | 1–50, default 20. |
offsetoptional | integer | 0–10,000, default 0. |
actionrequiredstring"read".viewrequiredstring"customers".limitoptionalinteger1–50, default 20.offsetoptionalinteger0–10,000, default 0.
curl -X POST \
https://app.dskr.ai/functions/agentApi \
-H "Authorization: Bearer $DSKR_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "read",
"view": "customers"
}'{
"customers": [
{
"id": "6a0f1e2d3c4b5a6978877665",
"name": "Acme Dental"
}
],
"next_offset": null
}List requesters
https://app.dskr.ai/functions/agentApiLists the people at one customer who can be requesters on a ticket.
Body parameters
| Parameter | Type | Description |
|---|---|---|
actionrequired | string | "read". |
viewrequired | string | "requesters". |
customer_idrequired | string | From List customers. |
limitoptional | integer | 1–50, default 20. |
offsetoptional | integer | 0–10,000, default 0. |
actionrequiredstring"read".viewrequiredstring"requesters".customer_idrequiredstringFrom List customers.limitoptionalinteger1–50, default 20.offsetoptionalinteger0–10,000, default 0.
curl -X POST \
https://app.dskr.ai/functions/agentApi \
-H "Authorization: Bearer $DSKR_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "read",
"view": "requesters",
"customer_id": "6a0f1e2d3c4b5a6978877665"
}'{
"requesters": [
{
"id": "6a3d4e5f60718293a4b5c6d7",
"name": "Jane Doe"
}
],
"next_offset": null
}List assignees
https://app.dskr.ai/functions/agentApiLists team members who can be assigned tickets.
Body parameters
| Parameter | Type | Description |
|---|---|---|
actionrequired | string | "read". |
viewrequired | string | "assignees". |
limitoptional | integer | 1–50, default 20. |
offsetoptional | integer | 0–10,000, default 0. |
actionrequiredstring"read".viewrequiredstring"assignees".limitoptionalinteger1–50, default 20.offsetoptionalinteger0–10,000, default 0.
curl -X POST \
https://app.dskr.ai/functions/agentApi \
-H "Authorization: Bearer $DSKR_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "read",
"view": "assignees"
}'{
"assignees": [
{
"id": "6a4e5f60718293a4b5c6d7e8",
"name": "Ana Ruiz"
}
],
"next_offset": null
}Create a ticket
https://app.dskr.ai/functions/agentApiOpens a new ticket for a requester at one of your customers.
Body parameters
| Parameter | Type | Description |
|---|---|---|
actionrequired | string | "create". |
customer_idrequired | string | From List customers. |
requester_idrequired | string | From List requesters. |
subjectrequired | string | 1–200 characters. |
bodyrequired | string | 1–10,000 characters. |
request_idrequired | UUID | A new UUID for this action. See Idempotency. |
actionrequiredstring"create".customer_idrequiredstringFrom List customers.requester_idrequiredstringFrom List requesters.subjectrequiredstring1–200 characters.bodyrequiredstring1–10,000 characters.request_idrequiredUUIDA new UUID for this action. See Idempotency.
curl -X POST \
https://app.dskr.ai/functions/agentApi \
-H "Authorization: Bearer $DSKR_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "create",
"customer_id": "6a0f1e2d3c4b5a6978877665",
"requester_id": "6a3d4e5f60718293a4b5c6d7",
"subject": "Printer offline",
"body": "Our front desk printer stopped working this morning.",
"request_id": "6f1c2b9e-3a47-4d2e-9b1f-8c0e5a7d2f41"
}'{
"ticket_id": "6a1b2c3d4e5f60718293a4b5",
"ticket_number": 1043
}Reply to a customer
https://app.dskr.ai/functions/agentApiPosts a reply the customer sees and receives by email.
Body parameters
| Parameter | Type | Description |
|---|---|---|
actionrequired | string | "send". |
ticket_idrequired | string | The ticket's id. |
bodyrequired | string | 1–10,000 characters. |
request_idrequired | UUID | A new UUID for this action. See Idempotency. |
actionrequiredstring"send".ticket_idrequiredstringThe ticket's id.bodyrequiredstring1–10,000 characters.request_idrequiredUUIDA new UUID for this action. See Idempotency.
curl -X POST \
https://app.dskr.ai/functions/agentApi \
-H "Authorization: Bearer $DSKR_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "send",
"ticket_id": "6a1b2c3d4e5f60718293a4b5",
"body": "Thanks for reaching out. We'\''re looking into this now.",
"request_id": "0b7e9c2a-5d14-4f3e-a8b6-2c9d1e4f7a30"
}'{
"message_id": "6a5f60718293a4b5c6d7e8f9",
"ticket_id": "6a1b2c3d4e5f60718293a4b5"
}Add an internal note
https://app.dskr.ai/functions/agentApiAdds a note only your team can see. The customer is never notified.
Body parameters
| Parameter | Type | Description |
|---|---|---|
actionrequired | string | "note". |
ticket_idrequired | string | The ticket's id. |
bodyrequired | string | 1–10,000 characters. |
request_idrequired | UUID | A new UUID for this action. See Idempotency. |
actionrequiredstring"note".ticket_idrequiredstringThe ticket's id.bodyrequiredstring1–10,000 characters.request_idrequiredUUIDA new UUID for this action. See Idempotency.
curl -X POST \
https://app.dskr.ai/functions/agentApi \
-H "Authorization: Bearer $DSKR_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "note",
"ticket_id": "6a1b2c3d4e5f60718293a4b5",
"body": "Thanks for reaching out. We'\''re looking into this now.",
"request_id": "7e3a9c1d-2b4f-4e6a-9d8c-1f5b3a7e9c2d"
}'{
"message_id": "6a5f60718293a4b5c6d7e8f9",
"ticket_id": "6a1b2c3d4e5f60718293a4b5"
}Save a draft
https://app.dskr.ai/functions/agentApiSaves a reply for a team member to review and send. It is never sent automatically.
Body parameters
| Parameter | Type | Description |
|---|---|---|
actionrequired | string | "draft". |
ticket_idrequired | string | The ticket's id. |
bodyrequired | string | 1–10,000 characters. |
request_idrequired | UUID | A new UUID for this action. See Idempotency. |
actionrequiredstring"draft".ticket_idrequiredstringThe ticket's id.bodyrequiredstring1–10,000 characters.request_idrequiredUUIDA new UUID for this action. See Idempotency.
curl -X POST \
https://app.dskr.ai/functions/agentApi \
-H "Authorization: Bearer $DSKR_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "draft",
"ticket_id": "6a1b2c3d4e5f60718293a4b5",
"body": "Thanks for reaching out. We'\''re looking into this now.",
"request_id": "5a9b3c7d-4e2f-4a1b-8c6d-3e9f1a5b7c2d"
}'{
"draft_id": "6a60718293a4b5c6d7e8f9a0",
"ticket_id": "6a1b2c3d4e5f60718293a4b5",
"status": "pending",
"message": "Draft saved for a person to review. It has not been sent."
}Change status
https://app.dskr.ai/functions/agentApiSets a ticket to Open, Waiting, Resolved or Closed.
Body parameters
| Parameter | Type | Description |
|---|---|---|
actionrequired | string | "status". |
ticket_idrequired | string | The ticket's id. |
statusrequired | string | Exactly Open, Waiting, Resolved or Closed. |
request_idrequired | UUID | A new UUID for this action. See Idempotency. |
actionrequiredstring"status".ticket_idrequiredstringThe ticket's id.statusrequiredstringExactlyOpen,Waiting,ResolvedorClosed.request_idrequiredUUIDA new UUID for this action. See Idempotency.
ticket_id and status.curl -X POST \
https://app.dskr.ai/functions/agentApi \
-H "Authorization: Bearer $DSKR_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "status",
"ticket_id": "6a1b2c3d4e5f60718293a4b5",
"status": "Resolved",
"request_id": "3c8a1f2e-7b45-4d9c-b0e6-5a2f8d1c9e47"
}'{
"ticket_id": "6a1b2c3d4e5f60718293a4b5",
"status": "Resolved",
"resolved_at": "2026-10-18T14:30:00.000Z",
"closed_at": ""
}Assign a ticket
https://app.dskr.ai/functions/agentApiAssigns a ticket to a team member, or unassigns it.
Body parameters
| Parameter | Type | Description |
|---|---|---|
actionrequired | string | "assign". |
ticket_idrequired | string | The ticket's id. |
assignee_idrequired | string | An id from List assignees, or "" to unassign. |
request_idrequired | UUID | A new UUID for this action. See Idempotency. |
actionrequiredstring"assign".ticket_idrequiredstringThe ticket's id.assignee_idrequiredstringAn id from List assignees, or""to unassign.request_idrequiredUUIDA new UUID for this action. See Idempotency.
curl -X POST \
https://app.dskr.ai/functions/agentApi \
-H "Authorization: Bearer $DSKR_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "assign",
"ticket_id": "6a1b2c3d4e5f60718293a4b5",
"assignee_id": "6a4e5f60718293a4b5c6d7e8",
"request_id": "9d2e4b6a-1c3f-4a5e-8b7d-0f2a4c6e8b1d"
}'{
"ticket_id": "6a1b2c3d4e5f60718293a4b5",
"assigned_to_id": "6a4e5f60718293a4b5c6d7e8"
}Webhooks
Overview
dskr.ai can send an HTTPS POST to your server when something happens. In Settings → AI agents & automation, open a connection, enter your HTTPS address and choose up to 5 events. You'll get an alert secret starting with dskr_evt_, shown once.
Events
| Event | Sent when |
|---|---|
ticket_created | A new ticket is opened |
new_reply | A new reply is added |
status_changed | A ticket's status changes |
assigned | A ticket is assigned |
resolved | A ticket is resolved |
test | You send a test from Settings |
ticket_createdA new ticket is openednew_replyA new reply is addedstatus_changedA ticket's status changesassignedA ticket is assignedresolvedA ticket is resolvedtestYou send a test from Settings
Alerts are sent only for customer-visible tickets.
Payload
Each alert is a POST with these headers and a JSON body.
Message texts and email addresses are never included. The timestamp is Unix time in seconds.
X-Dskr-Event: ticket_created
X-Dskr-Event-Id: 9f2c4a7e1b3d5f60a8c2e4b6d8f0a1c3e5b7d9f1a3c5e7b9d0f2a4c6e8b0d2f4
X-Dskr-Timestamp: 1792332000
X-Dskr-Signature: v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd{
"ticket_number": 1042,
"subject": "Can't log in to my account",
"status": "Open",
"customer_name": "Acme Dental",
"link": "https://app.dskr.ai/helpdesk/6a1b2c3d4e5f60718293a4b5"
}Verify signatures
Always check the signature before trusting an alert:
- 1Join
timestamp,event id,event nameand the raw request body with dots. - 2Compute HMAC-SHA256 of that text with your alert secret, as lowercase hex.
- 3Compare it with the value after
v1=using a constant-time comparison. - 4Reject timestamps older than 5 minutes, and event ids you've already processed.
X-Dskr-Event: ticket_created
X-Dskr-Event-Id: 9f2c4a7e1b3d5f60a8c2e4b6d8f0a1c3e5b7d9f1a3c5e7b9d0f2a4c6e8b0d2f4
X-Dskr-Timestamp: 1792332000
X-Dskr-Signature: v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
{
"ticket_number": 1042,
"subject": "Can't log in to my account",
"status": "Open",
"customer_name": "Acme Dental",
"link": "https://app.dskr.ai/helpdesk/6a1b2c3d4e5f60718293a4b5"
}Retries
Answer with any 2xx status within 10 seconds. Otherwise we retry after 5, 15 and 60 minutes (4 attempts in total). Redirects aren't followed. Retries keep the same X-Dskr-Event-Id, so use it to skip duplicates.
AI assistants (MCP)
Overview
dskr.ai includes an MCP server, so AI assistants can use the same actions as the API, limited to what the connection allows. There are 11 tools: list tickets, get ticket, list customers, list requesters, list assignees, create ticket, reply to customer, add internal note, change status, assign, save draft.
Claude
- 1In Claude, open Settings → Connectors → Add custom connector.
- 2Enter
https://app.dskr.ai/api/mcp - 3Sign in with your dskr.ai team account.
- 4Choose the AI connection to use and click Approve.
https://app.dskr.ai/api/mcpChatGPT
- 1In ChatGPT, open Settings → Apps, turn on Developer mode, then Create app.
- 2Enter
https://app.dskr.ai/api/mcpand choose OAuth. - 3Sign in, choose the AI connection, and approve.
https://app.dskr.ai/api/mcpKey-based clients (n8n, Claude Code, custom agents)
Connect to https://app.dskr.ai/functions/agentMcp as a Streamable HTTP server with the header Authorization: Bearer dskr_agent_your_key_here. Only the tools your connection allows are listed.
https://app.dskr.ai/functions/agentMcp
Authorization: Bearer dskr_agent_your_key_hereResources
Security
- Each key works for one company only, and only for the actions you tick.
- Keys are stored hashed and shown only once. If a key leaks, delete the connection and create a new one.
- Connections can never read internal notes, change settings, users or keys, or edit archived tickets.
- Every action is logged in the connection's activity in Settings.
Changelog
- October 2026: API, webhooks and MCP server launched.
Support
Questions? Email dskr@email.dskr.ai.