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

# API Integration Examples

> Practical examples for integrating the Famulor API into various applications

# API Integration Examples

This guide shows you practical implementation examples for common use cases of the Famulor API.

## Basic Integration

### Listing Assistants

Before making calls, list available assistants:

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function getAssistants() {
    const response = await fetch('https://app.famulor.de/api/user/assistants', {
      headers: {
        'Authorization': `Bearer ${process.env.FAMULOR_API_KEY}`,
        'Content-Type': 'application/json'
      }
    });
    
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    
    const assistants = await response.json();
    return assistants;
  }

  // Usage
  getAssistants()
    .then(assistants => console.log('Available assistants:', assistants))
    .catch(error => console.error('Error:', error));
  ```

  ```python Python theme={null}
  import requests
  import os
  from typing import Dict, List

  def get_assistants() -> List[Dict]:
      """Fetch all available assistants"""
      api_key = os.getenv('FAMULOR_API_KEY')
      
      response = requests.get(
          'https://app.famulor.de/api/user/assistants',
          headers={
              'Authorization': f'Bearer {api_key}',
              'Content-Type': 'application/json'
          }
      )
      
      response.raise_for_status()  # Raises exception on HTTP error
      return response.json()

  # Usage
  try:
      assistants = get_assistants()
      print(f"Found: {len(assistants)} assistants")
      for assistant in assistants:
          print(f"- {assistant['name']} (ID: {assistant['id']})")
  except requests.exceptions.RequestException as e:
      print(f"API error: {e}")
  ```
</CodeGroup>

## Call Integration

### Simple Call

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function makeCall(phoneNumber, assistantId, variables = {}) {
    const response = await fetch('https://app.famulor.de/api/user/make_call', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.FAMULOR_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        phone_number: phoneNumber,
        assistant_id: assistantId,
        variables: variables
      })
    });

    const result = await response.json();
    
    if (!response.ok) {
      throw new Error(`API Error: ${result.message}`);
    }

    return result;
  }

  // Example usage
  makeCall('+4915123456789', 123, {
    customer_name: 'Max Mustermann',
    email: 'max@example.com',
    appointment_time: '2024-03-15 14:00'
  })
  .then(result => {
    console.log('Call started successfully:', result.call_id);
    console.log('Status:', result.status);
  })
  .catch(error => console.error('Error:', error.message));
  ```

  ```python Python theme={null}
  import requests
  import json
  from typing import Dict, Optional

  def make_call(phone_number: str, assistant_id: int, variables: Optional[Dict] = None) -> Dict:
      """Make a call using the Famulor API"""
      api_key = os.getenv('FAMULOR_API_KEY')
      
      payload = {
          'phone_number': phone_number,
          'assistant_id': assistant_id,
          'variables': variables or {}
      }
      
      response = requests.post(
          'https://app.famulor.de/api/user/make_call',
          headers={
              'Authorization': f'Bearer {api_key}',
              'Content-Type': 'application/json'
          },
          json=payload
      )
      
      if not response.ok:
          error_data = response.json()
          raise Exception(f"API Error: {error_data.get('message', 'Unknown error')}")
      
      return response.json()

  # Example usage
  try:
      result = make_call(
          phone_number='+4915123456789',
          assistant_id=123,
          variables={
              'customer_name': 'Max Mustermann',
              'email': 'max@example.com'
          }
      )
      print(f"Call started - Call ID: {result['call_id']}")
  except Exception as e:
      print(f"Error making call: {e}")
  ```
</CodeGroup>

## CRM Integration Examples

### HubSpot Integration

```javascript theme={null}
// HubSpot contact to Famulor call
class HubSpotFamulor {
  constructor(hubspotToken, famularToken) {
    this.hubspotToken = hubspotToken;
    this.famularToken = famularToken;
  }

  async getHubSpotContact(email) {
    const response = await fetch(`https://api.hubapi.com/crm/v3/objects/contacts/search`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${this.hubspotToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        filterGroups: [{
          filters: [{
            propertyName: 'email',
            operator: 'EQ',
            value: email
          }]
        }]
      })
    });
    
    const data = await response.json();
    return data.results[0];
  }

  async callHubSpotContact(email, assistantId) {
    try {
      // 1. Retrieve HubSpot contact
      const contact = await this.getHubSpotContact(email);
      
      if (!contact) {
        throw new Error('Contact not found in HubSpot');
      }

      // 2. Famulor call with HubSpot data
      const callResult = await fetch('https://app.famulor.de/api/user/make_call', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${this.famularToken}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          phone_number: contact.properties.phone,
          assistant_id: assistantId,
          variables: {
            customer_name: `${contact.properties.firstname} ${contact.properties.lastname}`,
            email: contact.properties.email,
            company: contact.properties.company,
            last_interaction: contact.properties.notes_last_updated
          }
        })
      });

      const result = await callResult.json();
      console.log('Call started for HubSpot contact:', result.call_id);
      
      return result;
    } catch (error) {
      console.error('CRM integration error:', error);
      throw error;
    }
  }
}

