Skip to main content

Support Tickets API

The Tickets API allows you to create, track, and manage support tickets directly from your application. Use it to report payment issues, request refunds, ask integration questions, and communicate with the LuxCore support team — all without leaving your platform.

API Key Scopes

Your API key must have the appropriate scopes to access ticket endpoints:
ScopePermissions
tickets.createCreate new support tickets
tickets.readList tickets, view details, list comments, download attachments
tickets.updateClose/reopen tickets, add comments, upload attachments
Contact your account manager or request scope changes via the Dashboard to enable ticket scopes on your API key.

Creating a Ticket

Create a new support ticket with POST /tickets.
curl -X POST "https://api.lux-core.io/api/v1/tickets" \
  -H "X-API-Key: qp_test_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Payment stuck in pending",
    "description": "Payment pay_123456 has been pending for over 2 hours",
    "category": "payment_issue",
    "payment_id": "pay_123456",
    "requires_refund": false
  }'

Request Body

FieldTypeRequiredDescription
titlestringYesTicket title (max 200 characters)
descriptionstringNoDetailed description of the issue
categorystringNoTicket category (see Categories)
sub_categorystringNoSub-category for further classification
payment_idstringNoRelated payment ID
transaction_idstringNoRelated transaction ID
referencestringNoYour external reference
requires_refundbooleanNoWhether a refund is required (default: false)

Response

{
  "success": true,
  "data": {
    "ticket_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "title": "Payment stuck in pending",
    "description": "Payment pay_123456 has been pending for over 2 hours",
    "status": "open",
    "category": "payment_issue",
    "sub_category": null,
    "payment_id": "pay_123456",
    "transaction_id": null,
    "reference": null,
    "requires_refund": false,
    "resolution_summary": null,
    "attachments": [],
    "created_at": "2026-02-26T14:30:00.000Z",
    "updated_at": "2026-02-26T14:30:00.000Z",
    "resolved_at": null,
    "last_activity_at": "2026-02-26T14:30:00.000Z"
  }
}
Creating a duplicate ticket for the same payment ID returns a 409 Conflict error. Use a comment on the existing ticket instead.

Listing Tickets

Retrieve a paginated list of your tickets with GET /tickets.
curl -X GET "https://api.lux-core.io/api/v1/tickets?status=open&sort_by=created_at&sort_order=desc&limit=20" \
  -H "X-API-Key: qp_test_your_key"

Query Parameters

ParameterTypeDefaultDescription
statusstring[]Filter by status (can pass multiple: ?status=open&status=in_progress)
categorystring[]Filter by category (can pass multiple)
searchstringFree-text search in title and description (max 100 chars)
payment_idstringFilter by related payment ID
created_afterstringISO 8601 date — tickets created after this date
created_beforestringISO 8601 date — tickets created before this date
sort_bystringcreated_atSort field: created_at, updated_at, last_activity_at, status
sort_orderstringdescSort order: asc or desc
limitinteger20Items per page (1–100)
offsetinteger0Number of items to skip

Response

{
  "success": true,
  "data": [
    {
      "ticket_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "title": "Payment stuck in pending",
      "status": "open",
      "category": "payment_issue",
      "payment_id": "pay_123456",
      "requires_refund": false,
      "created_at": "2026-02-26T14:30:00.000Z",
      "updated_at": "2026-02-26T14:30:00.000Z",
      "last_activity_at": "2026-02-26T14:30:00.000Z"
    }
  ],
  "pagination": {
    "limit": 20,
    "offset": 0,
    "total": 1,
    "total_pages": 1
  }
}

Getting Ticket Details

Retrieve full details of a specific ticket with GET /tickets/:ticketId.
curl -X GET "https://api.lux-core.io/api/v1/tickets/a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "X-API-Key: qp_test_your_key"
Returns the same MerchantTicketResponseDto structure as the create endpoint.

Closing a Ticket

Close an open ticket with POST /tickets/:ticketId/close. You can optionally include a closing comment.
curl -X POST "https://api.lux-core.io/api/v1/tickets/a1b2c3d4-e5f6-7890-abcd-ef1234567890/close" \
  -H "X-API-Key: qp_test_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "comment": "Issue resolved, closing ticket"
  }'
Only tickets in open, in_progress, waiting_customer, or resolved status can be closed. Attempting to close a ticket in another status returns 400 Bad Request.

Reopening a Ticket

Reopen a resolved or closed ticket with POST /tickets/:ticketId/reopen.
curl -X POST "https://api.lux-core.io/api/v1/tickets/a1b2c3d4-e5f6-7890-abcd-ef1234567890/reopen" \
  -H "X-API-Key: qp_test_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "comment": "Issue persists, reopening"
  }'

Comments

Adding a Comment

