Webhooks
Automatically notify external services when timer events occur — trigger lighting cues, switch OBS scenes, post to Slack, and more, all without writing a single line of polling code.
What Are Webhooks?
Webhooks are automated HTTP POST requests that Tevyr sends to a URL you specify whenever a timer or session event occurs. Instead of constantly checking for updates (polling), your integration receives a JSON payload the moment something happens.
Each webhook can subscribe to one or more event types, and Tevyr handles retries, signing, and delivery logging automatically.
How Webhooks Work
Timer warning, session start, overtime...
Automatic HTTP POST to your URL
JSON payload + HMAC signature
Webhook Events
Tevyr supports 11 webhook events across two categories:
Timer Events
| Event | Trigger |
|---|---|
| timer.started | A timer begins counting down |
| timer.paused | A running timer is paused |
| timer.reset | A timer is reset to its original duration |
| timer.warning | The remaining time crosses the warning threshold (e.g., 2 minutes left) |
| timer.critical | The remaining time crosses the critical threshold (e.g., 30 seconds left) |
| timer.finished | The timer reaches zero |
timer.warning, timer.critical, and timer.finished are edge-triggered — they fire the moment the remaining-seconds value crosses from above the threshold to at or below it, and they will not re-fire while the timer stays past the threshold. Pausing and resuming past the threshold does not re-fire them. Resetting (or otherwise moving back above the threshold) re-arms the edge, so the next crossing fires once again. The other 8 events (timer.started/paused/reset, all session.*) are imperative — they fire whenever their action happens, no edge logic.
Session Events
| Event | Trigger |
|---|---|
| session.started | A session becomes the active session |
| session.completed | A session is marked as completed |
| session.changed | The active session switches to a different session |
| session.overtime | A session enters overtime (timer passed zero and is still running) |
| session.upcoming | The next session in the schedule is queued and about to begin |
Two delivery modes
Webhooks ship with two destinations, chosen at create time:
- URL mode — POST the JSON payload (shape below) to a URL you paste. HMAC-signed, retry-aware. The classic webhook flow.
- Integration mode — fire a saved HTTP integration (Slack, Discord, Notion, Resend, Generic HTTP, etc.) using that integration's template-specific request shape. The webhook event's data fills in
{{ctx.*}}placeholders in the integration's action fields.
Use URL mode for OBS / DMX / your custom endpoint. Use Integration mode when you want Tevyr's webhook to format the call for you (e.g. post to Slack on timer.warning without writing any code).
Creating a Webhook
Open the Webhooks panel from the control bar (or the integrations section in your event settings), click Add Webhook, and walk through the wizard:
Step 1 — Setup (pick destination)
The top of the Setup step has a segmented Destination picker with two tabs:
- Custom URL — paste your own HTTPS endpoint. Tevyr POSTs the JSON payload (shape below) with an HMAC signature. Optionally add custom request headers.
- Integration — fire one of your saved HTTP integrations (Slack, Discord, Notion, Resend, vMix, OpenAI, Hue, WLED, Anthropic, Zapier, or Generic HTTP). Tevyr formats the call for you using the template's action shape.
If you pick Custom URL
- Paste the HTTPS URL (e.g.
https://hooks.example.com/timer-events) - Give it a name (e.g. "Stage Lighting Controller")
- (Optional) Click + Add header to attach static custom headers — useful for tokens like
Authorization: Bearer …. Reserved names (X-Webhook-*,Host,Content-Length) are blocked. - Body type is fixed to JSON; signature header is auto-generated.
If you pick Integration
- Connection dropdown — pick from your saved integrations. Each row shows the template icon and the connection's display name (e.g. "Slack — Tevyr Workspace").
- Don't see your integration? Click + Connect new integration at the bottom of the dropdown — Tevyr opens the Integrations modal in-place so you can add a fresh connection without losing the wizard. Once you save the new connection, focus jumps back to the dropdown with the new connection pre-selected.
- Action dropdown — pick what the integration should do (e.g. Slack
send_message, Notionappend_to_page, Resendsend_email). Action options come from the template. - Action fields — fill in the action's parameters. Every URL / header / body / text / JSON field flagged
interpolatable: truein the template supports{{ctx.*}}placeholders that resolve at fire time. (Dropdowns, numeric, and enum fields don't show the button.) - Variables menu — each interpolatable field has a small
{ }Variables button next to it. Click it to open a grouped menu of every available token. Picking a row inserts{{path}}at the cursor (replacing any current selection), so partial typing + click-to-finish works.
The full canonical variable set Tevyr passes to interpolation is below — 15 tokens across 4 groups.
| Group | Token | Sample value |
|---|---|---|
| Event | {{event.name}} | timer.warning |
| Event | {{event.id}} | evt-abc123 |
| Event | {{event.title}} | My Conference |
| Event | {{event.timestamp}} | 2026-03-31T14:30:00Z |
| Session | {{session.id}} | session-1 |
| Session | {{session.title}} | Opening Keynote |
| Session | {{session.speaker_name}} | Jane Doe |
| Session | {{session.duration_seconds}} | 1800 |
| Timer | {{timer.remaining_seconds}} | 120 |
| Timer | {{timer.duration_seconds}} | 1800 |
| Timer | {{timer.is_running}} | true |
| Timer | {{timer.warning_threshold}} | 120 |
| Timer | {{timer.critical_threshold}} | 30 |
| Now | {{now.iso}} | 2026-03-31T14:30:00Z |
| Now | {{now.unix_ms}} | 1743431400000 |
Step 2 — Pick events
Select one or more event types from the 11 listed below (Timer or Session). The wizard shows checkboxes grouped by category.
Step 3 — Test
The Test step adapts to the destination mode:
- Custom URL — sends a sample payload (
timer.startedshape) withX-Webhook-Test: 1header. Reports HTTP status + response time. No signature is sent on this test ping (your secret hasn't been issued yet). - Integration — fires the action against the live integration using a sample event context. Reports success/failure inline. No log entry is written (this is a probe, not a delivery).
Step 4 — Save
Click Create to save the webhook. For Custom URL destinations, Tevyr generates an HMAC signing secret and displays it once — copy it immediately, then click I've saved it to dismiss.
The HMAC signing secret is only shown once when the webhook is created. Copy it and store it securely. You'll need it to verify that incoming payloads are genuinely from Tevyr. Integration mode doesn't need a secret — the integration's existing credentials are used for outbound auth.
Managing your webhooks
Once saved, each webhook shows up as a card in the Webhooks panel. Open the panel any time to test, edit, disable, or delete a webhook — and to see its 3 most-recent delivery records inline.
The webhook card
Every webhook renders as a card with this shape:
- Logo tile (left) — the template's brand icon (Slack mark, Notion logo, etc.) for Integration mode; a generic violet webhook icon for Custom URL mode. Custom URL mode stays brand-neutral — Tevyr does not infer a brand from the URL host.
- Headline — the name you gave the webhook (or the connection name, or
${Template} action, falling back toUntitled webhook). - Status pills (next to headline):
- Active (green dot) — webhook is enabled and ready to fire
- Disabled (grey dot) — toggle is off; webhook will not fire
- Last failed (red dot) — only shown when the webhook is enabled AND the most-recent delivery returned non-2xx or timed out. Disabled webhooks never show this pill, even if their last attempt failed.
- Meta strip (one line, dot-separated):
- Integration mode:
Slack · Tevyr Workspace · OAuth · Triggers on timer.warning, timer.finished +2 - URL mode:
Custom URL · HMAC signed · Triggers on timer.started, session.changed +1 - The trigger summary shows the first 3 selected events with
+Noverflow.
- Integration mode:
Header buttons
Each card has six icon-only action buttons in the header:
| Icon | Label | What it does |
|---|---|---|
| Lightning | Test | Sends a sample payload to the webhook's destination. Subject to the rate limits below. |
| File-text | View recent log records | Toggles the last-3 delivery records inline under the card. |
| Chevron | Expand | Reveals the card body — URL/secret/headers for URL mode, action field summary for Integration mode. |
| Toggle switch | Enable / Disable | Flips `enabled`. Disabled webhooks are skipped on fire and lose the 'Last failed' pill. |
| Pencil | Edit | Re-opens the wizard so you can change events, action fields, or the destination. |
| Trash | Delete | Permanently removes the webhook and its delivery history. |
Expanded card body
Click the chevron to reveal the body. What you see depends on the mode:
- URL mode — a URL chip (label + monospace value + copy button), a Secret chip with an eye toggle that swaps between the masked form (
…lastEightChars) and the full secret, and a collapsible Custom headers section listing any static headers you attached during setup. - Integration mode — the saved action label (e.g.
Slack send_message) plus each action field with its literal configured value.{{ctx.*}}placeholders are shown unresolved here, since interpolation happens at fire time using the live event context.
Payload Format
Every webhook delivery sends a JSON payload with this structure:
{
"event": "timer.warning",
"timestamp": "2026-03-31T14:30:00.000Z",
"event_id": "evt_abc123",
"event_title": "Annual Conference 2026",
"session": {
"id": "sess_xyz789",
"title": "Opening Keynote",
"speaker_name": "Jane Smith",
"duration_seconds": 1800
},
"timer": {
"remaining_seconds": 120,
"duration_seconds": 1800,
"is_running": true,
"warning_threshold": 120,
"critical_threshold": 30
}
}
HTTP Headers
Every delivery includes these headers:
| Header | Description |
|---|---|
| Content-Type | application/json |
| User-Agent | Tevyr-Webhooks/1.0 |
| X-Webhook-Event | The event type (e.g., timer.warning) |
| X-Webhook-Timestamp | ISO 8601 timestamp of when the payload was generated |
| X-Webhook-Delivery-Id | Unique ID for this delivery attempt (use for idempotency) |
| X-Webhook-Signature | HMAC-SHA256 signature for payload verification |
Security
HMAC-SHA256 Signatures
Every payload is signed using your webhook's secret key. To verify a delivery is genuine:
- Read the
X-Webhook-Signatureheader - Compute
HMAC-SHA256(secret, rawRequestBody) - Compare the computed signature with the header value
This prevents anyone from sending fake payloads to your endpoint.
URL Requirements
- HTTPS required — Webhook URLs must use HTTPS. HTTP URLs are rejected.
- SSRF protection — Tevyr blocks URLs pointing to private IP ranges (
10.x.x.x,192.168.x.x,127.0.0.1, etc.), localhost, and internal hostnames. DNS resolution is checked at delivery time to prevent DNS rebinding attacks.
Delivery & Reliability
| Property | Value |
|---|---|
| Timeout | 10 seconds per delivery attempt |
| Retries | 3 attempts on failure |
| Retry delays | 1 second, 3 seconds, 10 seconds |
| Success codes | Any 2xx HTTP status |
| Delivery log | Last 100 deliveries per webhook |
| Idempotency | Use X-Webhook-Delivery-Id to deduplicate retries |
If your endpoint returns a non-2xx status or times out, Tevyr retries with exponential backoff. After 3 failed attempts, the delivery is marked as failed and logged.
Testing & Delivery Logs
Test triggers
There are two places to fire a test payload, each governed by its own rate-limit bucket:
- From a saved webhook card — click the lightning icon to send a sample payload to the webhook's destination. Limit: 5 tests per webhook per 60 seconds.
- From the wizard Test step (before Save) — Tevyr POSTs a sample payload to your URL (or fires the action against your integration) so you can verify the wiring before committing. The webhook has no ID yet, so this bucket keys on the event passcode. Limit: 5 test pings per passcode per 60 seconds.
Both limits return HTTP 429 with the body "Too many test requests. Please wait a minute before trying again.". The buckets are independent — hitting one does not consume the other.
The wizard's per-passcode bucket fills faster than per-webhook when you're testing multiple URLs in sequence (every wizard click counts against the same passcode). If you hit 429 mid-setup, wait ~60 seconds before resuming.
Delivery records
When you click the View recent log records button on a webhook card, the 3 most-recent delivery records appear inline below the card header. The server stores up to 100 deliveries per webhook in the database — older deliveries are pruned automatically, and the inline panel always shows the 3 newest from that set.
Each row displays:
- Status badge — green ✓ for success, red ✗ with HTTP code for failure (e.g.
✗ 500,✗ timeout) - Event type — the event that fired the delivery (e.g.
timer.warning) - Request letter —
A/B/C, whereAis the most-recent attempt - Relative timestamp —
2m ago,1h ago, etc. - Chevron — click the row to expand the body
The expanded body shows the full parsed JSON payload, the HTTP response code your endpoint returned, and the total delivery duration in milliseconds.
Live updates
Delivery records stream in real time. When the server fires a webhook, it broadcasts a WEBHOOK_DELIVERY message over WebSocket to every connected control surface. The Webhooks panel listens for these broadcasts and:
- Prepends the new delivery to the inline panel (it becomes the new
A, the oldAbecomesB, and so on — without a refetch). - Flips the card's Last failed pill on or off depending on whether the new delivery succeeded.
No manual refresh is required. Open the panel during an event and watch every fire land in real time — useful for live debugging when a Slack post or DMX cue isn't behaving.
Use the delivery records to debug integration issues, audit signature failures, and monitor reliability over time.
Use Cases
What you can automate
Timer hits warning threshold
Lights change to yellow
Session starts
Camera switches to speaker
Speaker finishes
'Keynote done!' posted to #events
Critical threshold hit
Gentle chime plays in venue
Next session queued
'You're up in 5 minutes!'
Session changes
Display shows new session title
Webhooks are available on Premium and Enterprise plans. See pricing
Stage Lighting (DMX)
OBS / vMix Scene Switching
Slack / Teams Notifications
Confidence Monitors
Green Room Alerts
Sound System Cues
Recording Control
Digital Signage
Webhooks are available on Premium (up to 5 per event) and Enterprise (up to 20 per event) plans. See pricing