// Usage
const integration = new HubSpotFamulor(
  process.env.HUBSPOT_TOKEN,
  process.env.FAMULOR_API_KEY
);

integration.callHubSpotContact('max@example.com', 123);
```

### Salesforce Integration

```python theme={null}
import requests
from simple_salesforce import Salesforce

class SalesforceFamulor:
    def __init__(self, sf_username, sf_password, sf_token, famulor_key):
        self.sf = Salesforce(
            username=sf_username,
            password=sf_password,
            security_token=sf_token
        )
        self.famulor_key = famulor_key
    
    def call_salesforce_lead(self, lead_id: str, assistant_id: int):
        """Call Salesforce lead with Famulor"""
        try:
            # 1. Fetch lead data from Salesforce
            lead = self.sf.Lead.get(lead_id)
            
            # 2. Validate phone number
            phone = lead['Phone']
            if not phone:
                raise ValueError("Lead has no phone number")
            
            # 3. Make Famulor call
            payload = {
                'phone_number': phone,
                'assistant_id': assistant_id,
                'variables': {
                    'customer_name': f"{lead['FirstName']} {lead['LastName']}",
                    'email': lead['Email'],
                    'company': lead['Company'],
                    'lead_source': lead['LeadSource'],
                    'salesforce_id': lead_id
                }
            }
            
            response = requests.post(
                'https://app.famulor.de/api/user/make_call',
                headers={
                    'Authorization': f'Bearer {self.famulor_key}',
                    'Content-Type': 'application/json'
                },
                json=payload
            )
            
            result = response.json()
            
            # 4. Update Salesforce with call ID
            self.sf.Lead.update(lead_id, {
                'Description': f"Famulor call started - Call ID: {result['call_id']}"
            })
            
            return result
            
        except Exception as e:
            print(f"Salesforce integration error: {e}")
            raise

# Usage
integration = SalesforceFamulor(
    sf_username='user@company.com',
    sf_password='password',
    sf_token='security_token',
    famulor_key=os.getenv('FAMULOR_API_KEY')
)

integration.call_salesforce_lead('00Q1234567890ABC', 123)
```

## Web Application Integration

### React Component

```jsx theme={null}
import React, { useState, useEffect } from 'react';

const FamuloreCallComponent = () => {
  const [assistants, setAssistants] = useState([]);
  const [selectedAssistant, setSelectedAssistant] = useState('');
  const [phoneNumber, setPhoneNumber] = useState('');
  const [customerName, setCustomerName] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [callResult, setCallResult] = useState(null);

  // Load assistants on component mount
  useEffect(() => {
    fetchAssistants();
  }, []);

  const fetchAssistants = async () => {
    try {
      const response = await fetch('/api/famulor/assistants'); // Backend proxy
      const data = await response.json();
      setAssistants(data);
      if (data.length > 0) {
        setSelectedAssistant(data[0].id);
      }
    } catch (error) {
      console.error('Error loading assistants:', error);
    }
  };

  const handleCall = async (e) => {
    e.preventDefault();
    setIsLoading(true);
    setCallResult(null);

    try {
      const response = await fetch('/api/famulor/make-call', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          phone_number: phoneNumber,
          assistant_id: selectedAssistant,
          variables: {
            customer_name: customerName
          }
        }),
      });

      const result = await response.json();

      if (response.ok) {
        setCallResult({
          success: true,
          message: `Call started successfully! Call ID: ${result.call_id}`,
          callId: result.call_id
        });
        // Reset form
        setPhoneNumber('');
        setCustomerName('');
      } else {
        setCallResult({
          success: false,
          message: result.message || 'Error making call'
        });
      }
    } catch (error) {
      setCallResult({
        success: false,
        message: 'Network error: ' + error.message
      });
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <div className="famulor-call-component">
      <h2>Start Voice Agent Call</h2>
      
      <form onSubmit={handleCall}>
        <div className="form-group">
          <label htmlFor="assistant">AI Assistant:</label>
          <select
            id="assistant"
            value={selectedAssistant}
            onChange={(e) => setSelectedAssistant(e.target.value)}
            required
          >
            <option value="">Select an assistant...</option>
            {assistants.map(assistant => (
              <option key={assistant.id} value={assistant.id}>
                {assistant.name}
              </option>
            ))}
          </select>
        </div>

        <div className="form-group">
          <label htmlFor="phone">Phone Number:</label>
          <input
            type="tel"
            id="phone"
            value={phoneNumber}
            onChange={(e) => setPhoneNumber(e.target.value)}
            placeholder="+4915123456789"
            required
          />
        </div>

        <div className="form-group">
          <label htmlFor="name">Customer Name:</label>
          <input
            type="text"
            id="name"
            value={customerName}
            onChange={(e) => setCustomerName(e.target.value)}
            placeholder="Max Mustermann"
          />
        </div>

        <button 
          type="submit" 
          disabled={isLoading || !selectedAssistant}
          className="call-button"
        >
          {isLoading ? 'Calling...' : 'Start Call'}
        </button>
      </form>

      {callResult && (
        <div className={`result ${callResult.success ? 'success' : 'error'}`}>
          <p>{callResult.message}</p>
          {callResult.success && (
            <p>
              <small>Call ID: {callResult.callId}</small>
            </p>
          )}
        </div>
      )}
    </div>
  );
};