Add a comment to an existing ticket with POST /tickets/:ticketId/comments.
curl -X POST "https://api.lux-core.io/api/v1/tickets/a1b2c3d4-e5f6-7890-abcd-ef1234567890/comments" \
  -H "X-API-Key: qp_test_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "Here is the screenshot of the error",
    "attachments": [
      {
        "attachment_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
        "key": "ticket-api/123/1709912345678-xyz789.png",
        "filename": "error-screenshot.png",
        "content_type": "image/png"
      }
    ]
  }'

Comment Request Body

FieldTypeRequiredDescription
bodystringYesComment text
attachmentsarrayNoPre-uploaded attachment references (see Attachments)
Each attachment reference requires:
FieldTypeDescription
attachment_idstringAttachment ID from the upload-url response
keystringStorage key from the upload-url response
filenamestringOriginal filename
content_typestringMIME content type

Listing Comments

Retrieve comments on a ticket with GET /tickets/:ticketId/comments.
curl -X GET "https://api.lux-core.io/api/v1/tickets/a1b2c3d4-e5f6-7890-abcd-ef1234567890/comments?limit=50&offset=0" \
  -H "X-API-Key: qp_test_your_key"

Comment Response

{
  "success": true,
  "data": [
    {
      "id": 1042,
      "author_type": "merchant",
      "body": "Here is the screenshot of the error",
      "attachments": [
        {
          "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
          "name": "error-screenshot.png",
          "type": "image/png",
          "size": 245120
        }
      ],
      "created_at": "2026-02-26T15:00:00.000Z",
      "updated_at": "2026-02-26T15:00:00.000Z"
    },
    {
      "id": 1043,
      "author_type": "admin",
      "body": "Thank you, we're investigating the issue",
      "attachments": [],
      "created_at": "2026-02-26T15:10:00.000Z",
      "updated_at": "2026-02-26T15:10:00.000Z"
    }
  ],
  "pagination": {
    "limit": 50,
    "offset": 0,
    "total": 2,
    "total_pages": 1
  }
}
Only public comments are returned. Internal team comments are not visible via the API.

Attachments

File attachments use a two-step upload flow: first generate a pre-signed upload URL, then upload the file directly to storage.

Supported File Types

MIME TypeExtension
image/jpeg.jpg, .jpeg
image/png.png
image/webp.webp
image/gif.gif
application/pdf.pdf
video/mp4.mp4
video/webm.webm

Upload Workflow

1

Generate Upload URL

Call POST /tickets/:ticketId/upload-url with the filename, content type, and optionally file size:
curl -X POST "https://api.lux-core.io/api/v1/tickets/a1b2c3d4-e5f6-7890-abcd-ef1234567890/upload-url" \
  -H "X-API-Key: qp_test_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "receipt.png",
    "content_type": "image/png",
    "file_size": 245120
  }'
FieldTypeRequiredDescription
filenamestringYesOriginal filename (max 255 chars)
content_typestringYesMIME type (see Supported File Types)
file_sizeintegerNoFile size in bytes. If provided, must not exceed 10MB (10,485,760 bytes)
Response:
{
  "success": true,
  "data": {
    "upload_url": "https://storage.lux-core.io/support-tickets/ticket-api/123/1709912345678-abc123.png?X-Amz-...",
    "attachment_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "key": "ticket-api/123/1709912345678-abc123.png",
    "expires_in": 300
  }
}
2

Upload the File

Use the pre-signed upload_url to upload the file directly via HTTP PUT:
curl -X PUT "UPLOAD_URL_FROM_PREVIOUS_STEP" \
  -H "Content-Type: image/png" \
  --data-binary @receipt.png
3

Reference in Comment

Include the attachment_id, key, filename, and content type when adding a comment:
{
  "body": "Attached the receipt",
  "attachments": [
    {
      "attachment_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
      "key": "ticket-api/123/1709912345678-abc123.png",
      "filename": "receipt.png",
      "content_type": "image/png"
    }
  ]
}
Upload URLs expire after the time indicated in expires_in (in seconds). Generate a new URL if the previous one has expired.

Downloading Attachments

To download an attachment, request a pre-signed download URL: Ticket-level attachment:
curl -X GET "https://api.lux-core.io/api/v1/tickets/a1b2c3d4-e5f6-7890-abcd-ef1234567890/attachments/f47ac10b-58cc-4372-a567-0e02b2c3d479" \
  -H "X-API-Key: qp_test_your_key"
Comment attachment:
curl -X GET "https://api.lux-core.io/api/v1/tickets/a1b2c3d4-e5f6-7890-abcd-ef1234567890/comments/1/attachments/f47ac10b-58cc-4372-a567-0e02b2c3d479" \
  -H "X-API-Key: qp_test_your_key"
