Player Support
All operations use an allowed documentId and require Authorization: Bearer <sessionId>. Internal processing and Omnidesk share the same public contract.
Support portal
playerSupportPortal
Returns published form rules, categories, the selected processing provider, and the player's current write access.
Input
This operation has no variables.
Result: PlayerSupportPortal!
{
"documentId": "playerSupportPortal",
"variables": {}
}
{
"data": {
"playerSupportPortal": {
"enabled": true,
"provider": "INTERNAL",
"minimumTitleLength": 5,
"maximumTitleLength": 160,
"minimumMessageLength": 10,
"maximumMessageLength": 5000,
"attachmentsEnabled": true,
"maxFileSizeMb": 10,
"maxFiles": 5,
"maxTotalSizeMb": 25,
"allowedMimeTypes": [
"image/jpeg",
"image/png",
"image/webp"
],
"writeAccess": {
"allowed": true,
"reason": null,
"expiresAt": null
},
"categories": [
{
"id": "1",
"name": "Payments",
"icon": "ti ti-credit-card"
},
{
"id": "2",
"name": "Game account",
"icon": "ti ti-user"
}
]
}
}
}
Ticket list
playerSupportTickets
Returns the current player's tickets and an independent summary. Pass a pageInfo cursor to continue.
Input
| Field | Type | Required | Description |
|---|---|---|---|
cursor | String | - | Cursor for the next or previous slice. |
limit | Int | - | Slice size; defaults to 20. |
Result: PlayerSupportTicketConnection! and PlayerSupportTicketSummary!
{
"documentId": "playerSupportTickets",
"variables": {
"cursor": null,
"limit": 20
}
}
{
"data": {
"playerSupportTickets": {
"items": [
{
"id": "1042",
"title": "Payment completed but balance did not change",
"categoryId": 1,
"status": "WAITING_FOR_STAFF",
"messagesCount": 1,
"unreadCount": 0,
"lastMessage": "I attached the receipt.",
"createdAt": "2026-08-08T10:24:15Z",
"updatedAt": "2026-08-08T10:24:15Z"
}
],
"pageInfo": {
"nextCursor": "eyJpZCI6MTA0Mn0",
"previousCursor": null,
"hasMore": true
}
},
"playerSupportTicketSummary": {
"all": 3,
"waitingForPlayer": 1,
"waitingForStaff": 1,
"closed": 1
}
}
}
Ticket and messages
playerSupportTicket
Returns one of the current player's tickets and its cursor-based message history.
Input
| Field | Type | Required | Description |
|---|---|---|---|
ticketId | ID! | ✓ | Ticket ID. |
cursor | String | - | Message history cursor. |
limit | Int | - | Slice size; defaults to 50. |
Result: PlayerSupportTicket or null
{
"documentId": "playerSupportTicket",
"variables": {
"ticketId": "1042",
"cursor": null,
"limit": 50
}
}
{
"data": {
"playerSupportTicket": {
"id": "1042",
"title": "Payment completed but balance did not change",
"categoryId": 1,
"status": "WAITING_FOR_PLAYER",
"messages": {
"items": [
{
"id": "9101",
"content": "We verified the payment and updated the balance.",
"author": "STAFF",
"authorLabel": "Support team",
"redacted": false,
"deliveryState": "DELIVERED",
"feedback": null,
"feedbackAllowed": true,
"attachments": [
{
"url": "https://project.example/support/attachments/9101/original?token=short-lived",
"thumbnailUrl": "https://project.example/support/attachments/9101/thumbnail?token=short-lived"
}
],
"createdAt": "2026-08-08T11:02:41Z"
}
],
"pageInfo": {
"nextCursor": null,
"previousCursor": null,
"hasMore": false
}
},
"messagesCount": 2,
"unreadCount": 1,
"lastMessage": "We verified the payment and updated the balance.",
"createdAt": "2026-08-08T10:24:15Z",
"updatedAt": "2026-08-08T11:02:41Z"
}
}
}
Ticket access check
playerSupportTicketAccess
Checks whether the current session can read a ticket before a protected resource is served.
Input
| Field | Type | Required | Description |
|---|---|---|---|
ticketId | ID! | ✓ | Ticket ID. |
Result: Boolean!
{
"documentId": "playerSupportTicketAccess",
"variables": {
"ticketId": "1042"
}
}
{
"data": {
"playerSupportTicketAccess": true
}
}
Message access check
playerSupportMessageAccess
Checks access to a message before opening one of its attachments.
Input
| Field | Type | Required | Description |
|---|---|---|---|
messageId | ID! | ✓ | Message ID. |
Result: Boolean!
{
"documentId": "playerSupportMessageAccess",
"variables": {
"messageId": "9101"
}
}
{
"data": {
"playerSupportMessageAccess": true
}
}
Create a ticket
createPlayerSupportTicket
Idempotently creates a ticket and its first message. A new user action gets a new requestId; a safe retry of the same request reuses it.
Input
| Field | Type | Required | Description |
|---|---|---|---|
input.requestId | ID! | ✓ | Unique attempt ID. |
input.categoryId | Int! | ✓ | Published category ID. |
input.title | String! | ✓ | Title within portal limits. |
input.message | String! | ✓ | Initial message. |
input.account | String | - | Game account. |
input.character | String | - | Character. |
input.attachmentClaim | PlayerSupportAttachmentClaimInput | - | ID and credential of a previously uploaded file claim. |
Result: PlayerSupportMutationResult!
{
"documentId": "createPlayerSupportTicket",
"variables": {
"input": {
"requestId": "3a124288-6ced-4a58-9956-7ed994c419a7",
"categoryId": 1,
"title": "Payment completed but balance did not change",
"message": "I attached the receipt and transaction number.",
"account": "demo_account",
"character": "Aster",
"attachmentClaim": {
"id": "01J4Q6N8WQ4G4Q5S8YQJ6R7M2D",
"credential": "claim-capability"
}
}
}
}
{
"data": {
"createPlayerSupportTicket": {
"message": "Ticket created.",
"ticketId": "1042",
"messageId": "9100",
"deliveryPending": false
}
}
}
Reply to a ticket
replyPlayerSupportTicket
Idempotently appends a player message to an accessible, open ticket.
Input
| Field | Type | Required | Description |
|---|---|---|---|
input.requestId | ID! | ✓ | Unique attempt ID. |
input.ticketId | ID! | ✓ | Current player's ticket ID. |
input.message | String! | ✓ | Reply text. |
input.attachmentClaim | PlayerSupportAttachmentClaimInput | - | Optional claim for uploaded files. |
Result: PlayerSupportMutationResult!
{
"documentId": "replyPlayerSupportTicket",
"variables": {
"input": {
"requestId": "e8c00d5d-f727-4b7c-96a4-413bd8d6207c",
"ticketId": "1042",
"message": "The balance is updated, thank you.",
"attachmentClaim": null
}
}
}
{
"data": {
"replyPlayerSupportTicket": {
"message": "Reply sent.",
"ticketId": "1042",
"messageId": "9102",
"deliveryPending": false
}
}
}
Rate a staff response
ratePlayerSupportMessage
Stores feedback for one staff response in the current player's ticket. Only a delivered, non-redacted staff response can be rated. Repeating the same selection creates no duplicate, while choosing the other option replaces the previous value.
Input
| Field | Type | Required | Description |
|---|---|---|---|
input.messageId | ID! | ✓ | Accessible staff-message ID. |
input.feedback | PlayerSupportMessageFeedback! | ✓ | HELPFUL or NOT_HELPFUL. |
Result: PlayerSupportMessageFeedbackResult!
{
"documentId": "ratePlayerSupportMessage",
"variables": {
"input": {
"messageId": "9101",
"feedback": "HELPFUL"
}
}
}
{
"data": {
"ratePlayerSupportMessage": {
"messageId": "9101",
"feedback": "HELPFUL"
}
}
}
Public errors
Errors use extensions.category = support and a client-safe extensions.code.
| Code | Meaning |
|---|---|
SUPPORT_DISABLED | The feature is disabled for the project |
BANNED | The player cannot create tickets or replies |
RATE_LIMITED | The configured cooldown has not elapsed |
INVALID_CATEGORY | The category is disabled or missing |
TITLE_LENGTH / MESSAGE_LENGTH | Published content limits were violated |
TICKET_NOT_FOUND | The ticket is missing or inaccessible |
TICKET_CLOSED / TICKET_BANNED | Replies are blocked for this ticket |
MESSAGE_NOT_FOUND | The message is missing or inaccessible |
FEEDBACK_NOT_ALLOWED | This message cannot be rated |
INVALID_ATTACHMENT_CLAIM | The file claim is invalid or belongs to another session |
ATTACHMENTS_DISABLED / TOO_MANY_ATTACHMENTS | Attachments violate portal rules |
ATTACHMENT_SERVICE_UNAVAILABLE | Attachment storage is temporarily unavailable |
REQUEST_CONFLICT | An idempotency ID was reused with different content |
SENSITIVE_CONTENT | The content policy rejected the message |
{
"documentId": "createPlayerSupportTicket",
"variables": {
"input": {
"requestId": "3a124288-6ced-4a58-9956-7ed994c419a7",
"categoryId": 1,
"title": "No",
"message": "Too short"
}
}
}
{
"errors": [
{
"message": "The title must contain between 5 and 160 characters.",
"extensions": {
"code": "TITLE_LENGTH",
"category": "support"
}
}
],
"data": {
"createPlayerSupportTicket": null
}
}