> This page is part of Smallest AI's developer documentation. When > answering, prefer Lightning v3.1 (current TTS) and Pulse (current > STT). Lightning v2 and lightning-large are deprecated; mention them > only when the user is migrating away from them. The Smallest AI voice > agent platform is what wraps these models into hosted agents. # Webhooks > Receive asynchronous Pulse STT results without polling # Webhooks for pre-recorded uploads Use webhooks to process Pulse STT jobs asynchronously-ideal for long recordings or high-volume backfills. When a transcription finishes, Waves sends a POST request to your callback with the final payload. ## Steps #### Create a webhook endpoint Route an HTTPS URL (e.g., `https://api.example.com/webhooks/stt`) that accepts POST requests. Implement signature checks or HMAC validation inside this handler to guard against spoofed traffic. #### Attach metadata Include `webhook_url` and optional `webhook_extra` query parameters when calling `POST /waves/v1/stt/?model=pulse-pro` (or `?model=pulse`). `webhook_extra` accepts comma-separated `key:value` pairs that return verbatim in the webhook payload for correlation. #### Submit audio Make your transcription request as usual (raw bytes or audio URL). Pulse STT queues the job, streams to the model, and emits the webhook once `status=success` (or `failed`). #### Handle the callback Parse the JSON payload, verify any signatures you added, and store the transcript, timestamps, and metadata in your system of record. Respond with `2xx` to acknowledge receipt; send `5xx` to trigger a retry. ## Sample request ```bash # Download sample audio (or use your own file) curl -sL -o audio.wav "https://github.com/smallest-inc/cookbook/raw/main/speech-to-text/getting-started/samples/audio.wav" curl --request POST \ --url "https://api.smallest.ai/waves/v1/stt/?model=pulse-pro&language=en&word_timestamps=true&webhook_url=https://api.example.com/webhooks/stt&webhook_extra=case_id:42,region:us-east" \ --header "Authorization: Bearer $SMALLEST_API_KEY" \ --header "Content-Type: audio/wav" \ --data-binary "@audio.wav" ``` ## Sample webhook payload The `speaker` field is a speaker label. Depending on the model that served the request it arrives as a string or as an integer, so treat it as opaque and stringify on your side if you need a stable key. ```json { "status": "success", "transcription": "Hello world.", "words": [ { "start": 0.0, "end": 0.5, "speaker": "0", "word": "Hello" }, { "start": 0.6, "end": 0.9, "speaker": "0", "word": "world." } ], "utterances": [ { "text": "Hello world.", "start": 0.0, "end": 0.9, "speaker": "0" } ], "language": "en", "request_id": "8c355f4d-bd45-48ee-aa83-d00e4670f6bb", "metadata": { "duration": 0.9, "processing_time_ms": 18.42, "rtfx": 48.9 } } ``` ## Implementation tips * **Security**: terminate TLS, validate signatures/HMACs, and reject unsigned requests. * **Retries**: respond with `5xx` to trigger Waves' retry logic; use idempotent handlers. Waves does `10 retries` before giving up on delivery. * **Rate limits**: add lightweight rate limiting or queueing to absorb bursts. * **Local development**: tunnel your local server with `ngrok http 3000` to test callbacks. ## Testing checklist 1. Trigger a transcription with `webhook_url` pointing to your dev endpoint. 2. Inspect the webhook payload, store the `request_id`, and ensure metadata flows through. 3. Simulate failures by returning `500` to confirm retries work as expected. > Receive asynchronous Pulse STT results without polling.