export default FamuloreCallComponent;
```

### Backend API Proxy (Express.js)

```javascript theme={null}
// Backend proxy for secure API calls
const express = require('express');
const router = express.Router();

// Fetch assistants
router.get('/assistants', async (req, res) => {
  try {
    const response = await fetch('https://app.famulor.de/api/user/assistants', {
      headers: {
        'Authorization': `Bearer ${process.env.FAMULOR_API_KEY}`,
        'Content-Type': 'application/json'
      }
    });

    if (!response.ok) {
      throw new Error(`API Error: ${response.status}`);
    }

    const assistants = await response.json();
    res.json(assistants);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Make a call
router.post('/make-call', async (req, res) => {
  try {
    const { phone_number, assistant_id, variables } = req.body;

    // Input validation
    if (!phone_number || !assistant_id) {
      return res.status(400).json({
        error: 'phone_number and assistant_id are required'
      });
    }

    const response = await fetch('https://app.famulor.de/api/user/make_call', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.FAMULOR_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        phone_number,
        assistant_id,
        variables: variables || {}
      })
    });

    const result = await response.json();

    if (!response.ok) {
      return res.status(response.status).json(result);
    }

    res.json(result);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

module.exports = router;
```

## Error Handling and Retry Logic

```javascript theme={null}
class FamuloreApiClient {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = 'https://app.famulor.de/api';
    this.maxRetries = 3;
    this.retryDelay = 1000; // 1 second
  }

  async makeRequest(endpoint, options = {}) {
    const url = `${this.baseUrl}${endpoint}`;
    
    const defaultOptions = {
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json',
        ...options.headers
      }
    };

    const requestOptions = { ...defaultOptions, ...options };

    for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
      try {
        const response = await fetch(url, requestOptions);
        
        // Handle rate limiting
        if (response.status === 429) {
          const retryAfter = response.headers.get('Retry-After') || this.retryDelay / 1000;
          console.log(`Rate limit reached, waiting ${retryAfter} seconds...`);
          await this.sleep(retryAfter * 1000);
          continue;
        }

        const data = await response.json();

        if (!response.ok) {
          throw new Error(`HTTP ${response.status}: ${data.message || 'Unknown error'}`);
        }

        return data;
      } catch (error) {
        console.log(`Attempt ${attempt}/${this.maxRetries} failed:`, error.message);
        
        if (attempt === this.maxRetries) {
          throw error;
        }

        // Exponential backoff
        const delay = this.retryDelay * Math.pow(2, attempt - 1);
        await this.sleep(delay);
      }
    }
  }

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

  // API methods
  async getAssistants() {
    return this.makeRequest('/user/assistants');
  }

  async makeCall(phoneNumber, assistantId, variables = {}) {
    return this.makeRequest('/user/make_call', {
      method: 'POST',
      body: JSON.stringify({
        phone_number: phoneNumber,
        assistant_id: assistantId,
        variables
      })
    });
  }

  async getCalls(limit = 50) {
    return this.makeRequest(`/user/calls?limit=${limit}`);
  }
}

