Deleting a ticket attachment in Zoho Desk is done via a DELETE API call that targets the specific attachment by its ID, scoped to the parent ticket — and in some cases, to a specific thread or transition draft within that ticket.
Why this matters
When a file is attached to a ticket in error — wrong version, sensitive data, or simply clutter — you need a reliable way to remove it programmatically. Zoho Desk exposes several DELETE endpoints depending on *where* the attachment lives: directly on the ticket, inside a thread reply, or on a transition draft. Choosing the wrong endpoint will result in a failed request, so understanding the hierarchy matters.
> Beam Help is independent expert support for Zoho — not official Zoho support.
---
Step-by-step
Step 1. Confirm your OAuth scopes.
Before making any deletion call, verify that your connected app has been granted the Desk.tickets.ALL or Desk.tickets.DELETE scope. Without the correct scope, the API will reject the request with an authorisation error. [6]
Step 2. Retrieve the attachment ID.
If you don't already have the attachmentId, call the list endpoint first. Issue a GET request to /api/v1/tickets/{ticketId}/attachments, passing the relevant ticketId as a path parameter. The response will include each attachment's ID, which you'll need for the deletion call. [7]
Step 3. Delete a top-level ticket attachment.
For attachments that belong directly to the ticket (not inside a thread), send a DELETE request to:
DELETE /api/v1/tickets/{ticketId}/attachments/{attachmentId}
Supply both ticketId and attachmentId as path parameters. A successful call removes the file from the ticket record. [1][2]
In Python, the call looks like this:
client.delete_ticket_attachment(ticketId="123456", attachmentId="987654")
Step 4. Delete an attachment inside a ticket thread.
If the file was attached to a specific reply or thread within the ticket, you must include the thread_id in the path. Use:
DELETE /api/v1/tickets/{ticket_id}/threads/{thread_id}/attachments/{attachment_id}
All three path parameters — ticketid, threadid, and attachment_id — are required. Omitting the thread segment will cause the request to fail or target the wrong resource. [3]
In Python:
client.delete_attachment(ticket_id="123456", thread_id="111", attachment_id="987654")
Step 5. Delete an attachment from a transition draft.
If the attachment is associated with a blueprint or workflow transition draft rather than the ticket body or thread, a separate endpoint applies:
DELETE /api/v1/tickets/{ticketId}/transitions/{transitionId}/attachments/{attachmentId}
Here you need the ticketId, the transitionId of the draft, and the attachmentId. [4]
In Python:
client.op_16_delete_attachment_of_transition(
ticketId="123456",
transitionId="555",
attachmentId="987654"
)
---
Common pitfalls
- Using the ticket-level endpoint for thread attachments. The top-level
DELETE /api/v1/tickets/{ticketId}/attachments/{attachmentId}endpoint will not locate files that are scoped to a thread. Always match the endpoint to where the attachment actually lives. [1][3]
- Missing or incorrect scope. The
Desk.tickets.DELETEscope must be explicitly included in your OAuth configuration. If you only requestedREADorWRITE, deletion calls will be rejected. [6]
- Confusing ticket attachments with account attachments. There is a separate endpoint —
DELETE /api/v1/accounts/{accountId}/attachments/{attachmentId}— for files attached to account records. Do not use this for ticket-level files. [8]
- Not fetching the attachment ID first. Hard-coding or guessing attachment IDs is unreliable. Always use the list endpoint (
GET /api/v1/tickets/{ticketId}/attachments) to retrieve the correct ID before attempting deletion. [7]
---
What to check
- Correct path parameters: Confirm that
ticketId,attachmentId, and (where applicable)thread_idortransitionIdall correspond to real, existing records before sending the request. [1][3][4] - OAuth scope coverage: Verify that
Desk.tickets.DELETEorDesk.tickets.ALLis present in your active token's scope list. [6] - Attachment location: After deletion, re-run
GET /api/v1/tickets/{ticketId}/attachmentsto confirm the attachment no longer appears in the response. [7]