Edit a trigger
To change a trigger, send the complete new request to /schedule and identify the trigger with one of these headers:
ttr-trigger-idheader: thetriggerIdreturned when you created the trigger. See Edit by trigger ID.ttr-custom-keyheader: the custom key you created it with. See Edit by custom key.
curl -X POST https://api.timetriggers.io/schedule \-H "ttr-api-key: YOUR_API_KEY" \-H "ttr-trigger-id: 3f2b8c1e-7d4a-4e5b-9c2f-1a2b3c4d5e6f" \-H "ttr-url: https://example.com/webhooks/reminder" \-H "ttr-scheduled-at: 2030-01-02T09:00:00Z" \-H "ttr-title: Reminder for appointment 42" \-H "ttr-tags: reminders" \-H "Content-Type: application/json" \-d '{"reminderId": 42}'
The trigger keeps its ID, and the response has "operation": "reschedule":
{"triggerId": "3f2b8c1e-7d4a-4e5b-9c2f-1a2b3c4d5e6f","scheduledAt": "2030-01-02T09:00:00.000Z","operation": "reschedule","kind": "job","monthQuotaRemaining": 498}
Every edit costs one unit of quota, like any /schedule call, even one that fails with 404 or 410.
An edit replaces the whole trigger
An edit is not a patch. The trigger is rebuilt from your edit request alone, the same way /schedule builds a new trigger, and nothing carries over from the previous version. Resend everything you want to keep: the method, headers and body, ttr-scheduled-atheader, ttr-titleheader, ttr-tagsheader and, when it applies, ttr-run-missedheader.
| Part of the trigger | Taken from your edit request | Good to know |
|---|---|---|
| Time | ttr-scheduled-atheader | Leave it out and it means now: the trigger fires right away. |
| Target URL | ttr-urlheader | Required on every call. |
| Method | The request method | A PUT edit turns a POST trigger into a PUT trigger. |
| Headers | Every non-ttr- header (details) | Headers you don't resend are removed. Headers your client adds by itself, such as User-Agent or Accept, are stored too. |
| Body | The request body | No body, or a GET, HEAD, OPTIONS or TRACE edit, leaves the trigger without a body. |
| Title | ttr-titleheader | Leave it out and the title is cleared. One-shot triggers only. |
| Tags | ttr-tagsheader | Leave it out and all tags are removed. |
| Time zone | The time zone in cron(...) | Recurring triggers only. Leave it out and it goes back to UTC. |
An edit never changes a trigger's custom key. You can't add, change or remove the key of an existing trigger.
The two easiest mistakes: an edit without ttr-scheduled-atheader makes the trigger fire right away, and an edit without ttr-tagsheader takes the trigger out of every tag policy, so its retries, throughput and concurrency limits and timeout no longer apply. Build edits with the same code that creates the trigger, so every call sends the full request.
Edit by trigger ID
Send the triggerId in ttr-trigger-idheader. It identifies a trigger in your project.
- One-shot triggers can only be edited while they're
registered, which means scheduled and not yet due. Once a trigger isskipped,queued,running,retrying,completedorcancelled, you get410Job is no longer in registered state(see Trigger statuses). A skipped or retrying trigger can still be edited by custom key. - Recurring triggers can only be edited while they're active. A cancelled one returns
410Generator is no longer active. - The kind must match. Send a one-shot trigger's ID with a date or
now, and a recurring trigger's ID withcron(...). Otherwise you get404(Trigger not foundorGenerator not found), the same as for an unknown ID, and nothing changes. To change the kind, see Switch between one-shot and recurring. - A past time fires right away. Edits by ID don't apply the past-dated rule: a trigger moved to a past time stays
registeredand fires as soon as possible, with or withoutttr-run-missedheader.
Edit by custom key
Send the key in ttr-custom-keyheader. What happens depends on the trigger that holds the key:
| Trigger with this key | Result |
|---|---|
One-shot, registered, skipped or retrying (you send a date or now) | Edited in place: same triggerId, "operation": "reschedule". For retrying, see Triggers waiting to retry. |
One-shot, queued, running, completed or cancelled (you send a date or now) | A new trigger is created: new triggerId, "operation": "schedule". The old one is left alone. |
Recurring, active (you send cron(...)) | Edited in place: same triggerId, "operation": "reschedule". |
Recurring, cancelled (you send cron(...)) | A new recurring trigger is created: new triggerId, "operation": "schedule". |
| A trigger of the other kind | It's cancelled and replaced. See Switch between one-shot and recurring. |
| None | A new trigger is created, as with any /schedule call. |
Edits by key apply the past-dated rule again:
- Moving a one-shot trigger to a past date that isn't based on
now, withoutttr-run-missedheader:true, makes itskipped, so it won't fire. The response is still200with"operation": "reschedule". Exception: a trigger that was waiting to retry still makes its pending attempt (see Triggers waiting to retry). - To revive a skipped trigger, send the key again with a future time, a
now-based time orttr-run-missedheader:true. It'sregisteredagain. This only works by key: by ID, a skipped trigger returns410.
A custom key prevents duplicate pending triggers, not a second delivery. Once a trigger's time has come, it becomes queued within about a second, and it stays queued while a tag policy holds it back. From then on, sending the same key creates a second trigger. The first one still fires, so your target receives both requests, and the first one can no longer be cancelled with /cancel. If a late edit must not lead to a second request, make your endpoint ignore duplicates; see Delivery guarantees.
Triggers waiting to retry
A retrying trigger has already fired, failed, and is waiting for its next attempt (retries). An edit by custom key updates it in place but doesn't move that attempt:
- The next attempt fires at its original retry time, with the new URL, method, headers and body, even if the edit made the trigger
skipped. - The new
ttr-scheduled-atheader is never used, whether it's earlier or later. After the attempt, the trigger completes or keeps retrying according to the policies of its new tags. The dashboard shows the new time as the trigger's due time, but nothing fires then.
To fire at the new time instead, first cancel the trigger by its ID (DELETE /cancel with ttr-trigger-idheader), so the pending attempt never fires. Then send your /schedule request with the same key: it creates a new trigger with a new triggerId. /declare and /bulk do this for you: a changed item that matches a retrying trigger replaces it with a new one.
Edit a recurring trigger
Send cron(...) in ttr-scheduled-atheader, with the recurring trigger's ID (the triggerId returned with "kind": "generator") or its custom key:
- Everything is replaced: cron expression, time zone, URL, method, headers, body and tags. Resend the time zone, for example
cron(0 9 * * 1-5, Europe/Paris), or it goes back to UTC.ttr-titleheader is ignored. - The upcoming instance is replaced. It's cancelled, and a new one with a new ID is created right away from the new settings. Instances that are already queued, running or waiting to retry keep their old settings, and queued or retrying ones still fire with them.
- Response: the same
triggerId,"operation": "reschedule","kind": "generator", andscheduledAtset to the next run.
An edit must use cron(...). Without ttr-scheduled-atheader, or with a date:
- by ID, you get
404and nothing changes (the kind must match); - by custom key, the recurring trigger is cancelled and replaced by a one-shot trigger (switch). Without
ttr-scheduled-atheader, that one-shot fires right away.
See Recurring triggers for an example and for how instances work.
Edit the recurring trigger, not a single instance. Each run is an instance with its own ID, shown as the Trigger ID in the dashboard. /schedule accepts an instance ID with a date, but:
- Only that run changes, as a one-shot edit: without
ttr-tagsheader, it loses the recurring trigger's tags. - The recurring trigger creates no further instances until that run comes due. Moving it far into the future pauses the recurring trigger, and the times in between aren't caught up. Moving it earlier can make the same time run twice.
To undo this, send the recurring trigger's cron(...) again with its ID or custom key. That replaces the moved instance.
Switch between one-shot and recurring
Switch with the custom key:
- One-shot to recurring: send
cron(...)with the one-shot trigger's key. The pending one-shot is cancelled, and a new recurring trigger takes over the key. - Recurring to one-shot: send a date or
nowwith the recurring trigger's key. The recurring trigger and its upcoming instance are cancelled, past instances are kept, and a new one-shot trigger takes over the key.
A switch creates a new trigger. The response has "operation": "schedule", a new triggerId and the new kind. The old trigger stays visible as cancelled and its ID can no longer be edited, so store the new one. The next call with the same key and kind is an ordinary edit ("operation": "reschedule").
Good to know
- Send only
ttr-custom-keyheader. You can't switch by ID: the old trigger's ID fails with404(the kind must match). - A one-shot trigger that is already queued or running isn't cancelled and still fires.
- If earlier one-shot triggers have used the same key, the pending one may not be cancelled by the switch. Cancel it by its
triggerIdfirst.
Sending both identifiers
Send only one of ttr-trigger-idheader and ttr-custom-keyheader. Unlike /cancel, /schedule accepts both and decides like this:
- One-shot: if the key belongs to a
registered,skippedorretryingtrigger, that trigger is edited and the ID is ignored. Otherwise the trigger with the ID is edited under the ID rules, and it doesn't take on the key. - Recurring (
cron(...)): the ID decides which recurring trigger is edited, and the key isn't assigned to it.
Status codes
| Status code | Meaning |
|---|---|
| 200OK | The trigger was edited ("operation": "reschedule"), or a new one was created ("operation": "schedule"). |
| 404Not Found | Trigger not found: no one-shot trigger has this ttr-trigger-idheader, for example because it's a recurring trigger's ID sent with a date. Generator not found: no recurring trigger has this ID, for example because it's a one-shot trigger's or an instance's ID sent with cron(...). |
| 410Gone | Job is no longer in registered state: the one-shot trigger isn't registered anymore. Generator is no longer active: the recurring trigger was cancelled. |
Other errors (400, 401, 402, 431) are the same as when you create a trigger; see Status codes and Errors.
Other ways to edit
/declare and /bulk also update triggers, matched by customKey in a JSON body; they can't target a trigger by ID. Some rules differ from /schedule, for example for triggers waiting to retry. See their pages.