API Reference

Tickets API

Create and manage support tickets via the API

The Tickets API allows you to create, retrieve, update, and list support tickets programmatically. Request bodies reject fields that are not listed for the relevant endpoint rather than silently ignoring them.

Endpoints

MethodEndpointDescription
GET/api/v1/ticketsList tickets
POST/api/v1/ticketsCreate a ticket
GET/api/v1/tickets/:idGet a ticket
PATCH/api/v1/tickets/:idUpdate a ticket
GET/api/v1/tickets/:id/messagesList messages
POST/api/v1/tickets/:id/messagesAdd a message

List Tickets

Retrieve a paginated list of tickets.

GET /api/v1/tickets

Query Parameters

ParameterTypeDescription
pagepositive integerPage number (default: 1)
per_pagepositive integerItems per page (max: 100, default: 20)
statusstringFilter by status: open, pending, escalated, solved, closed
workspace_idstringMust match the workspace bound to the API key

Example Request

curl "https://app.keva.support/api/v1/tickets?status=open&per_page=10" \
  -H "Authorization: Bearer keva_live_your_api_key"

Example Response

{
  "data": [
    {
      "id": "0f8fad5b-d9cb-469f-a165-70867728950e",
      "subject": "Cannot access my account",
      "status": "open",
      "priority": "high",
      "customerEmail": "customer@example.com",
      "customerName": "John Doe",
      "source": "email",
      "assignedTo": null,
      "resolutionType": null,
      "tags": ["login", "urgent"],
      "createdAt": "2024-01-15T10:30:00Z",
      "updatedAt": "2024-01-15T10:30:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "perPage": 10,
    "total": 45
  }
}

Ticket responses are limited to the fields shown above. Internal workspace custody, provider identifiers, confirmation state, presence, and channel-thread metadata are not part of the public response contract.

Create Ticket

Create a new support ticket.

POST /api/v1/tickets

Request Body

The complete JSON request body is limited to 1 MiB (1,048,576 bytes). Requests over this limit return 413 Payload Too Large.

FieldTypeRequiredDescription
subjectstringYesTicket subject line (1-500 characters)
customerEmailstringYesCustomer's email address (maximum 320 characters)
customerNamestringNoCustomer's name (max 200 characters)
bodystringYesInitial message content (1-100,000 characters)
sourcestringNoemail, widget, or api (default: api)
languagestringNoTwo- or three-letter lowercase language code, such as en

Example Request

curl https://app.keva.support/api/v1/tickets \
  -H "Authorization: Bearer keva_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "subject": "Order not delivered",
    "customerEmail": "customer@example.com",
    "customerName": "Jane Smith",
    "body": "My order #12345 was supposed to arrive yesterday but I havent received it yet.",
    "language": "en"
  }'

Example Response

{
  "data": {
    "id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "subject": "Order not delivered",
    "status": "open",
    "priority": "medium",
    "customerEmail": "customer@example.com",
    "customerName": "Jane Smith",
    "source": "api",
    "createdAt": "2024-01-15T14:22:00Z"
  }
}

Get Ticket

Retrieve a single ticket by ID.

GET /api/v1/tickets/:id

Example Request

curl https://app.keva.support/api/v1/tickets/0f8fad5b-d9cb-469f-a165-70867728950e \
  -H "Authorization: Bearer keva_live_your_api_key"

Update Ticket

Update ticket properties.

PATCH /api/v1/tickets/:id

Request Body

The complete JSON request body is limited to 1 MiB (1,048,576 bytes). Requests over this limit return 413 Payload Too Large.

FieldTypeDescription
statusstringopen, pending, escalated, solved, closed
prioritystringlow, medium, high, urgent
assignedTostringUser ID to assign (max 200 characters); must be a current member or admin in the API key's exact workspace. The ai sentinel is also allowed. Blank, whitespace-only, and null values are invalid.

At least one supported field must be present in an update request.

Assigning a human user is limited to current members and admins of the exact workspace bound to the API key. Blank, whitespace-only, or null assignees are malformed and return 400; a syntactically valid human ID that is not a current member or admin of that exact workspace returns 403.

Example Request

curl -X PATCH https://app.keva.support/api/v1/tickets/0f8fad5b-d9cb-469f-a165-70867728950e \
  -H "Authorization: Bearer keva_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"status": "pending", "priority": "high"}'

List Messages

Get messages for a ticket. By default, the newest 100 messages are returned; limit may be set from 1 to 200. Results are returned oldest-first within the selected window. The response includes total (the true count) and truncated (whether older messages were omitted). Use before with a message id or ISO timestamp to select an older window; a message-id cursor must belong to this ticket, otherwise the API returns 400.

GET /api/v1/tickets/:id/messages?limit=100&before=2024-01-15T10:30:00Z

Example Response

{
  "data": [
    {
      "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "ticketId": "0f8fad5b-d9cb-469f-a165-70867728950e",
      "direction": "inbound",
      "senderEmail": "customer@example.com",
      "senderName": "John Doe",
      "body": "I need help with my order",
      "bodyHtml": null,
      "createdAt": "2024-01-15T10:30:00Z"
    },
    {
      "id": "7c9e6679-7425-40de-944b-e07fc1f90ae8",
      "ticketId": "0f8fad5b-d9cb-469f-a165-70867728950e",
      "direction": "outbound",
      "senderEmail": "support@company.com",
      "senderName": null,
      "body": "Happy to help! Let me look into this.",
      "bodyHtml": null,
      "createdAt": "2024-01-15T10:31:00Z"
    }
  ],
  "total": 2,
  "truncated": false
}

An invalid limit or malformed/out-of-range before returns 400.

Message responses are limited to the fields shown above. Provider IDs and email-thread headers are not returned by the public API.

Add Message

Add a message to an existing ticket.

POST /api/v1/tickets/:id/messages

Request Body

FieldTypeRequiredDescription
bodystringYesMessage content (1-100,000 characters)
senderEmailstringYesSender's email (maximum 320 characters)
senderNamestringNoSender's name (max 200 characters)
directionstringNoinbound or outbound (default: inbound)
bodyHtmlstring or nullNoHTML version of message (max 200,000 characters)

The full JSON request body is limited to 2 MiB (2,097,152 bytes). Requests that exceed this limit return 413 Payload Too Large.

Inbound messages are the supported public write path. Direct public outbound delivery remains unavailable; a request with direction: "outbound" returns 409 Conflict until it is routed through approval and delivery custody.

An inbound message counts as a customer reply only when senderEmail matches the ticket's customer email (ignoring case and surrounding spaces). A matching inbound sender emits the registered TICKET_MESSAGE_ADDED acceptance event. That event may start configured follow-up/agent/workflow handling and downstream reply or approval behavior, including "message received" workflows and reopening a pending or solved ticket. A mismatched sender emits no event; the message is saved on the ticket but starts nothing. The 201 Created response says which happened in meta.messageAddedEvent:

ValueMeaning
emittedThe message was treated as a customer reply
skipped_sender_mismatchsenderEmail did not match the ticket's customer; the message was saved only

Example Request

curl -X POST https://app.keva.support/api/v1/tickets/0f8fad5b-d9cb-469f-a165-70867728950e/messages \
  -H "Authorization: Bearer keva_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "Any update on this?",
    "senderEmail": "customer@example.com",
    "direction": "inbound"
  }'