> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tryhamsa.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Quick Start

> Complete guide to integrating with all Hamsa APIs - authentication, REST endpoints, real-time APIs, SDKs, and webhooks

## Overview

This comprehensive guide covers everything you need to integrate with Hamsa's platform, including REST APIs, real-time WebSocket connections, the Voice Agents SDK, webhooks, and the tool system. Whether you're building a simple integration or a complex production system, this guide has you covered.

<Info>
  **What This Guide Covers:**

  * Authentication methods for all API types
  * REST API integration patterns
  * Real-time Speech-to-Text and Text-to-Speech APIs
  * Voice Agents Web SDK
  * Webhook integration for event-driven architectures
  * Tool system for extending agent capabilities
  * Error handling and edge cases
  * Rate limiting and quota management
  * Production best practices
</Info>

***

## Authentication

Hamsa uses different authentication methods depending on the API type. Understanding these is crucial for successful integration.

### API Key Token Authentication

Used for all REST API endpoints.

```bash theme={null}
# Header format
Authorization: Token <YOUR_API_KEY>

# Example request
curl -X GET https://api.tryhamsa.com/v1/voice-agents \
  -H "Authorization: Token sk_live_abc123xyz789" \
  -H "Content-Type: application/json"
```

<Warning>
  **Security Best Practices:**

  * Never expose API keys in client-side code
  * Store keys in environment variables or secure vaults
  * Use separate keys for development and production
  * Rotate keys periodically
  * Use least-privilege keys when possible
</Warning>

### Bearer Token (JWT) Authentication

Used for webhooks and tool authentication that the user adds to the system.

```bash theme={null}
# Header format
Authorization: Bearer <JWT_TOKEN>

# Example
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```

### Custom Header Authentication

For tools and integrations that require custom authentication schemes.

```json theme={null}
{
  "headers": [
    {
      "name": "X-API-Key",
      "value": "your_custom_api_key"
    },
    {
      "name": "X-Client-ID",
      "value": "client_123"
    }
  ]
}
```

### Getting Your API Key

