Docs
Dashboard

Support

Create and manage support tickets, browse the knowledge base, and run live chat conversations. The Support API exposes the primitives that back the merchant, consumer, and developer help experiences: ticketing, published help articles, and multi-channel chat threads.

Authentication

Every Support endpoint requires a valid JWT or API key. Send it as Authorization: Bearer <token>. The authenticated user becomes the requester on tickets and a participant on chat conversations they create. The base URL for all requests is https://gullypay.xyz/api.

Endpoints

POST/support/ticketsOpen a new support ticket (auth required)
GET/support/ticketsList the authenticated user's tickets (auth required)
GET/support/tickets/:idGet a ticket with its message thread (auth required)
PATCH/support/tickets/:idUpdate ticket status, priority, or assignee (auth required)
POST/support/tickets/:id/messagesAdd a message to a ticket (auth required)
POST/support/tickets/:id/assignAssign or unassign a ticket (admin only)
GET/support/agentsList assignable support staff (auth required)
POST/support/tickets/:id/csatSubmit a CSAT rating for a resolved ticket (auth required)
GET/support/csat/summaryCSAT average and distribution for your tickets (auth required)
GET/support/articlesList published knowledge-base articles (auth required)
GET/support/chatList chat conversations with last-message summary (auth required)
POST/support/chatStart a chat conversation (auth required)
POST/support/chat/:conversationId/messagesSend a chat message (auth required)
GET/support/chat/:conversationId/messagesList messages in a conversation (auth required)
What this API covers today

This API provides ticketing (with priority-based SLA tracking, deterministic type-based auto-routing, and manual assignment), CSAT ratings, a knowledge base, and chat. On creation a ticket is routed to a queue by its type and assigned to the least-loaded active staff member; every listed/fetched ticket carries a live slaStatus computed from its timestamps. The AI support agent and omnichannel unified-inbox behaviors described in the platform overview are not yet exposed as separate endpoints — chat messages are stored and retrieved exactly as posted.


Open a Ticket

POST/support/ticketsOpen a new support ticket
Request Body
{
"subject": "Payout was not received",
"type": "payout_issue",
"priority": "high",
"body": "My scheduled payout for June has not arrived.",
"merchantId": "merch_abc123"
}
FieldTypeRequiredDescription
subjectstringYesTicket subject (max 500 chars)
typestringYesTicket category, e.g. payment_issue, payout_issue, technical_support, api_support
prioritystringNoOne of low, normal, high, urgent (default: normal)
bodystringYesFirst message body — stored as the opening message on the ticket
merchantIdstringNoAssociate the ticket with a merchant

The ticket is created with the authenticated user as requester, and body is inserted as the first message in the thread. The response is the created ticket row.

Response
{
"id": "tkt_9f8e7d6c",
"requesterId": "usr_abc123",
"merchantId": "merch_abc123",
"subject": "Payout was not received",
"type": "payout_issue",
"priority": "high",
"status": "open",
"assigneeId": null,
"createdAt": "2026-06-09T12:00:00Z",
"updatedAt": "2026-06-09T12:00:00Z"
}

Get a Ticket

GET/support/tickets/:idGet a ticket with its message thread

Returns the ticket fields plus a messages array of every message on the thread, in insertion order. Every ticket (from Get and List) is enriched with a resolved assignee object (or null) and a live slaStatus computed from the ticket's timestamps and priority target. slaStatus.state is the worst of the first-response and resolution metrics: on_track, at_risk, breached, or met.

Response
{
"id": "tkt_9f8e7d6c",
"requesterId": "usr_abc123",
"subject": "Payout was not received",
"type": "payout_issue",
"priority": "high",
"status": "open",
"assigneeId": "usr_agent456",
"assignee": { "id": "usr_agent456", "name": "Dana Ops", "email": "[email protected]" },
"slaStatus": {
  "priority": "high",
  "state": "on_track",
  "firstResponse": { "dueAt": "2026-06-09T13:00:00Z", "completedAt": null, "state": "on_track", "minutesRemaining": 48 },
  "resolution": { "dueAt": "2026-06-09T16:00:00Z", "completedAt": null, "state": "on_track", "minutesRemaining": 228 }
},
"messages": [
  {
    "id": "msg_1",
    "ticketId": "tkt_9f8e7d6c",
    "senderId": "usr_abc123",
    "body": "My scheduled payout for June has not arrived.",
    "isInternal": 0,
    "createdAt": "2026-06-09T12:00:00Z"
  }
]
}

Update a Ticket

