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

# Developer documentation

> Integrate Thoughtly with the REST API and webhooks — authenticate, list agents, trigger calls, manage contacts, and stream live call events.

export const NextSection = ({title, icon, href, description}) => <div>
		<br />
		<Card title={title} icon={icon} href={href} horizontal="true">
			{description}
		</Card>
	</div>;

After [building a Voice Agent](/getting-started/quick-start) using our no-code interface, deploy it using our RESTful API to integrate with your existing systems or build custom solutions.

## Two Approaches to Triggering Calls

### 1. API-Based Triggering (Programmatic)

Trigger calls programmatically from your application code.

**Process**:

1. Create a Contact using `/contact/create`
2. Trigger a call using `/contact/call` with the Contact ID
3. Receive webhook notifications about call status

**Best For**:

* Custom applications
* Direct system integration
* Real-time call triggering
* Fine-grained control

**Example**:

```javascript theme={null}
// Create a contact
const contact = await fetch('https://api.thoughtly.com/contact/create', {
  method: 'POST',
  headers: {
    'x-api-token': 'your_api_token',
    'team_id': 'your_team_id',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    phone_number: '+15551234567',
    name: 'John Doe',
    email: 'john@example.com'
  })
});

// Trigger the call
const call = await fetch('https://api.thoughtly.com/contact/call', {
  method: 'POST',
  headers: {
    'x-api-token': 'your_api_token',
    'team_id': 'your_team_id',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    contact_id: contact.id,
    agent_id: 'your_agent_id'
  })
});
```

### 2. Automation-Based Triggering (Recommended)

Use [Automations](/automations/getting-started) to trigger calls via webhooks.

**Process**:

1. Set up an Automation with a webhook trigger
2. Send webhook POST requests to trigger calls
3. Configure workflow logic in the Thoughtly UI

**Best For**:

* Team collaboration (non-engineers can modify logic)
* Complex workflows
* No-code/low-code solutions
* Rapid iteration

**Benefits**:

* **Non-technical teams** can modify call logic
* **No code changes** required for workflow updates
* **Visual workflow builder** for complex scenarios
* **Flexible webhook payload** for dynamic data

**Learn More**: [Automations Documentation →](/automations/getting-started)

## A Note on Naming

In the API, Voice Agents are referred to as **interviews** (e.g., `/interview/{interview_id}`). This is an internal naming convention — "interview" and "Voice Agent" mean the same thing. All dashboard and documentation references use "Voice Agent," but API paths and response payloads use `interview`.

## Authentication

All API requests require two headers:

```javascript theme={null}
{
  'x-api-token': 'your_api_token',      // Found in dashboard settings
  'team_id': 'your_team_id'              // Your team identifier
}
```

**Find Your Credentials**:

1. Log into [Thoughtly Dashboard](https://app.thoughtly.com)
2. Navigate to **[Settings → Developer](/platform/settings/developer)**
3. Copy your API token and Team ID

<Warning>
  **Security Best Practices**:

  * Never expose your API token in client-side code. Always make API calls from your backend server.
  * If an API token is compromised, revoke it immediately through the dashboard to prevent unauthorized access.
  * Regularly rotate API tokens as part of your security practices.
</Warning>

## Rate Limits

* **Limit**: 100 requests per minute
* **Response**: `429 Too Many Requests` if exceeded
* **Best Practice**: Implement exponential backoff for retries

```javascript theme={null}
async function apiCallWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await fetch(url, options);
    
    if (response.status === 429) {
      // Wait before retrying
      await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000));
      continue;
    }
    
    return response;
  }
  throw new Error('Max retries exceeded');
}
```

## Common Integration Patterns

### Pattern 1: CRM-Triggered Calls

When a new lead is added to your CRM, trigger a qualification call:

```javascript theme={null}
// Webhook from CRM (e.g., Salesforce)
app.post('/webhook/new-lead', async (req, res) => {
  const { phone, name, email, company } = req.body;
  
  // Create contact in Thoughtly
  const contact = await thoughtly.createContact({
    phone_number: phone,
    name: name,
    email: email,
    custom_fields: { company }
  });
  
  // Trigger call with qualification agent
  await thoughtly.triggerCall({
    contact_id: contact.id,
    agent_id: 'qualification_agent_id'
  });
  
  res.status(200).send('Call triggered');
});
```

### Pattern 2: Calendar-Based Reminders

Send appointment reminders 24 hours before scheduled appointments:

```javascript theme={null}
// Daily cron job
cron.schedule('0 9 * * *', async () => {
  const tomorrow = new Date();
  tomorrow.setDate(tomorrow.getDate() + 1);
  
  // Get appointments for tomorrow
  const appointments = await getAppointments(tomorrow);
  
  // Trigger reminder call for each
  for (const apt of appointments) {
    await thoughtly.triggerAutomation({
      webhook_url: 'your_automation_webhook',
      payload: {
        contact_phone: apt.phone,
        appointment_time: apt.time,
        service_name: apt.service
      }
    });
  }
});
```

### Pattern 3: Real-Time Customer Support

Connect voice agent to your support queue:

```javascript theme={null}
// When customer requests callback
app.post('/request-callback', async (req, res) => {
  const { phone, issue_type, priority } = req.body;
  
  // Create contact with context
  const contact = await thoughtly.createContact({
    phone_number: phone,
    custom_fields: {
      issue_type,
      priority,
      timestamp: new Date().toISOString()
    }
  });
  
  // Trigger immediate callback
  await thoughtly.triggerCall({
    contact_id: contact.id,
    agent_id: 'support_agent_id',
    priority: priority === 'urgent' ? 'high' : 'normal'
  });
  
  res.json({ message: 'Callback initiated' });
});
```

## Webhooks: Receiving Call Data

<Note>
  If your goal is to get structured data **after a call ends**, use an Automation with the **Thoughtly -> On Call Completed** trigger. It supports scoping to **one agent, multiple agents, or All Agents**, and keeps the workflow in Thoughtly (no webhook server required). See [Triggers](/automations/triggers#thoughtly-on-call-completed).
</Note>

If you need server-to-server delivery to your own infrastructure, configure webhooks to receive real-time updates about calls:

```javascript theme={null}
// Webhook endpoint to receive call completion data
app.post('/webhook/call-completed', async (req, res) => {
  const { 
    call_id, 
    agent_id, 
    duration, 
    outcome, 
    transcript,
    variables 
  } = req.body;
  
  // Update your CRM
  await crm.updateLead({
    phone: variables.phone,
    call_outcome: outcome,
    call_duration: duration,
    last_contacted: new Date()
  });
  
  // Send notification to sales team
  if (outcome === 'qualified') {
    await slack.notify('#sales', `New qualified lead: ${variables.name}`);
  }
  
  res.status(200).send('Webhook processed');
});
```

**Learn More**: [Webhooks Documentation →](/integrations/webhooks)

## API Reference

For complete API documentation, see:

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

  <Card title="Webhooks" icon="webhook" href="/integrations/webhooks">
    Receive real-time notifications about events
  </Card>

  <Card title="Automations" icon="code-branch" href="/automations/getting-started">
    Build workflows without code
  </Card>

  <Card title="Integration Examples" icon="plug" href="/integrations/getting-started">
    Connect with popular platforms
  </Card>
</CardGroup>

## Best Practices

### 1. Always Validate Phone Numbers

```javascript theme={null}
function isValidPhone(phone) {
  // E.164 format: +[country code][number]
  const regex = /^\+[1-9]\d{1,14}$/;
  return regex.test(phone);
}
```

### 2. Handle Errors Gracefully

```javascript theme={null}
try {
  await thoughtly.triggerCall({ contact_id, agent_id });
} catch (error) {
  if (error.status === 429) {
    // Rate limited - retry later
    await queueForRetry({ contact_id, agent_id });
  } else if (error.status === 403) {
    // Forbidden - check if outbound calling is disabled
    logger.error('Outbound calling may be disabled for this agent', error);
  } else if (error.status === 400) {
    // Invalid request - log and alert
    logger.error('Invalid API request', error);
  } else {
    // Unexpected error
    throw error;
  }
}
```

### 3. Use Environment Variables

```javascript theme={null}
// .env file
THOUGHTLY_API_TOKEN=your_token_here
THOUGHTLY_TEAM_ID=your_team_id_here
THOUGHTLY_AGENT_ID=your_agent_id_here

// In your code
const config = {
  apiToken: process.env.THOUGHTLY_API_TOKEN,
  teamId: process.env.THOUGHTLY_TEAM_ID,
  agentId: process.env.THOUGHTLY_AGENT_ID
};
```

### 4. Implement Logging

```javascript theme={null}
const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'thoughtly.log' })
  ]
});

logger.info('Call triggered', { 
  contact_id, 
  agent_id, 
  timestamp: new Date() 
});
```

## Support & Resources

* **API Reference**: [Complete documentation →](/api-reference)
* **Email**: [support@thoughtly.com](mailto:support@thoughtly.com)

<NextSection title="API Reference" icon="book" href="/api-reference" description="Explore complete API endpoint documentation ->" />