<Steps>
  <Step title="Create Account">
    Sign up at [Hamsa Agents](https://agents.tryhamsa.com)
  </Step>

  <Step title="Navigate to API Keys">
    Go to **Settings** → **API Keys** in the dashboard
  </Step>

  <Step title="Create New Key">
    Click **Create API Key** and provide a descriptive name
  </Step>

  <Step title="Copy and Store Securely">
    Copy the key immediately - it won't be shown again
  </Step>
</Steps>

***

## REST API Integration

### Base URL

All REST API requests use the following base URL:

```
https://api.tryhamsa.com
```

### API Versioning

Hamsa provides multiple API versions. Use the version specified in the endpoint path:

```
/v1/voice-agents     # Version 1
/v2/voice-agents     # Version 2 (recommended for new integrations)
```

<Note>
  Some v1 endpoints are marked as **deprecated** in the API reference. For these endpoints, migrate to the v2 equivalent when available. Endpoints not marked as deprecated are fully supported.
</Note>

### Request Format

All requests should include:

```bash theme={null}
Content-Type: application/json
Authorization: Token <YOUR_API_KEY>
```

### Response Format

Almost all responses follow a consistent structure:

**Success Response:**

```json theme={null}
{
  "success": true,
  "data": {
    // Response data
  }
}
```

**Error Response:**

```json theme={null}
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error message",
    "details": {}
  }
}
```

### Core API Endpoints

#### Voice Agents

| Method  | Endpoint                 | Description                   |
| ------- | ------------------------ | ----------------------------- |
| `POST`  | `/v2/voice-agents`       | Create a new voice agent      |
| `GET`   | `/v2/voice-agents`       | List all voice agents         |
| `GET`   | `/v2/voice-agents/{id}`  | Get agent details             |
| `PATCH` | `/v2/voice-agents/{id}`  | Update an agent               |
| `POST`  | `/v1/voice-agents/clone` | Clone an existing agent       |
| `POST`  | `/v1/voice-agents/call`  | Initiate a call with an agent |

**Create Agent Example:**

```javascript theme={null}
const response = await fetch('https://api.tryhamsa.com/v2/voice-agents', {
  method: 'POST',
  headers: {
    'Authorization': 'Token sk_live_abc123',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'Customer Support Agent',
    preamble: 'You are a helpful customer support agent...',
    greeting: 'Hello! How can I help you today?',
    language: 'en-US',
    voiceId: 'voice_abc123'
  })
});

const data = await response.json();
console.log('Created agent:', data.data.id);
```

#### Phone Numbers

| Method   | Endpoint                             | Description                |
| -------- | ------------------------------------ | -------------------------- |
| `GET`    | `/v1/voice-agents/phone-number`      | List phone numbers         |
| `POST`   | `/v1/voice-agents/phone-number`      | Add a phone number         |
| `DELETE` | `/v1/voice-agents/phone-number`      | Delete a phone number      |
| `POST`   | `/v1/voice-agents/assign-number`     | Assign number to agent     |
| `POST`   | `/v1/voice-agents/unassign`          | Unassign number from agent |
| `POST`   | `/v1/voice-agents/phone-number/call` | Make outbound call         |

#### Knowledge Base

| Method   | Endpoint                                            | Description              |
| -------- | --------------------------------------------------- | ------------------------ |
| `POST`   | `/v1/voice-agents/knowledge-base`                   | Create knowledge item    |
| `GET`    | `/v1/voice-agents/knowledge-base/list`              | List all items           |
| `GET`    | `/v1/voice-agents/knowledge-base/{id}`              | Get item details         |
| `PATCH`  | `/v1/voice-agents/knowledge-base/{id}`              | Update item              |
| `DELETE` | `/v1/voice-agents/knowledge-base/{id}`              | Delete item              |
| `POST`   | `/v1/voice-agents/knowledge-base/{id}/url`          | Add URL to item          |
| `POST`   | `/v1/voice-agents/knowledge-base/toggle-activation` | Activate/deactivate item |

**Knowledge Base Item Types:**

* `TEXT` - Structured text content (50-5,000 characters)
* `FILE` - PDF, DOCS, DOC, TXT, HTML, EPUB.
* `URL` - Web pages (up to 100 URLs per item)

#### Web Tools (Custom API Integration)

| Method  | Endpoint                                      | Description         |
| ------- | --------------------------------------------- | ------------------- |
| `POST`  | `/v2/web-tool`                                | Create a web tool   |
| `GET`   | `/v2/web-tool/list`                           | List all tools      |
| `PATCH` | `/v2/web-tool/{id}`                           | Update a tool       |
| `POST`  | `/v1/voice-agents/web-tool/test-api-tool`     | Test a tool         |
| `POST`  | `/v1/voice-agents/web-tool/toggle-activation` | Activate/deactivate |

#### Campaigns (Outbound Calling)

| Method  | Endpoint                                 | Description          |
| ------- | ---------------------------------------- | -------------------- |
| `POST`  | `/v1/voice-agents/campaigns`             | Create campaign      |
| `GET`   | `/v1/voice-agents/campaigns`             | List campaigns       |
| `GET`   | `/v1/voice-agents/campaigns/{id}`        | Get campaign details |
| `PATCH` | `/v1/voice-agents/campaigns/{id}`        | Update campaign      |
| `POST`  | `/v1/voice-agents/campaigns/{id}/pause`  | Pause campaign       |
| `POST`  | `/v1/voice-agents/campaigns/{id}/resume` | Resume campaign      |
| `POST`  | `/v1/voice-agents/campaigns/{id}/cancel` | Cancel campaign      |
| `POST`  | `/v1/voice-agents/campaigns/{id}/retry`  | Retry failed calls   |

#### Call History & Conversations

| Method | Endpoint                              | Description                  |
| ------ | ------------------------------------- | ---------------------------- |
| `POST` | `/v1/voice-agents/conversations/list` | List calls with filters      |
| `GET`  | `/v1/voice-agents/conversation/{id}`  | Get call details             |
| `POST` | `/v1/voice-agents/join-call`          | Join active call as listener |

#### Media Processing (Jobs)

| Method | Endpoint                  | Description               |
| ------ | ------------------------- | ------------------------- |
| `POST` | `/v1/jobs/transcribe`     | Transcribe audio/video    |
| `POST` | `/v1/jobs/text-to-speech` | Generate speech from text |
| `POST` | `/v1/jobs/ai-content`     | Generate AI Docs          |
| `GET`  | `/v1/jobs`                | Get job status            |
| `POST` | `/v1/jobs/all`            | List all jobs             |

***

## Real-Time APIs

### Real-Time Speech-to-Text (WebSocket)

Connect to the real-time STT endpoint for live transcription:

```javascript theme={null}
const ws = new WebSocket('wss://api.tryhamsa.com/v1/realtime/ws?api_key=YOUR_API_KEY');

ws.onopen = async () => {
  // Get audio from microphone
  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
  const mediaRecorder = new MediaRecorder(stream);
  const chunks = [];

  mediaRecorder.ondataavailable = (e) => chunks.push(e.data);

  mediaRecorder.onstop = async () => {
    const blob = new Blob(chunks);
    const buffer = await blob.arrayBuffer();
    const base64 = btoa(String.fromCharCode(...new Uint8Array(buffer)));

    ws.send(JSON.stringify({
      type: 'stt',
      payload: {
        audioBase64: base64,
        language: 'ar',
        isEosEnabled: true,
        eosThreshold: 0.3
      }
    }));
  };

  mediaRecorder.start();
  setTimeout(() => mediaRecorder.stop(), 3000); // Record 3 seconds
};

ws.onmessage = (event) => {
  if (typeof event.data === 'string') {
    try {
      const json = JSON.parse(event.data);
      if (json.type === 'error') {
        console.error('Error:', json.payload.message);
      }
    } catch {
      // Plain text transcription result
      console.log('Transcription:', event.data);
    }
  }
};
```

**Supported Audio Formats:**

* `linear16` - 16-bit linear PCM
* `mulaw` - 8-bit μ-law

### Real-Time Text-to-Speech (REST)

**Standard TTS Request:**

```javascript theme={null}
const response = await fetch('https://api.tryhamsa.com/v1/realtime/tts', {
  method: 'POST',
  headers: {
    'Authorization': 'Token sk_live_abc123',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    text: 'Hello, how can I help you today?',
    voiceId: 'voice_abc123',
    speed: 1.0,
    pitch: 1.0
  })
});

const audioBlob = await response.blob();
const audioUrl = URL.createObjectURL(audioBlob);
```

**Streaming TTS Request:**

If you are facing any issues while working with this endpoint, please refer to this [JSFiddle](https://jsfiddle.net/x9so4jyz/) which has a solution our team created as an example.

```javascript theme={null}
const response = await fetch('https://api.tryhamsa.com/v1/realtime/tts-stream', {
  method: 'POST',
  headers: {
    'Authorization': 'Token sk_live_abc123',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    text: 'This is a longer text that will be streamed...',
    voiceId: 'voice_abc123'
  })
});

// Process streaming response
const reader = response.body.getReader();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  // Process audio chunk
  playAudioChunk(value);
}
```

***

## Voice Agents SDK Integration

The Hamsa Voice Agents SDK provides a seamless way to embed voice interactions into web applications.

### Installation

```bash theme={null}
npm install @hamsa-ai/voice-agents-sdk
```

Or via CDN:

```html theme={null}
<script src="https://unpkg.com/@hamsa-ai/voice-agents-sdk@latest/dist/index.umd.js"></script>
```

### Basic Usage

```javascript theme={null}
import { HamsaVoiceAgent } from '@hamsa-ai/voice-agents-sdk';

// Initialize
const agent = new HamsaVoiceAgent('YOUR_API_KEY');

// Start conversation
await agent.start({
  agentId: 'agent_abc123',
  params: {
    user_name: 'John Doe',
    user_id: 'user_123',
    session_id: 'session_abc'
  },
  voiceEnablement: true,
  userId: 'user_123'
});

// Event listeners
agent.on('callStarted', ({ jobId }) => {
  console.log('Call started:', jobId);
});

agent.on('transcriptionReceived', (text) => {
  console.log('User said:', text);
});

agent.on('answerReceived', (text) => {
  console.log('Agent said:', text);
});

agent.on('agentStateChanged', (state) => {
  // state: 'idle' | 'initializing' | 'listening' | 'thinking' | 'speaking'
  updateUI(state);
});

agent.on('callEnded', () => {
  console.log('Call ended');
});

agent.on('error', (error) => {
  console.error('Error:', error);
});
```

### Advanced Configuration

```javascript theme={null}
await agent.start({
  agentId: 'agent_abc123',
  voiceEnablement: true,

  // Custom parameters (echoed in webhooks)
  params: {
    application_id: '12345',
    user_id: 'user_789',
    session_id: 'sess_abc',
    custom_data: JSON.stringify({ key: 'value' })
  },

  // User tracking
  userId: 'user_789',

  // iOS optimization
  preferHeadphonesForIosDevices: true,

  // Platform-specific connection delays
  connectionDelay: {
    android: 3000,  // Android needs longer delay
    ios: 500,
    default: 1000
  },

  // Audio capture for third-party services
  onAudioData: (audioData) => {
    thirdPartyWebSocket.send(audioData);
  },

  // Client-side tools
  tools: [
    {
      function_name: 'getUserInfo',
      description: 'Get user information',
      parameters: [
        { name: 'userId', type: 'string', description: 'User ID' }
      ],
      required: ['userId'],
      fn: async (userId) => {
        return await fetchUserInfo(userId);
      }
    }
  ]
});
```

### Audio Controls

```javascript theme={null}
// Volume control
agent.setVolume(0.8);
const volume = agent.getOutputVolume();

// Microphone control
agent.setMicMuted(true);
agent.setMicMuted(false);
const isMuted = agent.isMicMuted();

// Audio levels
const inputLevel = agent.getInputVolume();
const outputLevel = agent.getOutputVolume();

// Frequency data for visualization
const inputFreqData = agent.getInputByteFrequencyData();
const outputFreqData = agent.getOutputByteFrequencyData();
```

### Analytics & Monitoring

```javascript theme={null}
// Get comprehensive analytics
const analytics = agent.getCallAnalytics();
console.log(analytics);
/*
{
  connectionStats: { quality: 'good', connectionAttempts: 1, ... },
  audioMetrics: { userAudioLevel: 0.8, agentAudioLevel: 0.3, ... },
  performanceMetrics: { callDuration: 60000, responseTime: 1200, ... },
  participants: [...],
  trackStats: { totalTracks: 2, activeTracks: 2, ... }
}
*/

// Real-time connection quality
agent.on('connectionQualityChanged', ({ quality, metrics }) => {
  if (quality === 'poor') {
    showNetworkWarning();
  }
});

// Custom events from agent
agent.on('customEvent', (eventType, eventData, metadata) => {
  if (eventType === 'tool_execution') {
    console.log('Tool called:', eventData.toolName);
  }
});
```

### Conversation Control

```javascript theme={null}
// Pause conversation
agent.pause();

// Resume conversation
agent.resume();

// End conversation
agent.end();

// Get current job ID
const jobId = agent.getJobId();
```

***

## Webhook Integration

Webhooks provide real-time notifications about call events, transcriptions, and outcomes.

### Webhook Events

| Event                  | Description      | When Triggered    |
| ---------------------- | ---------------- | ----------------- |
| `call.started`         | Call begins      | Call initiated    |
| `call.answered`        | Call connected   | User picks up     |
| `transcription.update` | Real-time speech | User/agent speaks |
| `tool.executed`        | Tool called      | Agent uses tool   |
| `call.ended`           | Call completes   | Call terminates   |

### Setting Up Webhooks

<Steps>
  <Step title="Create Webhook Endpoint">
    Create a publicly accessible HTTPS endpoint that accepts POST requests.
  </Step>

  <Step title="Configure in Dashboard">
    Navigate to your agent's settings and add the webhook URL.
  </Step>

  <Step title="Set Authentication">
    Configure Bearer token authentication for security.
  </Step>

  <Step title="Handle Events">
    Implement handlers for different event types.
  </Step>
</Steps>

### Webhook Configuration

```json theme={null}
{
  "webhookUrl": "https://api.yourcompany.com/webhook/hamsa",
  "webhookAuth": {
    "authKey": "bearer",
    "authSecret": "Bearer your_secret_token_here"
  }
}
```

### Webhook Handler Implementation

<Tabs>
  <Tab title="Node.js">
    ```javascript theme={null}
    const express = require('express');
    const app = express();

    app.use(express.json());

    // Verify Bearer token
    function verifyToken(req, res, next) {
      const authHeader = req.headers.authorization;
      const expectedToken = process.env.WEBHOOK_SECRET;

      if (authHeader !== expectedToken) {
        return res.status(401).json({ error: 'Unauthorized' });
      }
      next();
    }

    app.post('/webhook/hamsa', verifyToken, async (req, res) => {
      const event = req.body;

      try {
        switch (event.eventType) {
          case 'call.started':
            await handleCallStarted(event);
            break;
          case 'call.ended':
            await handleCallEnded(event);
            break;
          case 'transcription.update':
            await handleTranscription(event);
            break;
          case 'tool.executed':
            await handleToolExecution(event);
            break;
        }

        // Respond quickly to acknowledge receipt
        res.status(200).json({ received: true });

      } catch (error) {
        console.error('Webhook error:', error);
        // Still return 200 to prevent retries
        res.status(200).json({ received: true, error: true });
      }
    });

    async function handleCallEnded(event) {
      const { conversationId, conversationRecording, transcription, outcomeResult } = event.data.data;

      // Extract echoed identifiers
      const applicationId = outcomeResult.application_id;
      const userId = outcomeResult.user_id;

      // Extract collected data
      const extractedData = {
        expectedSalary: outcomeResult.expectedSalary,
        noticePeriod: outcomeResult.noticePeriod,
        sentiment: outcomeResult.sentiment_score
      };

      // Update your database
      await database.calls.update({
        where: { applicationId },
        data: {
          conversationId,
          recordingUrl: conversationRecording,
          transcript: transcription,
          ...extractedData,
          completedAt: new Date()
        }
      });
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from flask import Flask, request, jsonify
    import os

    app = Flask(__name__)

    def verify_token():
        auth_header = request.headers.get('Authorization')
        expected_token = os.environ.get('WEBHOOK_SECRET')
        return auth_header == expected_token

    @app.route('/webhook/hamsa', methods=['POST'])
    def webhook():
        if not verify_token():
            return jsonify({'error': 'Unauthorized'}), 401

        event = request.get_json()

        try:
            event_type = event.get('eventType')

            if event_type == 'call.started':
                handle_call_started(event)
            elif event_type == 'call.ended':
                handle_call_ended(event)
            elif event_type == 'transcription.update':
                handle_transcription(event)
            elif event_type == 'tool.executed':
                handle_tool_execution(event)

            return jsonify({'received': True}), 200

        except Exception as e:
            print(f'Webhook error: {e}')
            return jsonify({'received': True, 'error': True}), 200

    def handle_call_ended(event):
        data = event['data']['data']

        conversation_id = data['conversationId']
        recording_url = data['conversationRecording']
        transcription = data['transcription']
        outcome = data['outcomeResult']

        # Extract echoed identifiers
        application_id = outcome.get('application_id')
        user_id = outcome.get('user_id')

        # Update database
        update_database(
            application_id=application_id,
            conversation_id=conversation_id,
            recording_url=recording_url,
            transcription=transcription,
            outcome=outcome
        )
    ```
  </Tab>
</Tabs>

### Call Ended Payload Structure

```json theme={null}
{
  "eventType": "call.ended",
  "callId": "call_uuid_12345",
  "timestamp": "2024-01-15T14:35:00.000Z",
  "projectId": "proj_abc123",
  "agentId": "agent_xyz789",
  "agentName": "Customer Support Agent",
  "data": {
    "timestamp": "2024-01-15T14:35:00.000Z",
    "data": {
      "conversationId": "conv-123-abc-456-def",
      "conversationRecording": "https://storage.tryhamsa.com/recordings/conv-123.mp3",
      "transcription": [
        { "Agent": "Hello! How can I help you today?" },
        { "User": "I need help with my order." },
        { "Agent": "I'd be happy to help. What's your order number?" },
        { "User": "It's ORD-12345." }
      ],
      "outcomeResult": {
        "application_id": "12345",
        "user_id": "user-789",
        "session_id": "sess-abc-def",
        "order_number": "ORD-12345",
        "issue_type": "order_inquiry",
        "resolved": true,
        "sentiment_score": 0.8
      }
    }
  }
}
```

### Webhook Best Practices

<CardGroup cols={2}>
  <Card title="Respond Quickly" icon="bolt">
    Return 200 OK within 5 seconds to avoid timeouts
  </Card>

  <Card title="Process Async" icon="clock">
    Queue events for async processing after acknowledging
  </Card>

  <Card title="Implement Idempotency" icon="shield-check">
    Use callId + eventType + timestamp to detect duplicates
  </Card>

  <Card title="Secure Endpoints" icon="lock">
    Use HTTPS and Bearer token authentication
  </Card>
</CardGroup>

***

## Error Handling

### HTTP Status Codes

| Status | Meaning             | Action                         |
| ------ | ------------------- | ------------------------------ |
| `200`  | Success             | Process response               |
| `400`  | Bad Request         | Check request parameters       |
| `401`  | Unauthorized        | Verify API key                 |
| `403`  | Forbidden           | Check permissions              |
| `404`  | Not Found           | Verify resource ID             |
| `429`  | Rate Limited        | Wait and retry with backoff    |
| `500`  | Server Error        | Retry with exponential backoff |
| `503`  | Service Unavailable | Retry later                    |

### Common Error Codes

Error codes are created by adding two pieces of numbers, first piece refers to the system part responsible for that error, and the second is the status code. Status code is always the last three numbers.

| Code    | Description                | Resolution                        |
| ------- | -------------------------- | --------------------------------- |
| `10403` | Invalid or missing API key | Check API key format and validity |
| `10404` | Resource doesn't exist     | Verify resource ID                |
| `10400` | Request validation failed  | Check request body format         |
| `10429` | Too many requests          | Implement rate limiting           |

### Error Handling Pattern

```javascript theme={null}
async function apiRequest(endpoint, options = {}) {
  const maxRetries = 3;
  let lastError;

  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch(`https://api.tryhamsa.com${endpoint}`, {
        ...options,
        headers: {
          'Authorization': `Token ${process.env.HAMSA_API_KEY}`,
          'Content-Type': 'application/json',
          ...options.headers
        }
      });

      const data = await response.json();

      if (!response.ok) {
        // Handle specific error codes
        switch (response.status) {
          case 401:
            throw new AuthenticationError('Invalid API key');
          case 404:
            throw new NotFoundError(data.error?.message || 'Resource not found');
          case 429:
            // Rate limited - wait and retry
            const retryAfter = response.headers.get('Retry-After') || 60;
            await sleep(retryAfter * 1000);
            continue;
          case 500:
          case 503:
            // Server error - retry with backoff
            if (attempt < maxRetries) {
              await sleep(Math.pow(2, attempt) * 1000);
              continue;
            }
            throw new ServerError(data.error?.message || 'Server error');
          default:
            throw new APIError(data.error?.message || 'API request failed', response.status);
        }
      }

      return data;

    } catch (error) {
      lastError = error;

      // Don't retry on authentication or validation errors
      if (error instanceof AuthenticationError || error instanceof ValidationError) {
        throw error;
      }

      // Retry on network errors
      if (attempt < maxRetries && error.name === 'NetworkError') {
        await sleep(Math.pow(2, attempt) * 1000);
        continue;
      }
    }
  }

  throw lastError;
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}
```

***

## Rate Limiting & Quotas

### Rate Limits

| Endpoint Type                           | Limit        | Window     |
| --------------------------------------- | ------------ | ---------- |
| Standard APIs                           | 100 requests | Per minute |
| Realtime APIs and Websocket Connections | 100 requests | Per minute |

### Rate Limit Headers

```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1609459200
```

### Handling Rate Limits

```javascript theme={null}
async function handleRateLimit(response) {
  if (response.status === 429) {
    const retryAfter = response.headers.get('Retry-After');
    const resetTime = response.headers.get('X-RateLimit-Reset');

    // Wait for the specified time
    const waitTime = retryAfter
      ? parseInt(retryAfter) * 1000
      : (parseInt(resetTime) - Date.now());

    console.log(`Rate limited. Waiting ${waitTime}ms before retry...`);
    await sleep(waitTime);

    return true; // Indicates retry is needed
  }
  return false;
}
```

### Storage Quotas

| Resource       | Free Tier | Starter Tier | Creator Tier | Pro Tier  | Business Tier | Enterprise |
| -------------- | --------- | ------------ | ------------ | --------- | ------------- | ---------- |
| Knowledge Base | 1 MB      | 5 MB         | 10 MB        | 50 MB     | 100 MB        | 300 MB     |
| URLs per Item  | 100       | 100          | 100          | 100       | 100           | 100        |
| Agents         | Unlimited | Unlimited    | Unlimited    | Unlimited | Unlimited     | Unlimited  |
| Phone Numbers  | Unlimited | Unlimited    | Unlimited    | Unlimited | Unlimited     | Unlimited  |

## Edge Cases & Best Practices

### Network Connectivity Issues

```javascript theme={null}
// Implement connection health checks
class ConnectionMonitor {
  constructor(sdk) {
    this.sdk = sdk;
    this.reconnectAttempts = 0;
    this.maxReconnectAttempts = 5;

    this.sdk.on('connectionQualityChanged', ({ quality }) => {
      if (quality === 'lost') {
        this.handleDisconnection();
      }
    });

    this.sdk.on('reconnecting', () => {
      console.log('Attempting to reconnect...');
    });

    this.sdk.on('reconnected', () => {
      this.reconnectAttempts = 0;
      console.log('Reconnected successfully');
    });
  }

