Documentation

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

1
Event Occurs

Timer warning, session start, overtime...

2
Tevyr Fires Webhook

Automatic HTTP POST to your URL

3
Your System Receives

JSON payload + HMAC signature

4
Stage LightsLights change to yellow
Automatic3 retries on failureHMAC-SHA256 signedHTTPS only

Webhook Events

Tevyr supports 11 webhook events across two categories:

Timer Events

EventTrigger
timer.startedA timer begins counting down
timer.pausedA running timer is paused
timer.resetA timer is reset to its original duration
timer.warningThe remaining time crosses the warning threshold (e.g., 2 minutes left)
timer.criticalThe remaining time crosses the critical threshold (e.g., 30 seconds left)
timer.finishedThe timer reaches zero
Threshold events fire once per crossing

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

EventTrigger
session.startedA session becomes the active session
session.completedA session is marked as completed
session.changedThe active session switches to a different session
session.overtimeA session enters overtime (timer passed zero and is still running)
session.upcomingThe 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).

Compatible integrations

All 11 integration templates accept webhook events as triggers in Integration mode: Slack, Discord, Notion, OpenAI, Anthropic, Resend, WLED, Philips Hue, vMix, Zapier, and Generic HTTP. Each template's doc page covers its setup details.

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

  1. Paste the HTTPS URL (e.g. https://hooks.example.com/timer-events)
  2. Give it a name (e.g. "Stage Lighting Controller")
  3. (Optional) Click + Add header to attach static custom headers — useful for tokens like Authorization: Bearer …. Reserved names (X-Webhook-*, Host, Content-Length) are blocked.
  4. Body type is fixed to JSON; signature header is auto-generated.

If you pick Integration

  1. Connection dropdown — pick from your saved integrations. Each row shows the template icon and the connection's display name (e.g. "Slack — Tevyr Workspace").
  2. 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.
  3. Action dropdown — pick what the integration should do (e.g. Slack send_message, Notion append_to_page, Resend send_email). Action options come from the template.
  4. Action fields — fill in the action's parameters. Every URL / header / body / text / JSON field flagged interpolatable: true in the template supports {{ctx.*}} placeholders that resolve at fire time. (Dropdowns, numeric, and enum fields don't show the button.)
  5. 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.

GroupTokenSample 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.started shape) with X-Webhook-Test: 1 header. 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.

Save your secret (URL mode only)

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 to Untitled 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 +N overflow.

Header buttons

Each card has six icon-only action buttons in the header:

IconLabelWhat it does
LightningTestSends a sample payload to the webhook's destination. Subject to the rate limits below.
File-textView recent log recordsToggles the last-3 delivery records inline under the card.
ChevronExpandReveals the card body — URL/secret/headers for URL mode, action field summary for Integration mode.
Toggle switchEnable / DisableFlips `enabled`. Disabled webhooks are skipped on fire and lose the 'Last failed' pill.
PencilEditRe-opens the wizard so you can change events, action fields, or the destination.
TrashDeletePermanently 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:

HeaderDescription
Content-Typeapplication/json
User-AgentTevyr-Webhooks/1.0
X-Webhook-EventThe event type (e.g., timer.warning)
X-Webhook-TimestampISO 8601 timestamp of when the payload was generated
X-Webhook-Delivery-IdUnique ID for this delivery attempt (use for idempotency)
X-Webhook-SignatureHMAC-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:

  1. Read the X-Webhook-Signature header
  2. Compute HMAC-SHA256(secret, rawRequestBody)
  3. 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

PropertyValue
Timeout10 seconds per delivery attempt
Retries3 attempts on failure
Retry delays1 second, 3 seconds, 10 seconds
Success codesAny 2xx HTTP status
Delivery logLast 100 deliveries per webhook
IdempotencyUse 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.

Pace your wizard clicks

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 letterA / B / C, where A is the most-recent attempt
  • Relative timestamp2m 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:

  1. Prepends the new delivery to the inline panel (it becomes the new A, the old A becomes B, and so on — without a refetch).
  2. 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

Stage Lights

Timer hits warning threshold

Lights change to yellow

Live Stream (OBS)

Session starts

Camera switches to speaker

Team Chat (Slack)

Speaker finishes

'Keynote done!' posted to #events

Sound System

Critical threshold hit

Gentle chime plays in venue

Green Room

Next session queued

'You're up in 5 minutes!'

Lobby Signage

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

Info

Webhooks are available on Premium (up to 5 per event) and Enterprise (up to 20 per event) plans. See pricing