Support tickets
Support tickets
Operator support connects human operator staff with Polynion Support. Every endpoint requires an active staff-session bearer token; no extra resource scope is required. An API key cannot call these routes, even with *. There is no support:read or support:write scope to grant.
Tickets are shared within the authenticated operator, not private to the creating staff member. The server derives operator and actor identity; operatorId or operator_id query parameters are rejected. Do not send credentials, launch tokens, raw wallet headers, or other secrets in subjects/messages.
| Method and path | Request | Response |
|---|---|---|
GET /api/operator/support-tickets | Optional list query below. | { ok: true, tickets, total, page, pageSize, totalPages } |
POST /api/operator/support-tickets | JSON { category, subject, body }. | { ok: true, ticketId } |
GET /api/operator/support-tickets/{id} | Positive canonical decimal safe-integer ticket ID; no body. | { ok: true, ticket } |
POST /api/operator/support-tickets/{id}/messages | JSON { body }. | { ok: true } |
POST /api/operator/support-tickets/{id}/reopen | No body or an empty JSON object {}. | { ok: true } |
Operator routes do not provide close, delete, category/subject edit, or attachment-upload operations. Closing is a Polynion Support action. Reopening an already-open owned ticket succeeds without creating another status-change event.
List filters
| Parameter | Values/default |
|---|---|
status | Optional open or closed; omission includes both. |
search | Optional trimmed text, maximum 200 characters; case-insensitive literal substring of subject, ticket ID text, or the authenticated operator's name/slug. It does not search message bodies. |
page | Positive canonical decimal safe integer, default 1. |
pageSize | Positive canonical decimal integer, default 20, maximum 100; camelCase, not page_size. |
Unknown query keys and invalid values return 400 INVALID_INPUT; values over the page-size maximum are rejected, not capped. Pagination offset must also be a safe integer. Results are ordered by lastMessageAt descending, then ID descending; totalPages is at least 1.
Create and reply
{
"category": "operations",
"subject": "Unknown wallet credit for redemption item 123",
"body": "Please review operation 456. Our ledger reference is redacted-reference; no second credit has been issued."
}
| Field | Required contract |
|---|---|
category | technical, billing, operations, or other. Required only on create. |
subject | Trimmed, nonempty text, maximum 255 characters. Required only on create. |
body | Trimmed, nonempty text, maximum 20000 characters. Required on create and reply. |
Bodies are strict objects; unknown fields are rejected. Creation stores the ticket as open, the first operator message, and an open status event. The API does not return a notification-delivery receipt or a response-time guarantee.
A reply does not reopen a closed ticket: it returns 409 TICKET_CLOSED. Reopen first, then send the message. There is no creation/reply idempotency-key field; after an ambiguous response, inspect the list/detail before retrying to avoid duplicate tickets or messages.
Response objects
List ticket objects and detail use camelCase:
| Fields | Type/meaning |
|---|---|
id, operatorId, createdByAdminUserId | Numeric identities. |
operatorName, operatorSlug | Operator display identity. |
category, subject, status | Category enum, subject text, and open/closed. |
createdAt, updatedAt, lastMessageAt | ISO timestamps. |
lastOperatorReplyAt, lastAdminReplyAt, closedAt | Nullable ISO timestamps. |
Detail adds:
messages:{ id, actorType, actorName, body, createdAt };events:{ id, actorType, actorName, adminUserId, status, createdAt }.
actorType is operator or admin; actorName is nullable. Messages and status events are ordered oldest first by timestamp, then ID, and are not separately paginated. Render message text as text, not executable HTML. A ticket is an operational conversation, not authorization to issue a new payment or override cash-flow reconciliation.
Errors
400 INVALID_INPUT: invalid ID, schema, filters, unknown fields, or operator selectors.403 STAFF_SESSION_REQUIRED: API-key authentication is not supported.404 TICKET_NOT_FOUND: missing ticket, wrong operator, or actor no longer eligible for a mutation.409 TICKET_CLOSED: reply attempted on a closed ticket; reopen before replying.
Standard bearer authentication errors also apply. A ticket ID is not a public access link; read and mutation requests must remain authenticated.