  async handleDisconnection() {
    if (this.reconnectAttempts >= this.maxReconnectAttempts) {
      this.notifyUser('Connection lost. Please refresh the page.');
      return;
    }

    this.reconnectAttempts++;
    const backoffTime = Math.pow(2, this.reconnectAttempts) * 1000;

    await sleep(backoffTime);

    try {
      await this.sdk.reconnect();
    } catch (error) {
      this.handleDisconnection();
    }
  }
}
```

### Handling Partial Responses

```javascript theme={null}
// Handle partial webhook data
function processWebhookPayload(payload) {
  const { outcomeResult } = payload.data?.data || {};

  // Safely extract with defaults
  const data = {
    applicationId: outcomeResult?.application_id ?? null,
    userId: outcomeResult?.user_id ?? null,
    expectedSalary: outcomeResult?.expectedSalary ?? 'Not provided',
    noticePeriod: outcomeResult?.noticePeriod ?? 'Not provided',
    sentiment: outcomeResult?.sentiment_score ?? null
  };

  // Log missing critical fields
  if (!data.applicationId) {
    console.warn('Missing application_id in webhook payload');
  }

  return data;
}
```

### Concurrent Request Management

```javascript theme={null}
// Use a request queue for concurrent operations
class RequestQueue {
  constructor(maxConcurrent = 5) {
    this.maxConcurrent = maxConcurrent;
    this.running = 0;
    this.queue = [];
  }

