Edit a trigger

To change a trigger, send the complete new request to /schedule and identify the trigger with one of these headers:

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 triggerTaken from your edit requestGood to know
Timettr-scheduled-atheaderLeave it out and it means now: the trigger fires right away.
Target URLttr-urlheaderRequired on every call.
MethodThe request methodA PUT edit turns a POST trigger into a PUT trigger.
HeadersEvery 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.
BodyThe request bodyNo body, or a GET, HEAD, OPTIONS or TRACE edit, leaves the trigger without a body.
Titlettr-titleheaderLeave it out and the title is cleared. One-shot triggers only.
Tagsttr-tagsheaderLeave it out and all tags are removed.
Time zoneThe 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 is skipped, queued, running, retrying, completed or cancelled, you get 410 Job 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 410 Generator 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 with cron(...). Otherwise you get 404 (Trigger not found or Generator 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 registered and fires as soon as possible, with or without ttr-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 keyResult
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 kindIt's cancelled and replaced. See Switch between one-shot and recurring.
NoneA 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, without ttr-run-missedheader: true, makes it skipped, so it won't fire. The response is still 200 with "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 or ttr-run-missedheader: true. It's registered again. This only works by key: by ID, a skipped trigger returns 410.

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", and scheduledAt set to the next run.

An edit must use cron(...). Without ttr-scheduled-atheader, or with a date:

  • by ID, you get 404 and 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 now with 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 with 404 (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 triggerId first.

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, skipped or retrying trigger, 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 codeMeaning
200OKThe trigger was edited ("operation": "reschedule"), or a new one was created ("operation": "schedule").
404Not FoundTrigger 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(...).
410GoneJob 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.

TimeTriggers — Schedule HTTP requests at any time.