# Advanced Features Source: https://docs.burki.dev/advanced-features Explore Burki's advanced voice AI features including IVR Explorer, memory, spam detection, BYO API keys, integrations, and automation tools. Burki Voice AI includes powerful advanced features for complex use cases—from IVR conversion and intelligent memory to spam protection and flexible API key management. *** ## Featured Advanced Capabilities **Map and convert existing IVR systems** Automatically explore any IVR phone tree, visualize the menu structure, and convert it to an AI-powered assistant with one click. * Map competitor IVRs * Document legacy systems * One-click AI conversion **Personalized, context-aware conversations** Enable your assistants to remember information across calls, manage prompt versions with A/B testing, and evaluate changes before deployment. * Persistent caller memory * Prompt versioning & canary rollouts * Evaluation datasets **AI-powered call protection** Automatically detect and terminate spam, scam, and unwanted calls in real-time using configurable LLM-based evaluation. * Custom spam scenarios * Allowlist legitimate patterns * Fail-open safety design **Bring your own provider accounts** Use your own API keys for TTS, STT, and LLM providers. Control costs directly and leverage enterprise agreements. * Per-assistant configuration * Encrypted key storage * Mix managed and BYO *** ## Data Extraction **Purpose:** Automatically extract structured data from conversations (names, phone numbers, addresses, etc.). **Use Cases:** * Lead qualification forms * Customer information collection * Order processing * Appointment scheduling * Survey responses **How it works:** 1. Define fields you want to extract 2. AI identifies relevant information during calls 3. Data is stored in structured format 4. Export or integrate with your systems **Field Configuration:** * **Field Name:** What to call the extracted data * **Field Type:** Text, number, email, phone, date, etc. * **Required:** Whether the field must be collected * **Validation:** Rules for valid data **Example Fields:** ```json theme={null} { "name": {"type": "text", "required": true}, "phone": {"type": "phone", "required": true}, "email": {"type": "email", "required": false}, "budget": {"type": "number", "required": false} } ``` Start with essential fields only. You can always add more later as you refine your process. *** ## Webhook Integrations **What are webhooks?** Real-time notifications sent to your systems when events occur. **Common Events:** * Call started * Call ended * Data extracted * Tool used (transfer, end call) * Errors occurred **Use Cases:** * Update CRM systems * Trigger follow-up emails * Log call analytics * Sync with databases * Notify team members **Setup Steps:** 1. Provide your webhook URL 2. Choose which events to send 3. Configure authentication (if needed) 4. Test webhook delivery **Webhook Payload Example:** ```json theme={null} { "event": "call_ended", "call_sid": "CA123456789", "timestamp": "2026-01-15T10:30:00Z", "duration": 180, "extracted_data": { "name": "John Doe", "phone": "+1234567890" } } ``` Ensure your webhook endpoint can handle the expected volume of calls and responds quickly (less than 3 seconds). *** ## Background Sounds **Purpose:** Add ambient sounds to create realistic environments. **Use Cases:** * Restaurant sounds for food ordering * Office ambiance for business calls * Hold music for transfers * Brand-specific audio * Noise masking **Configuration:** * **Audio File:** Upload your background sound * **Volume:** Adjust relative to voice * **Loop:** Continuous or one-time playback * **Fade:** Smooth transitions Keep background sounds subtle (10-20% volume) to avoid interfering with conversation clarity. *** ## Interruption Handling **What it does:** Handles when callers speak over the AI. **Configuration:** * **Interruption Threshold:** How many words trigger interruption * **Min Speaking Time:** Minimum time AI must speak before interruption * **Cooldown Period:** Time between interruptions * **Buffer Management:** How to handle overlapping speech **Settings:** * **Threshold:** 3 words (default) * **Min Time:** 0.5 seconds (default) * **Cooldown:** 2.0 seconds (default) Too sensitive = choppy conversations. Too insensitive = poor user experience. Test and adjust based on your use case. *** ## Outbound Calling **What it enables:** AI-initiated calls to customers. **Use Cases:** * Appointment reminders * Follow-up calls * Survey collection * Lead nurturing * Customer check-ins * Payment reminders **Configuration:** * **Campaign Setup:** Define call lists and timing * **Custom Scripts:** Tailored messages per campaign * **Scheduling:** When to make calls * **Compliance:** Do-not-call list management See [Campaigns](/campaigns) for full documentation on outbound calling campaigns. *** ## Advanced Analytics **Available Metrics:** * Call volume and duration * Completion rates * Data extraction success * Tool usage patterns * Customer satisfaction scores * Cost per call **Custom Reports:** * Performance dashboards * Trend analysis * A/B testing results * ROI calculations * Quality metrics **Integration:** * Export to BI tools * API for custom analytics * Real-time monitoring * Automated alerts *** ## Best Practices * **Start simple:** Implement basic features before advanced ones * **Test thoroughly:** Advanced features require more testing * **Monitor performance:** Track impact on call quality and user experience * **Document configurations:** Keep detailed records of settings and changes * **Security first:** Ensure proper authentication and data protection * **Compliance:** Follow industry regulations and best practices *** ## Getting Help Advanced features often require technical expertise. Consider working with your development team or contacting support for complex implementations. **Resources:** * [API Documentation](/api-reference/introduction) * [SDK Documentation](/sdks/overview) * [FAQ](/faq) * [Support](mailto:info@burki.dev) # AI Configuration (Deep Dive) Source: https://docs.burki.dev/ai-configuration Configure Burki voice AI assistants with LLM, TTS, STT, fallback providers, voice settings, speech detection, and prompt behavior. This is where you give your assistant its brain, voice, and ears! Configure how your AI thinks, speaks, and listens. *** ## The Tabbed Interface When creating or editing an assistant, you'll see three main tabs: ### What is the LLM? The LLM (Large Language Model) is the "brain" of your assistant. It understands what callers say and generates smart, helpful responses. ### Key Settings * **Provider:** Choose from OpenAI, Azure OpenAI, Anthropic, Gemini, xAI, Groq, custom HTTP, or custom WebSocket. * **API Key:** Paste your provider's API key (get it from their dashboard). * **Model:** Pick the model or deployment name (e.g., GPT-4o, Claude 3.5 Sonnet, Gemini 2.5 Flash, Groq Llama, or an Azure deployment). * **Base URL:** Set a custom API endpoint for Azure OpenAI and custom/OpenAI-compatible providers. * **Temperature:** Controls creativity. Lower = more predictable, higher = more creative.
0.7 is a good starting point! * **Max Tokens:** Limits response length.
1000 tokens ≈ 250 words. Good for phone calls. * **System Prompt:** Set your AI's personality and instructions (e.g., "You are a friendly assistant..."). * **Greeting Message:** The first thing callers hear. * **Fallback Providers:** Add up to 3 backup LLMs for reliability.
If your main LLM provider fails (e.g., rate limit, downtime), the system automatically tries your backup providers in order. This keeps your assistant reliable—even if one provider has issues.
### What is TTS? TTS turns the AI's text responses into a natural-sounding voice. Choose from multiple providers to match your quality, speed, and language needs. ### Quick Provider Guide **Premium Quality & Multilingual** 70+ languages, voice cloning, advanced controls **Ultra-Low Latency** vendor-reported low latency, phone-optimized **AI-Powered Emotions** Multilingual with emotional markup **Custom Brand Voices** Unlimited voice creation **OpenAI-Native Voices** `tts-1`, `tts-1-hd`, and `gpt-4o-mini-tts` **Enterprise and Multilingual** Azure Speech, Cartesia, plus Kokoro, Murf, Soniox, and Uplift ### Configuration Overview * **Provider:** Choose based on your needs ([Full Comparison](/tts-providers)) * **Model:** Select quality/speed balance (varies by provider) * **Voice:** Pick from available voices or use custom voices * **Language:** Select for multilingual providers * **Voice Controls:** Fine-tune stability, similarity, style ([Voice Tuning Guide](/tts-providers/voice-tuning)) * **Advanced Settings:** Encoding, sample rate, streaming options **Phone Calls:** ElevenLabs Flash v2.5 or Deepgram Aura-2 for speed **Customer Service:** ElevenLabs Bella or Inworld Ashley for warmth **Multilingual:** ElevenLabs v3, Cartesia Sonic 3, or Inworld `inworld-tts-1.5-mini` with native voices **Brand Voice:** Resemble AI with custom voice training → [See detailed recommendations](/tts-providers#quick-start-guide) **Dive Deeper:** * [TTS Provider Comparison](/tts-providers) - Choose the right provider * [Voice Tuning Guide](/tts-providers/voice-tuning) - Optimize voice settings * [Best Practices](/tts-providers/best-practices) - Performance & cost optimization * [Troubleshooting](/tts-providers/troubleshooting) - Fix common issues ### What is STT? STT converts what the caller says into text for the AI to understand. ### Key Settings * **Provider:** Choose Deepgram, ElevenLabs, Azure, Uplift, Speechmatics, Telnyx, or Soniox. * **Model:** Choose a provider model such as `nova-3`, `flux-general-en`, `scribe_v2_realtime`, `standard`, `deepgram/nova-3`, or `stt-rt-v4`. * **Language:** Choose the spoken language (or enter a custom code) * **Advanced Timing Controls:** Fine-tune speech detection (see [STT Providers](/stt-providers) for details) * **Punctuation/Interim Results/Smart Format:** Make transcripts easier to read and use * **Audio Denoising:** Remove background noise for better accuracy (uses RNNoise) * **Keywords/Keyterms:** Boost recognition of important words (e.g., company names) * If the AI misses words, try enabling denoising or adding keywords * For non-English calls, set the correct language code * Adjust silence settings if the AI cuts callers off too soon or waits too long
*** ## Best Practices * **Start simple:** Use recommended defaults, then experiment with advanced settings * **Test with real calls:** Try different voices, models, and fallback setups * **Document your changes:** Keep track of what works best for your use case *** ## What's Next? Configure interruption handling, timeouts, and conversation flow control Fine-tune speech detection timing and audio processing Deep dive into voice options and audio optimization Built-in call actions plus custom integrations with APIs, Python functions, and AWS Lambda If you get stuck, check the tooltips in the UI or visit the [FAQ](/faq). # Account and Organization API Source: https://docs.burki.dev/api-reference/account/overview Users, invitations, organization settings, API keys, provider credentials, fallback keys, and concurrency settings. Account and organization endpoints live under `/api/v1`. Other API families keep their documented prefixes, such as billing under `/v1/billing`, learning under `/api/learning`, SMS under `/sms`, and carrier webhooks at root webhook paths. ```text theme={null} https://api.burki.dev/api/v1 ``` Most endpoints accept API key, JWT bearer token, or dashboard session auth. Some sensitive endpoints require a verified user or organization admin. Several organization endpoints accept or return secrets such as provider API keys, SIP credentials, carrier credentials, or one-time user API keys. Treat full responses as sensitive and avoid logging them. ## Current User ### Get Current User ```http theme={null} GET /api/v1/users/me Authorization: Bearer YOUR_TOKEN ``` Returns the authenticated user profile. ### Update Current User ```http theme={null} PUT /api/v1/users/me Authorization: Bearer YOUR_TOKEN Content-Type: application/json ``` ```json Request theme={null} { "first_name": "Ada", "last_name": "Lovelace", "email": "ada@example.com", "preferences": { "timezone": "America/New_York" } } ``` ### Change Password ```http theme={null} POST /api/v1/users/me/change-password Authorization: Bearer YOUR_TOKEN Content-Type: application/json ``` ```json Request theme={null} { "current_password": "old-password", "new_password": "new-strong-password" } ``` OAuth users can set their first password with an empty `current_password`. ## Organization Users and Invitations ### List Users ```http theme={null} GET /api/v1/users Authorization: Bearer YOUR_TOKEN ``` Returns users in the current organization. ### Invite User ```http theme={null} POST /api/v1/users/invite Authorization: Bearer ADMIN_TOKEN Content-Type: application/json ``` Admin only. ```json Request theme={null} { "email": "teammate@example.com", "first_name": "Grace", "last_name": "Hopper", "role": "user" } ``` ### Validate Invitation ```http theme={null} GET /api/v1/users/invitation/{token} ``` This endpoint is public. The invitation token acts as bearer access to the invitation metadata. ### Accept Invitation ```http theme={null} POST /api/v1/users/accept-invitation Content-Type: application/json ``` ```json Request theme={null} { "token": "invitation-token", "password": "new-strong-password", "confirm_password": "new-strong-password" } ``` The response includes an access token. Store it like any other JWT. ## Organization ### Get Organization ```http theme={null} GET /api/v1/organization Authorization: Bearer YOUR_TOKEN ``` The organization response can include stored provider credentials and API key configuration. Do not log full responses. ### Create Organization ```http theme={null} POST /api/v1/organization/create Authorization: Bearer YOUR_TOKEN Content-Type: application/json ``` ```json Request theme={null} { "name": "Acme Support", "slug": "acme-support", "description": "Support automation team", "domain": "example.com" } ``` Trial credits are provisioned only for the user's first eligible organization. ### Update Organization ```http theme={null} PUT /api/v1/organization Authorization: Bearer ADMIN_TOKEN Content-Type: application/json ``` Admin only. Supports organization metadata, settings, provider keys, carrier credentials, Vonage settings, and SIP trunk config. ```json Request theme={null} { "name": "Acme Support", "settings": { "default_timezone": "America/New_York" }, "api_keys": { "openai": { "api_key": "sk-..." } }, "sip_trunk_config": { "sip_domain": "sip.example.com", "inbound_enabled": true } } ``` If HIPAA/BAA requirements block BYO activation, the backend may save keys but prevent activation until compliance requirements are met. ### Validate Twilio Credentials ```http theme={null} POST /api/v1/organization/twilio/validate Authorization: Bearer ADMIN_TOKEN Content-Type: application/json ``` Admin only. Sends raw Twilio credentials for validation. ```json Request theme={null} { "account_sid": "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "auth_token": "twilio-auth-token" } ``` ## User API Keys ### List API Keys ```http theme={null} GET /api/v1/users/me/api-keys Authorization: Bearer YOUR_TOKEN ``` Returns key metadata only, including `key_prefix`, never the full secret. ### Create API Key ```http theme={null} POST /api/v1/users/me/api-keys Authorization: Bearer VERIFIED_USER_TOKEN Content-Type: application/json ``` Requires a verified user account. ```json Request theme={null} { "name": "Production integration", "permissions": { "assistants": "read_write", "calls": "read_write" } } ``` ```json Response theme={null} { "api_key": { "id": 12, "user_id": 17, "name": "Production integration", "key_prefix": "burki_live_abc123", "last_used_at": null, "usage_count": 0, "is_active": true, "permissions": { "assistants": "read_write", "calls": "read_write" }, "rate_limit": null, "created_at": "2026-04-30T10:00:00Z", "updated_at": "2026-04-30T10:00:00Z" }, "key": "burki_live_abc123_full_secret_shown_once" } ``` The `key` field is shown only once. Store it immediately and never commit it to source control. ### Update or Delete API Key ```http theme={null} PUT /api/v1/users/me/api-keys/{key_id} DELETE /api/v1/users/me/api-keys/{key_id} ``` Both require a verified user account. ## LLM Presets ```http theme={null} GET /api/v1/organization/llm-presets GET /api/v1/organization/llm-presets/{preset_id}/apply POST /api/v1/organization/llm-presets PUT /api/v1/organization/llm-presets/{preset_id} DELETE /api/v1/organization/llm-presets/{preset_id} ``` List responses mask stored API keys. The `apply` endpoint is admin-only and can return an unmasked stored API key when the preset includes one. ## Configuration Status ```http theme={null} GET /api/v1/organization/configuration-status Authorization: Bearer YOUR_TOKEN ``` Returns organization provider configuration health and readiness status. ## Fallback Keys ```http theme={null} GET /api/v1/organization/fallback-keys/{provider} POST /api/v1/organization/fallback-keys/{provider} PUT /api/v1/organization/fallback-keys/{provider}/{key_id} DELETE /api/v1/organization/fallback-keys/{provider}/{key_id} ``` `GET` returns metadata only. `POST` and `PUT` accept provider secrets. ```json Request theme={null} { "name": "Deepgram backup key", "api_key": "provider-secret", "enabled": true, "concurrent_limit": 20 } ``` For updates, `api_key` changes only when a non-empty value is provided. ## Concurrency Settings ```http theme={null} GET /api/v1/organization/concurrency-settings PUT /api/v1/organization/concurrency-settings ``` Admin only. Controls provider concurrency for TTS/STT. ```json Request theme={null} { "tts": { "elevenlabs": { "enabled": true, "concurrent_limit": 20 } }, "stt": { "deepgram": { "enabled": true, "concurrent_limit": 20 } } } ``` ## Related Docs * [BYO API Keys](/byo-api-keys) * [Billing API](/api-reference/billing/overview) * [SIP Trunks API](/api-reference/sip-trunks/overview) # Create Cloned Voice Source: https://docs.burki.dev/api-reference/assistants/cloned-voices/create POST /api/v1/assistants/{assistant_id}/cloned-voices/create Create cloned voice from uploaded sample. This endpoint creates a cloned voice from an uploaded voice sample using the specified TTS provider. The cloning process varies by provider but typically completes within seconds to minutes. ### Path Parameters * `assistant_id` (integer, **required**): The unique identifier of the assistant to associate the cloned voice with. ### Request Body The request body is a JSON object containing cloning configuration. * `voice_sample_id` (integer, **required**): ID of the uploaded voice sample to clone from * `provider` (string, **required**): TTS provider to use for voice cloning * Common values: `elevenlabs`, `resemble`, `cartesia`. The providers endpoint returns the current supported set. * `name` (string, **required**): Display name for the cloned voice * `description` (string, optional): Description of the cloned voice * `language` (string, optional): Language code (e.g., `en`, `es`, `fr`) * `enhance_quality` (boolean, optional): Whether to apply quality enhancement. Default: `true` * `tags` (array of strings, optional): Tags for organizing cloned voices * `metadata` (object, optional): Additional provider-specific metadata ### Example Request ```json Request theme={null} { "voice_sample_id": 123, "provider": "elevenlabs", "name": "Custom Professional Voice", "description": "Professional voice for customer service interactions", "language": "en", "enhance_quality": true, "tags": ["professional", "customer-service"], "metadata": { "use_case": "customer_support" } } ``` ### Response A successful request returns the cloned voice object with initial status. ```json Response theme={null} { "success": true, "cloned_voice": { "id": 456, "name": "Custom Professional Voice", "provider": "elevenlabs", "provider_voice_id": "voice_abc123xyz", "status": "processing", "created_at": "2024-01-15T10:45:00Z" } } ``` ### Response Fields * `id` (integer): Unique identifier for the cloned voice * `name` (string): Display name of the cloned voice * `provider` (string): TTS provider used for cloning * `provider_voice_id` (string): Provider-specific voice identifier * `status` (string): Current cloning status * `created_at` (string): ISO 8601 timestamp of creation ### Cloning Status * `processing`: Voice cloning is in progress * `ready`: Voice is successfully cloned and ready for use * `failed`: Voice cloning failed, check error details ### Provider-Specific Behavior #### ElevenLabs * **Processing Time**: Usually 5-30 seconds * **Instant Preview**: Voice ready for immediate testing * **Voice ID**: Returns ElevenLabs voice ID for direct API usage * **Quality**: High-quality instant cloning from short samples #### Resemble AI * **Processing Time**: 2-10 minutes for training * **Professional Quality**: Advanced training algorithms * **Voice UUID**: Returns Resemble voice UUID * **Customization**: Supports advanced voice customization options ### Example Requests ```bash ElevenLabs Voice Cloning theme={null} curl -X POST "https://api.burki.dev/api/v1/assistants/123/cloned-voices/create" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "voice_sample_id": 123, "provider": "elevenlabs", "name": "ElevenLabs Professional Voice", "language": "en", "enhance_quality": true }' ``` ```bash Resemble AI Voice Cloning theme={null} curl -X POST "https://api.burki.dev/api/v1/assistants/123/cloned-voices/create" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "voice_sample_id": 123, "provider": "resemble", "name": "Resemble Custom Voice", "description": "High-quality professional voice", "enhance_quality": true, "tags": ["professional", "brand"] }' ``` ### Error Responses **400 Bad Request** - Invalid parameters: ```json theme={null} { "detail": "Provider 'invalid_provider' is not supported" } ``` **404 Not Found** - Voice sample not found: ```json theme={null} { "detail": "Voice sample with ID 123 not found" } ``` **500 Internal Server Error** - Provider cloning failed: ```json theme={null} { "detail": "Voice cloning failed: Insufficient account balance" } ``` ### Best Practices 1. **Provider Selection**: Choose ElevenLabs for quick prototyping, Resemble for production quality 2. **Naming Convention**: Use descriptive names that indicate voice characteristics 3. **Quality Settings**: Enable quality enhancement for better results 4. **Monitoring**: Check cloning status regularly for large-scale operations 5. **Error Handling**: Implement retry logic for transient provider failures # Delete Cloned Voice Source: https://docs.burki.dev/api-reference/assistants/cloned-voices/delete DELETE /api/v1/assistants/{assistant_id}/cloned-voices/{voice_id} Delete a cloned voice. This endpoint permanently deletes a cloned voice from both your organization's database and the TTS provider's system. This action cannot be undone. ### Path Parameters * `assistant_id` (integer, **required**): The unique identifier of the assistant that owns the cloned voice. * `voice_id` (integer, **required**): The unique identifier of the cloned voice to delete. ### Response A successful deletion returns a confirmation message. ```json Response theme={null} { "success": true, "message": "Cloned voice deleted successfully" } ``` ### Deletion Process When you delete a cloned voice, the following actions occur: 1. **Provider Cleanup**: The voice is deleted from the TTS provider (ElevenLabs, Resemble, etc.) 2. **Database Removal**: All voice records and metadata are removed from the database 3. **Usage Logs**: Historical usage logs are preserved for analytics 4. **Immediate Effect**: The voice becomes unavailable for new synthesis requests **Permanent Action**: Once deleted, a cloned voice cannot be recovered. You would need to re-upload the voice sample and recreate the cloned voice if needed. ### Example Request ```bash cURL theme={null} curl -X DELETE "https://api.burki.dev/api/v1/assistants/123/cloned-voices/456" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### Error Responses **404 Not Found** - Voice not found: ```json theme={null} { "detail": "Cloned voice with ID 456 not found" } ``` **403 Forbidden** - Insufficient permissions: ```json theme={null} { "detail": "You don't have permission to delete this cloned voice" } ``` **500 Internal Server Error** - Provider deletion failed: ```json theme={null} { "detail": "Failed to delete voice from provider, but removed from database" } ``` ### Integration Examples ```python Python - Safe Deletion with Confirmation theme={null} import requests def delete_cloned_voice(assistant_id, voice_id, confirm=False): if not confirm: raise ValueError("Deletion requires explicit confirmation") url = f"https://api.burki.dev/api/v1/assistants/{assistant_id}/cloned-voices/{voice_id}" headers = {"Authorization": "Bearer YOUR_API_KEY"} response = requests.delete(url, headers=headers) if response.status_code == 200: return response.json() else: raise Exception(f"Deletion failed: {response.json()['detail']}") # Usage with explicit confirmation result = delete_cloned_voice(123, 456, confirm=True) ``` ```javascript Node.js - Batch Deletion theme={null} async function deleteUnusedVoices(assistantId, daysUnused = 30) { // First, get all voices const voicesResponse = await axios.get( `https://api.burki.dev/api/v1/assistants/${assistantId}/cloned-voices`, { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } } ); const voices = voicesResponse.data.cloned_voices; const cutoffDate = new Date(); cutoffDate.setDate(cutoffDate.getDate() - daysUnused); // Find unused voices const unusedVoices = voices.filter(voice => { if (voice.synthesis_count === 0) return true; if (!voice.last_used_at) return true; return new Date(voice.last_used_at) < cutoffDate; }); // Delete unused voices const deletionResults = []; for (const voice of unusedVoices) { try { await axios.delete( `https://api.burki.dev/api/v1/assistants/${assistantId}/cloned-voices/${voice.id}`, { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } } ); deletionResults.push({ id: voice.id, name: voice.name, success: true }); } catch (error) { deletionResults.push({ id: voice.id, name: voice.name, success: false, error: error.response?.data?.detail }); } } return deletionResults; } ``` ### Best Practices #### Before Deletion 1. **Check Usage**: Verify the voice isn't actively used by assistants 2. **Backup Information**: Save voice metadata and configuration if needed 3. **User Notification**: Inform team members who might be using the voice 4. **Alternative Voices**: Ensure alternative voices are available #### Batch Operations When deleting multiple voices: 1. **Rate Limiting**: Add delays between deletions to avoid API limits 2. **Error Handling**: Handle partial failures gracefully 3. **Logging**: Keep detailed logs of deletion operations 4. **Rollback Plan**: Have a plan to recreate accidentally deleted voices #### Cleanup Strategy ```python Automated Cleanup Strategy theme={null} def cleanup_old_voices(assistant_id, criteria): """ Cleanup voices based on multiple criteria """ voices = get_cloned_voices(assistant_id) voices_to_delete = [] for voice in voices: should_delete = False # Check usage criteria if criteria.get('min_quality') and voice.get('quality_score', 1.0) < criteria['min_quality']: should_delete = True # Check age criteria if criteria.get('max_age_days'): voice_age = (datetime.now() - datetime.fromisoformat(voice['created_at'])).days if voice_age > criteria['max_age_days'] and voice['synthesis_count'] == 0: should_delete = True # Check status criteria if voice['status'] == 'failed': should_delete = True if should_delete: voices_to_delete.append(voice) # Delete identified voices for voice in voices_to_delete: try: delete_cloned_voice(assistant_id, voice['id'], confirm=True) print(f"Deleted voice: {voice['name']} (ID: {voice['id']})") except Exception as e: print(f"Failed to delete voice {voice['name']}: {e}") # Example usage cleanup_criteria = { 'min_quality': 0.7, # Delete voices with quality < 0.7 'max_age_days': 90 # Delete unused voices older than 90 days } cleanup_old_voices(123, cleanup_criteria) ``` ### Recovery Considerations Since deletion is permanent, consider these recovery strategies: 1. **Voice Sample Retention**: Keep original voice samples to recreate voices if needed 2. **Metadata Backup**: Export voice configurations before deletion 3. **Staging Environment**: Test deletion procedures in a staging environment 4. **Alternative Providers**: Have voices cloned with multiple providers as backup ### Monitoring and Alerts Set up monitoring for: * Voice deletion events * Failed deletion attempts * Accidental deletion of high-usage voices * Provider-side deletion failures # List Cloned Voices Source: https://docs.burki.dev/api-reference/assistants/cloned-voices/list GET /api/v1/assistants/{assistant_id}/cloned-voices List cloned voices for organization (legacy assistant-scoped endpoint). This endpoint retrieves a list of all cloned voices available to a specific assistant's organization. You can filter by status and provider to find specific voices. ### Path Parameters * `assistant_id` (integer, **required**): The unique identifier of the assistant whose cloned voices you want to list. ### Query Parameters * `status` (string, optional): Filter cloned voices by status. * Allowed values: `processing`, `ready`, `failed`, `archived` * `provider` (string, optional): Filter cloned voices by TTS provider. * Common values: `elevenlabs`, `resemble`, `cartesia`. The providers endpoint returns the current supported set. ### Response The response is a JSON object containing an array of cloned voice objects. ```json Response theme={null} { "success": true, "cloned_voices": [ { "id": 456, "name": "Custom Professional Voice", "provider": "elevenlabs", "provider_voice_id": "voice_abc123xyz", "language": "en", "gender": null, "status": "ready", "quality_score": 0.95, "created_at": "2024-01-15T10:45:00Z", "last_used_at": "2024-01-15T14:30:00Z", "synthesis_count": 47 }, { "id": 457, "name": "Resemble Brand Voice", "provider": "resemble", "provider_voice_id": "uuid-def456-ghi789", "language": "en", "gender": "female", "status": "ready", "quality_score": 0.98, "created_at": "2024-01-14T16:20:00Z", "last_used_at": "2024-01-15T12:15:00Z", "synthesis_count": 23 }, { "id": 458, "name": "Training Voice Sample", "provider": "elevenlabs", "provider_voice_id": "voice_training123", "language": "en", "gender": null, "status": "processing", "quality_score": null, "created_at": "2024-01-15T15:00:00Z", "last_used_at": null, "synthesis_count": 0 } ] } ``` ### Response Fields * `id` (integer): Unique identifier for the cloned voice * `name` (string): Display name of the cloned voice * `provider` (string): TTS provider that hosts this voice * `provider_voice_id` (string): Provider-specific voice identifier * `language` (string): Language code of the voice (e.g., "en", "es") * `gender` (string): Detected or specified gender ("male", "female", null) * `status` (string): Current status of the cloned voice * `quality_score` (float): Quality score from 0.0 to 1.0 (if available) * `created_at` (string): ISO 8601 timestamp of voice creation * `last_used_at` (string): ISO 8601 timestamp of last synthesis (null if never used) * `synthesis_count` (integer): Total number of times this voice has been used ### Voice Status Values * `processing`: Voice cloning is still in progress * `ready`: Voice is successfully cloned and ready for use * `failed`: Voice cloning failed during processing * `archived`: Voice has been archived and is no longer active ### Quality Scores Quality scores are provider-dependent and may not be available for all voices: * **0.9 - 1.0**: Excellent quality, production-ready * **0.8 - 0.9**: Good quality, suitable for most use cases * **0.7 - 0.8**: Fair quality, may need improvement * **\< 0.7**: Poor quality, consider re-training with better samples ### Example Requests ```bash List All Cloned Voices theme={null} curl -X GET "https://api.burki.dev/api/v1/assistants/123/cloned-voices" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```bash Filter by Status theme={null} curl -X GET "https://api.burki.dev/api/v1/assistants/123/cloned-voices?status=ready" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```bash Filter by Provider theme={null} curl -X GET "https://api.burki.dev/api/v1/assistants/123/cloned-voices?provider=elevenlabs" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```bash Multiple Filters theme={null} curl -X GET "https://api.burki.dev/api/v1/assistants/123/cloned-voices?status=ready&provider=resemble" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### Usage Analytics The response includes usage analytics for each voice: * **synthesis\_count**: Track voice popularity and ROI * **last\_used\_at**: Identify unused voices for cleanup * **quality\_score**: Make data-driven decisions about voice selection ### Integration Examples ```python Python - List and Analyze Voices theme={null} import requests def analyze_voice_usage(assistant_id): url = f"https://api.burki.dev/api/v1/assistants/{assistant_id}/cloned-voices" headers = {"Authorization": "Bearer YOUR_API_KEY"} response = requests.get(url, headers=headers) voices = response.json()['cloned_voices'] # Analyze usage patterns total_synthesis = sum(voice['synthesis_count'] for voice in voices) ready_voices = [v for v in voices if v['status'] == 'ready'] unused_voices = [v for v in voices if v['synthesis_count'] == 0] return { 'total_voices': len(voices), 'ready_voices': len(ready_voices), 'unused_voices': len(unused_voices), 'total_synthesis': total_synthesis } ``` ```javascript Node.js - Filter High-Quality Voices theme={null} async function getHighQualityVoices(assistantId, minQuality = 0.8) { const response = await axios.get( `https://api.burki.dev/api/v1/assistants/${assistantId}/cloned-voices?status=ready`, { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } } ); return response.data.cloned_voices.filter(voice => voice.quality_score && voice.quality_score >= minQuality ); } ``` ### Best Practices 1. **Regular Monitoring**: Check voice status regularly for processing voices 2. **Quality Assessment**: Use quality scores to evaluate voice performance 3. **Usage Tracking**: Monitor synthesis\_count to identify popular voices 4. **Cleanup**: Archive or delete unused voices to reduce costs 5. **Provider Comparison**: Compare quality and usage across providers # Create Assistant Source: https://docs.burki.dev/api-reference/assistants/create POST /api/v1/assistants Create a new assistant. Creates a new voice assistant with the specified configuration. The assistant will be associated with the authenticated user's organization.

Create an Assistant

Use this endpoint to create a voice AI assistant from your own product, CRM, onboarding flow, or admin tools.

The assistant record holds model settings, voice, speech recognition, tools, webhooks, and telephony configuration.

How this endpoint maps to the product

The JSON body mirrors the assistant creation form in the Burki dashboard. The same groups appear in the API: basic info, LLM, TTS, STT, calling behavior, tools, knowledge, post-call actions, safety, and telephony.

A successful request creates the assistant in your organization and returns the full record, including the assistant ID for updates, phone-number assignment, and routing.

Request body

The request body is a JSON object with the assistant's configuration. Fields follow the same groups as the dashboard. Many fields are conditional on the provider or options you pick.

* `name` (string, **required**): A unique name for your assistant. * `description` (string, optional): A brief description of the assistant's purpose. * `is_active` (boolean, optional): Set to `true` to allow the assistant to handle calls. Defaults to `true`. This section covers the core AI models for language, speech-to-text, and text-to-speech. **LLM (Language Model)** * `llm_provider` (string): The language model provider. * **Enum**: `"openai"`, `"azure"`, `"azure_ai_foundry"`, `"anthropic"`, `"gemini"`, `"groq"`, `"xai"`, `"custom"`, `"custom_websocket"`. Defaults to `openai`. * `llm_provider_config` (object): Provider-specific configuration. * `api_key` (string, **required**): The API key for the selected provider. * `model` (string, optional): The specific model to use (e.g., `gpt-4o-mini`). * `base_url` (string, optional): A custom or Azure-compatible API base URL. * `custom_config.ws_url` (string, optional): WebSocket URL for `custom_websocket`. * `custom_config.fallback_base_url` (string, optional): HTTP fallback URL for `custom_websocket`. * `custom_config.api_version` (string, optional): Azure OpenAI API version when using Azure deployments. * `llm_settings` (object): General parameters for the language model. * `temperature` (number): Controls creativity. Defaults to `0.7`. * `max_tokens` (integer): Max length of the AI's response in tokens. Defaults to `1000`. * `top_p` (number): Nucleus sampling control. Defaults to `1.0`. * `system_prompt` (string): The AI's personality and instructions. * `welcome_message` (string): The first thing the assistant says to the caller. * `llm_fallback_providers` (object, optional): Configure backup LLM providers. * `enabled` (boolean): Set to `true` to enable fallbacks. * `fallbacks` (array of objects): An ordered list of fallback providers. Each object contains `provider`, `model`, `api_key`, and `base_url`. **TTS (Text-to-Speech)** * `tts_settings` (object): * `provider` (string): The TTS provider. **Enum**: `"elevenlabs"`, `"deepgram"`, `"azure"`, `"cartesia"`, `"inworld"`, `"openai"`, `"murf"`, `"soniox"`, `"uplift"`. * `voice_id` (string): The specific voice character to use (e.g., `rachel`). * `model_id` (string, optional): The voice model (e.g., `eleven_flash_v2_5`). * **Conditional Fields**: * If `provider` is `elevenlabs`: * `stability` (number): Voice consistency. * `similarity_boost` (number): Voice accuracy. * `style` (number): Speaking style variation. * `use_speaker_boost` (boolean): Enhances audio quality. * If `provider` is `resemble`: * `provider_config.project_uuid` (string): Your Resemble AI project UUID. * If `provider` is `inworld`: * `provider_config.language` (string): Language code (e.g., `en`). * `background_audio` / provider-specific background sound settings may be supplied for assistants that use ambient audio. **STT (Speech-to-Text)** * `stt_settings` (object): * `provider` (string, optional): STT provider. Supported dashboard providers include `deepgram`, `azure`, `uplift`, `elevenlabs`, `speechmatics`, `soniox`, and `telnyx`. * `model` (string): The STT model (e.g., `nova-2`, `nova-3`). * `language` (string): Language code (e.g., `en-US`). * `punctuate`, `smart_format`, `interim_results`, `audio_denoising` (boolean, optional): Transcript formatting and audio cleanup controls. * `endpointing.silence_threshold`, `utterance_end_ms`, `utterance_timeout_seconds` (optional): Turn-detection controls for providers that consume them. * `keywords` (array of objects, optional): An array of keywords to boost. Each object should have a `keyword` (string) and an optional `intensifier` (float). For `nova-2` and older models. * `keyterms` (array of strings, optional): An array of key terms to boost. For `nova-3` model only. * `speculative_config` (object, optional): Speculative LLM settings for lower latency on partial transcripts. * `enable_speculative_llm` (boolean): Start the LLM before final STT arrives. * `speculative_stream_to_tts` (boolean): Stream speculative output immediately to TTS. * `speculative_debounce_ms` (integer): Delay after the latest partial before speculation. * `speculative_min_words` (integer): Minimum words before triggering speculation. * `speculative_similarity_threshold` (number): Confirmation threshold between partial and final transcript. * `interruption_settings` (object): * `interruption_mode` (string, optional): `"aggressive"`, `"balanced"`, or `"conservative"`. * `interruption_threshold` (integer): Number of words a user must speak to interrupt. * `min_speaking_time` (number): How long the assistant must speak before being interruptible (in seconds). * `interruption_cooldown` (number): Cooldown period after an interruption (in seconds). * `min_confidence`, `tentative_window_ms`, `backchannel_filter`, `tail_protection_ms` (optional): Advanced interruption controls. * `backchannel_settings` (object, optional): Active listening cues such as "uh-huh". * `enabled` (boolean): Enable backchannels. * `frequency` (integer): Backchannel frequency from rare to aggressive. * `conversation_continuity_enabled` (boolean, optional): Continue context for repeat calls from the same number within the continuity window. * `idle_message` (string, optional): Message sent when the caller is silent. * `max_idle_messages` (integer, optional): How many idle messages to send before ending the call. * `idle_timeout` (integer, optional): Seconds of silence before sending an idle message. * `max_call_length` (integer or null, optional): Maximum call duration in seconds. Omit or set null for no assistant-level cap. * `tools_settings` (object): Configure built-in tools. * `end_call` (object, optional): * `enabled` (boolean): Enable/disable the tool. * `scenarios` (array of strings): Phrases that will trigger ending the call. * `transfer_call` (object, optional): * `enabled` (boolean): Enable/disable the tool. * `scenarios` (array of strings): Phrases that will trigger a transfer. * `transfer_numbers` (array of strings): Phone numbers where calls can be transferred. * `transfer_type` (string): `"cold"` for blind transfer or `"warm"` for an agent-briefed handoff. * `agent_confirmation` (boolean): For warm transfer, require the agent to confirm before connection. * `unavailable_message` (string, optional): Message for failed/unavailable warm transfer. * `dtmf_solver` (object, optional): Enable IVR/touch-tone navigation and provide scenarios. * `send_sms` (object, optional): Enable SMS sending, provide scenarios, and optionally set `default_message`. * `custom_tools` (array, optional): Assigned organization tools with `tool_id`, `enabled`, `timeout_seconds`, and optional custom configuration. * `rag_settings` (object, optional): Enable the assistant knowledge base and tune retrieval. * `enabled` (boolean): Enable retrieval-augmented answers from uploaded documents. * `search_limit` (integer): Max documents/chunks to retrieve. * `similarity_threshold` (number): Minimum relevance score. * `recording_settings` (object, optional): Recording and disclosure controls. * `enabled` (boolean): Store call audio. * `record_user_only_track` (boolean or null, optional): Store a caller-only track. Omit/null to use organization default. * `disclosure_enabled`, `disclosure_message`, `disclosure_mode`: Caller recording disclosure controls. * `bypass_numbers` (array of strings, optional): E.164 numbers that skip the AI and route directly to transfer. * Spam detection scenarios and thresholds are managed through the assistant spam detection endpoints and dashboard Safety section. * `custom_settings` (object, optional): For advanced data extraction. * `structured_data_prompt` (string): Custom instructions for the AI on how to extract data. * `structured_data_schema` (object): A JSON schema defining the data to be extracted. * `twilio_config` (object, optional): Bring your own Twilio credentials. * `account_sid` (string): Your Twilio Account SID. * `auth_token` (string): Your Twilio Auth Token. * `telnyx_config` (object, optional): Bring your own Telnyx credentials. * `api_key` (string): Your Telnyx API Key. * `connection_id` (string): Your Telnyx Connection ID for Call Control Application. * `webhook_url` (string, optional): A URL to send call event webhooks to. * `sms_webhook_url` (string, optional): A URL to send SMS event webhooks to. * `messaging_service_sid` (string, optional): Twilio Messaging Service SID for SMS handling. **Telephony Provider Selection:** * If multiple provider configs are provided, the system will use the one associated with assigned phone numbers. * If no provider config is provided, the system uses your organization's default telephony credentials. * Each assistant can use a different telephony provider.

Example requests

These payloads mirror the dashboard. Arrays such as scenarios and transfer\_numbers use JSON arrays of strings.

Example: Twilio assistant

```json theme={null} { "name": "ServiceBot 5000", "description": "Handles initial customer service questions and escalations.", "is_active": true, "llm_provider": "openai", "llm_provider_config": { "model": "gpt-4o-mini", "api_key": "sk-your-openai-api-key" }, "llm_settings": { "temperature": 0.7, "max_tokens": 1000, "system_prompt": "You are a friendly and efficient customer support agent. Your goal is to help with common questions and transfer to a human when requested.", "welcome_message": "Hello! Thanks for calling support. How can I assist you today?" }, "tts_settings": { "provider": "elevenlabs", "voice_id": "rachel", "model_id": "eleven_flash_v2_5", "stability": 0.5, "similarity_boost": 0.75, "style": 0.0, "use_speaker_boost": true }, "stt_settings": { "model": "nova-2", "language": "en-US", "keywords": [ {"keyword": "agent", "intensifier": 1.5}, {"keyword": "human"}, {"keyword": "representative"} ] }, "interruption_settings": { "interruption_threshold": 3, "min_speaking_time": 0.5, "interruption_cooldown": 2.0 }, "idle_message": "Are you still there? I'm here to help if you need anything.", "max_idle_messages": 2, "idle_timeout": 20, "tools_settings": { "transfer_call": { "enabled": true, "scenarios": [ "speak to an agent", "talk to a human", "transfer me to a representative" ], "transfer_numbers": [ "+18005551212" ] } }, "twilio_config": { "account_sid": "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "auth_token": "your_twilio_auth_token" }, "webhook_url": "https://yourapi.com/webhooks/call-events" } ```

Example: Telnyx assistant

```json theme={null} { "name": "SalesBot Pro", "description": "Modern sales assistant using Telnyx for competitive call rates.", "is_active": true, "llm_provider": "openai", "llm_provider_config": { "model": "gpt-4o-mini", "api_key": "sk-your-openai-api-key" }, "llm_settings": { "temperature": 0.8, "max_tokens": 1000, "system_prompt": "You are an enthusiastic sales representative. Help qualify leads and schedule demos.", "welcome_message": "Hi there! Thanks for your interest in our product. How can I help you today?" }, "tts_settings": { "provider": "elevenlabs", "voice_id": "charlie", "model_id": "eleven_flash_v2_5", "stability": 0.6, "similarity_boost": 0.8, "use_speaker_boost": true }, "stt_settings": { "model": "nova-3", "language": "en-US", "keyterms": ["demo", "pricing", "features", "schedule"] }, "telnyx_config": { "api_key": "KEYxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "connection_id": "1234567890" }, "webhook_url": "https://yourapi.com/webhooks/telnyx-events" } ```
# Delete Assistant Source: https://docs.burki.dev/api-reference/assistants/delete DELETE /api/v1/assistants/{assistant_id} Delete an assistant. Permanently deletes the assistant if it belongs to your organization. This action cannot be undone. This endpoint permanently deletes an assistant from your organization. This action cannot be undone. Once an assistant is deleted, all associated configurations are removed. ### Path Parameters * `assistant_id` (string, **required**): The unique identifier for the assistant you want to delete. ### Response A successful deletion will return a `200 OK` status code with a confirmation message. ```json Response theme={null} { "message": "Assistant deleted successfully" } ``` # Delete Document Source: https://docs.burki.dev/api-reference/assistants/documents/delete DELETE /api/v1/assistants/{assistant_id}/documents/{document_id} Delete a document from an assistant's knowledge base. This endpoint permanently deletes a document from an assistant's knowledge base. This action also removes all associated vector embeddings from the database and cannot be undone. ### Path Parameters * `assistant_id` (string, **required**): The identifier of the assistant that owns the document. * `document_id` (string, **required**): The identifier of the document to delete. ### Response A successful deletion will return a `200 OK` status code with a confirmation message. ```json Response theme={null} { "message": "Document deleted successfully" } ``` # Get Document Status Source: https://docs.burki.dev/api-reference/assistants/documents/get-status GET /api/v1/assistants/{assistant_id}/documents/{document_id}/status Get processing status of a document. After uploading a document, you can use this endpoint to check its processing status. This is useful for determining when the document is fully processed and ready for use in RAG. ### Path Parameters * `assistant_id` (string, **required**): The identifier of the assistant that owns the document. * `document_id` (string, **required**): The identifier of the document whose status you want to check. ### Response The response contains the document object, which includes the current `status`. The status will be one of `processing`, `completed`, or `failed`. ```json Response theme={null} { "id": "doc_1234567890", "user_id": "user_12345", "organization_id": "org_67890", "assistant_id": "asst_a1b2c3d4e5f67890", "name": "product_faq.pdf", "status": "completed", "created_at": "2023-10-28T16:00:00Z", "updated_at": "2023-10-28T16:05:00Z" } ``` # List Documents Source: https://docs.burki.dev/api-reference/assistants/documents/list GET /api/v1/assistants/{assistant_id}/documents List documents for an assistant. This endpoint retrieves a list of all documents that have been uploaded to a specific assistant's knowledge base. ### Path Parameters * `assistant_id` (string, **required**): The unique identifier of the assistant whose documents you want to list. ### Response The response is a JSON array of document objects, each containing details about a specific document. ```json Response theme={null} [ { "id": "doc_1234567890", "user_id": "user_12345", "organization_id": "org_67890", "assistant_id": "asst_a1b2c3d4e5f67890", "name": "product_faq.pdf", "status": "completed", "created_at": "2023-10-28T16:00:00Z", "updated_at": "2023-10-28T16:05:00Z" }, { "id": "doc_abcdefghij", "user_id": "user_12345", "organization_id": "org_67890", "assistant_id": "asst_a1b2c3d4e5f67890", "name": "onboarding_guide.docx", "status": "completed", "created_at": "2023-10-29T10:00:00Z", "updated_at": "2023-10-29T10:02:00Z" } ] ``` # Upload Document Source: https://docs.burki.dev/api-reference/assistants/documents/upload POST /api/v1/assistants/{assistant_id}/documents/upload Upload a document to an assistant's knowledge base. This endpoint allows you to upload a document to an assistant's knowledge base for Retrieval-Augmented Generation (RAG). The document will be processed, chunked, and stored as embeddings in a vector database. The request must be sent as `multipart/form-data`. ### Path Parameters * `assistant_id` (string, **required**): The unique identifier of the assistant to whom the document will be added. ### Form Data * `file` (file, **required**): The document to upload. Supported file types include `.txt`, `.pdf`, `.md`, and `.docx`. ### Response A successful request will immediately return a document object with a `processing` status. The document will be processed in the background. You can check the status later using the [Get Document Status](/api-reference/assistants/documents/get-status) endpoint. ```json Response theme={null} { "id": "doc_1234567890", "user_id": "user_12345", "organization_id": "org_67890", "assistant_id": "asst_a1b2c3d4e5f67890", "name": "product_faq.pdf", "status": "processing", "created_at": "2023-10-28T16:00:00Z", "updated_at": "2023-10-28T16:00:00Z" } ``` # Get Assistant by ID Source: https://docs.burki.dev/api-reference/assistants/get-by-id GET /api/v1/assistants/{assistant_id} Get a specific assistant by ID. Returns the assistant details if it belongs to your organization. The response body is the assistant object directly (not wrapped in a "data" key). It includes top-level recording_settings with an enabled field. The frontend should use the response body as the assistant payload, e.g. setAssistantData({ ...defaultAssistantData, ...response }).

Retrieve One Assistant

Use this endpoint to load the current configuration for a single assistant before showing an edit screen, debugging call behavior, or syncing settings into another system.

How this endpoint behaves

Pass the assistant identifier you received from create or list. The response is the same assistant shape as in list responses, with the full configuration for that record.

Use this call before editing an assistant in your own UI, before assigning numbers, or when debugging why a live call used certain model or voice settings.

Path parameters

Response

When the ID exists, the API returns one assistant object. The shape matches each item from the list endpoint.

```json Response theme={null} { "id": "asst_a1b2c3d4e5f67890", "user_id": "user_12345", "organization_id": "org_67890", "name": "Customer Service Bot", "description": "Handles inbound customer queries and provides support.", "is_active": true, "llm_settings": { "provider": "openai", "model": "gpt-4-turbo", "temperature": 0.7, "max_tokens": 250 }, "tts_settings": { "provider": "elevenlabs", "voice_id": "EXAVITQu4vr4xnSDxMaL", "stability": 0.5, "similarity_boost": 0.75 }, "stt_settings": { "provider": "deepgram", "language": "en-US", "model": "nova-2" }, "interruption_settings": { "enabled": true, "threshold": 0.5, "evaluation_period": 0.8 }, "tools_settings": { "end_call": { "enabled": true, "scenarios": ["The user says goodbye.", "The issue is fully resolved."] }, "transfer_call": { "enabled": false } }, "created_at": "2023-10-26T10:00:00Z", "updated_at": "2023-10-26T10:00:00Z" } ```
# Get Assistant by Phone Source: https://docs.burki.dev/api-reference/assistants/get-by-phone GET /api/v1/assistants/by-phone/{phone_number} Get a specific assistant by phone number. Returns the assistant details if it belongs to your organization. Uses the new PhoneNumber table for lookups. # Get count Source: https://docs.burki.dev/api-reference/assistants/get-count # Get Organization Info Source: https://docs.burki.dev/api-reference/assistants/get-organization GET /api/v1/assistants/me/organization Get information about your organization. Returns basic organization information for the authenticated user. # List Assistants Source: https://docs.burki.dev/api-reference/assistants/list GET /api/v1/assistants Get a list of assistants for your organization. Returns all assistants that belong to your organization, with optional filtering by status and creator.

List Organization Assistants

Use this endpoint to build assistant dashboards, selection menus, audits, and sync jobs. It returns the assistants your authenticated organization can access.

How this endpoint behaves

Results include every assistant in the authenticated organization. Use skip and limit to page through large directories without loading everything at once.

Each item in the array uses the same assistant object shape as the get-by-id response, so you can reuse one client model across list and detail views.

### Query Parameters * `skip` (integer, optional, default: 0): The number of assistants to skip before starting to collect the result set. * `limit` (integer, optional, default: 100): The maximum number of assistants to return. * `active_only` (boolean, optional, default: false): Return only active assistants. * `my_assistants_only` (boolean, optional, default: false): Return only assistants created by the authenticated user. * `include_stats` (boolean, optional, default: false): Include calculated call stats fields (`total_calls`, `avg_call_duration`, `success_rate`) when available. ### Response The response is a JSON array where each object represents an assistant, containing its full configuration details. ```json Response theme={null} [ { "id": 124, "user_id": 17, "organization_id": 9, "name": "Customer Service Bot", "description": "Handles inbound customer queries and provides support.", "is_active": true, "llm_settings": { "provider": "openai", "model": "gpt-4-turbo", "temperature": 0.7, "max_tokens": 250 }, "tts_settings": { "provider": "elevenlabs", "voice_id": "EXAVITQu4vr4xnSDxMaL", "stability": 0.5, "similarity_boost": 0.75 }, "stt_settings": { "provider": "deepgram", "language": "en-US", "model": "nova-2" }, "interruption_settings": { "enabled": true, "threshold": 0.5, "evaluation_period": 0.8 }, "tools_settings": { "end_call": { "enabled": true, "scenarios": ["The user says goodbye.", "The issue is fully resolved."] }, "transfer_call": { "enabled": false } }, "created_at": "2023-10-26T10:00:00Z", "updated_at": "2023-10-26T10:00:00Z", "total_calls": 1482, "avg_call_duration": 213, "success_rate": 96.3 } ] ```
# List Providers Source: https://docs.burki.dev/api-reference/assistants/list-providers GET /api/v1/assistants/providers Get list of supported LLM providers. Returns the available LLM providers and their default configurations. # Assign Phone Number by String Source: https://docs.burki.dev/api-reference/assistants/phonenumbers/assign-by-number POST /api/v1/assistants/{assistant_id}/phone-numbers/assign-by-number Assign a phone number to an assistant by phone number string. Auto-syncs from Twilio if the number doesn't exist locally and auto_sync is True. This is the recommended way to assign newly purchased Twilio numbers. Args: assistant_id: ID of the assistant to assign the phone number to request: JSON request containing phone_number, friendly_name (optional), and auto_sync This endpoint assigns an available phone number from your organization to a specific assistant. The phone number must already exist within your organization's pool of numbers and not be currently assigned to another assistant. ### Path Parameters * `assistant_id` (string, **required**): The unique identifier of the assistant to whom the phone number will be assigned. ### Request Body * `phone_number` (string, **required**): The phone number to assign, specified in E.164 format (e.g., `+15551234567`). ```json Request theme={null} { "phone_number": "+15551234567" } ``` ### Response A successful assignment will return the updated phone number object, which now includes the `assistant_id`. ```json Response theme={null} { "id": "pn_1a2b3c4d5e6f7g8h", "phone_number": "+15551234567", "assistant_id": "asst_a1b2c3d4e5f67890", "organization_id": "org_67890", "created_at": "2023-10-26T11:00:00Z", "updated_at": "2023-10-28T14:00:00Z" } ``` # Bulk Assign Phone Numbers Source: https://docs.burki.dev/api-reference/assistants/phonenumbers/bulk-assign POST /api/v1/assistants/{assistant_id}/phone-numbers/bulk-assign Assign multiple phone numbers to an assistant at once. Assigns all specified phone numbers to the assistant. Returns success if all assignments succeed, or details about failures. # List Assigned Phone Numbers Source: https://docs.burki.dev/api-reference/assistants/phonenumbers/list GET /api/v1/assistants/{assistant_id}/phone-numbers Get all phone numbers assigned to a specific assistant. Returns a list of phone numbers currently assigned to the assistant. This endpoint retrieves a list of all phone numbers currently assigned to a specific assistant. This is useful for seeing which numbers will route incoming calls to this assistant. ### Path Parameters * `assistant_id` (string, **required**): The unique identifier of the assistant whose phone numbers you want to list. ### Response The response is a JSON array of phone number objects. Each object contains details about the assigned phone number. ```json Response theme={null} [ { "id": "pn_1a2b3c4d5e6f7g8h", "phone_number": "+15551234567", "assistant_id": "asst_a1b2c3d4e5f67890", "organization_id": "org_67890", "created_at": "2023-10-26T11:00:00Z", "updated_at": "2023-10-26T11:00:00Z" }, { "id": "pn_9i8j7k6l5m4n3o2p", "phone_number": "+15557654321", "assistant_id": "asst_a1b2c3d4e5f67890", "organization_id": "org_67890", "created_at": "2023-10-27T12:30:00Z", "updated_at": "2023-10-27T12:30:00Z" } ] ``` # List Available Phone Numbers Source: https://docs.burki.dev/api-reference/assistants/phonenumbers/list-available GET /api/v1/assistants/{assistant_id}/available-phone-numbers Get all unassigned phone numbers that can be assigned to an assistant. Returns phone numbers in your organization that are not currently assigned to any assistant. # Unassign Phone Number by ID Source: https://docs.burki.dev/api-reference/assistants/phonenumbers/unassign-by-id POST /api/v1/assistants/{assistant_id}/phone-numbers/{phone_number_id}/unassign Unassign a phone number from an assistant. Removes the phone number assignment from the assistant. The phone number will remain in your organization but won't route calls to any assistant. # Unassign Phone Number by String Source: https://docs.burki.dev/api-reference/assistants/phonenumbers/unassign-by-number POST /api/v1/assistants/{assistant_id}/phone-numbers/unassign-by-number Unassign a phone number from an assistant by phone number string. This endpoint unassigns a phone number from an assistant, making it available again within your organization's pool. The phone number is not deleted from your account, but it will no longer route calls to this assistant. ### Path Parameters * `assistant_id` (string, **required**): The unique identifier of the assistant from whom the phone number will be unassigned. ### Request Body * `phone_number` (string, **required**): The phone number to unassign, specified in E.164 format (e.g., `+15551234567`). ```json Request theme={null} { "phone_number": "+15551234567" } ``` ### Response A successful request will return the updated phone number object, where the `assistant_id` is now `null`. ```json Response theme={null} { "id": "pn_1a2b3c4d5e6f7g8h", "phone_number": "+15551234567", "assistant_id": null, "organization_id": "org_67890", "created_at": "2023-10-26T11:00:00Z", "updated_at": "2023-10-28T15:00:00Z" } ``` # Update Assistant Source: https://docs.burki.dev/api-reference/assistants/update PUT /api/v1/assistants/{assistant_id} Update an assistant. Updates the assistant configuration if it belongs to your organization. Only the fields provided in the request will be updated. This endpoint allows you to modify the configuration of an existing assistant by providing its unique ID. You only need to include the fields you wish to update in the request body; any omitted fields will remain unchanged. ### Path Parameters * `assistant_id` (string, **required**): The unique identifier of the assistant you want to update. ### Request Body The request body is a JSON object containing the fields to update. The structure and available parameters are identical to those used when creating an assistant. Refer to [Create Assistant](/api-reference/assistants/create) for the full schema and [Assistant configuration](/assistant-configuration) for how dashboard settings map to runtime behavior. ### Examples **Update basic settings:** ```json Request theme={null} { "name": "Senior Customer Support Specialist", "llm_settings": { "temperature": 0.5, "top_p": 0.9 } } ``` **Enable warm transfer:** ```json Request theme={null} { "transfer_call_message": "Let me brief a specialist and connect you.", "tools_settings": { "transfer_call": { "enabled": true, "scenarios": [ "caller asks for a human", "billing dispute requires account team" ], "transfer_numbers": ["+18005551212"], "transfer_type": "warm", "agent_confirmation": true, "unavailable_message": "I could not reach an agent right now. I can take a message or schedule a callback." } } } ``` **Enable speculative LLM for lower latency:** ```json Request theme={null} { "stt_settings": { "provider": "deepgram", "model": "nova-3", "language": "en-US", "interim_results": true, "speculative_config": { "enable_speculative_llm": true, "speculative_stream_to_tts": false, "speculative_debounce_ms": 300, "speculative_min_words": 3, "speculative_similarity_threshold": 0.85 } } } ``` **Switch from Twilio to Telnyx:** ```json Request theme={null} { "telnyx_config": { "api_key": "KEYxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "connection_id": "1234567890" }, "twilio_config": { "account_sid": null, "auth_token": null } } ``` **Add Telnyx as backup to Twilio:** ```json Request theme={null} { "telnyx_config": { "api_key": "KEYxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "connection_id": "1234567890" } } ``` ### Response A successful update will return the complete, updated assistant object. ```json Response theme={null} { "id": "asst_a1b2c3d4e5f67890", "user_id": "user_12345", "organization_id": "org_67890", "name": "Senior Customer Support Specialist", "description": "Handles inbound customer queries and provides support.", "is_active": true, "llm_settings": { "temperature": 0.5, "max_tokens": 1000, "top_p": 0.9, "system_prompt": "You are a senior customer support specialist.", "welcome_message": "Hello, how can I help today?" }, "llm_provider": "openai", "llm_provider_config": { "model": "gpt-5", "api_key": "********" }, "tts_settings": { "provider": "openai", "model_id": "tts-1", "voice_id": "alloy" }, "stt_settings": { "provider": "deepgram", "language": "en-US", "model": "nova-3", "interim_results": true }, "interruption_settings": { "interruption_mode": "balanced", "interruption_threshold": 3, "min_speaking_time": 0.8, "interruption_cooldown": 2.0 }, "tools_settings": { "end_call": { "enabled": true, "scenarios": ["The user says goodbye.", "The issue is fully resolved."] }, "transfer_call": { "enabled": true, "transfer_type": "warm", "transfer_numbers": ["+18005551212"] } }, "created_at": "2023-10-26T10:00:00Z", "updated_at": "2023-10-26T10:15:00Z" } ``` # List Voice Cloning Providers Source: https://docs.burki.dev/api-reference/assistants/voice-cloning/providers GET /api/v1/assistants/{assistant_id}/voice-cloning/providers List available voice cloning providers. This endpoint returns a list of TTS providers that support voice cloning, along with their specific requirements and capabilities. Use this information to determine which provider best fits your voice cloning needs. ### Path Parameters * `assistant_id` (integer, **required**): The unique identifier of the assistant for which you want to list available voice cloning providers. ### Response Returns an array of providers with their capabilities and requirements. ```json Response theme={null} { "success": true, "providers": [ { "provider": "elevenlabs", "name": "ElevenLabs", "requirements": { "formats": ["mp3", "wav", "flac", "m4a"], "min_duration": 10, "max_duration": 300, "max_size": 26214400, "quality_requirements": { "sample_rate": 22050, "bit_rate": 128, "channels": 1 } } }, { "provider": "resemble", "name": "Resemble", "requirements": { "formats": ["wav", "mp3", "flac"], "min_duration": 10, "max_duration": 600, "max_size": 52428800, "quality_requirements": { "sample_rate": 22050, "bit_rate": 128, "channels": 1 } } }, { "provider": "cartesia", "name": "Cartesia", "requirements": { "formats": ["mp3", "wav", "flac", "m4a", "ogg"], "min_duration": 5, "max_duration": 30, "max_size": 10485760, "quality_requirements": { "sample_rate": 16000, "bit_rate": 128, "channels": 1, "note": "Clean mono audio without background noise is recommended" } } } ] } ``` ### Provider Information Each provider object contains: * **provider**: Unique identifier for the provider (used in API calls) * **name**: Human-readable name of the provider * **requirements**: Technical requirements for voice samples ### Requirements Details #### File Formats * **formats**: Array of supported audio file extensions * Common formats: `mp3`, `wav`, `flac`, `m4a`, `ogg` #### Duration Limits * **min\_duration**: Minimum audio duration in seconds * **max\_duration**: Maximum audio duration in seconds * Typical range: 10-600 seconds (10 seconds to 10 minutes) #### File Size * **max\_size**: Maximum file size in bytes * Typical range: 25MB to 50MB #### Quality Requirements * **sample\_rate**: Minimum sample rate in Hz * **bit\_rate**: Minimum bit rate in kbps * **channels**: Number of audio channels (1 = mono, 2 = stereo) ### Provider Comparison | Provider | Quality | Speed | Cost | Special Features | | - | - | - | - | - | | **ElevenLabs** | High | Fast | Premium | Instant cloning, emotion control | | **Resemble** | High | Medium | Moderate | Real-time streaming, voice mixing | | **Cartesia** | High | Fast | Usage-based | Low-latency multilingual cloned voices | ### Example Request ```bash cURL theme={null} curl -X GET "https://api.burki.dev/api/v1/assistants/123/voice-cloning/providers" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### Integration Examples ```python Python - Provider Selection Logic theme={null} import requests def select_best_provider(audio_file_info): """ Select the best provider based on audio file characteristics """ # Get available providers response = requests.get( "https://api.burki.dev/api/v1/assistants/123/voice-cloning/providers", headers={"Authorization": "Bearer YOUR_API_KEY"} ) providers = response.json()["providers"] file_size = audio_file_info["size"] duration = audio_file_info["duration"] format = audio_file_info["format"] compatible_providers = [] for provider in providers: req = provider["requirements"] # Check compatibility if (format.lower() in req["formats"] and req["min_duration"] <= duration <= req["max_duration"] and file_size <= req["max_size"]): compatible_providers.append({ "provider": provider["provider"], "name": provider["name"], "quality_score": calculate_quality_score(provider, audio_file_info) }) # Sort by quality score compatible_providers.sort(key=lambda x: x["quality_score"], reverse=True) return compatible_providers def calculate_quality_score(provider, audio_info): """ Calculate a quality score based on provider capabilities and audio characteristics """ score = 0 req = provider["requirements"] # Bonus for higher quality requirements if req["quality_requirements"]["sample_rate"] >= 22050: score += 10 if req["quality_requirements"]["bit_rate"] >= 128: score += 10 # Provider-specific bonuses if provider["provider"] == "elevenlabs": score += 20 # High quality, instant cloning elif provider["provider"] == "resemble": score += 15 # Good quality, streaming support return score # Usage audio_info = { "size": 15000000, # 15MB "duration": 45, # 45 seconds "format": "wav" } best_providers = select_best_provider(audio_info) print(f"Best provider: {best_providers[0]['name']}") ``` ```javascript Node.js - Requirements Validation theme={null} async function validateAudioFile(assistantId, audioFile) { try { // Get provider requirements const response = await axios.get( `https://api.burki.dev/api/v1/assistants/${assistantId}/voice-cloning/providers`, { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } } ); const providers = response.data.providers; const validationResults = {}; // Check file against each provider for (const provider of providers) { const req = provider.requirements; const validation = { provider: provider.provider, name: provider.name, compatible: true, issues: [] }; // Check file format const fileExtension = audioFile.name.split('.').pop().toLowerCase(); if (!req.formats.includes(fileExtension)) { validation.compatible = false; validation.issues.push( `Format '${fileExtension}' not supported. Supported: ${req.formats.join(', ')}` ); } // Check file size if (audioFile.size > req.max_size) { validation.compatible = false; validation.issues.push( `File too large (${(audioFile.size / 1024 / 1024).toFixed(1)}MB). Max: ${(req.max_size / 1024 / 1024).toFixed(1)}MB` ); } // Check duration (if available) if (audioFile.duration) { if (audioFile.duration < req.min_duration) { validation.compatible = false; validation.issues.push( `Audio too short (${audioFile.duration}s). Min: ${req.min_duration}s` ); } if (audioFile.duration > req.max_duration) { validation.compatible = false; validation.issues.push( `Audio too long (${audioFile.duration}s). Max: ${req.max_duration}s` ); } } validationResults[provider.provider] = validation; } return validationResults; } catch (error) { console.error('Failed to validate audio file:', error); throw error; } } // Usage example const audioFile = { name: 'voice-sample.wav', size: 15000000, // 15MB duration: 45 // 45 seconds }; const results = await validateAudioFile(123, audioFile); // Show compatible providers const compatibleProviders = Object.values(results) .filter(result => result.compatible) .map(result => result.name); console.log('Compatible providers:', compatibleProviders.join(', ')); // Show issues for incompatible providers Object.values(results) .filter(result => !result.compatible) .forEach(result => { console.log(`${result.name} issues:`, result.issues.join('; ')); }); ``` ### Error Responses **404 Not Found** - Assistant not found: ```json theme={null} { "detail": "Assistant not found" } ``` **403 Forbidden** - Insufficient permissions: ```json theme={null} { "detail": "You don't have permission to access this assistant" } ``` **500 Internal Server Error** - Service unavailable: ```json theme={null} { "detail": "Error listing providers" } ``` ### Best Practices #### Provider Selection Strategy 1. **Quality First**: Choose providers with higher quality requirements for professional use 2. **Cost Consideration**: Factor in provider pricing for your usage volume 3. **Feature Requirements**: Select based on needed features (streaming, emotions, etc.) 4. **Fallback Options**: Always have a secondary provider configured #### Audio Preparation ```python Audio Preparation Helper theme={null} import librosa import soundfile as sf def prepare_audio_for_cloning(input_file, target_provider): """ Optimize audio file for a specific provider's requirements """ # Load audio audio, sr = librosa.load(input_file, sr=None) # Get provider requirements providers = get_voice_cloning_providers() provider_req = next(p for p in providers if p["provider"] == target_provider) req = provider_req["requirements"] # Optimize for provider target_sr = req["quality_requirements"]["sample_rate"] target_channels = req["quality_requirements"]["channels"] # Resample if needed if sr != target_sr: audio = librosa.resample(audio, orig_sr=sr, target_sr=target_sr) # Convert to mono if required if target_channels == 1 and len(audio.shape) > 1: audio = librosa.to_mono(audio) # Trim silence audio, _ = librosa.effects.trim(audio, top_db=20) # Normalize volume audio = librosa.util.normalize(audio) # Check duration duration = len(audio) / target_sr if duration < req["min_duration"]: raise ValueError(f"Audio too short: {duration:.1f}s, minimum: {req['min_duration']}s") if duration > req["max_duration"]: # Trim to maximum duration max_samples = int(req["max_duration"] * target_sr) audio = audio[:max_samples] return audio, target_sr # Usage optimized_audio, sample_rate = prepare_audio_for_cloning( "voice-sample.wav", "elevenlabs" ) # Save optimized version sf.write("optimized-sample.wav", optimized_audio, sample_rate) ``` #### Multi-Provider Strategy ```python Multi-Provider Voice Cloning theme={null} async def clone_voice_multi_provider(audio_file, voice_name): """ Clone voice with multiple providers for redundancy """ providers = await get_voice_cloning_providers() compatible_providers = validate_audio_file(audio_file, providers) cloning_results = {} for provider in compatible_providers: try: # Upload voice sample upload_result = await upload_voice_sample( audio_file, assistant_id=123, provider=provider["provider"] ) # Create cloned voice clone_result = await create_cloned_voice( voice_sample_id=upload_result["voice_sample"]["id"], provider=provider["provider"], name=f"{voice_name} ({provider['name']})" ) cloning_results[provider["provider"]] = { "success": True, "voice_id": clone_result["cloned_voice"]["id"], "status": clone_result["cloned_voice"]["status"] } except Exception as e: cloning_results[provider["provider"]] = { "success": False, "error": str(e) } return cloning_results # Usage results = await clone_voice_multi_provider("voice-sample.wav", "My Custom Voice") # Check results successful_clones = {k: v for k, v in results.items() if v["success"]} print(f"Successfully cloned with {len(successful_clones)} providers") ``` ### Dynamic Provider Configuration For applications that need to adapt to changing provider availability: ```python Dynamic Provider Management theme={null} class VoiceCloningManager: def __init__(self, assistant_id): self.assistant_id = assistant_id self.provider_cache = {} self.cache_expiry = 300 # 5 minutes async def get_available_providers(self, force_refresh=False): """Get available providers with caching""" now = time.time() if (not force_refresh and 'providers' in self.provider_cache and now - self.provider_cache['timestamp'] < self.cache_expiry): return self.provider_cache['providers'] try: providers = await fetch_voice_cloning_providers(self.assistant_id) self.provider_cache = { 'providers': providers, 'timestamp': now } return providers except Exception as e: # Return cached data if available, otherwise raise if 'providers' in self.provider_cache: return self.provider_cache['providers'] raise e async def select_optimal_provider(self, audio_characteristics, preferences=None): """Select the best provider based on audio and preferences""" providers = await self.get_available_providers() # Filter compatible providers compatible = [] for provider in providers: if self.is_compatible(provider, audio_characteristics): score = self.calculate_provider_score(provider, preferences or {}) compatible.append((provider, score)) if not compatible: raise ValueError("No compatible providers found") # Sort by score and return best compatible.sort(key=lambda x: x[1], reverse=True) return compatible[0][0] def is_compatible(self, provider, audio_characteristics): """Check if provider is compatible with audio characteristics""" req = provider["requirements"] return ( audio_characteristics.get("format", "").lower() in req["formats"] and req["min_duration"] <= audio_characteristics.get("duration", 0) <= req["max_duration"] and audio_characteristics.get("size", 0) <= req["max_size"] ) def calculate_provider_score(self, provider, preferences): """Calculate provider score based on preferences""" score = 0 # Base score from quality requirements req = provider["requirements"] score += req["quality_requirements"]["sample_rate"] / 1000 score += req["quality_requirements"]["bit_rate"] / 10 # Preference bonuses if preferences.get("prioritize_quality") and provider["provider"] == "elevenlabs": score += 50 if preferences.get("prioritize_cost") and provider["provider"] == "resemble": score += 30 if preferences.get("prioritize_speed") and provider["provider"] in ["elevenlabs"]: score += 40 return score # Usage manager = VoiceCloningManager(assistant_id=123) audio_chars = { "format": "wav", "duration": 60, "size": 10000000 } preferences = { "prioritize_quality": True, "prioritize_speed": False } best_provider = await manager.select_optimal_provider(audio_chars, preferences) print(f"Selected provider: {best_provider['name']}") ``` # List Voice Samples Source: https://docs.burki.dev/api-reference/assistants/voice-samples/list GET /api/v1/assistants/{assistant_id}/voice-samples List voice samples for organization. This endpoint retrieves a list of all voice samples that have been uploaded for a specific assistant's organization. ### Path Parameters * `assistant_id` (integer, **required**): The unique identifier of the assistant whose voice samples you want to list. ### Query Parameters * `status` (string, optional): Filter voice samples by processing status. * Allowed values: `uploaded`, `processing`, `ready`, `failed` ### Response The response is a JSON object containing an array of voice sample objects. ```json Response theme={null} { "success": true, "voice_samples": [ { "id": 123, "name": "Professional Voice Sample", "original_filename": "professional_voice.wav", "content_type": "audio/wav", "file_size": 2457600, "duration_seconds": 45.2, "status": "ready", "error_message": null, "tags": ["professional", "clear", "business"], "created_at": "2024-01-15T10:30:00Z" }, { "id": 124, "name": "Casual Voice Sample", "original_filename": "casual_voice.mp3", "content_type": "audio/mpeg", "file_size": 1843200, "duration_seconds": 32.1, "status": "uploaded", "error_message": null, "tags": ["casual", "friendly"], "created_at": "2024-01-15T11:15:00Z" } ] } ``` ### Response Fields * `id` (integer): Unique identifier for the voice sample * `name` (string): Display name of the voice sample * `original_filename` (string): Original filename of the uploaded audio * `content_type` (string): MIME type of the audio file * `file_size` (integer): Size of the audio file in bytes * `duration_seconds` (float): Duration of the audio in seconds * `status` (string): Processing status of the sample * `error_message` (string): Error message if processing failed * `tags` (array): Array of tags associated with the sample * `created_at` (string): ISO 8601 timestamp of when the sample was uploaded ### Status Values * `uploaded`: Sample uploaded successfully, ready for voice cloning * `processing`: Sample is being processed (validation, analysis) * `ready`: Sample processed and available for voice cloning * `failed`: Sample processing failed, check error\_message ### Example Request ```bash cURL theme={null} curl -X GET "https://api.burki.dev/api/v1/assistants/123/voice-samples?status=ready" \ -H "Authorization: Bearer YOUR_API_KEY" ``` # Upload Voice Sample Source: https://docs.burki.dev/api-reference/assistants/voice-samples/upload POST /api/v1/assistants/{assistant_id}/voice-samples/upload Upload voice sample for cloning. This endpoint allows you to upload audio samples for voice cloning. The uploaded sample will be processed and prepared for creating cloned voices using supported TTS providers. ### Path Parameters * `assistant_id` (integer, **required**): The unique identifier of the assistant to associate the voice sample with. ### Form Data The request must be sent as `multipart/form-data`. * `file` (file, **required**): The audio file to upload. Supported formats: MP3, WAV, FLAC, M4A, OGG. * `name` (string, optional): Custom name for the voice sample. Defaults to filename if not provided. * `description` (string, optional): Description of the voice sample. * `tags` (string, optional): Comma-separated tags for organizing voice samples. ### File Requirements * **Formats**: MP3, WAV, FLAC, M4A, OGG * **Size**: Maximum 50MB * **Duration**: 10 seconds to 10 minutes * **Quality**: 22kHz+ sample rate, 128kbps+ bit rate recommended * **Content**: Clear speech, single speaker, minimal background noise ### Response A successful upload returns the voice sample object with processing status. ```json Response theme={null} { "success": true, "voice_sample": { "id": 123, "name": "Professional Voice Sample", "filename": "professional_voice.wav", "status": "uploaded", "created_at": "2024-01-15T10:30:00Z" } } ``` ### Example Request ```bash cURL theme={null} curl -X POST "https://api.burki.dev/api/v1/assistants/123/voice-samples/upload" \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "file=@voice_sample.wav" \ -F "name=Professional Voice" \ -F "description=Clear professional speaking voice" \ -F "tags=professional,clear,business" ``` ### Error Responses **400 Bad Request** - Invalid file or parameters: ```json theme={null} { "detail": "File too large (max 50MB)" } ``` **413 Payload Too Large** - File exceeds size limit: ```json theme={null} { "detail": "Audio file exceeds maximum size limit" } ``` **422 Unprocessable Entity** - Invalid audio format: ```json theme={null} { "detail": "Unsupported audio format: audio/mpeg3" } ``` # Billing API Source: https://docs.burki.dev/api-reference/billing/overview Wallet, ledger, usage, statements, and managed-service billing endpoints. The Billing API manages prepaid wallet balance, Stripe top-ups, auto top-up settings, ledger history, usage summaries, monthly charges, statements, and hybrid managed-service preferences. Billing endpoints use the `/v1/billing` prefix: ```text theme={null} https://api.burki.dev/v1/billing ``` Billing endpoints use authenticated user context for the current organization. Wallet top-up and auto top-up require a verified user account. ## Get Wallet Balance ```http theme={null} GET /v1/billing/wallet Authorization: Bearer YOUR_TOKEN ``` Returns wallet balance, reserved funds, available balance, auto top-up settings, trial state, and current pricing knobs. ```json Response theme={null} { "balance_cents": 2500, "reserved_cents": 0, "available_cents": 2500, "balance_dollars": 25.0, "available_dollars": 25.0, "auto_topup_enabled": true, "auto_topup_threshold_cents": 500, "auto_topup_amount_cents": 2000, "has_payment_method": true, "trial_minutes_remaining": 143, "trial_platform_minutes_remaining": 143, "trial_carrier_minutes_remaining": 143, "trial_status": "active", "trial_number_free_until": "2026-05-30T00:00:00", "pricing": { "voice_platform_fee_cents_per_min": 3.0, "sms_platform_fee_cents_per_segment": 0.1, "managed_fee_percent": 0, "number_mgmt_cents_per_number_per_month": 100 } } ``` ## Create Wallet Top-Up ```http theme={null} POST /v1/billing/wallet/topup Authorization: Bearer YOUR_TOKEN Content-Type: application/json ``` Creates a Stripe PaymentIntent for adding funds to the wallet. Minimum amount is `$1.00`. ```json Request theme={null} { "amount_cents": 2000 } ``` ```json Response theme={null} { "success": true, "client_secret": "pi_123_secret_abc", "payment_intent_id": "pi_123", "amount_cents": 2000, "error": null } ``` ## Configure Auto Top-Up ```http theme={null} POST /v1/billing/wallet/auto-topup Authorization: Bearer YOUR_TOKEN Content-Type: application/json ``` Enable auto top-up by providing a threshold, amount, and payment method. Disable it with `enabled: false`. ```json Request theme={null} { "enabled": true, "threshold_cents": 500, "amount_cents": 2000, "payment_method_id": "pm_123" } ``` ```json Response theme={null} { "success": true, "message": "Auto top-up configured successfully" } ``` ## Get Ledger Entries ```http theme={null} GET /v1/billing/ledger?limit=50&offset=0&type=charge&from_date=2026-04-01&to_date=2026-04-30 Authorization: Bearer YOUR_TOKEN ``` | Query Parameter | Type | Description | | - | - | - | | `limit` | integer | Number of entries to return (`1-100`, default `50`) | | `offset` | integer | Number of entries to skip | | `type` | string | Optional filter: `topup`, `charge`, `refund`, `adjustment`, or `grant` | | `from_date` | string | Start date in `YYYY-MM-DD` format | | `to_date` | string | End date in `YYYY-MM-DD` format | ```json Response theme={null} { "entries": [ { "id": "ledger_123", "created_at": "2026-04-30T10:15:00", "type": "charge", "direction": "debit", "amount_cents": 9, "amount_dollars": 0.09, "reference_type": "call", "reference_id": "101", "description": "Voice platform fee" } ], "total": 1, "limit": 50, "offset": 0 } ``` ## Get Usage Summary ```http theme={null} GET /v1/billing/usage/summary?from_date=2026-04-01&to_date=2026-04-30 Authorization: Bearer YOUR_TOKEN ``` ```json Response theme={null} { "voice_minutes": 128, "sms_segments": 54, "total_events": 182, "event_breakdown": { "voice_call": 128, "sms_sent": 54 } } ``` The `managed_fee_percent` compatibility field is always zero for current pricing. New provider usage has no management markup. Historical charges and statements retain their original recorded amounts; a historical `managed_fee` value is not a fee on new usage. The examples below illustrate zero-markup charges. ## Get Charges Summary ```http theme={null} GET /v1/billing/charges?month=2026-04-01 Authorization: Bearer YOUR_TOKEN ``` ```json Response theme={null} { "month": "2026-04-01", "total_cents": 4242, "total_dollars": 42.42, "charge_count": 312, "breakdown": { "platform_fee": 384, "pass_through": 3858, "managed_fee": 0 } } ``` ## List Statements ```http theme={null} GET /v1/billing/statements?limit=12 Authorization: Bearer YOUR_TOKEN ``` ```json Response theme={null} { "statements": [ { "id": "stmt_123", "month": "2026-04-01", "total_platform_cents": 384, "total_pass_through_cents": 3858, "total_managed_fee_cents": 0, "total_number_mgmt_cents": 100, "total_cents": 4342, "total_dollars": 43.42, "total_voice_minutes": 128, "total_sms_segments": 54, "status": "finalized" } ] } ``` ## Get Statement ```http theme={null} GET /v1/billing/statements/2026-04-01 Authorization: Bearer YOUR_TOKEN ``` The `month` path parameter must be a date string in `YYYY-MM-DD` format. Burki normalizes it to the first day of that month. ```json Response theme={null} { "statement": { "id": "stmt_123", "month": "2026-04-01", "total_platform_cents": 384, "total_pass_through_cents": 3858, "total_managed_fee_cents": 0, "total_number_mgmt_cents": 100, "total_cents": 4342, "total_dollars": 43.42, "total_voice_minutes": 128, "total_sms_segments": 54, "status": "finalized" }, "charges_breakdown": { "platform_fee": 384, "pass_through": 3858, "managed_fee": 0 } } ``` ## Managed Services ```http theme={null} GET /v1/billing/managed-services Authorization: Bearer YOUR_TOKEN ``` ```json Response theme={null} { "llm": true, "tts": true, "stt": false, "carrier": true, "message": null } ``` Update hybrid mode preferences: ```http theme={null} PATCH /v1/billing/managed-services Authorization: Bearer YOUR_TOKEN Content-Type: application/json ``` ```json Request theme={null} { "llm": false, "tts": true, "stt": true, "carrier": true } ``` ```json Response theme={null} { "llm": false, "tts": true, "stt": true, "carrier": true, "message": "Managed services updated successfully" } ``` ## Admin Credit Grants ```http theme={null} POST /v1/billing/admin/grant-credits Authorization: Bearer ADMIN_TOKEN Content-Type: application/json ``` This endpoint is restricted to users with admin role. ```json Request theme={null} { "amount_cents": 1000, "description": "Promotional credit" } ``` ```json Response theme={null} { "success": true, "message": "Granted $10.00 in credits", "new_balance_cents": 3500 } ``` # Export Transcripts Source: https://docs.burki.dev/api-reference/calls/data/export-transcripts GET /api/v1/calls/{call_id}/transcripts/export Export call transcripts in various formats. This endpoint allows you to export the full transcript of a specific call to a file. You can choose from several formats. The response from this endpoint will be a file download. ### Path Parameters * `call_id` (integer, **required**): The unique identifier for the call whose transcript you want to export. ### Query Parameters * `format` (string, optional, default: `txt`): The desired file format. * Allowed values: `txt`, `json`, `csv`. * `speaker` (string, optional): Filter transcripts to include only a specific speaker (`user` or `assistant`). ### Response Formats **If `format=txt`:** You will receive a plain text file (`.txt`) with the conversation formatted for readability. ```text Text Content theme={null} [10:00:05] assistant: Hello, how can I help you today? [10:00:10] user: Yes, I'd like to check the status of my order. ``` **If `format=json`:** You will receive a JSON file (`.json`) containing call metadata and a `transcripts` array. ```json JSON Content theme={null} { "call_id": 101, "call_sid": "CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "started_at": "2023-10-30T10:00:00Z", "transcripts": [ { "speaker": "assistant", "content": "Hello, how can I help you today?", "timestamp": "2023-10-30T10:00:05Z", "confidence": 0.95, "segment_start": 0.0, "segment_end": 2.5 }, { "speaker": "user", "content": "Yes, I'd like to check the status of my order.", "timestamp": "2023-10-30T10:00:10Z", "confidence": 0.93, "segment_start": 4.1, "segment_end": 7.2 } ] } ``` **If `format=csv`:** You will receive a CSV file (`.csv`) with the transcript data. ```csv CSV Content theme={null} "Timestamp","Speaker","Content","Confidence" "2023-10-30T10:00:05Z","assistant","Hello, how can I help you today?","0.95" "2023-10-30T10:00:10Z","user","Yes, I'd like to check the status of my order.","0.93" ``` # Get Call Messages Source: https://docs.burki.dev/api-reference/calls/data/get-messages GET /api/v1/calls/{call_id}/messages Get all chat messages for a call. Returns chat messages if the call belongs to an assistant in your organization. Retrieve the LLM conversation messages for a specific call. This returns the actual messages exchanged between the system, user, and AI assistant during the call - including system prompts, user inputs, and AI responses. **Messages vs Transcripts**: Messages are the LLM conversation log (what the AI "thought" and said). Transcripts are the raw speech-to-text output of what was actually spoken. Use messages for debugging AI behavior; use transcripts for the actual conversation content. ## Path Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `call_id` | integer | Yes | The internal call ID (not the call SID) | ## Query Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `role` | string | No | Filter by message role: `system`, `user`, or `assistant` | ## Request ```bash theme={null} curl "https://api.burki.dev/api/v1/calls/101/messages" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### With Role Filter ```bash theme={null} curl "https://api.burki.dev/api/v1/calls/101/messages?role=assistant" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ## Response Returns an array of chat message objects ordered by message index. ```json theme={null} [ { "id": 1, "call_id": 101, "role": "system", "content": "You are a friendly customer service assistant for Acme Corp...", "message_index": 0, "timestamp": "2024-01-15T10:00:00Z", "llm_provider": "openai", "llm_model": "gpt-4o-mini", "prompt_tokens": null, "completion_tokens": null, "total_tokens": null }, { "id": 2, "call_id": 101, "role": "user", "content": "Hi, I'd like to check on my order status.", "message_index": 1, "timestamp": "2024-01-15T10:00:05Z", "llm_provider": null, "llm_model": null, "prompt_tokens": null, "completion_tokens": null, "total_tokens": null }, { "id": 3, "call_id": 101, "role": "assistant", "content": "Of course! I'd be happy to help you check your order status. Could you please provide your order number or the email address associated with your account?", "message_index": 2, "timestamp": "2024-01-15T10:00:07Z", "llm_provider": "openai", "llm_model": "gpt-4o-mini", "prompt_tokens": 150, "completion_tokens": 35, "total_tokens": 185 } ] ``` ## Response Fields | Field | Type | Description | | - | - | - | | `id` | integer | Unique message ID | | `call_id` | integer | ID of the parent call | | `role` | string | Message role: `system`, `user`, or `assistant` | | `content` | string | The message content | | `message_index` | integer | Position in the conversation (0-indexed) | | `timestamp` | string | When the message was recorded (ISO 8601) | | `llm_provider` | string | LLM provider used (e.g., `openai`, `anthropic`) | | `llm_model` | string | Specific model used (e.g., `gpt-4o-mini`) | | `prompt_tokens` | integer | Number of prompt tokens used | | `completion_tokens` | integer | Number of completion tokens generated | | `total_tokens` | integer | Total tokens for this message | ## Message Roles | Role | Description | | - | - | | `system` | System prompt and instructions for the AI | | `user` | User's spoken input (transcribed) | | `assistant` | AI assistant's response | ## Error Responses ### 404 Not Found ```json theme={null} { "detail": "Call with ID 101 not found in your organization" } ``` ## Use Cases ### Debug AI Behavior ```python theme={null} import requests def analyze_call_conversation(call_id): response = requests.get( f"https://api.burki.dev/api/v1/calls/{call_id}/messages", headers={"Authorization": "Bearer YOUR_API_KEY"} ) messages = response.json() # Get the system prompt system_prompt = next( (m["content"] for m in messages if m["role"] == "system"), None ) print(f"System Prompt: {system_prompt[:100]}...") # Count exchanges user_messages = [m for m in messages if m["role"] == "user"] assistant_messages = [m for m in messages if m["role"] == "assistant"] print(f"User messages: {len(user_messages)}") print(f"Assistant messages: {len(assistant_messages)}") # Calculate token usage total_tokens = sum( m.get("total_tokens", 0) or 0 for m in messages ) print(f"Total tokens used: {total_tokens}") ``` ### Export Conversation for Review ```python theme={null} def export_conversation(call_id): response = requests.get( f"https://api.burki.dev/api/v1/calls/{call_id}/messages", headers={"Authorization": "Bearer YOUR_API_KEY"} ) messages = response.json() # Format as readable conversation conversation = [] for msg in messages: if msg["role"] == "system": continue # Skip system prompt for readability speaker = "Customer" if msg["role"] == "user" else "AI" conversation.append(f"{speaker}: {msg['content']}") return "\n\n".join(conversation) ``` ### Analyze Token Costs ```python theme={null} def calculate_call_llm_costs(call_id): response = requests.get( f"https://api.burki.dev/api/v1/calls/{call_id}/messages", headers={"Authorization": "Bearer YOUR_API_KEY"} ) messages = response.json() total_prompt_tokens = 0 total_completion_tokens = 0 for msg in messages: if msg.get("prompt_tokens"): total_prompt_tokens += msg["prompt_tokens"] if msg.get("completion_tokens"): total_completion_tokens += msg["completion_tokens"] # GPT-4o-mini pricing (example) prompt_cost = total_prompt_tokens * 0.00015 / 1000 completion_cost = total_completion_tokens * 0.0006 / 1000 return { "prompt_tokens": total_prompt_tokens, "completion_tokens": total_completion_tokens, "estimated_cost": prompt_cost + completion_cost } ``` ## Notes * Messages are stored in the order they occurred during the conversation * The first message is typically the system prompt (role: `system`) * Token counts are only available for assistant messages (LLM responses) * For the actual spoken words, use the [Get Transcripts](/api-reference/calls/data/get-transcripts-by-id) endpoint # Get Recordings by Call ID Source: https://docs.burki.dev/api-reference/calls/data/get-recordings-by-id GET /api/v1/calls/{call_id}/recordings Get all recordings for a call. Returns recordings if the call belongs to an assistant in your organization. This endpoint retrieves a list of all recordings associated with a specific call, identified by its database ID. ### Path Parameters * `call_id` (integer, **required**): The unique identifier for the call. ### Response The response is a JSON array of recording objects. Each object contains details about a recording, including the provider or storage URL when available. ```json Response theme={null} [ { "id": 1, "call_id": 101, "recording_sid": "RExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "file_path": null, "recording_url": "https://api.twilio.com/2010-04-01/Accounts/AC.../Recordings/RE...", "duration": 300.0, "format": "wav", "recording_type": "full", "recording_source": "twilio", "status": "completed", "created_at": "2023-10-30T10:05:01Z" } ] ``` # Get Recordings by Call SID Source: https://docs.burki.dev/api-reference/calls/data/get-recordings-by-sid GET /api/v1/calls/sid/{call_sid}/recordings Get all recordings for a call by Twilio Call SID. Returns recordings if the call belongs to an assistant in your organization. This endpoint retrieves a list of all recordings associated with a specific call, identified by its Burki call SID / provider call identifier. ### Path Parameters * `call_sid` (string, **required**): The Burki call SID / provider call identifier. ### Response The response is a JSON array of recording objects. Each object contains details about a recording, including the provider or storage URL when available. ```json Response theme={null} [ { "id": 1, "call_id": 101, "recording_sid": "RExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "file_path": null, "recording_url": "https://api.twilio.com/2010-04-01/Accounts/AC.../Recordings/RE...", "duration": 300.0, "format": "wav", "recording_type": "full", "recording_source": "twilio", "status": "completed", "created_at": "2023-10-30T10:05:01Z" } ] ``` # Get Transcripts by Call ID Source: https://docs.burki.dev/api-reference/calls/data/get-transcripts-by-id GET /api/v1/calls/{call_id}/transcripts Get all transcripts for a call. Returns transcripts if the call belongs to an assistant in your organization. This endpoint retrieves a list of all transcript entries for a specific call, identified by its database ID. ### Path Parameters * `call_id` (integer, **required**): The unique identifier for the call. ### Query Parameters * `speaker` (string, optional): Filter transcripts by the speaker. * Allowed values: `user`, `assistant`. * `include_interim` (boolean, optional, default: false): Whether to include non-final (interim) transcript entries. ### Response The response is a JSON array of transcript objects. ```json Response theme={null} [ { "id": 1, "call_id": 101, "content": "Hello, how can I help you today?", "speaker": "assistant", "is_final": true, "segment_start": 0.0, "segment_end": 2.5, "confidence": 0.95, "stt_timestamps": null, "created_at": "2023-10-30T10:00:05Z" }, { "id": 2, "call_id": 101, "content": "Yes, I'd like to check the status of my order.", "speaker": "user", "is_final": true, "segment_start": 4.1, "segment_end": 7.2, "confidence": 0.93, "stt_timestamps": null, "created_at": "2023-10-30T10:00:10Z" } ] ``` # Get Transcripts by Call SID Source: https://docs.burki.dev/api-reference/calls/data/get-transcripts-by-sid GET /api/v1/calls/sid/{call_sid}/transcripts Get all transcripts for a call by Twilio Call SID. Returns transcripts if the call belongs to an assistant in your organization. This endpoint retrieves a list of all transcript entries for a specific call, identified by its Burki call SID / provider call identifier. ### Path Parameters * `call_sid` (string, **required**): The Burki call SID / provider call identifier. ### Query Parameters * `speaker` (string, optional): Filter transcripts by the speaker. * Allowed values: `user`, `assistant`. * `include_interim` (boolean, optional, default: false): Whether to include non-final (interim) transcript entries. ### Response The response is a JSON array of transcript objects. ```json Response theme={null} [ { "id": 1, "call_id": 101, "content": "Hello, how can I help you today?", "speaker": "assistant", "is_final": true, "segment_start": 0.0, "segment_end": 2.5, "confidence": 0.95, "stt_timestamps": null, "created_at": "2023-10-30T10:00:05Z" }, { "id": 2, "call_id": 101, "content": "Yes, I'd like to check the status of my order.", "speaker": "user", "is_final": true, "segment_start": 4.1, "segment_end": 7.2, "confidence": 0.93, "stt_timestamps": null, "created_at": "2023-10-30T10:00:10Z" } ] ``` # Get Webhook Logs Source: https://docs.burki.dev/api-reference/calls/data/get-webhook-logs GET /api/v1/calls/{call_id}/webhook-logs Get all webhook logs for a call. Returns webhook logs if the call belongs to an assistant in your organization. Retrieve all webhook delivery logs for a specific call. This endpoint provides detailed information about webhook requests sent during the call, including request payloads, response status codes, timing, and any errors. **Debugging Webhooks**: Use this endpoint to troubleshoot webhook delivery issues, verify that events are being sent correctly, and understand the timing of webhook deliveries during a call. ## Path Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `call_id` | integer | Yes | The internal call ID (not the call SID) | ## Query Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `webhook_type` | string | No | Filter by webhook type (e.g., `call_started`, `call_ended`, `transcript`) | ## Request ```bash theme={null} curl "https://api.burki.dev/api/v1/calls/101/webhook-logs" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### Filter by Webhook Type ```bash theme={null} curl "https://api.burki.dev/api/v1/calls/101/webhook-logs?webhook_type=call_ended" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ## Response Returns an array of webhook log objects ordered by attempt time. ```json theme={null} [ { "id": 1, "call_id": 101, "assistant_id": 123, "webhook_url": "https://yourapi.com/webhooks/burki", "webhook_type": "call_started", "request_payload": { "event": "call_started", "call_sid": "CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "customer_phone_number": "+15559876543", "assistant_id": 123, "timestamp": "2024-01-15T10:00:00Z" }, "response_status_code": 200, "response_body": "{\"received\": true}", "response_time_ms": 145, "attempted_at": "2024-01-15T10:00:00Z", "success": true, "error_message": null, "retry_count": 0, "webhook_metadata": null }, { "id": 2, "call_id": 101, "assistant_id": 123, "webhook_url": "https://yourapi.com/webhooks/burki", "webhook_type": "call_ended", "request_payload": { "event": "call_ended", "call_sid": "CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "duration": 180, "status": "completed", "transcript_summary": "Customer inquired about order status...", "timestamp": "2024-01-15T10:03:00Z" }, "response_status_code": 200, "response_body": "{\"received\": true}", "response_time_ms": 89, "attempted_at": "2024-01-15T10:03:01Z", "success": true, "error_message": null, "retry_count": 0, "webhook_metadata": null } ] ``` ## Response Fields | Field | Type | Description | | - | - | - | | `id` | integer | Unique webhook log ID | | `call_id` | integer | ID of the parent call | | `assistant_id` | integer | ID of the assistant that triggered the webhook | | `webhook_url` | string | The URL where the webhook was sent | | `webhook_type` | string | Type of webhook event | | `request_payload` | object | The JSON payload sent to the webhook URL | | `response_status_code` | integer | HTTP status code returned by your server | | `response_body` | string | Response body from your server (truncated if large) | | `response_time_ms` | integer | Time taken for your server to respond (milliseconds) | | `attempted_at` | string | When the webhook was attempted (ISO 8601) | | `success` | boolean | Whether the delivery was successful | | `error_message` | string | Error message if delivery failed | | `retry_count` | integer | Number of retry attempts made | | `webhook_metadata` | object | Additional metadata about the webhook | ## Webhook Types | Type | Description | | - | - | | `call_started` | Sent when a call begins | | `call_ended` | Sent when a call completes | | `transcript` | Sent for real-time transcript updates | | `tool_call` | Sent when a tool/function is invoked | | `transfer_initiated` | Sent when a call transfer begins | | `sms_received` | Sent when an SMS is received | ## Error Responses ### 404 Not Found ```json theme={null} { "detail": "Call with ID 101 not found in your organization" } ``` ## Use Cases ### Debug Failed Webhooks ```python theme={null} import requests def find_failed_webhooks(call_id): response = requests.get( f"https://api.burki.dev/api/v1/calls/{call_id}/webhook-logs", headers={"Authorization": "Bearer YOUR_API_KEY"} ) logs = response.json() failed = [log for log in logs if not log["success"]] for log in failed: print(f"Failed webhook: {log['webhook_type']}") print(f" URL: {log['webhook_url']}") print(f" Error: {log['error_message']}") print(f" Status Code: {log['response_status_code']}") print(f" Retries: {log['retry_count']}") print() return failed ``` ### Monitor Webhook Latency ```python theme={null} def analyze_webhook_performance(call_id): response = requests.get( f"https://api.burki.dev/api/v1/calls/{call_id}/webhook-logs", headers={"Authorization": "Bearer YOUR_API_KEY"} ) logs = response.json() successful_logs = [log for log in logs if log["success"]] if not successful_logs: return None response_times = [log["response_time_ms"] for log in successful_logs] return { "avg_response_time_ms": sum(response_times) / len(response_times), "max_response_time_ms": max(response_times), "min_response_time_ms": min(response_times), "total_webhooks": len(successful_logs) } ``` ### Verify Webhook Delivery ```python theme={null} def verify_webhook_delivery(call_id, expected_types): """Verify that all expected webhook types were delivered successfully.""" response = requests.get( f"https://api.burki.dev/api/v1/calls/{call_id}/webhook-logs", headers={"Authorization": "Bearer YOUR_API_KEY"} ) logs = response.json() delivered_types = set( log["webhook_type"] for log in logs if log["success"] ) missing = set(expected_types) - delivered_types return { "all_delivered": len(missing) == 0, "delivered": list(delivered_types), "missing": list(missing) } # Example usage result = verify_webhook_delivery(101, ["call_started", "call_ended"]) print(f"All webhooks delivered: {result['all_delivered']}") ``` ## Notes * Webhooks are only sent if you have configured a `webhook_url` on your assistant * Failed webhooks are automatically retried up to 3 times with exponential backoff * Response bodies larger than 10KB are truncated in the logs * Webhook logs are retained for 30 days # Call Metrics Source: https://docs.burki.dev/api-reference/calls/data/metrics Retrieve or recalculate per-call analytics and conversation quality metrics. Call metrics summarize response time, turn-taking, silence, word counts, engagement, confidence, and emotional trajectory metrics for one call. ## Get Call Metrics ```http theme={null} GET /api/v1/calls/{call_id}/metrics Authorization: Bearer YOUR_TOKEN ``` If metrics are already stored, Burki returns them from the database. If they are missing, Burki calculates them from transcripts and stores them when possible. ```json Response theme={null} { "avg_response_time": 1.42, "fastest_response_time": 0.8, "response_time_std": 0.31, "conversation_flow_score": 82, "engagement_score": 76, "user_percentage": 47, "ai_percentage": 53, "user_turns": 12, "ai_turns": 13, "avg_customer_response_time": 1.8, "avg_ai_response_time": 0.9, "fastest_customer_response": 0.7, "fastest_ai_response": 0.4, "slowest_customer_response": 4.2, "slowest_ai_response": 2.1, "avg_customer_turn_duration": 3.6, "avg_ai_turn_duration": 2.9, "longest_customer_monologue": 18.2, "longest_ai_monologue": 12.4, "avg_silence_duration": 0.7, "awkward_silences_count": 1, "total_silence_time": 9.4, "silence_percentage": 7.8, "total_customer_words": 420, "total_ai_words": 510, "customer_words_per_minute": 112.5, "ai_words_per_minute": 124.7, "avg_customer_words_per_turn": 35.0, "avg_ai_words_per_turn": 39.2, "total_speaking_time_customer": 43.2, "total_speaking_time_ai": 48.1, "speaking_time_ratio": 0.9, "interruption_count": 0, "avg_confidence_customer": 0.94, "avg_confidence_ai": 0.98, "low_confidence_segments_count": 0, "avg_user_frustration": 0.12, "avg_user_stress": 0.18, "interaction_sentiment_delta": 0.22, "frustration_scores_ts": [ { "t": 12.4, "score": 0.1 } ], "stress_scores_ts": [ { "t": 12.4, "score": 0.2 } ], "price_sensitivity": 0.0, "primary_intent": "reschedule_appointment", "intent_resolved": true, "ai_response_accuracy": 0.86 } ``` ## Recalculate Call Metrics ```http theme={null} PATCH /api/v1/calls/{call_id}/metrics Authorization: Bearer YOUR_TOKEN ``` Forces recalculation and overwrites existing metrics. Use this when: * Previous metrics were generated before an algorithm update. * LLM-based metrics used a misconfigured key. * You need to rerun analytics for the same transcript. ## Error Responses | Status | Cause | | - | - | | `404` | Call was not found in the authenticated user's organization | | `500` | Unexpected metrics calculation or persistence error | # Play Call Recording Source: https://docs.burki.dev/api-reference/calls/data/play-recording Stream a call recording through an authenticated API endpoint. Use this endpoint when a client needs to stream a recording with bearer-token authentication instead of loading a raw storage URL directly. ```http theme={null} GET /api/v1/calls/{call_id}/recording/{recording_id}/play Authorization: Bearer YOUR_TOKEN ``` ## Path Parameters | Parameter | Type | Description | | - | - | - | | `call_id` | integer | Burki call ID | | `recording_id` | integer | Recording ID linked to the call | ## Response Returns audio bytes with the appropriate content type: * `audio/mpeg` for MP3 recordings * `audio/wav` for WAV recordings ## Error Responses | Status | Cause | | - | - | | `202` | Recording is still processing | | `404` | Call, recording, or S3 recording file not found | | `500` | Error downloading the recording file | Recordings are only available when the call belongs to an assistant in the authenticated user's organization. # Update Call Metadata Source: https://docs.burki.dev/api-reference/calls/data/update-metadata PATCH /api/v1/calls/{call_id}/metadata Update the metadata for a specific call. This allows you to store custom information against a call record. The new metadata will be merged with any existing metadata. Update the custom metadata associated with a call. This allows you to attach arbitrary data to call records for tracking, analytics, or integration with your systems. **Merge Behavior**: New metadata is merged with existing metadata. To remove a field, set its value to `null`. ## Use Cases * **CRM Integration**: Link calls to customer records, tickets, or opportunities * **Campaign Tracking**: Tag calls with campaign IDs for attribution * **Custom Analytics**: Add business-specific data for reporting * **Outcome Tracking**: Record call outcomes like "appointment\_booked" or "sale\_completed" ## Path Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `call_id` | integer | Yes | The internal call ID (not the call SID) | ## Request Body | Field | Type | Required | Description | | - | - | - | - | | `metadata` | object | Yes | Key-value pairs to store with the call | ```json theme={null} { "metadata": { "customer_id": "cust_abc123", "ticket_id": "TICKET-456", "campaign": "spring_outreach_2024", "outcome": "appointment_scheduled", "custom_field": "any_value" } } ``` ## Request Example ```bash theme={null} curl -X PATCH "https://api.burki.dev/api/v1/calls/101/metadata" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "metadata": { "customer_id": "cust_abc123", "outcome": "appointment_scheduled" } }' ``` ## Response Returns the updated call object with the merged metadata. ```json theme={null} { "id": 101, "call_sid": "CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "assistant_id": 123, "assistant_name": "ServiceBot", "to_phone_number": "+15551234567", "customer_phone_number": "+15559876543", "status": "completed", "duration": 180, "started_at": "2024-01-15T10:00:00Z", "ended_at": "2024-01-15T10:03:00Z", "call_meta": { "customer_id": "cust_abc123", "outcome": "appointment_scheduled" }, "total_cost": 0.05, "llm_cost": 0.02, "tts_cost": 0.01, "stt_cost": 0.01, "telephony_cost": 0.01, "cost_currency": "USD" } ``` ## Error Responses ### 404 Not Found ```json theme={null} { "detail": "Call with ID 101 not found in your organization" } ``` ### 422 Unprocessable Entity ```json theme={null} { "detail": [ { "loc": ["body", "metadata"], "msg": "field required", "type": "value_error.missing" } ] } ``` ## Examples ### Python - Add Customer Info ```python theme={null} import requests def tag_call_with_customer(call_id, customer_id, customer_name): response = requests.patch( f"https://api.burki.dev/api/v1/calls/{call_id}/metadata", headers={"Authorization": "Bearer YOUR_API_KEY"}, json={ "metadata": { "customer_id": customer_id, "customer_name": customer_name, "tagged_at": "2024-01-15T10:00:00Z" } } ) return response.json() ``` ### Python - Record Call Outcome ```python theme={null} def record_call_outcome(call_id, outcome, notes=None): metadata = { "outcome": outcome, # e.g., "sale", "no_answer", "callback_requested" "outcome_recorded_at": "2024-01-15T10:00:00Z" } if notes: metadata["outcome_notes"] = notes response = requests.patch( f"https://api.burki.dev/api/v1/calls/{call_id}/metadata", headers={"Authorization": "Bearer YOUR_API_KEY"}, json={"metadata": metadata} ) return response.json() ``` ### Node.js - Campaign Attribution ```javascript theme={null} const axios = require('axios'); async function attributeCallToCampaign(callId, campaignId, source) { const response = await axios.patch( `https://api.burki.dev/api/v1/calls/${callId}/metadata`, { metadata: { campaign_id: campaignId, traffic_source: source, attributed_at: new Date().toISOString() } }, { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } } ); return response.data; } ``` ### Webhook Integration A common pattern is to update call metadata from your webhook handler: ```python theme={null} from flask import Flask, request import requests app = Flask(__name__) @app.route('/webhooks/burki', methods=['POST']) def handle_webhook(): event = request.json if event['event'] == 'call_ended': call_id = event['call_id'] # Look up the customer in your CRM customer = lookup_customer_by_phone(event['customer_phone_number']) if customer: # Tag the call with customer info requests.patch( f"https://api.burki.dev/api/v1/calls/{call_id}/metadata", headers={"Authorization": "Bearer YOUR_API_KEY"}, json={ "metadata": { "crm_customer_id": customer['id'], "account_type": customer['type'], "lifetime_value": customer['ltv'] } } ) return {'received': True} ``` ## Best Practices 1. **Use Consistent Keys**: Establish naming conventions for your metadata fields (e.g., `customer_id` vs `customerId`) 2. **Keep Values Simple**: Store IDs and references rather than large objects. Use your own systems for detailed data. 3. **Timestamp Important Updates**: Include timestamps when recording outcomes or status changes. 4. **Don't Store Sensitive Data**: Avoid storing PII, credentials, or other sensitive information in call metadata. ## Notes * Metadata is stored as JSON and supports nested objects * There is no size limit enforced, but keep metadata concise * Metadata is included in call exports and analytics * Use the [List Calls](/api-reference/calls/list) endpoint to filter calls by supported top-level metadata fields such as customer phone, assistant, status, dates, and call SID. # Export Calls Source: https://docs.burki.dev/api-reference/calls/export GET /api/v1/calls/export Export calls data in CSV or JSON format. Returns call data with the specified filters applied (same filter set as GET /). This endpoint allows you to export a list of calls to either a CSV or JSON file. It supports a subset of the filtering options available on the [List Calls](/api-reference/calls/list) endpoint. The response from this endpoint will be a file download. ### Query Parameters * `format` (string, optional, default: `csv`): The desired file format. Can be `csv` or `json`. * `status` (string, optional): Filter calls by their status. * `assistant_id` (integer, optional): Filter calls by a specific assistant. * `customer_phone` (string, optional): Filter by customer phone number. * `call_sid` (string, optional): Filter by Burki call SID / provider call identifier. * `assistant_name` (string, optional): Filter by assistant name. * `date_from` (string, optional): Filter calls from this date (ISO 8601 format). * `date_to` (string, optional): Filter calls to this date (ISO 8601 format). Export filters are combined with AND semantics. ### Response The response is not a JSON object in the traditional sense, but a file that your browser will prompt you to download. **If `format=csv`:** You will receive a `text/csv` file named `calls_export.csv`. ```csv CSV Content theme={null} "Call ID","Call SID","Assistant Name","Customer Phone","Status","Duration (seconds)","Started At","Ended At" 101,"CA...","Support Bot","+1555...","completed",300,"2023-10-30T10:00:00","2023-10-30T10:05:00" 102,"CA...","Sales Rep","+1555...","completed",450,"2023-10-30T11:00:00","2023-10-30T11:07:30" ``` **If `format=json`:** You will receive an `application/json` file named `calls_export.json` containing an array of call objects. ```json JSON Content theme={null} [ { "id": 101, "call_sid": "CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "assistant_name": "Support Bot", "customer_phone_number": "+15559876543", "status": "completed", "duration": 300, "started_at": "2023-10-30T10:00:00", "ended_at": "2023-10-30T10:05:00" } ] ``` # Get Call Analytics Source: https://docs.burki.dev/api-reference/calls/get-analytics GET /api/v1/calls/analytics Get detailed call analytics for your organization. Returns comprehensive metrics and trends for the specified period.

Call Analytics Summary

Use this endpoint to monitor call volume, completion rate, failure rate, duration, and top-performing assistants across a selected reporting period.

How this endpoint behaves

Analytics aggregate calls for your organization over the selected period. Metrics include totals, completion and failure counts, duration, daily rollups, and top assistants.

Pair this endpoint with list and detail call APIs when you need to drill down from a dashboard summary into individual call records.

### Query Parameters * `period` (string, optional, default: `7d`): The time period for the analysis. * Allowed values: `1d`, `7d`, `30d`, `90d`. ### Response The response is a JSON object containing a comprehensive breakdown of call metrics. ```json Response theme={null} { "total_calls": 150, "completed_calls": 135, "failed_calls": 10, "ongoing_calls": 5, "total_duration": 40500, "avg_duration": 300, "success_rate": 90, "daily_stats": { "2023-10-29": { "total": 70, "completed": 65, "failed": 5 }, "2023-10-30": { "total": 80, "completed": 70, "failed": 5 } }, "top_assistants": [ { "assistant_id": 123, "assistant_name": "Support Bot", "calls": 100, "duration": 30000 }, { "assistant_id": 456, "assistant_name": "Sales Rep", "calls": 50, "duration": 10500 } ] } ``` # Get Call by ID Source: https://docs.burki.dev/api-reference/calls/get-by-id GET /api/v1/calls/{call_id} Get a specific call by ID. Returns the call details if it belongs to an assistant in your organization. This endpoint retrieves a single call record by its unique database ID. ### Path Parameters * `call_id` (integer, **required**): The unique identifier for the call you want to retrieve. ### Response The response is a JSON object containing the full details of the specified call. The structure is the same as the objects returned by the [List Calls](/api-reference/calls/list) endpoint. ```json Response theme={null} { "id": 101, "call_sid": "CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "assistant_id": 123, "assistant_name": "ServiceBot", "flow_name": "Customer Support Flow", "to_phone_number": "+15551234567", "customer_phone_number": "+15559876543", "status": "completed", "started_at": "2023-10-30T10:00:00Z", "ended_at": "2023-10-30T10:05:00Z", "duration": 300, "call_meta": { "customer_id": "cust_abc123" }, "total_cost": 0.05, "llm_cost": 0.02, "tts_cost": 0.01, "stt_cost": 0.01, "telephony_cost": 0.01, "cost_currency": "USD" } ``` # Get Call by SID Source: https://docs.burki.dev/api-reference/calls/get-by-sid GET /api/v1/calls/sid/{call_sid} Get a specific call by Twilio Call SID. Returns the call details if it belongs to an assistant in your organization. This endpoint retrieves a single call record by its Burki call SID / provider call identifier. This is useful when you have the call SID from provider webhooks, logs, or call initiation responses and need to look up the corresponding call in Burki. ### Path Parameters * `call_sid` (string, **required**): The Burki call SID / provider call identifier. ### Response The response is a JSON object containing the full details of the specified call. The structure is the same as the objects returned by the [List Calls](/api-reference/calls/list) endpoint. ```json Response theme={null} { "id": 101, "call_sid": "CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "assistant_id": 123, "assistant_name": "ServiceBot", "flow_name": "Customer Support Flow", "to_phone_number": "+15551234567", "customer_phone_number": "+15559876543", "status": "completed", "started_at": "2023-10-30T10:00:00Z", "ended_at": "2023-10-30T10:05:00Z", "duration": 300, "call_meta": { "customer_id": "cust_abc123" }, "total_cost": 0.05, "llm_cost": 0.02, "tts_cost": 0.01, "stt_cost": 0.01, "telephony_cost": 0.01, "cost_currency": "USD" } ``` # Get Call Count Source: https://docs.burki.dev/api-reference/calls/get-count GET /api/v1/calls/count Get the count of calls in your organization with filtering options. This endpoint returns the total number of calls that match a given set of filters. It is useful for quickly getting a count without retrieving the full list of call objects. ### Query Parameters This endpoint supports a subset of the [List Calls](/api-reference/calls/list) filters: * `status` (string, optional) * `assistant_id` (integer, optional) * `date_from` (string, optional) * `date_to` (string, optional) It does not support the search-style filters (`customer_phone`, `call_sid`, `assistant_name`) or duration filters. Use List Calls when you need those filters and read the `total` field from the paginated response. ### Response The response is a JSON object containing the total count of matching calls. ```json Response theme={null} { "count": 42 } ``` # Get Call Stats Source: https://docs.burki.dev/api-reference/calls/get-stats GET /api/v1/calls/stats Get call statistics for your organization. Returns various metrics about calls in your organization. This endpoint provides a high-level summary of call statistics for your entire organization. It's a quick way to get key metrics without any parameters. ### Response The response is a JSON object containing the following key statistics: * `total_calls`: The total number of calls ever made. * `total_duration`: The sum of the duration of all calls, in seconds. * `average_duration`: The average duration of a call, in seconds. ```json Response theme={null} { "total_calls": 500, "total_duration": 150000, "average_duration": 300 } ``` # Initiate Call Source: https://docs.burki.dev/api-reference/calls/initiate POST /calls/initiate Initiates an outbound call from an assistant to a specified phone number. This endpoint is protected and requires authentication. Programmatically initiate an outbound call from one of your AI assistants to a phone number. This is one of the most powerful features of Burki - it allows you to trigger AI-powered outbound calls from your application logic, enabling use cases like appointment reminders, lead follow-ups, and proactive customer outreach. ## How It Works 1. **You send a request** with the source phone number, destination number, and optional customization 2. **Burki resolves the assistant** based on your parameters 3. **The call is placed** through your configured telephony provider (Twilio, Telnyx, Vonage, or SIP) 4. **Your AI assistant** handles the conversation automatically **Multi-Provider Support**: This endpoint works with Twilio, Telnyx, Vonage, and BYO SIP Trunk. The system automatically uses the provider configured for the specified `from_phone_number`. ## Request Body | Parameter | Type | Required | Description | | - | - | - | - | | `from_phone_number` | string | Yes | Phone number to call from (E.164 format). Must be assigned to an assistant or ConversationFlow in your organization. | | `to_phone_number` | string | Yes | Destination phone number (E.164 format). | | `assistant_id` | integer | No | Override the default assistant. If not provided, uses the assistant assigned to `from_phone_number`. | | `welcome_message` | string | No | Custom greeting for this call. Supports `{{variable}}` template syntax. | | `agenda` | string | No | Call objective/context for the AI. Supports `{{variable}}` template syntax. | | `variables` | object | No | Key-value pairs for template substitution in `welcome_message` and `agenda`. | ### Assistant Resolution The system determines which assistant handles the call: 1. **If `assistant_id` is provided**: Uses that specific assistant 2. **If phone number has a ConversationFlow**: Uses the flow's base assistant and live flow runtime 3. **If phone number has a direct assistant**: Uses the assigned assistant ## Examples ### Minimal Request ```json theme={null} { "from_phone_number": "+15551234567", "to_phone_number": "+15559876543" } ``` ### With Custom Welcome Message ```json theme={null} { "from_phone_number": "+15551234567", "to_phone_number": "+15559876543", "welcome_message": "Hi! This is Sarah from Acme Corp calling about your recent inquiry." } ``` ### With Template Variables ```json theme={null} { "from_phone_number": "+15551234567", "to_phone_number": "+15559876543", "welcome_message": "Hello {{name}}, this is a reminder about your {{appointment_type}} appointment tomorrow at {{time}}.", "agenda": "Confirm the appointment with {{name}} and answer any questions they have.", "variables": { "name": "John Smith", "appointment_type": "dental cleaning", "time": "2:30 PM" } } ``` ### With Assistant Override ```json theme={null} { "from_phone_number": "+15551234567", "to_phone_number": "+15559876543", "assistant_id": 456, "welcome_message": "Hi there! I'm calling from our sales team." } ``` ## Response ```json theme={null} { "message": "Call initiated successfully", "call_sid": "CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } ``` | Field | Type | Description | | - | - | - | | `message` | string | Status message | | `call_sid` | string | Unique identifier for the call from your telephony provider | ## Error Responses ### 400 Bad Request Missing or invalid parameters. ```json theme={null} { "detail": "from_phone_number is required" } ``` ```json theme={null} { "detail": "Invalid phone number format. Use E.164 format (e.g., +1234567890)" } ``` ### 402 Payment Required Insufficient account balance. ```json theme={null} { "detail": "Insufficient balance. Please add funds to continue making calls." } ``` ### 403 Forbidden Phone number or assistant doesn't belong to your organization. ```json theme={null} { "detail": "Unauthorized: phone number +15551234567 does not belong to your organization" } ``` ### 404 Not Found No assistant found for the phone number. ```json theme={null} { "detail": "No assistant found for phone number +15551234567" } ``` ## Use Cases ### Appointment Reminders ```python theme={null} import requests def send_appointment_reminder(patient_phone, patient_name, appointment_time, appointment_type): response = requests.post( "https://api.burki.dev/calls/initiate", headers={"Authorization": "Bearer YOUR_API_KEY"}, json={ "from_phone_number": "+15551234567", "to_phone_number": patient_phone, "welcome_message": "Hello {{name}}, this is a friendly reminder about your {{type}} appointment scheduled for {{time}}.", "agenda": "Confirm the appointment, offer rescheduling if needed, and answer any questions.", "variables": { "name": patient_name, "type": appointment_type, "time": appointment_time } } ) return response.json() ``` ### Lead Follow-up ```javascript theme={null} const axios = require('axios'); async function followUpWithLead(leadPhone, leadName, productInterest) { const response = await axios.post('https://api.burki.dev/calls/initiate', { from_phone_number: '+15551234567', to_phone_number: leadPhone, welcome_message: `Hi {{name}}! I'm following up on your interest in our {{product}}.`, agenda: 'Qualify the lead, answer questions about the product, and schedule a demo if interested.', variables: { name: leadName, product: productInterest } }, { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }); return response.data; } ``` ### Bulk Outreach with Campaign For large-scale outbound calling, consider using the [Campaigns API](/api-reference/campaigns) which provides scheduling, retry logic, and analytics. # List Calls Source: https://docs.burki.dev/api-reference/calls/list GET /api/v1/calls Retrieve and filter paginated call records for your Burki organization by status, assistant, customer phone number, date range, and duration. Retrieve a paginated list of all calls for your organization. This endpoint provides comprehensive filtering options to help you find specific calls. ## Query Parameters | Parameter | Type | Default | Description | | - | - | - | - | | `skip` | integer | 0 | Number of records to skip (for pagination) | | `limit` | integer | 100 | Maximum records to return (1-1000) | | `status` | string | - | Filter by status: `ongoing`, `completed`, `failed` | | `assistant_id` | integer | - | Filter by specific assistant ID | | `customer_phone` | string | - | Filter by customer phone (supports partial match) | | `call_sid` | string | - | Filter by call SID/provider call identifier (supports partial match) | | `assistant_name` | string | - | Filter by assistant name (supports partial match and deleted-assistant snapshots) | | `date_from` | string | - | Filter calls started on/after this date (ISO 8601) | | `date_to` | string | - | Filter calls started on/before this date (ISO 8601) | | `min_duration` | integer | - | Minimum call duration in seconds | | `max_duration` | integer | - | Maximum call duration in seconds | Filters are combined with AND semantics. For example, `status=completed&assistant_name=support` returns completed calls whose assistant name matches `support`. ## Request Examples ### Basic Request ```bash theme={null} curl "https://api.burki.dev/api/v1/calls" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### With Filters ```bash theme={null} curl "https://api.burki.dev/api/v1/calls?status=completed&assistant_id=123&date_from=2024-01-01T00:00:00Z&limit=50" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### Pagination ```bash theme={null} # Page 1 curl "https://api.burki.dev/api/v1/calls?skip=0&limit=50" # Page 2 curl "https://api.burki.dev/api/v1/calls?skip=50&limit=50" ``` ## Response The response is a paginated object containing the call items and pagination metadata. ```json theme={null} { "items": [ { "id": 101, "call_sid": "CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "assistant_id": 123, "assistant_name": "ServiceBot", "flow_name": "Customer Support Flow", "to_phone_number": "+15551234567", "customer_phone_number": "+15559876543", "status": "completed", "duration": 180, "started_at": "2024-01-15T10:00:00Z", "ended_at": "2024-01-15T10:03:00Z", "call_meta": { "customer_id": "cust_abc123", "campaign": "winter_outreach" }, "total_cost": 0.05, "llm_cost": 0.02, "tts_cost": 0.01, "stt_cost": 0.01, "telephony_cost": 0.01, "cost_currency": "USD" } ], "total": 1250, "skip": 0, "limit": 100 } ``` ## Response Fields ### Pagination | Field | Type | Description | | - | - | - | | `items` | array | Array of call objects | | `total` | integer | Total number of calls matching the filters | | `skip` | integer | Number of records skipped | | `limit` | integer | Maximum records returned | ### Call Object | Field | Type | Description | | - | - | - | | `id` | integer | Internal call ID | | `call_sid` | string | Burki call SID / provider call identifier | | `assistant_id` | integer | ID of the assistant that handled the call | | `assistant_name` | string | Name of the assistant | | `flow_name` | string | Name of the ConversationFlow (if call was routed through a flow) | | `to_phone_number` | string | The phone number that was called (your number) | | `customer_phone_number` | string | The customer's phone number | | `status` | string | Call status: `ongoing`, `completed`, `failed` | | `duration` | integer | Call duration in seconds | | `started_at` | string | When the call started (ISO 8601) | | `ended_at` | string | When the call ended (ISO 8601) | | `call_meta` | object | Custom metadata attached to the call | ### Cost Fields | Field | Type | Description | | - | - | - | | `total_cost` | number | Total cost of the call | | `llm_cost` | number | Cost of LLM usage (tokens) | | `tts_cost` | number | Cost of text-to-speech | | `stt_cost` | number | Cost of speech-to-text | | `telephony_cost` | number | Cost of telephony (minutes) | | `cost_currency` | string | Currency code (e.g., `USD`) | ## Call Statuses | Status | Description | | - | - | | `ongoing` | Call is currently active | | `completed` | Call ended normally | | `failed` | Call failed (e.g., no answer, busy, error) | ## Examples ### Python - Iterate All Calls ```python theme={null} import requests def get_all_calls(filters=None): """Fetch all calls with pagination.""" all_calls = [] skip = 0 limit = 100 while True: params = {"skip": skip, "limit": limit} if filters: params.update(filters) response = requests.get( "https://api.burki.dev/api/v1/calls", headers={"Authorization": "Bearer YOUR_API_KEY"}, params=params ) data = response.json() all_calls.extend(data["items"]) if len(data["items"]) < limit: break skip += limit return all_calls # Get all completed calls from January 2024 calls = get_all_calls({ "status": "completed", "date_from": "2024-01-01T00:00:00Z", "date_to": "2024-01-31T23:59:59Z" }) print(f"Found {len(calls)} calls") ``` ### Node.js - Filter by Assistant ```javascript theme={null} const axios = require('axios'); async function getCallsByAssistant(assistantId) { const response = await axios.get('https://api.burki.dev/api/v1/calls', { headers: { 'Authorization': 'Bearer YOUR_API_KEY' }, params: { assistant_id: assistantId, status: 'completed', limit: 100 } }); return response.data; } ``` ### Calculate Total Cost ```python theme={null} def calculate_total_costs(assistant_id, date_from, date_to): """Calculate total costs for an assistant in a date range.""" response = requests.get( "https://api.burki.dev/api/v1/calls", headers={"Authorization": "Bearer YOUR_API_KEY"}, params={ "assistant_id": assistant_id, "date_from": date_from, "date_to": date_to, "status": "completed", "limit": 1000 } ) data = response.json() totals = { "calls": len(data["items"]), "duration_seconds": 0, "total_cost": 0, "llm_cost": 0, "tts_cost": 0, "stt_cost": 0, "telephony_cost": 0 } for call in data["items"]: totals["duration_seconds"] += call.get("duration", 0) or 0 totals["total_cost"] += call.get("total_cost", 0) or 0 totals["llm_cost"] += call.get("llm_cost", 0) or 0 totals["tts_cost"] += call.get("tts_cost", 0) or 0 totals["stt_cost"] += call.get("stt_cost", 0) or 0 totals["telephony_cost"] += call.get("telephony_cost", 0) or 0 return totals ``` ## Notes * Results are ordered by `started_at` descending (newest first) * The `flow_name` field is only populated when the call was routed through a ConversationFlow * Cost fields may be `null` for calls that are still ongoing or if cost tracking is not enabled # Filter Calls Source: https://docs.burki.dev/api-reference/calls/search GET /api/v1/calls Burki does not expose a separate full-text `/calls/search` endpoint. Use the [List Calls](/api-reference/calls/list) endpoint with filter query parameters to find calls by phone number, call SID, assistant name, status, assistant, date range, or duration. ### Query Parameters * `customer_phone` (string, optional): Filter by customer phone number. Partial matches are supported after phone normalization. * `call_sid` (string, optional): Filter by Burki call SID / provider call identifier. Partial matches are supported. * `assistant_name` (string, optional): Filter by assistant name. Partial matches are supported, including deleted-assistant snapshots. * `status` (string, optional): Filter by call status. * `assistant_id` (integer, optional): Filter by assistant ID. * `date_from` (string, optional): Filter calls started on or after this ISO timestamp. * `date_to` (string, optional): Filter calls started on or before this ISO timestamp. * `skip` (integer, optional, default: 0): Number of records to skip. * `limit` (integer, optional, default: 100): Maximum number of records to return. Filters are combined with AND semantics. ### Response The response is the same paginated object returned by the [List Calls](/api-reference/calls/list) endpoint. ```json Response theme={null} { "items": [ { "id": 101, "call_sid": "CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "assistant_id": 123, "assistant_name": "ServiceBot", "flow_name": null, "to_phone_number": "+15551234567", "customer_phone_number": "+15559876543", "status": "completed", "started_at": "2023-10-30T10:00:00Z", "ended_at": "2023-10-30T10:05:00Z", "duration": 300, "call_meta": { "customer_id": "cust_abc123" }, "total_cost": 0.05, "llm_cost": 0.02, "tts_cost": 0.01, "stt_cost": 0.01, "telephony_cost": 0.01, "cost_currency": "USD" } ], "total": 1, "skip": 0, "limit": 100 } ``` # Terminate Call Source: https://docs.burki.dev/api-reference/calls/terminate POST /api/v1/calls/{call_sid}/terminate Terminate an ongoing call. This will hang up the call via the telephony provider (Twilio, Telnyx, or SIP). Only works for calls that are currently in 'ongoing' status. Immediately terminate an ongoing call. This endpoint allows you to programmatically hang up a call that is currently in progress. This endpoint requires an authenticated, verified user account. ## Use Cases * **Admin intervention**: Manually end calls that need immediate termination * **Automatic cutoff**: End calls when certain conditions are met (e.g., time limits, budget caps) * **Emergency stop**: Quickly terminate all active calls during system maintenance * **Quality control**: End calls that are not meeting quality standards ## Path Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `call_sid` | string | Yes | The unique call SID (Session ID) from your telephony provider | ## Request This endpoint does not require a request body. ```bash theme={null} curl -X POST "https://api.burki.dev/api/v1/calls/CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/terminate" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ## Response ### Success (200 OK) ```json theme={null} { "success": true, "message": "Call terminated successfully via twilio" } ``` | Field | Type | Description | | - | - | - | | `success` | boolean | Whether the termination was successful | | `message` | string | Status message including the provider used | ## Error Responses ### 400 Bad Request Returned when the call is not in an "ongoing" status. ```json theme={null} { "detail": "Cannot terminate call - current status is 'completed'" } ``` ### 404 Not Found Returned when the call doesn't exist or doesn't belong to your organization. ```json theme={null} { "detail": "Call not found or you don't have permission to terminate it" } ``` ### 500 Internal Server Error Returned when the telephony provider fails to terminate the call. ```json theme={null} { "detail": "Failed to terminate call via twilio" } ``` ## Provider Support This endpoint works with all supported telephony providers: | Provider | Notes | | - | - | | Twilio | Uses Twilio's call update API | | Telnyx | Uses Telnyx Call Control API | | Vonage | Uses Vonage Voice API | | SIP bridge | Uses the configured SIP bridge termination path | The system automatically detects which provider to use based on the call's metadata. ## Examples ### Python ```python theme={null} import requests def terminate_call(call_sid): response = requests.post( f"https://api.burki.dev/api/v1/calls/{call_sid}/terminate", headers={"Authorization": "Bearer YOUR_API_KEY"} ) if response.status_code == 200: print(f"Call {call_sid} terminated successfully") return response.json() else: print(f"Failed to terminate: {response.json()['detail']}") return None ``` ### Node.js ```javascript theme={null} const axios = require('axios'); async function terminateCall(callSid) { try { const response = await axios.post( `https://api.burki.dev/api/v1/calls/${callSid}/terminate`, {}, { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } } ); console.log('Call terminated:', response.data); return response.data; } catch (error) { console.error('Failed to terminate:', error.response.data.detail); throw error; } } ``` ### Terminate All Active Calls ```python theme={null} import requests def terminate_all_active_calls(): # First, get all ongoing calls calls_response = requests.get( "https://api.burki.dev/api/v1/calls", headers={"Authorization": "Bearer YOUR_API_KEY"}, params={"status": "ongoing"} ) ongoing_calls = calls_response.json()["items"] # Terminate each one for call in ongoing_calls: terminate_call(call["call_sid"]) print(f"Terminated call {call['call_sid']}") ``` ## Notes * The call status will be updated to "completed" in the database after termination * Call duration is calculated up to the termination time * Billing is recorded for the portion of the call that occurred * Termination is immediate - there is no graceful hangup message by default # List Campaign Contacts Source: https://docs.burki.dev/api-reference/campaigns/contacts GET /api/v1/campaigns/{campaign_id}/contacts Get paginated list of contacts for a campaign. Retrieve a paginated list of contacts for a specific campaign, with optional status filtering. ### Path Parameters * `campaign_id` (integer, required): The unique identifier of the campaign ### Query Parameters * `page` (integer, optional, default: 1): Page number for pagination * `per_page` (integer, optional, default: 50, max: 1000): Results per page * `status` (string, optional): Filter by contact status ### Contact Statuses | Status | Description | | - | - | | `pending` | Waiting to be contacted | | `in_progress` | Currently being contacted | | `completed` | Successfully contacted | | `failed` | Contact attempt failed | | `skipped` | Skipped (DNC, re-execute filter, etc.) | | `no_answer` | Call not answered | | `busy` | Line was busy | | `voicemail` | Reached voicemail | ### Response ```json Response theme={null} { "contacts": [ { "id": 101, "phone_number": "+15551234567", "contact_data": { "company": "Acme Corp", "source": "csv_import", "variable_values": { "name": "John Smith", "appointment_date": "2024-02-15" }, "custom_fields": { "customer_id": "CUST001" } }, "status": "completed", "attempts": 1, "created_at": "2024-02-01T10:30:00Z", "last_attempt_at": "2024-02-01T11:15:00Z" }, { "id": 102, "phone_number": "+15559876543", "contact_data": { "company": "Tech Inc", "source": "csv_import", "variable_values": { "name": "Jane Doe", "appointment_date": "2024-02-16" } }, "status": "pending", "attempts": 0, "created_at": "2024-02-01T10:30:00Z", "last_attempt_at": null } ], "pagination": { "page": 1, "per_page": 50, "total_items": 250, "total_pages": 5, "has_next": true, "has_prev": false } } ``` ### Response Fields | Field | Description | | - | - | | `id` | Unique contact identifier | | `phone_number` | Contact's phone number (E.164 format) | | `contact_data` | Contact metadata and custom fields | | `contact_data.variable_values` | Values available for templates | | `contact_data.custom_fields` | Additional imported fields | | `status` | Current contact status | | `attempts` | Number of contact attempts | | `created_at` | When contact was added | | `last_attempt_at` | Last contact attempt timestamp | ### Example Code ```bash cURL theme={null} # Get all contacts curl -X GET "https://api.burki.dev/api/v1/campaigns/42/contacts" \ -H "Authorization: Bearer YOUR_API_KEY" # Get failed contacts only curl -X GET "https://api.burki.dev/api/v1/campaigns/42/contacts?status=failed" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```python Python theme={null} import requests # Get all contacts with pagination response = requests.get( "https://api.burki.dev/api/v1/campaigns/42/contacts", params={"page": 1, "per_page": 100}, headers={"Authorization": "Bearer YOUR_API_KEY"} ) data = response.json() for contact in data["contacts"]: print(f"{contact['phone_number']}: {contact['status']}") print(f"Page {data['pagination']['page']} of {data['pagination']['total_pages']}") ``` ```javascript JavaScript theme={null} // Get pending contacts const response = await fetch( "https://api.burki.dev/api/v1/campaigns/42/contacts?status=pending&per_page=100", { headers: { "Authorization": "Bearer YOUR_API_KEY" } } ); const data = await response.json(); data.contacts.forEach(contact => { console.log(`${contact.phone_number}: ${contact.status}`); }); console.log(`Page ${data.pagination.page} of ${data.pagination.total_pages}`); ``` ### Error Responses | Status Code | Description | | - | - | | 400 | Invalid status value | | 404 | Campaign not found | | 401 | Unauthorized | # Create Campaign Source: https://docs.burki.dev/api-reference/campaigns/create POST /api/v1/campaigns Create a new campaign for automated outreach. The campaign starts in `draft` status and must be started separately. ### Request Body | Field | Type | Required | Description | | - | - | - | - | | `name` | string | Yes | Campaign name (1-200 characters) | | `description` | string | No | Campaign description | | `campaign_type` | string | Yes | Type: `call`, `sms`, or `mixed` | | `assistant_id` | integer | Yes | ID of the assistant to use | | `welcome_message_template` | string | No | Template for welcome message | | `agenda_template` | string | No | Template for call agenda | | `end_call_message_template` | string | No | Template for end call message | | `sms_message_template` | string | No | Template for SMS content | | `fallback_values` | object | No | Fallback values for missing variables | | `max_attempts` | integer | No | Max retry attempts (default: 3) | | `retry_delay_minutes` | integer | No | Delay between retries (default: 30) | ### Template Variables Templates support Jinja2-style variables: * `{{name}}` - Contact's name * `{{company}}` - Contact's company * `{{assistant_name}}` - Your assistant's name * Any custom field from imported contacts ### Example Request ```json Request Body theme={null} { "name": "February Appointment Reminders", "description": "Reminder calls for February appointments", "campaign_type": "call", "assistant_id": 123, "welcome_message_template": "Hi {{name|title_case}}, this is {{assistant_name}} calling from {{company}}.", "agenda_template": "I'm calling to remind you about your appointment on {{appointment_date}}.", "end_call_message_template": "Thank you for your time, {{name}}. Have a great day!", "fallback_values": { "name": "there", "company": "our office" }, "max_attempts": 3, "retry_delay_minutes": 60 } ``` ### Response ```json Response theme={null} { "id": 42, "name": "February Appointment Reminders", "description": "Reminder calls for February appointments", "campaign_type": "call", "status": "draft", "assistant_id": 123, "welcome_message_template": "Hi {{name|title_case}}, this is {{assistant_name}} calling from {{company}}.", "agenda_template": "I'm calling to remind you about your appointment on {{appointment_date}}.", "end_call_message_template": "Thank you for your time, {{name}}. Have a great day!", "sms_message_template": null, "fallback_values": { "name": "there", "company": "our office" }, "variable_mappings": {}, "available_variables": [], "max_attempts": 3, "retry_delay_minutes": 60, "total_contacts": 0, "completed_contacts": 0, "failed_contacts": 0, "created_at": "2024-02-01T10:00:00Z", "updated_at": "2024-02-01T10:00:00Z" } ``` ### Example Code ```bash cURL theme={null} curl -X POST "https://api.burki.dev/api/v1/campaigns" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "February Appointment Reminders", "campaign_type": "call", "assistant_id": 123, "welcome_message_template": "Hi {{name}}, this is {{assistant_name}}." }' ``` ```python Python theme={null} import requests response = requests.post( "https://api.burki.dev/api/v1/campaigns", headers={"Authorization": "Bearer YOUR_API_KEY"}, json={ "name": "February Appointment Reminders", "campaign_type": "call", "assistant_id": 123, "welcome_message_template": "Hi {{name}}, this is {{assistant_name}}." } ) campaign = response.json() print(f"Created campaign ID: {campaign['id']}") ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.burki.dev/api/v1/campaigns", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" }, body: JSON.stringify({ name: "February Appointment Reminders", campaign_type: "call", assistant_id: 123, welcome_message_template: "Hi {{name}}, this is {{assistant_name}}." }) }); const campaign = await response.json(); console.log(`Created campaign ID: ${campaign.id}`); ``` ### Next Steps After creating a campaign: 1. **Import contacts** using the import-data endpoint 2. **Configure scheduling** using the schedule endpoint 3. **Start the campaign** using the start endpoint # Delete Campaign Source: https://docs.burki.dev/api-reference/campaigns/delete DELETE /api/v1/campaigns/{campaign_id} Delete a campaign and all associated data. Permanently delete a campaign and all associated data including contacts, executions, and schedules. Running campaigns cannot be deleted. Stop the campaign first using the stop endpoint. This action is irreversible. All campaign data, contacts, and execution history will be permanently deleted. ### Path Parameters * `campaign_id` (integer, required): The unique identifier of the campaign ### Response ```json Response theme={null} { "message": "Campaign deleted successfully" } ``` ### What Gets Deleted When you delete a campaign, the following data is permanently removed: * Campaign configuration and settings * All imported contacts * Execution history and logs * Schedule configurations * Import history records ### Example Code ```bash cURL theme={null} curl -X DELETE "https://api.burki.dev/api/v1/campaigns/42" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```python Python theme={null} import requests response = requests.delete( "https://api.burki.dev/api/v1/campaigns/42", headers={"Authorization": "Bearer YOUR_API_KEY"} ) if response.status_code == 200: print("Campaign deleted successfully") else: print(f"Error: {response.json()}") ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.burki.dev/api/v1/campaigns/42", { method: "DELETE", headers: { "Authorization": "Bearer YOUR_API_KEY" } }); if (response.ok) { console.log("Campaign deleted successfully"); } else { const error = await response.json(); console.error("Error:", error); } ``` ### Error Responses | Status Code | Description | | - | - | | 400 | Cannot delete running campaign - stop it first | | 404 | Campaign not found | | 401 | Unauthorized - invalid or missing API key | ### Best Practices * **Export data first** if you need to keep records of the campaign results * **Stop running campaigns** before attempting to delete * **Consider pausing** instead of deleting if you might need the campaign later # Get Campaign Executions Source: https://docs.burki.dev/api-reference/campaigns/executions GET /api/v1/campaigns/{campaign_id}/executions Get campaign executions with pagination. Retrieve a paginated list of execution records for a campaign. Each execution represents a single contact attempt (call or SMS). ### Path Parameters * `campaign_id` (integer, required): The unique identifier of the campaign ### Query Parameters * `page` (integer, optional, default: 1): Page number for pagination * `per_page` (integer, optional, default: 50, max: 1000): Results per page ### Response ```json Response theme={null} { "executions": [ { "id": 1001, "execution_id": 1001, "contact_id": 101, "contact_name": "John Smith", "contact_phone": "+15551234567", "status": "completed", "call_id": "CA123abc456def789ghi", "sms_id": null, "executed_at": "2024-02-15T14:29:30Z", "execution_data": { "from_number": "+15550001234", "to_number": "+15551234567", "personalized_welcome_message": "Hi John, this is Sarah from Acme Healthcare.", "template_variables_used": { "name": "John Smith", "appointment_date": "February 15th" }, "duration_seconds": 45, "voice_analytics": { "sentiment_score": 0.85 } } }, { "id": 1000, "execution_id": 1000, "contact_id": 102, "contact_name": "Jane Doe", "contact_phone": "+15559876543", "status": "no_answer", "call_id": "CA789ghi012jkl345mno", "sms_id": null, "executed_at": "2024-02-15T14:28:45Z", "execution_data": { "from_number": "+15550001234", "to_number": "+15559876543", "error_details": "No answer after 30 seconds", "retry_reason": "Will retry in 30 minutes" } } ], "pagination": { "page": 1, "per_page": 50, "total_items": 350, "total_pages": 7, "has_next": true, "has_prev": false } } ``` ### Response Fields #### Execution Object | Field | Description | | - | - | | `id` | Execution record ID | | `execution_id` | Same as id (for compatibility) | | `contact_id` | Associated contact ID | | `contact_name` | Contact's name | | `contact_phone` | Contact's phone number | | `status` | Execution status | | `call_id` | Twilio/Telnyx Call SID (for calls) | | `sms_id` | SMS message ID (for SMS) | | `executed_at` | Execution timestamp | | `execution_data` | Detailed execution information | #### Execution Data Object | Field | Description | | - | - | | `from_number` | Outbound caller ID | | `to_number` | Recipient phone number | | `personalized_welcome_message` | Rendered welcome message | | `personalized_agenda` | Rendered agenda (if set) | | `template_variables_used` | Variables and values used | | `duration_seconds` | Call duration | | `error_details` | Error description (if failed) | | `retry_reason` | Retry information | | `voice_analytics` | Voice analysis results | ### Execution Statuses | Status | Description | | - | - | | `pending` | Queued for execution | | `in_progress` | Currently executing | | `completed` | Successfully completed | | `failed` | Execution failed | | `no_answer` | Call not answered | | `busy` | Line was busy | | `voicemail` | Reached voicemail | | `skipped` | Skipped execution | ### Example Code ```bash cURL theme={null} curl -X GET "https://api.burki.dev/api/v1/campaigns/42/executions?page=1&per_page=100" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```python Python theme={null} import requests def get_all_executions(campaign_id, api_key): """Fetch all executions with pagination.""" executions = [] page = 1 while True: response = requests.get( f"https://api.burki.dev/api/v1/campaigns/{campaign_id}/executions", params={"page": page, "per_page": 100}, headers={"Authorization": f"Bearer {api_key}"} ) data = response.json() executions.extend(data["executions"]) if not data["pagination"]["has_next"]: break page += 1 return executions # Get all executions all_executions = get_all_executions(42, "YOUR_API_KEY") # Analyze results completed = [e for e in all_executions if e["status"] == "completed"] failed = [e for e in all_executions if e["status"] in ["failed", "no_answer"]] print(f"Total executions: {len(all_executions)}") print(f"Completed: {len(completed)}") print(f"Failed/No Answer: {len(failed)}") ``` ```javascript JavaScript theme={null} async function getAllExecutions(campaignId, apiKey) { const executions = []; let page = 1; while (true) { const response = await fetch( `https://api.burki.dev/api/v1/campaigns/${campaignId}/executions?page=${page}&per_page=100`, { headers: { "Authorization": `Bearer ${apiKey}` } } ); const data = await response.json(); executions.push(...data.executions); if (!data.pagination.has_next) break; page++; } return executions; } // Get and analyze const executions = await getAllExecutions(42, "YOUR_API_KEY"); const completed = executions.filter(e => e.status === "completed"); console.log(`Success rate: ${(completed.length / executions.length * 100).toFixed(1)}%`); ``` ### Use Cases * **Audit trail**: Review all contact attempts * **Analytics**: Calculate custom metrics * **Debugging**: Investigate failed calls * **Export**: Extract data for external analysis * **Compliance**: Track contact history ### Error Responses | Status Code | Description | | - | - | | 404 | Campaign not found | | 401 | Unauthorized | # Get Campaign Source: https://docs.burki.dev/api-reference/campaigns/get GET /api/v1/campaigns/{campaign_id} Get detailed information about a specific campaign. Retrieve detailed information about a specific campaign, including contact statistics, templates, and configuration. ### Path Parameters * `campaign_id` (integer, required): The unique identifier of the campaign ### Response Returns the complete campaign object with all configuration and statistics. ```json Response theme={null} { "id": 42, "name": "February Appointment Reminders", "description": "Reminder calls for February appointments", "campaign_type": "call", "status": "running", "assistant_id": 123, "assistant_name": "Appointment Bot", "welcome_message_template": "Hi {{name|title_case}}, this is {{assistant_name}} calling from {{company}}.", "agenda_template": "I'm calling to remind you about your appointment on {{appointment_date}}.", "end_call_message_template": "Thank you for your time, {{name}}. Have a great day!", "sms_message_template": null, "fallback_values": { "name": "there", "company": "our office" }, "variable_mappings": { "Phone": "phone_number", "Name": "name", "Appointment Date": "appointment_date" }, "available_variables": ["name", "phone_number", "appointment_date", "company"], "max_attempts": 3, "retry_delay_minutes": 60, "total_contacts": 250, "completed_contacts": 125, "failed_contacts": 10, "created_at": "2024-02-01T10:00:00Z", "updated_at": "2024-02-05T14:30:00Z", "last_import_file_name": "contacts_february.csv", "last_import_date": "2024-02-01T10:30:00Z", "last_import_row_count": 250 } ``` ### Response Fields | Field | Description | | - | - | | `id` | Unique campaign identifier | | `name` | Campaign name | | `description` | Campaign description | | `campaign_type` | Type: `call`, `sms`, or `mixed` | | `status` | Current status | | `assistant_id` | Associated assistant ID | | `assistant_name` | Name of the associated assistant | | `welcome_message_template` | Opening message template | | `agenda_template` | Call agenda template | | `end_call_message_template` | Closing message template | | `sms_message_template` | SMS message template | | `fallback_values` | Default values for missing variables | | `variable_mappings` | Column to variable mappings | | `available_variables` | List of detected variables | | `max_attempts` | Maximum retry attempts | | `retry_delay_minutes` | Delay between retries | | `total_contacts` | Total number of contacts | | `completed_contacts` | Successfully contacted | | `failed_contacts` | Failed contact attempts | | `last_import_file_name` | Name of last imported file | | `last_import_date` | Date of last import | | `last_import_row_count` | Rows in last import | ### Example Code ```bash cURL theme={null} curl -X GET "https://api.burki.dev/api/v1/campaigns/42" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```python Python theme={null} import requests response = requests.get( "https://api.burki.dev/api/v1/campaigns/42", headers={"Authorization": "Bearer YOUR_API_KEY"} ) campaign = response.json() print(f"Campaign: {campaign['name']}") print(f"Status: {campaign['status']}") print(f"Progress: {campaign['completed_contacts']}/{campaign['total_contacts']}") ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.burki.dev/api/v1/campaigns/42", { headers: { "Authorization": "Bearer YOUR_API_KEY" } }); const campaign = await response.json(); console.log(`Campaign: ${campaign.name}`); console.log(`Status: ${campaign.status}`); console.log(`Progress: ${campaign.completed_contacts}/${campaign.total_contacts}`); ``` ### Error Responses | Status Code | Description | | - | - | | 404 | Campaign not found | | 401 | Unauthorized - invalid or missing API key | | 403 | Forbidden - campaign belongs to different organization | # Import Campaign Contacts Source: https://docs.burki.dev/api-reference/campaigns/import-data POST /api/v1/campaigns/{campaign_id}/import-data Accept a CSV upload and queue it for asynchronous processing. Previously this endpoint parsed and inserted contacts inline, which on a 10k-row CSV would block the FastAPI worker for 30+ seconds and time out most reverse proxies. Now the route persists the upload as a `CampaignDataImport` row, dispatches a Celery task, and returns 202. The client polls `GET /campaigns/{id}/imports/{import_id}` for status. Bulk import contacts from a CSV file into a campaign. The endpoint validates phone numbers, detects duplicates, and maps columns to contact fields. ### Path Parameters * `campaign_id` (integer, required): The unique identifier of the campaign ### Request This endpoint accepts `multipart/form-data` with the following fields: | Field | Type | Required | Description | | - | - | - | - | | `file` | file | Yes | CSV file with contacts | | `column_mappings` | string | No | JSON string mapping CSV columns to fields | ### CSV Format Requirements * **Required column**: Phone number (auto-detected or mapped) * **Recommended**: Header row with column names * **Encoding**: UTF-8 * **Delimiter**: Comma (,) ### Phone Number Validation Phone numbers are automatically normalized to E.164 format: | Input Format | Normalized Output | | - | - | | `5551234567` | `+15551234567` | | `1-555-123-4567` | `+15551234567` | | `(555) 123-4567` | `+15551234567` | | `+15551234567` | `+15551234567` | ### Column Mapping The system auto-detects common column names: | CSV Column Names | Maps To | | - | - | | phone, phone\_number, mobile, cell | `phone_number` | | name, full\_name, contact\_name | `name` | | email, email\_address | `email` | | company, organization | `company` | For custom mappings, provide a JSON object: ```json theme={null} { "Phone Number": "phone_number", "Customer Name": "name", "Appt Date": "appointment_date" } ``` ### Example CSV ```csv theme={null} Phone Number,Customer Name,Company,Appointment Date 5551234567,John Smith,Acme Corp,2024-02-15 5559876543,Jane Doe,Tech Inc,2024-02-16 ``` ### Response ```json Response theme={null} { "success": true, "message": "Successfully imported 250 contacts", "imported_count": 250, "total_contacts": 250, "file_name": "contacts_february.csv", "import_date": "2024-02-01T10:30:00Z" } ``` ### Response Fields | Field | Description | | - | - | | `success` | Whether import succeeded | | `message` | Human-readable status | | `imported_count` | Number of contacts imported | | `total_contacts` | Total contacts in campaign after import | | `file_name` | Original filename | | `import_date` | Import timestamp | ### Example Code ```bash cURL theme={null} curl -X POST "https://api.burki.dev/api/v1/campaigns/42/import-data" \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "file=@contacts.csv" \ -F 'column_mappings={"Phone": "phone_number", "Name": "name"}' ``` ```python Python theme={null} import requests # Simple import with open("contacts.csv", "rb") as f: response = requests.post( "https://api.burki.dev/api/v1/campaigns/42/import-data", headers={"Authorization": "Bearer YOUR_API_KEY"}, files={"file": ("contacts.csv", f, "text/csv")} ) result = response.json() print(f"Imported {result['imported_count']} contacts") # With custom column mappings import json with open("contacts.csv", "rb") as f: response = requests.post( "https://api.burki.dev/api/v1/campaigns/42/import-data", headers={"Authorization": "Bearer YOUR_API_KEY"}, files={"file": ("contacts.csv", f, "text/csv")}, data={ "column_mappings": json.dumps({ "Phone": "phone_number", "Customer Name": "name", "Appt Date": "appointment_date" }) } ) ``` ```javascript JavaScript theme={null} const formData = new FormData(); formData.append("file", fileInput.files[0]); formData.append("column_mappings", JSON.stringify({ "Phone": "phone_number", "Name": "name" })); const response = await fetch("https://api.burki.dev/api/v1/campaigns/42/import-data", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY" }, body: formData }); const result = await response.json(); console.log(`Imported ${result.imported_count} contacts`); ``` ### Behavior * **Existing contacts** with same phone number are skipped (not duplicated) * **Pending/skipped contacts** from previous imports are deleted before new import * **Completed/failed contacts** are preserved for history * **All custom columns** become template variables ### Error Responses | Status Code | Description | | - | - | | 400 | Invalid file format or empty file | | 404 | Campaign not found | | 500 | Import processing failed | ### Best Practices 1. **Validate data** before importing - check phone number formats 2. **Use consistent column names** across imports 3. **Include name column** for better personalization 4. **Remove duplicates** in your source data 5. **Test with small file** before large imports # List Campaigns Source: https://docs.burki.dev/api-reference/campaigns/list GET /api/v1/campaigns This endpoint retrieves a paginated list of all campaigns for your organization. Use filters to find specific campaigns by status, type, or assistant. ### Query Parameters * `status` (string, optional): Filter by campaign status (`draft`, `scheduled`, `running`, `paused`, `completed`, `cancelled`, `failed`) * `campaign_type` (string, optional): Filter by type (`call`, `sms`, `mixed`) * `assistant_id` (integer, optional): Filter by assistant ID * `search` (string, optional): Search by campaign name or description * `page` (integer, optional, default: 1): Page number for pagination * `per_page` (integer, optional, default: 50, max: 1000): Number of results per page ### Response Returns a paginated list of campaigns with basic metrics. ```json Response theme={null} { "campaigns": [ { "id": 1, "name": "Q1 Appointment Reminders", "description": "Reminder calls for January appointments", "campaign_type": "call", "status": "completed", "assistant_id": 123, "assistant_name": "Appointment Bot", "total_contacts": 500, "completed_contacts": 485, "failed_contacts": 15, "created_at": "2024-01-15T10:00:00Z", "started_at": "2024-01-16T09:00:00Z", "completed_at": "2024-01-16T14:30:00Z", "metrics": { "success_rate": 97.0, "answer_rate": 85.0, "average_duration_seconds": 45, "total_duration_seconds": 21825, "actual_cost": 12.50 } } ], "pagination": { "page": 1, "per_page": 50, "total_items": 1, "total_pages": 1, "has_next": false, "has_prev": false } } ``` ### Example Request ```bash cURL theme={null} curl -X GET "https://api.burki.dev/api/v1/campaigns?status=running&page=1&per_page=20" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```python Python theme={null} import requests response = requests.get( "https://api.burki.dev/api/v1/campaigns", params={"status": "running", "page": 1, "per_page": 20}, headers={"Authorization": "Bearer YOUR_API_KEY"} ) campaigns = response.json() for campaign in campaigns["campaigns"]: print(f"{campaign['name']}: {campaign['status']}") ``` ```javascript JavaScript theme={null} const response = await fetch( "https://api.burki.dev/api/v1/campaigns?status=running&page=1&per_page=20", { headers: { "Authorization": "Bearer YOUR_API_KEY" } } ); const data = await response.json(); data.campaigns.forEach(campaign => { console.log(`${campaign.name}: ${campaign.status}`); }); ``` # Pause Campaign Source: https://docs.burki.dev/api-reference/campaigns/pause POST /api/v1/campaigns/{campaign_id}/pause Pause campaign execution. Pause a running campaign. In-progress calls will complete, but no new calls will be initiated. ### Path Parameters * `campaign_id` (integer, required): The unique identifier of the campaign ### Behavior When you pause a campaign: * **In-progress calls** continue until completion * **Pending contacts** remain in queue * **No new calls** are initiated * Campaign status changes to `paused` ### Response ```json Response theme={null} { "message": "Campaign paused successfully" } ``` ### Example Code ```bash cURL theme={null} curl -X POST "https://api.burki.dev/api/v1/campaigns/42/pause" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```python Python theme={null} import requests response = requests.post( "https://api.burki.dev/api/v1/campaigns/42/pause", headers={"Authorization": "Bearer YOUR_API_KEY"} ) if response.status_code == 200: print("Campaign paused successfully") else: print(f"Error: {response.json()}") ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.burki.dev/api/v1/campaigns/42/pause", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY" } }); if (response.ok) { console.log("Campaign paused successfully"); } else { const error = await response.json(); console.error("Error:", error); } ``` ### Error Responses | Status Code | Description | | - | - | | 400 | Campaign is not running | | 404 | Campaign not found | ### Resuming a Paused Campaign To resume a paused campaign, use the [Start Campaign](/api-reference/campaigns/start) endpoint. The campaign will continue from where it left off. ```bash theme={null} curl -X POST "https://api.burki.dev/api/v1/campaigns/42/start" \ -H "Authorization: Bearer YOUR_API_KEY" ``` # Get Campaign Progress Source: https://docs.burki.dev/api-reference/campaigns/progress GET /api/v1/campaigns/{campaign_id}/progress Get real-time campaign execution progress. Get real-time progress and metrics for a campaign. Use this endpoint for dashboard displays and progress tracking. For live updates without polling, use the [Campaign Progress WebSocket](/api-reference/websockets/campaign-progress) instead. ### Path Parameters * `campaign_id` (integer, required): The unique identifier of the campaign ### Response ```json Response theme={null} { "campaign_id": 42, "status": "running", "progress": { "total_contacts": 500, "completed": 325, "failed": 25, "processing": 5, "pending": 145, "progress_percentage": 70.0, "success_rate": 92.86 }, "metrics": { "total_duration_seconds": 14625, "average_duration_seconds": 45, "success_rate": 92.86, "answer_rate": 85.0, "completion_rate": 70.0, "cost_estimate": 25.50, "actual_cost": 18.75, "last_updated": "2024-02-15T14:30:00Z" }, "started_at": "2024-02-15T09:00:00Z", "completed_at": null, "recent_activity": [ { "execution_id": 1001, "contact_phone": "+15551234567", "status": "completed", "executed_at": "2024-02-15T14:29:30Z", "call_id": "CA123abc456def" }, { "execution_id": 1000, "contact_phone": "+15559876543", "status": "failed", "executed_at": "2024-02-15T14:28:45Z", "call_id": "CA789ghi012jkl" } ] } ``` ### Response Fields #### Progress Object | Field | Description | | - | - | | `total_contacts` | Total contacts to process | | `completed` | Successfully contacted | | `failed` | Failed contact attempts | | `processing` | Currently being contacted | | `pending` | Waiting to be contacted | | `progress_percentage` | Completion percentage | | `success_rate` | Success rate (completed / (completed + failed)) | #### Metrics Object | Field | Description | | - | - | | `total_duration_seconds` | Total call time | | `average_duration_seconds` | Average call duration | | `success_rate` | Success percentage | | `answer_rate` | Calls answered percentage | | `completion_rate` | Progress percentage | | `cost_estimate` | Estimated total cost | | `actual_cost` | Actual cost so far | | `last_updated` | Last metrics update | #### Recent Activity Array of recent execution events (most recent first): | Field | Description | | - | - | | `execution_id` | Execution record ID | | `contact_phone` | Contact's phone number | | `status` | Execution status | | `executed_at` | Execution timestamp | | `call_id` | Call SID (for calls) | ### Example Code ```bash cURL theme={null} curl -X GET "https://api.burki.dev/api/v1/campaigns/42/progress" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```python Python theme={null} import requests import time def monitor_campaign(campaign_id, api_key): """Poll campaign progress until complete.""" while True: response = requests.get( f"https://api.burki.dev/api/v1/campaigns/{campaign_id}/progress", headers={"Authorization": f"Bearer {api_key}"} ) data = response.json() progress = data["progress"] print(f"Progress: {progress['completed']}/{progress['total_contacts']} " f"({progress['progress_percentage']:.1f}%)") print(f"Success rate: {progress['success_rate']:.1f}%") if data["status"] in ["completed", "cancelled", "failed"]: print(f"Campaign {data['status']}") break time.sleep(5) # Poll every 5 seconds monitor_campaign(42, "YOUR_API_KEY") ``` ```javascript JavaScript theme={null} async function monitorCampaign(campaignId, apiKey) { while (true) { const response = await fetch( `https://api.burki.dev/api/v1/campaigns/${campaignId}/progress`, { headers: { "Authorization": `Bearer ${apiKey}` } } ); const data = await response.json(); const progress = data.progress; console.log(`Progress: ${progress.completed}/${progress.total_contacts} ` + `(${progress.progress_percentage.toFixed(1)}%)`); console.log(`Success rate: ${progress.success_rate.toFixed(1)}%`); if (["completed", "cancelled", "failed"].includes(data.status)) { console.log(`Campaign ${data.status}`); break; } await new Promise(resolve => setTimeout(resolve, 5000)); } } monitorCampaign(42, "YOUR_API_KEY"); ``` ### Error Responses | Status Code | Description | | - | - | | 404 | Campaign not found | | 401 | Unauthorized | ### Polling Recommendations | Scenario | Recommended Interval | | - | - | | Active dashboard | 5-10 seconds | | Background monitoring | 30-60 seconds | | Batch processing | On-demand | For real-time updates without polling overhead, use the WebSocket endpoint. # Re-execute Campaign Source: https://docs.burki.dev/api-reference/campaigns/re-execute POST /api/v1/campaigns/{campaign_id}/re-execute Re-execute a completed or failed campaign with new contact selection and scheduling. Preserves all execution history while allowing a fresh run with customizable parameters. Re-execute a completed, cancelled, or failed campaign with flexible contact selection and scheduling options. ### Path Parameters * `campaign_id` (integer, required): The unique identifier of the campaign ### Request Body | Field | Type | Required | Description | | - | - | - | - | | `contact_selection` | string or array | Yes | Which contacts to include | | `schedule_settings` | object | No | Scheduling configuration | ### Contact Selection Options | Value | Description | | - | - | | `"all"` | Reset and re-contact all contacts | | `"failed_only"` | Only retry failed, no\_answer, busy, voicemail contacts | | `[1, 2, 3]` | Array of specific contact IDs to retry | ### Schedule Settings ```json theme={null} { "schedule_settings": { "schedule_type": "immediate", "scheduled_at": "2024-02-15T09:00:00Z", "timezone": "America/New_York" } } ``` | Field | Type | Description | | - | - | - | | `schedule_type` | string | `immediate`, `scheduled`, or `recurring` | | `scheduled_at` | string | ISO 8601 datetime for scheduled type | | `timezone` | string | Timezone for scheduling | ### Example Requests **Re-execute all contacts immediately:** ```json theme={null} { "contact_selection": "all", "schedule_settings": { "schedule_type": "immediate" } } ``` **Retry only failed contacts:** ```json theme={null} { "contact_selection": "failed_only", "schedule_settings": { "schedule_type": "scheduled", "scheduled_at": "2024-02-15T09:00:00", "timezone": "America/New_York" } } ``` **Retry specific contacts:** ```json theme={null} { "contact_selection": [101, 102, 103], "schedule_settings": { "schedule_type": "immediate" } } ``` ### Response ```json Response (Immediate) theme={null} { "success": true, "message": "Campaign ready for re-execution with 150 contacts", "campaign_id": 42, "contacts_reset": 150, "schedule_type": "immediate", "status": "draft" } ``` ```json Response (Scheduled) theme={null} { "success": true, "message": "Campaign scheduled for re-execution at 2024-02-15 09:00:00 UTC with 50 contacts", "campaign_id": 42, "contacts_reset": 50, "schedule_type": "once", "status": "scheduled", "scheduled_time": "2024-02-15T09:00:00Z" } ``` ### Example Code ```bash cURL theme={null} curl -X POST "https://api.burki.dev/api/v1/campaigns/42/re-execute" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "contact_selection": "failed_only", "schedule_settings": { "schedule_type": "immediate" } }' ``` ```python Python theme={null} import requests response = requests.post( "https://api.burki.dev/api/v1/campaigns/42/re-execute", headers={"Authorization": "Bearer YOUR_API_KEY"}, json={ "contact_selection": "failed_only", "schedule_settings": { "schedule_type": "immediate" } } ) result = response.json() print(f"Reset {result['contacts_reset']} contacts for re-execution") ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.burki.dev/api/v1/campaigns/42/re-execute", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" }, body: JSON.stringify({ contact_selection: "failed_only", schedule_settings: { schedule_type: "immediate" } }) }); const result = await response.json(); console.log(`Reset ${result.contacts_reset} contacts for re-execution`); ``` ### Behavior When re-executing a campaign: 1. **Selected contacts** are reset to `pending` status 2. **Non-selected contacts** are marked as `skipped` 3. **Campaign counters** are reset to 0 4. **Metrics** are cleared for fresh tracking 5. **Execution history** is preserved for audit 6. **Campaign status** becomes `draft` (immediate) or `scheduled` ### Error Responses | Status Code | Description | | - | - | | 400 | Campaign status doesn't allow re-execution | | 400 | No contacts match the selection criteria | | 400 | Scheduled time is in the past | | 404 | Campaign not found | ### Valid Starting Statuses Re-execution is only available for: * `completed` - All contacts processed * `cancelled` - Campaign was stopped * `failed` - Campaign encountered an error # Get Campaign Schedule Source: https://docs.burki.dev/api-reference/campaigns/schedule-get GET /api/v1/campaigns/{campaign_id}/schedule Get the schedule configuration for a campaign. Retrieve the current schedule configuration for a campaign. ### Path Parameters * `campaign_id` (integer, required): The unique identifier of the campaign ### Response ```json Response (Immediate) theme={null} { "schedule_type": "immediate", "scheduled_at": null, "recurring_config": null } ``` ```json Response (Scheduled) theme={null} { "schedule_type": "scheduled", "scheduled_at": "2024-02-15T09:00:00Z", "recurring_config": null } ``` ```json Response (Recurring) theme={null} { "schedule_type": "recurring", "scheduled_at": "2024-02-15T09:00:00Z", "recurring_config": { "frequency": "weekly", "interval": 1, "days_of_week": [1, 2, 3, 4, 5], "time": "09:00", "time_zone": "America/New_York", "end_date": "2024-12-31" } } ``` ### Response Fields | Field | Description | | - | - | | `schedule_type` | Schedule type: `immediate`, `scheduled`, or `recurring` | | `scheduled_at` | Scheduled execution time (ISO 8601 UTC) | | `recurring_config` | Recurring schedule configuration (if applicable) | ### Recurring Config Fields | Field | Description | | - | - | | `frequency` | `daily`, `weekly`, or `monthly` | | `interval` | Every N days/weeks/months | | `days_of_week` | Days for weekly schedules (1=Monday, 7=Sunday) | | `time` | Execution time (24-hour format) | | `time_zone` | Timezone for scheduling | | `end_date` | When to stop recurring | ### Example Code ```bash cURL theme={null} curl -X GET "https://api.burki.dev/api/v1/campaigns/42/schedule" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```python Python theme={null} import requests from datetime import datetime response = requests.get( "https://api.burki.dev/api/v1/campaigns/42/schedule", headers={"Authorization": "Bearer YOUR_API_KEY"} ) schedule = response.json() if schedule["schedule_type"] == "immediate": print("Campaign set for immediate execution") elif schedule["schedule_type"] == "scheduled": scheduled_at = datetime.fromisoformat(schedule["scheduled_at"].replace("Z", "+00:00")) print(f"Campaign scheduled for: {scheduled_at}") elif schedule["schedule_type"] == "recurring": config = schedule["recurring_config"] print(f"Recurring {config['frequency']} at {config['time']} {config['time_zone']}") ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.burki.dev/api/v1/campaigns/42/schedule", { headers: { "Authorization": "Bearer YOUR_API_KEY" } }); const schedule = await response.json(); switch (schedule.schedule_type) { case "immediate": console.log("Campaign set for immediate execution"); break; case "scheduled": console.log(`Campaign scheduled for: ${schedule.scheduled_at}`); break; case "recurring": const config = schedule.recurring_config; console.log(`Recurring ${config.frequency} at ${config.time} ${config.time_zone}`); break; } ``` ### Error Responses | Status Code | Description | | - | - | | 404 | Campaign not found | | 401 | Unauthorized | # Save Campaign Schedule Source: https://docs.burki.dev/api-reference/campaigns/schedule-save POST /api/v1/campaigns/{campaign_id}/schedule Create or update a campaign schedule. Create or update the schedule for a campaign. Setting a schedule automatically updates the campaign status. ### Path Parameters * `campaign_id` (integer, required): The unique identifier of the campaign ### Request Body | Field | Type | Required | Description | | - | - | - | - | | `schedule_type` | string | Yes | `immediate`, `scheduled`, or `recurring` | | `scheduled_at` | string | Conditional | ISO 8601 datetime (required for `scheduled`) | | `timezone` | string | No | Timezone for scheduling (default: UTC) | | `recurring_config` | object | Conditional | Required for `recurring` type | ### Schedule Types Campaign starts when you call the start endpoint. ```json Request theme={null} { "schedule_type": "immediate" } ``` **Effect**: Campaign status remains `draft` until started manually. Campaign runs at a specific date and time. ```json Request theme={null} { "schedule_type": "scheduled", "scheduled_at": "2024-02-15T09:00:00", "timezone": "America/New_York" } ``` **Effect**: Campaign status changes to `scheduled` automatically. Campaign runs on a repeating schedule. ```json Request theme={null} { "schedule_type": "recurring", "scheduled_at": "2024-02-15T09:00:00", "timezone": "America/New_York", "recurring_config": { "frequency": "weekly", "interval": 1, "days_of_week": [1, 2, 3, 4, 5], "time": "09:00", "time_zone": "America/New_York", "end_date": "2024-12-31" } } ``` **Effect**: Campaign status changes to `scheduled` automatically. ### Recurring Config Options | Field | Type | Description | | - | - | - | | `frequency` | string | `daily`, `weekly`, or `monthly` | | `interval` | integer | Every N periods (e.g., every 2 weeks) | | `days_of_week` | array | Days for weekly (1-7, Monday=1) | | `day_of_month` | integer | Day for monthly (1-31) | | `time` | string | Time in 24-hour format (HH:MM) | | `time_zone` | string | Timezone (e.g., `America/New_York`) | | `end_date` | string | Stop date (ISO 8601) | ### Response ```json Response theme={null} { "success": true, "message": "Schedule saved" } ``` ### Example Code ```bash cURL theme={null} # Immediate schedule curl -X POST "https://api.burki.dev/api/v1/campaigns/42/schedule" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"schedule_type": "immediate"}' # Scheduled for specific time curl -X POST "https://api.burki.dev/api/v1/campaigns/42/schedule" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "schedule_type": "scheduled", "scheduled_at": "2024-02-15T09:00:00", "timezone": "America/New_York" }' ``` ```python Python theme={null} import requests # Schedule for a specific time response = requests.post( "https://api.burki.dev/api/v1/campaigns/42/schedule", headers={"Authorization": "Bearer YOUR_API_KEY"}, json={ "schedule_type": "scheduled", "scheduled_at": "2024-02-15T09:00:00", "timezone": "America/New_York" } ) print(response.json()["message"]) # Set up weekly recurring schedule response = requests.post( "https://api.burki.dev/api/v1/campaigns/42/schedule", headers={"Authorization": "Bearer YOUR_API_KEY"}, json={ "schedule_type": "recurring", "scheduled_at": "2024-02-15T09:00:00", "timezone": "America/New_York", "recurring_config": { "frequency": "weekly", "interval": 1, "days_of_week": [1, 2, 3, 4, 5], # Monday-Friday "time": "09:00", "time_zone": "America/New_York", "end_date": "2024-12-31" } } ) ``` ```javascript JavaScript theme={null} // Schedule for a specific time const response = await fetch("https://api.burki.dev/api/v1/campaigns/42/schedule", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" }, body: JSON.stringify({ schedule_type: "scheduled", scheduled_at: "2024-02-15T09:00:00", timezone: "America/New_York" }) }); const result = await response.json(); console.log(result.message); ``` ### Behavior * **Immediate**: Clears any existing schedule, reverts to `draft` if `scheduled` * **Scheduled/Recurring**: Creates schedule record, sets campaign to `scheduled` * **Past times**: Returns 400 error - time must be in the future ### Error Responses | Status Code | Description | | - | - | | 400 | Scheduled time is in the past | | 400 | Invalid schedule configuration | | 404 | Campaign not found | | 500 | Failed to save schedule | ### Timezone Support Common timezone values: | Region | Timezone | | - | - | | US Eastern | `America/New_York` | | US Central | `America/Chicago` | | US Mountain | `America/Denver` | | US Pacific | `America/Los_Angeles` | | UK | `Europe/London` | | Central Europe | `Europe/Paris` | | UTC | `UTC` | Always specify timezone when scheduling. If omitted, the system uses UTC which may not match your intended local time. # Start Campaign Source: https://docs.burki.dev/api-reference/campaigns/start POST /api/v1/campaigns/{campaign_id}/start Start campaign execution immediately using Celery background tasks. Start campaign execution. The campaign will begin contacting contacts based on the configured schedule. ### Path Parameters * `campaign_id` (integer, required): The unique identifier of the campaign ### Prerequisites Before starting a campaign, ensure: * Campaign has at least one contact imported * Campaign status is `draft`, `scheduled`, or `paused` * Associated assistant is properly configured ### Behavior * **Immediate schedule**: Execution begins immediately * **Scheduled**: Execution is queued for the scheduled time * **Paused campaigns**: Resume execution from where it stopped ### Response ```json Response (Immediate Start) theme={null} { "success": true, "message": "Campaign execution started immediately", "task_id": "abc123-def456", "campaign_id": 42, "status": "scheduled" } ``` ```json Response (Scheduled) theme={null} { "success": true, "message": "Campaign scheduled for 2024-02-15 09:00:00 UTC", "task_id": "abc123-def456", "campaign_id": 42, "status": "scheduled", "scheduled_time": "2024-02-15T09:00:00Z" } ``` ### Response Fields | Field | Description | | - | - | | `success` | Whether the operation succeeded | | `message` | Human-readable status message | | `task_id` | Background task identifier for tracking | | `campaign_id` | The campaign ID | | `status` | New campaign status | | `scheduled_time` | When execution will begin (for scheduled campaigns) | ### Example Code ```bash cURL theme={null} curl -X POST "https://api.burki.dev/api/v1/campaigns/42/start" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```python Python theme={null} import requests response = requests.post( "https://api.burki.dev/api/v1/campaigns/42/start", headers={"Authorization": "Bearer YOUR_API_KEY"} ) result = response.json() if result["success"]: print(f"Campaign started! Task ID: {result['task_id']}") else: print(f"Error: {result}") ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.burki.dev/api/v1/campaigns/42/start", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY" } }); const result = await response.json(); if (result.success) { console.log(`Campaign started! Task ID: ${result.task_id}`); } else { console.error("Error:", result); } ``` ### Error Responses | Status Code | Description | | - | - | | 400 | Cannot start campaign - invalid status or no contacts | | 400 | Scheduled time is in the past | | 404 | Campaign not found | | 500 | Background task system unavailable | ### Common Error Messages | Error | Solution | | - | - | | "No contacts found" | Import contacts before starting | | "All contacts have already been processed" | Use re-execute endpoint to retry | | "Campaign is not running" | Campaign is already in wrong status | | "Scheduled time is in the past" | Update schedule to future time | # Stop Campaign Source: https://docs.burki.dev/api-reference/campaigns/stop POST /api/v1/campaigns/{campaign_id}/stop Stop campaign execution permanently. Stop (cancel) a campaign permanently. Use this to terminate a running, paused, or scheduled campaign. Stopping a campaign marks all remaining pending contacts as cancelled. Use pause if you plan to resume later. ### Path Parameters * `campaign_id` (integer, required): The unique identifier of the campaign ### Behavior When you stop a campaign: * Campaign status changes to `cancelled` * **In-progress calls** may complete (provider-dependent) * **Pending contacts** are marked as cancelled * **Completion timestamp** is recorded * Campaign cannot be resumed (use re-execute for new run) ### Valid Starting Statuses You can stop campaigns that are: * `running` - Currently executing * `paused` - Temporarily paused * `scheduled` - Waiting to start ### Response ```json Response theme={null} { "message": "Campaign stopped successfully" } ``` ### Example Code ```bash cURL theme={null} curl -X POST "https://api.burki.dev/api/v1/campaigns/42/stop" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```python Python theme={null} import requests response = requests.post( "https://api.burki.dev/api/v1/campaigns/42/stop", headers={"Authorization": "Bearer YOUR_API_KEY"} ) if response.status_code == 200: print("Campaign stopped successfully") else: print(f"Error: {response.json()}") ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.burki.dev/api/v1/campaigns/42/stop", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY" } }); if (response.ok) { console.log("Campaign stopped successfully"); } else { const error = await response.json(); console.error("Error:", error); } ``` ### Error Responses | Status Code | Description | | - | - | | 400 | Campaign cannot be stopped (invalid status) | | 404 | Campaign not found | ### Pause vs Stop | Action | Resume Possible | Status | Use Case | | - | - | - | - | | **Pause** | Yes | `paused` | Temporary break, plan to continue | | **Stop** | No | `cancelled` | Permanent termination | ### Re-running a Stopped Campaign To run a stopped campaign again, use the [Re-execute Campaign](/api-reference/campaigns/re-execute) endpoint to reset contacts and start fresh. # Preview Template Source: https://docs.burki.dev/api-reference/campaigns/template-preview POST /api/v1/campaigns/template-preview Preview how a template will be rendered with sample data. Preview how a template will render with sample variable values. Use this to test and validate your templates before starting a campaign. ### Request Body | Field | Type | Required | Description | | - | - | - | - | | `template` | string | Yes | Template string to preview | | `variables` | object | No | Sample variable values | ### Template Syntax Templates use Jinja2-style syntax: | Syntax | Description | | - | - | | `{{variable}}` | Insert variable value | | `{{variable\|filter}}` | Apply filter to variable | | `{{variable\|default('fallback')}}` | Use fallback if missing | ### Available Filters | Filter | Description | Example | | - | - | - | | `phone_format` | Format phone number | `(555) 123-4567` | | `title_case` | Capitalize words | `John Smith` | | `upper` | UPPERCASE | `JOHN SMITH` | | `lower` | lowercase | `john smith` | | `default(value)` | Fallback value | Use if variable missing | ### System Variables These are always available: | Variable | Default Value | | - | - | | `assistant_name` | `"your assistant"` | | `company` | `"our company"` | | `name` | `"there"` | ### Example Request ```json Request Body theme={null} { "template": "Hi {{name|title_case}}, this is {{assistant_name}} from {{company}}. I'm calling about your appointment on {{appointment_date}}.", "variables": { "name": "john smith", "assistant_name": "Sarah", "company": "Acme Healthcare", "appointment_date": "February 15th" } } ``` ### Response ```json Response theme={null} { "success": true, "rendered_template": "Hi John Smith, this is Sarah from Acme Healthcare. I'm calling about your appointment on February 15th." } ``` ### Example with Missing Variables Request with missing `appointment_date`: ```json Request Body theme={null} { "template": "Hi {{name|default('there')}}, your appointment is on {{appointment_date|default('your scheduled date')}}.", "variables": { "name": "John" } } ``` ```json Response theme={null} { "success": true, "rendered_template": "Hi John, your appointment is on your scheduled date." } ``` ### Example Code ```bash cURL theme={null} curl -X POST "https://api.burki.dev/api/v1/campaigns/template-preview" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "template": "Hi {{name}}, this is {{assistant_name}} calling.", "variables": { "name": "John", "assistant_name": "Sarah" } }' ``` ```python Python theme={null} import requests response = requests.post( "https://api.burki.dev/api/v1/campaigns/template-preview", headers={"Authorization": "Bearer YOUR_API_KEY"}, json={ "template": "Hi {{name|title_case}}, this is {{assistant_name}}.", "variables": { "name": "john smith", "assistant_name": "Sarah" } } ) result = response.json() if result["success"]: print(f"Preview: {result['rendered_template']}") else: print("Template error occurred") ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.burki.dev/api/v1/campaigns/template-preview", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" }, body: JSON.stringify({ template: "Hi {{name|title_case}}, this is {{assistant_name}}.", variables: { name: "john smith", assistant_name: "Sarah" } }) }); const result = await response.json(); if (result.success) { console.log(`Preview: ${result.rendered_template}`); } ``` ### Template Examples **Welcome message:** ```text theme={null} Hi {{name|title_case}}, this is {{assistant_name}} from {{company}}. How are you today? ``` **Appointment reminder:** ```text theme={null} I'm calling to remind you about your {{appointment_type|default('appointment')}} scheduled for {{appointment_date}} at {{appointment_time|default('your scheduled time')}}. ``` **SMS message:** ```text theme={null} Hi {{name}}, reminder: You have an appointment at {{company}} on {{date}}. Reply CONFIRM to confirm or call us to reschedule. ``` ### Error Handling If template has syntax errors: ```json Response (Error) theme={null} { "success": false, "rendered_template": "[Template Error: unexpected '}'...] Original template here..." } ``` ### Best Practices 1. **Always test templates** before starting campaigns 2. **Use default filters** for optional variables 3. **Keep templates concise** - especially for SMS 4. **Use title\_case** for names to handle different input formats 5. **Test with empty variables** to ensure fallbacks work # Update Campaign Source: https://docs.burki.dev/api-reference/campaigns/update PUT /api/v1/campaigns/{campaign_id} Update an existing campaign. Update an existing campaign's configuration. Only campaigns in `draft` or `paused` status can be updated. Running or completed campaigns cannot be updated. Pause the campaign first if you need to make changes. ### Path Parameters * `campaign_id` (integer, required): The unique identifier of the campaign ### Request Body All fields are optional. Only include fields you want to update. | Field | Type | Description | | - | - | - | | `name` | string | Campaign name (1-200 characters) | | `description` | string | Campaign description | | `welcome_message_template` | string | Template for welcome message | | `agenda_template` | string | Template for call agenda | | `end_call_message_template` | string | Template for end call message | | `sms_message_template` | string | Template for SMS content | | `fallback_values` | object | Fallback values for missing variables | | `max_attempts` | integer | Max retry attempts | | `retry_delay_minutes` | integer | Delay between retries | ### Example Request ```json Request Body theme={null} { "name": "Updated Campaign Name", "agenda_template": "I'm calling about your upcoming appointment on {{appointment_date}} at {{appointment_time}}.", "max_attempts": 5 } ``` ### Response Returns a success message when the update is complete. ```json Response theme={null} { "message": "Campaign updated successfully" } ``` ### Example Code ```bash cURL theme={null} curl -X PUT "https://api.burki.dev/api/v1/campaigns/42" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Updated Campaign Name", "max_attempts": 5 }' ``` ```python Python theme={null} import requests response = requests.put( "https://api.burki.dev/api/v1/campaigns/42", headers={"Authorization": "Bearer YOUR_API_KEY"}, json={ "name": "Updated Campaign Name", "max_attempts": 5 } ) result = response.json() print(result["message"]) ``` ```javascript JavaScript theme={null} const response = await fetch("https://api.burki.dev/api/v1/campaigns/42", { method: "PUT", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" }, body: JSON.stringify({ name: "Updated Campaign Name", max_attempts: 5 }) }); const result = await response.json(); console.log(result.message); ``` ### Error Responses | Status Code | Description | | - | - | | 400 | Cannot update running or completed campaigns | | 404 | Campaign not found | | 401 | Unauthorized - invalid or missing API key | # Conversation Flows API Source: https://docs.burki.dev/api-reference/flows/overview Create, update, list, delete, and assign phone numbers to ConversationFlows. Conversation Flows are available under `/api/v1/flows`. They are the current orchestration API for multi-step call routing. ## Flow Object ```json theme={null} { "id": 42, "organization_id": 1, "base_assistant_id": 123, "name": "Customer Support Flow", "graph": { "begin_node": "start", "global_prompt": "Be concise.", "nodes": { "start": { "type": "conversation", "prompt": "Greet the caller.", "transitions": [ { "kind": "prompt", "to": "support", "when": "The caller needs support." } ] }, "support": { "type": "conversation", "prompt": "Help the caller with support questions." } } }, "is_active": true, "created_at": "2026-04-29T14:00:00Z", "updated_at": "2026-04-29T14:00:00Z" } ``` ## List Flows Query parameters: | Field | Type | Description | | - | - | - | | `active_only` | boolean | Defaults to `true`. When true, returns only active flows. | ```bash theme={null} curl "https://api.burki.dev/api/v1/flows?active_only=true" \ -H "Authorization: Bearer $BURKI_API_KEY" ``` ## Create Flow ```json theme={null} { "name": "Customer Support Flow", "base_assistant_id": 123, "graph": { "begin_node": "start", "nodes": { "start": { "type": "conversation", "prompt": "Greet the caller and ask how you can help." } } }, "is_active": true } ``` `base_assistant_id` and `graph` can be omitted to let the backend create a starter assistant and one-node starter flow. ## Get Flow ```bash theme={null} curl https://api.burki.dev/api/v1/flows/42 \ -H "Authorization: Bearer $BURKI_API_KEY" ``` ## Update Flow Send any subset of `name`, `base_assistant_id`, `graph`, and `is_active`. ```bash theme={null} curl -X PUT https://api.burki.dev/api/v1/flows/42 \ -H "Authorization: Bearer $BURKI_API_KEY" \ -H "Content-Type: application/json" \ -d '{"is_active": false}' ``` ## Delete Flow Deleting a flow removes the flow record and clears phone-number assignments through database foreign keys. ## Assign Phone Number Assigns a phone number to a flow and clears any direct assistant assignment. ```bash theme={null} curl -X POST https://api.burki.dev/api/v1/flows/42/phone-numbers/7/assign \ -H "Authorization: Bearer $BURKI_API_KEY" ``` ## Unassign Phone Number ```bash theme={null} curl -X POST https://api.burki.dev/api/v1/flows/42/phone-numbers/7/unassign \ -H "Authorization: Bearer $BURKI_API_KEY" ``` # REST API introduction — authentication, base URL & conventions | Burki Docs Source: https://docs.burki.dev/api-reference/introduction Welcome to the Burki Voice AI API Reference. # Burki REST API — Introduction The Burki Voice AI API allows you to programmatically manage assistants, calls, and other resources in your organization. You can use it to build powerful integrations and automate your voice AI workflows. This API is built on REST principles and uses standard HTTP response codes and authentication. All API responses are returned in JSON format. ## Base URL All API requests should be made to: ``` https://api.burki.dev ``` ## Authentication Most API endpoints accept a bearer token in the `Authorization` header: * **API key** (recommended for server-to-server integrations) * **JWT access token** (for user-authenticated app sessions) Many dashboard-backed endpoints also support session auth when used from the web app, but external integrations should use API keys. You can generate and manage API keys in your dashboard at [burki.dev/dashboard](https://burki.dev/dashboard). ### Bearer Authentication Example ```bash cURL theme={null} curl "https://api.burki.dev/api/v1/assistants" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```python Python theme={null} import requests api_key = "YOUR_API_KEY" headers = { "Authorization": f"Bearer {api_key}" } response = requests.get( "https://api.burki.dev/api/v1/assistants", headers=headers ) print(response.json()) ``` ```javascript Node.js theme={null} const fetch = require('node-fetch'); const apiKey = 'YOUR_API_KEY'; fetch('https://api.burki.dev/api/v1/assistants', { headers: { 'Authorization': `Bearer ${apiKey}` } }) .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Error:', error)); ``` Treat your API keys like passwords! Do not share them publicly or commit them to version control. ## Response Format Responses are JSON, but shape varies by endpoint. Two common patterns: ### Resource/List Responses ```json theme={null} [ { "id": 123, "name": "Customer Service Bot" } ] ``` ```json theme={null} { "items": [], "total": 0, "skip": 0, "limit": 100 } ``` ### Error Response (FastAPI style) ```json theme={null} { "detail": "Error description" } ``` ## Error Codes | Code | Description | | - | - | | `400` | Bad Request - Invalid parameters | | `401` | Unauthorized - Invalid or missing API key | | `403` | Forbidden - Insufficient permissions | | `404` | Not Found - Resource doesn't exist | | `429` | Too Many Requests - Endpoint or account rate limit exceeded | | `500` | Internal Server Error | ## Rate Limits Rate limiting is endpoint-specific and can vary by auth type and environment configuration. * Expect `429 Too Many Requests` when limits are exceeded. * Some endpoints include `Retry-After` in the response headers. * Build clients with retry/backoff for transient limit errors. For critical workloads, contact Burki support for current limits and throughput planning. ## Pagination Most list endpoints use `skip` + `limit` pagination: | Parameter | Description | Default | | - | - | - | | `skip` | Number of records to skip | `0` | | `limit` | Maximum number of records to return | endpoint-specific | Common paginated response shape: ```json theme={null} { "items": [...], "total": 100, "skip": 0, "limit": 100 } ``` ## Webhooks Burki can send webhook notifications for various events. Configure webhooks in your dashboard under **Settings** → **Webhooks**. Current webhook payloads are sent as typed JSON messages: * Voice call lifecycle webhooks use `message.type` values such as: * `status-update` * `end-of-call-report` * SMS webhooks use top-level `type` such as: * `sms_received` Always parse webhook payloads by their explicit `type` field instead of assuming dotted event names. ## SDKs & Libraries Official SDKs are available for Python, JavaScript/TypeScript, and Go: | Language | Package | Installation | | - | - | - | | **Python** | `burki` | `pip install burki` | | **JavaScript/TypeScript** | `@burki.dev/sdk` | `npm install @burki.dev/sdk` | | **Go** | `github.com/burki-ai/burki-go` | `go get github.com/burki-ai/burki-go` | Compare SDKs and get started quickly Full Python SDK documentation TypeScript-first SDK with full types Idiomatic Go SDK with channels *** ## Next Steps Get started with Burki in 5 minutes Create and manage AI assistants # Learning and Memory API Source: https://docs.burki.dev/api-reference/learning/overview Memory, prompt lifecycle, eval datasets, stats, graph, DSPy, and call trace APIs. Learning and memory endpoints use the `/api/learning` prefix, not `/api/v1`. ```text theme={null} https://api.burki.dev/api/learning ``` Authentication uses the standard flexible auth path: API key, JWT bearer token, or dashboard session. Results are scoped to the authenticated user's organization. Learning APIs may return customer conversation content, stable caller hashes, memories, prompts, and trace details. Treat these responses as sensitive operational data. ## Memories ### List Memories ```http theme={null} GET /api/learning/memories Authorization: Bearer YOUR_TOKEN ``` Returns memory objects: ```json Response theme={null} [ { "id": 42, "fact": "Customer prefers morning appointment reminders", "memory_type": "preference", "memory_scope": "organization", "subject_type": "caller", "subject_id": "caller_hash_abc", "confidence": 0.91, "importance": 0.7, "provenance": { "source": "call_trace" }, "created_at": "2026-04-30T10:00:00Z", "access_count": 3 } ] ``` ### Get, Create, Update, Delete ```http theme={null} GET /api/learning/memories/{memory_id} POST /api/learning/memories PUT /api/learning/memories/{memory_id} DELETE /api/learning/memories/{memory_id} ``` Create/update request fields include `fact`, `memory_type`, `memory_scope`, `subject_type`, `subject_id`, `confidence`, and optional provenance/edit metadata. Updating `fact` causes the backend to clear the old embedding so it can be regenerated. ### Search Memories ```http theme={null} POST /api/learning/memories/search Authorization: Bearer YOUR_TOKEN Content-Type: application/json ``` ```json Request theme={null} { "query": "What appointment reminder preferences should I know?", "caller_hash": "caller_hash_abc", "assistant_id": 123, "limit": 10 } ``` ```json Response theme={null} { "memories": [ { "id": 42, "fact": "Customer prefers morning appointment reminders", "confidence": 0.91, "source": "call_trace" } ], "intent": "preference_lookup", "traversal_path": [], "retrieval_ms": 12.4 } ``` ### Memory Edges ```http theme={null} GET /api/learning/memories/{memory_id}/edges ``` Returns graph relationships for a memory. ## Prompt Lifecycle Prompt endpoints are scoped to an assistant: ```text theme={null} /api/learning/assistants/{assistant_id}/prompts ``` | Method | Path | Purpose | | - | - | - | | `GET` | `/api/learning/assistants/{assistant_id}/prompts` | List prompt versions | | `GET` | `/api/learning/assistants/{assistant_id}/prompts/{version_id}` | Get one prompt version, including full `system_prompt` | | `POST` | `/api/learning/assistants/{assistant_id}/prompts` | Create a prompt version | | `POST` | `/api/learning/assistants/{assistant_id}/prompts/{version_id}/approve` | Approve a version | | `POST` | `/api/learning/assistants/{assistant_id}/prompts/{version_id}/start-canary` | Start canary rollout | | `PATCH` | `/api/learning/assistants/{assistant_id}/prompts/{version_id}/canary` | Update canary percentage | | `POST` | `/api/learning/assistants/{assistant_id}/prompts/{version_id}/promote` | Promote canary/current version | | `POST` | `/api/learning/assistants/{assistant_id}/prompts/{version_id}/rollback` | Roll back a version | | `GET` | `/api/learning/assistants/{assistant_id}/prompts/{version_id}/gates` | Inspect promotion gates | Create request: ```json Request theme={null} { "system_prompt": "You are a helpful support assistant...", "change_summary": "Tighten escalation behavior" } ``` Canary update request: ```json Request theme={null} { "percentage": 25 } ``` ## Eval Datasets and Cases ```text theme={null} /api/learning/assistants/{assistant_id}/eval-datasets ``` | Method | Path | Purpose | | - | - | - | | `GET` | `/api/learning/assistants/{assistant_id}/eval-datasets` | List datasets | | `GET` | `/api/learning/assistants/{assistant_id}/eval-datasets/{dataset_id}` | Get dataset | | `POST` | `/api/learning/assistants/{assistant_id}/eval-datasets` | Create dataset | | `POST` | `/api/learning/assistants/{assistant_id}/eval-datasets/import` | Import dataset with cases | | `POST` | `/api/learning/assistants/{assistant_id}/eval-datasets/{dataset_id}/publish` | Publish dataset | | `GET` | `/api/learning/assistants/{assistant_id}/eval-datasets/{dataset_id}/cases` | List cases | | `POST` | `/api/learning/assistants/{assistant_id}/eval-datasets/{dataset_id}/cases` | Add case | | `PUT` | `/api/learning/assistants/{assistant_id}/eval-datasets/{dataset_id}/cases/{case_id}` | Update case | | `DELETE` | `/api/learning/assistants/{assistant_id}/eval-datasets/{dataset_id}/cases/{case_id}` | Delete case | | `POST` | `/api/learning/assistants/{assistant_id}/eval-datasets/{dataset_id}/cases/from-trace` | Create case from call trace | Add case request: ```json Request theme={null} { "case_name": "Reschedule appointment", "conversation": [ { "role": "user", "content": "I need to move my appointment" } ], "expected_response": "Ask for the customer's preferred new day and time.", "mock_tool_responses": {}, "difficulty": "medium" } ``` ## Run Evals ```http theme={null} POST /api/learning/assistants/{assistant_id}/eval/run Authorization: Bearer YOUR_TOKEN Content-Type: application/json ``` Runs an evaluation against a dataset/prompt configuration. ## Stats and Graph ```http theme={null} GET /api/learning/assistants/{assistant_id}/stats GET /api/learning/graph-stats GET /api/learning/assistants/{assistant_id}/memory-graph ``` Assistant stats include memory counts, prompt versions, active/canary versions, eval datasets, and last eval pass rate. ## DSPy Optimization ```http theme={null} POST /api/learning/assistants/{assistant_id}/dspy/optimize Authorization: Bearer YOUR_TOKEN ``` Starts DSPy prompt optimization for an assistant. If `dspy-ai` is not installed in the backend environment, the endpoint returns `503 Service Unavailable`. ## Call Traces ```http theme={null} GET /api/learning/call-traces GET /api/learning/call-traces/{trace_id} ``` List responses return shortened caller hashes. The detail endpoint can include full trace data, full `caller_hash`, turns, tool calls, and conversation content. ```json Response theme={null} { "id": 77, "assistant_id": 123, "call_sid": "CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "caller_hash": "abc123...", "turns": [ { "role": "user", "content": "Can I reschedule?" } ], "created_at": "2026-04-30T10:00:00Z" } ``` ## Related * [Learning & Memory Guide](/learning-memory) * [Assistants API](/api-reference/assistants/create) * [Call Data](/api-reference/calls/data/get-transcripts-by-id) # Cancel SMS Source: https://docs.burki.dev/api-reference/messaging/cancel-sms DELETE /sms/{message_id} Cancel a queued SMS message. Only messages that are still in 'queued' status can be cancelled. Messages that are already being sent or have been delivered cannot be cancelled. Args: message_id: The unique message ID returned when queueing the SMS Returns: SMSCancelResponse: Result of the cancellation attempt Cancel a queued SMS message before it is sent. Only messages still in `queued` status can be cancelled. Messages already sending or already sent cannot be cancelled. ### Path Parameters * `message_id` (string, **required**): The Burki message ID returned by `POST /sms/send`. ### Response ```json Response theme={null} { "success": true, "message_id": "8ad70ee5-3d47-4fa4-a6ab-c9f2238d5f54", "message": "SMS cancelled successfully", "previous_status": "queued", "new_status": "cancelled" } ``` If the message cannot be cancelled, the endpoint returns `success: false` with the current status. ```json Response theme={null} { "success": false, "message_id": "8ad70ee5-3d47-4fa4-a6ab-c9f2238d5f54", "message": "Message is already sending", "previous_status": "sending", "new_status": null } ``` # Get SMS Status Source: https://docs.burki.dev/api-reference/messaging/get-sms-status GET /sms/{message_id}/status Get the status of a queued SMS message. Use this endpoint to check if a queued SMS has been delivered, is still pending, or has failed. Args: message_id: The unique message ID returned when queueing the SMS Returns: SMSStatusResponse: Current status of the message Check the current status of a queued SMS message. Use the `message_id` returned by [Send SMS](/api-reference/messaging/send-sms). For queued sends, this is Burki's internal queue UUID. When the provider returns its own ID, it appears as `provider_message_id`. ### Path Parameters * `message_id` (string, **required**): The Burki message ID returned by `POST /sms/send`. ### Response ```json Response theme={null} { "message_id": "8ad70ee5-3d47-4fa4-a6ab-c9f2238d5f54", "status": "sent", "provider_message_id": "SM1234567890abcdef", "error": null, "updated_at": "2026-04-30T10:15:00Z" } ``` ### Status Values | Status | Description | | - | - | | `queued` | Message is waiting in Burki's provider queue | | `sending` | Message is currently being sent | | `sent` | Provider accepted or delivered the message | | `failed` | Sending failed; inspect `error` | | `cancelled` | Message was cancelled before delivery | ### Error Responses ```json 404 theme={null} { "detail": "Message 8ad70ee5-3d47-4fa4-a6ab-c9f2238d5f54 not found" } ``` # SMS Queue Stats Source: https://docs.burki.dev/api-reference/messaging/queue-stats GET /sms/queue/stats Get SMS queue statistics for monitoring. Returns the number of active and pending messages in each provider queue. Args: provider: Optional provider filter (twilio, vonage, telnyx) Returns: SMSQueueStatsResponse: Queue statistics Inspect SMS queue statistics for provider-level monitoring. ### Query Parameters * `provider` (string, optional): Filter queue stats to one provider. Supported values include `twilio`, `telnyx`, and `vonage`. ### Response ```json Response theme={null} { "timestamp": "2026-04-30T10:15:00Z", "queues": { "twilio": { "pending": 2, "active": 1, "failed": 0 }, "telnyx": { "pending": 0, "active": 0, "failed": 0 }, "vonage": { "pending": 1, "active": 0, "failed": 0 } } } ``` The exact queue keys may vary as the queue service evolves, but the response always includes a `timestamp` and a `queues` object keyed by provider. # Send SMS Source: https://docs.burki.dev/api-reference/messaging/send-sms POST /sms/send Send an SMS message through an assistant. This endpoint allows sending SMS messages using the telephony provider associated with the assistant that owns the from_phone_number. The system will automatically: 1. Find the assistant associated with the from_phone_number 2. Use the assistant's configured telephony provider (Twilio, Telnyx, or Vonage) 3. Queue the SMS for delivery with per-provider rate limiting (default) or send immediately if queue=False 4. Persist the outbound message to the SMS conversation (ChatMessage) for UI display Idempotency: Pass an Idempotency-Key header (or idempotency_key in body) to dedupe sends from retried requests. Format: e.g., "chat:123:msg:456:outbound" Args: sms_data: SMS request containing from/to numbers, message, and queue option Returns: SendSMSResponse: Success status, message ID, status, and provider info This endpoint allows you to send SMS messages through one of your AI assistants. The system automatically determines which telephony provider (Twilio, Telnyx, or Vonage) to use based on the assistant configuration associated with the `from_phone_number`. ## How It Works 1. **Assistant Lookup**: The system identifies the assistant based on the `from_phone_number` 2. **Provider Selection**: Automatically uses the assistant's configured telephony provider 3. **Queue or Send**: Either queues the message (with rate limiting) or sends immediately 4. **Response**: Returns success status with message ID, provider, and delivery status **Multi-Provider Support**: Your assistants can use different telephony providers. The system automatically routes SMS messages through the correct provider based on which assistant owns the phone number. ## Request Body | Parameter | Type | Required | Description | | - | - | - | - | | `from_phone_number` | string | Yes | Sender phone number (must be assigned to an assistant) | | `to_phone_number` | string | Yes | Recipient phone number in E.164 format | | `message` | string | Yes | SMS message content (max 1600 characters) | | `media_urls` | array | No | List of media URLs for MMS messages | | `queue` | boolean | No | If `true` (default), queue with rate limiting. If `false`, send immediately | | `idempotency_key` | string | No | Unique key to prevent duplicate sends on retries (max 256 chars) | ### Basic Request ```json theme={null} { "from_phone_number": "+1234567890", "to_phone_number": "+1555123456", "message": "Hello! This is a follow-up message from your AI assistant." } ``` ### With All Options ```json theme={null} { "from_phone_number": "+1234567890", "to_phone_number": "+1555123456", "message": "Hello! This is a follow-up message from your AI assistant.", "media_urls": ["https://example.com/image.jpg"], "queue": false, "idempotency_key": "order:12345:confirmation" } ``` ## Queue vs Immediate Send When `queue: true` (default): * Messages are queued and sent with rate limiting * Helps avoid carrier throttling * Better for bulk sends * Returns `status: "queued"` ```json theme={null} { "queue": true } ``` When `queue: false`: * Message is sent immediately * Better for time-sensitive messages * May hit rate limits if sending many messages * Returns `status: "sent"` on success; provider failures return an error response instead of a successful `failed` response ```json theme={null} { "queue": false } ``` ## Idempotency The `idempotency_key` parameter prevents duplicate messages when retrying failed requests: ```json theme={null} { "from_phone_number": "+1234567890", "to_phone_number": "+1555123456", "message": "Your order #12345 has shipped!", "idempotency_key": "order:12345:shipped:notification" } ``` **Header Alternative**: You can also provide the idempotency key via the `Idempotency-Key` header. If both are provided, the header takes precedence. ## Response A successful request returns a `200 OK` status with details about the message. ```json theme={null} { "success": true, "message_id": "8ad70ee5-3d47-4fa4-a6ab-c9f2238d5f54", "message": "SMS queued successfully via telnyx", "provider": "telnyx", "status": "queued" } ``` ## Response Fields | Field | Type | Description | | - | - | - | | `success` | boolean | Whether the operation was successful | | `message_id` | string | Unique identifier for the message | | `message` | string | Human-readable status message | | `provider` | string | Telephony provider used (`twilio`, `telnyx`, or `vonage`) | | `status` | string | Delivery status: `queued`, `sent`, or `failed` | For queued messages, `message_id` is Burki's queue UUID. After delivery, use the status endpoint to inspect the provider's message ID when available. ## Error Responses ### 400 Bad Request Returned when the request is malformed or contains invalid data. ```json theme={null} { "detail": "Invalid to_phone_number format. Use E.164 format (e.g., +1234567890)" } ``` Common causes: * Missing required fields (`from_phone_number`, `to_phone_number`, `message`) * Invalid phone number format (must be E.164) * Empty message content * Message exceeds 1600 characters ### 403 Forbidden Returned when the authenticated user does not own the assistant or phone number used as `from_phone_number`. ```json theme={null} { "detail": "Unauthorized: phone number belongs to different organization" } ``` ### 404 Not Found Returned when no assistant is found for the specified `from_phone_number`. ```json theme={null} { "detail": "No assistant found for phone number +1234567890" } ``` ### 500 Internal Server Error Returned when the telephony provider fails to send the message. ```json theme={null} { "detail": "Failed to send SMS via telnyx" } ``` ## Use Cases ### Customer Service Follow-up Send follow-up messages after customer service calls: ```json theme={null} { "from_phone_number": "+1234567890", "to_phone_number": "+1555123456", "message": "Thanks for calling! Here's the link we discussed: https://support.example.com/article/123", "queue": false } ``` ### Appointment Reminders Send automated appointment reminders with idempotency: ```json theme={null} { "from_phone_number": "+1234567890", "to_phone_number": "+1555123456", "message": "Reminder: Your appointment is tomorrow at 2:00 PM. Reply CONFIRM to confirm or RESCHEDULE to change the time.", "idempotency_key": "appointment:456:reminder:24h" } ``` ### Bulk Notifications (Queued) For sending many messages, use the queue to avoid rate limits: ```json theme={null} { "from_phone_number": "+1234567890", "to_phone_number": "+1555123456", "message": "Flash sale! 50% off all items today only. Shop now: example.com/sale", "queue": true } ``` ### Document Sharing (MMS) Send documents or images via MMS: ```json theme={null} { "from_phone_number": "+1234567890", "to_phone_number": "+1555123456", "message": "Here are the documents you requested:", "media_urls": [ "https://yourdomain.com/documents/contract.pdf", "https://yourdomain.com/images/diagram.png" ] } ``` ## Authentication This endpoint requires authentication. Include your API key in the request headers: ```bash theme={null} curl -X POST "https://api.burki.dev/sms/send" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "from_phone_number": "+1234567890", "to_phone_number": "+1555123456", "message": "Hello from your AI assistant!" }' ``` ## Related Endpoints | Method | Path | Purpose | | - | - | - | | `GET` | `/sms/{message_id}/status` | Check queued, sent, failed, or cancelled status | | `DELETE` | `/sms/{message_id}` | Cancel a queued message before delivery | | `GET` | `/sms/queue/stats` | Inspect queue statistics by provider | ## Rate Limits SMS sending is subject to rate limits imposed by the underlying telephony providers: * **Twilio**: Varies by account type and message volume * **Telnyx**: Varies by account configuration * **Vonage**: Varies by account type **Provider Limits**: Each telephony provider has its own rate limits and restrictions. Use the `queue: true` option (default) to let Burki handle rate limiting automatically. ## Best Practices ### Phone Number Format Always use E.164 format for phone numbers: * Correct: `+1234567890` * Incorrect: `(123) 456-7890`, `123-456-7890`, `1234567890` ### Idempotency Keys Use meaningful idempotency keys that include: * The type of message (e.g., `order`, `appointment`, `alert`) * A unique identifier (e.g., order ID, appointment ID) * The specific action (e.g., `confirmation`, `reminder`) Examples: * `order:12345:confirmation` * `appointment:789:reminder:24h` * `chat:session123:msg:456:outbound` ### Message Content * Keep messages concise and clear * Include clear call-to-action when needed * Respect opt-out requests and regulatory requirements * Consider character limits for international SMS ### Error Handling Always handle potential errors in your code: ```python theme={null} import requests response = requests.post( "https://api.burki.dev/sms/send", headers={"Authorization": "Bearer YOUR_API_KEY"}, json={ "from_phone_number": "+1234567890", "to_phone_number": "+1555123456", "message": "Hello from your AI assistant!", "idempotency_key": "test:123:hello" } ) if response.status_code == 200: data = response.json() print(f"SMS {data['status']} via {data['provider']}") print(f"Message ID: {data['message_id']}") else: print(f"Error: {response.status_code} - {response.json()['detail']}") ``` ## Integration Examples ### Node.js ```javascript theme={null} const axios = require('axios'); async function sendSMS(fromPhone, toPhone, message, options = {}) { try { const response = await axios.post('https://api.burki.dev/sms/send', { from_phone_number: fromPhone, to_phone_number: toPhone, message: message, queue: options.queue ?? true, idempotency_key: options.idempotencyKey }, { headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); console.log(`SMS ${response.data.status}:`, response.data); return response.data; } catch (error) { console.error('Failed to send SMS:', error.response.data); throw error; } } // Usage sendSMS('+1234567890', '+1555123456', 'Hello!', { queue: false, idempotencyKey: 'test:123' }); ``` ### Python ```python theme={null} import requests def send_sms(from_phone, to_phone, message, media_urls=None, queue=True, idempotency_key=None): url = "https://api.burki.dev/sms/send" headers = { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" } payload = { "from_phone_number": from_phone, "to_phone_number": to_phone, "message": message, "queue": queue } if media_urls: payload["media_urls"] = media_urls if idempotency_key: payload["idempotency_key"] = idempotency_key response = requests.post(url, json=payload, headers=headers) if response.status_code == 200: return response.json() else: raise Exception(f"SMS failed: {response.json()['detail']}") # Usage result = send_sms( "+1234567890", "+1555123456", "Your order has shipped!", idempotency_key="order:789:shipped" ) print(f"Status: {result['status']}") ``` ### PHP ```php theme={null} $fromPhone, 'to_phone_number' => $toPhone, 'message' => $message, 'queue' => $options['queue'] ?? true ]; if (isset($options['media_urls'])) { $data['media_urls'] = $options['media_urls']; } if (isset($options['idempotency_key'])) { $data['idempotency_key'] = $options['idempotency_key']; } $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode === 200) { return json_decode($response, true); } else { throw new Exception("SMS failed: " . $response); } } ?> ``` # SMS 10DLC Campaign APIs Source: https://docs.burki.dev/api-reference/messaging/sms-10dlc Carrier campaign helper APIs for Telnyx, Twilio, and Vonage SMS registration. Burki exposes helper APIs for selecting and inspecting carrier 10DLC campaigns for Telnyx, Twilio, and Vonage. Authentication uses API key, JWT bearer token, or dashboard session. Each provider group requires the matching organization credentials. ## Telnyx Campaigns Base path: ```text theme={null} /api/telnyx ``` | Method | Path | Purpose | | - | - | - | | `GET` | `/api/telnyx/brands` | List Telnyx brands | | `GET` | `/api/telnyx/campaigns?brand_id={brand_id}` | List campaigns for a brand | | `POST` | `/api/telnyx/campaign/select` | Select campaign and brand for new Telnyx numbers | | `GET` | `/api/telnyx/campaign/selected` | Get selected Telnyx campaign | | `GET` | `/api/telnyx/phone-number/{phone_number}/status` | Check Telnyx phone-number campaign status | ```json Request theme={null} { "campaign_id": "campaign_123", "brand_id": "brand_123" } ``` ## Twilio 10DLC Base path: ```text theme={null} /api/twilio-10dlc ``` | Method | Path | Purpose | | - | - | - | | `GET` | `/api/twilio-10dlc/messaging-services` | List messaging services | | `GET` | `/api/twilio-10dlc/messaging-services/{service_sid}/campaigns` | List campaigns for a service | | `POST` | `/api/twilio-10dlc/messaging-service/select` | Select a default messaging service | | `GET` | `/api/twilio-10dlc/messaging-service/selected` | Get selected messaging service | | `POST` | `/api/twilio-10dlc/campaign/select` | Select campaign, messaging service, and brand | | `GET` | `/api/twilio-10dlc/campaign/selected` | Get selected Twilio campaign | ```json Request theme={null} { "campaign_id": "QE1234567890abcdef", "messaging_service_sid": "MG1234567890abcdef", "brand_sid": "BN1234567890abcdef" } ``` ## Vonage 10DLC Base path: ```text theme={null} /api/vonage-10dlc ``` | Method | Path | Purpose | | - | - | - | | `GET` | `/api/vonage-10dlc/brands` | List Vonage brands | | `GET` | `/api/vonage-10dlc/brands/{brand_id}/campaigns` | List campaigns for a brand | | `POST` | `/api/vonage-10dlc/campaign/select` | Select a Vonage campaign | | `GET` | `/api/vonage-10dlc/campaign/selected` | Get selected Vonage campaign | ```json Request theme={null} { "campaign_id": "campaign_123", "brand_id": "brand_123", "usecase": "appointment_reminders", "description": "Appointment reminder and customer support messages" } ``` ## Common Errors | Status | Cause | | - | - | | `400` | Missing provider credentials or invalid selection | | `502` | Upstream provider returned an error | | `503` | Provider API unavailable | | `504` | Provider API timed out | ## Related * [SMS Compliance API](/api-reference/messaging/sms-compliance) * [SMS Messaging](/messaging) * [Telephony Providers](/telephony-providers) # SMS Compliance API Source: https://docs.burki.dev/api-reference/messaging/sms-compliance Verify SMS use cases, check outbound eligibility, and inspect compliance status. The SMS Compliance API manages verification for outbound messaging use cases. ```text theme={null} Base path: /v1/sms-compliance ``` Unlike most messaging endpoints, SMS compliance routes use the auth API's JWT user dependency. Send a Bearer JWT access token. API keys and cookie-only sessions are not accepted by these routes unless your client also sends the JWT. ## Use Case Values Valid `use_case` values: * `customer_support` * `appointment_reminders` * `order_notifications` * `two_factor_auth` * `marketing` * `other` ## Get Compliance Status ```http theme={null} GET /v1/sms-compliance/status/{phone_number_id} Authorization: Bearer JWT_TOKEN ``` ```json Response theme={null} { "found": true, "phone_number": "+15551234567", "phone_number_id": 55, "status": "approved", "use_case": "appointment_reminders", "submitted_at": "2026-04-20T10:00:00Z", "approved_at": "2026-04-21T12:00:00Z", "rejection_reason": null, "can_send_outbound": true, "is_toll_free": false } ``` ## Submit Verification ```http theme={null} POST /v1/sms-compliance/submit Authorization: Bearer JWT_TOKEN Content-Type: application/json ``` ```json Request theme={null} { "phone_number_id": 55, "use_case": "appointment_reminders", "business_name": "Acme Dental", "business_description": "Dental clinic appointment reminders and patient follow-up", "sample_messages": [ "Reminder: your appointment is tomorrow at 2 PM. Reply STOP to opt out." ], "website_url": "https://example.com", "contact_email": "ops@example.com" } ``` `sample_messages` must contain 1-3 examples, each 10-320 characters. ## Can Send ```http theme={null} GET /v1/sms-compliance/can-send/{phone_number_id} Authorization: Bearer JWT_TOKEN ``` ```json Response theme={null} { "can_send": true, "reason": "approved", "status": "approved" } ``` ## Compliance Summary ```http theme={null} GET /v1/sms-compliance/summary Authorization: Bearer JWT_TOKEN ``` ```json Response theme={null} { "total_numbers": 4, "approved": 2, "pending": 1, "rejected": 1, "numbers": [ { "phone_number_id": 55, "phone_number": "+15551234567", "status": "approved", "use_case": "appointment_reminders" } ] } ``` ## Admin Status Update ```http theme={null} PUT /v1/sms-compliance/admin/status/{phone_number_id}?new_status=approved Authorization: Bearer ADMIN_JWT ``` This endpoint requires `current_user.role == "admin"`. Use `rejection_reason` when setting a rejected status: ```http theme={null} PUT /v1/sms-compliance/admin/status/55?new_status=rejected&rejection_reason=Missing%20opt-out%20language ``` # SMS Conversations API Source: https://docs.burki.dev/api-reference/messaging/sms-conversations List, inspect, export, archive, and debug SMS conversation threads. SMS conversations are stored as SMS-channel call records. Use these endpoints to build inboxes, customer timelines, exports, and conversation cleanup flows. ```text theme={null} Base path: /api/v1/sms-conversations ``` Authentication uses the standard flexible auth path: API key, JWT bearer token, or dashboard session. ## Conversation Object ```json theme={null} { "id": 101, "session_id": "sms:+15559876543:+15551234567", "assistant_id": 123, "assistant_name": "Support Bot", "customer_phone_number": "+15559876543", "assistant_phone_number": "+15551234567", "status": "active", "message_count": 8, "started_at": "2026-04-30T10:00:00Z", "last_activity": "2026-04-30T10:14:00Z", "ended_at": null, "channel": "sms" } ``` ## List Conversations ```http theme={null} GET /api/v1/sms-conversations/?skip=0&limit=100&status=active&assistant_id=123 Authorization: Bearer YOUR_TOKEN ``` | Query Parameter | Type | Description | | - | - | - | | `skip` | integer | Records to skip | | `limit` | integer | Records to return (`1-1000`) | | `status` | string | `active` or `completed` | | `assistant_id` | integer | Filter by assistant | | `customer_phone` | string | Filter by customer phone | | `date_from` | string | ISO timestamp lower bound | | `date_to` | string | ISO timestamp upper bound | Deleted/archived SMS threads are excluded. ## Get Conversation ```http theme={null} GET /api/v1/sms-conversations/{conversation_id} Authorization: Bearer YOUR_TOKEN ``` Returns one conversation object or `404`. ## Get Messages ```http theme={null} GET /api/v1/sms-conversations/{conversation_id}/messages Authorization: Bearer YOUR_TOKEN ``` ```json Response theme={null} [ { "id": 1, "conversation_id": 101, "role": "user", "content": "Can I reschedule?", "timestamp": "2026-04-30T10:05:00Z", "message_index": 1, "llm_provider": null, "llm_model": null }, { "id": 2, "conversation_id": 101, "role": "assistant", "content": "Sure. What day works best?", "timestamp": "2026-04-30T10:05:05Z", "message_index": 2, "llm_provider": "openai", "llm_model": "gpt-4o-mini" } ] ``` ## Related Conversations ```http theme={null} GET /api/v1/sms-conversations/{conversation_id}/related Authorization: Bearer YOUR_TOKEN ``` Returns recent related call/SMS records for the same customer and assistant context. ```json Response theme={null} [ { "id": 101, "call_sid": "sms_101", "channel": "sms", "direction": "inbound", "status": "active", "duration": null, "message_count": 8, "started_at": "2026-04-30T10:00:00Z", "ended_at": null, "is_current": true } ] ``` ## Export Conversation ```http theme={null} GET /api/v1/sms-conversations/{conversation_id}/export?format=json Authorization: Bearer YOUR_TOKEN ``` Supported `format` values: `txt`, `csv`, `json`. This endpoint returns a file download with `Content-Disposition`, not a JSON wrapper. ## Archive Conversation ```http theme={null} DELETE /api/v1/sms-conversations/{conversation_id} Authorization: Bearer YOUR_TOKEN ``` Archiving an SMS conversation: * Marks the SMS call record as deleted via `call_meta.sms_deleted`. * Deletes chat messages for the SMS conversation. * Deletes SMS audit and message logs linked to that conversation. * Clears related Redis keys on a best-effort basis. * Allows future messages between the same numbers to start a fresh conversation. ```json Response theme={null} { "success": true, "message": "SMS conversation archived successfully" } ``` ## SMS Sessions Session endpoints expose Redis-backed active SMS session state. ```http theme={null} GET /api/v1/sms-sessions/ DELETE /api/v1/sms-sessions/ POST /api/v1/sms-sessions/cleanup DELETE /api/v1/sms-sessions/{phone_number}/{assistant_id} ``` `GET /api/v1/sms-sessions/` returns: ```json Response theme={null} { "success": true, "data": { "active_sessions": 12, "ttl_seconds": 86400 } } ``` `DELETE /api/v1/sms-sessions/` clears sessions: ```json Response theme={null} { "success": true, "message": "SMS sessions cleared", "cleared_count": 12 } ``` # SMS Logs and Debug Timeline Source: https://docs.burki.dev/api-reference/messaging/sms-logs Inspect SMS provider logs, delivery stats, and merged debug timelines. SMS logs provide provider-level delivery diagnostics. SMS debug timelines merge audit events and message-log delivery events for one SMS conversation. Authentication uses API key, JWT bearer token, or dashboard session. ## List SMS Logs ```http theme={null} GET /api/v1/sms-logs/?skip=0&limit=100&provider=telnyx&status=failed Authorization: Bearer YOUR_TOKEN ``` | Query Parameter | Type | Description | | - | - | - | | `skip` | integer | Records to skip | | `limit` | integer | Records to return (`1-500`) | | `provider` | string | Provider filter | | `direction` | string | `inbound` or `outbound` | | `status` | string | Delivery status filter | | `from_number` | string | Sender phone filter | | `to_number` | string | Recipient phone filter | | `assistant_id` | integer | Assistant filter | | `date_from` | string | ISO lower bound | | `date_to` | string | ISO upper bound | | `search` | string | Body/content search | ```json Response theme={null} { "items": [ { "id": 1, "provider": "telnyx", "provider_message_id": "msg_abc123", "direction": "outbound", "from_number": "+15551234567", "to_number": "+15559876543", "body": "Your appointment is confirmed.", "media_urls": [], "segment_count": 1, "status": "sent", "provider_status": "delivered", "error_code": null, "error_message": null, "assistant_id": 123, "assistant_name": "Support Bot", "created_at": "2026-04-30T10:00:00Z", "updated_at": "2026-04-30T10:00:05Z" } ], "total": 1, "skip": 0, "limit": 100, "has_more": false } ``` ## Get SMS Log ```http theme={null} GET /api/v1/sms-logs/{log_id} Authorization: Bearer YOUR_TOKEN ``` Returns the same fields as list items plus organization and provider debug payload details. ```json Response theme={null} { "id": 1, "organization_id": 9, "provider": "telnyx", "provider_message_id": "msg_abc123", "direction": "outbound", "from_number": "+15551234567", "to_number": "+15559876543", "body": "Your appointment is confirmed.", "media_urls": [], "segment_count": 1, "status": "sent", "provider_status": "delivered", "provider_payload": { "raw_status": "delivered" }, "created_at": "2026-04-30T10:00:00Z", "updated_at": "2026-04-30T10:00:05Z" } ``` ## SMS Log Stats ```http theme={null} GET /api/v1/sms-logs/stats?date_from=2026-04-01&date_to=2026-04-30 Authorization: Bearer YOUR_TOKEN ``` ```json Response theme={null} { "total": 240, "by_direction": { "inbound": 93, "outbound": 147 }, "by_status": { "sent": 130, "delivered": 90, "failed": 20 }, "by_provider": { "twilio": 40, "telnyx": 160, "vonage": 40 }, "failed_count": 20, "success_rate": 91.67 } ``` ## SMS Debug Timeline ```http theme={null} GET /api/v1/sms/{call_id}/logs Authorization: Bearer YOUR_TOKEN ``` The `call_id` is the SMS conversation call record ID. ```json Response theme={null} { "call_id": 101, "log_count": 3, "logs": [ { "id": 1, "timestamp": "2026-04-30T10:00:00Z", "event_type": "webhook_received", "level": "info", "message": "Inbound SMS webhook received", "event_data": { "provider": "telnyx" } } ] } ``` Errors: * `404` if the call/conversation is missing. * `403` if the conversation does not belong to the authenticated user's organization. # List Available Phone Numbers Source: https://docs.burki.dev/api-reference/organization/list-available-phonenumbers GET /api/v1/assistants/organization/phone-numbers/available List phone numbers available for assignment (not assigned to any assistant). This endpoint is a convenient way to retrieve a list of all phone numbers in your organization that are not currently assigned to an assistant. This is useful for finding numbers that are available to be linked to a new or existing assistant. This endpoint takes no parameters. ### Response The response is a JSON array of phone number objects. The `assistant` field for each object will be `null`. ```json Response theme={null} [ { "id": 2, "phone_number": "+15557654321", "friendly_name": "Sales Department", "provider": "telnyx", "twilio_sid": "PNyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", "is_active": true, "is_verified_caller_id": false, "capabilities": { "voice": true, "sms": false, "mms": false }, "sip_trunk_id": null, "allowed_country_codes": null, "assistant": null, "flow": null, "created_at": "2023-10-02T12:00:00Z", "updated_at": null } ] ``` # List Organization Phone Numbers Source: https://docs.burki.dev/api-reference/organization/list-phonenumbers GET /api/v1/assistants/organization/phone-numbers List all phone numbers in your organization. Returns phone numbers with their assignment status and assistant information. By default, only active phone numbers are returned. This endpoint retrieves a list of all phone numbers belonging to your organization. It includes information about each number's assignment status. ### Query Parameters * `include_assigned` (boolean, optional, default: true): Set to `false` to exclude numbers that are currently assigned to an assistant. * `include_unassigned` (boolean, optional, default: true): Set to `false` to exclude numbers that are not currently assigned to any assistant. * `include_inactive` (boolean, optional, default: false): Set to `true` to include inactive or deactivated numbers. ### Response The response is a JSON array of phone number objects. If a number is assigned, the `assistant` field will contain details about the assistant it's linked to. ```json Response theme={null} [ { "id": 1, "phone_number": "+15551234567", "friendly_name": "Main Support Line", "provider": "twilio", "twilio_sid": "PNxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "is_active": true, "is_verified_caller_id": false, "capabilities": { "voice": true, "sms": true, "mms": true }, "sip_trunk_id": null, "allowed_country_codes": null, "assistant": { "id": 123, "name": "Customer Service Bot", "is_active": true }, "flow": null, "created_at": "2023-10-01T10:00:00Z", "updated_at": "2023-10-26T11:00:00Z" }, { "id": 2, "phone_number": "+15557654321", "friendly_name": "Sales Department", "provider": "telnyx", "twilio_sid": "PNyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", "is_active": true, "is_verified_caller_id": false, "capabilities": { "voice": true, "sms": false, "mms": false }, "sip_trunk_id": null, "allowed_country_codes": null, "assistant": null, "flow": null, "created_at": "2023-10-02T12:00:00Z", "updated_at": null } ] ``` # Sync Phone Numbers Source: https://docs.burki.dev/api-reference/organization/sync-phonenumbers POST /api/v1/assistants/organization/phone-numbers/sync Sync phone numbers from all configured providers (Twilio, Telnyx, and Vonage) to local database. This will import any new phone numbers from your Twilio, Telnyx, and/or Vonage accounts that aren't already in the local database. This endpoint triggers synchronization with all configured telephony providers for your organization. It can import new phone numbers from Twilio, Telnyx, and Vonage accounts and add numbers that are not already present in your organization's database. This is useful for ensuring that Burki is up-to-date with the phone numbers available in your provider accounts. The request does not require a body. ### Response A successful sync will return a JSON object confirming the operation and stating how many new numbers were added. ```json Response theme={null} { "success": true, "message": "Successfully synced 3 phone numbers: Twilio: 1, Telnyx: 1, Vonage: 1", "synced_count": 3 } ``` # List Country Codes Source: https://docs.burki.dev/api-reference/phone-numbers/countries GET /api/v1/phone-numbers/countries List available country codes for phone number search. This endpoint returns a list of country codes where phone numbers are available for purchase from the specified provider. Args: provider: Provider to get country codes from ('twilio' or 'telnyx') Returns: CountryCodesResponse: List of available country codes This endpoint returns a list of country codes where phone numbers are available for purchase from the specified telephony provider. Use this to populate country selection dropdowns or validate country availability before searching for numbers. ## How It Works 1. **Provider Query**: Queries the specified provider for available countries 2. **Code Retrieval**: Returns standardized ISO country codes 3. **Coverage Check**: Helps determine global coverage for each provider **Provider Coverage**: Different providers have varying global coverage. Twilio generally supports more countries than Telnyx, but Telnyx may offer better pricing in supported regions. ## Query Parameters * `provider` (string, optional): Provider to get country codes from (`"twilio"` or `"telnyx"`). Defaults to `"telnyx"` ```bash theme={null} GET /api/v1/phone-numbers/countries?provider=telnyx ``` ## Response A successful request returns a `200 OK` status with available country codes. ```json Response theme={null} { "success": true, "country_codes": [ "US", "CA", "GB", "AU", "DE", "FR", "IT", "ES", "NL", "BE" ], "provider": "telnyx" } ``` ## Response Fields * `success` (boolean): Whether the request completed successfully * `country_codes` (array of strings): List of available ISO 3166-1 alpha-2 country codes * `provider` (string): Provider used for the query ("twilio" or "telnyx") ## Country Code Reference ### Common Country Codes | Code | Country | Twilio | Telnyx | | - | - | - | - | | `US` | United States | ✅ | ✅ | | `CA` | Canada | ✅ | ✅ | | `GB` | United Kingdom | ✅ | ✅ | | `AU` | Australia | ✅ | ✅ | | `DE` | Germany | ✅ | ✅ | | `FR` | France | ✅ | ✅ | | `IT` | Italy | ✅ | ✅ | | `ES` | Spain | ✅ | ✅ | | `NL` | Netherlands | ✅ | ✅ | | `BE` | Belgium | ✅ | ✅ | | `SE` | Sweden | ✅ | ⚠️ | | `NO` | Norway | ✅ | ⚠️ | | `DK` | Denmark | ✅ | ⚠️ | **Coverage Notes**: * ✅ = Generally available * ⚠️ = Limited availability or specific restrictions * Coverage may vary by region within countries ## Error Responses ### 400 Bad Request Returned when an invalid provider is specified. ```json theme={null} { "detail": "Provider must be 'twilio' or 'telnyx'" } ``` ### 500 Internal Server Error Returned when the provider API is unavailable. ```json theme={null} { "detail": "Internal error: Provider API unavailable" } ``` ## Usage Examples ### Basic Request ```bash theme={null} curl -X GET "https://api.burki.dev/api/v1/phone-numbers/countries?provider=telnyx" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### Compare Provider Coverage ```bash theme={null} # Get Telnyx coverage curl -X GET "https://api.burki.dev/api/v1/phone-numbers/countries?provider=telnyx" \ -H "Authorization: Bearer YOUR_API_KEY" # Get Twilio coverage curl -X GET "https://api.burki.dev/api/v1/phone-numbers/countries?provider=twilio" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ## Integration Examples ### Node.js ```javascript theme={null} const axios = require('axios'); async function getCountryCodes(provider = 'telnyx') { try { const response = await axios.get(`https://api.burki.dev/api/v1/phone-numbers/countries?provider=${provider}`, { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }); return response.data.country_codes; } catch (error) { console.error('Failed to get country codes:', error.response.data); throw error; } } // Usage const telnyxCountries = await getCountryCodes('telnyx'); const twilioCountries = await getCountryCodes('twilio'); // Find countries supported by both providers const commonCountries = telnyxCountries.filter(code => twilioCountries.includes(code) ); ``` ### Python ```python theme={null} import requests def get_country_codes(provider='telnyx'): url = f"https://api.burki.dev/api/v1/phone-numbers/countries?provider={provider}" headers = {"Authorization": "Bearer YOUR_API_KEY"} response = requests.get(url, headers=headers) if response.status_code == 200: return response.json()['country_codes'] else: raise Exception(f"Failed to get country codes: {response.json()['detail']}") # Usage telnyx_countries = get_country_codes('telnyx') twilio_countries = get_country_codes('twilio') # Compare coverage print(f"Telnyx supports {len(telnyx_countries)} countries") print(f"Twilio supports {len(twilio_countries)} countries") ``` ### PHP ```php theme={null} ``` ## Use Cases ### UI Population Build country selection dropdowns: ```javascript theme={null} async function buildCountryDropdown() { const countries = await getCountryCodes('telnyx'); const countryNames = { 'US': 'United States', 'CA': 'Canada', 'GB': 'United Kingdom', 'AU': 'Australia', 'DE': 'Germany', // ... more mappings }; return countries.map(code => ({ value: code, label: countryNames[code] || code })); } ``` ### Provider Comparison Compare global coverage: ```python theme={null} def compare_provider_coverage(): telnyx_countries = get_country_codes('telnyx') twilio_countries = get_country_codes('twilio') only_telnyx = set(telnyx_countries) - set(twilio_countries) only_twilio = set(twilio_countries) - set(telnyx_countries) both_providers = set(telnyx_countries) & set(twilio_countries) return { 'telnyx_only': list(only_telnyx), 'twilio_only': list(only_twilio), 'both_providers': list(both_providers) } ``` ### Validation Validate country availability before search: ```javascript theme={null} async function validateCountrySupport(countryCode, provider) { const supportedCountries = await getCountryCodes(provider); if (!supportedCountries.includes(countryCode)) { throw new Error(`${provider} does not support country code: ${countryCode}`); } return true; } // Usage try { await validateCountrySupport('FR', 'telnyx'); // Proceed with number search } catch (error) { console.error('Country not supported:', error.message); } ``` ## Provider-Specific Details ### Telnyx Coverage **Strong Coverage**: * North America (US, CA) * Western Europe (GB, DE, FR, NL) * Major English-speaking markets **Features**: * Competitive pricing * Modern API * Good voice quality * SMS/MMS support ### Twilio Coverage **Extensive Coverage**: * Country availability is provider-dependent and changes by product * Broad international inventory in supported regions * Coverage should be checked through this endpoint before promising availability **Features**: * Mature platform * Extensive documentation * Wide feature set * Provider-backed reliability ## Caching Strategy Country codes change infrequently, so implement caching: ```javascript theme={null} class CountryCodeCache { constructor() { this.cache = new Map(); this.TTL = 24 * 60 * 60 * 1000; // 24 hours } async getCountryCodes(provider) { const cacheKey = `countries_${provider}`; const cached = this.cache.get(cacheKey); if (cached && Date.now() - cached.timestamp < this.TTL) { return cached.data; } const countries = await getCountryCodes(provider); this.cache.set(cacheKey, { data: countries, timestamp: Date.now() }); return countries; } } ``` ## Best Practices ### Performance Optimization 1. **Cache Results**: Country codes rarely change, cache for 24+ hours 2. **Parallel Requests**: Fetch both providers' codes simultaneously 3. **Local Fallback**: Maintain a local fallback list for critical countries ### User Experience 1. **Sort by Popularity**: Put common countries first (US, GB, CA, AU) 2. **Search Functionality**: Allow users to search/filter countries 3. **Visual Indicators**: Show which providers support each country ### Error Handling ```python theme={null} def get_country_codes_with_fallback(provider='telnyx'): try: return get_country_codes(provider) except Exception as e: # Log error but return common countries as fallback logger.error(f"Failed to fetch country codes from {provider}: {e}") return ['US', 'CA', 'GB', 'AU', 'DE', 'FR'] # Common fallback ``` ## Regional Considerations ### Regulatory Requirements Some countries have special requirements: * **Germany**: May require local business registration * **India**: Requires local entity for certain number types * **China**: Limited availability through international providers * **UAE**: Specific documentation requirements ### Number Types Different countries offer different number types: * **Toll-Free**: Available in most countries * **Local Numbers**: City/region specific * **National Numbers**: Country-wide accessibility * **Mobile Numbers**: Mobile-specific ranges **Regulatory Compliance**: Always check local telecommunications regulations before purchasing numbers in new countries. Some regions have specific requirements for foreign entities. ## Troubleshooting ### Common Issues | Issue | Cause | Solution | | - | - | - | | Empty country list | Provider API down | Try alternative provider | | Unexpected countries | API change | Update your country mappings | | Missing countries | Provider limitations | Check provider documentation | ### Monitoring Monitor country code availability: ```javascript theme={null} async function monitorCountryAvailability() { const providers = ['twilio', 'telnyx']; const results = {}; for (const provider of providers) { try { const countries = await getCountryCodes(provider); results[provider] = { success: true, count: countries.length, countries: countries }; } catch (error) { results[provider] = { success: false, error: error.message }; } } return results; } ``` This endpoint is essential for building location-aware phone number search interfaces and ensuring your application can handle global phone number requirements across different telephony providers. # Get Phone Number Webhooks Source: https://docs.burki.dev/api-reference/phone-numbers/get-webhooks GET /api/v1/phone-numbers/{phone_number}/webhooks Get current webhook configuration for a phone number. This endpoint returns the current voice and SMS webhook URLs configured for a specific phone number. Args: phone_number: Phone number to get webhooks for (E.164 format) provider: Provider ('twilio' or 'telnyx'). Auto-detected if not provided Returns: GetWebhookResponse: Current webhook configuration This endpoint retrieves the current webhook configuration for a phone number, showing the URLs configured to receive voice call and SMS events. ## How It Works 1. **Provider Detection**: Automatically detects which provider owns the number 2. **Configuration Retrieval**: Fetches current webhook URLs from the provider 3. **Detailed Response**: Returns complete webhook configuration with metadata **Real-Time Data**: This endpoint queries the provider directly to get the most current webhook configuration, not cached data. ## Path Parameters * `phone_number` (string, **required**): Phone number to get webhooks for in E.164 format (e.g., `+14155551234`) ## Query Parameters * `provider` (string, optional): Provider (`"twilio"` or `"telnyx"`). Auto-detected if not provided ```bash theme={null} GET /api/v1/phone-numbers/+14155551234/webhooks?provider=twilio ``` ## Response A successful request returns a `200 OK` status with current webhook configuration. ```json Response theme={null} { "success": true, "phone_number": "+14155551234", "provider": "twilio", "voice_webhook_url": "https://your-app.com/webhooks/voice", "sms_webhook_url": "https://your-app.com/webhooks/sms", "configuration": { "voice_webhook_url": "https://your-app.com/webhooks/voice", "voice_method": "POST", "sms_webhook_url": "https://your-app.com/webhooks/sms", "sms_method": "POST", "status_callback_url": "https://your-app.com/status", "status_callback_method": "POST", "phone_number_sid": "PN1234567890abcdef", "friendly_name": "Support Line" } } ``` ## Response Fields * `success` (boolean): Whether the request completed successfully * `phone_number` (string): The queried phone number * `provider` (string): Provider that owns the number ("twilio" or "telnyx") * `voice_webhook_url` (string): Current voice webhook URL * `sms_webhook_url` (string): Current SMS webhook URL * `configuration` (object): Complete webhook configuration details * **For Twilio**: * `voice_method` (string): HTTP method for voice webhooks * `sms_method` (string): HTTP method for SMS webhooks * `status_callback_url` (string): URL for status callbacks * `phone_number_sid` (string): Twilio phone number SID * `friendly_name` (string): Display name for the number * **For Telnyx**: * `connection_id` (string): Associated connection ID * `phone_number_id` (string): Telnyx phone number ID * `status` (string): Number status * `features` (array): Supported features ## Error Responses ### 400 Bad Request Returned when the request contains invalid parameters. ```json theme={null} { "detail": "Phone number must be in E.164 format (e.g., +1234567890)" } ``` ### 404 Not Found Returned when the phone number is not found. ```json theme={null} { "detail": "Phone number +14155551234 not found in database" } ``` ### 500 Internal Server Error Returned when the provider API fails. ```json theme={null} { "detail": "Could not retrieve webhook configuration for +14155551234" } ``` ## Provider-Specific Responses ### Twilio Response Example ```json theme={null} { "success": true, "phone_number": "+14155551234", "provider": "twilio", "voice_webhook_url": "https://api.yourapp.com/twiml", "sms_webhook_url": "https://api.yourapp.com/twiml", "configuration": { "voice_webhook_url": "https://api.yourapp.com/twiml", "voice_method": "POST", "sms_webhook_url": "https://api.yourapp.com/twiml", "sms_method": "POST", "status_callback_url": "https://api.yourapp.com/status", "status_callback_method": "POST", "phone_number_sid": "PN1234567890abcdef1234567890abcdef", "friendly_name": "Customer Support Line" } } ``` ### Telnyx Response Example ```json theme={null} { "success": true, "phone_number": "+14155551234", "provider": "telnyx", "voice_webhook_url": "Configured at Connection level", "sms_webhook_url": "Configured at Connection level", "configuration": { "connection_id": "1234567890", "phone_number_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "status": "active", "features": ["voice", "sms", "mms"] } } ``` **Telnyx Limitation**: Telnyx webhook URLs are configured at the Connection/Application level, so the exact webhook URLs may not be directly retrievable via the phone number API. The response indicates they are "Configured at Connection level". ## Use Cases ### Configuration Audit Verify webhook configurations across all numbers: ```bash theme={null} # Check multiple numbers for number in "+14155551234" "+12125555678" "+13105559999"; do curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.burki.dev/api/v1/phone-numbers/${number}/webhooks" done ``` ### Environment Verification Confirm webhooks point to the correct environment: ```javascript theme={null} async function verifyEnvironmentWebhooks(phoneNumbers, expectedEnv) { for (const phoneNumber of phoneNumbers) { const config = await getPhoneWebhooks(phoneNumber); if (!config.voice_webhook_url.includes(expectedEnv)) { console.warn(`❌ ${phoneNumber} points to wrong environment`); } else { console.log(`✅ ${phoneNumber} correctly configured`); } } } ``` ### Debugging Call Issues Check webhook configuration when troubleshooting: ```python theme={null} def debug_call_issues(phone_number): config = get_phone_webhooks(phone_number) print(f"Phone: {config['phone_number']}") print(f"Provider: {config['provider']}") print(f"Voice Webhook: {config['voice_webhook_url']}") # Test webhook accessibility if config['voice_webhook_url']: try: response = requests.get(config['voice_webhook_url']) print(f"Webhook Status: {response.status_code}") except Exception as e: print(f"Webhook Error: {e}") ``` ## Integration Examples ### Node.js ```javascript theme={null} const axios = require('axios'); async function getPhoneWebhooks(phoneNumber, provider = null) { try { const params = provider ? `?provider=${provider}` : ''; const response = await axios.get(`https://api.burki.dev/api/v1/phone-numbers/${phoneNumber}/webhooks${params}`, { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }); return response.data; } catch (error) { console.error('Failed to get webhooks:', error.response.data); throw error; } } // Usage const config = await getPhoneWebhooks('+14155551234'); console.log('Voice webhook:', config.voice_webhook_url); console.log('SMS webhook:', config.sms_webhook_url); ``` ### Python ```python theme={null} import requests def get_phone_webhooks(phone_number, provider=None): url = f"https://api.burki.dev/api/v1/phone-numbers/{phone_number}/webhooks" headers = {"Authorization": "Bearer YOUR_API_KEY"} params = {} if provider: params["provider"] = provider response = requests.get(url, headers=headers, params=params) if response.status_code == 200: return response.json() else: raise Exception(f"Failed to get webhooks: {response.json()['detail']}") # Usage config = get_phone_webhooks("+14155551234") print(f"Voice: {config['voice_webhook_url']}") print(f"SMS: {config['sms_webhook_url']}") ``` ### PHP ```php theme={null} ``` ## Batch Operations ### Check Multiple Numbers ```javascript theme={null} async function auditPhoneWebhooks(phoneNumbers) { const results = []; for (const phoneNumber of phoneNumbers) { try { const config = await getPhoneWebhooks(phoneNumber); results.push({ phoneNumber, provider: config.provider, voiceWebhook: config.voice_webhook_url, smsWebhook: config.sms_webhook_url, status: 'success' }); } catch (error) { results.push({ phoneNumber, status: 'error', error: error.message }); } } return results; } // Usage const phoneNumbers = ['+14155551234', '+12125555678', '+13105559999']; const audit = await auditPhoneWebhooks(phoneNumbers); console.table(audit); ``` ### Environment Comparison ```python theme={null} def compare_webhook_environments(phone_numbers): """Compare webhook environments across numbers""" environments = {} for phone_number in phone_numbers: try: config = get_phone_webhooks(phone_number) voice_url = config.get('voice_webhook_url', '') # Extract environment from URL if 'staging' in voice_url: env = 'staging' elif 'production' in voice_url or 'api' in voice_url: env = 'production' else: env = 'unknown' if env not in environments: environments[env] = [] environments[env].append(phone_number) except Exception as e: print(f"Error checking {phone_number}: {e}") return environments ``` ## Monitoring and Alerting ### Webhook Health Check ```python theme={null} import requests from urllib.parse import urlparse def health_check_webhooks(phone_numbers): """Check if webhook URLs are accessible""" issues = [] for phone_number in phone_numbers: config = get_phone_webhooks(phone_number) for webhook_type in ['voice_webhook_url', 'sms_webhook_url']: url = config.get(webhook_type) if url and not url.startswith('Configured'): try: response = requests.get(url, timeout=5) if response.status_code >= 400: issues.append({ 'phone_number': phone_number, 'webhook_type': webhook_type, 'url': url, 'status_code': response.status_code }) except Exception as e: issues.append({ 'phone_number': phone_number, 'webhook_type': webhook_type, 'url': url, 'error': str(e) }) return issues ``` ### Configuration Drift Detection ```javascript theme={null} async function detectConfigDrift(phoneNumbers, expectedWebhooks) { const drifts = []; for (const phoneNumber of phoneNumbers) { const current = await getPhoneWebhooks(phoneNumber); const expected = expectedWebhooks[phoneNumber]; if (!expected) continue; if (current.voice_webhook_url !== expected.voice) { drifts.push({ phoneNumber, type: 'voice', current: current.voice_webhook_url, expected: expected.voice }); } if (current.sms_webhook_url !== expected.sms) { drifts.push({ phoneNumber, type: 'sms', current: current.sms_webhook_url, expected: expected.sms }); } } return drifts; } ``` ## Best Practices ### Regular Auditing 1. **Daily Checks**: Verify webhook configurations daily 2. **Environment Validation**: Ensure correct environment URLs 3. **Accessibility Testing**: Test webhook URL accessibility 4. **Provider Sync**: Compare with provider console settings ### Error Handling ```python theme={null} def safe_get_webhooks(phone_number, retries=3): """Get webhooks with retry logic""" for attempt in range(retries): try: return get_phone_webhooks(phone_number) except Exception as e: if attempt == retries - 1: raise time.sleep(2 ** attempt) # Exponential backoff ``` ### Caching Strategy ```javascript theme={null} class WebhookConfigCache { constructor(ttl = 300000) { // 5 minutes TTL this.cache = new Map(); this.ttl = ttl; } async get(phoneNumber) { const cached = this.cache.get(phoneNumber); if (cached && Date.now() - cached.timestamp < this.ttl) { return cached.data; } const fresh = await getPhoneWebhooks(phoneNumber); this.cache.set(phoneNumber, { data: fresh, timestamp: Date.now() }); return fresh; } } ``` This endpoint is essential for maintaining visibility into your webhook configurations and ensuring your phone numbers are properly connected to your application's event handling endpoints. # Purchase Phone Number Source: https://docs.burki.dev/api-reference/phone-numbers/purchase POST /api/v1/phone-numbers/purchase Purchase a Twilio or Telnyx phone number, configure webhooks automatically, and assign it to a Burki voice AI assistant. This endpoint allows you to purchase a phone number from Twilio or Telnyx and automatically configure it for use with your AI assistants. The purchased number is immediately ready to receive calls and can be assigned to an assistant. ## How It Works 1. **Number Selection**: Use a phone number from the search results 2. **Provider Purchase**: The system purchases the number from the specified provider 3. **Automatic Configuration**: Webhooks are automatically configured for voice and SMS 4. **Database Registration**: The number is added to your organization's phone number inventory 5. **Assistant Assignment**: Optionally assign the number to an assistant immediately **Instant Activation**: Purchased numbers are immediately active and ready to receive calls and messages. No additional configuration required. ## Request Body The request body is a JSON object containing purchase details. * `phone_number` (string, **required**): The phone number to purchase in E.164 format * `provider` (string, **required**): Provider to use (`"twilio"` or `"telnyx"`) * `assistant_id` (integer, optional): ID of the assistant to assign this number to * `friendly_name` (string, optional): Custom display name for the number ```json Request theme={null} { "phone_number": "+14155551234", "provider": "telnyx", "assistant_id": 123, "friendly_name": "San Francisco Support Line" } ``` ## Response A successful request returns a `200 OK` status with purchase confirmation. ```json Response theme={null} { "success": true, "phone_number": "+14155551234", "provider": "telnyx", "purchase_details": { "id": "ORD_12345", "phone_number": "+14155551234", "status": "completed", "connection_id": "1234567890", "date_created": "2024-01-15T10:30:00Z" }, "message": "Successfully purchased phone number +14155551234 from telnyx" } ``` ## Response Fields * `success` (boolean): Whether the purchase completed successfully * `phone_number` (string): The purchased phone number * `provider` (string): Provider used for the purchase ("twilio" or "telnyx") * `purchase_details` (object): Provider-specific purchase information * **For Twilio**: * `sid` (string): Twilio phone number SID * `friendly_name` (string): Display name * `voice_url` (string): Configured voice webhook URL * `sms_url` (string): Configured SMS webhook URL * `capabilities` (object): Number capabilities * `date_created` (string): Purchase timestamp * **For Telnyx**: * `id` (string): Telnyx order ID * `status` (string): Order status ("completed", "pending", etc.) * `connection_id` (string): Associated connection ID * `date_created` (string): Purchase timestamp * `message` (string): Human-readable confirmation message ## Error Responses ### 400 Bad Request Returned when the request contains invalid data. ```json theme={null} { "detail": "Phone number must be in E.164 format (e.g., +1234567890)" } ``` Common causes: * Invalid `provider` value (must be "twilio" or "telnyx") * Invalid phone number format (must start with '+') * Invalid `assistant_id` (assistant doesn't exist) ### 404 Not Found Returned when the specified assistant doesn't exist. ```json theme={null} { "detail": "Assistant with ID 123 not found" } ``` ### 500 Internal Server Error Returned when the purchase fails at the provider level. ```json theme={null} { "detail": "Failed to purchase phone number from telnyx" } ``` Common causes: * Number no longer available * Insufficient account balance * Provider API error * Invalid provider credentials ## Purchase Workflow ### Complete Purchase Flow ```bash theme={null} # 1. Search for available numbers curl -X POST "https://api.burki.dev/api/v1/phone-numbers/search" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "area_code": "415", "provider": "telnyx" }' # 2. Purchase a number from the results curl -X POST "https://api.burki.dev/api/v1/phone-numbers/purchase" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "phone_number": "+14155551234", "provider": "telnyx", "assistant_id": 123 }' # 3. Number is immediately ready for calls! ``` ## Provider Differences ### Twilio Purchase * **Immediate Activation**: Number is instantly active * **Webhook Configuration**: Voice and SMS URLs automatically set * **Billing**: Charged to your Twilio account * **Features**: Full voice, SMS, MMS support ### Telnyx Purchase * **Order-Based**: Purchase creates an order that typically completes instantly * **Connection Assignment**: Number assigned to specified connection ID * **Billing**: Charged to your Telnyx account * **Features**: Voice, SMS, MMS support with competitive pricing ## Use Cases ### Scaling Customer Support Purchase numbers for different regions: ```json theme={null} { "phone_number": "+12125551234", "provider": "telnyx", "assistant_id": 101, "friendly_name": "NYC Customer Support" } ``` ### Multi-Brand Management Assign different numbers to different assistants: ```json theme={null} { "phone_number": "+18005551234", "provider": "twilio", "assistant_id": 202, "friendly_name": "Premium Brand Support" } ``` ### Geographic Expansion Establish local presence in new markets: ```json theme={null} { "phone_number": "+14155551234", "provider": "telnyx", "assistant_id": 303, "friendly_name": "West Coast Sales" } ``` ## Advanced Features ### Automatic Assistant Assignment When you provide an `assistant_id`, the system: 1. **Assigns Ownership**: Links the number to the specified assistant 2. **Configures Provider**: Uses the assistant's configured telephony provider 3. **Sets Webhooks**: Configures appropriate webhook URLs based on provider 4. **Enables Features**: Activates voice and SMS capabilities ### Webhook Configuration **Twilio Numbers**: Automatically configured with: * Voice URL: `https://yourdomain.com/twiml` * SMS URL: `https://yourdomain.com/twiml` **Telnyx Numbers**: Automatically configured with: * Webhook URL: `https://yourdomain.com/telnyx-webhook` * Connection ID from assistant or organization settings ## Integration Examples ### Node.js ```javascript theme={null} const axios = require('axios'); async function purchasePhoneNumber(phoneNumber, provider, assistantId) { try { const response = await axios.post('https://api.burki.dev/api/v1/phone-numbers/purchase', { phone_number: phoneNumber, provider: provider, assistant_id: assistantId, friendly_name: `${provider.charAt(0).toUpperCase() + provider.slice(1)} Number` }, { headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); console.log(`Successfully purchased ${phoneNumber} via ${provider}`); return response.data; } catch (error) { console.error('Purchase failed:', error.response.data); throw error; } } // Usage const result = await purchasePhoneNumber('+14155551234', 'telnyx', 123); ``` ### Python ```python theme={null} import requests def purchase_phone_number(phone_number, provider, assistant_id=None, friendly_name=None): url = "https://api.burki.dev/api/v1/phone-numbers/purchase" headers = { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" } payload = { "phone_number": phone_number, "provider": provider } if assistant_id: payload["assistant_id"] = assistant_id if friendly_name: payload["friendly_name"] = friendly_name response = requests.post(url, json=payload, headers=headers) if response.status_code == 200: return response.json() else: raise Exception(f"Purchase failed: {response.json()['detail']}") # Usage result = purchase_phone_number( phone_number="+14155551234", provider="telnyx", assistant_id=123, friendly_name="Support Line" ) ``` ### PHP ```php theme={null} $phoneNumber, 'provider' => $provider ]; if ($assistantId) { $data['assistant_id'] = $assistantId; } if ($friendlyName) { $data['friendly_name'] = $friendlyName; } $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode === 200) { return json_decode($response, true); } else { throw new Exception("Purchase failed: " . $response); } } // Usage $result = purchasePhoneNumber('+14155551234', 'telnyx', 123, 'Support Line'); ?> ``` ## Best Practices ### Pre-Purchase Validation 1. **Verify Availability**: Always search before purchasing 2. **Check Pricing**: Confirm costs, especially setup fees 3. **Assistant Readiness**: Ensure the target assistant is properly configured 4. **Provider Selection**: Choose the provider that best fits your needs ### Error Handling Implement robust error handling for common purchase failures: ```javascript theme={null} async function safePurchase(phoneNumber, provider, assistantId) { try { return await purchasePhoneNumber(phoneNumber, provider, assistantId); } catch (error) { if (error.response?.status === 500) { // Number may no longer be available, search for alternatives console.log('Number unavailable, searching for alternatives...'); return await findAndPurchaseAlternative(phoneNumber, provider); } throw error; } } ``` ### Cost Management 1. **Monitor Spending**: Track number purchases across providers 2. **Compare Costs**: Evaluate monthly costs vs. setup fees 3. **Bulk Purchases**: Consider purchasing multiple numbers for volume discounts 4. **Regular Audits**: Review and release unused numbers ### Security Considerations 1. **API Key Protection**: Secure your API keys 2. **Purchase Validation**: Validate purchases in your application 3. **Audit Logging**: Log all purchase activities 4. **Access Control**: Restrict purchase permissions to authorized users **Billing Impact**: Phone number purchases result in immediate charges to your telephony provider account. Ensure you have sufficient account balance and proper billing alerts configured. ## Troubleshooting ### Common Issues | Issue | Cause | Solution | | - | - | - | | "Number not available" | Number purchased by someone else | Search for new numbers | | "Insufficient balance" | Account balance too low | Add funds to provider account | | "Invalid credentials" | Wrong API keys | Check provider credentials | | "Assistant not found" | Invalid assistant ID | Verify assistant exists | ### Provider-Specific Issues **Twilio**: * Ensure account is verified for international numbers * Check geographic permissions for certain area codes **Telnyx**: * Verify connection ID is correct * Ensure account has number purchasing enabled ### Support If you encounter persistent issues: 1. Check provider account status 2. Verify API credentials 3. Contact provider support for account-specific issues 4. Reach out to Burki support for integration help # Release Phone Number Source: https://docs.burki.dev/api-reference/phone-numbers/release POST /api/v1/phone-numbers/release Release a phone number from Twilio or Telnyx account. This endpoint releases a phone number, removing it from your account and making it available for others to purchase. Args: release_data: Release details including phone number and provider Returns: ReleasePhoneNumberResponse: Release confirmation This endpoint allows you to release (delete) a phone number from your Twilio or Telnyx account, making it available for others to purchase. The number is immediately removed from your account and all associated configurations are cleaned up. ## How It Works 1. **Provider Detection**: The system automatically detects which provider owns the number 2. **Account Removal**: The number is released from your telephony provider account 3. **Database Cleanup**: The number is removed from your organization's inventory 4. **Immediate Effect**: The number stops receiving calls and messages instantly **Permanent Action**: Once released, a phone number cannot be recovered. The number becomes available for others to purchase and you cannot guarantee getting it back. ## Request Body The request body is a JSON object containing release details. * `phone_number` (string, **required**): The phone number to release in E.164 format * `provider` (string, optional): Provider to use (`"twilio"` or `"telnyx"`). Auto-detected if not provided ```json Request theme={null} { "phone_number": "+14155551234", "provider": "telnyx" } ``` ### Auto-Detection Example ```json Request theme={null} { "phone_number": "+14155551234" } ``` **Provider Auto-Detection**: If you don't specify a provider, the system will automatically detect which provider owns the number based on your database records. ## Response A successful request returns a `200 OK` status with release confirmation. ```json Response theme={null} { "success": true, "phone_number": "+14155551234", "provider": "telnyx", "message": "Successfully released phone number +14155551234 from telnyx" } ``` ## Response Fields * `success` (boolean): Whether the release completed successfully * `phone_number` (string): The released phone number * `provider` (string): Provider from which the number was released * `message` (string): Human-readable confirmation message ## Error Responses ### 400 Bad Request Returned when the request contains invalid data. ```json theme={null} { "detail": "Provider must be 'twilio' or 'telnyx'" } ``` Common causes: * Invalid `provider` value * Invalid phone number format * Number not in E.164 format ### 404 Not Found Returned when the phone number is not found. ```json theme={null} { "detail": "Phone number +14155551234 not found in database" } ``` This occurs when: * Number doesn't exist in your account * Number was never purchased through Burki * Number already released ### 500 Internal Server Error Returned when the release fails at the provider level. ```json theme={null} { "detail": "Failed to release phone number from telnyx" } ``` Common causes: * Provider API error * Network connectivity issues * Invalid provider credentials * Number has active calls/usage ## Release Workflow ### Standard Release Process ```bash theme={null} # 1. List your current numbers (optional) curl -X GET "https://api.burki.dev/api/v1/assistants/organization/phone-numbers" \ -H "Authorization: Bearer YOUR_API_KEY" # 2. Release a specific number curl -X POST "https://api.burki.dev/api/v1/phone-numbers/release" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "phone_number": "+14155551234" }' # 3. Number is immediately unavailable ``` ### Bulk Release Process ```bash theme={null} # Release multiple numbers for number in "+14155551234" "+14155551235" "+14155551236"; do curl -X POST "https://api.burki.dev/api/v1/phone-numbers/release" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d "{\"phone_number\": \"$number\"}" done ``` ## Use Cases ### Cost Optimization Release unused numbers to reduce monthly costs: ```json theme={null} { "phone_number": "+14155551234" } ``` ### Account Cleanup Remove test or temporary numbers: ```json theme={null} { "phone_number": "+18005551234", "provider": "twilio" } ``` ### Geographic Reorganization Release numbers when closing regional offices: ```json theme={null} { "phone_number": "+12125551234" } ``` ### Provider Migration Release numbers before switching providers: ```json theme={null} { "phone_number": "+14155551234", "provider": "twilio" } ``` ## Provider Behavior ### Twilio Release * **Immediate Deactivation**: Number stops working instantly * **Billing**: Pro-rated refund may apply (check Twilio terms) * **Reactivation**: Number returns to Twilio's available pool * **Dependencies**: All webhooks and configurations are removed ### Telnyx Release * **Immediate Deactivation**: Number stops working instantly * **Billing**: Check Telnyx terms for refund policies * **Reactivation**: Number returns to available inventory * **Order Status**: Associated orders marked as released ## Safety Considerations ### Pre-Release Checklist Before releasing a number, ensure: 1. **No Active Calls**: Number isn't currently handling calls 2. **No Dependencies**: No critical integrations depend on this number 3. **Customer Communication**: Customers are notified of number changes 4. **Documentation Updated**: Internal docs reflect the change 5. **Backup Plan**: Alternative contact methods are available ### Impact Assessment Releasing a number affects: * **Incoming Calls**: All incoming calls will fail * **SMS Messages**: SMS/MMS will not be delivered * **Assistant Assignment**: Associated assistant becomes unreachable * **Customer Experience**: Customers lose ability to contact you ## Integration Examples ### Node.js ```javascript theme={null} const axios = require('axios'); async function releasePhoneNumber(phoneNumber, provider = null) { try { const requestData = { phone_number: phoneNumber }; if (provider) { requestData.provider = provider; } const response = await axios.post('https://api.burki.dev/api/v1/phone-numbers/release', requestData, { headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); console.log(`Successfully released ${phoneNumber} from ${response.data.provider}`); return response.data; } catch (error) { console.error('Release failed:', error.response.data); throw error; } } // Usage with auto-detection await releasePhoneNumber('+14155551234'); // Usage with explicit provider await releasePhoneNumber('+14155551234', 'telnyx'); ``` ### Python ```python theme={null} import requests def release_phone_number(phone_number, provider=None): url = "https://api.burki.dev/api/v1/phone-numbers/release" headers = { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" } payload = {"phone_number": phone_number} if provider: payload["provider"] = provider response = requests.post(url, json=payload, headers=headers) if response.status_code == 200: return response.json() else: raise Exception(f"Release failed: {response.json()['detail']}") # Usage examples result = release_phone_number("+14155551234") # Auto-detect provider result = release_phone_number("+14155551234", "telnyx") # Explicit provider ``` ### PHP ```php theme={null} $phoneNumber]; if ($provider) { $data['provider'] = $provider; } $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode === 200) { return json_decode($response, true); } else { throw new Exception("Release failed: " . $response); } } // Usage $result = releasePhoneNumber('+14155551234'); ?> ``` ## Batch Operations ### Release Multiple Numbers ```javascript theme={null} async function releaseMultipleNumbers(phoneNumbers) { const results = []; for (const phoneNumber of phoneNumbers) { try { const result = await releasePhoneNumber(phoneNumber); results.push({ phoneNumber, success: true, result }); } catch (error) { results.push({ phoneNumber, success: false, error: error.message }); } } return results; } // Usage const numbersToRelease = ['+14155551234', '+14155551235', '+14155551236']; const results = await releaseMultipleNumbers(numbersToRelease); ``` ### Conditional Release ```python theme={null} def release_unused_numbers(unused_threshold_days=30): """Release numbers that haven't been used for calls in X days""" # Get list of numbers and their last usage numbers = get_phone_number_usage() for number_info in numbers: if number_info['days_since_last_call'] > unused_threshold_days: try: result = release_phone_number(number_info['phone_number']) print(f"Released unused number: {number_info['phone_number']}") except Exception as e: print(f"Failed to release {number_info['phone_number']}: {e}") ``` ## Best Practices ### Planning and Preparation 1. **Audit First**: Review which numbers are actually needed 2. **Usage Analysis**: Check call/SMS volume before releasing 3. **Customer Impact**: Assess impact on customer communications 4. **Gradual Release**: Release numbers in phases, not all at once ### Error Handling ```javascript theme={null} async function safeRelease(phoneNumber) { try { // Check if number has recent activity const usage = await checkNumberUsage(phoneNumber); if (usage.recentCalls > 0) { throw new Error('Number has recent activity, release cancelled'); } return await releasePhoneNumber(phoneNumber); } catch (error) { console.error(`Failed to release ${phoneNumber}:`, error.message); // Log to monitoring system // Send alert to admin throw error; } } ``` ### Cost Management 1. **Regular Audits**: Monthly review of number usage 2. **Automated Cleanup**: Script to release unused numbers 3. **Approval Process**: Require approval for number releases 4. **Recovery Planning**: Consider number porting instead of release ### Documentation Always document releases: * Which numbers were released * Reason for release * Date and time * Impact assessment * Rollback plan (if applicable) ## Troubleshooting ### Common Issues | Issue | Cause | Solution | | - | - | - | | "Number not found" | Number not in account | Verify number ownership | | "Release failed" | Provider API error | Check provider status | | "Active usage" | Number currently in use | Wait for calls to complete | | "Permission denied" | Insufficient privileges | Check API key permissions | ### Recovery Options If you accidentally release a critical number: 1. **Immediate Search**: Search for the number in available pools 2. **Quick Purchase**: Purchase it back if still available 3. **Alternative Numbers**: Find similar numbers in the same area 4. **Customer Communication**: Notify customers of temporary number changes ### Monitoring Set up monitoring for: * Number release events * Failed release attempts * Usage patterns before release * Customer impact metrics **Best Practice**: Always test the release process with non-critical numbers first to understand the full impact and timing. # Search Phone Numbers Source: https://docs.burki.dev/api-reference/phone-numbers/search POST /api/v1/phone-numbers/search Search available Twilio or Telnyx phone numbers by country, area code, locality, region, pattern, provider, and result limit. This endpoint allows you to search for available phone numbers for purchase from Twilio or Telnyx. You can filter results by country, area code, location, or number patterns to find the perfect number for your assistant. ## How It Works 1. **Provider Selection**: Choose between Twilio or Telnyx as your search provider 2. **Filter Application**: Apply location, pattern, or feature-based filters 3. **Results with Pricing**: Get available numbers with cost information and capabilities 4. **Direct Purchase**: Use the returned phone numbers in the purchase endpoint **Multi-Provider Comparison**: Search both Twilio and Telnyx separately to compare pricing and availability across providers for the same region. ## Request Body The request body is a JSON object containing search criteria. * `country_code` (string, optional): Country code to search in. Defaults to `"US"` * `area_code` (string, optional): Specific area code to search within * `contains` (string, optional): Pattern or digits the number should contain * `locality` (string, optional): City or locality name to search in * `region` (string, optional): State or region to search within * `limit` (integer, optional): Maximum number of results (1-50). Defaults to `10` * `provider` (string, optional): Provider to search (`"twilio"` or `"telnyx"`). Defaults to `"telnyx"` ```json Request theme={null} { "country_code": "US", "area_code": "415", "locality": "San Francisco", "region": "CA", "limit": 20, "provider": "telnyx" } ``` ## Response A successful request returns a `200 OK` status with available phone numbers. ```json Response theme={null} { "success": true, "numbers": [ { "phone_number": "+14155551234", "friendly_name": "+14155551234", "locality": "San Francisco", "region": "CA", "country_code": "US", "capabilities": { "voice": true, "sms": true, "mms": true, "fax": false }, "cost": "$1.00", "setup_cost": "$0.00", "provider": "telnyx" } ], "total_found": 15, "provider": "telnyx" } ``` ## Response Fields * `success` (boolean): Whether the search completed successfully * `numbers` (array): List of available phone numbers with details * `phone_number` (string): The phone number in E.164 format * `friendly_name` (string): Display name for the number * `locality` (string): City/locality where the number is located * `region` (string): State/region where the number is located * `country_code` (string): Country code * `capabilities` (object): Features supported by this number * `voice` (boolean): Supports voice calls * `sms` (boolean): Supports SMS messages * `mms` (boolean): Supports MMS messages * `fax` (boolean): Supports fax transmissions * `cost` (string): Monthly cost for the number (Telnyx only) * `setup_cost` (string): One-time setup fee (Telnyx only) * `provider` (string): Provider name ("twilio" or "telnyx") * `total_found` (integer): Total number of results found * `provider` (string): Provider used for the search ## Error Responses ### 400 Bad Request Returned when the request contains invalid parameters. ```json theme={null} { "detail": "Provider must be 'twilio' or 'telnyx'" } ``` Common causes: * Invalid `provider` value * `limit` outside of 1-50 range * Invalid country code format ### 500 Internal Server Error Returned when the provider API fails or is unavailable. ```json theme={null} { "detail": "Internal error: Provider API unavailable" } ``` ## Search Examples ### Basic Area Code Search Search for numbers in the 415 area code: ```json theme={null} { "area_code": "415", "provider": "telnyx", "limit": 10 } ``` ### Location-Based Search Find numbers in a specific city: ```json theme={null} { "locality": "New York", "region": "NY", "country_code": "US", "provider": "twilio" } ``` ### Pattern-Based Search Search for numbers containing specific digits: ```json theme={null} { "contains": "777", "area_code": "212", "provider": "telnyx" } ``` ### International Search Search for numbers in other countries: ```json theme={null} { "country_code": "GB", "locality": "London", "provider": "telnyx", "limit": 5 } ``` ## Provider Comparison ### Twilio vs Telnyx | Feature | Twilio | Telnyx | | - | - | - | | **Search Filters** | Area code, locality, region, contains | Area code, locality, region, contains | | **Pricing Info** | Not included in search | Monthly + setup costs shown | | **Global Coverage** | Provider-dependent and changes by product | Provider-dependent and changes by product | | **API Speed** | Fast | Very fast | | **Features** | Voice, SMS, MMS, Fax | Voice, SMS, MMS | ### Cost Optimization Tips 1. **Compare Providers**: Search both providers for the same criteria 2. **Check Setup Fees**: Telnyx shows setup costs upfront 3. **Consider Features**: Ensure the number supports required capabilities 4. **Location Matters**: Local numbers may have better rates ```bash theme={null} # Compare pricing between providers curl -X POST "https://api.burki.dev/api/v1/phone-numbers/search" \ -H "Content-Type: application/json" \ -d '{"area_code": "415", "provider": "telnyx"}' curl -X POST "https://api.burki.dev/api/v1/phone-numbers/search" \ -H "Content-Type: application/json" \ -d '{"area_code": "415", "provider": "twilio"}' ``` ## Use Cases ### Sales Team Numbers Search for memorable numbers for sales teams: ```json theme={null} { "contains": "SALE", "area_code": "800", "provider": "telnyx" } ``` ### Local Presence Establish local presence in multiple markets: ```json theme={null} { "locality": "Chicago", "region": "IL", "limit": 5, "provider": "twilio" } ``` ### Toll-Free Numbers Search for toll-free numbers: ```json theme={null} { "area_code": "800", "country_code": "US", "provider": "telnyx" } ``` ## Integration Examples ### Node.js ```javascript theme={null} const axios = require('axios'); async function searchPhoneNumbers(criteria) { try { const response = await axios.post('https://api.burki.dev/api/v1/phone-numbers/search', { ...criteria, provider: 'telnyx' }, { headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); return response.data.numbers; } catch (error) { console.error('Search failed:', error.response.data); throw error; } } // Usage const numbers = await searchPhoneNumbers({ area_code: '415', locality: 'San Francisco', limit: 10 }); ``` ### Python ```python theme={null} import requests def search_phone_numbers(criteria, provider='telnyx'): url = "https://api.burki.dev/api/v1/phone-numbers/search" headers = { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" } payload = {**criteria, "provider": provider} response = requests.post(url, json=payload, headers=headers) if response.status_code == 200: return response.json()['numbers'] else: raise Exception(f"Search failed: {response.json()['detail']}") # Usage numbers = search_phone_numbers({ 'area_code': '415', 'locality': 'San Francisco', 'limit': 10 }) ``` ### PHP ```php theme={null} $provider]); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode === 200) { return json_decode($response, true)['numbers']; } else { throw new Exception("Search failed: " . $response); } } // Usage $numbers = searchPhoneNumbers([ 'area_code' => '415', 'locality' => 'San Francisco', 'limit' => 10 ]); ?> ``` ## Best Practices ### Search Strategy 1. **Start Broad**: Begin with area code only, then add filters 2. **Multiple Searches**: Try different combinations to find the best options 3. **Provider Comparison**: Always compare both Twilio and Telnyx 4. **Capability Check**: Verify the number supports your required features ### Performance Tips 1. **Reasonable Limits**: Use appropriate limit values (10-20 for UI, 5 for automation) 2. **Cache Results**: Cache search results for a few minutes to avoid repeated calls 3. **Parallel Searches**: Search multiple providers simultaneously for comparison ### Error Handling Always implement proper error handling for provider outages: ```javascript theme={null} async function robustSearch(criteria) { const providers = ['telnyx', 'twilio']; for (const provider of providers) { try { return await searchPhoneNumbers({...criteria, provider}); } catch (error) { console.warn(`${provider} search failed, trying next provider`); } } throw new Error('All providers failed'); } ``` **Rate Limits**: Search requests are subject to rate limits. For high-volume applications, implement appropriate throttling and retry logic. # Update Phone Number Webhooks Source: https://docs.burki.dev/api-reference/phone-numbers/update-webhooks PUT /api/v1/phone-numbers/webhooks Update voice webhook URL and/or disable SMS webhooks for a phone number. This endpoint allows you to update the voice webhook URL and optionally disable SMS webhooks. SMS webhook URLs are managed automatically through assistant assignment. Args: webhook_data: Webhook update details including phone number and voice URL Returns: UpdateWebhookResponse: Update confirmation with details ## Overview Update voice webhook URLs and optionally disable SMS webhooks for a phone number. **Important**: SMS webhook URLs are managed automatically through assistant assignment. This endpoint only allows you to disable SMS webhooks when needed. ## Request Body * `phone_number` (string, **required**): The phone number to update in E.164 format (e.g., `+1234567890`) * `voice_webhook_url` (string, optional): URL for voice call webhooks * `disable_sms` (boolean, optional): Set to `true` to disable SMS webhooks. Default: `false` * `enable_sms` (boolean, optional): Set to `true` to enable SMS webhooks based on assigned assistant. Default: `false` * `provider` (string, optional): Provider (`twilio` or `telnyx`). Auto-detected if not provided **Note**: You cannot set both `disable_sms` and `enable_sms` to `true` in the same request. ## SMS Webhook Management SMS webhooks are automatically configured when you assign a phone number to an assistant with an `sms_webhook_url`. Use this endpoint to temporarily disable SMS handling: ### Disabling SMS Webhooks ```json Request - Disable SMS theme={null} { "phone_number": "+15551234567", "disable_sms": true } ``` **What happens when SMS is disabled:** * **Twilio**: Sets SMS webhook to `https://demo.twilio.com/welcome/sms/reply` * **Telnyx**: Sets SMS webhook to empty URL * **Result**: Incoming SMS messages are ignored ### Re-enabling SMS Webhooks ```json Request - Enable SMS theme={null} { "phone_number": "+15551234567", "enable_sms": true } ``` **What happens when SMS is enabled:** * **Prerequisites**: Phone number must be assigned to an assistant with `sms_webhook_url` configured * **Twilio**: Sets SMS webhook to `/twilio-sms-webhook` and enables messaging service * **Telnyx**: Sets SMS webhook to `/telnyx-webhook` * **Result**: SMS messages are forwarded to assistant's `sms_webhook_url` ### Updating Voice Webhooks ```json Request - Update Voice Webhook theme={null} { "phone_number": "+15551234567", "voice_webhook_url": "https://your-app.com/voice-webhook" } ``` ### Combined Updates ```json Request - Update Voice + Disable SMS theme={null} { "phone_number": "+15551234567", "voice_webhook_url": "https://your-app.com/voice-webhook", "disable_sms": true } ``` ```json Request - Update Voice + Enable SMS theme={null} { "phone_number": "+15551234567", "voice_webhook_url": "https://your-app.com/voice-webhook", "enable_sms": true } ``` ## Response ```json Response theme={null} { "success": true, "phone_number": "+15551234567", "provider": "twilio", "updated_webhooks": { "voice": "https://your-app.com/voice-webhook", "sms": "disabled (demo URL)" }, "message": "Successfully updated 2 webhook(s) for +15551234567 via twilio" } ``` ## Use Cases 1. **Voice webhook updates** - Change where voice call events are sent 2. **Temporary SMS disable** - Disable SMS during maintenance or testing 3. **Emergency SMS disable** - Quickly stop SMS processing if needed 4. **Re-enable SMS after maintenance** - Restore SMS functionality without reassigning phone numbers ## Notes * At least one of `voice_webhook_url`, `disable_sms`, or `enable_sms` must be provided * Cannot set both `disable_sms` and `enable_sms` to `true` in the same request * To enable SMS, the phone number must be assigned to an assistant with `sms_webhook_url` configured * Voice webhooks can be updated independently of SMS webhook state # SIP Trunks API Source: https://docs.burki.dev/api-reference/sip-trunks/overview Create, update, prioritize, enable, disable, and sync BYO SIP trunks. The SIP Trunks API lets organization admins manage BYO SIP trunk gateways for outbound and inbound telephony through Burki's SIP bridge. All SIP trunk management endpoints require an authenticated organization admin. ```text theme={null} Base path: /api/v1/sip-trunks ``` SIP trunk records include carrier credentials. Treat responses and request bodies as sensitive and avoid logging full payloads. ## SIP Trunk Object ```json theme={null} { "id": "7f502da1-0f06-4e52-9262-4e625c53791a", "name": "Primary Carrier", "gateway": "sip.example-carrier.com", "username": "sip-user", "password": "sip-password", "priority": 1, "enabled": true, "register": true, "provider": "example-carrier" } ``` | Field | Type | Description | | - | - | - | | `id` | string | Server-generated trunk UUID | | `name` | string | Unique trunk name within the organization | | `gateway` | string | SIP gateway hostname or IP | | `username` | string | SIP auth username | | `password` | string | SIP auth password | | `priority` | integer | Lower values are preferred first | | `enabled` | boolean | Whether the trunk is eligible for routing | | `register` | boolean | Whether FreeSWITCH should register to the gateway | | `provider` | string | Optional provider label | ## PBX Allowlist and Registration For the current hosted SIP bridge, customer PBX/carrier firewalls should allow: ```text theme={null} Burki SIP bridge IP: 52.21.157.171 SIP signaling: UDP/TCP 5060 SIP TLS: TCP 5061, if enabled RTP media: UDP 10000-20000 ``` Use `gateway` for the customer's PBX/carrier host and port. If Burki should register to the PBX, set `register: true` and provide the PBX username/password. If the PBX uses IP authentication instead, set `register: false` and allowlist `52.21.157.171` as the trusted SIP peer. ```json Registration example theme={null} { "name": "FreePBX primary", "gateway": "138.197.131.209:6061", "username": "9228", "password": "sip-password", "priority": 1, "enabled": true, "register": true, "provider": "freepbx" } ``` For inbound calls, configure the PBX/carrier to send INVITEs to `52.21.157.171:5060` and allow RTP from `52.21.157.171` on UDP `10000-20000`. For outbound calls, the PBX/carrier must accept INVITEs and RTP from `52.21.157.171`. If multiple DIDs share one PBX extension, the PBX must also include the original called DID in `Diversion`, `History-Info`, `X-Original-Called`, `P-Called-Party-ID`, the Request-URI, or the `To` user so Burki can route to the correct assistant. ## List Trunks ```http theme={null} GET /api/v1/sip-trunks Authorization: Bearer YOUR_TOKEN ``` Returns trunks sorted by `priority`. ```json Response theme={null} [ { "id": "7f502da1-0f06-4e52-9262-4e625c53791a", "name": "Primary Carrier", "gateway": "sip.example-carrier.com", "username": "sip-user", "password": "sip-password", "priority": 1, "enabled": true, "register": true, "provider": "example-carrier" } ] ``` ## Create Trunk ```http theme={null} POST /api/v1/sip-trunks Authorization: Bearer YOUR_TOKEN Content-Type: application/json ``` ```json Request theme={null} { "name": "Primary Carrier", "gateway": "sip.example-carrier.com", "username": "sip-user", "password": "sip-password", "priority": 1, "enabled": true, "register": true, "provider": "example-carrier" } ``` Returns `201 Created` with the created SIP trunk object. Duplicate names return `400 Bad Request`. ## Get Trunk ```http theme={null} GET /api/v1/sip-trunks/{trunk_id} Authorization: Bearer YOUR_TOKEN ``` Returns one SIP trunk object or `404 Not Found`. ## Update Trunk ```http theme={null} PUT /api/v1/sip-trunks/{trunk_id} Authorization: Bearer YOUR_TOKEN Content-Type: application/json ``` All fields are optional. Empty fields are ignored only if omitted; send the exact value you want persisted. ```json Request theme={null} { "name": "Primary Carrier", "gateway": "sip2.example-carrier.com", "priority": 2, "enabled": true } ``` Returns the updated trunk. ## Delete Trunk ```http theme={null} DELETE /api/v1/sip-trunks/{trunk_id} Authorization: Bearer YOUR_TOKEN ``` Returns `204 No Content` on success. ## Enable or Disable Trunk ```http theme={null} POST /api/v1/sip-trunks/{trunk_id}/enable Authorization: Bearer YOUR_TOKEN ``` ```http theme={null} POST /api/v1/sip-trunks/{trunk_id}/disable Authorization: Bearer YOUR_TOKEN ``` Returns the updated trunk object. ## Sync to SIP Bridge ```http theme={null} POST /api/v1/sip-trunks/sync Authorization: Bearer YOUR_TOKEN ``` Pushes enabled trunk configuration to the SIP bridge. The API returns success if Burki updates its org config and starts bridge sync; bridge sync failures are logged server-side and should be checked in operations logs. ```json Response theme={null} { "success": true, "message": "Synced 2 enabled trunk(s) to bridge", "total_trunks": 3, "enabled_trunks": 2 } ``` ## Assign a Phone Number to a SIP Trunk Phone-number routing uses the organization phone-number update endpoint: ```http theme={null} PATCH /api/v1/assistants/organization/phone-numbers/{phone_number_id} Authorization: Bearer YOUR_TOKEN Content-Type: application/json ``` ```json Request theme={null} { "sip_trunk_id": "7f502da1-0f06-4e52-9262-4e625c53791a" } ``` Send an empty string to clear the SIP trunk assignment: ```json theme={null} { "sip_trunk_id": "" } ``` ## Error Responses | Status | Cause | | - | - | | `400` | Duplicate trunk name or invalid update | | `403` | Authenticated user is not an organization admin | | `404` | Organization or trunk not found | | `500` | Unexpected server error | # Assign Tool to Assistant Source: https://docs.burki.dev/api-reference/tools/assign POST /api/v1/tools/{tool_id}/assign This endpoint assigns a tool to one or more assistants, making it available during their conversations. Tools can be assigned to multiple assistants simultaneously. ### Path Parameters * `tool_id` (integer, **required**): The unique identifier of the tool to assign ### Request Body ```json Request theme={null} { "assistant_ids": [123, 456, 789], "enabled": true } ``` ### Request Fields * `assistant_ids` (array of integers, **required**): List of assistant IDs to assign the tool to * `enabled` (boolean, optional): Whether the tool is enabled for these assistants (default: `true`) ### Response A successful request returns assignment details: ```json Response theme={null} { "success": true, "tool_id": 123, "assignments": [ { "assistant_id": 123, "assistant_name": "Customer Service Bot", "enabled": true, "assigned_at": "2024-01-15T16:30:00Z" }, { "assistant_id": 456, "assistant_name": "Sales Assistant", "enabled": true, "assigned_at": "2024-01-15T16:30:00Z" }, { "assistant_id": 789, "assistant_name": "Technical Support", "enabled": true, "assigned_at": "2024-01-15T16:30:00Z" } ], "message": "Tool successfully assigned to 3 assistants" } ``` ### Single Assistant Assignment You can also assign a tool to a single assistant: ```json Single Assignment Request theme={null} { "assistant_ids": [123], "enabled": true } ``` ```json Single Assignment Response theme={null} { "success": true, "tool_id": 123, "assignments": [ { "assistant_id": 123, "assistant_name": "Customer Service Bot", "enabled": true, "assigned_at": "2024-01-15T16:30:00Z" } ], "message": "Tool successfully assigned to 1 assistant" } ``` ### Error Responses **404 Not Found - Tool** ```json theme={null} { "detail": "Tool with ID 123 not found" } ``` **404 Not Found - Assistant** ```json theme={null} { "detail": "Assistant with ID 456 not found in your organization" } ``` **400 Bad Request - Already Assigned** ```json theme={null} { "detail": "Tool is already assigned to assistant 123" } ``` **400 Bad Request - Empty Assignment** ```json theme={null} { "detail": "assistant_ids cannot be empty" } ``` ### Unassign Tool To unassign a tool from assistants, use the unassign endpoint: ```bash Unassign Tool theme={null} POST /api/v1/tools/{tool_id}/unassign ``` ```json Unassign Request theme={null} { "assistant_ids": [123, 456] } ``` ### List Tool Assignments Get all current assignments for a tool: ```bash List Assignments theme={null} GET /api/v1/tools/{tool_id}/assignments ``` ```json Assignments Response theme={null} { "tool_id": 123, "tool_name": "get_customer_info", "assignments": [ { "assistant_id": 123, "assistant_name": "Customer Service Bot", "enabled": true, "assigned_at": "2024-01-15T16:30:00Z", "usage_count": 45, "last_used_at": "2024-01-15T18:15:00Z" }, { "assistant_id": 456, "assistant_name": "Sales Assistant", "enabled": false, "assigned_at": "2024-01-15T16:30:00Z", "usage_count": 12, "last_used_at": "2024-01-15T17:20:00Z" } ], "total_assignments": 2 } ``` ### Integration Examples ```javascript theme={null} const axios = require('axios'); async function assignToolToAssistants(toolId, assistantIds, enabled = true) { try { const response = await axios.post(`https://api.burki.dev/api/v1/tools/${toolId}/assign`, { assistant_ids: assistantIds, enabled: enabled }, { headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); return response.data; } catch (error) { console.error('Failed to assign tool:', error.response.data); throw error; } } async function unassignToolFromAssistants(toolId, assistantIds) { try { const response = await axios.post(`https://api.burki.dev/api/v1/tools/${toolId}/unassign`, { assistant_ids: assistantIds }, { headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); return response.data; } catch (error) { console.error('Failed to unassign tool:', error.response.data); throw error; } } async function getToolAssignments(toolId) { try { const response = await axios.get(`https://api.burki.dev/api/v1/tools/${toolId}/assignments`, { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }); return response.data; } catch (error) { console.error('Failed to get assignments:', error.response.data); throw error; } } // Usage examples const result = await assignToolToAssistants(123, [456, 789]); const assignments = await getToolAssignments(123); await unassignToolFromAssistants(123, [456]); ``` ```python theme={null} import requests from typing import List, Dict, Any def assign_tool_to_assistants( tool_id: int, assistant_ids: List[int], enabled: bool = True ) -> Dict[str, Any]: """Assign a tool to multiple assistants.""" url = f"https://api.burki.dev/api/v1/tools/{tool_id}/assign" headers = { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" } payload = { "assistant_ids": assistant_ids, "enabled": enabled } response = requests.post(url, json=payload, headers=headers) if response.status_code == 200: return response.json() else: raise Exception(f"Failed to assign tool: {response.json()['detail']}") def unassign_tool_from_assistants( tool_id: int, assistant_ids: List[int] ) -> Dict[str, Any]: """Unassign a tool from multiple assistants.""" url = f"https://api.burki.dev/api/v1/tools/{tool_id}/unassign" headers = { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" } payload = {"assistant_ids": assistant_ids} response = requests.post(url, json=payload, headers=headers) if response.status_code == 200: return response.json() else: raise Exception(f"Failed to unassign tool: {response.json()['detail']}") def get_tool_assignments(tool_id: int) -> Dict[str, Any]: """Get all assignments for a tool.""" url = f"https://api.burki.dev/api/v1/tools/{tool_id}/assignments" headers = {"Authorization": "Bearer YOUR_API_KEY"} response = requests.get(url, headers=headers) if response.status_code == 200: return response.json() else: raise Exception(f"Failed to get assignments: {response.json()['detail']}") # Usage examples result = assign_tool_to_assistants(123, [456, 789]) assignments = get_tool_assignments(123) unassign_result = unassign_tool_from_assistants(123, [456]) ``` ```php theme={null} $assistantIds, 'enabled' => $enabled ]; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode === 200) { return json_decode($response, true); } else { throw new Exception("Failed to assign tool: " . $response); } } function getToolAssignments($toolId) { $url = "https://api.burki.dev/api/v1/tools/{$toolId}/assignments"; $headers = ['Authorization: Bearer YOUR_API_KEY']; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode === 200) { return json_decode($response, true); } else { throw new Exception("Failed to get assignments: " . $response); } } // Usage $result = assignToolToAssistants(123, [456, 789]); $assignments = getToolAssignments(123); ?> ``` ### Best Practices 1. **Selective Assignment**: Only assign tools to assistants that actually need them 2. **Testing**: Test tool functionality with specific assistants before broad deployment 3. **Monitoring**: Track tool usage per assistant to optimize assignments 4. **Documentation**: Document which tools are assigned to which assistants 5. **Performance**: Consider the number of tools per assistant for optimal performance ### Use Cases **Scenario**: Different tools for different departments ```javascript theme={null} // Assign CRM lookup to sales assistants await assignToolToAssistants( crmToolId, [salesBot1, salesBot2, salesBot3] ); // Assign technical diagnostics to support assistants await assignToolToAssistants( diagToolId, [supportBot1, supportBot2] ); ``` **Scenario**: Rolling out new tools gradually ```javascript theme={null} // Phase 1: Assign to test assistant await assignToolToAssistants(newToolId, [testAssistant]); // Phase 2: Assign to pilot group await assignToolToAssistants(newToolId, [pilot1, pilot2]); // Phase 3: Full rollout await assignToolToAssistants(newToolId, allAssistantIds); ``` **Scenario**: Temporary tool access for specific periods ```javascript theme={null} // Assign tool for promotion period await assignToolToAssistants(promoToolId, marketingBots, true); // Later: Disable tool after promotion await assignToolToAssistants(promoToolId, marketingBots, false); ``` # Create Tool Source: https://docs.burki.dev/api-reference/tools/create POST /api/v1/tools Create a custom Burki tool backed by an HTTP endpoint, Python function, or AWS Lambda function for assistants to call during conversations.

Create a Callable Tool

Tools let assistants take action during a conversation. Use this endpoint to register HTTP endpoints, Python functions, or AWS Lambda functions that the AI can call when it needs external data or business logic.

  • Expose customer lookup, scheduling, payment, quoting, or ticketing workflows.
  • Define a JSON schema so the assistant knows which arguments to collect.
  • Keep sensitive credentials out of prompts and route them through tool configuration instead.

How this endpoint behaves

You register a tool once per organization, then attach it to one or more assistants. Supported tool types are HTTP endpoints, Python functions, and AWS Lambda functions.

Each tool carries a name, description, and configuration block. The assistant uses that metadata when deciding whether to invoke the tool during a conversation.

### Request Body The request body structure varies based on the tool type you're creating: Create a tool that makes HTTP requests to external APIs. ```json Example Request theme={null} { "name": "get_customer_info", "display_name": "Customer Information Lookup", "description": "Look up customer information by phone number or email", "tool_type": "endpoint", "configuration": { "url": "https://api.yourcompany.com/customers/lookup", "method": "GET", "headers": { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" }, "timeout_seconds": 30 }, "function_definition": { "name": "get_customer_info", "description": "Look up customer information by phone number or email", "parameters": { "type": "object", "properties": { "phone_number": { "type": "string", "description": "Customer's phone number in E.164 format" }, "email": { "type": "string", "description": "Customer's email address" } }, "required": ["phone_number"] } }, "is_active": true, "is_public": false } ``` Create a tool that executes custom Python code. ```json Example Request theme={null} { "name": "calculate_loan_payment", "display_name": "Loan Payment Calculator", "description": "Calculate monthly loan payment based on principal, rate, and term", "tool_type": "python_function", "configuration": { "python_code": "def calculate_monthly_payment(principal, annual_rate, years):\n monthly_rate = annual_rate / 100 / 12\n num_payments = years * 12\n \n if monthly_rate == 0:\n return principal / num_payments\n \n payment = principal * (monthly_rate * (1 + monthly_rate) ** num_payments) / ((1 + monthly_rate) ** num_payments - 1)\n \n return {\n 'monthly_payment': round(payment, 2),\n 'total_paid': round(payment * num_payments, 2),\n 'total_interest': round((payment * num_payments) - principal, 2)\n }", "timeout_seconds": 10 }, "function_definition": { "name": "calculate_loan_payment", "description": "Calculate monthly loan payment and totals", "parameters": { "type": "object", "properties": { "principal": { "type": "number", "description": "Loan principal amount in dollars" }, "annual_rate": { "type": "number", "description": "Annual interest rate as percentage (e.g., 5.5 for 5.5%)" }, "years": { "type": "integer", "description": "Loan term in years" } }, "required": ["principal", "annual_rate", "years"] } }, "is_active": true } ``` Create a tool that invokes AWS Lambda functions. ```json Example Request theme={null} { "name": "customer_lookup_lambda", "display_name": "Customer Lookup (Lambda)", "description": "Look up customer data using AWS Lambda function", "tool_type": "lambda", "configuration": { "function_name": "customer-lookup-function", "aws_region": "us-east-1", "invocation_type": "RequestResponse", "timeout_seconds": 30 }, "function_definition": { "name": "customer_lookup", "description": "Look up customer information from database", "parameters": { "type": "object", "properties": { "customer_id": { "type": "string", "description": "Unique customer identifier" }, "phone_number": { "type": "string", "description": "Customer phone number" } }, "required": ["customer_id"] } }, "is_active": true } ``` ### Response A successful request returns the created tool object: ```json Response theme={null} { "id": 123, "name": "get_customer_info", "display_name": "Customer Information Lookup", "description": "Look up customer information by phone number or email", "tool_type": "endpoint", "configuration": { "url": "https://api.yourcompany.com/customers/lookup", "method": "GET", "headers": { "Authorization": "Bearer [REDACTED]", "Content-Type": "application/json" }, "timeout_seconds": 30 }, "function_definition": { "name": "get_customer_info", "description": "Look up customer information by phone number or email", "parameters": { "type": "object", "properties": { "phone_number": { "type": "string", "description": "Customer's phone number in E.164 format" }, "email": { "type": "string", "description": "Customer's email address" } }, "required": ["phone_number"] } }, "is_active": true, "is_public": false, "execution_count": 0, "success_rate": 0.0, "avg_execution_time": 0.0, "organization_id": 456, "created_by": 789, "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-15T10:30:00Z" } ``` ### Field Descriptions * `name` (string, **required**): Unique identifier for the tool within your organization * `display_name` (string, **required**): Human-readable name shown in the UI * `description` (string, **required**): Clear description of what the tool does * `tool_type` (string, **required**): Type of tool (`"endpoint"`, `"python_function"`, or `"lambda"`) * `configuration` (object, **required**): Tool-specific configuration (varies by type) * `function_definition` (object, **required**): OpenAI function calling schema * `is_active` (boolean, optional): Whether the tool is active (default: `true`) * `is_public` (boolean, optional): Whether other organizations can see this tool (default: `false`) * `timeout_seconds` (integer, optional): Maximum execution time (default: 30) * `retry_attempts` (integer, optional): Number of retry attempts on failure (default: 1) ### Error Responses **400 Bad Request** ```json theme={null} { "detail": "Tool name 'get_customer_info' already exists in organization" } ``` **422 Validation Error** ```json theme={null} { "detail": [ { "loc": ["body", "tool_type"], "msg": "value is not a valid enumeration member; permitted: 'endpoint', 'python_function', 'lambda'", "type": "type_error.enum" } ] } ``` ### Configuration Examples ```json theme={null} { "configuration": { "url": "https://api.example.com/data", "method": "POST", "headers": { "Authorization": "Bearer ${API_KEY}", "Content-Type": "application/json", "User-Agent": "Burki/1.0" }, "timeout_seconds": 45, "follow_redirects": true, "verify_ssl": true, "request_template": { "query": "SELECT * FROM customers WHERE phone = '${phone_number}'", "format": "json" } } } ``` ```json theme={null} { "configuration": { "python_code": "import requests\nimport pandas as pd\nfrom datetime import datetime\n\ndef analyze_customer_data(customer_id):\n # Fetch data from API\n response = requests.get(f'https://api.internal.com/customers/{customer_id}')\n data = response.json()\n \n # Process with pandas\n df = pd.DataFrame(data['transactions'])\n \n return {\n 'total_spent': df['amount'].sum(),\n 'transaction_count': len(df),\n 'last_purchase': df['date'].max(),\n 'avg_order_value': df['amount'].mean()\n }", "timeout_seconds": 15, "max_memory_mb": 128 } } ``` ```json theme={null} { "configuration": { "function_name": "customer-analytics-prod", "aws_region": "us-west-2", "invocation_type": "RequestResponse", "environment_variables": { "DATABASE_URL": "${DB_URL}", "API_KEY": "${EXTERNAL_API_KEY}" }, "timeout_seconds": 60 } } ``` ### Best Practices 1. **Naming**: Use clear, descriptive names that indicate the tool's purpose 2. **Security**: Never include sensitive data in tool definitions; use environment variables 3. **Testing**: Test tools thoroughly before deploying to production 4. **Documentation**: Provide clear descriptions and parameter documentation 5. **Error Handling**: Design tools to handle failures gracefully 6. **Performance**: Set appropriate timeouts and optimize for call-time execution
# Discover Lambda Functions Source: https://docs.burki.dev/api-reference/tools/discover-lambda POST /api/lambda/list-functions List available Lambda functions in user's AWS account. This endpoint discovers and lists all AWS Lambda functions available in your AWS account for the specified region and credentials. This is used by the Tools Library to provide an interactive function selector when creating Lambda tools. ### Request Body Send AWS credentials and region as query parameters: ```json Example Request theme={null} { "access_key": "AKIA1234567890EXAMPLE", "secret_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "region": "us-east-1" } ``` ### Query Parameters * `access_key` (string, **required**): AWS Access Key ID with Lambda permissions * `secret_key` (string, **required**): AWS Secret Access Key * `region` (string, optional, default: "us-east-1"): AWS region to search for Lambda functions ### Response A successful request returns detailed information about all Lambda functions: ```json Response theme={null} { "success": true, "message": "Found 3 Lambda functions", "functions": [ { "function_name": "customer-lookup-service", "description": "Look up customer data by phone number", "runtime": "python3.9", "timeout": 30, "memory_size": 512, "code_size": 1024000, "handler": "lambda_function.lambda_handler", "version": "$LATEST", "last_modified": "2024-01-15T10:30:00.000Z" }, { "function_name": "order-processing", "description": "Process customer orders and payments", "runtime": "nodejs18.x", "timeout": 60, "memory_size": 1024, "code_size": 2048000, "handler": "index.handler", "version": "$LATEST", "last_modified": "2024-01-10T14:22:15.000Z" }, { "function_name": "send-notifications", "description": "", "runtime": "python3.8", "timeout": 15, "memory_size": 256, "code_size": 512000, "handler": "notification.send", "version": "$LATEST", "last_modified": "2024-01-05T09:15:30.000Z" } ] } ``` ### Function Object Properties Each function object in the response contains: | Property | Type | Description | | - | - | - | | `function_name` | string | Lambda function name (used for invocation) | | `description` | string | Function description (may be empty) | | `runtime` | string | Runtime environment (e.g., python3.9, nodejs18.x) | | `timeout` | integer | Function timeout in seconds | | `memory_size` | integer | Allocated memory in MB | | `code_size` | integer | Function code size in bytes | | `handler` | string | Function entry point | | `version` | string | Function version (usually \$LATEST) | | `last_modified` | string | ISO timestamp of last modification | ### Error Responses #### 400 Bad Request - Missing Credentials ```json theme={null} { "success": false, "detail": "AWS credentials not found. Please configure access_key and secret_key." } ``` #### 400 Bad Request - Invalid Credentials ```json theme={null} { "success": false, "detail": "AWS error (InvalidUserID.NotFound): The AWS Access Key Id you provided does not exist in our records." } ``` #### 400 Bad Request - Access Denied ```json theme={null} { "success": false, "detail": "AWS error (AccessDenied): User: arn:aws:iam::123456789012:user/example is not authorized to perform: lambda:ListFunctions" } ``` #### 500 Internal Server Error ```json theme={null} { "success": false, "detail": "Failed to list Lambda functions: Connection timeout" } ``` ### Required IAM Permissions Your AWS credentials must have the following permissions: ```json IAM Policy theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "lambda:ListFunctions", "lambda:GetFunctionConfiguration" ], "Resource": "*" } ] } ``` ### Usage in Tools Library This endpoint is automatically called when users: 1. **Enter AWS credentials** in the Lambda tool configuration form 2. **Select a region** from the dropdown 3. **Click "🔍 Discover Functions"** button 4. **Browse the interactive function list** to select their Lambda function The selected function's metadata is used to auto-populate the tool configuration form. ### Rate Limits * **Rate Limit:** 10 requests per minute per organization * **Timeout:** 30 seconds maximum per request * **Pagination:** Returns all functions (AWS API handles pagination internally) **Security Note:** This endpoint requires AWS credentials to be sent in the request. Credentials are not stored on our servers and are only used for the duration of the API call. ### Example Usage ```bash cURL Example theme={null} curl -X POST "https://api.burki.dev/api/lambda/list-functions" \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "access_key=AKIA1234567890EXAMPLE" \ -d "secret_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \ -d "region=us-west-2" ``` ```python Python Example theme={null} import requests response = requests.post( "https://api.burki.dev/api/lambda/list-functions", headers={"Authorization": "Bearer YOUR_API_TOKEN"}, data={ "access_key": "AKIA1234567890EXAMPLE", "secret_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "region": "us-east-1" } ) functions = response.json()["functions"] print(f"Found {len(functions)} Lambda functions") ``` ```javascript JavaScript Example theme={null} const response = await fetch('/api/lambda/list-functions', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ access_key: 'AKIA1234567890EXAMPLE', secret_key: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', region: 'us-east-1' }) }); const data = await response.json(); console.log(`Discovered ${data.functions.length} functions`); ``` # Get Tool by ID Source: https://docs.burki.dev/api-reference/tools/get-by-id GET /api/v1/tools/{tool_id} This endpoint retrieves a specific tool by its unique ID, including full configuration details and usage statistics. ### Path Parameters * `tool_id` (integer, **required**): The unique identifier of the tool to retrieve ### Response A successful request returns the complete tool object: ```json Response theme={null} { "id": 123, "name": "get_customer_info", "display_name": "Customer Information Lookup", "description": "Look up customer information by phone number or email", "tool_type": "endpoint", "configuration": { "url": "https://api.yourcompany.com/customers/lookup", "method": "GET", "headers": { "Authorization": "Bearer [REDACTED]", "Content-Type": "application/json" }, "timeout_seconds": 30, "follow_redirects": true, "verify_ssl": true }, "function_definition": { "name": "get_customer_info", "description": "Look up customer information by phone number or email", "parameters": { "type": "object", "properties": { "phone_number": { "type": "string", "description": "Customer's phone number in E.164 format" }, "email": { "type": "string", "description": "Customer's email address" } }, "required": ["phone_number"] } }, "is_active": true, "is_public": false, "timeout_seconds": 30, "retry_attempts": 1, "execution_count": 156, "success_rate": 94.2, "avg_execution_time": 1.24, "last_executed_at": "2024-01-15T15:30:00Z", "organization_id": 456, "created_by": 789, "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-15T15:45:00Z" } ``` ### Error Responses **404 Not Found** ```json theme={null} { "detail": "Tool with ID 123 not found" } ``` **403 Forbidden** ```json theme={null} { "detail": "You don't have permission to access this tool" } ``` ### Tool Type Specific Fields ```json theme={null} { "configuration": { "url": "https://api.example.com/endpoint", "method": "POST", "headers": { "Authorization": "Bearer [REDACTED]", "Content-Type": "application/json" }, "timeout_seconds": 30, "follow_redirects": true, "verify_ssl": true, "request_template": { "query": "SELECT * FROM customers WHERE id = '${customer_id}'" }, "response_mapping": { "customer_name": "data.name", "customer_email": "data.email" } } } ``` ```json theme={null} { "configuration": { "python_code": "def calculate_payment(principal, rate, years):\n monthly_rate = rate / 100 / 12\n num_payments = years * 12\n payment = principal * (monthly_rate * (1 + monthly_rate) ** num_payments) / ((1 + monthly_rate) ** num_payments - 1)\n return {'monthly_payment': round(payment, 2)}", "timeout_seconds": 10, "max_memory_mb": 128, "allowed_modules": ["math", "datetime", "json"] } } ``` ```json theme={null} { "configuration": { "function_name": "customer-lookup-prod", "aws_region": "us-east-1", "invocation_type": "RequestResponse", "timeout_seconds": 30, "environment_variables": { "DATABASE_URL": "[REDACTED]", "API_KEY": "[REDACTED]" } } } ``` ### Usage Statistics The response includes detailed usage statistics: * `execution_count`: Total number of times the tool has been executed * `success_rate`: Percentage of successful executions (0-100) * `avg_execution_time`: Average execution time in seconds * `last_executed_at`: Timestamp of the most recent execution ### Integration Examples ```javascript Node.js theme={null} const axios = require('axios'); async function getTool(toolId) { try { const response = await axios.get(`https://api.burki.dev/api/v1/tools/${toolId}`, { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }); return response.data; } catch (error) { if (error.response?.status === 404) { throw new Error(`Tool with ID ${toolId} not found`); } throw error; } } // Usage const tool = await getTool(123); console.log(`Tool: ${tool.display_name}`); console.log(`Type: ${tool.tool_type}`); console.log(`Success Rate: ${tool.success_rate}%`); ``` ```python Python theme={null} import requests def get_tool(tool_id: int) -> dict: """Get a tool by its ID.""" url = f"https://api.burki.dev/api/v1/tools/{tool_id}" headers = {"Authorization": "Bearer YOUR_API_KEY"} response = requests.get(url, headers=headers) if response.status_code == 200: return response.json() elif response.status_code == 404: raise Exception(f"Tool with ID {tool_id} not found") else: raise Exception(f"Failed to get tool: {response.json()['detail']}") # Usage tool = get_tool(123) print(f"Tool: {tool['display_name']}") print(f"Type: {tool['tool_type']}") print(f"Executions: {tool['execution_count']}") ``` ```php PHP theme={null} ``` ### Security Notes * **Sensitive Data**: Configuration fields containing sensitive information (API keys, passwords) are redacted in responses * **Access Control**: Users can only access tools within their organization * **Audit Trail**: Tool access is logged for security monitoring # List Tools Source: https://docs.burki.dev/api-reference/tools/list GET /api/v1/tools List, search, filter, sort, and paginate custom tools available in your Burki organization for assistant workflows.

List Organization Tools

Use this endpoint to show available tools in an admin UI, audit tool health, or let builders attach existing tools to assistants.

  • Filter by tool type and active status.
  • Search by tool name, display name, or description.
  • Sort by usage, performance, creation date, or update date.

How this endpoint behaves

The list is paginated and supports search, filters, and sort order. Use it for admin screens, audits, and attaching existing tools to assistants without creating duplicates.

Tool records include type, status, and usage-oriented sort fields so you can find stale or high-traffic tools quickly.

### Query Parameters * `page` (integer, optional, default: 1): Page number for pagination * `per_page` (integer, optional, default: 12): Number of tools per page (max: 100) * `tool_type` (string, optional): Filter by tool type * Allowed values: `"endpoint"`, `"python_function"`, `"lambda"` * `is_active` (boolean, optional): Filter by active status * `search` (string, optional): Search in tool names, display names, and descriptions * `sort_by` (string, optional, default: `"name"`): Sort field * Allowed values: `"name"`, `"display_name"`, `"created_at"`, `"updated_at"`, `"execution_count"` * `sort_order` (string, optional, default: `"asc"`): Sort direction * Allowed values: `"asc"`, `"desc"` ### Example Requests ```bash Basic List theme={null} curl -X GET "https://api.burki.dev/api/v1/tools" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```bash Filtered by Type theme={null} curl -X GET "https://api.burki.dev/api/v1/tools?tool_type=endpoint&is_active=true" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```bash Search and Sort theme={null} curl -X GET "https://api.burki.dev/api/v1/tools?search=customer&sort_by=execution_count&sort_order=desc" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```bash Paginated theme={null} curl -X GET "https://api.burki.dev/api/v1/tools?page=2&per_page=20" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### Response A successful request returns a paginated list of tools: ```json Response theme={null} { "tools": [ { "id": 123, "name": "get_customer_info", "display_name": "Customer Information Lookup", "description": "Look up customer information by phone number or email", "tool_type": "endpoint", "configuration": { "url": "https://api.yourcompany.com/customers/lookup", "method": "GET", "timeout_seconds": 30 }, "function_definition": { "name": "get_customer_info", "description": "Look up customer information by phone number or email", "parameters": { "type": "object", "properties": { "phone_number": { "type": "string", "description": "Customer's phone number in E.164 format" } }, "required": ["phone_number"] } }, "is_active": true, "is_public": false, "execution_count": 156, "success_rate": 94.2, "avg_execution_time": 1.24, "organization_id": 456, "created_by": 789, "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-15T15:45:00Z" }, { "id": 124, "name": "calculate_loan_payment", "display_name": "Loan Payment Calculator", "description": "Calculate monthly loan payment based on principal, rate, and term", "tool_type": "python_function", "configuration": { "timeout_seconds": 10 }, "function_definition": { "name": "calculate_loan_payment", "description": "Calculate monthly loan payment and totals", "parameters": { "type": "object", "properties": { "principal": {"type": "number"}, "annual_rate": {"type": "number"}, "years": {"type": "integer"} }, "required": ["principal", "annual_rate", "years"] } }, "is_active": true, "is_public": false, "execution_count": 89, "success_rate": 100.0, "avg_execution_time": 0.12, "organization_id": 456, "created_by": 789, "created_at": "2024-01-14T09:15:00Z", "updated_at": "2024-01-14T09:15:00Z" } ], "pagination": { "page": 1, "per_page": 12, "total": 2, "total_pages": 1, "has_next": false, "has_prev": false } } ``` ### Response Fields **Tool Object:** * `id` (integer): Unique tool identifier * `name` (string): Tool name (unique within organization) * `display_name` (string): Human-readable name * `description` (string): Tool description * `tool_type` (string): Tool type (`"endpoint"`, `"python_function"`, or `"lambda"`) * `configuration` (object): Tool-specific configuration (sensitive data redacted) * `function_definition` (object): OpenAI function calling schema * `is_active` (boolean): Whether the tool is active * `is_public` (boolean): Whether the tool is publicly visible * `execution_count` (integer): Total number of executions * `success_rate` (float): Success rate percentage (0-100) * `avg_execution_time` (float): Average execution time in seconds * `organization_id` (integer): Organization that owns the tool * `created_by` (integer): User ID who created the tool * `created_at` (string): ISO 8601 creation timestamp * `updated_at` (string): ISO 8601 last update timestamp **Pagination Object:** * `page` (integer): Current page number * `per_page` (integer): Items per page * `total` (integer): Total number of tools * `total_pages` (integer): Total number of pages * `has_next` (boolean): Whether there's a next page * `has_prev` (boolean): Whether there's a previous page ### Filtering Examples **Endpoint Tools Only:** ```bash theme={null} GET /api/v1/tools?tool_type=endpoint ``` **Python Function Tools Only:** ```bash theme={null} GET /api/v1/tools?tool_type=python_function ``` **Active Lambda Tools:** ```bash theme={null} GET /api/v1/tools?tool_type=lambda&is_active=true ``` **Search by Name:** ```bash theme={null} GET /api/v1/tools?search=customer ``` **Search by Description:** ```bash theme={null} GET /api/v1/tools?search=lookup ``` **Complex Search:** ```bash theme={null} GET /api/v1/tools?search=payment%20calculator&tool_type=python_function ``` **Most Used Tools:** ```bash theme={null} GET /api/v1/tools?sort_by=execution_count&sort_order=desc ``` **Recently Created:** ```bash theme={null} GET /api/v1/tools?sort_by=created_at&sort_order=desc ``` **Best Performance:** ```bash theme={null} GET /api/v1/tools?sort_by=success_rate&sort_order=desc ``` **Fastest Execution:** ```bash theme={null} GET /api/v1/tools?sort_by=avg_execution_time&sort_order=asc ``` ### Error Responses **400 Bad Request** ```json theme={null} { "detail": "Invalid sort_by field. Must be one of: name, display_name, created_at, updated_at, execution_count" } ``` **422 Validation Error** ```json theme={null} { "detail": [ { "loc": ["query", "per_page"], "msg": "ensure this value is less than or equal to 100", "type": "value_error.number.not_le" } ] } ``` ### Integration Examples ```javascript theme={null} const axios = require('axios'); async function listTools(filters = {}) { try { const params = new URLSearchParams(); // Add filters if (filters.toolType) params.append('tool_type', filters.toolType); if (filters.isActive !== undefined) params.append('is_active', filters.isActive); if (filters.search) params.append('search', filters.search); if (filters.sortBy) params.append('sort_by', filters.sortBy); if (filters.sortOrder) params.append('sort_order', filters.sortOrder); if (filters.page) params.append('page', filters.page); if (filters.perPage) params.append('per_page', filters.perPage); const response = await axios.get(`https://api.burki.dev/api/v1/tools?${params}`, { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }); return response.data; } catch (error) { console.error('Failed to list tools:', error.response.data); throw error; } } // Usage examples const allTools = await listTools(); const endpointTools = await listTools({ toolType: 'endpoint', isActive: true }); const mostUsed = await listTools({ sortBy: 'execution_count', sortOrder: 'desc' }); ``` ```python theme={null} import requests from typing import Optional, Dict, Any def list_tools( tool_type: Optional[str] = None, is_active: Optional[bool] = None, search: Optional[str] = None, sort_by: str = "name", sort_order: str = "asc", page: int = 1, per_page: int = 12 ) -> Dict[str, Any]: """List tools with optional filtering and sorting.""" url = "https://api.burki.dev/api/v1/tools" headers = {"Authorization": "Bearer YOUR_API_KEY"} params = { "sort_by": sort_by, "sort_order": sort_order, "page": page, "per_page": per_page } # Add optional filters if tool_type: params["tool_type"] = tool_type if is_active is not None: params["is_active"] = is_active if search: params["search"] = search response = requests.get(url, headers=headers, params=params) if response.status_code == 200: return response.json() else: raise Exception(f"Failed to list tools: {response.json()['detail']}") # Usage examples all_tools = list_tools() python_tools = list_tools(tool_type="python_function", is_active=True) search_results = list_tools(search="customer", sort_by="execution_count", sort_order="desc") ``` ```php theme={null} 'endpoint', 'is_active' => true]); $searchResults = listTools(['search' => 'customer', 'sort_by' => 'execution_count']); ?> ``` ### Performance Considerations * **Pagination**: Use appropriate page sizes (default 12, max 100) * **Filtering**: Apply filters to reduce response size * **Caching**: Consider caching results for frequently accessed tool lists * **Search**: Use specific search terms for better performance
# Initiate Outbound Call Source: https://docs.burki.dev/api-reference/webhooks/initiate-call POST /calls/initiate Initiates an outbound call from an assistant to a specified phone number. This endpoint is protected and requires authentication. This endpoint is used to programmatically initiate an outbound call from one of your AI assistants to an external phone number. It is a system endpoint that you call from your own application logic, not a webhook for an external service. When you call this endpoint, Burki instructs your telephony provider (Twilio, Telnyx, or Vonage) to place a call. The provider then makes a request to your webhook, passing along the parameters you provide here as metadata. This allows the system to route the call to the correct assistant and begin the conversation. **Multi-Provider Support**: This endpoint works with Twilio, Telnyx, Vonage, and BYO SIP Trunk. The system automatically uses the provider configured for the specified `from_phone_number`. ## Request Body The request body is a JSON object specifying the details of the outbound call. ### Required Parameters * `from_phone_number` (string, **required**): The phone number to call from, in E.164 format. This number must be assigned to an assistant or ConversationFlow in your organization. * `to_phone_number` (string, **required**): The destination phone number to call, in E.164 format. ### Optional Parameters * `assistant_id` (integer, optional): Override the default assistant attached to the phone number. If not provided, the system uses the assistant assigned to `from_phone_number`. * `welcome_message` (string, optional): A custom welcome message for the assistant to say at the beginning of the call. If not provided, the assistant's default greeting will be used. Supports variable substitution using `{{variable}}` syntax. * `agenda` (string, optional): A specific topic or goal for the call. This can be used to guide the assistant's conversation. Supports variable substitution using `{{variable}}` syntax. * `variables` (object, optional): A dictionary of custom variables for template substitution in the `welcome_message` and `agenda`. Variables can be referenced using double curly brackets, e.g., `{{name}}`, `{{company}}`, etc. ### Assistant Resolution Logic The system determines which assistant handles the call using the following priority: 1. **If `assistant_id` is provided**: Use the specified assistant (must belong to your organization) 2. **If `from_phone_number` is assigned to a ConversationFlow**: Use the flow's base assistant and live flow runtime 3. **If `from_phone_number` is assigned to an assistant**: Use that assistant If no assistant can be resolved through the above logic, the request will fail with a 404 error. ## Examples ### Basic Call (Using Phone Number's Default Assistant) ```json Request theme={null} { "from_phone_number": "+15551234567", "to_phone_number": "+15559876543" } ``` ### Call with Custom Message and Variables ```json Request theme={null} { "from_phone_number": "+15551234567", "to_phone_number": "+15559876543", "welcome_message": "Hello {{name}}, this is a reminder about your appointment tomorrow at {{time}}.", "agenda": "Remind {{name}} about their upcoming {{appointment_type}} appointment.", "variables": { "name": "John Doe", "time": "10 AM", "appointment_type": "dental" } } ``` ### Call with Assistant Override Use a different assistant than the one assigned to the phone number: ```json Request theme={null} { "from_phone_number": "+15551234567", "to_phone_number": "+15559876543", "assistant_id": 456, "welcome_message": "Hi there! I'm calling about your recent inquiry." } ``` ## Response A successful request will return a `200 OK` status with a JSON object containing the `call_sid` from your telephony provider, confirming that the call has been initiated. ```json Response theme={null} { "message": "Call initiated successfully", "call_sid": "CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } ``` ## Error Responses ### 400 Bad Request Returned when required parameters are missing or invalid. ```json theme={null} { "detail": "from_phone_number is required" } ``` ### 402 Payment Required Returned when your organization has insufficient funds. ```json theme={null} { "detail": "Insufficient balance. Please add funds to continue making calls." } ``` ### 403 Forbidden Returned when the phone number or assistant doesn't belong to your organization. ```json theme={null} { "detail": "Unauthorized: phone number +15551234567 does not belong to your organization" } ``` ### 404 Not Found Returned when no assistant can be resolved for the call. ```json theme={null} { "detail": "No assistant found for phone number +15551234567" } ``` # Recording Status Webhook Source: https://docs.burki.dev/api-reference/webhooks/recording-status POST /recording-status Handle Twilio recording status callbacks. This endpoint is called by Twilio when a recording is completed or failed. Uses the unified webhook processing architecture for consistent handling. This webhook is designed to receive status updates from Twilio about call recordings. When you enable call recording on your Twilio account or for a specific call, Twilio will automatically make a `POST` request to this endpoint once the recording is processed and available. Its primary function is to receive the recording metadata from Twilio and update the corresponding call record in your database with the recording URL, duration, and other details. ### Request from Twilio This endpoint consumes `application/x-www-form-urlencoded` data sent by Twilio. The following are the key parameters: * `CallSid` (string): The unique identifier of the call that was recorded. This is used to look up the call in your database. * `RecordingSid` (string): A unique identifier for the recording itself. * `RecordingUrl` (string): The URL where the recording's audio file can be accessed. This URL will be stored in your database. * `RecordingDuration` (string): The duration of the recording in seconds. * `RecordingStatus` (string): The status of the recording, typically `completed`. ### Response to Twilio The endpoint responds with a `200 OK` status and a simple confirmation message to acknowledge receipt of the data. The response body is not typically used by Twilio. ```json Response theme={null} { "message": "Recording status updated" } ``` # SIP Webhooks and Prewarm Source: https://docs.burki.dev/api-reference/webhooks/sip-webhooks SIP bridge webhook, status, and prewarm endpoints. Burki exposes root-level SIP endpoints used by SIP carriers and the SIP bridge. These endpoints do not use the `/api/v1` prefix. ```text theme={null} https://api.burki.dev/sip-webhook https://api.burki.dev/sip-status https://api.burki.dev/sip-prewarm ``` The SIP bridge that terminates customer PBX/carrier traffic currently advertises: ```text theme={null} SIP bridge IP: 52.21.157.171 SIP signaling: UDP/TCP 5060 SIP TLS: TCP 5061, if enabled RTP media: UDP 10000-20000 ``` PBX/carrier firewalls should allow `52.21.157.171` for both inbound calls to Burki and outbound calls from Burki. The HTTP endpoints above are backend/provider coordination endpoints; customer PBXs should send SIP traffic to the SIP bridge IP, not to `/sip-webhook` directly. ## SIP Webhook ```http theme={null} POST /sip-webhook ``` Receives SIP inbound call and provider event payloads. This endpoint is called by the SIP bridge/provider layer, not by normal application clients. ## SIP Status ```http theme={null} POST /sip-status ``` Receives SIP status callbacks for call lifecycle tracking. ## SIP Prewarm ```http theme={null} POST /sip-prewarm Content-Type: application/json ``` The SIP bridge calls this endpoint before media streaming so Burki can prepare call context. ### Request ```json Request theme={null} { "call_sid": "sip-20260430-abc123", "from_number": "+15559876543", "to_number": "+15551234567", "outbound": false, "assistant_id": 123, "welcome_message": "Thanks for calling. How can I help?", "agenda": "Customer support intake", "variables": { "customer_id": "cust_123" } } ``` | Field | Type | Required | Description | | - | - | - | - | | `call_sid` | string | Yes | Burki/SIP call identifier used for the stream | | `from_number` | string | No | Caller phone number | | `to_number` | string | No | Destination or assistant phone number | | `outbound` | boolean | No | Whether this is an outbound SIP call | | `assistant_id` | integer | No | Assistant to prepare | | `welcome_message` | string | No | Optional first assistant message | | `agenda` | string | No | Optional call agenda/instructions | | `variables` | object or JSON string | No | Runtime variables for the call | ### Response ```json Response theme={null} { "call_sid": "sip-20260430-abc123", "prewarmed": true } ``` ### Error Responses ```json 400 theme={null} { "error": "call_sid required" } ``` ## Operational Notes * `/sip-webhook` and `/sip-status` are CSRF-exempt provider endpoints. * `/sip-prewarm` is intended for SIP bridge integrations before media streaming. * SIP bridge synchronization uses `SIP_BRIDGE_URL` and `SIP_BRIDGE_API_KEY` on the backend. * The bridge sync API receives org config at `POST {SIP_BRIDGE_URL}/api/sync-organization`. # SMS Webhooks Source: https://docs.burki.dev/api-reference/webhooks/sms-webhooks Configure SMS webhook endpoints to receive SMS events ## Overview When an SMS is received by your assistant's phone number, Burki can forward the SMS data to your configured webhook URL. This allows you to integrate SMS functionality into your applications. ## Webhook Configuration To receive SMS webhooks, configure the `sms_webhook_url` field when creating or updating an assistant. For Twilio, you can also optionally specify a `messaging_service_sid`: ```javascript Node.js theme={null} const response = await fetch('https://api.burki.dev/api/v1/assistants', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ name: "SMS Assistant", sms_webhook_url: "https://your-app.com/webhooks/sms", messaging_service_sid: "MG1234567890abcdef1234567890abcdef", // Optional: Twilio Messaging Service SID // ... other assistant configuration }) }); ``` ```python Python theme={null} import requests response = requests.post( 'https://api.burki.dev/api/v1/assistants', headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, json={ 'name': 'SMS Assistant', 'sms_webhook_url': 'https://your-app.com/webhooks/sms', 'messaging_service_sid': 'MG1234567890abcdef1234567890abcdef', # Optional: Twilio Messaging Service SID # ... other assistant configuration } ) ``` ```bash cURL theme={null} curl -X POST "https://api.burki.dev/api/v1/assistants" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "SMS Assistant", "sms_webhook_url": "https://your-app.com/webhooks/sms" }' ``` ## Webhook Payload When an SMS is received, Burki sends a POST request to your `sms_webhook_url` with the following payload: ```json theme={null} { "type": "sms_received", "timestamp": "2024-01-15T10:30:00.123456", "data": { "message_id": "SM123abc...", "from": "+1234567890", "to": "+1987654321", "body": "Hello, this is a test message", "media_urls": ["https://example.com/image.jpg"], "provider": "twilio" } } ``` ### Payload Fields | Field | Type | Description | | - | - | - | | `type` | string | Always `"sms_received"` for incoming SMS | | `timestamp` | string | ISO 8601 timestamp when the webhook was sent | | `data.message_id` | string | Unique identifier for the SMS message | | `data.from` | string | Phone number that sent the SMS (E.164 format) | | `data.to` | string | Phone number that received the SMS (your assistant's number) | | `data.body` | string | Text content of the SMS message | | `data.media_urls` | array | URLs of any media attachments (MMS) | | `data.provider` | string | Telephony provider (`"twilio"`, `"telnyx"`, or `"vonage"`) | ## Provider Support Provider webhooks are received by Burki at canonical unified endpoints: * **Twilio**: `POST /webhooks/sms/twilio` * **Telnyx**: `POST /webhooks/sms/telnyx` * **Vonage**: `POST` or `GET /webhooks/sms/vonage` Legacy aliases remain available for existing provider configurations: * `/twilio-sms-webhook` * `/telnyx-sms-webhook` * `/vonage-sms-webhook` The payload format is normalized between providers, so your webhook handler works regardless of the underlying provider. ## Webhook Requirements ### Response Your webhook endpoint should respond with: * **Status Code**: 200 (OK) * **Response Time**: Under 10 seconds * **Body**: Any response body is acceptable ### Security * **HTTPS Required**: Your webhook URL must use HTTPS * **User-Agent**: Webhooks are sent with `User-Agent: Burki-SMS-Webhook/1.0` * **Timeout**: Requests timeout after 10 seconds ## Example Implementation ```javascript Express.js theme={null} app.post('/webhooks/sms', express.json(), (req, res) => { const { type, timestamp, data } = req.body; if (type === 'sms_received') { console.log(`SMS from ${data.from} to ${data.to}: ${data.body}`); // Process the SMS message if (data.body.toLowerCase().includes('help')) { // Send response SMS via Burki API sendResponseSMS(data.from, "How can I help you?"); } // Handle media attachments if (data.media_urls.length > 0) { console.log(`Received ${data.media_urls.length} media attachments`); } } res.status(200).send('OK'); }); ``` ```python Flask theme={null} from flask import Flask, request, jsonify app = Flask(__name__) @app.route('/webhooks/sms', methods=['POST']) def handle_sms_webhook(): data = request.json if data['type'] == 'sms_received': sms_data = data['data'] print(f"SMS from {sms_data['from']} to {sms_data['to']}: {sms_data['body']}") # Process the SMS message if 'help' in sms_data['body'].lower(): # Send response SMS via Burki API send_response_sms(sms_data['from'], "How can I help you?") # Handle media attachments if sms_data['media_urls']: print(f"Received {len(sms_data['media_urls'])} media attachments") return jsonify({'status': 'ok'}), 200 ``` ```php PHP theme={null} ``` ## Troubleshooting ### Common Issues 1. **No webhooks received** * Verify `sms_webhook_url` is set on your assistant * Check that your endpoint is publicly accessible * Ensure your server responds with 200 status 2. **Webhooks timing out** * Your endpoint must respond within 10 seconds * Consider processing SMS asynchronously 3. **Missing SMS messages** * Verify your assistant's phone number is correctly configured * Check that SMS capabilities are enabled on your phone number ### Testing You can test your SMS webhook by: 1. Sending an SMS to your assistant's phone number 2. Checking your webhook endpoint logs 3. Using the live transcript WebSocket to monitor events ## Next Steps * [Send SMS Messages](/api-reference/messaging/send-sms) * [Phone Number Management](/api-reference/phone-numbers/update-webhooks) * [Assistant Configuration](/api-reference/assistants/create) # Twilio TwiML Webhook Source: https://docs.burki.dev/api-reference/webhooks/twilio-twiml POST /twiml Generate TwiML for incoming Twilio calls. This endpoint is called by Twilio when a call comes in. It uses the unified webhook processing architecture: 1. Parse and validate webhook 2. Retrieve metadata for outbound calls 3. Process call using CallWebhookProcessor 4. Build and return TwiML response This webhook is the primary entry point for all voice calls handled by the platform. It is designed to be called exclusively by Twilio when one of your configured phone numbers receives an incoming call or when you initiate an outbound call via the API. Its core function is to generate a TwiML (Twilio Markup Language) response that instructs Twilio to open a bi-directional media stream over a WebSocket connection to your server. This stream is what enables real-time voice interaction with your AI assistants. ### How It Works 1. **Twilio Call**: A call is made to one of your Twilio phone numbers. 2. **Webhook Request**: Twilio sends a `POST` request to this `/twiml` endpoint. 3. **Assistant Lookup**: The system identifies the appropriate assistant based on the phone number called (for inbound) or metadata (for outbound). 4. **Database Record**: A new call record is created in the database. 5. **TwiML Response**: The server responds with TwiML containing a `` verb, instructing Twilio to connect to the `/streams` WebSocket endpoint. ### Request from Twilio This endpoint consumes `application/x-www-form-urlencoded` data sent by Twilio. The following are the key parameters your system uses: * `CallSid` (string): A unique identifier for the call, provided by Twilio. * `To` (string): The Twilio phone number that was called. * `From` (string): The phone number of the person initiating the call. For outbound calls, additional query parameters are used internally to pass metadata, such as `assistant_id` and a `welcome_message`. ### Response to Twilio The response is not JSON, but rather an XML document in TwiML format. It instructs Twilio to connect to your WebSocket server to begin streaming audio. ```xml Response theme={null} ``` # Campaign Progress WebSocket Source: https://docs.burki.dev/api-reference/websockets/campaign-progress Real-time campaign progress updates via WebSocket connection ## WebSocket Endpoint **Endpoint:** `wss://api.burki.dev/ws/campaigns/{campaign_id}/progress?token={api_key}` This WebSocket endpoint provides real-time streaming of campaign progress updates. Use it to build live monitoring dashboards without polling. ### Path Parameters * `campaign_id` (integer, **required**): The ID of the campaign to monitor ### Authentication **Required**: All connections must be authenticated using a valid API key. Pass your API key as a query parameter: ``` wss://api.burki.dev/ws/campaigns/42/progress?token=your_api_key ``` ### Connection Process 1. **Connect**: Establish WebSocket connection with campaign ID and token 2. **Receive Acknowledgment**: Server confirms connection 3. **Get Initial Data**: Receive current progress state 4. **Stream Updates**: Receive real-time progress updates 5. **Handle Events**: Process contact completions, failures, etc. ## Server-to-Client Messages ### Connection Acknowledgment Sent immediately after connection is established: ```json theme={null} { "type": "connection_ack", "campaign_id": 42, "timestamp": 1707984000.123 } ``` ### Initial Progress Current campaign state sent after connection: ```json theme={null} { "type": "initial_progress", "data": { "campaign_id": 42, "status": "running", "progress": { "total_contacts": 500, "completed": 325, "failed": 25, "processing": 5, "pending": 145, "progress_percentage": 70.0, "success_rate": 92.86 }, "started_at": "2024-02-15T09:00:00Z", "completed_at": null, "recent_activity": [] } } ``` ### Progress Update Sent whenever progress changes (contact completed, failed, etc.): ```json theme={null} { "type": "progress_update", "data": { "campaign_id": 42, "status": "running", "progress": { "total_contacts": 500, "completed": 326, "failed": 25, "processing": 4, "pending": 145, "progress_percentage": 70.2, "success_rate": 92.88 }, "started_at": "2024-02-15T09:00:00Z", "completed_at": null } } ``` ### Metrics History Response to `request_history` client message: ```json theme={null} { "type": "metrics_history", "data": [ { "timestamp": "2024-02-15T09:00:00Z", "completed": 0, "failed": 0, "success_rate": 0 }, { "timestamp": "2024-02-15T10:00:00Z", "completed": 150, "failed": 10, "success_rate": 93.75 } ] } ``` ### Pong Response Response to ping health check: ```json theme={null} { "type": "pong" } ``` ## Client-to-Server Messages ### Ping (Health Check) Send periodically to maintain connection: ```json theme={null} { "type": "ping" } ``` ### Request Update Request current progress on demand: ```json theme={null} { "type": "request_update" } ``` ### Request History Request historical metrics (last N hours): ```json theme={null} { "type": "request_history", "hours": 24 } ``` ## Progress Fields | Field | Type | Description | | - | - | - | | `total_contacts` | integer | Total contacts in campaign | | `completed` | integer | Successfully contacted | | `failed` | integer | Failed contact attempts | | `processing` | integer | Currently being contacted | | `pending` | integer | Waiting to be contacted | | `progress_percentage` | float | Completion percentage | | `success_rate` | float | Success rate percentage | ## Campaign Status Values | Status | Description | | - | - | | `draft` | Not yet started | | `scheduled` | Waiting for scheduled time | | `running` | Actively executing | | `paused` | Temporarily paused | | `completed` | All contacts processed | | `cancelled` | Stopped before completion | | `failed` | Campaign error | ## Example Usage ### JavaScript ```javascript theme={null} const campaignId = 42; const apiKey = "your_api_key_here"; const ws = new WebSocket(`wss://api.burki.dev/ws/campaigns/${campaignId}/progress?token=${apiKey}`); ws.onopen = function() { console.log("Connected to campaign progress stream"); // Set up ping interval to keep connection alive setInterval(() => { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: "ping" })); } }, 30000); }; ws.onmessage = function(event) { const message = JSON.parse(event.data); switch (message.type) { case "connection_ack": console.log("Connection confirmed for campaign", message.campaign_id); break; case "initial_progress": case "progress_update": const progress = message.data.progress; console.log(`Progress: ${progress.completed}/${progress.total_contacts} ` + `(${progress.progress_percentage.toFixed(1)}%)`); console.log(`Status: ${message.data.status}`); // Update your UI here updateProgressBar(progress.progress_percentage); updateStats(progress); // Check if campaign completed if (message.data.status === "completed") { console.log("Campaign completed!"); } break; case "pong": // Connection is healthy break; default: console.log("Unknown message type:", message.type); } }; ws.onerror = function(error) { console.error("WebSocket error:", error); }; ws.onclose = function(event) { console.log("Connection closed:", event.code, event.reason); // Implement reconnection logic here }; // Request historical metrics function requestHistory() { ws.send(JSON.stringify({ type: "request_history", hours: 24 })); } ``` ### Python ```python theme={null} import asyncio import websockets import json async def monitor_campaign(campaign_id: int, api_key: str): """Monitor campaign progress via WebSocket.""" uri = f"wss://api.burki.dev/ws/campaigns/{campaign_id}/progress?token={api_key}" async with websockets.connect(uri) as websocket: print(f"Connected to campaign {campaign_id}") # Start ping task to keep connection alive async def ping_task(): while True: await asyncio.sleep(30) await websocket.send(json.dumps({"type": "ping"})) ping = asyncio.create_task(ping_task()) try: async for message in websocket: data = json.loads(message) if data["type"] == "connection_ack": print(f"Connection confirmed for campaign {data['campaign_id']}") elif data["type"] in ["initial_progress", "progress_update"]: progress = data["data"]["progress"] status = data["data"]["status"] print(f"Status: {status}") print(f"Progress: {progress['completed']}/{progress['total_contacts']} " f"({progress['progress_percentage']:.1f}%)") print(f"Success rate: {progress['success_rate']:.1f}%") print("---") if status == "completed": print("Campaign completed!") break elif data["type"] == "pong": pass # Connection healthy finally: ping.cancel() # Run the monitor asyncio.run(monitor_campaign(42, "your_api_key_here")) ``` ### React Hook Example ```javascript theme={null} import { useEffect, useState, useCallback, useRef } from 'react'; function useCampaignProgress(campaignId, apiKey) { const [progress, setProgress] = useState(null); const [status, setStatus] = useState('disconnected'); const [error, setError] = useState(null); const wsRef = useRef(null); const connect = useCallback(() => { const ws = new WebSocket( `wss://api.burki.dev/ws/campaigns/${campaignId}/progress?token=${apiKey}` ); ws.onopen = () => { setStatus('connected'); setError(null); }; ws.onmessage = (event) => { const message = JSON.parse(event.data); if (message.type === 'initial_progress' || message.type === 'progress_update') { setProgress(message.data); } }; ws.onerror = () => { setError('Connection error'); setStatus('error'); }; ws.onclose = () => { setStatus('disconnected'); // Reconnect after 5 seconds setTimeout(connect, 5000); }; wsRef.current = ws; // Ping every 30 seconds const pingInterval = setInterval(() => { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: 'ping' })); } }, 30000); return () => { clearInterval(pingInterval); ws.close(); }; }, [campaignId, apiKey]); useEffect(() => { const cleanup = connect(); return cleanup; }, [connect]); return { progress, status, error }; } // Usage in component function CampaignDashboard({ campaignId, apiKey }) { const { progress, status, error } = useCampaignProgress(campaignId, apiKey); if (status === 'disconnected') return
Connecting...
; if (error) return
Error: {error}
; if (!progress) return
Loading...
; return (

Campaign Progress

Status: {progress.status}

Progress: {progress.progress.completed}/{progress.progress.total_contacts}

Success Rate: {progress.progress.success_rate.toFixed(1)}%

); } ``` ## Connection Management ### Reconnection Strategy Implement exponential backoff for reconnection: ```javascript theme={null} let reconnectAttempts = 0; const maxReconnectAttempts = 10; function reconnect() { if (reconnectAttempts >= maxReconnectAttempts) { console.error("Max reconnection attempts reached"); return; } const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 30000); reconnectAttempts++; console.log(`Reconnecting in ${delay}ms (attempt ${reconnectAttempts})`); setTimeout(connect, delay); } ``` ### Keep-Alive Send ping messages every 30 seconds to maintain the connection: ```javascript theme={null} setInterval(() => { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: "ping" })); } }, 30000); ``` ## Error Handling ### Close Codes | Code | Description | | - | - | | 1000 | Normal closure | | 1001 | Going away | | 1008 | Authentication failed | | 4000 | Internal server error | | 4004 | Campaign not found | ### Authentication Errors If authentication fails, the connection is immediately closed with code 1008: ```javascript theme={null} ws.onclose = function(event) { if (event.code === 1008) { console.error("Authentication failed - check your API key"); } }; ``` ## Best Practices 1. **Always authenticate**: Include valid API key in query parameter 2. **Implement ping/pong**: Send pings every 30 seconds to keep connection alive 3. **Handle reconnection**: Implement exponential backoff for reconnection 4. **Process all message types**: Handle connection\_ack, initial\_progress, progress\_update 5. **Monitor connection state**: Track open/close events for UI feedback 6. **Clean up on unmount**: Close WebSocket when component unmounts ## Multi-Campaign Monitoring To monitor multiple campaigns, use the campaigns monitor endpoint: **Endpoint:** `wss://api.burki.dev/ws/campaigns/monitor?token={api_key}` This provides an overview of all campaigns in your organization with the ability to subscribe to specific campaigns. # Live transcript WebSocket API — endpoint, auth & message formats | Burki Docs Source: https://docs.burki.dev/api-reference/websockets/live-transcript WebSocket API for real-time call transcript streaming, authentication, and message formats. # Live Transcript WebSocket API Reference ## WebSocket Endpoint **Endpoint:** `wss://api.burki.dev/live-transcript/{call_sid}?token={api_key}` This WebSocket endpoint provides real-time streaming of call transcripts as they are generated. It's designed for building live monitoring dashboards, call supervision tools, and accessibility features. ### Path Parameters * `call_sid` (string, **required**): The Burki call SID you want to monitor ### Authentication **Required**: All connections must be authenticated with a valid API key or JWT token. **Methods Supported:** 1. **Query Parameter** (Recommended): ``` wss://api.burki.dev/live-transcript/{call_sid}?token=your_api_key ``` 2. **Authorization Header** (non-browser clients): Use this with server-side WebSocket clients (Node `ws`, Python `websockets`, etc.). Browsers do not allow custom `Authorization` headers on `new WebSocket(...)`. 3. **WebSocket Subprotocol**: ```javascript theme={null} const websocket = new WebSocket(url, ['burki-token-your_api_key']); ``` ### Access Control * Only users from the same organization as the call's assistant can access the transcript stream * API keys must have valid permissions and belong to an active user * Invalid authentication results in immediate connection termination with code 1008 ### Connection Process 1. **Establish Connection**: Connect to the WebSocket endpoint with a valid Call SID 2. **Receive Confirmation**: Server sends a connection established message 3. **Get Historical Data**: Receive any existing transcripts for the call 4. **Stream Live Data**: Receive real-time transcripts as they're generated 5. **Handle Status Updates**: Receive call status changes (start, end, etc.) ## Server-to-Client Messages ### Connection Established ```json theme={null} { "type": "connection_established", "call_sid": "CA123abc...", "timestamp": "2024-01-01T12:00:00.000Z", "message": "Connected to live transcript stream" } ``` ### Transcript Data ```json theme={null} { "type": "transcript", "call_sid": "CA123abc...", "timestamp": "2024-01-01T12:00:05.123Z", "data": { "content": "Hello, how can I help you today?", "speaker": "assistant", "is_final": true, "confidence": 0.95, "segment_start": 5.2, "segment_end": 7.1, "created_at": "2024-01-01T12:00:05.123Z" } } ``` **Field Descriptions:** * `content`: The transcribed text * `speaker`: `"user"` or `"assistant"` * `is_final`: Whether this is a final transcript * `confidence`: Speech recognition confidence (0.0 - 1.0) * `segment_start`: Start time in seconds from call start * `segment_end`: End time in seconds from call start * `created_at`: ISO timestamp of transcript creation ### Call Status Updates ```json theme={null} { "type": "call_status", "call_sid": "CA123abc...", "timestamp": "2024-01-01T12:00:00.000Z", "status": "in-progress", "metadata": { "assistant_id": 123, "started_at": "2024-01-01T12:00:00.000Z", "to_phone_number": "+1234567890", "customer_phone_number": "+0987654321", "call_type": "inbound", "duration": 45 } } ``` **Status Values:** * `in-progress`: Call is active * `completed`: Call ended successfully * `failed`: Call failed or was terminated ### Error Messages ```json theme={null} { "type": "error", "call_sid": "CA123abc...", "timestamp": "2024-01-01T12:00:00.000Z", "message": "Call CA123abc... not found" } ``` ### Pong Response ```json theme={null} { "type": "pong", "timestamp": "2024-01-01T12:00:00.000Z" } ``` ## Client-to-Server Messages ### Ping (Health Check) Send a ping to test connection health: ```json theme={null} { "type": "ping" } ``` **Response:** Server will reply with a `pong` message ### Request Status Request the current call status: ```json theme={null} { "type": "request_status" } ``` **Response:** Server will reply with a `call_status` message ## Features ### Historical Transcripts When connecting to an ongoing call, you'll immediately receive all existing transcripts before new live data starts streaming. ### Multiple Connections Multiple clients can connect to the same call simultaneously without performance impact. ### Automatic Cleanup Connections are automatically cleaned up when: * Clients disconnect * Calls end * Connection errors occur ### Error Handling The server gracefully handles: * Invalid Call SIDs * Connection timeouts * Malformed messages * Concurrent connection limits ## Example Usage ### JavaScript ```javascript theme={null} const callSid = 'CA123abc...'; const apiKey = 'burki_your_api_key_here'; const websocket = new WebSocket(`wss://api.burki.dev/live-transcript/${callSid}?token=${apiKey}`); websocket.onopen = function() { console.log('Connected to live transcript stream'); // Send a ping to test connection websocket.send(JSON.stringify({ type: 'ping' })); }; websocket.onmessage = function(event) { const message = JSON.parse(event.data); switch (message.type) { case 'transcript': console.log(`${message.data.speaker}: ${message.data.content}`); break; case 'call_status': console.log(`Call status: ${message.status}`); break; case 'pong': console.log('Connection is healthy'); break; case 'error': console.error(`Error: ${message.message}`); break; } }; websocket.onclose = function(event) { console.log('Connection closed:', event.code); }; ``` ### Python ```python theme={null} import asyncio import websockets import json async def listen_to_call(call_sid, api_key): uri = f"wss://api.burki.dev/live-transcript/{call_sid}?token={api_key}" try: async with websockets.connect(uri) as websocket: print(f"Connected to call {call_sid}") # Send ping await websocket.send(json.dumps({"type": "ping"})) async for message in websocket: data = json.loads(message) if data['type'] == 'transcript': transcript = data['data'] print(f"{transcript['speaker']}: {transcript['content']}") elif data['type'] == 'call_status': print(f"Status: {data['status']}") elif data['type'] == 'error': print(f"Error: {data['message']}") break except websockets.exceptions.ConnectionClosed: print("Connection closed") except Exception as e: print(f"Error: {e}") # Usage asyncio.run(listen_to_call("CA123abc...", "burki_your_api_key_here")) ``` ## Error Codes | Code | Description | | - | - | | 1000 | Normal closure | | 1001 | Going away | | 1002 | Protocol error | | 1003 | Unsupported data | | 1006 | Abnormal closure | | 1008 | Authentication failed or access denied | | 1011 | Server error | ## Best Practices 1. **Secure API Keys**: Store API keys securely and never expose them in client-side code 2. **Implement Reconnection**: Use exponential backoff for reconnection attempts 3. **Handle All Message Types**: Process all message types to avoid missing important updates 4. **Validate Call SID**: Ensure Call SID format is valid before connecting 5. **Use Ping/Pong**: Regularly send pings to maintain connection health 6. **Error Handling**: Always handle connection errors and authentication failures gracefully 7. **Use Query Parameters**: Prefer query parameter authentication for simplicity ## Security Considerations * Validate Call SID ownership before connecting * Implement rate limiting in production * Use secure WebSocket connections (WSS) over HTTPS * Consider implementing authentication tokens for sensitive deployments # Core Concepts & Architecture Source: https://docs.burki.dev/architecture Understanding how Burki Voice AI works Understanding these core concepts will help you get the most out of Burki Voice AI! *** ## What is an Assistant? An **Assistant** is a virtual agent that can answer calls, talk to users, and perform tasks using AI. Each assistant has its own configuration for language, voice, hearing, and integrations. * You can create multiple assistants for different use cases (e.g., sales, support, info lines). * Each assistant can have its own settings, phone number, and knowledge base. * Assistants can be updated in real-time without any downtime. *** ## Multi-Tenancy & Organizations * **Organizations:** Group your assistants, team members, and resources under one account. Great for agencies, teams, or businesses with many brands. * **Role-Based Access:** Admins, members, and viewers each have different permissions. * **Resource Isolation:** Each organization's data is completely separate and secure. *** ## Provider Modes Burki offers flexibility in how you connect to AI providers: We manage all provider connections. You select providers and models, we handle the rest. Connect your own API keys. You pay providers directly and maintain full control. * **Cost Control:** Use your existing enterprise pricing with BYO mode * **Try New Models:** Test new AI models as they launch * **Optimize:** Use Burki Cloud for convenience, BYO for specific needs * **Fallbacks:** Set up backup providers for reliability *** ## How a Call Flows Through the System When someone calls your Burki phone number, here's what happens: ```mermaid theme={null} graph TD A[Caller dials number] --> B[Telephony Provider] B --> C[Burki Platform] C --> D[STT: Speech to Text] D --> E[LLM: Generate Response] E --> F[TTS: Text to Speech] F --> G[Caller hears response] E -->|"If tool needed"| H[Execute Tool] H --> E E -->|"If primary fails"| I[Fallback LLM] I --> F ``` ### Component Breakdown | Component | What It Does | Providers | | - | - | - | | **Telephony** | Handles phone calls and audio streaming | Twilio, Vonage, Telnyx | | **STT** | Converts caller's speech to text | Deepgram, ElevenLabs, Azure, Uplift, Speechmatics, Telnyx, Soniox | | **LLM** | Understands context and generates responses | OpenAI, Azure OpenAI, Anthropic, Gemini, Groq, xAI, custom HTTP/WebSocket | | **TTS** | Converts AI response back to speech | ElevenLabs, Deepgram, Cartesia, Azure, Inworld, Resemble, OpenAI, Kokoro, Uplift, Murf, Soniox | | **Tools** | Executes actions (transfers, API calls, etc.) | Built-in + Custom | You can customize every step—choose your favorite providers, set up fallbacks, and fine-tune the experience! *** ## Configuration Sections When setting up an assistant, you'll configure these sections: | Section | What You Configure | | - | - | | **Basic Info** | Name, description, and greeting message | | **AI Configuration** | LLM, TTS, and STT providers and models | | **RAG (Knowledge Base)** | Documents and web pages for context | | **Call Management** | Interruptions, recordings, and call flow | | **Tools** | Actions your assistant can take | | **Phone Numbers** | Which numbers route to this assistant | *** ## System Architecture ```mermaid theme={null} graph TD subgraph users [Users] Web[Web Dashboard] API[REST API] Phone[Phone Calls] end subgraph platform [Burki Platform] Gateway[API Gateway] Core[Core Engine] Audio[Audio Processing] Tools[Tool Execution] end subgraph providers [AI Providers] STT[Speech-to-Text] LLM[Language Models] TTS[Text-to-Speech] end subgraph telephony [Telephony] Twilio[Twilio] Vonage[Vonage] Telnyx[Telnyx] end subgraph storage [Data] DB[(Database)] Files[(File Storage)] end Web --> Gateway API --> Gateway Phone --> telephony telephony --> Gateway Gateway --> Core Core --> Audio Core --> Tools Audio --> STT Audio --> TTS Core --> LLM Core --> DB Core --> Files ``` ### Key Components * **API Gateway**: Handles authentication, rate limiting, and request routing * **Core Engine**: Manages conversation state, assistant configuration, and business logic * **Audio Processing**: Real-time audio streaming with noise reduction * **Tool Execution**: Secure execution environment for custom actions * **Storage**: Encrypted storage for conversations, recordings, and configuration *** ## Data Flow ### Inbound Call 1. **Call arrives** at your phone number via telephony provider 2. **Audio streams** to Burki in real-time over WebSocket 3. **STT converts** speech to text with sub-second latency 4. **LLM generates** a contextual response 5. **TTS converts** response to audio 6. **Audio streams** back to the caller 7. **Call data** is logged for analytics and transcripts ### Outbound Call (API-Initiated) 1. **API request** triggers an outbound call 2. **Burki connects** via telephony provider 3. **Same flow** as inbound once connected 4. **Webhook** notifies your system of call status *** ## Security & Compliance * **Encryption**: All data encrypted at rest (AES-256) and in transit (TLS 1.3) * **API Key Security**: Keys are hashed and never stored in plain text * **Provider Isolation**: BYO keys are encrypted and only used for your requests * **Audit Logs**: Complete audit trail of all system access * **Data Retention**: Configurable retention policies for recordings and transcripts *** ## Next Steps Learn about LLM, TTS, and STT configuration options Connect with external services and APIs # Assistant configuration guide — edit page settings, flows, transfers & latency controls Source: https://docs.burki.dev/assistant-configuration Map every major setting in the Burki assistant edit page to runtime behavior, API fields, and related docs. # Assistant Configuration Guide The assistant edit page is the control surface for a single Burki voice agent. Use this page as the map between what operators see in the dashboard and what happens at runtime. For exact request/response schemas, see [Create Assistant](/api-reference/assistants/create) and [Update Assistant](/api-reference/assistants/update). ## Edit Assistant Sections | Dashboard section | What it controls | Related docs | | - | - | - | | Model & Prompt | System prompt, prompt enhancement, welcome message, LLM provider/model/API key, Azure and org presets, generation settings, LLM fallbacks, custom HTTP/WebSocket LLM endpoints | [LLM Providers](/llm-providers), [AI Configuration](/ai-configuration) | | Voice | TTS provider, model, voice, cloned/custom voice IDs, provider-specific voice tuning, pronunciation dictionaries, background sounds | [TTS Providers](/tts-providers), [Voice Cloning](/voice-cloning) | | Transcription | STT provider/model/language, punctuation, smart formatting, interim results, denoising, turn detection, keyterms, provider-specific options, speculative LLM | [STT Providers](/stt-providers), [Call Management](/call-management) | | Calling | Barge-in style, active listening/backchannels, incomplete utterance detection, conversation continuity, idle/end/transfer messages, timeouts, max call length | [Call Management](/call-management) | | Tools | End call, transfer call, DTMF solver, send SMS, custom tools from the organization library | [Tools](/tools), [Custom tools](/tools/custom-tools-deep-dive) | | Knowledge | RAG enablement, document upload/list/delete, retrieval tuning | [RAG](/rag) | | Post Call | Webhook URL, SMS webhook URL, Messaging Service SID, structured extraction fields and instructions | [Webhooks](/api-reference/introduction#webhooks), [Advanced Features](/advanced-features) | | Safety | Recording, caller-only track, recording disclosure, spam detection, bypass numbers | [Spam Detection](/spam-detection), [Call Management](/call-management) | ## Conversation Flows vs Assistant Settings Conversation Flows and assistants are related but separate: * A **base assistant** owns the model, voice, STT, tools, RAG, recording, safety, and post-call settings. * A **Conversation Flow** owns graph routing: nodes, prompts, global prompt, variables, and transitions. * A phone number can be assigned directly to an assistant or to a flow. When assigned to a flow, `flow_id` is set on the phone number and the direct `assistant_id` assignment is cleared. * The flow editor embeds the same assistant form for the base assistant, so changes to voice/tools/RAG/safety still come from the assistant settings. Use flows when the call needs multiple states or departments, such as intake → billing → support → escalation. Use a single assistant when one prompt can handle the whole call. ## Cold and Warm Transfers The Transfer Call tool supports both transfer modes: | Mode | Behavior | When to use | | - | - | - | | Cold transfer | The caller is transferred immediately to the configured number. The agent receives the call without a prior AI briefing. | Simple handoffs, sales queues, reception desks, or when speed matters more than context. | | Warm transfer | The caller hears hold behavior while the agent is briefed with an AI-generated summary before connection. | Support escalations, high-value sales, healthcare/admin handoffs, or any call where context matters. | Warm transfer options: * `agent_confirmation`: when enabled, the agent must confirm before the caller is connected. * `unavailable_message`: what the caller hears if no agent is available or the warm transfer cannot complete. * `transfer_numbers`: one or more destinations. Keep them in E.164 format where possible. * `scenarios`: plain-language conditions that tell the model when transfer is appropriate. The transfer message shown in **Calling** is what the caller hears before transfer. The transfer tool settings shown in **Tools** determine when and where the transfer happens. ## Speculative LLM / Speculative Execution Speculative LLM is the dashboard label for speculative execution on partial transcripts. When enabled, Burki can start the LLM before the final STT result arrives, reducing dead air after the caller stops speaking. It appears under **Voice → Transcription → Speculative LLM** for non-Flux STT models. Deepgram Flux already has native eager end-of-turn behavior, so the generic speculative LLM controls are hidden for Flux. | Setting | API field | Notes | | - | - | - | | Enable speculative LLM | `stt_settings.speculative_config.enable_speculative_llm` | Master toggle. | | Stream to TTS | `speculative_stream_to_tts` | Aggressive mode. Caller can hear speculative output immediately. Disable for safer buffering until final text confirms. | | Debounce | `speculative_debounce_ms` | Delay after the latest partial transcript before triggering LLM. | | Min words | `speculative_min_words` | Minimum partial transcript length before speculation starts. | | Similarity | `speculative_similarity_threshold` | How close the final transcript must be to confirm the speculative response. | Use speculative LLM on latency-sensitive calls after you have tested interruption handling and STT stability. Avoid aggressive streaming in regulated or high-stakes conversations unless you have verified that partial transcripts are accurate enough. ## Calling Behavior The **Calling** section controls timing and turn-taking: * **Interruption style**: fast barge-in, balanced, or patient listener. These presets set interruption threshold, minimum AI speaking time, cooldown, confidence, filler filtering, and tail protection. * **Active listening**: generates short acknowledgments such as "uh-huh" while the caller speaks. Frequency controls how often the assistant backchannels. * **Incomplete utterance detection**: waits when a caller says partial thoughts like "I want to..." instead of responding too early. * **Conversation continuity**: calls from the same number within the configured continuity window can continue recent context. * **Messages and timeouts**: end-call message, transfer message, idle message, idle timeout, max idle messages, and max call length. * **Advanced interruption overrides**: minimum AI speaking time, cooldown between interruptions, and filler/backchannel filtering. ## Tools and Actions Built-in tools in the edit page: * **End Call**: ends the call when scenario text matches the conversation state. * **Transfer Call**: cold or warm transfer to one or more phone numbers. * **DTMF Solver**: lets the AI send touch-tone digits for IVR navigation. * **Send SMS**: sends confirmations, links, or follow-up text messages. SMS requires a valid messaging setup and carrier compliance where applicable. * **Custom Tools**: assign existing endpoint, Python function, or Lambda tools from the organization tool library and set per-tool timeout seconds. Write scenarios as business conditions, not keywords. For example, use "caller has a billing dispute that requires a human" instead of only "billing". ## Knowledge, Post-Call, and Safety Knowledge base settings: * Enable RAG. * Upload PDFs, DOCX, TXT, Markdown, and image files after saving the assistant. * Tune retrieval with max documents per query, relevance threshold, chunk size, and context window settings. Post-call settings: * `webhook_url`: call events, transcripts, extracted data, and integration payloads. * `sms_webhook_url`: inbound SMS workflow events. * `messaging_service_sid`: Twilio Messaging Service SID when required for SMS. * Structured data extraction fields: name, type, description, required flag, and enum options. Safety settings: * Recording enablement. * Caller-only track: org default, on, or off. * Recording disclosure message and frequency. * Spam detection scenarios: denied patterns and allowed overrides. * Spam evaluation thresholds: max evaluations, minimum confidence, grace duration, transcript length, and caller turns. * Bypass numbers: callers that skip the AI and route directly to transfer. ## Related Surfaces Not Inside Edit Assistant * **Conversation Flow canvas**: node graph, transitions, global prompt, runtime variables, and phone-number assignment to flows. * **Phone numbers page**: assign numbers to assistants or flows, manage providers, and route SIP/Twilio/Telnyx/Vonage numbers. * **History panel**: edit-assistant history, change notes, and rollback for assistant settings. * **Browser test panel**: test an assistant after it has a name, prompt, and voice. # BYO API Keys Source: https://docs.burki.dev/byo-api-keys Bring Your Own (BYO) API Keys lets you use your own provider accounts for TTS, STT, and LLM services. Control costs directly with your provider, leverage enterprise agreements, and maintain full billing transparency. *** ## Overview BYO API Keys gives you flexibility in how you pay for AI services: Pay your providers directly at your negotiated rates. Use existing volume discounts or enterprise contracts. Mix managed and BYO keys across different assistants. *** ## How It Works When a call uses a provider service, Burki checks for API keys in this order: ```mermaid theme={null} flowchart TD A[Need Provider API Key] --> B{Assistant-level BYO key?} B -->|Yes| C[Use Assistant's BYO Key] B -->|No| D{Organization-level key?} D -->|Yes| E[Use Organization Key] D -->|No| F[Use Burki Managed Key] ``` 1. **Assistant-level BYO keys** (highest priority) 2. **Organization-level credentials** 3. **System default keys** (Burki managed) This allows you to use BYO keys for specific assistants while others use managed keys. At the organization level, Burki also stores managed-service preferences for LLM, TTS, STT, and carrier usage. These are exposed through the billing managed-services API (`GET`/`PATCH /v1/billing/managed-services`) and determine which service categories Burki manages for hybrid billing. For organization-level API key, fallback key, and provider credential endpoints, see [Account and Organization API](/api-reference/account/overview). Assistant-level `byo_api_keys` and organization-level fallback keys are separate mechanisms: assistant keys override for a specific assistant, while fallback keys provide provider-level backup capacity for the organization. *** ## Supported Providers Configure BYO keys for the following providers: | Provider | Service | Required Fields | | - | - | - | | **ElevenLabs** | TTS | `api_key` | | **Deepgram** | STT, TTS | `api_key` | | **Cartesia** | TTS | `api_key` | | **OpenAI** | TTS, LLM | `api_key` | | **Anthropic** | LLM | `api_key` | | **Gemini** | LLM | `api_key` | | **xAI** | LLM | `api_key` | | **Groq** | LLM | `api_key` | | **Azure Speech** | TTS, STT | `api_key`, `region` | | **Azure OpenAI** | LLM | `api_key`, `base_url`, deployment `model` | | **Inworld** | TTS | `bearer_token`, `workspace_id` | | **Resemble AI** | TTS | `api_key` | | **Speechmatics** | STT | `api_key` | | **Soniox** | STT, TTS | `api_key` | | **Uplift** | STT, TTS | `api_key` | | **Murf** | TTS | `api_key` | | **Telnyx** | STT, Telephony, SMS | Telnyx API key / org carrier credentials | *** ## Configuration ### TTS Provider Keys Add BYO keys in your assistant's TTS settings: ```json theme={null} { "tts_settings": { "provider": "elevenlabs", "byo_api_keys": { "elevenlabs": { "api_key": "your-elevenlabs-api-key" } } } } ``` ```json theme={null} { "tts_settings": { "provider": "deepgram", "byo_api_keys": { "deepgram": { "api_key": "your-deepgram-api-key" } } } } ``` ```json theme={null} { "tts_settings": { "provider": "cartesia", "byo_api_keys": { "cartesia": { "api_key": "your-cartesia-api-key" } } } } ``` ```json theme={null} { "tts_settings": { "provider": "openai", "byo_api_keys": { "openai": { "api_key": "your-openai-api-key" } } } } ``` ### STT Provider Keys Add BYO keys in your assistant's STT settings: See the [STT Providers & Settings](/stt-providers) guide for provider-specific model, endpointing, VAD, and silence controls. ```json theme={null} { "stt_settings": { "provider": "deepgram", "byo_api_keys": { "deepgram": { "api_key": "your-deepgram-api-key" } } } } ``` ### Azure Speech (Special Configuration) Azure Speech requires both an API key and a region: ```json theme={null} { "tts_settings": { "provider": "azure", "byo_api_keys": { "azure": { "api_key": "your-azure-speech-key", "region": "eastus" } } } } ``` Azure region must match where your Speech resource is deployed. Common regions: `eastus`, `westus2`, `westeurope`, `southeastasia`. ### Inworld (Bearer Token) Inworld uses a bearer token and workspace ID: ```json theme={null} { "tts_settings": { "provider": "inworld", "byo_api_keys": { "inworld": { "bearer_token": "your-inworld-bearer-token", "workspace_id": "your-workspace-id" } } } } ``` *** ## Dashboard Configuration Navigate to your assistant's configuration page. Find the TTS or STT configuration section. Toggle "Use my own API key" or similar option. Paste your API key (and region if required). Save settings and make a test call to verify. *** ## Security Your API keys are protected with encryption and access controls: All API keys are encrypted using AES-256 encryption before storage. Keys are decrypted only when needed for API calls and never stored in plaintext. BYO API keys are never returned in API responses. When you retrieve assistant settings, key fields show masked values like `***...***` or are omitted entirely. Key usage is logged for security auditing: * When keys are added or updated * Which services use which keys * Failed authentication attempts All API calls using your keys are made over HTTPS/TLS 1.3. Keys are transmitted securely to provider endpoints. *** ## Cost Implications **You pay the provider directly:** * Direct billing from ElevenLabs, Deepgram, etc. * Your negotiated rates apply * No markup from Burki on provider costs * Burki charges only platform fees **Best for:** * High volume usage * Existing enterprise agreements * Organizations requiring direct vendor relationships **Burki provides and bills:** * Actual provider usage at cost, with no additional markup * Single invoice from Burki * No need to manage multiple vendor accounts * Included usage tracking and analytics **Best for:** * Getting started quickly * Lower volume usage * Simplified billing **Mix and match:** * Use BYO keys for high-volume providers * Use managed for occasional services * Different settings per assistant **Example:** * BYO: ElevenLabs (high TTS volume) * Managed: Deepgram STT (lower volume) *** ## Testing Your Keys After configuring BYO keys, verify they work: ### Quick Test 1. Make a test call to your assistant 2. Check the call logs for provider usage 3. Verify the call completed successfully 4. Check your provider dashboard for the corresponding API call ### Common Issues **Symptom:** Calls fail immediately or fall back to managed keys **Solution:** * Verify the key is correct (no extra spaces) * Check key hasn't expired * Confirm key has required permissions * Ensure key is for the correct environment (production vs. sandbox) **Symptom:** Azure calls fail with region errors **Solution:** * Match region exactly to your Azure Speech resource * Use lowercase (e.g., `eastus` not `East US`) * Check Azure portal for the correct region name **Symptom:** Some calls succeed, others fail **Solution:** * Check your provider's rate limits * Upgrade your provider plan if needed * Consider spreading load across multiple keys **Symptom:** Specific operations fail **Solution:** * Verify API key has access to required services * For ElevenLabs: Ensure voice cloning permissions if using cloned voices * For Deepgram: Check STT and TTS are both enabled if using both *** ## Provider Setup Guides 1. Log in to [ElevenLabs](https://elevenlabs.io) 2. Go to **Profile** > **API Keys** 3. Create a new API key or copy existing 4. Ensure your plan supports the voices you want to use **Permissions needed:** Text-to-Speech generation 1. Log in to [Deepgram Console](https://console.deepgram.com) 2. Go to **API Keys** 3. Create a new key with STT and/or TTS permissions 4. Copy the key (shown only once) **Permissions needed:** `listen` for STT, `speak` for TTS 1. Log in to [Cartesia](https://play.cartesia.ai) 2. Navigate to API settings 3. Generate a new API key 4. Copy and store securely **Permissions needed:** Speech synthesis 1. Create or open an Azure Speech resource using the [Azure AI Speech quickstart](https://learn.microsoft.com/azure/ai-services/speech-service/get-started-speech-to-text) 2. Create or navigate to your Speech resource 3. Go to **Keys and Endpoint** 4. Copy Key 1 or Key 2 5. Note the region (e.g., `eastus`) **Permissions needed:** Speech Services access 1. Open your OpenAI Platform dashboard 2. Go to **API Keys** 3. Create a new secret key 4. Copy immediately (not shown again) **Permissions needed:** Model access for TTS models (tts-1, tts-1-hd) *** ## Best Practices * **Rotate keys periodically**: Update API keys every 90 days for security * **Use separate keys**: Create dedicated keys for Burki rather than sharing with other applications * **Monitor usage**: Check provider dashboards regularly for unexpected usage * **Set up alerts**: Configure billing alerts with your providers * **Test after changes**: Always verify calls work after updating keys * **Keep backups**: Store keys securely in a password manager When testing BYO keys, start with a low-volume assistant before rolling out to production traffic. This catches configuration issues without impacting critical workloads. *** ## API Reference ### Setting BYO Keys via API ```bash theme={null} curl -X PUT "https://api.burki.dev/api/v1/assistants/{assistant_id}" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tts_settings": { "provider": "elevenlabs", "byo_api_keys": { "elevenlabs": { "api_key": "your-elevenlabs-key" } } }, "stt_settings": { "provider": "deepgram", "byo_api_keys": { "deepgram": { "api_key": "your-deepgram-key" } } } }' ``` ### Removing BYO Keys To revert to managed keys, set `byo_api_keys` to `null` or an empty object: ```bash theme={null} curl -X PUT "https://api.burki.dev/api/v1/assistants/{assistant_id}" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tts_settings": { "byo_api_keys": null } }' ``` BYO keys are not included in assistant export/import to prevent accidental key exposure. Re-enter keys after importing an assistant. # BYO SIP Trunking Source: https://docs.burki.dev/byo-sip-trunk Connect your own SIP carriers to Burki through the SIP bridge. BYO SIP trunking lets you connect carrier trunks directly to Burki while still using Burki assistants, routing, transcripts, recordings, billing, and observability. ## Architecture ```mermaid theme={null} flowchart LR Carrier[SIP carrier] <--> Bridge[SIP bridge + FreeSWITCH] Bridge <--> Burki[Burki backend] Burki --> Assistant[Voice assistant pipeline] ``` The bridge handles SIP signaling and audio adaptation. Burki handles assistant lookup, call state, streaming, transcripts, and AI orchestration. ## When to Use BYO SIP * You already have carrier contracts or number inventory. * You need SIP-level control over routing, failover, or origination. * You want a self-hosted or hybrid telephony path. * You need to keep carrier billing outside Burki managed carrier costs. ## Setup Flow Add one or more trunks through the SIP Trunks API or dashboard. Each trunk includes gateway, username, password, priority, enabled status, and registration behavior. Burki pushes enabled trunk configuration to the SIP bridge. The bridge generates FreeSWITCH gateway config and reloads SIP profiles. Assign organization phone numbers to the relevant `sip_trunk_id` so outbound calls and routing resolve the correct carrier path. Place inbound and outbound test calls, then inspect call logs, transcripts, recordings, and SIP bridge logs. ## PBX Network Setup For the current hosted SIP bridge, configure your PBX/carrier to talk to Burki at: ```text theme={null} Burki SIP bridge IP: 52.21.157.171 SIP signaling: UDP/TCP 5060 SIP TLS: TCP 5061, if your trunk uses TLS RTP media: UDP 10000-20000 ``` Allowlist `52.21.157.171` on your PBX firewall and SIP ACLs for both signaling and media. Do not expose your PBX to all public SIP traffic if your PBX supports source IP restrictions. | Direction | PBX/Carrier Requirement | Burki Requirement | | - | - | - | | Inbound calls to Burki | Send INVITEs to `52.21.157.171:5060` or your assigned bridge address. Allow RTP from `52.21.157.171` on UDP `10000-20000`. | Phone number must exist in Burki and be assigned to an assistant or flow. | | Outbound calls from Burki | Accept SIP INVITEs and RTP from `52.21.157.171`. If Burki registers to your PBX, allow the configured SIP username from this bridge IP. | SIP trunk `gateway` should be your PBX/carrier host and port, such as `pbx.example.com:5060` or `138.197.131.209:6061`. | | Registration-based trunks | Enable registration on the PBX for the username/password you give Burki. | Set `register: true` on the SIP trunk. | | IP-auth trunks | Allow `52.21.157.171` as a trusted peer. | Set `register: false` and configure the gateway host/IP. | Example PBX-facing trunk record: ```json theme={null} { "name": "Customer PBX", "gateway": "138.197.131.209:6061", "username": "9228", "password": "sip-password", "priority": 1, "enabled": true, "register": true, "provider": "freepbx" } ``` In this example, Burki registers to the customer's PBX at `138.197.131.209:6061` with username `9228`, while the customer's PBX sends inbound calls to Burki at `52.21.157.171:5060`. ## Multiple DIDs on One PBX Extension Burki routes calls by the real called phone number, not by short extension digits alone. Forwarding many numbers to one PBX extension works only if the PBX preserves the original called DID in SIP signaling. When a PBX forwards several DIDs to one extension, ask it to include one of these headers on the INVITE to Burki: | Header / Field | Purpose | | - | - | | `Diversion` | Preferred for forwarded calls; include the original called DID. | | `History-Info` | Standard history header that can carry the original called number. | | `X-Original-Called`, `X-Original-Number`, `X-Forwarded-To`, or `X-Original-To` | Accepted custom headers for PBX-specific setups. | | `P-Called-Party-ID` | Accepted when the carrier/PBX uses this to preserve called-party identity. | | Request-URI or `To` user | Also works if the PBX sends the real E.164 DID instead of only the short extension. | Example forwarded INVITE target that works: ```text theme={null} INVITE sip:9228@52.21.157.171:5060 Diversion: ;reason=unconditional ``` Without one of those headers, a call that arrives only as `sip:9228@52.21.157.171` is ambiguous once more than one Burki number is assigned behind that extension. Burki will not guess between multiple assigned DIDs because guessing can send the caller to the wrong AI agent. For FreePBX, enable Diversion/History-Info or add a custom header on each inbound route before forwarding to the Burki trunk. After the PBX sends a test call, Burki support can confirm whether the original DID is visible in the captured INVITE. ## Routing and Failover SIP trunks have a `priority` field. Lower values are preferred first. Disable a trunk to remove it from routing without deleting credentials. ```json theme={null} { "name": "Primary Carrier", "gateway": "sip.example-carrier.com", "username": "sip-user", "password": "sip-password", "priority": 1, "enabled": true, "register": true, "provider": "example-carrier" } ``` ## Bridge Sync The backend syncs organization SIP config to the bridge using: ```text theme={null} POST {SIP_BRIDGE_URL}/api/sync-organization Authorization: Bearer {SIP_BRIDGE_API_KEY} ``` The bridge stores/generated gateway configuration and reloads FreeSWITCH. A successful API response from Burki means the org config was updated and sync was attempted; check bridge logs for FreeSWITCH reload failures. ## API References * [SIP Trunks API](/api-reference/sip-trunks/overview) * [SIP Webhooks and Prewarm](/api-reference/webhooks/sip-webhooks) * [Organization phone numbers](/api-reference/organization/list-phonenumbers) ## Troubleshooting | Symptom | Check | | - | - | | Trunk does not register | Gateway, username/password, `register`, bridge FreeSWITCH logs | | Calls route through the wrong carrier | Phone number `sip_trunk_id`, trunk `priority`, enabled status | | Sync succeeds but calls fail | Bridge reload logs, generated gateway XML, carrier ACL/auth rules | | Audio format issues | SIP bridge audio adapter logs and codec settings | # Call Management Source: https://docs.burki.dev/call-management Configure interruption handling, timeouts, and call flow control for natural conversations **Think of this as traffic control for your phone calls!** These settings determine how smoothly conversations flow between your AI and callers. *** ## What is Call Management? Call Management controls the **conversation flow** during phone calls. It's separate from AI Configuration (which handles what the AI says) - this section controls **when** and **how** the AI responds to callers. Just like traffic lights control car flow, these settings control conversation flow between your AI assistant and callers. *** ## Interruption Settings **Most Important Section**: These settings directly affect how natural your conversations feel! ### Interruption Style The dashboard exposes three presets: | Style | Best for | Behavior | | - | - | - | | Fast barge-in | Sales, demos, fast support | Stops playback quickly when the caller starts speaking. With Deepgram Flux, playback stops from the provider's speech-start signal. | | Balanced | Most assistants | Filters filler sounds and short noise before yielding. | | Patient listener | Healthcare, senior callers, noisy rooms, IVR replacement | Waits longer before treating caller speech as a real interruption. | The advanced fields below override the selected preset for providers that do not have native turn detection. ### Interruption Threshold **Simple Explanation**: How many words a caller needs to say before the AI stops talking and listens. **Technical Details**: * **Measured in**: Number of words * **Default**: 3 words * **Range**: 1-10 words (recommended) **Real Example**: * Threshold = 3: Caller says "Wait, I have a..." → AI stops talking * Threshold = 1: Caller says "Um..." → AI stops talking (very sensitive) * Threshold = 5: Caller says "Actually, I need to tell you..." → AI stops talking **When to Adjust**: * **Lower (1-2 words)**: For very responsive, human-like conversations * **Higher (4-5 words)**: For formal business calls where AI should finish sentences * **Much higher (6+ words)**: For presentations or when AI shouldn't be interrupted **Technical Note**: The system counts words by splitting text on spaces. Both word count AND min speaking time must be met for a valid interruption. ### Min Speaking Time **Simple Explanation**: How long the AI must talk before it can be interrupted. **Technical Details**: * **Measured in**: Seconds (decimals allowed like 0.5) * **Default**: 0.5 seconds * **Range**: 0.1-3.0 seconds (recommended) **Real Example**: * Min Speaking Time = 0.5s: AI says "Hello, how can I—" → Caller can interrupt * Min Speaking Time = 2.0s: AI says "Hello, how can I help you today? I'm here to assist with—" → Now caller can interrupt **When to Adjust**: * **Lower (0.1-0.3s)**: For very natural, back-and-forth conversations * **Higher (1-2s)**: For business calls where AI should complete thoughts * **Much higher (2-3s)**: For announcements or important information delivery **Technical Note**: The AI must be speaking for at least this duration before any interruption is considered valid, regardless of word count. ### Interruption Cooldown **Simple Explanation**: How long to pause after a caller interrupts before the AI starts talking again. **Technical Details**: * **Measured in**: Seconds (decimals allowed) * **Default**: 2.0 seconds * **Range**: 0.5-5.0 seconds (recommended) **Real Example**: * Cooldown = 2.0s: Caller interrupts → AI pauses for 2 seconds → AI responds with buffered speech * Cooldown = 0.5s: Very quick responses (might feel rushed) * Cooldown = 4.0s: Longer pauses (might feel awkward) **Technical Note**: During cooldown, any additional speech from the caller is buffered and processed together when the cooldown period ends. **When to Adjust**: * **Lower (0.5-1s)**: For fast-paced conversations or tech support * **Higher (3-4s)**: For emotional or sensitive conversations * **Much higher (5s+)**: For elderly callers or those needing processing time ### Active Listening and Incomplete Utterances **Active listening** enables short backchannel acknowledgments such as "uh-huh" or "mm-hmm" while the caller is speaking. Use the frequency control to keep these cues rare, occasional, moderate, frequent, or aggressive. **Incomplete utterance detection** helps prevent the assistant from answering too early when a caller says a partial phrase like "I want to..." or "Can you help me with...". **Conversation continuity** lets calls from the same number within a short window continue with recent context instead of starting completely fresh. **Natural Conversations**: Threshold=2, Speaking=0.3s, Cooldown=1.5s **Business Formal**: Threshold=4, Speaking=1.0s, Cooldown=2.5s **Customer Support**: Threshold=3, Speaking=0.5s, Cooldown=2.0s (default) *** ## Timeout Settings ### Idle Timeout **Simple Explanation**: How long to wait for the caller to respond before asking "Are you still there?" **Technical Details**: * **Measured in**: Seconds * **Default**: 30 seconds * **Range**: 10-120 seconds (recommended) **Real Example**: * Timeout = 30s: AI asks question → 30 seconds of silence → AI says idle message * Timeout = 10s: Very short patience (good for quick interactions) * Timeout = 60s: Very patient (good for complex questions) **When to Adjust**: * **Lower (10-20s)**: For simple, quick interactions * **Higher (45-60s)**: For complex questions or elderly callers * **Much higher (90s+)**: For forms or detailed information gathering **Technical Note**: The timer resets when the caller completes an utterance (speech\_final) or when the AI responds with meaningful content. ### Max Idle Messages **Simple Explanation**: How many "Are you still there?" messages to send before ending the call. **Technical Details**: * **Measured in**: Number of messages * **Default**: 3 messages * **Range**: 1-5 messages (recommended) **Real Example**: * Max = 3: AI asks "Are you there?" → Silence → AI asks again → Silence → AI asks again → Silence → Call ends * Max = 1: AI asks once, then ends call (not very patient) * Max = 5: AI is very patient (might annoy callers) **When to Adjust**: * **Lower (1-2)**: For business efficiency or when call time is expensive * **Higher (4-5)**: For customer service or when you don't want to lose calls **Quick Service**: Idle=15s, Max Messages=2 **Standard Support**: Idle=30s, Max Messages=3 (default) **Patient Service**: Idle=45s, Max Messages=4 *** ## Call Control Messages ### End Call Message **Simple Explanation**: The last thing callers hear when the call ends (either by AI decision or timeout). **Default**: "Thank you for calling. Goodbye!" **Examples**: * Business: "Thank you for calling \[Company]. Have a great day!" * Support: "Your issue has been resolved. Thank you for calling!" * Appointment: "Your appointment is confirmed. We'll see you soon!" **Best Practices**: * Keep it under 10 words for clarity * Include your company name if branding is important * Match the tone of your assistant's personality ### Transfer Call Message **Simple Explanation**: What the AI says when transferring a caller to a human agent. **Default**: "Please hold while I transfer your call." **Examples**: * Professional: "Let me connect you with a specialist who can help." * Urgent: "I'm transferring you to our emergency support team now." * Specific: "Connecting you to our billing department. Please hold." **Best Practices**: * Explain WHO they're being transferred to * Keep them calm with reassuring language * Include estimated wait time if known ### Idle Message **Simple Explanation**: What the AI says when there's been silence for too long. **Default**: "Are you still there? I'm here to help if you need anything." **Examples**: * Friendly: "Hello? I'm still here if you have more questions!" * Professional: "Are you still on the line? I'm ready to assist." * Helpful: "Take your time. Let me know when you're ready to continue." **Best Practices**: * Sound patient, not annoyed * Offer help or reassurance * Keep it conversational and warm *** ## Recording Settings **Configurable per assistant**: Recording can be enabled or disabled from the Safety section. When enabled, Burki can store mixed call audio, optionally store a caller-only track, and play a disclosure message before recording. Recording-related settings now live in **Safety**: * **Enable recording:** store call audio for review and compliance. * **User-only track:** use organization default, force on, or force off for a caller-only recording. * **Recording disclosure:** play a disclosure message every call or only the first call per caller. *** ## Common Configuration Scenarios ### Customer Service **Goal**: Helpful but efficient support ```yaml theme={null} Interruption Threshold: 3 words Min Speaking Time: 0.5 seconds Interruption Cooldown: 2.0 seconds Idle Timeout: 30 seconds Max Idle Messages: 3 ``` **Why**: Balanced responsiveness with professional patience ### Sales Calls **Goal**: Let prospects talk, show you're listening ```yaml theme={null} Interruption Threshold: 2 words Min Speaking Time: 0.3 seconds Interruption Cooldown: 1.5 seconds Idle Timeout: 45 seconds Max Idle Messages: 4 ``` **Why**: Very responsive to show engagement, patient to let them think ### Appointment Booking **Goal**: Clear information exchange ```yaml theme={null} Interruption Threshold: 4 words Min Speaking Time: 1.0 seconds Interruption Cooldown: 2.5 seconds Idle Timeout: 20 seconds Max Idle Messages: 2 ``` **Why**: AI needs to finish instructions, but move efficiently through booking ### Tech Support **Goal**: Handle frustrated callers patiently ```yaml theme={null} Interruption Threshold: 2 words Min Speaking Time: 0.4 seconds Interruption Cooldown: 3.0 seconds Idle Timeout: 40 seconds Max Idle Messages: 4 ``` **Why**: Very responsive to interruptions, longer cooldowns for emotional regulation *** ## Troubleshooting Common Issues **Problem**: Interruption threshold too low or min speaking time too short **Solutions**: * Increase interruption threshold to 4-5 words * Increase min speaking time to 1.0-1.5 seconds * Callers won't interrupt as quickly **Test**: Make a call and see if AI can complete full sentences **Problem**: Interruption cooldown too long or threshold too high **Solutions**: * Decrease interruption cooldown to 1.0-1.5 seconds * Decrease interruption threshold to 2-3 words * AI will feel more responsive and natural **Test**: Try interrupting mid-sentence - AI should stop and respond quickly **Problem**: Idle timeout too short or max idle messages too low **Solutions**: * Increase idle timeout to 45-60 seconds * Increase max idle messages to 4-5 * Give callers more time to think and respond **Test**: Stay silent during a call - should get multiple "are you there?" messages **Problem**: Idle timeout too long or max idle messages too high **Solutions**: * Decrease idle timeout to 15-20 seconds * Decrease max idle messages to 1-2 * Calls will end more efficiently **Test**: Simulate an unresponsive caller - call should end reasonably quickly *** ## Best Practices 1. **Start with defaults** and adjust based on real call feedback 2. **Test with real users** - your perception differs from callers' 3. **Match your business style** - formal vs casual affects all settings 4. **Monitor call recordings** to see where conversations break down 5. **Adjust gradually** - small changes have big impacts on conversation feel **Pro Tip**: Record yourself having a conversation with your assistant, then listen back. You'll immediately hear what needs adjusting! *** **Ready to fine-tune your AI's brain?** Check out [AI Configuration](/ai-configuration) to optimize your LLM, TTS, and STT settings. **Need better speech detection?** See [STT Providers & Settings](/stt-providers) for endpointing, VAD, and silence threshold tuning. **Need live monitoring?** See [Live Transcript WebSocket](/live-transcript) for real-time call monitoring and transcript streaming. **Need voice- and audio-specific tuning?** Use [Voice tuning](/tts-providers/voice-tuning) for TTS controls, then refine STT/TTS and interruptions in [AI Configuration](/ai-configuration). # Campaign Management Source: https://docs.burki.dev/campaigns Automate outbound calls and SMS with AI-powered campaigns Campaign Management allows you to automate outbound voice calls and SMS messaging at scale. Create personalized campaigns with your AI assistant, import contacts, schedule execution, and monitor progress in real-time. *** ## What are Campaigns? A **Campaign** is an automated outreach system that connects your AI assistant with a list of contacts. Campaigns can: * Make **outbound voice calls** with your AI assistant * Send **bulk SMS messages** with personalized content * Combine both for **mixed campaigns** Each contact receives a personalized experience using template variables from your imported data. ```mermaid theme={null} flowchart LR A[Import Contacts] --> B[Configure Templates] B --> C[Schedule Campaign] C --> D[Execute Calls/SMS] D --> E[Monitor Progress] E --> F[Analyze Results] ``` *** ## Campaign Types **Outbound voice calling** with your AI assistant. * AI makes calls to each contact in your list * Personalized welcome messages and conversation flow * Handles voicemail detection automatically * Records calls and generates transcripts **Best for:** Appointment reminders, sales outreach, customer follow-ups, surveys **Bulk SMS messaging** with personalized content. * Send templated SMS messages to contacts * Supports variable substitution for personalization * Track delivery status and responses **Best for:** Promotional messages, appointment confirmations, notifications **Combines voice and SMS** for comprehensive outreach. * Start with a call, follow up with SMS (or vice versa) * Different messaging per channel * Unified tracking and analytics **Best for:** Multi-touch outreach, lead nurturing, complex workflows *** ## Contact Management ### Importing Contacts Import your contacts from CSV or JSON files. The system automatically: * Validates phone number formats (converts to E.164) * Detects duplicate contacts * Maps columns to contact fields * Extracts variables for templating **Supported Formats:** * **CSV**: Comma-separated values with header row * **JSON**: Array of contact objects Your CSV should have headers that map to contact fields: ```csv theme={null} phone_number,name,company,appointment_date +15551234567,John Smith,Acme Corp,2024-02-15 +15559876543,Jane Doe,Tech Inc,2024-02-16 ``` **Required columns:** * `phone_number` (or `phone`, `mobile`, `cell`) **Recommended columns:** * `name` (or `full_name`, `contact_name`) * Any custom fields for template variables The system suggests mappings based on column names: | CSV Column | Maps To | | - | - | | phone, phone\_number, mobile | `phone_number` | | name, full\_name, contact\_name | `name` | | email, email\_address | `email` | | company, organization | `company` | Custom columns become template variables automatically. ### Contact Statuses | Status | Description | | - | - | | `pending` | Waiting to be contacted | | `in_progress` | Currently being contacted | | `completed` | Successfully contacted | | `failed` | Contact attempt failed | | `no_answer` | Call not answered | | `busy` | Line was busy | | `voicemail` | Reached voicemail | | `skipped` | Skipped (DNC list, etc.) | *** ## Template System Campaigns use **Jinja2-style templating** for personalized messages. Variables from your contact data are automatically available. ### Variable Syntax Use double curly braces to insert variables: ```text theme={null} Hi {{name}}, this is {{assistant_name}} from {{company}}. ``` ### Built-in Filters | Filter | Description | Example | | - | - | - | | `phone_format` | Format phone numbers | `{{phone_number\|phone_format}}` | | `title_case` | Capitalize words | `{{name\|title_case}}` | | `upper` | UPPERCASE | `{{company\|upper}}` | | `lower` | lowercase | `{{email\|lower}}` | ### System Variables These variables are always available: | Variable | Description | | - | - | | `assistant_name` | Name of your AI assistant | | `company` | Your company name | | `name` | Contact's name (falls back to "there") | ### Fallback Values Define fallback values for missing variables: ```json theme={null} { "fallback_values": { "name": "there", "company": "our company", "appointment_time": "your scheduled time" } } ``` ### Template Types | Template | Used For | | - | - | | `welcome_message_template` | Opening message when call connects | | `agenda_template` | Purpose/agenda of the call | | `end_call_message_template` | Closing message | | `sms_message_template` | SMS message content | **Welcome Message:** ```text theme={null} Hi {{name|title_case}}, this is {{assistant_name}} calling from {{company}}. I'm reaching out about your appointment on {{appointment_date}}. ``` **With fallbacks:** ```text theme={null} Hi {{name|default('there')}}, this is {{assistant_name}} from {{company|default('our team')}}. ``` *** ## Scheduling Campaigns support flexible scheduling options: ### Schedule Types Campaign starts execution right away when you click "Start". ```json theme={null} { "schedule_type": "immediate" } ``` Campaign runs at a specific date and time. ```json theme={null} { "schedule_type": "scheduled", "scheduled_at": "2024-02-15T09:00:00Z", "timezone": "America/New_York" } ``` Campaign runs on a repeating schedule. ```json theme={null} { "schedule_type": "recurring", "recurring_config": { "frequency": "weekly", "interval": 1, "days_of_week": [1, 2, 3, 4, 5], "time": "09:00", "time_zone": "America/New_York", "end_date": "2024-12-31" } } ``` **Frequency options:** `daily`, `weekly`, `monthly` ### Time Windows Respect calling hours by configuring allowed time windows: ```json theme={null} { "execution_settings": { "allowed_hours": { "start": "09:00", "end": "17:00", "days": ["monday", "tuesday", "wednesday", "thursday", "friday"] }, "time_zone": "America/New_York" } } ``` *** ## Execution Settings Control how your campaign executes: | Setting | Description | Default | | - | - | - | | `max_attempts` | Maximum call attempts per contact | 3 | | `retry_delay_minutes` | Wait time between retries | 30 | | `concurrent_limit` | Max simultaneous calls | 5 | | `rate_limit_per_minute` | Max calls initiated per minute | 10 | ### Voicemail Detection (AMD) For call campaigns, enable automatic voicemail detection: ```json theme={null} { "advanced_settings": { "enable_voicemail_detection": true, "leave_voicemail": true, "voicemail_message": "Hi {{name}}, this is {{assistant_name}}. Please call us back at..." } } ``` *** ## Monitoring & Analytics ### Real-Time Progress Monitor your campaign in real-time via WebSocket or REST API: * **Total contacts** - Number of contacts in campaign * **Completed** - Successfully contacted * **Failed** - Contact attempts that failed * **Pending** - Waiting to be contacted * **Processing** - Currently being contacted ### WebSocket Updates Connect to the WebSocket endpoint for live updates: ```javascript theme={null} const ws = new WebSocket(`wss://api.burki.dev/ws/campaigns/${campaignId}/progress?token=${apiKey}`); ws.onmessage = (event) => { const data = JSON.parse(event.data); if (data.type === 'progress_update') { console.log(`Progress: ${data.data.progress.completed}/${data.data.progress.total_contacts}`); } }; ``` ### Metrics | Metric | Description | | - | - | | `completion_rate` | Percentage of contacts processed | | `success_rate` | Successful contacts / total attempted | | `answer_rate` | Calls answered / total calls made | | `average_duration` | Average call duration (seconds) | | `total_cost` | Estimated total cost | *** ## Campaign Lifecycle ```mermaid theme={null} stateDiagram-v2 [*] --> Draft: Create Draft --> Scheduled: Schedule/Start Scheduled --> Running: Execute Running --> Paused: Pause Paused --> Running: Resume Running --> Completed: All Done Running --> Cancelled: Stop Scheduled --> Cancelled: Cancel Completed --> [*] Cancelled --> [*] ``` | Status | Description | | - | - | | `draft` | Campaign created, not yet started | | `scheduled` | Campaign scheduled for future execution | | `running` | Campaign actively executing | | `paused` | Campaign paused, can resume | | `completed` | All contacts processed | | `cancelled` | Campaign stopped before completion | | `failed` | Campaign encountered fatal error | *** ## Best Practices * Only contact people who have opted in * Respect Do-Not-Call lists * Call during reasonable hours (typically 8 AM - 9 PM local time) * Provide opt-out mechanisms * Keep records of consent * **Best days:** Tuesday through Thursday * **Best times:** 10 AM - 12 PM, 2 PM - 4 PM local time * Avoid Monday mornings and Friday afternoons * Consider your audience's timezone * Keep messages concise and clear * Personalize with contact data * Set appropriate fallback values * Test templates before launching * Use professional, conversational tone * Start with lower concurrent limits and increase gradually * Monitor answer rates to optimize timing * Use retry logic for no-answers * Segment contacts for targeted campaigns *** ## Quick Start 1. **Create a campaign** with your assistant and campaign type 2. **Import contacts** from CSV or JSON 3. **Configure templates** with personalization variables 4. **Set scheduling** (immediate or scheduled) 5. **Start the campaign** and monitor progress 6. **Analyze results** and optimize Use the WebSocket endpoint to build real-time monitoring dashboards for your campaigns. # Changelog Source: https://docs.burki.dev/changelog What's new in Burki Voice AI Stay up-to-date with the latest features, improvements, and bug fixes in Burki Voice AI. We're constantly improving the platform based on user feedback and emerging technologies. *** ## January 2026 ### New Features * **Pricing & Billing System**: Complete prepaid wallet system with auto top-up * **Hybrid Mode**: Mix BYO and managed providers for maximum flexibility * **Free Trial**: 200 connected trial minutes for new organizations * **Vonage Integration**: Added Vonage as a telephony provider option * **Cartesia TTS**: New high-quality TTS provider option ### Improvements * **Dashboard Redesign**: Cleaner, more intuitive assistant configuration * **Wallet UI**: Real-time balance tracking and usage breakdowns * **Provider Selection**: Easier switching between Burki Cloud and BYO modes *** ## December 2025 ### New Features * **Multi-Provider TTS Support**: Added support for Deepgram, Inworld, and Resemble TTS providers * **Enhanced STT Models**: Support for Deepgram Nova-3 with keyterms functionality * **Advanced Tools Configuration**: Improved end call and transfer call tools with custom scenarios * **Audio Denoising**: Real-time noise reduction using RNNoise for better transcription accuracy * **Background Sound Support**: Add ambient audio to create realistic call environments ### Improvements * **Provider Fallback System**: Automatic fallback to backup providers when primary fails * **Enhanced Voice Selection**: Expanded voice options across all TTS providers * **Improved Interruption Handling**: Better management of conversation flow and overlapping speech * **Recording Enhancements**: Support for MP3 format with quality improvements * **UI/UX Improvements**: Streamlined assistant configuration interface ### Bug Fixes * Fixed WebSocket connection stability issues * Resolved audio quality problems in noisy environments * Fixed provider switching edge cases * Improved error handling for API failures *** ## October 2025 ### New Features * **Multi-Provider LLM Support**: Added support for Anthropic, Gemini, xAI, and Groq * **RAG (Knowledge Base)**: Upload documents and web pages for context-aware responses * **Data Extraction**: Automatically extract structured data from conversations * **Webhook Integration**: Real-time notifications for call events * **Outbound Calling**: AI-initiated calls with custom agendas ### Improvements * **Enhanced Security**: Improved authentication and data protection * **Better Analytics**: More detailed call metrics and reporting * **Performance Optimizations**: Faster response times and reduced latency *** ## August 2025 ### New Features * **Multi-Tenant Support**: Isolated environments for multiple organizations * **Custom Voice Cloning**: Support for custom voice models * **Advanced Call Controls**: Improved call flow management * **S3 Integration**: Cloud storage for call recordings ### Improvements * **Dashboard Enhancements**: Redesigned configuration interface * **API Improvements**: More robust and comprehensive API endpoints *** ## June 2025 ### Initial Launch * **Core Voice AI**: Full voice assistant functionality * **Telephony Integration**: Twilio phone call handling and media streaming * **ElevenLabs TTS**: High-quality text-to-speech * **Deepgram STT**: Real-time speech-to-text transcription * **OpenAI Integration**: GPT-powered conversation handling * **Web Dashboard**: Configuration and monitoring interface *** ## Roadmap Notes Public roadmap items move as customer deployments and provider support change. For planned features, breaking changes, or migration timing, check your dashboard announcements or contact support. *** ## Platform Updates All platform updates are deployed automatically with: * **Zero-downtime deployments** * **Automatic rollback** on issues * **Email notifications** for major changes Breaking changes are announced at least 2 weeks in advance via email. Check the dashboard for migration guides when needed. *** ## Feedback & Requests Tell us what features you'd like to see Report bugs or issues you encounter # Configuration Source: https://docs.burki.dev/configuration Configure your Burki Voice AI assistants and providers This page covers the key configuration options for Burki Voice AI assistants and integrations. *** ## Provider Configuration Burki supports two configuration modes for AI providers: ### Burki Cloud (Managed) When using Burki Cloud, no API keys are required. Simply: 1. Select **Burki Cloud** mode in your assistant settings 2. Choose your preferred providers and models 3. Costs are automatically billed to your wallet ### BYO (Bring Your Own) When using your own API keys, configure them in **Settings** → **Provider Keys**: | Provider | Key Required | | - | - | | **OpenAI** | API Key | | **Anthropic** | API Key | | **Azure OpenAI** | API Key + Endpoint | | **Gemini** | API Key | | **xAI** | API Key | | **Groq** | API Key | | **ElevenLabs** | API Key | | **Cartesia** | API Key | | **Deepgram** | API Key | | **Azure Speech** | API Key + Region | | **Speechmatics** | API Key | | **Soniox** | API Key | | **Uplift** | API Key | | **Murf** | API Key | | **Twilio** | Account SID + Auth Token | | **Vonage** | API Key + API Secret | | **Telnyx** | API Key + Connection ID | *** ## Assistant Configuration Each assistant can be configured with: ### Basic Settings * **Name** — Display name for the assistant * **Description** — Internal notes about the assistant's purpose * **Greeting Message** — First message spoken when a call connects ### AI Configuration * **LLM Provider** — OpenAI, Azure OpenAI, Anthropic, Gemini, Groq, xAI, custom HTTP, or custom WebSocket * **LLM Model** — Specific model (e.g., gpt-4o, claude-3-sonnet) * **System Prompt** — Instructions defining the assistant's behavior * **Temperature** — Creativity level (0.0-1.0) ### Voice Configuration * **TTS Provider** — ElevenLabs, Deepgram, Cartesia, Azure, Inworld, Resemble, OpenAI, Kokoro, Uplift, Murf, or Soniox * **Voice** — Specific voice ID or name * **Speed** — Speaking rate adjustment * **Stability** — Voice consistency (provider-dependent) ### Speech Recognition * **STT Provider** — Deepgram (recommended), ElevenLabs, Azure, Uplift, Speechmatics, Telnyx, or Soniox * **Model** — Nova-3, Flux, Scribe v2, Azure, Telnyx-hosted, Soniox, or other provider models * **Language** — Primary language for recognition * **Keywords** — Important terms for better recognition *** ## Call Management Settings ### Recording * **Enable Recording** — Toggle call recording on/off * **Recording Format** — MP3 or WAV ### Interruption Handling * **Allow Interruptions** — Let callers interrupt the assistant * **Interruption Threshold** — Sensitivity (0.0-1.0) ### Timeouts * **Max Call Duration** — Maximum call length in minutes * **Silence Timeout** — End call after N seconds of silence *** ## API Configuration For API access, you'll need: | Setting | Value | | - | - | | **API Base URL** | `https://api.burki.dev` | | **API Key** | Generated in Dashboard → Settings → API Keys | ### Example API Request ```bash theme={null} curl -X GET "https://api.burki.dev/api/v1/assistants/" \ -H "Authorization: Bearer YOUR_API_KEY" ``` *** ## Webhook Configuration Configure webhooks in **Settings** → **Webhooks** to receive real-time notifications: | Event | Description | | - | - | | `call.started` | Call connected | | `call.ended` | Call completed | | `call.recording.ready` | Recording available | | `message.sent` | SMS sent | | `message.received` | SMS received | *** ## Best Practices Shorter system prompts reduce latency. Focus on essential instructions and personality traits. For simple tasks, faster models (GPT-4o-mini) may be better than more powerful ones. Set up backup providers for critical assistants to ensure reliability. Use the test call feature to verify your configuration before assigning real phone numbers. *** ## Next Steps Deep dive into LLM, TTS, and STT settings Tune speech-to-text provider, VAD, endpointing, and silence controls Full API documentation # Conversation Flows Source: https://docs.burki.dev/conversation-flows Build RetellAI-style call flows with conversation nodes, global prompts, variables, and LLM-decided transitions. Conversation Flows let one base assistant route a call through multiple conversation nodes. Each node has its own prompt and optional transitions. During the call, the LLM chooses transitions by calling generated tools such as `transition_to_billing` or `transition_to_support`. The model is intentionally small: one JSON document, one base assistant for voice/STT/TTS/tools, Redis-backed live state, and API-first editing. ## Flow Shape ```json theme={null} { "begin_node": "start", "global_prompt": "Be concise and helpful.", "nodes": { "start": { "type": "conversation", "prompt": "Greet the caller and ask how you can help.", "transitions": [ { "kind": "prompt", "to": "support", "when": "The caller needs product support." } ] }, "support": { "type": "conversation", "prompt": "Help the caller with support questions." } } } ``` ## Runtime Behavior * `begin_node` is the first node for every call. * `global_prompt` is applied to every node. * Node prompts are combined with the base assistant prompt. * Transition tools are generated only for the current node's outgoing transitions. * Dynamic variables such as `{{from_number}}` are read-only and interpolated into prompts. * Existing assistant tools such as `endCall`, transfer, DTMF, SMS, and custom tools still come from the base assistant. ## What the Base Assistant Controls The flow graph controls routing and node prompts. The base assistant still controls the rest of the live call stack: | Setting area | Comes from | | - | - | | LLM provider, prompt defaults, fallbacks | Base assistant | | TTS voice, cloned voices, background sound | Base assistant | | STT provider, turn detection, speculative LLM | Base assistant | | Cold/warm transfer, DTMF, SMS, custom tools | Base assistant | | RAG documents and retrieval settings | Base assistant | | Recording, spam detection, bypass numbers | Base assistant | | Nodes, transitions, global prompt, variables | Conversation Flow | The flow editor embeds the same assistant configuration form for the base assistant. If a flow behaves correctly but sounds wrong, uses the wrong tool, or has bad interruption timing, update the base assistant settings. ## Node and Transition Design Use one node for each stable conversational mode: greeting, qualification, billing, support, escalation, booking, or wrap-up. Avoid creating a new node for every question; keep node prompts focused on behavior and use transitions for routing decisions. Transition examples: ```json theme={null} { "kind": "prompt", "to": "billing", "when": "The caller asks about invoices, payments, refunds, or account balance." } ``` At runtime, Burki exposes only the current node's transition tools to the LLM. This keeps routing decisions local and reduces accidental jumps across the graph. ## Phone Number Assignment Phone numbers can be assigned to either a single assistant or a ConversationFlow. A flow assignment stores `flow_id` on the phone number and clears the direct assistant assignment. Use the Flow phone-number endpoints to bind numbers to flows: ```bash theme={null} curl -X POST https://api.burki.dev/api/v1/flows/42/phone-numbers/7/assign \ -H "Authorization: Bearer $BURKI_API_KEY" ``` See the [Conversation Flows API reference](/api-reference/flows/overview) for CRUD and assignment endpoints. ## Common Flow Patterns | Pattern | Use when | Notes | | - | - | - | | Intake → department routing | The first step is identifying caller intent. | Start node asks one broad question, then routes to department nodes. | | Qualification → transfer | AI should qualify before handoff. | Use a warm transfer tool on the base assistant for high-context handoffs. | | FAQ → escalation | AI can answer common questions but escalates complex cases. | Keep RAG enabled on the base assistant and transfer scenarios specific. | | Appointment booking | Caller needs multi-step scheduling. | Use node prompts for required fields and post-call extraction for structured data. | # FAQ Source: https://docs.burki.dev/faq Answers about Burki's browser trial, GPT Live assistants, prepaid usage, telephone setup and call review. ## Getting started Burki helps you create a business voice assistant, practice a draft in your browser, connect callers and review results. Start with one useful task, such as answering approved questions or collecting a service request. [Create your account](https://burki.dev/auth/register), verify your email when requested, and open **Assistants**. Describe the business and desired call outcome, review the editable starter, then select **Test this version**. You do not need a telephone number or provider keys for eligible managed browser testing. Follow the [Quickstart](/quickstart) for the complete journey. New assistants start with managed GPT Live, which handles speech directly. You do not need to choose separate transcription and speech-synthesis providers for this preset. Other provider configurations are available where supported; check their readiness before testing or publishing. No. Review and test the selected draft version, then use the publishing controls when you are ready. A successful browser test does not by itself establish telephone, booking or transfer readiness. ## Trial and billing Eligible new workspaces can use up to **three minutes of personalized browser testing**, shared across sessions, with a **two-minute maximum per call** and one concurrent trial call. Provider-cost limits and shared daily capacity also apply. Burki shows your effective remaining allowance before a test. This is browser practice time, not cash wallet credit, 200 telephone minutes or a free phone number. Existing grants and paid balances retain their own terms. Trial actions are simulated; live external actions and stored recordings require funded usage and supported connections. Check the browser test allowance as well as **Usage & billing**: trial time and wallet funds are different. Verify your email if requested and read the reason shown before testing. The allowance may be used, unavailable for the workspace, or temporarily limited by daily capacity. Your draft remains editable. If it seems wrong, use Burki's Help or support-ticket option and include the visible message; never send provider keys. Burki uses prepaid usage. Review the configured platform, managed-provider and telephone charges before a call. Number rental is a separate recurring charge and may continue even when the number is not used. See [current pricing](https://burki.dev/pricing) and your workspace's quote for the selected configuration. An estimate or temporary reservation is not the same as the final settled charge. The editable starter is free. Optional AI workflow generation is separately priced and requires funded usage. Review the displayed generation price before confirming; browser trial time does not pay for it. With BYO credentials, you pay the connected provider directly for its usage. Burki platform charges and any other applicable managed or telephone charges remain separate. Use supported provider settings and check the effective quote; a provider cost that Burki cannot calculate is unknown, not free. Open **Usage & billing** and choose a supported top-up amount. Review any auto-top-up setting separately; it is optional. Exhausting trial time does not automatically authorize paid calling. ## Real calls and actions Check the carrier-specific setup in **Phone numbers**. Telnyx, Twilio and Vonage configuration paths have different requirements; a connected credential or imported number does not prove end-to-end readiness. Use a number you control, resolve the displayed setup checks, and complete an authorized test before sending customer calls to it. Configure the supported Cal.com action, select an appointment type and use funded usage for a real booking. The assistant should confirm the caller's date, timezone and details before creating it, and only report success from the returned booking result. A simulated trial action does not create an appointment. Funded telephone calls require an eligible carrier route, a configured number and a ready assistant version. Transfer behavior depends on the selected runtime, workflow and destination. Check the supported configuration and test the specific handoff, including rejection or timeout, before using it with customers. Fallback behavior is configuration-specific. Do not assume a separate STT, LLM or TTS fallback applies to a GPT Live speech-to-speech session. Review the options and readiness checks for the selected provider combination. ## Reviewing calls and troubleshooting Open **Calls** and select a call to inspect its transcript, available activity and cost. Recordings are available only when recording was enabled with the required consent, permissions and storage configuration. Trial browser calls retain transcripts; they do not include stored recordings. A completed call does not necessarily mean a booking or other business action succeeded, so review the action result too. Read the test or call error first. For browser practice, check microphone permission and trial allowance or available funds. For telephone calls, also check the assigned and published assistant, carrier readiness and provider credentials. Include the call or request ID when contacting support, without sharing keys or private caller details unnecessarily. Compare a browser test in a quiet setting with the affected telephone call. Check the microphone, network connection, selected model and provider status. Review the transcript and timing before changing providers. Avoid assuming that a different TTS provider will affect a GPT Live session, which produces speech directly. Check that the request uses a valid key for the correct workspace in the `Authorization: Bearer YOUR_KEY` header. Review its access and expiration in **API keys**. Keep keys server-side and never paste them into a support ticket. ## Need help? Use **Help** in your Burki workspace to review guidance or create a support ticket. You can also [contact the Burki team](mailto:meeran@burki.dev). Include the affected page, error message and request ID if available. # Features Source: https://docs.burki.dev/features A complete voice AI platform with everything you need to build intelligent phone assistants # Features Burki Voice AI provides a complete, production-ready platform for building voice-powered applications. From ultra-low latency to advanced call management, everything you need is built in. ## Core Voice AI Capabilities * **Multi-Provider Telephony**: Support for Twilio, Vonage, and Telnyx with automatic provider selection per assistant. * **Speech-to-Text**: Real-time transcription using Deepgram Nova/Flux, ElevenLabs, Azure, Uplift, Speechmatics, Telnyx, or Soniox. * **Natural Language Processing**: Generate intelligent responses with OpenAI, Azure OpenAI, Anthropic, Gemini, Groq, xAI, or custom LLM endpoints. * **Text-to-Speech**: Convert responses to natural speech with ElevenLabs, Deepgram, Cartesia, Azure, Inworld, Resemble, OpenAI, Kokoro, Uplift, Murf, or Soniox. * **Audio Denoising**: Built-in RNNoise integration for crystal-clear audio quality. * **Call Recording**: Automatic recording with transcription storage and metadata tracking. * **Live Transcript Streaming**: Real-time WebSocket streaming of call transcripts for monitoring and analytics. * **SMS & MMS Messaging**: Send text messages and multimedia content through your assistants with multi-provider support. Burki supports multiple TTS and LLM providers. You can easily switch or add new integrations at any time. ## Performance & Reliability * **Low-Latency Voice Pipeline**: Typical response times depend on telephony, STT, LLM, TTS, region, and model configuration * **Automatic Scaling**: Infrastructure scales automatically with your call volume * **Production Reliability Controls**: Provider fallbacks, retries, and monitoring for business-critical assistants * **Regional Provider Routing**: Route calls through the telephony and AI providers that fit your deployment * **Provider Fallbacks**: Automatic failover to backup providers if primary fails ## Multi-Assistant Management * **Multiple AI Assistants**: Configure different assistants with unique personalities and settings. * **Phone Number Assignment**: Assign specific assistants to different phone numbers. * **Real-time Configuration**: Update assistant settings without any restart or downtime. * **Organization Management**: Manage multiple teams and assistants under one account. ## SMS & MMS Messaging * **Multi-Provider SMS**: Send messages through Twilio, Vonage, or Telnyx based on assistant configuration. * **Multimedia Support**: Send images, documents, and other media files via MMS. * **Assistant-Based Routing**: Messages automatically use the correct provider for each assistant. * **E.164 Validation**: Automatic phone number format validation for reliable delivery. * **Delivery Tracking**: Receive message IDs and delivery status from telephony providers. * **Integration Ready**: Easy-to-use REST API for sending messages from your applications. ## Phone Number Management * **Search & Discovery**: Find available phone numbers with advanced filtering by area code, locality, and patterns. * **Direct Purchase**: Buy phone numbers instantly through the API with automatic webhook configuration and assistant assignment. * **Multi-Provider Support**: Purchase and manage numbers from Twilio, Vonage, and Telnyx in a unified interface. * **Cost Comparison**: Compare pricing between providers for the same region to optimize costs. * **Provider Coverage**: Access phone numbers through supported carrier inventories, with country availability varying by provider and product. * **Inventory Management**: Centralized dashboard to track usage, costs, and assignment across all providers. * **Release Management**: Release unused numbers to reduce monthly costs with automatic cleanup. * **Bulk Operations**: Purchase or release multiple numbers simultaneously for scaling operations. ## Web Dashboard & Management * **Modern Web Interface**: Beautiful, responsive dashboard for managing your voice AI system. * **Real-time Analytics**: Advanced call statistics, success rates, and performance metrics. * **Call Management**: View call history, transcripts, and recordings through the web interface. * **Assistant Configuration**: Create and manage assistants through an intuitive web UI. * **Usage Monitoring**: Track minutes, costs, and wallet balance in real-time. ## Authentication & Security * **Multi-tenant Architecture**: Support for multiple organizations with isolated data. * **Google OAuth Integration**: Seamless authentication with Google accounts. * **API Key Management**: Generate and manage API keys for programmatic access. * **Session Management**: Secure session handling with JWT tokens. * **Role-based Access Control**: User roles and permissions system. * **Data Encryption**: All data encrypted at rest and in transit. ## Advanced Audio Processing * **Real-time Noise Reduction**: RNNoise integration for superior audio quality. * **Voice Activity Detection**: Smart silence detection and speech processing. * **Audio Format Support**: Support for various audio formats with automatic conversion. * **Recording Quality Enhancement**: Post-processing tools for improving recording quality. * **Background Sounds**: Add ambient audio to create realistic call environments. ## Tools & Custom Actions * **Built-in Call Tools**: Call transfers, graceful call endings, and essential call management actions. * **Endpoint Tools**: Make HTTP requests to external APIs during conversations for CRM lookups, database queries, and third-party integrations. * **Python Function Tools**: Execute custom Python code with access to popular libraries for calculations, data processing, and business logic. * **AWS Lambda Tools**: Trigger serverless functions for scalable processing, machine learning inference, and complex workflows. * **Tool Library**: Create reusable tools that can be assigned to multiple assistants across your organization. * **Real-time Execution**: Tools execute during conversations with built-in timeout protection and error handling. * **Sandbox Security**: Python functions run in secure, isolated environments with resource limits. * **Usage Analytics**: Track tool performance, success rates, and execution times across all assistants. Tools are independent of assistants, allowing you to build a library of reusable integrations. Create once, use everywhere. ## API & Integration * **RESTful API**: Comprehensive REST API for all system operations. * **WebSocket Support**: Real-time communication for live call handling and transcript streaming. * **Webhook Support**: Configurable webhooks for call events and notifications. * **Custom LLM Support**: Integrate with various LLM providers beyond the built-in options. * **Live Transcript API**: WebSocket endpoint for real-time transcript monitoring and analytics. For a full list of integrations and setup instructions, see the [Integrations](/integrations) page. ## Flexible Deployment Options **Fully managed platform** * No infrastructure to manage * Automatic updates and scaling * 24/7 monitoring and support * Best for most use cases **Bring your own API keys** * Use your existing provider accounts * Full control over costs * Platform fees only * Best for cost optimization ## Getting Started Create your first assistant in 5 minutes Simple, transparent pricing Full API documentation # Getting Started Source: https://docs.burki.dev/getting-started Set up your Burki account and configure AI providers This guide covers account setup and provider configuration. If you haven't created an account yet, start with the [Quickstart](/quickstart) guide. ## Overview Burki separates **account and organization setup** from **assistant configuration**. Your organization owns billing, team members, shared phone numbers, and assistant resources. Each assistant then chooses telephony, speech-to-text, reasoning, and text-to-speech providers, plus optional tools, webhooks, and knowledge sources. This guide walks through creating the org, inviting collaborators, and selecting managed versus bring-your-own provider modes so you can standardize patterns across teams without re-implementing telephony or streaming audio. Expect to spend most of your time on provider credentials, model selection, and call behavior—not on wiring raw carrier APIs. When you are ready to automate provisioning, use the REST API and webhooks documented in the API reference. *** ## Account Setup ### Creating Your Organization When you sign up for Burki, an organization is automatically created for you. Organizations help you: * **Manage multiple assistants** under one account * **Invite team members** with different roles * **Track usage and billing** across all assistants * **Share resources** like phone numbers and knowledge bases ### Adding Team Members 1. Go to **Settings** → **Team** 2. Click **"Invite Member"** 3. Enter their email and select a role: * **Admin:** Full access to all settings and billing * **Member:** Can create and manage assistants * **Viewer:** Read-only access to dashboards and analytics *** ## Provider Configuration Burki supports two modes for AI providers. You can use either mode or mix them. ### Burki Cloud (Managed) With Burki Cloud, we handle all provider integrations: When creating or editing an assistant, choose **"Burki Cloud"** for each provider section. Select from available options: * **LLM:** OpenAI, Azure OpenAI, Anthropic, Gemini, Groq, xAI, custom HTTP/WebSocket * **TTS:** ElevenLabs, Deepgram, Cartesia, Azure, Inworld, Resemble, OpenAI, Kokoro, Uplift, Murf, Soniox * **STT:** Deepgram, ElevenLabs, Azure, Uplift, Speechmatics, Telnyx, Soniox Customize model selection, voice options, and language settings. Burki Cloud charges the platform fee plus actual provider usage, with no additional management markup. Check the [Pricing](/pricing) page for details. ### BYO (Bring Your Own Keys) Use your own API keys for full control over costs: Sign up for accounts with your preferred providers: | Provider | Sign Up URL | | - | - | | OpenAI | [openai.com/api](https://openai.com/api/) | | Anthropic | [console.anthropic.com](https://console.anthropic.com) | | ElevenLabs | [elevenlabs.io](https://elevenlabs.io) | | Deepgram | [deepgram.com](https://deepgram.com) | | Cartesia | [cartesia.ai](https://cartesia.ai) | Go to **Settings** → **API Keys** → **Provider Keys** and enter your credentials. When configuring an assistant, choose **"BYO"** and select your connected provider. Your API keys are encrypted and stored securely. We never share them or use them for anything other than making API calls on your behalf. ### Hybrid Mode Mix BYO and Burki Cloud based on your needs: | Service | Example Configuration | | - | - | | LLM | BYO (OpenAI) - You have negotiated enterprise pricing | | TTS | Burki Cloud - Easier management of voice options | | STT | Burki Cloud - Deepgram is optimized for phone audio | *** ## Telephony Setup ### Start with Browser Practice Eligible new workspaces can practice an assistant in the browser before connecting a telephone number. Check your workspace's allowance and follow the [Quickstart](/quickstart). Browser trial time does not include a free telephone number; existing grants keep their own terms. ### Purchasing Additional Numbers 1. Go to **Phone Numbers** → **Purchase** 2. Search by country, area code, or pattern 3. Compare prices across providers (Twilio, Vonage, Telnyx) 4. Click **"Purchase"** to add to your account Number availability, carrier rental and applicable management charges depend on the selected route and account. Review the recurring charges in your workspace before connecting a number; charges may continue while it is idle. ### Bringing Your Own Numbers If you have existing phone numbers with Twilio, Vonage, or Telnyx: 1. Go to **Settings** → **Provider Keys** 2. Add your telephony provider credentials 3. Go to **Phone Numbers** → **Sync** 4. Your numbers will appear and can be assigned to assistants *** ## Wallet & Billing ### Adding Funds 1. Go to **Billing** → **Wallet** 2. Click **"Add Funds"** 3. Enter the amount and complete payment via Stripe ### Setting Up Auto Top-up Never run out of credits: 1. Go to **Billing** → **Auto Top-up** 2. Set your **threshold** (e.g., when balance drops below \$10) 3. Set your **top-up amount** (e.g., add \$50) 4. Save your payment method Auto top-up has a 5-minute cooldown to prevent duplicate charges during high-volume periods. ### Funded Usage Readiness Review the selected configuration's quote and available balance in **Usage & billing**. Calls can reserve funds before starting, and provider usage can settle afterward. Browser trial time does not automatically turn into paid usage. Before directing customer traffic, resolve carrier and provider checks, publish the reviewed assistant version, and complete an authorized telephone test. An imported or assigned number alone does not establish readiness. *** ## API Integration If you're integrating via API or SDK, you'll need your API key: Go to **Settings** → **API Keys** → **Generate New Key** Copy the key immediately - you won't be able to see it again. Store it securely in your application's configuration. Use the API base URL `https://api.burki.dev` with your key in the Authorization header. Check out our [SDKs](/sdks/overview) for Python, JavaScript, and Go - they handle authentication and API calls for you. *** ## Next Steps Deep dive into LLM, TTS, and STT settings Configure Twilio, Vonage, or Telnyx Add call transfers, CRM lookups, and more Full API documentation # Burki Voice AI Documentation — guides, API & platform reference Source: https://docs.burki.dev/index Welcome to Burki Voice AI Documentation # Burki Voice AI Documentation