  async add(requestFn) {
    return new Promise((resolve, reject) => {
      this.queue.push({ fn: requestFn, resolve, reject });
      this.process();
    });
  }

  async process() {
    if (this.running >= this.maxConcurrent || this.queue.length === 0) {
      return;
    }

    this.running++;
    const { fn, resolve, reject } = this.queue.shift();

    try {
      const result = await fn();
      resolve(result);
    } catch (error) {
      reject(error);
    } finally {
      this.running--;
      this.process();
    }
  }
}

// Usage
const queue = new RequestQueue(3);

const results = await Promise.all([
  queue.add(() => fetchAgent(1)),
  queue.add(() => fetchAgent(2)),
  queue.add(() => fetchAgent(3)),
  queue.add(() => fetchAgent(4)),
  queue.add(() => fetchAgent(5))
]);
```

### Idempotency for Critical Operations

```javascript theme={null}
// Idempotent operation wrapper
async function idempotentOperation(operationId, operation) {
  const key = `operation:${operationId}`;

  // Check if already processed
  const cached = await redis.get(key);
  if (cached) {
    return JSON.parse(cached);
  }

  // Execute operation
  const result = await operation();

  // Cache result with TTL
  await redis.setex(key, 86400, JSON.stringify(result));

  return result;
}

// Usage
await idempotentOperation(
  `create-agent-${sessionId}`,
  () => createVoiceAgent(agentConfig)
);
```

### Graceful Degradation

```javascript theme={null}
// Fallback strategy for service unavailability
async function callWithFallback(primaryFn, fallbackFn, options = {}) {
  const { timeout = 5000, retries = 2 } = options;

  for (let i = 0; i <= retries; i++) {
    try {
      const result = await Promise.race([
        primaryFn(),
        sleep(timeout).then(() => { throw new TimeoutError(); })
      ]);
      return result;
    } catch (error) {
      if (i === retries) {
        console.warn('Primary service failed, using fallback');
        return fallbackFn();
      }
      await sleep(Math.pow(2, i) * 1000);
    }
  }
}