PATCH/support/tickets/:idUpdate ticket status, priority, or assignee
Request Body
{
"status": "in_progress",
"assigneeId": "usr_agent456",
"priority": "urgent"
}
FieldTypeRequiredDescription
statusstringNoOne of open, pending, in_progress, resolved, closed
assigneeIdstringNoUser ID of the agent handling the ticket
prioritystringNoOne of low, normal, high, urgent

Changing status records a status_changed audit event against the ticket. The response is the updated ticket including its messages thread (same shape as Get a Ticket).

Add a Message

POST/support/tickets/:id/messagesAdd a message to a ticket
Request Body
{
"body": "Thanks — we are investigating and will update you shortly.",
"isInternal": false
}
FieldTypeRequiredDescription
bodystringYesMessage body
isInternalbooleanNoWhen true, the message is stored as an internal (agent-only) note

The response is the created message row.


Assignment & Routing

Tickets auto-route on creation: the ticket's type selects a queue (payments, trust & safety, engineering, or general) and it is assigned to the least-loaded active staff member in the platform. Admins can reassign at any time.

List Agents

GET/support/agentsList assignable support staff

Returns active users with a platform role other than user, ordered by email. Use this to populate an assignee picker.

Response
[
{ "id": "usr_agent456", "name": "Dana Ops", "email": "[email protected]" }
]

Assign a Ticket

POST/support/tickets/:id/assignAssign or unassign a ticket (admin only)

Requires the admin platform role. Pass an assigneeId to assign, or null/omit to unassign. The change records an assigned audit event and the response is the updated ticket (same enriched shape as Get a Ticket).

Request Body
{
"assigneeId": "usr_agent456"
}
FieldTypeRequiredDescription
assigneeIdstring | nullNoUser to assign the ticket to; null or omitted unassigns

Customer Satisfaction (CSAT)

Requesters can rate a ticket once it is resolved or closed. Each ticket accepts exactly one rating.

Submit a Rating

POST/support/tickets/:id/csatSubmit a CSAT rating for a resolved ticket

Returns 400 if the ticket is not yet resolved/closed, or if it has already been rated. The response is the created rating row.

Request Body
{
"score": 5,
"comment": "Resolved quickly, thank you."
}
FieldTypeRequiredDescription
scoreintegerYesRating from 1 (very dissatisfied) to 5 (very satisfied)
commentstringNoOptional free-text feedback (max 2000 chars)

CSAT Summary

GET/support/csat/summaryCSAT average and distribution for your tickets

Aggregates ratings across the authenticated user's tickets (same scope as List Tickets). average is null when there are no ratings yet.

Response
{
"count": 12,
"average": 4.58,
"distribution": { "1": 0, "2": 1, "3": 0, "4": 3, "5": 8 }
}

Knowledge Base

GET/support/articlesList published knowledge-base articles

Returns published knowledge-base articles. Pass ?category=<slug> to filter by category; without a category, only articles with status = published are returned.

Request
curl https://gullypay.xyz/api/support/articles?category=payouts \
-H "Authorization: Bearer YOUR_TOKEN"
QueryTypeRequiredDescription
categorystringNoFilter articles by category slug

Live Chat

Chat conversations are threads with one or more participants over a channel (web, mobile, etc.).

List Conversations

GET/support/chatList chat conversations with last-message summary

Returns conversations ordered by most recent activity (latest message, falling back to when the conversation started). Pass ?status=<status> to filter. Each row includes a last-message preview and message count.

Response
[
{
  "id": "cnv_abc123",
  "channel": "web",
  "participantIds": ["usr_abc123"],
  "status": "open",
  "startedAt": "2026-06-09T11:50:00Z",
  "endedAt": null,
  "lastMessageAt": "2026-06-09T12:01:00Z",
  "lastMessagePreview": "How can I help?",
  "messageCount": 3
}
]
QueryTypeRequiredDescription
statusstringNoFilter by conversation status

Start a Conversation

POST/support/chatStart a chat conversation
Request Body
{
"channel": "web"
}
FieldTypeRequiredDescription
channelstringNoChannel the conversation originates from (default: web)

The authenticated user is added as the first participant. The response is the created conversation row.

Send a Message

POST/support/chat/:conversationId/messagesSend a chat message
Request Body
{
"body": "I need help with a refund."
}
FieldTypeRequiredDescription
bodystringYesMessage body

List Messages

GET/support/chat/:conversationId/messagesList messages in a conversation

Returns every message in the conversation in insertion order.

Response
[
{
  "id": "cmsg_1",
  "conversationId": "cnv_abc123",
  "senderId": "usr_abc123",
  "body": "I need help with a refund.",
  "createdAt": "2026-06-09T12:00:30Z"
}
]

Next Steps