> 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

```json
{
  "status": "success",
  "transcription": "Hello world.",
  "words": [
    { "start": 0.0, "end": 0.5, "speaker": "speaker_0", "word": "Hello" },
    { "start": 0.6, "end": 0.9, "speaker": "speaker_0", "word": "world." }
  ],
  "utterances": [
    { "text": "Hello world.", "start": 0.0, "end": 0.9, "speaker": "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.