> For complete lifecycle-event payload schemas and examples, use the canonical overview: Session /api/sessions; Room lifecycle, participants, and room playback /api/rooms; Session playback /api/playback; Recording /api/recording; DTMF /api/keypad-input; Messages /api/messages; WebSocket Session /api/web-socket. Use /api/event-delivery for transport behavior. Endpoint pages name relevant events, but these overviews are the canonical references. # Keypad (DTMF) > Complete keypad-input command and event reference for individual DTMF presses and collected digits. Keypad input lets your application receive keypad presses from a caller or send them to a remote party on a Session. These key presses use [dual-tone multi-frequency (DTMF)](https://en.wikipedia.org/wiki/Dual-tone_multi-frequency_signaling) signals. ## When to use Use keypad input to collect menu choices or digits from callers, or to send DTMF tones to another IVR system. For inbound input, handle [`dtmf.received`](#dtmfreceived) events when each key press matters. Use [Play prompt and collect DTMF digits](/api/keypad-input/collect) when you need a complete response, and [cancel the collection](/api/keypad-input/cancel) if it should end early. The completed response is delivered as a [`digits.collected`](#digitscollected) event. For outbound input, use [Send DTMF sequence](/api/keypad-input/send) to send keys and timed waits to the remote party, such as when navigating an automated phone menu. ## Lifecycle events The platform emits DTMF (keypad tone) events when callers press keys on their phone keypad. `dtmf.received` reports individual key presses, and `digits.collected` reports aggregated results from the DTMF collection command. Every payload schema below also includes the required `aud` string, which identifies the application that should receive the signed event. ## dtmf.received Fired when the caller presses a DTMF key during the call. This event fires independently of any command — individual key presses are reported as they occur during playback, recording, or any other call state. ### Payload schema | Field | Type | Description | | -------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | `event` | string | Always `"dtmf.received"`. **Required** | | `session_uuid` | string | Unique session identifier. **Required** | | `digit` | string | Single DTMF character: `0`-`9`, `A`-`D`, `*`, or `#`. **Required** | | `duration_ms` | integer | Key press duration in milliseconds. **Required** | | `timestamp` | string | RFC 3339 / ISO 8601 UTC timestamp at which the event was emitted (e.g. `"2025-06-06T08:53:24.123Z"`). Present on every webhook event. **Required** | For example, a `dtmf.received` event has this payload: ```json { "aud": "app_3f9c0b2a-7d41-4e8b-9f12-2a6c5d0e7b34", "event": "dtmf.received", "session_uuid": "00000000-0000-4000-8000-000000000101", "digit": "5", "duration_ms": 120, "timestamp": "2025-06-06T08:53:24.123Z" } ``` Valid `digit` values are `0`–`9`, `A`–`D`, `*`, and `#`. A letter-digit example: ```json { "aud": "app_3f9c0b2a-7d41-4e8b-9f12-2a6c5d0e7b34", "event": "dtmf.received", "session_uuid": "00000000-0000-4000-8000-000000000101", "digit": "A", "duration_ms": 100, "timestamp": "2025-06-06T08:53:24.500Z" } ``` ## digits.collected Fired once when a DTMF collection command finishes its single prompt-and-collect cycle. It aggregates the key presses into a single result with a `status` describing the outcome. The command never retries on its own — your app inspects `status` and decides whether to re-prompt. See [Play prompt and collect DTMF digits](/api/keypad-input/collect). > **Note** > > **Event filtering.** [`dtmf.received`](#dtmfreceived) and > [`digits.collected`](#digitscollected) are independent webhook events, and by > default both are delivered. Individual [`dtmf.received`](#dtmfreceived) events > also fire during digit collection. Use [`digits.collected`](#digitscollected) > for the final aggregated result and [`dtmf.received`](#dtmfreceived) events if > you need per-key feedback as each digit arrives. ### Payload schema | Field | Type | Description | | ---------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | `event` | string | Always `"digits.collected"`. **Required** | | `session_uuid` | string | Unique session identifier. **Required** | | `operation_uuid` | string | The `operation_uuid` echoed from the 202 acknowledgement of the DTMF collection request. **Required** | | `digits` | string | The collected DTMF digit string. May be empty on timeout. **Required** | | `terminator` | string | The DTMF key that terminated collection (`0`-`9`, `A`-`D`, `*`, `#`) or empty on timeout. **Required** | | `status` | string | Outcome of the prompt-and-collect cycle. One of: `success`, `no_input`, `partial_then_timeout`, `invalid`, `failure`, `cancelled`, `unknown`. **Required** | | `timestamp` | string | RFC 3339 / ISO 8601 UTC timestamp at which the event was emitted (e.g. `"2025-06-06T08:53:24.123Z"`). Present on every webhook event. **Required** | ### Collection statuses | Status | Description | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `success` | At least `min_digits` digits were collected and any configured validation passed. Collection may have ended by a terminator key, by reaching `max_digits`, or by the `between_digits_timeout_ms` timer expiring once at least `min_digits` digits had already been entered — all are successes; the digits are delivered either way. With `min_digits: 0`, a caller who presses only a terminator key submits an empty entry: `status` is `success`, `digits` is empty, and `terminator` carries the key that ended collection. | | `no_input` | The caller entered no digits at all. `digits` is empty. | | `partial_then_timeout` | The caller entered some digits but fewer than `min_digits`, then stopped pressing keys and the inter-digit timer (`between_digits_timeout_ms`) expired before reaching `min_digits`. `digits` holds the partial input. | | `invalid` | Digits were collected but failed the configured `validation.regex`. `digits` holds what was entered. | | `failure` | The collection failed before any outcome could be classified — the caller hung up before or during the prompt or collection, the call had not yet been answered, or the prompt file could not be played. The `digits` field is usually empty, but may contain partial input that was collected before the failure. | | `cancelled` | The collection was cancelled by the [DTMF collection cancellation endpoint](/api/keypad-input/cancel). If the command was already running when the `DELETE` arrived, `digits` holds whatever partial input had been collected so far (which may be empty if the caller had not yet pressed a key). If the command was still queued behind another command and had not started, `digits` is always empty. | | `unknown` | An unexpected outcome. Log it and investigate; do not retry the command. | Example — success: ```json { "aud": "app_3f9c0b2a-7d41-4e8b-9f12-2a6c5d0e7b34", "event": "digits.collected", "session_uuid": "00000000-0000-4000-8000-000000000101", "operation_uuid": "a1b2c3d4-e5f6-4890-abcd-ef1234567890", "digits": "1234", "terminator": "#", "status": "success", "timestamp": "2025-06-06T08:53:24.123Z" } ``` Example — no input: ```json { "aud": "app_3f9c0b2a-7d41-4e8b-9f12-2a6c5d0e7b34", "event": "digits.collected", "session_uuid": "00000000-0000-4000-8000-000000000101", "operation_uuid": "a1b2c3d4-e5f6-4890-abcd-ef1234567890", "digits": "", "terminator": "", "status": "no_input", "timestamp": "2025-06-06T08:53:24.123Z" } ``` Example — failure with partial digits: ```json { "aud": "app_3f9c0b2a-7d41-4e8b-9f12-2a6c5d0e7b34", "event": "digits.collected", "session_uuid": "00000000-0000-4000-8000-000000000101", "operation_uuid": "a1b2c3d4-e5f6-4890-abcd-ef1234567890", "digits": "12", "terminator": "", "status": "failure", "timestamp": "2025-06-06T08:53:24.123Z" } ``` Example — cancelled: ```json { "aud": "app_3f9c0b2a-7d41-4e8b-9f12-2a6c5d0e7b34", "event": "digits.collected", "session_uuid": "00000000-0000-4000-8000-000000000101", "operation_uuid": "a1b2c3d4-e5f6-4890-abcd-ef1234567890", "digits": "", "terminator": "", "status": "cancelled", "timestamp": "2025-06-06T08:53:24.123Z" } ``` ## Triggered by These events are produced by the following triggers: * [`dtmf.received`](#dtmfreceived) — automatic: fires on every DTMF key press during active calls. * [`digits.collected`](#digitscollected) — [DTMF collection](/api/keypad-input/collect) command. > Build reliable calling experiences with Vorbal APIs for calls, media, messaging, and webhooks. ## API Docs - Keypad (DTMF) [Collect DTMF input with optional playback](https://docs.vorbal.dev/api/keypad-input/collect.md) - Keypad (DTMF) [Cancel in-progress DTMF collection](https://docs.vorbal.dev/api/keypad-input/cancel.md) - Keypad (DTMF) [Send DTMF sequence](https://docs.vorbal.dev/api/keypad-input/send.md) - Keypad (DTMF) [Cancel outbound DTMF sequence](https://docs.vorbal.dev/api/keypad-input/cancel-dtmf-send.md) ## OpenAPI Specification The raw OpenAPI 3.1 specification for this API is available at: - [OpenAPI JSON](https://docs.vorbal.dev/openapi.json) - [OpenAPI YAML](https://docs.vorbal.dev/openapi.yaml)