Zendesk Deletion Schedules: Setting Up Data Retention
A Zendesk deletion schedule is how data retention gets enforced in Support: it removes tickets on a timer, with no undo, so it is the one piece of Zendesk configuration where you want to be certain what you saved. We built one to find out, and learned two things worth passing on: the scope is much narrower than the name suggests, and the rule Zendesk stores is not always the rule you sent.
Neither of those is a disaster. Both change how you should check your work.
What we did
We created a ticket deletion schedule on our developer instance through the API, deliberately inactive, with two conditions: the ticket is closed, and it has not been updated in two years. Zendesk returned 201 Created. Then we read the record back.
The stored schedule had one condition. The status test was gone.
conditions stored: {
"all": [
{
"field": "duration_since_last_update",
"operator": "greater_than",
"value": "P2Y"
}
],
"any": []
}
No error, no warning, no field in the response saying anything had been ignored.
Our first reading of that was wrong, and the correction is the most useful thing in this article. We assumed the saved rule now meant "any ticket older than two years", which would be alarming. It does not, because a ticket deletion schedule never had that reach. Zendesk's guide to creating ticket deletion schedules is explicit: "Deletion schedules delete only archived tickets, which are tickets that have been closed for more than 120 days."
So the status condition we sent was redundant. The schedule was already scoped to closed-and-archived tickets before we added it, and Status is not among the condition fields Zendesk lists for this object. The schedule we created does what its description says and nothing wider.
The name we gave it and the description Zendesk generated agree, as it turns out, because the scope was closed tickets all along. What the list page does not show is that we asked for a second condition and did not get it. Nothing on this screen would tell you.
Three inputs, three behaviors
It'd be easy to conclude Zendesk quietly discards anything it doesn't like. It doesn't, and the distinction matters if you want to trust anything you build here. We ran three probes against the same endpoint.
Sending a tags condition alongside the duration returned an error and created nothing:
{"error":"RecordInvalid","details":{"base":[{"description":"Invalid rule target: tags"}]}}
Sending status on its own, with no duration, also returned an error, naming exactly what was missing:
Invalid condition - duration_since_last_update / greater_than / value ,
duration_since_created_at / greater_than / value is required.
Sending status together with a valid duration returned 201 and stored the schedule without the status.
Three inputs, three different behaviors, and the interesting one is tags.
Tags is on Zendesk's documented list of conditions for a ticket deletion schedule. Sent to the API as a rule target named tags, it came back rejected. We have not established why: the API may expect a different field name than the UI label suggests, or the two surfaces may simply diverge here. What we can say is that a condition the documentation offers did not work under the name we tried, and the error gave no alternative.
status behaved differently again. It is not on the documented list. The API neither rejected it nor kept it: the request succeeded and the schedule was stored without it.
That is the finding worth carrying away, and it is about validation, not danger. A documented field errored, an undocumented field was silently discarded, and both requests looked successful enough to move on from. If you build schedules programmatically, a 201 does not tell you your conditions survived.
Condition counts are also entitlement-limited on this feature: Zendesk's guide to end-user deletion schedules says that without the ADPP add-on you can add only one condition beyond the required duration. That is worth knowing when you plan a policy, though it does not explain what we saw, since we sent exactly one additional condition.
That's an instance observation, reproduced three times on 28 August 2026. We're not claiming Zendesk documents this behavior or that it holds on every account. We're claiming that if you build a deletion schedule and assume your conditions were saved, you should check, because on our account one of them was not.
Read the schedule back. Every time.
The Admin Center makes this visible if you open the schedule, which is what the read-back looks like without an API client:
One condition row where we sent two. The line above it is the scope statement worth reading twice: conditions must be met "for the schedule to delete closed and archived tickets". That sentence is doing more work than its placement suggests, because it tells you the whole feature only ever touches tickets that are already closed.
The practical rule is short. After you create or edit a deletion schedule, fetch it again and compare the conditions to what you sent. In the API that is a single GET on the schedule id. In the Admin Center it means reopening the schedule and reading the conditions rather than trusting the confirmation toast.
This is ordinary defensive practice with any API, and it costs one request. What it catches is a schedule that is wider than you designed. That is the direction that matters: the archived-only envelope is fixed, but inside it a lost Brand or Form condition means the rule sweeps every brand's archived tickets instead of one brand's. Our own dropped condition happened to be redundant. A dropped restricting condition would not be.
The same logic argues for creating every schedule inactive, as we did, and activating it only after you have read back what was stored and confirmed the record matches your intent. Zendesk lets you save an inactive schedule, which is the whole point of the flag.
What the conditions actually accept
The duration isn't a number of days. It is an ISO 8601 duration, so two years is P2Y and thirty days is P30D. We established that from the API's own responses rather than from the reference: passing 730 returns "The provided time duration is invalid", which is accurate and unhelpful, and P2Y was accepted.
There are two duration fields, and the difference is worth a moment. duration_since_last_update measures from the last activity on the ticket. duration_since_created_at measures from when it was opened. A long-running ticket that people are still replying to is old by the second measure and young by the first. For retention you almost always want the first. A ticket somebody touched last week is current, whenever it started.
Zendesk's guide to creating ticket deletion schedules lists what the condition builder offers: Last updated, Created at, Custom fields, Attachment, Brand, Support type, Form, Group, Type, Requester, Organization, and Tags. Status is not on that list, which is consistent with the scope being fixed to archived tickets already.
Two operational numbers from the same page are worth knowing before you activate anything. Schedules "start to delete eligible tickets within 72 hours of meeting the defined criteria", so nothing happens the moment you save. And "up to 200,000 tickets per account per day will be deleted", which is the ceiling on how fast a large cleanup actually drains.
The object types are enumerated in an error message if you omit the object field, which is the fastest way to discover them:
deletion_schedule.object must be one of these: zen:ticket, zen:user,
zen:bot_only_conversation, zen:attachment, zen:custom_object:product_registration
Note the last entry. Custom objects appear in that list by name, so the set is account-specific. If you've built custom objects, they get their own deletion schedules, and if you haven't, that entry looks different on your instance.
The add-on gate, and why it sits where it does
The page sits under Account then Security in the Admin Center, which is not where most people look for retention. Above the list, our instance shows a notice:
"One active deletion schedule per data type is available. To add more schedules, upgrade to the Advanced Data Privacy and Protection (ADPP) add-on."
Zendesk documents the mechanics of both in Managing deletion schedules and the ticket-specific setup in Creating ticket deletion schedules.
One active schedule per data type is enough for a simple retention policy: delete tickets after N years, delete attachments after M. It stops being enough the moment your policy has exceptions, and real policies are mostly exceptions. Different retention for different brands. Shorter windows for a region with stricter rules. Keeping billing disputes longer than password resets.
Every one of those needs a second schedule for the same data type, and the second one is the paid one.
The incentive is legible and worth stating plainly. Complex retention requirements correlate almost perfectly with the accounts that have compliance obligations, and those accounts have budget. Zendesk prices the feature where the willingness to pay is. That's normal.
The practical consequence is what matters to you: if your retention policy has any exception in it, price the add-on before you design the policy, not after. Designing a multi-rule policy and then discovering you can run one rule is a worse sequence than knowing the constraint up front.
What deletion actually does
Two distinctions catch people out.
Deleting a ticket isn't the same as closing it. Closed is a terminal status; the ticket's still there, still searchable, still counted in storage. Deletion removes it. If your goal is to stop agents seeing stale tickets, views and status do that job. Deletion is for holding less data.
There is no undo. Zendesk states it plainly: "Deleted archived tickets can't be restored. Exercise caution when activating deletion schedules." This is not the soft-delete you get elsewhere in the product. When a schedule removes an archived ticket, it is gone.
So get the scope right before you activate. A scheduled deletion runs unattended by design, and nothing prompts anyone to check it afterwards.
A sane order of operations
Build the query before you build the schedule. Search for the tickets your conditions describe and look at what comes back. If your rule is "not updated in two years", run that search first and check the result set is the one you meant to remove.
Then create the schedule inactive, read it back, and compare. Then activate.
That sequence takes a few extra minutes and it's the difference between a retention policy and an accident. It also gives you something to show an auditor, which is usually why the policy exists in the first place.
Related reading: our Zendesk security checklist for admins covers the settings that sit beside this one, and the Zendesk API guide covers the request patterns if you're scripting the check. If you want somewhere safe to try a schedule end to end, a sandbox is the right place, though note it won't carry your production ticket volume unless you ask it to.
Common questions
Can I undo a deletion schedule after it has run? No. Zendesk's documentation says "Deleted archived tickets can't be restored." Treat activation as the irreversible step and check the schedule's scope before you flip it on.
Why did my condition disappear? On our instance, a status condition sent alongside a valid duration was accepted with a 201 and stored without the status. Status is not one of the condition fields Zendesk lists for ticket deletion schedules, which is likely why. Read the schedule back after saving and compare, and if a condition is missing, rebuild the rule using the documented fields.
Do deletion schedules delete open tickets? No. Zendesk documents that they act only on archived tickets, meaning tickets closed for more than 120 days. A schedule that looks like it would sweep your whole instance cannot reach anything still in play.
How many deletion schedules can I have? Our instance allows one active schedule per data type, and the page names the Advanced Data Privacy and Protection add-on as the route to more. Check what your own plan shows on the Deletion schedules page, since add-on entitlements vary by account.
What format does the duration take? ISO 8601. P2Y for two years, P6M for six months, P30D for thirty days. A plain integer is rejected as an invalid duration.
Does a deletion schedule remove attachments too? Attachments have their own object type, zen:attachment, and therefore their own schedule. If your storage problem is file storage rather than ticket count, that's the schedule you want.
Where an AI layer fits
Retention and automation pull in opposite directions, and it's worth being clear-eyed about that rather than pretending otherwise. An AI agent answering from your ticket history works better with more history. A retention policy exists to hold less of it.
That tension is real and it doesn't have a clever resolution. What it has is a design decision: decide what the agent needs to learn from, and make sure your retention window is longer than that. If your agent's useful context is the last eighteen months of resolved tickets, a two-year window is fine and a six-month window quietly degrades it.
Macha sits on top of your existing Zendesk, so your retention policy stays where it is and keeps applying. How much that is worth to you depends almost entirely on how deep your archive is. Two years of resolved tickets is a corpus. Six months, after an aggressive retention policy, is not, and no agent recovers what a schedule already removed. If you already run a long window and a maintained help center, you are in the good case here; if you are about to shorten one to save space, this is the decision to make deliberately. So set the window from your compliance requirement first, then check what is left to work with, because finding out in the other order means finding out too late.
Worth knowing what your history can still answer before you set the window that removes it: start a free trial.
Add AI agents to your Zendesk
Macha reads the ticket, drafts the reply and takes the action, inside the Zendesk you already run.
Shopify
Stripe
Slack
Notion
Google Workspace
Confluence