// Usage with error handling
const client = new FamuloreApiClient(process.env.FAMULOR_API_KEY);

async function robustCall() {
  try {
    const result = await client.makeCall('+4915123456789', 123, {
      customer_name: 'Test Kunde'
    });
    console.log('Call successful:', result.call_id);
  } catch (error) {
    console.error('Final error after all attempts:', error.message);
    // Notification, logging, etc. could happen here
  }
}
```

## Batch Processing

```python theme={null}
import asyncio
import aiohttp
from typing import List, Dict
import csv

class BatchCallProcessor:
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = 'https://app.famulor.de/api'
        self.session = None
        
    async def __aenter__(self):
        self.session = aiohttp.ClientSession(
            headers={'Authorization': f'Bearer {self.api_key}'}
        )
        return self
        
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        if self.session:
            await self.session.close()

    async def make_single_call(self, phone_number: str, assistant_id: int, 
                              variables: Dict = None, semaphore=None):
        """Make a single call asynchronously"""
        async with semaphore:  # Rate limiting
            try:
                payload = {
                    'phone_number': phone_number,
                    'assistant_id': assistant_id,
                    'variables': variables or {}
                }
                
                async with self.session.post(
                    f'{self.base_url}/user/make_call',
                    json=payload
                ) as response:
                    result = await response.json()
                    
                    if response.status == 200:
                        return {
                            'status': 'success',
                            'phone_number': phone_number,
                            'call_id': result['call_id'],
                            'message': result['message']
                        }
                    else:
                        return {
                            'status': 'error',
                            'phone_number': phone_number,
                            'error': result.get('message', 'Unknown error')
                        }
                        
            except Exception as e:
                return {
                    'status': 'error',
                    'phone_number': phone_number,
                    'error': str(e)
                }

    async def process_batch_calls(self, call_list: List[Dict], 
                                 concurrent_limit: int = 5):
        """Process a batch of calls"""
        semaphore = asyncio.Semaphore(concurrent_limit)
        
        tasks = []
        for call_data in call_list:
            task = self.make_single_call(
                phone_number=call_data['phone_number'],
                assistant_id=call_data['assistant_id'],
                variables=call_data.get('variables', {}),
                semaphore=semaphore
            )
            tasks.append(task)
        
        results = await asyncio.gather(*tasks, return_exceptions=True)
        return results

    @classmethod
    def from_csv(cls, csv_file: str, assistant_id: int, api_key: str):
        """Convert CSV file into call list"""
        call_list = []
        
        with open(csv_file, 'r', encoding='utf-8') as file:
            reader = csv.DictReader(file)
            for row in reader:
                call_data = {
                    'phone_number': row['phone_number'],
                    'assistant_id': assistant_id,
                    'variables': {
                        'customer_name': row.get('name', ''),
                        'email': row.get('email', ''),
                        'company': row.get('company', '')
                    }
                }
                call_list.append(call_data)
        
        return call_list

# Example usage
async def main():
    api_key = os.getenv('FAMULOR_API_KEY')
    
    # Load CSV data
    call_list = BatchCallProcessor.from_csv('leads.csv', assistant_id=123, api_key=api_key)
    
    # Batch processing
    async with BatchCallProcessor(api_key) as processor:
        results = await processor.process_batch_calls(call_list, concurrent_limit=3)
        
        # Evaluate results
        success_count = sum(1 for r in results if r['status'] == 'success')
        error_count = len(results) - success_count
        
        print(f"Processing complete:")
        print(f"✅ Successful: {success_count}")
        print(f"❌ Errors: {error_count}")
        
        # Error details
        for result in results:
            if result['status'] == 'error':
                print(f"Error for {result['phone_number']}: {result['error']}")

# Run
if __name__ == "__main__":
    asyncio.run(main())
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Webhook Integration" icon="link" href="/en/api-reference/webhooks/post-call">
    Automate post-call data processing
  </Card>

  <Card title="Automation Platform" icon="wand-magic-sparkles" href="/en/automation-platform/introduction">
    No-code alternative for complex workflows
  </Card>

  <Card title="API Reference" icon="book" href="/en/api-reference/introduction">
    Complete API documentation
  </Card>

  <Card title="Best Practices" icon="lightbulb" href="/en/developers/authentication-guide">
    Security and optimization
  </Card>
</CardGroup>

<Tip>
  Related pages: [Overview](/en/developers/overview), and [Authentication Guide](/en/developers/authentication-guide).
</Tip>
