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.
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:
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
Embeddable Widget
One script tag adds a floating help button with live chat and a ticket form to any site. No API key is exposed in the browser. Get your snippet and widget secret in Settings → Widget.
data-app labels chats and tickets by product so one help desk can serve many apps.
data-color, data-position ("left"), data-hide-launcher (use your own button with TacoHelp.open()).
JS API: TacoHelp.open("chat"|"ticket"), close(), toggle(), identify(identity), onUnread(fn).
Verified identity (signed-in users)
Sign the user on your server with the widget secret (HMAC-SHA256 over externalId\nemail\nname\nexp, email lowercased) and pass the result as data-identity. Visitors skip the name/email form and agents see a verified badge. A visitor cannot forge another user without the secret.
Requesters are contacts. Tickets created by the widget, the public form, email, or POST /api/external/tickets (createdByEmail, optional createdByName, sourceApp, sendReceipt:false) belong to an org-scoped Contact, never a login. Responses still return createdBy / author with isContact: true.
Email threads. Requesters get a receipt and every reply by email, branded with your org name and color; replying to those emails adds a comment to the ticket. Mail to <org>@reply.tacohelp.com opens a ticket.
Public endpoints used by the widget (no API key; per-IP rate limited; every follow-up call needs the X-Visitor-Token returned by start):
POST /api/chat/:org/start, GET|POST /api/chat/:org/messages, POST /api/chat/:org/typing, POST /api/chat/:org/attachments, POST /api/chat/:org/end, POST /api/chat/:org/satisfaction, GET /api/chat/:org/transcript, GET /api/chat/:org/availability, POST /api/tickets/public
Endpoints
/api/external/ticketsScope: tickets:readList 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
}/api/external/ticketsScope: tickets:writeCreate 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",
...
}/api/external/tickets/:ticketIdScope: tickets:readGet 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..."
}
],
...
}/api/external/tickets/:ticketIdScope: tickets:writeUpdate a ticket
Request Body
{
"status": "IN_PROGRESS",
"priority": "URGENT",
"assigneeId": "clx...",
"tags": ["bug", "urgent"]
}Response
{ "id": "clx...", "status": "IN_PROGRESS", ... }/api/external/tickets/:ticketId/commentsScope: tickets:writeAdd 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
/api/external/chat/conversationsScope: chat:readList 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
}/api/external/chat/conversationsScope: chat:writeStart 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..."
}/api/external/chat/conversations/:conversationIdScope: chat:readGet 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..."
}/api/external/chat/conversations/:conversationId/messagesScope: chat:writeSend 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..."
}/api/external/chat/conversations/:conversationIdScope: chat:writeEnd a conversation
Request Body
{
"status": "ended"
}Response
{
"id": "clx...",
"status": "ended",
"updatedAt": "2026-03-17T..."
}Error Responses
400Bad request — missing or invalid parameters401Unauthorized — invalid or missing API key403Forbidden — insufficient scope404Not found — resource doesn't exist in your org429Rate limit exceeded — wait and retry500Server error — contact support