Create ticket API
This article and the rest of the API documentation in this section are written for a technical audience — integrators and developers connecting external systems to Tickiti. Familiarity with HTTP, REST, JSON and bearer-token authentication is assumed.
The create ticket endpoint files a new ticket on behalf of a customer. Use it when an external system needs Tickiti to take ownership of a piece of work — for example a sales portal logging an enquiry, an order system creating a delivery exception ticket, or a CRM raising a contract review.
Endpoint
POST /api/v1/ticketsHeaders
Authorization: Bearer YOUR_TOKEN— required. Token must have thetickets:writeability.Idempotency-Key: <uuid>— required. See Idempotency.Content-Type: application/json— required.
Body
Required fields:
originator_email_address— the email of the customer the ticket is filed for. The address becomes the ticket’s originator and its first participant; it does not need a Tickiti account.
You must provide exactly one of the following payload shapes:
- A) Subject + content:
data.subjectanddata.content(HTML allowed). Use when the integration writes its own subject and body. - B) Template + data:
template_identifierplus adataobject — render a Tickiti email template by identifier with bound data. Use when content should follow a configured template. - C) Intervention + data:
intervention(the intervention’s name), adataobject and auid— drive a Tickiti intervention with bound data, the most expressive option.uidis your own reference for the request and is required with this shape. See Interventions guide.
Optional fields:
queue_name— the queue to place the ticket in, by name. Send it at the top level or asdata.queue_name; if you send both they must name the same queue. Defaults toInbox. Cannot be set for interventions (the intervention picks the queue), and a ticket cannot be created in the Drafts queue. Naming a queue also tells Tickiti the ticket belongs there — see Where the ticket lands. List the queue names withPOST /api/v1/tickets/queues.data.priority—"10"Low,"20"Normal,"30"High or"40"Urgent. Send it as a JSON string — an integer is rejected with 422. Omit to leave Tickiti’s default. An intervention configured to create urgent tickets overrides this.data.status—open(default),on-holdorclosed.data.on_hold_until— the date the hold ends (YYYY-MM-DD). Required whendata.statusison-hold.data.assigned_to_email— assign the ticket as it is created: a staff member’s email, or a team’s keyteam:<id>(list the keys withPOST /api/v1/tickets/teams). Defaults to Unassigned. An intervention that specifies an assignee overrides this.participants— an array of email addresses added as participants as the ticket is created.is_public— whether the opening response is public (default true). Set false to open the ticket with an internal staff note.use_passed_originator_as_responder— author the opening response as the originator rather than as the token’s owner. With a customer originator the opening response is then a customer response — see Where the ticket lands.strict_tags— boolean, default false. Refuse the request when an @tag or #hashtag indata.contentdoes not resolve, instead of posting it as text. See @tags and #hashtags.customer_name,company(up to 255 characters each) andother_fields— details about the person submitting the ticket.other_fieldsis an array of{ "label": ..., "value": ... }objects (label up to 255 characters, value up to 1000). When any of these is present Tickiti records them, with the originator’s email, as an internal note on the ticket so staff see who submitted it and how.attachments— an array of{ "sha256": ..., "name": ..., "file_size": ... }objects describing files already uploaded with the attachment-upload endpoint (see Attaching files). They are attached to the opening response as regular (non-inline) attachments.
Example A — subject + content
curl -X POST https://support.sole-provider.example/api/v1/tickets \
-H "Authorization: Bearer $TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"originator_email_address": "[email protected]",
"data": {
"queue_name": "Returns",
"subject": "Carbon plate cracked on first marathon",
"content": "The Vapor Pro 3 plate snapped at mile 14 of Berlin Marathon. Order SP-2412-0489."
}
}'Example B — template + data
Render the warranty_acknowledgement template with order/customer data:
{
"originator_email_address": "[email protected]",
"template_identifier": "warranty_acknowledgement",
"data": {
"queue_name": "Returns",
"order_reference": "SP-2412-0489",
"product": "Vapor Pro 3",
"customer_name": "Marcus Whitfield"
}
}Example C — intervention
Run the warranty_claim intervention. The intervention defines its own queue, templates and follow-up steps:
{
"originator_email_address": "[email protected]",
"intervention": "warranty_claim",
"uid": "crm-claim-58221",
"data": {
"order_reference": "SP-2412-0489",
"product": "Vapor Pro 3"
}
}Interventions are the recommended pattern for any workflow that recurs — see Interventions quick start. A request whose uid matches one Tickiti has already received continues that earlier ticket rather than opening a new one: Tickiti moves it to the intervention’s queue, applies data.status (default open) and posts the intervention’s responses onto it.
Example D — assigned, on hold, with participants and a file
{
"originator_email_address": "[email protected]",
"queue_name": "Returns",
"participants": ["[email protected]"],
"attachments": [
{ "sha256": "cc9b8a68...608baf", "name": "photo.jpg", "file_size": 482113 }
],
"data": {
"subject": "Replacement plate awaiting courier",
"content": "<p>Replacement booked. @priya please confirm stock.</p>",
"priority": "30",
"status": "on-hold",
"on_hold_until": "2026-05-08",
"assigned_to_email": "team:3"
}
}Inline images
In the subject + content shape, embed an image in data.content as a base64 data URI — an <img src="data:image/png;base64,…"> element placed where the image should appear. Tickiti decodes it, stores it as an inline attachment, and rewrites the body to reference it, so it renders within the ticket.
Attaching files
Upload each file first with POST /api/v1/tickets/attachment-upload (tickets:write), sending it as multipart/form-data in a field named file. Files up to 25 MB are accepted; a file type an administrator has hard-blocked is refused with 403 attachment_blocked. The response describes the stored file:
{
"ok": true,
"data": { "sha256": "cc9b8a68...608baf", "name": "photo.jpg", "file_size": 482113 }
}Pass that object, unchanged, in the attachments array of the create (or reply) request. Files are stored by content, so uploading the same bytes twice is harmless.
Where the ticket lands
Tickiti picks the queue in this order: the intervention’s queue, then queue_name / data.queue_name, then Inbox.
When the opening response is written by a non-staff author — for example with use_passed_originator_as_responder and a customer originator — and the ticket has no staff participant, Tickiti treats it as incoming customer mail and routes it to the inbox queue (the queue incoming email is delivered to), adding that queue’s inbox managers as participants. A request that names a queue, or that sets is_public to false, keeps the ticket in the queue it chose.
The response always reports where the ticket actually is: queue is the queue it is in, queue_requested the queue the request resolved to, and when the two differ rerouted is true and reroute_reason says why.
@tags and #hashtags
In an opening response written by a staff author, @name tags the staff member whose email or name starts with name — they are added as a participant and notified — and #word adds a hashtag. A tag starts with a letter, and a trailing full stop is not part of it, so “check with @dave.” tags dave. An @ or # inside a code block (<pre>, <code> or a Quill code block) is never a tag, so pasted diffs and logs post as written.
- An @tag that matches no staff member, or more than one, is kept as plain text and listed in
tag_warningsas{ "reason": "no_matches" | "not_unique", "tag": ..., "emails": [...] }(emailslists the candidates fornot_unique). - A hashtag that closely resembles an existing one is added as written and listed in
hashtag_warningsas{ "reason": "possible_misspelling", "tag": ..., "matches": [...] }. - With
"strict_tags": trueTickiti checksdata.contentbefore creating anything and refuses the request with422 validation_failed, naming the tags undererrors.tag_errorsanderrors.hashtag_errors(each a JSON-encoded list of the objects above).
Successful response
{
"ok": true,
"data": {
"ticket_id": 1,
"ticket_number": 220009,
"secure_id": "q8Z2...",
"queue": "Returns",
"queue_requested": "Returns",
"rerouted": false,
"reroute_reason": null,
"status": "open",
"assigned_to_email": "Unassigned",
"priority": "20",
"tag_warnings": [],
"hashtag_warnings": [],
"used_template": false,
"used_intervention": false,
"used_private_template": false,
"used_public_template": false
}
}ticket_id,ticket_number— the ticket’s internal id and its 6-digit number. Either one references the ticket in the other ticket endpoints.secure_id— the key in the originator’s personal link to the ticket. Treat it as a secret. It isnullwhen an intervention request continues an existing ticket.queue,queue_requested,rerouted,reroute_reason— see Where the ticket lands.status,assigned_to_email,priority— the ticket’s values after creation, including anything an intervention applied.tag_warnings,hashtag_warnings— see @tags and #hashtags.used_*— which payload path was taken.
Store ticket_number to read the ticket or reply to it later. For recurring workflows, an intervention (payload shape C) with a uid ties the ticket to your own record: Tickiti carries the uid through the intervention’s epilog callbacks. See Interventions technical reference.
Error responses
Every error has "ok": false and an error code. Validation errors list the problems per field under errors; the others carry a message:
{
"ok": false,
"error": "validation_failed",
"errors": {
"queue_name": ["TicketQueue not found."]
}
}422 validation_failed— a missing or malformed field, none or more than one payload shape, an unknown queue or intervention, a queue named alongside an intervention,queue_nameanddata.queue_namedisagreeing,on-holdwithoutdata.on_hold_until, an unknown team key, unresolved tags understrict_tags, or a missingIdempotency-Key.422 create_refused— Tickiti declined to post the opening response;messagesays why. Nothing is created.422 create_failed— the ticket could not be created;messagesays why.404 not_found— a record the request depends on does not exist;messagenames it.409 idempotency_key_conflict— the idempotency key was already used with a different payload. See Idempotency.401 unauthenticated— bad or missing token.403 forbidden— token does not have thetickets:writeability, or the user it acts as may not create a ticket in the target queue (messagesays so).500 internal_error— an unexpected server error.
What happens server-side
- Tickiti validates the request — including
strict_tags— before anything is created. - It picks the queue: the intervention’s queue, otherwise
queue_name/data.queue_name, otherwise Inbox. - It creates the ticket, applying
data.status,data.on_hold_until,data.priorityanddata.assigned_to_emailwhere supplied — otherwise status Open, Tickiti’s default priority and Unassigned. - It adds the originator and every address in
participantsas participants. An intervention then applies its own settings (urgent priority, assignee, extra participants). - It writes the opening response (public, or an internal note per
is_public) using the chosen subject/content/template/intervention, resolving @tags and #hashtags and applying the customer-mail routing rule described above. - It extracts any base64 data-URI images from the body, stores them as inline attachments, and rewrites the body to reference them; it attaches any uploaded
attachments. - It records
customer_name/company/other_fieldsas an internal note when supplied. - It dispatches the appropriate notification email to the originator (unless suppressed by the template configuration).
To change a ticket after creating it, use the Reply to ticket API; to delete, merge or link tickets, see the Ticket operations API.