API Documentation

Integrate TacoHelp into your apps with our REST API. Available on Pro plans and above.

Authentication

All API requests require a Bearer token. Create an API key in Settings → API Keys after signing in.

curl -H "Authorization: Bearer th_your_api_key" \ https://tacohelp.com/api/external/tickets

Key format: th_ prefix + 32 bytes base64url

Scopes: tickets:read, tickets:write, chat:read, chat:write, or *

Rate Limiting

100 requests per 60-second sliding window per API key. Rate limit info is returned in response headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1710648000

Webhooks

Register webhook endpoints in Settings → Webhooks to receive real-time events. Payloads are signed with HMAC-SHA256.

Events: ticket.created, ticket.updated, ticket.commented, ticket.resolved, ticket.closed, ticket.reopened, ticket.merged

Signature header: X-Webhook-Signature

Retry policy: 3 attempts with exponential backoff (2s, 8s, 32s)

Circuit breaker: Endpoint disabled after 10 consecutive failures

{ "event": "ticket.created", "timestamp": "2026-03-17T12:00:00.000Z", "data": { "ticket": { "id": "clx...", "ticketNumber": 42, ... } } }

Endpoints

GET/api/external/ticketsScope: tickets:read

List tickets for your organization

Query Parameters

statusstringFilter by status (OPEN, IN_PROGRESS, WAITING, RESOLVED, CLOSED)
limitnumberResults per page (default: 50, max: 100)
offsetnumberPagination offset (default: 0)

Response

{
  "tickets": [
    {
      "id": "clx...",
      "ticketNumber": 42,
      "subject": "Login page bug",
      "status": "OPEN",
      "priority": "HIGH",
      "tags": ["bug", "login"],
      "createdAt": "2026-03-17T...",
      "createdBy": { "name": "Sam", "email": "sam@example.com" },
      "assignee": { "name": "Jordan", "email": "dev@example.com" }
    }
  ],
  "total": 1,
  "limit": 50,
  "offset": 0
}
POST/api/external/ticketsScope: tickets:write

Create a new ticket

Request Body

{
  "subject": "Login page shows blank screen",
  "description": "When I try to log in using Safari...",
  "priority": "HIGH",
  "createdByEmail": "client@example.com",
  "tags": ["bug", "safari"]
}

Response

{
  "id": "clx...",
  "ticketNumber": 43,
  "subject": "Login page shows blank screen",
  "status": "OPEN",
  "priority": "HIGH",
  "source": "API",
  ...
}
GET/api/external/tickets/:ticketIdScope: tickets:read

Get a ticket with comments

Response

{
  "id": "clx...",
  "ticketNumber": 42,
  "subject": "Login page bug",
  "description": "Full description...",
  "comments": [
    {
      "id": "clx...",
      "content": "Thanks for reporting...",
      "author": { "name": "Jordan" },
      "createdAt": "2026-03-17T..."
    }
  ],
  ...
}
PATCH/api/external/tickets/:ticketIdScope: tickets:write

Update a ticket

Request Body

{
  "status": "IN_PROGRESS",
  "priority": "URGENT",
  "assigneeId": "clx...",
  "tags": ["bug", "urgent"]
}

Response

{ "id": "clx...", "status": "IN_PROGRESS", ... }
POST/api/external/tickets/:ticketId/commentsScope: tickets:write

Add a comment to a ticket

Request Body

{
  "content": "We've identified the issue...",
  "authorEmail": "dev@example.com"
}

Response

{
  "id": "clx...",
  "content": "We've identified the issue...",
  "author": { "name": "dev", "email": "dev@example.com" },
  "createdAt": "2026-03-17T..."
}

Chat API

GET/api/external/chat/conversationsScope: chat:read

List chat conversations for your organization

Query Parameters

statusstringFilter by status (active, ended)
limitnumberResults per page (default: 50, max: 100)
offsetnumberPagination offset (default: 0)

Response

{
  "conversations": [
    {
      "id": "clx...",
      "status": "active",
      "visitorName": "Sam",
      "visitorEmail": "sam@example.com",
      "createdAt": "2026-03-17T...",
      "updatedAt": "2026-03-17T..."
    }
  ],
  "total": 1,
  "limit": 50,
  "offset": 0
}
POST/api/external/chat/conversationsScope: chat:write

Start a new chat conversation

Request Body

{
  "visitorName": "Sam",
  "visitorEmail": "sam@example.com"
}

Response

{
  "id": "clx...",
  "status": "active",
  "visitorName": "Sam",
  "visitorEmail": "sam@example.com",
  "createdAt": "2026-03-17T..."
}
GET/api/external/chat/conversations/:conversationIdScope: chat:read

Get a conversation with its messages

Response

{
  "id": "clx...",
  "status": "active",
  "visitorName": "Sam",
  "visitorEmail": "sam@example.com",
  "messages": [
    {
      "id": "clx...",
      "senderType": "visitor",
      "content": "Hi, I need help with...",
      "createdAt": "2026-03-17T..."
    },
    {
      "id": "clx...",
      "senderType": "agent",
      "senderId": "clx...",
      "content": "Sure, let me look into that.",
      "createdAt": "2026-03-17T..."
    }
  ],
  "createdAt": "2026-03-17T...",
  "updatedAt": "2026-03-17T..."
}
POST/api/external/chat/conversations/:conversationId/messagesScope: chat:write

Send a message in a conversation

Request Body

{
  "content": "Hi, I need help with my account.",
  "senderType": "visitor"
}

Response

{
  "id": "clx...",
  "senderType": "visitor",
  "content": "Hi, I need help with my account.",
  "createdAt": "2026-03-17T..."
}
PATCH/api/external/chat/conversations/:conversationIdScope: chat:write

End a conversation

Request Body

{
  "status": "ended"
}

Response

{
  "id": "clx...",
  "status": "ended",
  "updatedAt": "2026-03-17T..."
}

Error Responses

400Bad request — missing or invalid parameters
401Unauthorized — invalid or missing API key
403Forbidden — insufficient scope
404Not found — resource doesn't exist in your org
429Rate limit exceeded — wait and retry
500Server error — contact support
{ "error": "Invalid API key" }