Response:
{
  "success": true,
  "data": {
    "download_url": "https://storage.lux-core.io/support-tickets/tickets/1709912345678-abc123.png?X-Amz-...",
    "thumb_url": "https://storage.lux-core.io/support-tickets/tickets/1709912345678-abc123-thumb.png?X-Amz-..."
  }
}
thumb_url is available for image attachments only. It is null for PDFs and videos.

Ticket Statuses

StatusDescription
openTicket created, awaiting support team review
in_progressSupport team is actively working on the issue
waiting_customerSupport team has responded and is waiting for your input
on_holdTicket is paused (e.g., waiting for a third party)
resolvedIssue has been resolved by the support team
closedTicket is closed (by merchant or after resolution)
canceledTicket was canceled

Status Lifecycle

A support agent picks up the ticket and begins investigation.
The support team has responded and needs additional information from you. Add a comment to move the ticket forward.
You replied with a comment — the ticket moves back to active investigation.
The issue requires external investigation (e.g., bank confirmation). The ticket is paused until the external party responds.
The support team has resolved the issue. You can close the ticket or reopen it if the problem persists.
You confirm the resolution by closing the ticket, or it is auto-closed after a period of inactivity.
You reopen the ticket via POST /tickets/:ticketId/reopen if the issue was not fully resolved.

Merchant-Allowed Transitions

As a merchant, you can perform these status transitions:
ActionAllowed FromEndpoint
Closeopen, in_progress, waiting_customer, resolvedPOST /tickets/:ticketId/close
Reopenresolved, closedPOST /tickets/:ticketId/reopen

Categories

CategoryDescription
payment_issueProblems with a specific payment (stuck, failed, wrong amount)
integrationAPI integration questions or issues
limitsBalance or transaction limit inquiries
accountAccount settings, credentials, or access issues
bugBug reports for the API or dashboard
questionGeneral questions
disputePayment dispute (replaces legacy appeals system)
refund_requestRequest for a payment refund
otherAnything that doesn’t fit other categories
Using the correct category helps the support team route your ticket faster. payment_issue and refund_request tickets are prioritized automatically.

Webhook Events

When you have webhooks configured, the following ticket events are delivered:
EventTriggerDescription
ticket.createdTicket created via APIA new support ticket was created
ticket.updatedTicket status changedTicket status was changed (e.g., reopened, moved to in_progress)
ticket.comment_addedAdmin replies to your ticketA public comment was added by the support team
ticket.resolvedAdmin resolves ticketTicket was resolved by the support team
ticket.closedTicket closedTicket was closed (by merchant or support team)
ticket.comment_added fires only when the support team adds a public reply to your ticket. Internal notes are never delivered. For ticket.comment_added, the comment content is included in data.comment alongside data.ticket.
The webhook payload follows the same envelope structure as payment events, with the full ticket object in the data.ticket field:
{
  "id": "evt_tkt_abc123",
  "created_at": "2026-02-26T15:10:00Z",
  "merchant_id": "123",
  "data": {
    "ticket": {
      "ticket_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "title": "Payment stuck in pending",
      "description": "Payment pay_123456 has been pending for over 2 hours",
      "status": "open",
      "category": "payment_issue",
      "sub_category": null,
      "payment_id": "pay_123456",
      "transaction_id": null,
      "reference": null,
      "requires_refund": false,
      "resolution_summary": null,
      "attachments": [],
      "created_at": "2026-02-26T14:30:00.000Z",
      "updated_at": "2026-02-26T14:30:00.000Z",
      "resolved_at": null,
      "last_activity_at": "2026-02-26T14:30:00.000Z"
    }
  }
}
To receive ticket events, add them to your webhook subscription. See the Webhooks Guide for setup instructions.

Error Handling

All API responses use a unified envelope. On errors, success is false and the error field contains a human-readable message:
{
  "success": false,
  "error": "Invalid status transition: closed -> closed. Allowed source statuses: open, in_progress, waiting_customer, resolved",
  "statusCode": 400
}
Status CodeMeaning
400Invalid request parameters or status transition
401Unauthorized — missing or invalid API key
404Ticket, comment, or attachment not found
409Duplicate ticket for this payment ID
HTTP status is always 200. Check the success field and statusCode in the response body to determine the actual result.

Best Practices

Link Payments to Tickets

Always include payment_id or transaction_id when creating tickets about payment issues. This helps the support team investigate faster.

Use Correct Categories

Categorize tickets accurately. payment_issue and refund_request are auto-prioritized for faster resolution.

Add Context in Comments

When replying, include relevant details like timestamps, amounts, and error messages. Attach screenshots when possible.

Monitor via Webhooks

Subscribe to ticket.updated and ticket.comment_added events to get real-time notifications instead of polling the API.