Schedule a trigger
A "trigger" is a scheduled HTTP request.
Any HTTP method
Our API accepts any HTTP method on the /schedule endpoint. We repeat your exact method when firing the trigger.
Send us a PUT? We fire a PUT. A POST with a JSON body? We forward the exact body. A GET? We fire a GET with no body.
Headers
Our API is designed for minimal changes to your existing HTTP calls. It heavily relies on HTTP headers prefixed with ttr-.
Think of TimeTriggers as a proxy that repeats your request at the scheduled time. We transparently pass any headers you set (except ttr- prefixed ones).
ttr- prefixed headers
Headers prefixed with ttr- are used to authenticate against our service and to set scheduling options. They are not forwarded to your target URL.
Request
There are 3 required headers to schedule a trigger:
ttr-api-keyheader : your API keyttr-urlheader : the URL you want to hit at the scheduled timettr-scheduled-atheader : when to execute the request. Either a one-shot date in ISO 8601 format (optionally combined with date operations likenow | add 2d), or acron(...)expression for recurring triggers — see Recurring triggers.
All request headers
| Header | Example | Description |
|---|---|---|
ttr-urlheader | https://httpbin.org/post | The URL to hit |
ttr-api-keyheader | ttr_abcdef123456 | Your API key |
ttr-scheduled-atheader | 2025-06-01T09:00:00Z or cron(0 9 * * 1-5) | When to fire the trigger. ISO 8601 for one-shot, cron(...) for recurring |
ttr-custom-keyheader | billing:customer-42:2025-03 | Optional. Custom key for idempotency. See Custom trigger keys |
Request body
For methods that support a body (POST, PUT, PATCH, DELETE), we store and forward the raw request body as-is.
For bodyless methods (GET, HEAD, OPTIONS), no body is read or stored.
Response
Successful response
A JSON body with the following fields:
| Field | Example | Description |
|---|---|---|
triggerId | clxyz123abc | Unique ID. For one-shot triggers this is the trigger ID; for recurring triggers this is the generator ID. Same value to hand back to /cancel either way. |
scheduledAt | 2025-06-01T09:00:00.000Z | For one-shot triggers, the resolved schedule time. For recurring triggers, the time of the next instance. |
operation | schedule | Either schedule (new) or reschedule (existing trigger or generator updated) |
kind | job | Either job (one-shot) or generator (recurring). |
monthQuotaRemaining | 455 | Triggers remaining in your monthly quota |
Status codes
| Status code | Meaning |
|---|---|
| 200OK | Trigger created or rescheduled |
| 400Bad Request | Invalid request (bad URL, bad date format, etc.) |
| 401Unauthorized | Invalid API key |
| 402Payment Required | Monthly quota exceeded |
| 404Not Found | Trigger not found (when rescheduling by ID) |
| 410Gone | Trigger already executed or cancelled |
Advanced Usage
Operations on ttr-scheduled-atheader
You can perform simple date arithmetic in the ttr-scheduled-atheader header:
| When to schedule | Header value |
|---|---|
| 2 days from now | now | add 2d |
| 1 hour before a date | 2025-06-01T09:00:00Z | add -1h |
| 30 minutes from now | now | add 30m |
Supported units: d (days), h (hours), m (minutes), s (seconds).
Try it out
Custom trigger keys
Add ttr-custom-keyheader to make your trigger idempotent. If you schedule a trigger with the same custom key, the existing trigger is rescheduled instead of creating a duplicate.
This is useful for scenarios like "send a reminder 24h before an appointment" where the appointment time might change — just re-send with the same key and we'll update the schedule.
The custom-key namespace is shared between one-shot triggers and recurring generators: a custom key is owned by at most one of them at a time. Posting a recurring trigger with the same custom key as an existing one-shot will cancel the one-shot and replace it with the generator (and vice-versa).
Recurring triggers
Use a cron(...) value in the ttr-scheduled-atheader header to schedule a recurring trigger. We call this a generator — it produces one trigger instance per cron tick.
ttr-scheduled-at: cron(0 9 * * 1-5)
The grammar is cron(<expr>[, <timezone>]):
| Header value | Meaning |
|---|---|
cron(0 9 * * 1-5) | Weekdays at 09:00 UTC |
cron(0 9 * * 1-5, Europe/Paris) | Weekdays at 09:00 Paris time |
cron(*/15 * * * *) | Every 15 minutes |
cron(0 0 1 * *) | First of every month at midnight UTC |
The timezone is optional and defaults to UTC. Any IANA timezone name is accepted.
How recurring triggers work
- The first instance is materialized immediately when you call
/schedule. The response'sscheduledAtfield is the time of that first instance. - After each instance is claimed for execution, the next instance is automatically materialized. There's always exactly one pending instance ahead.
- Each instance is a normal trigger: it gets the generator's URL, method, headers, body, and tags snapshotted at materialization time. Subsequent changes to the generator do not affect already-materialized instances.
- Rate limits apply to instances exactly like one-shot triggers. The
ttr-custom-keyheader applies to the generator itself, not to its instances.
Editing or cancelling a recurring trigger
Re-POST /schedule with the same generator ID or custom key (and a new cron expression, URL, headers, etc.) to update the generator. The currently-pending instance is replaced; past instances are unchanged.
DELETE /cancel with the generator ID or custom key cancels the generator and its currently-pending instance. Past instances (already executed or running) are kept. See Cancel a trigger.