API versioning
Teler versions the webhook wire format and event catalog with dated versions. Each Voice App and each SIP Trunk is pinned to exactly one version. Existing pins never change on their own. You decide when to migrate.
Two versions exist today:
| Version | Status | Wire format | New events |
|---|---|---|---|
2025-08-01 | Original: frozen, still supported for existing pins | Flat body, direct HTTP delivery | none |
2026-06-01 | Current: recommended for new integrations | Envelope with id, type, api_version, unified vocabulary | leg.* |
Only the webhook side is versioned. Call Flow JSON, SDK method signatures, and the media-streaming WebSocket protocol are not affected by this pin.
Which version should I choose?
Section titled “Which version should I choose?”Both versions run in parallel. Traffic on one has no effect on the other.
Pinning a version
Section titled “Pinning a version”The pin lives on the owning resource (Voice App or SIP Trunk) as the
webhook_api_version field. Set it when you create the resource in
the Teler dashboard; change it later by
editing the same resource.
Valid values today: "2025-08-01" or "2026-06-01". Any other value is
rejected.
How to tell which version fired an event
Section titled “How to tell which version fired an event”Two ways. Use whichever fits your handler.
HTTP header (present on every 2026-06-01 request; absent on 2025-08-01):
X-Teler-Api-Version: 2026-06-01Body field (2026-06-01 only, at the envelope root):
{ "api_version": "2026-06-01", "type": "call.initiated", ... }If neither is present, the event is 2025-08-01.
Concrete differences: 2025-08-01 → 2026-06-01
Section titled “Concrete differences: 2025-08-01 → 2026-06-01”The wire format is a hard break: parse the two shapes with separate code paths, don’t try to unify. This section is exhaustive for that reason.
Body envelope
Section titled “Body envelope”2025-08-01 sends a flat body with the event name inside:
{ "event": "call.initiated", "account_id": "<uuid>", "call_app_id": "<uuid>", "data": { "call_id": "<uuid>", "from": "...", "to": "...", ... }}2026-06-01 wraps the payload in an envelope:
{ "id": "evt_01H...", "type": "call.initiated", "api_version": "2026-06-01", "occurred_at": "2026-06-01T11:30:45.123Z", "account_id": "<uuid>", "call_id": "cs_01H...", "leg_id": null, "sip_trunk_id": null, "data": { "call_id": "cs_01H...", "from": "...", "to": "...", ... }}See Event envelope for the full field reference.
Field renames inside data
Section titled “Field renames inside data”Legacy 2025-08-01 | Current 2026-06-01 | Notes |
|---|---|---|
hangup_time | ended_at | Unified across call, leg, and sip-trunk events. |
hangup_source | ended_by | Values also normalized, see below. |
duration (integer, seconds) | duration_seconds (integer, seconds) | Same units, explicit name. |
answer_time | answered_at | Naming aligned with ended_at. |
direction: "incoming" | direction: "inbound" | Enum values renamed. |
direction: "outgoing" | direction: "outbound" | Enum values renamed. |
| none | reason | New field: normalized terminate reason (e.g. "no_answer", "busy"). |
ended_by values (was hangup_source)
Section titled “ended_by values (was hangup_source)”| Legacy value | Current value |
|---|---|
"caller" | "caller" |
"callee" | "callee" |
"platform" / "system" | "system" |
Identifiers
Section titled “Identifiers”2025-08-01 used raw FreeSWITCH UUIDs. 2026-06-01 returns prefixed,
resource-typed IDs everywhere:
| Prefix | Refers to | Where you see it |
|---|---|---|
acc_ | Account | Envelope account_id |
va_ | Voice App | Envelope voice_app_id |
st_ | SIP Trunk | Envelope sip_trunk_id |
cs_ | Call session (a whole call, root of the leg tree) | Envelope call_id for Voice App calls |
cl_ | Call leg (one participant leg on a call) | Envelope leg_id, and call_id inside per-leg events |
ms_ | Media stream (a bridged audio stream on a call) | data.stream_id in media-streaming events |
evt_ | Webhook event | Envelope id; also X-Teler-Event-Id header |
rec_ | Recording | data.recording_id in recording events |
pb_ | Playback (from the play verb / control API) | data.playback_id in play events |
data.call_id and envelope-root call_id always agree on 2026-06-01.
The wire field on the envelope is voice_app_id (not call_app_id).
Headers
Section titled “Headers”| Header | 2025-08-01 | 2026-06-01 |
|---|---|---|
X-Teler-Timestamp | ✓ | ✓ |
X-Teler-Signature | ✓ | ✓ |
X-Teler-Event-Id | none | ✓ (echoes envelope id) |
X-Teler-Api-Version | none | ✓ |
X-Teler-Source | none | ✓ (live | replay | recovery) |
Signature scheme is identical: HMAC-SHA256(secret, "{timestamp}.{raw_body}").
See Signing & verification.
Events
Section titled “Events”Events emitted on 2025-08-01:
call.initiated, call.answered, call.completed, call.failed,
stream.*, recording.completed, recording.failed.
Events emitted on 2026-06-01, everything above plus:
| Event | What it means |
|---|---|
leg.created | A new participant leg was originated (multi-party calls, dials). |
leg.answered | That leg was answered. |
leg.completed | That leg ended. |
Idempotency
Section titled “Idempotency”2025-08-01 has no idempotency key. Duplicate deliveries are
distinguishable only by content comparison.
2026-06-01 guarantees stable envelope id values across retries and
replays. Dedupe on id. See Retries & idempotency.
What is not different
Section titled “What is not different”| Aspect | Behavior on both versions |
|---|---|
| Signing algorithm | HMAC-SHA256 over "{timestamp}.{raw_body}" |
| Retry policy | Up to 5 attempts, exponential backoff |
| Timeout | 5 seconds for your response |
| Delivery ordering | Best-effort per call_id, use timestamps to reorder |
| Which secret is used | The one on the owning Voice App / SIP Trunk |
Migration checklist
Section titled “Migration checklist”Do these in order: the goal is that your handler can serve
2026-06-01 traffic successfully before you flip the pin.
- Deploy a handler that switches on the
X-Teler-Api-Versionheader (or on the presence of theapi_versionbody field). Keep the legacy parser online. - Point one low-traffic Voice App / SIP Trunk at
2026-06-01in staging. Confirm your handler processes at least one call end-to-end. - Flip production resources one at a time. Watch for
call.completedat the same rate on the new pin. - Once nothing is emitting
2025-08-01, delete the legacy parser.
Deprecation policy
Section titled “Deprecation policy”2025-08-01is frozen but continues to work indefinitely for existing pins. No end-of-life date is set.- New dated versions ship every 6-12 months when there is a wire-format change that isn’t backward-compatible.
- Announcements land in the Release notes and, for pinned customers, in an email to the account owner.