// Usage
const result = await callWithFallback(
  () => fetchFromPrimaryAPI(),
  () => fetchFromCachedData(),
  { timeout: 3000, retries: 2 }
);
```

***

## Production Deployment Checklist

### Security

* [ ] API keys stored in secure vault/environment variables
* [ ] HTTPS enabled for all endpoints
* [ ] Bearer token authentication for webhooks
* [ ] Input validation on all endpoints
* [ ] Rate limiting implemented
* [ ] Secrets rotated regularly
* [ ] Audit logging enabled

### Reliability

* [ ] Retry logic with exponential backoff
* [ ] Circuit breaker pattern for external calls
* [ ] Idempotency for critical operations
* [ ] Dead letter queue for failed webhooks
* [ ] Health check endpoints
* [ ] Graceful shutdown handling

### Monitoring

* [ ] Error tracking (Sentry, etc.)
* [ ] API metrics (latency, error rates)
* [ ] Webhook delivery monitoring
* [ ] Call analytics dashboard
* [ ] Alerting for anomalies
* [ ] Log aggregation

### Performance

* [ ] Connection pooling
* [ ] Request queuing
* [ ] Response caching where appropriate
* [ ] Async processing for webhooks
* [ ] Database query optimization
* [ ] CDN for static assets

***

## Next Steps

<CardGroup cols={2}>
  <Card title="API Reference" href="/api-reference/introduction" icon="code">
    Complete API endpoint documentation
  </Card>

  <Card title="Voice Agents Web SDK" href="/developers/sdks/voice-agents-web-sdk" icon="microphone">
    Full SDK reference and examples
  </Card>

  <Card title="Webhook Guide" href="/developers/agent-guides/webhook-integration-guide" icon="webhook">
    Deep dive into webhook integration
  </Card>

  <Card title="Tool System" href="/developers/apis/api-integration" icon="plug">
    Integrate external APIs with agents
  </Card>
</CardGroup>

***

## Support

<CardGroup cols={2}>
  <Card title="Contact Support" icon="headset" href="mailto:support@tryhamsa.com">
    Get help from our technical team
  </Card>

  <Card title="Documentation" icon="book" href="https://docs.tryhamsa.com">
    Browse complete documentation
  </Card>
</CardGroup>

<Info>
  **Document Version:** 1.0<br />
  **Last Updated:** 2026-01-07<br />
  **API Version:** v2.0.0
</Info>
