---
title: "Change a ticket"
method: PATCH
path: "/products/help-desk/tickets/{ticket_id}"
tags: ["help-desk"]
---

# Change a ticket

`PATCH /products/help-desk/tickets/{ticket_id}`

## Request

- Base URL: `https://api.sparkvault.com/v1`
- URL: `https://api.sparkvault.com/v1/products/help-desk/tickets/{ticket_id}`
- Auth: HTTP bearer
- TypeScript: <https://skmtc.dev/sparkvault/apis/sparkvault-api/docs/products/help-desk/tickets/:ticket_id/patch.ts> (a function with typed inputs; its response is checked against the schema)

Change its status (with a close reason, or a hold's end), priority, team, assignee or tags, against the `version` held: a ticket that moved on since refuses the change. Reopening a closed ticket counts as a reopen. Needs a Full License; the organization must hold the help desk.

## Path parameters

- `ticket_id` string, required

## Request body

- object
  - `patch` HelpDeskTicketPatch, required: The fields to change; one that names the value already held changes nothing.
    - `status` 'open' | 'waiting_on_customer' | 'on_hold' | 'closed'
    - `close_reason` 'resolved' | 'spam': With status closed. A duplicate closes by merging.
    - `hold_until` integer: With status on_hold: when the hold ends (epoch seconds), within a year.
    - `priority` 'urgent' | 'high' | 'normal' | 'low'
    - `team_id` string, nullable: A team of the organization; null for none.
    - `assignee_id` string, nullable: An active member with a Full License; null for nobody.
    - `tags` string[]: Every tag the ticket keeps.
    - `add_tags` string[]
    - `remove_tags` string[]
  - `version` integer, required

## Response `200`

What the change did.

- object
  - `data` HelpDeskTicketWrite, required: What a change did: the ticket as written and the events it appended.
    - `ticket` HelpDeskTicket, required: A ticket as the queue lists it.
      - `ticket_id` string, required
      - `number` integer, required: The ticket's number at the desk (#1042).
      - `status` 'open' | 'waiting_on_customer' | 'on_hold' | 'closed', required
      - `close_reason` 'resolved' | 'duplicate' | 'spam' | 'null', nullable, required
      - `hold_until` integer, nullable, required: When a hold ends (epoch seconds).
      - `priority` 'urgent' | 'high' | 'normal' | 'low', required
      - `team_id` string, nullable, required
      - `assignee_id` string, nullable, required
      - `requester_id` string, required
      - `subject` string, required
      - `preview` string, required: The start of the ticket's first message, as plain text.
      - `tags` string[], required
      - `created_at` integer, required
      - `last_activity_at` integer, nullable, required
      - `last_customer_message_at` integer, nullable, required
      - `first_operator_reply_at` integer, nullable, required
      - `last_operator_reply_at` integer, nullable, required
      - `merged_into` string, nullable, required
      - `merged_into_number` integer, nullable, required
      - `muted` boolean, required: Whether its requester turned its update emails off.
      - `awaiting_first_response` boolean, required
      - `version` integer, required: The version a change is made against.
      - `requester` HelpDeskRequester, nullable, required: The ticket's requester, their address and name opened for the desk; null once the desk holds no row for them.
        - `requester_id` string, required
        - `display_name` string, nullable, required
        - `email` string, required
        - `assurance` 'identity' | 'email_pin', required: How they last proved their address.
      - `presence` HelpDeskPresenceEntry[]: Who is on the ticket now (the queue only).
        - `user_id` string, required
        - `state` 'viewing' | 'replying', required
      - `source` 'portal', required: Where the ticket was opened: its customer's portal.
      - `routing_rule_id` string, nullable, required
      - `last_public_activity_at` integer, nullable, required
      - `first_closed_at` integer, nullable, required
      - `closed_at` integer, nullable, required
      - `reopen_count` integer, required
      - `merged_at` integer, nullable, required: When it was merged: the merge can be undone for 86400 seconds after.
      - `legal_hold` object, nullable, required: The legal hold on it, while held.
      - `email_undeliverable` object, nullable, required: The bounce that marked its requester's address undeliverable.
      - `held_count` integer, required: Emails held for an operator on it.
      - `feedback` HelpDeskOperatorFeedback, required: One current rating per request. The first vote freezes its public reply, author and reporting day; later changes replace that sample.
        - `eligible` boolean, required: Whether this request has an eligible published operator reply and accepts feedback.
        - `lookup_complete` boolean, required: False when the bounded legacy reply lookup could not finish. The ticket remains readable, but feedback is temporarily unavailable rather than reported as unrated.
        - `rating` 'positive' | 'negative' | 'null', nullable, required
        - `submitted_at` integer, nullable, required: First submission time; subsequent vote changes keep the original reporting day.
        - `updated_at` integer, nullable, required
        - `version` integer, required: Zero when unrated; increments only when the saved rating changes, including within the same second.
        - `reply_event_id` string, nullable, required: The public operator reply being rated. Existing feedback keeps its original reply.
        - `operator_id` string, nullable, required: The original public reply author credited at first submission; null when unrated.
    - `events` HelpDeskEvent[], required
      - `event_id` string, required
      - `seq` integer, required: Its place in the ticket's timeline.
      - `type` 'ticket_created' | 'customer_message' | 'operator_reply' | 'status_changed' | 'reopened' | 'resolved_by_customer' | 'merged_into' | 'merged_from' | 'internal_note' | 'reply_review_submitted' | 'reply_review_rejected' | 'reply_review_approved' | 'reply_review_reassigned' | 'reply_review_discarded' | 'feedback_changed' | 'assigned' | 'team_changed' | 'priority_changed' | 'tags_changed' | 'routing_applied' | 'email_held' | 'email_released' | 'email_discarded' | 'email_rejected' | 'email_replayed' | 'email_undeliverable' | 'email_declined' | 'auto_response_sent' | 'attachment_deleted' | 'attachment_refused' | 'requester_blocked' | 'requester_unblocked' | 'legal_hold_set' | 'legal_hold_cleared' | 'storage_full', required
      - `visibility` 'public' | 'internal', required: internal: never shown to the customer.
      - `created_at` integer, required
      - `actor` object, required
        - `kind` 'customer' | 'operator' | 'system' | 'rule', required
        - `id` string
        - `label` string, nullable
        - `avatar_handle` string, nullable
      - `body` object, nullable, required: A message's body as its safe-markup document tree; null for any other event, and for a message whose body cannot be read now.
      - `attachments` object[], required
        - `attachment_id` string, required
        - `name` string, required
        - `size` integer, required
        - `content_type` string, required
        - `disposition` 'inline' | 'attachment', required
        - `scan` 'pass' | 'not_scanned', required: `pass` when the mail scan passed the email that brought the file; `not_scanned` for an upload, and for an email part the scan could not judge.
      - `data` object, required: What the event records, by its type (docs/HELP_DESK.md → Timeline events).
    - `review` HelpDeskReplyReview: A private operator reply and its review state. Never included in customer responses or exports.
      - `review_id` string, required: The hdev_ id of the initial submission.
      - `ticket_id` string, required
      - `ticket_number` integer, required
      - `operator_id` string, required: The rep whose name the approved reply is sent under.
      - `manager_id` string, required: The only operator allowed to approve or return this review.
      - `status` 'pending' | 'rejected' | 'approved' | 'discarded', required
      - `created_at` integer, required
      - `submitted_at` integer, required
      - `updated_at` integer, required
      - `status_after` 'waiting_on_customer' | 'open', required
      - `version` integer, required
      - `body` object, nullable, required: The safe-markup AST; null if the sealed body is unavailable.
      - `body_markup` string, nullable, required: The safe-markup source for rewriting the reply.
      - `attachments` object[], required
        - `attachment_id` string
        - `name` string
        - `size` integer
        - `content_type` string
        - `disposition` string
        - `scan` string
      - `feedback` object, nullable, required
        - `comment` string
        - `reviewer_id` string
        - `created_at` integer
      - `discard` object, nullable, required: Why an administrator ended this review without sending it. Kept only in private review history.
        - `reason` string
        - `operator_id` string
        - `created_at` integer
  - `meta` ResponseMeta: Envelope metadata present on every successful response.
    - `api_version` string
    - `request_id` string
    - `response_ms` integer
    - `timestamp` integer: Unix seconds.
    - `pools` object: Storage and bandwidth capacity snapshot for the account.
    - `billing` object
      - `past_due` boolean
    - `quota` object: This minute of the account request budget. Present on authenticated success responses that passed the throttle; absent otherwise.
      - `limit` integer
      - `used` integer
      - `remaining` integer
      - `resets_at` integer: Unix seconds.

## Other responses

- `400`: VALIDATION_ERROR — a field is malformed (a close reason or a hold's end beside a status it does not belong to among them), names a team or assignee the desk cannot use, or the tags pass their cap. INVALID_TRANSITION — the status move is malformed (a status no ticket has, a close naming neither resolved nor spam, a hold not ending within the next year) or not allowed from the ticket's status.
- `401`: AUTHENTICATION_ERROR — missing or invalid credentials.
- `403`: LICENSE_REQUIRED — the member does not hold a Full License. HELP_DESK_SUBSCRIPTION_REQUIRED — the organization does not hold the help desk. FORBIDDEN — an API key or impersonated session.
- `404`: NOT_FOUND — the desk holds no such ticket.
- `409`: CONFLICT — the ticket changed since it was loaded; load it again. HELP_DESK_NOT_ACTIVE — the desk is not active.

## Changes

- **2026-10-28** `a9a10e328ba5`: 20 info
  - the response property `error/details` became required for the status `400`
  - the response property `error/details` became required for the status `401`
  - the response property `error/details` became required for the status `403`
  - the response property `error/details` became required for the status `404`
  - …16 more
- **2026-10-28** `56d7c265a77a`: 5 breaking
  - the response property `error/details` became nullable for the status `400`
  - the response property `error/details` became nullable for the status `401`
  - the response property `error/details` became nullable for the status `403`
  - the response property `error/details` became nullable for the status `404`
  - …1 more
- **2026-10-28** `fecd5111028e`: 2 warning
  - added the new `not_scanned` enum value to the `data/events/items/attachments/items/scan` response property for the response status `200`
  - added the new `pass` enum value to the `data/events/items/attachments/items/scan` response property for the response status `200`
- …earlier changes not shown

[Operation history](https://skmtc.dev/sparkvault/apis/sparkvault-api/changes/products/help-desk/tickets/:ticket_id/patch.md)

---

[API](https://skmtc.dev/sparkvault/apis/sparkvault-api.md) - [All operations](https://skmtc.dev/sparkvault/apis/sparkvault-api/llms.txt) - [OpenAPI document](https://skmtc.dev/sparkvault/apis/sparkvault-api/revisions/97cfadccd0c0?raw)
