This page documents all webhook event types, their triggers, and payload schemas.
Event Structure
All events follow this structure:
{
"id": "evt_abc123def456",
"type": "event.type",
"created_at": "2025-01-15T10:30:00Z",
"data": {
// Event-specific payload
}
}
| Field | Type | Description |
|---|
id | string | Unique event identifier. Use for deduplication. |
type | string | Event type (e.g., transcription.completed) |
created_at | string | ISO 8601 timestamp of when the event occurred |
data | object | Event-specific payload (see below) |
Recording Events
recording.created
Triggered when a new recording is created via the API.
{
"id": "evt_abc123",
"type": "recording.created",
"created_at": "2025-01-15T10:00:00Z",
"data": {
"id": "rec_xyz789",
"end_user_id": "eu_user123",
"device_id": "dev_device456",
"status": "created",
"consent_obtained": true,
"metadata": {
"session_type": "consultation"
},
"created_at": "2025-01-15T10:00:00Z"
}
}
| Field | Type | Description |
|---|
data.id | string | Recording ID |
data.end_user_id | string | Associated EndUser |
data.device_id | string | Device that will capture the recording |
data.status | string | Always created for this event |
data.consent_obtained | boolean | Whether consent was recorded |
data.metadata | object | Custom metadata attached to the recording |
data.created_at | string | When the recording was created |
Common Use Cases:
- Track new recordings in your system
- Initialize processing pipelines
- Update UI to show recording in progress
recording.uploaded
Triggered when audio upload completes successfully.
{
"id": "evt_def456",
"type": "recording.uploaded",
"created_at": "2025-01-15T10:05:00Z",
"data": {
"id": "rec_xyz789",
"end_user_id": "eu_user123",
"device_id": "dev_device456",
"status": "uploaded",
"duration_seconds": 1847.5,
"file_size_bytes": 44328000,
"format": "wav",
"started_at": "2025-01-15T09:30:00Z",
"ended_at": "2025-01-15T10:00:47Z",
"uploaded_at": "2025-01-15T10:05:00Z"
}
}
| Field | Type | Description |
|---|
data.id | string | Recording ID |
data.status | string | Always uploaded for this event |
data.duration_seconds | number | Audio duration in seconds |
data.file_size_bytes | integer | File size in bytes |
data.format | string | Audio format (wav, mp3, m4a, flac) |
data.started_at | string | When recording started (device time) |
data.ended_at | string | When recording ended (device time) |
data.uploaded_at | string | When upload completed |
Common Use Cases:
- Trigger transcription automatically
- Update recording status in your UI
- Calculate storage usage
recording.deleted
Triggered when a recording is deleted via the API.
{
"id": "evt_rst345",
"type": "recording.deleted",
"created_at": "2025-01-15T11:00:00Z",
"data": {
"id": "rec_xyz789",
"deleted_at": "2025-01-15T11:00:00Z"
}
}
| Field | Type | Description |
|---|
data.id | string | Recording ID that was deleted |
data.deleted_at | string | When the recording was deleted |
When a recording is deleted, all associated transcriptions and summaries are also deleted (cascade delete).
Common Use Cases:
- Clean up local cached data
- Update UI to remove deleted recordings
- Audit logging for compliance
Transcription Events
transcription.started
Triggered when transcription processing begins.
{
"id": "evt_uvw678",
"type": "transcription.started",
"created_at": "2025-01-15T10:05:30Z",
"data": {
"id": "txn_abc123",
"recording_id": "rec_xyz789",
"status": "processing",
"language": "en",
"created_at": "2025-01-15T10:05:20Z",
"started_at": "2025-01-15T10:05:30Z"
}
}
| Field | Type | Description |
|---|
data.id | string | Transcription ID |
data.recording_id | string | Associated recording |
data.status | string | Always processing for this event |
data.language | string | Language code (specified or auto-detected) |
data.started_at | string | When processing started |
Common Use Cases:
- Show “processing” status in UI
- Track processing time metrics
- Trigger dependent workflows
transcription.completed
Triggered when transcription processing completes successfully.
{
"id": "evt_ghi789",
"type": "transcription.completed",
"created_at": "2025-01-15T10:07:00Z",
"data": {
"id": "txn_abc123",
"recording_id": "rec_xyz789",
"status": "completed",
"text": "Hello, thank you for coming in today. How can I help you?",
"duration_seconds": 1847.5,
"segments": [
{
"start": 0.0,
"end": 2.5,
"text": "Hello, thank you for coming in today.",
"speaker": "SPEAKER_00",
"confidence": 0.95
},
{
"start": 2.8,
"end": 4.2,
"text": "How can I help you?",
"speaker": "SPEAKER_00",
"confidence": 0.97
}
],
"speakers": [
{
"id": "SPEAKER_00",
"label": "Speaker 1"
},
{
"id": "SPEAKER_01",
"label": "Speaker 2"
}
],
"language": "en",
"created_at": "2025-01-15T10:05:30Z",
"completed_at": "2025-01-15T10:07:00Z"
}
}
| Field | Type | Description |
|---|
data.id | string | Transcription ID |
data.recording_id | string | Associated recording |
data.status | string | Always completed for this event |
data.text | string | Full transcript text |
data.duration_seconds | number | Audio duration processed |
data.segments | array | Timestamped segments with speaker labels |
data.segments[].start | number | Segment start time (seconds) |
data.segments[].end | number | Segment end time (seconds) |
data.segments[].text | string | Segment text |
data.segments[].speaker | string | Speaker identifier |
data.segments[].confidence | number | Confidence score (0-1) |
data.speakers | array | List of detected speakers |
data.language | string | Detected language code |
data.completed_at | string | When transcription finished |
Common Use Cases:
- Display transcript to users
- Trigger summarization
- Index for search
- Extract action items or entities
transcription.failed
Triggered when transcription processing fails.
{
"id": "evt_jkl012",
"type": "transcription.failed",
"created_at": "2025-01-15T10:07:00Z",
"data": {
"id": "txn_abc123",
"recording_id": "rec_xyz789",
"status": "failed",
"error": {
"code": "audio_quality_insufficient",
"message": "Audio quality is too low for accurate transcription"
},
"created_at": "2025-01-15T10:05:30Z",
"failed_at": "2025-01-15T10:07:00Z"
}
}
| Field | Type | Description |
|---|
data.id | string | Transcription ID |
data.recording_id | string | Associated recording |
data.status | string | Always failed for this event |
data.error.code | string | Error code |
data.error.message | string | Human-readable error message |
data.failed_at | string | When transcription failed |
Error Codes:
| Code | Description |
|---|
audio_quality_insufficient | Audio too noisy or unclear |
audio_too_short | Recording less than 1 second |
audio_format_unsupported | Invalid audio format |
processing_error | Internal processing error |
timeout | Processing exceeded time limit |
Common Use Cases:
- Alert users to recording issues
- Retry with different settings
- Log for quality monitoring
Summary Events
summary.started
Triggered when summary generation begins.
{
"id": "evt_xyz012",
"type": "summary.started",
"created_at": "2025-01-15T10:07:30Z",
"data": {
"id": "sum_def456",
"transcription_id": "txn_abc123",
"recording_id": "rec_xyz789",
"status": "processing",
"template": "clinical",
"created_at": "2025-01-15T10:07:20Z",
"started_at": "2025-01-15T10:07:30Z"
}
}
| Field | Type | Description |
|---|
data.id | string | Summary ID |
data.transcription_id | string | Source transcription |
data.recording_id | string | Original recording |
data.status | string | Always processing for this event |
data.template | string | Template used (general, sales, clinical, legal) |
data.started_at | string | When processing started |
Common Use Cases:
- Show “generating summary” status in UI
- Track processing time metrics
- Update workflow progress indicators
summary.completed
Triggered when summary generation completes successfully.
{
"id": "evt_mno345",
"type": "summary.completed",
"created_at": "2025-01-15T10:08:00Z",
"data": {
"id": "sum_def456",
"transcription_id": "txn_abc123",
"recording_id": "rec_xyz789",
"status": "completed",
"text": "The patient presented with symptoms of seasonal allergies. Dr. Smith recommended over-the-counter antihistamines and a follow-up in two weeks if symptoms persist.",
"template": "medical_note",
"created_at": "2025-01-15T10:07:30Z",
"completed_at": "2025-01-15T10:08:00Z"
}
}
| Field | Type | Description |
|---|
data.id | string | Summary ID |
data.transcription_id | string | Source transcription |
data.recording_id | string | Original recording |
data.status | string | Always completed for this event |
data.text | string | Generated summary text |
data.template | string | Template used (if specified) |
data.completed_at | string | When summary finished |
Common Use Cases:
- Display summary to users
- Send notification that processing is complete
- Update clinical notes or CRM records
summary.failed
Triggered when summary generation fails.
{
"id": "evt_pqr678",
"type": "summary.failed",
"created_at": "2025-01-15T10:08:00Z",
"data": {
"id": "sum_def456",
"transcription_id": "txn_abc123",
"recording_id": "rec_xyz789",
"status": "failed",
"error": {
"code": "content_policy_violation",
"message": "Content could not be summarized due to policy restrictions"
},
"failed_at": "2025-01-15T10:08:00Z"
}
}
| Field | Type | Description |
|---|
data.error.code | string | Error code |
data.error.message | string | Human-readable error message |
Device Events (Optional)
These events are available for enterprise customers who enable device monitoring.
device.low_battery
Triggered when device battery falls below 20%.
{
"id": "evt_stu901",
"type": "device.low_battery",
"created_at": "2025-01-15T14:00:00Z",
"data": {
"id": "dev_device456",
"end_user_id": "eu_user123",
"battery_percent": 15,
"last_sync_at": "2025-01-15T13:55:00Z"
}
}
device.offline
Triggered when device hasn’t synced for 24+ hours.
{
"id": "evt_vwx234",
"type": "device.offline",
"created_at": "2025-01-16T10:00:00Z",
"data": {
"id": "dev_device456",
"end_user_id": "eu_user123",
"last_sync_at": "2025-01-15T10:00:00Z",
"hours_offline": 24
}
}
Device events require enterprise plan and explicit opt-in during webhook registration.
Subscribing to Events
When creating a webhook, specify which events to receive:
curl -X POST https://api.bota.dev/v1/webhooks \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/bota",
"events": [
"recording.created",
"recording.uploaded",
"recording.deleted",
"transcription.started",
"transcription.completed",
"transcription.failed",
"summary.started",
"summary.completed",
"summary.failed"
]
}'
Event Wildcards
Use * to subscribe to all events:
{
"url": "https://your-app.com/webhooks/bota",
"events": ["*"]
}
Or subscribe to all events in a category:
{
"url": "https://your-app.com/webhooks/bota",
"events": ["recording.*", "transcription.*"]
}
Event Delivery Order
Events are delivered in the order they occur, but network conditions may cause out-of-order arrival. Design your handlers to be order-independent.
Example Timeline:
recording.created — Recording entry created
recording.uploaded — Audio upload completed (may be minutes/hours later)
transcription.started — Transcription processing begins
transcription.completed — Transcription finished
summary.started — Summary generation begins
summary.completed — Summary generated
Each event is independent. You may receive transcription.completed before your handler has finished processing recording.uploaded.
Handling Tips
Async Processing
Process events asynchronously to avoid timeouts:
app.post('/webhooks/bota', async (req, res) => {
// Respond immediately
res.status(200).send('OK');
// Queue for async processing
await eventQueue.add('process-webhook', req.body);
});
Type-Safe Handlers
Use TypeScript or schema validation:
type BotaEvent =
| { type: 'recording.created'; data: RecordingCreatedData }
| { type: 'transcription.completed'; data: TranscriptionCompletedData }
| { type: 'summary.completed'; data: SummaryCompletedData };
function handleEvent(event: BotaEvent) {
switch (event.type) {
case 'recording.created':
// event.data is typed as RecordingCreatedData
break;
case 'transcription.completed':
// event.data is typed as TranscriptionCompletedData
break;
}
}