# Build and Configure AI Voice Agents
Source: https://docs.usesimple.ai/agents
Learn how to configure Simple AI agents with prompts, voices, tools, knowledge, analyzers, and conversation flows for phone, SMS, and chat.
Agents define everything about how your AI handles conversations — the instructions, voice, tools, knowledge, and conversation flow. Every call in Simple AI is powered by an agent.
You can create agents from the Simple AI dashboard or through the [API](/api-reference/agents/create-agent).
## What Makes Up an Agent
An agent is built from a few core building blocks:
| Component | Purpose |
| - | - |
| **Standard operating procedures** | Step-by-step instructions the agent follows during conversations |
| **Knowledge bases** | Reference material the agent can look up on the fly — product info, policies, FAQs |
| **Tools** | API connections that let the agent take actions — look up orders, update records, transfer calls |
| **Voice** | How the agent sounds during calls |
| **Parameters** | Dynamic values passed in per call — customer name, order ID, account details |
## Standard Operating Procedures
The most important part of an agent is its instructions — the standard operating procedures (SOPs) that tell the agent exactly how to handle a conversation.
Good SOPs are structured, specific, and cover the scenarios the agent will encounter. Rather than giving the agent a vague goal, you tell it exactly what to do at each stage.
For example, a support agent's SOP might include:
1. Greet the caller and ask how you can help
2. If the caller asks about an order, ask for the order number and look it up using the order status tool
3. Read back the order status and ask if there is anything else
4. If the caller asks about returns, reference the return policy from the knowledge base
5. If the issue cannot be resolved, transfer the call to a live agent
SOPs work best when they are clear about what to do in each situation, including edge cases. See [Prompting](/prompting) for detailed guidance on writing effective agent instructions.
## Knowledge and Policies
Agents often need access to information that is too detailed or too frequently updated to put directly in the SOP — product catalogs, pricing tables, compliance policies, troubleshooting guides, or FAQ content.
This is where [knowledge bases](/knowledgebase) come in. You attach one or more knowledge bases to an agent, and the agent searches them during the conversation when it needs to answer a question or reference a policy.
This separation keeps your SOPs focused on the conversation flow while the knowledge base handles the reference material.
Common use cases:
* **Company policies** — return policies, warranty terms, billing rules, escalation criteria
* **Product information** — features, pricing, specifications, compatibility
* **Troubleshooting guides** — step-by-step diagnostic procedures
* **FAQs** — common questions and their approved answers
* **Compliance scripts** — required disclosures, legal language, opt-out instructions
You can also publish your knowledge base content as a customer-facing [help center](/knowledge-base-help-center).
## Basic Agents vs. Flow-Based Agents
### Basic Agents
A basic agent has a single set of SOPs, one voice, and an optional set of [tools](/tools) and [knowledge bases](/knowledgebase). This is the fastest way to get started and works well for straightforward use cases like appointment confirmations, simple Q\&A, or quick tests.
### Flow-Based Agents
Flow-based agents give you fine-grained control over the conversation by defining a state machine. Each **node** in the flow represents a distinct stage of the conversation, and you define which nodes the agent can transition to from each state.
This is useful when:
* The agent needs to collect specific information in a specific order
* Different parts of the conversation need different tools or knowledge
* You want to enforce a structured workflow (e.g., verify identity, then look up order, then offer resolution)
* Different stages require different SOPs
Each node in the flow has:
* **SOPs** — instructions specific to that stage of the conversation
* **Tools** — the set of [tools](/tools) available in that node
* **Knowledge bases** — reference material relevant to that node
* **Transitions** — which other nodes the agent can move to from here
For example, a customer service agent might have:
1. **Greeting** — verify the caller's identity using an account lookup tool
2. **Issue triage** — determine what the caller needs and route to the right node
3. **Order support** — look up orders, check shipping, process returns (with order tools and return policy knowledge base)
4. **Billing support** — review charges, process refunds (with billing tools and billing policy knowledge base)
5. **Escalation** — transfer to a live agent when the issue cannot be resolved
Each node only has access to the tools, knowledge, and transitions it needs, which makes the agent more reliable and predictable.
The agent chooses transitions from the conversation, but it does not have to be the one deciding. A [code action](/code-actions) can name the node to run next from your own logic — useful when the routing rule is a fact rather than a judgment call, such as sending a delinquent account straight to collections.
## Agent Voice
Every agent has a voice that controls how it sounds during calls. You can browse available voices through the dashboard or the [Voices API](/api-reference/voices/list-voices) and assign one to your agent.
## Agent Tools
Agents can interact with external services during a call using [tools](/tools). You define the URL, headers, body format, and a description of when the tool should be used. The agent decides when to call each tool based on the conversation and its SOPs.
Built-in capabilities include:
* **DTMF / IVR navigation** — the agent can press keys on the keypad to navigate phone menus
* **End call** — the agent can hang up when the conversation is complete
* **Transfer** — the agent can transfer the call to another number or agent
For external service integrations, see [Integrations](/integrations/overview).
## Agent Parameters
Agents can accept parameters that are passed in when a call is created. These parameters can be referenced in the SOPs using `{{parameter_name}}` syntax, making each call personalized.
For example, an agent with the instruction "You are calling about order " can receive those values dynamically from the API or from a [campaign](/campaigns) CSV upload.
## API Reference
* [List agents](/api-reference/agents/list-agents)
* [Get agent](/api-reference/agents/get-agent)
* [Create agent](/api-reference/agents/create-agent)
* [Update agent](/api-reference/agents/update-agent)
# Extract Structured Insights from AI Phone Calls
Source: https://docs.usesimple.ai/analyzers
Configure post-call analyzers that turn Simple AI conversations into structured fields for reporting, automation, quality review, and integrations.
Analyzers let you run post-call analysis on interactions so you can extract structured information from transcripts and call data. You can use them to identify outcomes, classify conversations, and make call review more consistent.
## What Analyzers Do
You can use analyzers to:
* extract structured data from completed calls
* classify calls by outcome, sentiment, or intent
* power reporting and downstream workflows
* review call quality more consistently
## How to Add Analyzers
You can create analyzers in the dashboard and then enable them for the relevant agents, or make them always enabled for all agents.
When you create an analyzer, you define:
* a title
* a prompt
* an output type
* which agents the analyzer should run on
This is useful when you want to standardize the way insights are extracted across many calls.
## Output Types
Analyzers can return different kinds of outputs depending on what you want to capture.
Available output types include:
* free text
* number
* boolean
* enum
* structured data
Choose the simplest output type that still gives you the result you need. For example, if you only need a yes or no answer, a boolean output is usually better than free text.
## How to Define Structured Data
If you want an analyzer to return multiple fields in a consistent format, you can use structured data output.
In the dashboard, the `Structured Data Fields` section lets you switch between `UI` and `Raw`.
You can define structured output in two ways:
* use the UI to add fields and set the type for each field
* use raw text fields to define the structure manually
In the UI view, you can add fields one by one and define:
* the field name
* the field type
* a description
* whether the field is required
Available field types in structured output include:
* string
* number
* boolean
* object
* array
If you switch to Raw mode, you can define the same structure directly as JSON. The raw editor expects a JSON array of field definitions.
For example:
```json theme={null}
[
{
"name": "customerName",
"type": "string",
"description": "The customer's full name",
"required": true
},
{
"name": "issueResolved",
"type": "boolean",
"description": "Whether the customer's issue was resolved",
"required": true
}
]
```
Structured output also supports nested objects and arrays, which is useful when you need to capture more complex results in a predictable JSON structure.
This is useful when you want to collect data in a format that is easier to review, filter, export, or use in downstream workflows.
## How Analyzers Work
After a call is completed, Simple AI can run any analyzers associated with that interaction.
If an analyzer is always enabled, it can run on calls across all agents. Otherwise, it runs only for the agents where it has been enabled.
Analyzer results are stored with the call, which makes them available later for review, filtering, exports, and reporting.
## Rerun Analyzers on Previous Calls
If you create a new analyzer or update the prompt or output you want, you can rerun that analyzer on previous calls.
This is useful when:
* you added a new analyzer and want to backfill older calls
* you improved the analyzer prompt and want updated results
* you want to reprocess a date range for reporting or QA
When you rerun an analyzer, Simple AI can reprocess either eligible calls from a date range you select or an individual call when you want to refresh results for a specific interaction.
## How Reruns Work
Reruns are supported for previous calls and can be applied across a date range or on an individual call.
Depending on how you trigger the rerun, it may apply to:
* a single call
* all calls in the selected date range
* calls associated with the agents where that analyzer is enabled
If you are using date range reruns, it is a good idea to choose a clear range so you only reprocess the calls you actually want to update.
## Best Practices
Analyzers work best when the prompt is very specific about what should be extracted and what format the answer should use.
For example, instead of writing `Analyze whether this call went well`, it is usually better to write `Return true if the customer's issue was fully resolved during the call, otherwise return false`.
If you are using structured outputs, define the fields as clearly as possible so the result is easier to use later.
## Video Demo
### Creating and Rerunning Analyzers
### Rerunning Analyzers on Individual Calls
# Create an AI Voice Agent with the API
Source: https://docs.usesimple.ai/api-reference/agents/create-agent
POST /agents
Create a reusable Simple AI voice agent with a prompt, voice, tools, and configuration that you can reference when starting calls.
Learn how these settings work together in the [AI agents guide](/agents).
# Get an AI Agent Configuration with the API
Source: https://docs.usesimple.ai/api-reference/agents/get-agent
GET /agents/{agent_id}
Retrieve the latest configuration for a Simple AI voice agent, or request a specific saved version when you need stable call behavior.
See the [AI agents guide](/agents) for an overview of prompts, voices, tools, and agent versions.
# List AI Voice Agents with the Simple AI API
Source: https://docs.usesimple.ai/api-reference/agents/list-agents
GET /agents
List the Simple AI voice agents available to your organization so you can inspect configurations and select an agent for a call.
Learn how to configure and use agents in the [AI agents guide](/agents).
# Update an AI Voice Agent with the API
Source: https://docs.usesimple.ai/api-reference/agents/update-agent
PATCH /agents/{agent_id}
Update the prompt, voice, tools, or other configuration for an existing Simple AI agent while preserving any fields you omit.
If a field is not provided, it will not be updated. To remove a field, set it to `null`.
See the [AI agents guide](/agents) for details about the configuration you can apply to an agent.
### Pre-call webhook fields
You can configure a webhook that runs before an inbound call is created. Include any of the following fields when updating an agent:
* `pre_call_webhook_url` – URL that will receive a `POST` request before the call starts.
* `pre_call_webhook_auth_token` – optional bearer token sent in the `Authorization` header.
* `pre_call_webhook_timeout` – how many seconds to wait for the webhook response (defaults to `5`).
* `pre_call_webhook_enabled` – set to `true` to enable the webhook.
See [Call Flow](/call-flow) for details on when this webhook is triggered during a call.
# Create a Note for an AI Phone Call
Source: https://docs.usesimple.ai/api-reference/call-notes/create-note
POST /calls/{uuid}/call_notes
Add a note to a specific Simple AI phone call, with an optional score that your team can use for review and quality tracking.
See the [calls guide](/calls) for the lifecycle and data available for each AI phone call.
# List Notes for an AI Phone Call
Source: https://docs.usesimple.ai/api-reference/call-notes/list-notes
GET /calls/{uuid}/call_notes
Retrieve the paginated notes and optional review scores attached to a specific Simple AI phone call for quality analysis.
See the [calls guide](/calls) for the lifecycle and data available for each AI phone call.
# Create a Tag for AI Phone Calls
Source: https://docs.usesimple.ai/api-reference/call-tags/create-tag
POST /call_tags
Create a reusable tag that your organization can apply to Simple AI calls for filtering, reporting, and outcome tracking.
Learn how to organize calls with labels in the [call tags guide](/tags).
# List Tags for AI Phone Calls
Source: https://docs.usesimple.ai/api-reference/call-tags/list-tags
GET /call_tags
Retrieve your organization's paginated Simple AI call tags so you can display, select, and apply consistent call labels.
Learn how to organize calls with labels in the [call tags guide](/tags).
# Update Tags on an AI Phone Call
Source: https://docs.usesimple.ai/api-reference/call-tags/update-call-tags
PATCH /calls/{uuid}/tags
Replace the tags associated with a specific Simple AI phone call to keep call filtering, reporting, and outcomes current.
Learn how tags support call organization and analysis in the [call tags guide](/tags).
# Cancel an AI Phone Call with the API
Source: https://docs.usesimple.ai/api-reference/calls/cancel-call
POST /calls/{uuid}/cancel
Cancel a queued Simple AI outbound phone call before it is placed, using the call's identifier from the create call response.
Only calls with status `enqueued` can be cancelled. A call that has already started or finished returns `409 Conflict`. Cancelling a call that is already cancelled succeeds, so the request is safe to retry.
See the [call object guide](/call-object#status) for the full status lifecycle.
# Create an AI Phone Call with the API
Source: https://docs.usesimple.ai/api-reference/calls/create-call
POST /calls
Start an outbound AI phone call with a reusable agent or inline prompt, a destination number, and optional call parameters.
See the [AI phone calls guide](/calls) for call setup, lifecycle, data, and language support.
# Delete an AI Phone Call with the API
Source: https://docs.usesimple.ai/api-reference/calls/delete-call
DELETE /calls/{uuid}
Delete a Simple AI phone call and its associated stored data when the call record is no longer required by your organization.
See the [AI phone calls guide](/calls) for details about the data associated with a completed call.
# Find an AI Phone Call by Custom Attribute
Source: https://docs.usesimple.ai/api-reference/calls/find-by-attribute
GET /calls/find_by_attribute
Find the most recent Simple AI phone call matching a custom parameter or external identifier key and value supplied by your system.
## Overview
Use this endpoint to reconnect your application's identifiers with the corresponding call. See the [AI phone calls guide](/calls) for more about call data.
This endpoint allows you to find a call by searching for specific attributes in either the call's `params` or `external_identifiers` fields. It returns the **most recent call** that matches your search criteria.
## Search Options
You must provide at least one of the following search criteria:
### Search by Parameters
* `param_key` + `param_value`: Search for a call where the `params` field contains the specified key-value pair
### Search by External Identifiers
* `external_identifier_key` + `external_identifier_value`: Search for a call where the `external_identifiers` field contains the specified key-value pair
## Example Use Cases
1. **Find a call by customer ID**:
```text theme={null}
GET /calls/find_by_attribute?external_identifier_key=customer_id&external_identifier_value=CUST-123456
```
2. **Find a call by appointment type**:
```text theme={null}
GET /calls/find_by_attribute?param_key=appointment_type¶m_value=morning
```
3. **Return only fields needed by an IVR or reporting integration**:
```text theme={null}
GET /calls/find_by_attribute?param_key=sip.headers.x_callsessionid¶m_value=SESSION-ID&fields=id,summary,status,params.disposition
```
## Compact responses
Use the optional `fields` parameter to return only the data your integration needs, with a maximum of 50 fields. Supported call fields are `id`, `summary`, `status`, `duration`, `transferred`, `agent_id`, and `agent_commit_id`. Select an individual call parameter with `params.`.
For example, `fields=id,summary,status,params.disposition` returns:
```json theme={null}
{
"success": true,
"call": {
"id": "346fe020-e8c2-4abe-9d40-a4992b016bd6",
"summary": "The caller confirmed their appointment.",
"status": "completed",
"params": {
"disposition": "confirmed"
}
}
}
```
Missing requested parameter keys are returned as `null`. Unsupported fields return a validation error. When `fields` is omitted, the endpoint returns the complete call details for backward compatibility.
## Response
Returns a `CallFindByAttributeResponse` object containing:
* `success`: Boolean indicating if a call was found
* `call`: The requested compact fields, or the full call details when `fields` is omitted
## Notes
* Only returns the **most recent call** matching the criteria
* Searches are case-sensitive
* Returns 404 if no matching call is found
* Returns 422 if no search criteria are provided
# Get an AI Phone Call with the Simple AI API
Source: https://docs.usesimple.ai/api-reference/calls/get-call
GET /calls/{uuid}
Retrieve a specific Simple AI phone call, including its current status and the call data available to your organization.
See the [AI phone calls guide](/calls) for an overview of the call lifecycle and the data produced after completion.
Recording links in `recording_presigned_url` are temporary HTTPS playback URLs. Use each complete URL as returned, including its query parameters; the hostname and URL structure may change. Anyone with a valid link can access its recording until the link expires.
# List AI Phone Calls with the Simple AI API
Source: https://docs.usesimple.ai/api-reference/calls/list-calls
GET /calls
Retrieve and paginate the inbound and outbound Simple AI phone calls available to your organization for review and processing.
See the [AI phone calls guide](/calls) for an overview of call setup, lifecycle, and generated data.
# Delete Stored Contact Data with the API
Source: https://docs.usesimple.ai/api-reference/contacts/delete-contact-data
DELETE /contacts/{phoneNumber}/data
Delete the data stored for a contact phone number when your external system no longer wants Simple AI to return that state.
See the [integrations overview](/integrations/overview) for ways to connect external systems with Simple AI.
Use this endpoint when the state stored against a caller's phone number is no longer needed. Deletion removes the current value from reads while keeping the contact itself and its revision history.
Deletion is idempotent. If no current data exists, the endpoint still returns success with `"deleted": false`.
Data is scoped to your organization. US numbers can be formatted or sent as digits. Non-US numbers must include the `+` country code. URL-encode the `+` when it appears in the path.
Callers that do not have a phone number can be keyed by any other identifier, such as a chat or SIP identifier, of up to 128 characters. Simple stores those identifiers as provided and returns them unchanged in `phoneNumber`.
```bash theme={null}
curl -X DELETE 'https://api.usesimple.ai/api/v1/contacts/14155550100/data' \
-H 'Authorization: Bearer YOUR_API_KEY'
```
The response confirms the normalized phone number and whether a current value was deleted:
```json theme={null}
{
"phoneNumber": "+14155550100",
"deleted": true
}
```
The endpoint returns `401` when authentication fails and `422` when the caller identifier is invalid.
# Merge Stored Contact Data with the API
Source: https://docs.usesimple.ai/api-reference/contacts/merge-contact-data
PATCH /contacts/{phoneNumber}/data
Shallow-merge selected fields into the data stored for a contact phone number without replacing the contact's complete state.
See the [integrations overview](/integrations/overview) for ways to connect external systems with Simple AI.
Use this endpoint when an external system needs to update only part of the state stored against a caller's phone number. PATCH merges top-level fields into the existing object without replacing unrelated fields.
Set a field to `null` to delete that field. If no data exists yet, the patch starts with an empty object. A PATCH without `ttlSeconds` preserves the existing expiration; provide a new value to replace it.
The data is scoped to your organization and the phone number. A previously unknown number gets a contact automatically. US numbers can be formatted or sent as digits. Non-US numbers must include the `+` country code. URL-encode the `+` when it appears in the path.
Callers that do not have a phone number can be keyed by any other identifier, such as a chat or SIP identifier, of up to 128 characters. Simple stores those identifiers as provided and returns them unchanged in `phoneNumber`.
```bash theme={null}
curl -X PATCH 'https://api.usesimple.ai/api/v1/contacts/14155550100/data' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"data": {
"routingStatus": "completed",
"five9ConversationId": null
}
}'
```
For example, if the stored object is `{"five9ConversationId":"conv-48291","routingStatus":"queued"}`, the request above leaves `{"routingStatus":"completed"}`.
The response contains the normalized phone number, the merged object, its revision, and expiration metadata:
```json theme={null}
{
"phoneNumber": "+14155550100",
"data": {
"routingStatus": "completed"
},
"revision": 2,
"ttlSeconds": null,
"expiresAt": null
}
```
The serialized `data` object may be at most 64 KiB. When supplied, `ttlSeconds` must be an integer from 60 seconds through 90 days. Without an explicit TTL, a record has no expiry by default or keeps its current expiry when patching an existing record.
The endpoint returns `401` when authentication fails and `422` when the caller identifier or request body is invalid.
# Read Stored Contact Data with the API
Source: https://docs.usesimple.ai/api-reference/contacts/read-contact-data
GET /contacts/{phoneNumber}/data
Retrieve the state stored for a contact phone number so a connected workflow can use current customer context during an interaction.
See the [integrations overview](/integrations/overview) for ways to use contact context with connected systems.
Use this endpoint to retrieve state that Simple or another connected system previously stored for a caller. This lets a workflow such as Five9 look up context from an earlier touch using the caller's phone number.
Data is scoped to your organization. US numbers can be formatted or sent as digits. Non-US numbers must include the `+` country code. URL-encode the `+` when it appears in the path.
Callers that do not have a phone number can be keyed by any other identifier, such as a chat or SIP identifier, of up to 128 characters. Simple stores those identifiers as provided and returns them unchanged in `phoneNumber`.
```bash theme={null}
curl 'https://api.usesimple.ai/api/v1/contacts/%2B14155550100/data' \
-H 'Authorization: Bearer YOUR_API_KEY'
```
The response contains your stored fields at the top level, keyed alongside the normalized phone number. If a stored key is named `phoneNumber`, the identifier shadows the stored value in this response:
```json theme={null}
{
"phoneNumber": "+14155550100",
"five9ConversationId": "conv-48291",
"routingStatus": "awaiting_follow_up"
}
```
**Every value in this response is a string, and no value exceeds 250 characters.** This response is shaped for IVR platforms, which read it as a flat key-value list rather than as general JSON and hold each value in a fixed-width string variable. Five9, for example, declares script variables as `STRING:
Alphanumeric, maximum 250 characters`; a longer value overruns the variable, and the truncated JSON that results fails the IVR's parse entirely rather than dropping just the long field.
To keep the payload parseable:
* values longer than 250 characters are cut at the last sentence boundary that fits; a value with no usable boundary is truncated at 250 characters
* numbers and booleans are serialized as strings (`3` becomes `"3"`)
* null and absent values are serialized as an empty string, not `"null"`
* nested objects and arrays are serialized as JSON text, so a caller that wants the structure back can parse the value a second time
Revision and expiry metadata is deliberately **not** returned by this endpoint. An IVR never reads it, and every key it does not read is character budget taken from the ones it does. The `PUT`, `PATCH`, and `DELETE` responses still return `revision`, `ttlSeconds`, and `expiresAt`.
**The entire response body is also kept within 250 characters**, not just each value, because IVR scripts commonly assign the whole body to a single string variable before parsing it. When the combined payload would overrun the budget, the longest field is shortened until it fits, longest first — so a prose field such as a call summary absorbs the loss.
**Values of 64 characters or fewer are never shortened**, nor is the caller identifier. A routing value, status, or identifier carries its meaning in full: truncating `place_order` to `place_or` does not degrade it, it changes it, and an IVR comparing against that value silently takes the wrong branch. Long values are prose, which loses detail but stays correct when cut.
If a contact holds so many short values that the body cannot fit, the response is returned over budget rather than corrupting them. An oversized response that the IVR fails to parse leaves its variable empty, which is recoverable; a corrupted routing value is not. Store only what the IVR actually reads.
Records have no expiry by default. A record with an expired TTL, or a record that has been deleted, is treated as missing and returns `404`.
The endpoint returns `401` when authentication fails, `404` when no current data exists for the caller identifier, and `422` when the identifier is invalid.
# Replace Stored Contact Data with the API
Source: https://docs.usesimple.ai/api-reference/contacts/replace-contact-data
PUT /contacts/{phoneNumber}/data
Replace the complete state stored for a contact phone number so future reads and connected workflows receive the new data.
See the [integrations overview](/integrations/overview) for ways to connect external systems with Simple AI.
Use this endpoint to store the complete state that another system should find the next time the caller contacts you. For example, Five9 can save a conversation ID, routing status, or handoff details against the caller's phone number.
The data is scoped to your organization and the phone number. If the number does not belong to a contact yet, Simple creates a contact so the stored state is visible in the dashboard.
US numbers can be formatted or sent as digits. Non-US numbers must include the `+` country code. URL-encode the `+` when it appears in the path.
Callers that do not have a phone number can be keyed by any other identifier, such as a chat or SIP identifier, of up to 128 characters. Simple stores those identifiers as provided and returns them unchanged in `phoneNumber`.
```bash theme={null}
curl -X PUT 'https://api.usesimple.ai/api/v1/contacts/%2B14155550100/data' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"data": {
"five9ConversationId": "conv-48291",
"routingStatus": "awaiting_follow_up",
"preferredLanguage": "en"
},
"ttlSeconds": 86400
}'
```
The response contains the normalized phone number, the stored object, its revision, and expiration metadata:
```json theme={null}
{
"phoneNumber": "+14155550100",
"data": {
"five9ConversationId": "conv-48291",
"routingStatus": "awaiting_follow_up",
"preferredLanguage": "en"
},
"revision": 1,
"ttlSeconds": 86400,
"expiresAt": "2026-08-05T12:00:00.000Z"
}
```
Data does not expire unless you provide `ttlSeconds`. When supplied, it must be an integer from 60 seconds through 90 days. The serialized `data` object may be at most 64 KiB.
The endpoint returns `401` when authentication fails and `422` when the caller identifier or request body is invalid.
# Sync a Contact with the Simple AI API
Source: https://docs.usesimple.ai/api-reference/contacts/upsert-contact
POST /contacts/upsert
Create or update a Simple AI contact from your CRM, commerce platform, automation workflow, or another external source.
See the [integrations overview](/integrations/overview) for other ways to connect your customer systems with Simple AI.
Use this endpoint for Salesforce, Shopify, Zapier, or another system that sends one contact change at a time.
The combination of the source key and `externalId` is the durable sync identity. Send `"source": "your_crm"` to reuse or create a source. A new source uses that key as its name and `custom` as its provider; an existing source keeps its metadata. You can also send the object form with `key`, `name`, and `provider`.
The first request can define the source, its fields, and the contact in one operation. Later requests can omit `fields` unless the source schema changes.
## Define fields
Field keys use lowercase snake case. Supported types are `text`, `number`, `boolean`, `date`, `enum`, `url`, `email`, and `phone`.
Define the system field keys before sending their top-level values:
* `name` uses `text`
* `email` uses `email`
* `phone_number` uses `phone`
Each non-system key in `attributes` must have a field definition owned by the same source. Dates use ISO 8601 datetime strings. Enum values must match one of the field's `enumOptions`.
A field's type cannot change and its enum options cannot be removed. To redefine a field, first [delete the field](/api-reference/contacts/delete-synced-contact-field).
## Apply sparse updates
Omitted contact properties and attribute keys remain unchanged. Send an explicit `null` to clear a top-level value or attribute. The API does not treat missing contacts as deletions. To remove a contact, use [Delete Contact](/api-reference/contacts/delete-contact).
## Link another source
Use `match.email` or `match.phoneNumber` to attach a new external identity to an existing contact. Match values are lookup-only and do not update the contact.
A match succeeds only when the value identifies one contact. Ambiguous matches, or email and phone resolving to different contacts, return `409 Conflict`.
Each organization permits one active contact per phone number and one external identity per source on a contact. A second external ID from the same source that matches that contact returns `409`. Use different source keys when two systems have overlapping external IDs.
## Update consent
Include a `consents` array in the same request to record voice or text preferences for the supplied primary `phoneNumber`:
```json theme={null}
{
"source": "your_crm",
"externalId": "customer-42",
"fields": { "phone_number": { "label": "Phone", "type": "phone" } },
"phoneNumber": "+14155550100",
"eventId": "customer-42-update-1",
"sourceUpdatedAt": "2026-09-14T10:00:00Z",
"consents": [
{ "medium": "voice", "category": "all", "optOut": true },
{ "medium": "text", "category": "all", "optOut": true }
]
}
```
Nonempty consent updates require an E.164 phone number and both event fields. Each entry uses medium `voice` or `text` and category `all`, `transactional`, or `marketing`. An `all` opt-out covers both categories.
`optOut: true` reports an opt-out; `false` reports no opt-out from this source; null reports unknown. False does not establish affirmative consent or trigger a provider opt-in. Omit `consents`, send an empty array, or omit an entry to leave its existing state unchanged. Duplicate medium/category entries and a null `consents` array are rejected.
Any applicable source opt-out blocks the number, including decisions recorded by Simple. Your source can only change its own records. Preferences stay with the number when a contact changes phone numbers. Unclassified outbound calls and texts honor any category's opt-out. Imported flags are checked by Simple before sending. STOP/START messages and provider opt-out errors also update the consent records.
The response includes `contact.consents` for the contact's current number. Each entry includes `phoneNumber`, `sourceKey`, `medium`, `category`, `status`, and `observedAt`. The status is `opted_out`, `not_opted_out` (your source sent `false`), `unknown` (your source sent `null`), or `opted_in` (an explicit opt-in recorded by Simple). A null `sourceKey` identifies a decision recorded by Simple. Any `opted_out` record blocks the matching communication. A contact without a phone number has an empty consent array.
## Retry and order updates
Send `eventId` and `sourceUpdatedAt` together to enable deduplication and ordering. They are optional for requests without consent changes. Retry with the same event ID and request body.
* Accepted updates return `applied: true` and `reason: null`.
* Identical replays return `200`, `applied: false`, and `reason: "duplicate"`.
* Older timestamps for the same source and external ID return `200`, `applied: false`, and `reason: "stale"`.
* Conflicting event ID reuse or equal timestamps with different contents return `409`.
Skipped requests leave all fields and preferences unchanged and return the current contact with `created: false` and `linked: false`. Source, fields, contact values, consent, and event history are saved together.
Retrieve the contact later with [Get Contact](/api-reference/contacts/get-contact), or use [List Contacts](/api-reference/contacts/list-contacts) with `sourceKey` and `externalId` filters.
## Use with Zapier
Create a **Webhooks by Zapier** action that sends a `POST` request to:
```text theme={null}
https://api.usesimple.ai/api/v1/contacts/upsert
```
Send `Authorization: Bearer YOUR_API_KEY` and `Content-Type: application/json` headers, then map the CRM fields into the request body shown in the example.
# Create a Custom Data Record with the API
Source: https://docs.usesimple.ai/api-reference/custom-data/create-record
PUT /data/{table_name}
Create a record in a Simple AI custom data table by supplying values for the columns defined in that table schema.
Before writing records, [create a custom data table](/api-reference/custom-data/create-table) with the columns your application needs.
Send values for declared table columns. Managed columns such as `id`, `organization_id`, `created_at`, and `updated_at` are controlled by Simple AI.
For `REFERENCE` columns targeting `contacts` or `calls`, send the resource's public UUID. A contact UUID is returned as `contact.id` by [Upsert Synced Contact](/api-reference/contacts/upsert-contact). Numeric IDs remain accepted for compatibility. Other custom-table references use numeric row IDs.
Use the stored reference column name, which ends in `_ref`. For example:
```json theme={null}
{
"contact_ref": "11111111-1111-4111-8111-111111111111"
}
```
Contact references must identify an active contact in your organization. Responses return contact and call references as UUIDs. The same UUID support applies to updates and reference filters.
## Order source updates
For a table with a business key, add `?sourceUpdatedAtColumn=source_updated_at` to compare the supplied timestamp with the stored value atomically. The named column must have type `TIMESTAMP`, and every request using this option must include a non-null timestamp and the business key.
A new key is inserted. An existing key is updated only when its stored timestamp is null or the supplied timestamp is newer. Equal or older timestamps return `409` without changing the record. After a retry returns `409`, read the record to check its current state. Omitting this option preserves normal upsert behavior.
This option is available on individual `PUT` and `PATCH` requests.
# Create a Custom Data Table with the API
Source: https://docs.usesimple.ai/api-reference/custom-data/create-table
POST /data/tables
Create a Simple AI custom data table with a stable name, declared columns, and optional search indexing for application data.
After defining the schema, [create custom data records](/api-reference/custom-data/create-record) in the new table.
Table and column names are stable identifiers used by the record endpoints. Use lowercase letters, numbers, and underscores.
For `REFERENCE` columns, include `referenceTable`. For `OBJECT` columns, include `jsonSchema`.
# Delete All Records from a Custom Data Table
Source: https://docs.usesimple.ai/api-reference/custom-data/delete-all-records
DELETE /data/{table_name}
Delete every record from a Simple AI custom data table while preserving the table schema for future records and queries.
To remove the schema afterward, use [Delete Custom Data Table](/api-reference/custom-data/delete-table).
This removes records from the table but keeps the table schema.
# Delete a Custom Data Record with the API
Source: https://docs.usesimple.ai/api-reference/custom-data/delete-record
DELETE /data/{table_name}/{id}
Delete one record from a Simple AI custom data table by specifying its table name and unique record identifier.
Use [List Custom Data Records](/api-reference/custom-data/list-records) to locate records and their identifiers before deleting one.
# Delete a Custom Data Table with the API
Source: https://docs.usesimple.ai/api-reference/custom-data/delete-table
DELETE /data/tables/{id}
Delete an empty Simple AI custom data table after removing its records and any references to it from other table schemas.
Use [Delete All Custom Data Records](/api-reference/custom-data/delete-all-records) before deleting a table that still contains data.
Tables can only be deleted after all records are removed. A table also cannot be deleted while another table references it.
# Get Facets for a Custom Data Table
Source: https://docs.usesimple.ai/api-reference/custom-data/get-facets
GET /data/facets/{table_name}
Retrieve distinct facet values for the facetable columns in an indexed Simple AI custom data table to build filters.
Apply returned facets when you [query custom data](/api-reference/custom-data/query) with structured filters.
Facets are available for indexed tables and include distinct values for facetable columns.
# Get a Custom Data Record with the API
Source: https://docs.usesimple.ai/api-reference/custom-data/get-record
GET /data/{table_name}/{id}
Retrieve one record from a Simple AI custom data table by specifying the table name and unique record identifier.
Use [List Custom Data Records](/api-reference/custom-data/list-records) to find records and their identifiers.
# List Records in a Custom Data Table
Source: https://docs.usesimple.ai/api-reference/custom-data/list-records
GET /data/{table_name}
List, paginate, sort, and filter records stored in a Simple AI custom data table using table and managed column values.
For indexed free-text and facet filtering, use [Query Custom Data](/api-reference/custom-data/query).
Use query parameters for pagination and sorting. Query parameters that match table columns or managed columns are treated as exact-match filters.
# List Custom Data Reference Tables
Source: https://docs.usesimple.ai/api-reference/custom-data/list-reference-tables
GET /data/tables/reference-tables
List the Simple AI custom data tables that are eligible targets when defining a reference column in another table schema.
Use the returned tables when you [create a custom data table](/api-reference/custom-data/create-table) with reference columns.
Use this endpoint before creating or updating a table with `REFERENCE` columns.
The list includes the built-in `calls` and `contacts` targets alongside your organization's custom tables.
# List Custom Data Tables with the API
Source: https://docs.usesimple.ai/api-reference/custom-data/list-tables
GET /data/tables
List every Simple AI custom data table available to your organization, including each table schema and current record count.
Use the returned table names with the [custom data record endpoints](/api-reference/custom-data/list-records).
Returns every custom data table for your organization, including column definitions and record counts.
# Query a Custom Data Table with the API
Source: https://docs.usesimple.ai/api-reference/custom-data/query
POST /data/query
Search an indexed Simple AI custom data table with free text, structured facet filters, sorting, and pagination controls.
Retrieve available filter values with [Get Custom Data Facets](/api-reference/custom-data/get-facets).
This runs a deterministic query against a single table that has `searchIndexingEnabled` set to `true`.
Provide exact-match `filters` (each matches a column against one or more values) and/or a free-text `text` query. Omit both to get the most recent records. Use [`GET /data/facets/{table_name}`](/api-reference/custom-data/get-facets) to discover the available columns and values to filter on.
# Update a Custom Data Record with the API
Source: https://docs.usesimple.ai/api-reference/custom-data/update-record
PATCH /data/{table_name}/{id}
Update selected declared-column values on an existing Simple AI custom data record while leaving omitted fields unchanged.
Retrieve the current values with [Get Custom Data Record](/api-reference/custom-data/get-record) before applying a partial update.
Send values for the declared columns you want to update. Unknown columns are ignored.
Add `?sourceUpdatedAtColumn=source_updated_at` to apply the update only if the supplied timestamp is newer than the stored value, or the stored value is null. The named column must have type `TIMESTAMP` and be supplied with a non-null timestamp. The comparison and update are atomic. Equal or older timestamps return `409` without changes; a missing record returns `404`.
Use this option on every incoming source update that needs ordering. Requests without it retain normal partial-update behavior. See [source update ordering](/api-reference/custom-data/create-record#order-source-updates) for retry behavior.
# Update a Custom Data Table with the API
Source: https://docs.usesimple.ai/api-reference/custom-data/update-table
PATCH /data/tables/{id}
Update a Simple AI custom data table schema by submitting the complete desired column list for validation and application.
Use [List Custom Data Tables](/api-reference/custom-data/list-tables) to retrieve the current schema before replacing its column list.
Send the complete column list you want the table to have after the update. Changing a table schema validates existing records before applying the change.
# Add a Note to Call Transcript Feedback with the API
Source: https://docs.usesimple.ai/api-reference/feedback/add-feedback-note
POST /feedback/{uuid}/notes
Attach a note to a Simple AI call transcript feedback entry, such as investigation findings, without marking it fixed or reopening it.
Use [List Call Transcript Feedback](/api-reference/feedback/list-feedback) to find feedback entries and read their existing notes. To change whether an entry is fixed, use [Resolve Feedback](/api-reference/feedback/resolve-feedback).
# List Call Transcript Feedback with the API
Source: https://docs.usesimple.ai/api-reference/feedback/list-feedback
GET /feedback
List feedback submitted on Simple AI call transcripts so your organization can review reported issues and track their resolution.
See the [AI phone calls guide](/calls) for an overview of the transcripts and analysis produced for completed calls.
## Priority
Reviewers can assign a priority when they leave feedback: `critical`, `high`, `medium`, or `low`. Feedback left without a priority is returned with `priority: null`. Filter with the `priority` query parameter, and pass `priority=unspecified` to list only feedback that has no priority yet.
## Categories
Every feedback entry is categorized automatically when it is created (reviewers can also pick a category up front). The first five describe agent behavior (fixable in the prompt or configuration), the next three are platform or data failures, and the last two are meta.
| Category | Meaning |
| - | - |
| `wrong_action` | Performed an action the customer did not ask for, or the right action with the wrong target or scope: wrong record updated, wrong option selected, a booking, refund, or claim with the wrong scope or reason. |
| `skipped_verification` | Did not collect or confirm information the flow requires before acting: identity, contact details, addresses, dates and times, quantities, a readback before submitting. |
| `incorrect_information` | Stated something untrue or unsupported, or could not find the right answer: pricing, availability, policy, account or product facts, knowledge-base content, an unrelated substitute offered. |
| `transfer_handling` | Transferred when it should not have, failed to transfer when it should have, promised a handoff that never happened, or offered one outside business hours. |
| `conversation_quality` | The exchange itself was off: answered a different question than asked, ignored what the customer said, looped or repeated itself, duplicate offers, clumsy readbacks, wrong tone. |
| `data_integration` | What the agent confirmed did not land correctly in the connected system, or the system returned wrong customer, order, appointment, or account data. |
| `transcription_error` | Speech-to-text misheard the customer (names, numbers, terms, garbled text), or text-to-speech mispronounced something. |
| `interruption_handling` | Cut the customer off, talked over them, did not wait for an answer, left dead air, or stalled for a long time while waiting on a tool. |
| `tag_error` | One or more tags were incorrectly added to the interaction, or expected tags were not added. |
| `other` | Does not fit the above categories, or does not describe a reportable agent behavior. |
Feedback created before these categories were introduced was remapped: `wrong_response` became `wrong_action`, `hallucination` became `incorrect_information`, and `tone` and `missed_intent` became `conversation_quality`. `transcription_error`, `tag_error`, and `other` are unchanged. The `category` filter still accepts those retired values and treats them as their replacements, so existing integrations keep working.
# Resolve Call Transcript Feedback with the API
Source: https://docs.usesimple.ai/api-reference/feedback/resolve-feedback
POST /feedback/{uuid}/resolve
Mark a Simple AI call transcript feedback entry as fixed, or reopen it when the reported issue still requires attention.
Use [List Call Transcript Feedback](/api-reference/feedback/list-feedback) to find feedback entries and review their current state.
# Get a Voice or Text Interaction with the API
Source: https://docs.usesimple.ai/api-reference/interactions/get-interaction
GET /interactions/{uuid}
Retrieve a specific Simple AI voice or text interaction by UUID, including the channel-specific data available for that session.
Learn about [AI phone calls](/calls) and [SMS conversations](/sms) before working with their combined interaction records.
Recording links in `recording_presigned_url` and `recording_presigned_urls` are temporary HTTPS playback URLs. Use each complete URL as returned, including its query parameters; the hostname and URL structure may change. Anyone with a valid link can access its recording until the link expires.
# List Voice and Text Interactions with the API
Source: https://docs.usesimple.ai/api-reference/interactions/list-interactions
GET /interactions
List, paginate, and filter Simple AI voice calls and text conversations through one unified interactions API endpoint.
Learn about [AI phone calls](/calls) and [SMS conversations](/sms) before working with their combined interaction records.
By default, this endpoint returns native calls and conversations only. Pass `include_external_channels=true` to include external-channel sessions, grouped one per session, alongside native interactions. The `type` field identifies whether each interaction is a `call`, `conversation`, `email`, `sms`, or `external_channel`.
# Simple AI API Authentication and Quickstart
Source: https://docs.usesimple.ai/api-reference/introduction
Learn how to authenticate with the Simple AI API, use the production base URL, handle JSON responses and errors, and respect API rate limits.
## Base URL
All API requests are made to:
```text theme={null}
https://api.usesimple.ai/api/v1
```
## Authentication
All API endpoints require authentication using an API key. You can create and manage API keys in the Simple AI dashboard under **Settings > API Keys** (`/organization/settings/api-keys`).
Include your API key in the `Authorization` header as a Bearer token:
```bash theme={null}
Authorization: Bearer YOUR_API_KEY
```
### Example Request
```bash theme={null}
curl -X GET "https://api.usesimple.ai/api/v1/calls" \
-H "Authorization: Bearer sk_live_12345abcdef"
```
## Response Format
All responses are returned as JSON. Successful requests return a 2xx status code.
## Errors
Every error response has the same shape:
```json theme={null}
{
"status": "error",
"success": false,
"error": "Input validation failed",
"errors": ["to_number: Invalid to_number format"]
}
```
| Field | Description |
| - | - |
| `status` | Always `"error"` |
| `success` | Always `false` |
| `error` | A summary of what went wrong |
| `errors` | The individual problems. For validation errors, each entry names the field and what is wrong with it. |
| Status code | Meaning |
| - | - |
| `401` | The API key is missing or invalid |
| `404` | The resource does not exist or does not belong to your organization |
| `409` | The request conflicts with the current state, such as cancelling a call that has already started or reusing an `idempotency_key` with a different request body |
| `422` | The request failed validation |
| `500` | Something went wrong on our side |
## Safe Retries
When you create a call, pass an `idempotency_key` that is unique to that call. If a request times out and you send it again with the same key and the same body, Simple AI returns the original response instead of placing a second call. See [Create Call](/api-reference/calls/create-call).
## Rate Limits
API requests are rate limited. If you exceed the limit, you will receive a `429 Too Many Requests` response. Back off and retry with exponential backoff.
## Need Help?
If you run into issues with the API, reach out to [support@usesimple.ai](mailto:support@usesimple.ai).
# Assign an AI Agent to a Phone Number
Source: https://docs.usesimple.ai/api-reference/numbers/assign-agent
POST /numbers/{uuid}/assign_agent
Assign a specific Simple AI agent version to a phone number so that agent handles future inbound calls to the number.
See the [phone numbers guide](/numbers) for inbound routing and agent assignment concepts.
# Buy a Phone Number with the Simple AI API
Source: https://docs.usesimple.ai/api-reference/numbers/buy-number
POST /numbers/buy_number
Purchase an available phone number for your Simple AI organization so an agent can make outbound calls or receive inbound calls.
See the [phone numbers guide](/numbers) for number setup, assignment, and usage.
# Get Phone Number Details with the API
Source: https://docs.usesimple.ai/api-reference/numbers/get-number
GET /numbers/{uuid}
Retrieve the configuration and assignment details for a specific phone number owned by your Simple AI organization.
See the [phone numbers guide](/numbers) for number setup, assignment, and usage.
# List Phone Numbers with the Simple AI API
Source: https://docs.usesimple.ai/api-reference/numbers/list-numbers
GET /numbers
List the phone numbers owned by your Simple AI organization, including identifying details and any assigned voice agent.
See the [phone numbers guide](/numbers) for number setup, assignment, and usage.
# Release a Phone Number with the API
Source: https://docs.usesimple.ai/api-reference/numbers/release-number
DELETE /numbers/{uuid}
Release a phone number that your Simple AI organization no longer needs so it is removed from your available numbers.
See the [phone numbers guide](/numbers) before changing the numbers available to your agents.
# Search Available Phone Numbers with the API
Source: https://docs.usesimple.ai/api-reference/numbers/search-numbers
GET /numbers/search_available
Search for phone numbers available to purchase for your Simple AI organization before selecting and buying a number.
See the [phone numbers guide](/numbers) for number setup, assignment, and usage.
# Update a Phone Number with the API
Source: https://docs.usesimple.ai/api-reference/numbers/update-number
PATCH /numbers/{uuid}
Update the friendly name of a phone number so your Simple AI organization can identify and manage it more easily.
See the [phone numbers guide](/numbers) for number setup, assignment, and usage.
# Get an AI Voice with the Simple AI API
Source: https://docs.usesimple.ai/api-reference/voices/get-voice
GET /voices/{uuid}
Retrieve the configuration details for a specific Simple AI voice before selecting it for a reusable voice agent.
Learn how voices shape agent behavior in the [AI agents guide](/agents).
# List AI Voices with the Simple AI API
Source: https://docs.usesimple.ai/api-reference/voices/list-voices
GET /voices
List the AI voices available to your Simple AI organization so you can compare options and choose one for a voice agent.
Learn how voices shape agent behavior in the [AI agents guide](/agents).
# Handle Completed Call Webhooks
Source: https://docs.usesimple.ai/api-reference/webhooks/call-completed
Handle Simple AI completed-call webhooks to start post-call processing, then fetch the final call data, recording, transcript, and analysis results.
The `call.completed` webhook is sent when a call has finished.
The event tells you which call finished. To read its final status, transcript, recording, and analysis results, [get the call](/api-reference/calls/get-call) using `call_id`.
See the [webhooks guide](/webhooks) for delivery behavior, signature verification, retries, and webhook setup.
### Payload
The unique identifier (UUID) for the call.
The type of the webhook event. Will be "call.completed".
### Example
```json theme={null}
{
"call_id": "123e4567-e89b-12d3-a456-426614174000",
"event_type": "call.completed"
}
```
# Handle Call Started Webhooks
Source: https://docs.usesimple.ai/api-reference/webhooks/call-started
Handle Simple AI call-started webhooks to learn when an inbound or outbound phone call begins, including its direction and phone numbers.
The `call.started` webhook is sent when a call is created and the agent is connecting. It is sent for both inbound and outbound calls.
See the [webhooks guide](/webhooks) for delivery behavior, signature verification, retries, and webhook setup.
### Payload
The unique identifier (UUID) for the call.
The type of the webhook event. Will be "call.started".
The direction of the call: "inbound" or "outbound".
The phone number the call is placed from.
The phone number the call is placed to.
### Example
```json theme={null}
{
"call_id": "123e4567-e89b-12d3-a456-426614174000",
"event_type": "call.started",
"direction": "outbound",
"from_number": "+15559876543",
"to_number": "+15551234567"
}
```
# Handle Call Transferred Webhooks
Source: https://docs.usesimple.ai/api-reference/webhooks/call-transferred
Handle Simple AI call-transferred webhooks to learn when an agent hands a phone call to another number, then fetch the call for details.
The `call.transferred` webhook is sent when the agent transfers the call.
The event tells you which call was transferred. To read the handoff details and the transcript so far, [get the call](/api-reference/calls/get-call) using `call_id`.
See the [webhooks guide](/webhooks) for delivery behavior, signature verification, retries, and webhook setup.
### Payload
The unique identifier (UUID) for the call.
The type of the webhook event. Will be "call.transferred".
### Example
```json theme={null}
{
"call_id": "123e4567-e89b-12d3-a456-426614174000",
"event_type": "call.transferred"
}
```
# Create a Call Webhook with the Simple AI API
Source: https://docs.usesimple.ai/api-reference/webhooks/create
POST /webhooks
Create a Simple AI webhook endpoint and select the real-time call events that your application should receive and process.
See the [webhooks guide](/webhooks) for event formats, delivery behavior, security, and retry recommendations.
# Delete a Call Webhook with the Simple AI API
Source: https://docs.usesimple.ai/api-reference/webhooks/delete
DELETE /webhooks/{endpoint_id}
Delete a Simple AI webhook endpoint by ID so your application stops receiving the call events previously sent to that URL.
See the [webhooks guide](/webhooks) for event formats, delivery behavior, security, and retry recommendations.
# List Call Webhooks with the Simple AI API
Source: https://docs.usesimple.ai/api-reference/webhooks/list-webhooks
GET /webhooks
List the Simple AI webhook endpoints configured for your organization, including each URL's event subscriptions and status.
See the [webhooks guide](/webhooks) for event formats, delivery behavior, security, and retry recommendations.
# Handle Call Transcript Update Webhooks
Source: https://docs.usesimple.ai/api-reference/webhooks/transcript-update
Handle Simple AI transcript update webhooks to follow a phone call while it is in progress, then fetch the latest transcript entries.
The `call.transcript_update` webhook is sent whenever a new entry is added to a call's transcript during the conversation.
The event tells you which call changed. To read the transcript, [get the call](/api-reference/calls/get-call) using `call_id`.
See the [webhooks guide](/webhooks) for delivery behavior, signature verification, retries, and webhook setup.
### Payload
The unique identifier (UUID) for the call.
The type of the webhook event. Will be "call.transcript\_update".
### Example
```json theme={null}
{
"call_id": "123e4567-e89b-12d3-a456-426614174000",
"event_type": "call.transcript_update"
}
```
# Authenticate Actions and Store Credentials Securely
Source: https://docs.usesimple.ai/authentication
Configure managed OAuth connections, encrypted credential storage, API keys, and other authentication methods for Simple AI Actions.
Actions connect your agents to external systems. Simple provides encrypted configuration variables for credentials and managed OAuth connections for service-to-service APIs, so you can keep authentication separate from conversation inputs and Action source code.
## Choose an Authentication Method
| Your API requires | Use |
| - | - |
| OAuth 2.0 client credentials | A managed OAuth connection explicitly chosen for each fetch. Simple manages the access token. |
| An API key, static bearer token, or Basic authentication | A sensitive configuration variable referenced in an HTTP Action or Code Action. |
| An interactive OAuth sign-in or user consent | Your application's OAuth flow or middleware; managed connections support the client-credentials grant. |
| A signed request or custom token exchange | A Code Action or your own middleware. |
| A client certificate for mTLS | Middleware configured with the certificate and private key. |
## Managed OAuth Connections
An OAuth connection is a reusable configuration owned by your organization. In your Code Action, you explicitly choose the connection to use on **each fetch that needs authentication**. Saving a connection or making it available to an Action does not add credentials to requests automatically.
A fetch without an explicit connection selection receives no managed OAuth credential, even when its URL matches a saved connection's allowed origins. Simple does not infer the credential from the destination or reuse the selection from a previous fetch.
When you choose a connection for a request, Simple manages its token exchange and renewal. Your code selects the connection without copying its client secret or access token into source code.
The supported grant is **OAuth 2.0 client credentials** (`client_credentials`): your agent acts on behalf of a service account or application. This does not implement a user's browser sign-in, authorization-code callback, or refresh-token grant.
### Create a Connection
1. Open **Configuration → OAuth Connections** and add a connection.
2. Enter a descriptive name and the provider settings below.
3. Save the connection and use its test control to verify that the provider issues a token.
| Setting | What to enter |
| - | - |
| **Token URL** | The provider's HTTPS token endpoint, such as `https://auth.example.com/oauth/token`. |
| **Client ID** | The identifier issued for your application or service account. |
| **Client secret** | The secret issued by the provider. This is a write-only field. |
| **Client authentication method** | **HTTP Basic** (`client_secret_basic`) or **Form body** (`client_secret_post`), as required by the provider. This controls the token request, not the API request. |
| **Scopes** | The provider's permission scopes, separated by spaces. |
| **Audience / Resource** | Optional identifiers required by your provider for the target API. |
| **Extra token parameters** | Optional provider-specific values, one `KEY=value` per line. These cannot override the dedicated grant, client credential, scope, audience, or resource settings. |
| **Allowed origins** | The HTTPS API origins that may receive this connection's token, one per line, such as `https://api.example.com`. |
Extra token parameters and other connection settings are readable configuration. Put the client secret in its dedicated field; do not store additional secrets in descriptions, URLs, or extra token parameters.
### Choose Authentication on Each Fetch
For every fetch in your Code Action, decide whether it needs managed authentication and explicitly choose the saved connection if it does. Selection applies only to that request.
For example, a Code Action that calls the same CRM API twice can:
* Fetch a public endpoint without choosing authentication. No managed OAuth token is added.
* Fetch a protected account endpoint and explicitly choose the **Production CRM** connection for that request.
A second authenticated fetch must choose its connection again. Allowed origins constrain where the selected credential may be sent; they do not select or enable authentication. Simple continues to handle the selected connection's token lifecycle and keeps managed secrets out of Action code.
### Example: Choose an OAuth Connection in Code
With a saved OAuth connection named `production-crm` that allows `https://api.example.com`, explicitly choose it for the request:
```typescript theme={null}
import { defineAction } from '@simple-ai-lab/sdk';
export default defineAction(async ({ auth }) => {
const response = await auth.fetch('https://api.example.com/customers', {
connection: 'production-crm',
method: 'GET',
});
if (!response.ok) {
throw new Error(`Customer lookup failed: ${response.status}`);
}
return { customers: await response.json() };
});
```
Each `auth.fetch` explicitly names the saved OAuth connection. Simple handles token acquisition, renewal, and secure attachment for that request. Another authenticated request must select its connection again. Ordinary `fetch(...)` receives no managed OAuth credentials.
### How Tokens Are Obtained and Renewed
1. Your code explicitly selects a connection for the fetch. Simple verifies that the selected connection exists, belongs to the organization executing the Action, and permits the destination origin.
2. Simple reuses a cached token when it is sufficiently far from expiry. Otherwise, it exchanges the stored client credentials at the provider's token endpoint.
3. The access token is encrypted before it is cached. Concurrent requests coordinate token renewal to reduce duplicate exchanges.
4. Simple attaches the selected connection's token only to that explicitly authenticated request. Other fetches receive no managed OAuth credential unless they also select a connection.
Simple stops reusing a cached token within 60 seconds of its expiry and obtains a replacement when needed. Renewal is another client-credentials exchange; it does not use a refresh token or run as a continuous background refresh. If the provider omits an expiry, Simple uses a five-minute lifetime for cache decisions. Cache lifetime is capped at six hours.
Editing a connection invalidates its cached token, including when you change its secret, scopes, or token endpoint. Deleting a connection invalidates its cache too. A request selecting a missing connection must fail rather than silently continue without the requested authentication.
### Restrict Where Tokens Are Sent
Allowed origins match the exact HTTPS scheme, hostname, and port. For `https://api.example.com`:
* Requests to `https://api.example.com/orders` and `/customers` may use the connection when the fetch explicitly selects it.
* Requests to a different subdomain, lookalike hostname, non-default port, or an HTTP URL do not qualify.
* Paths are not an access boundary: entering a URL with a path stores its origin. Each fetch must still explicitly choose the connection.
Only allow API origins you trust to receive the credential. An allowed origin is a destination restriction, not an instruction to include credentials. Token attachment requires an explicit connection selection on that fetch. Limit the token's actual permissions with the provider's scopes and service-account permissions.
## How Credentials Are Stored and Protected
### Managed OAuth Credentials
* **Encryption at rest:** client secrets and cached access tokens use AES-256-GCM authenticated encryption.
* **Write-only secrets:** the client secret is accepted when creating or updating a connection, but is excluded from connection read responses. Those responses also exclude access tokens.
* **Organization isolation:** connection lookup and execution check organization ownership. Configuration read and write permissions control connection management.
* **Runtime separation:** Simple performs token exchange and header attachment outside the Code Action execution environment. The managed client secret and token are not supplied to the Action as inputs or configuration values.
* **Safe test results:** connection tests report success, token type, and expiry information rather than returning the access token. Provider response bodies and free-form OAuth error descriptions are omitted from token-exchange errors.
These protections cover Simple's credential handling. The destination API receives the access token, so use trusted destinations and avoid endpoints that reflect request credentials in their responses.
### API Keys and Other Configuration Secrets
For credentials you use directly, open **Configuration Variables**, add a named value, mark it **sensitive** with the lock control, and save it. Use names such as `CRM_API_KEY` or `BASIC_AUTH` so Actions can reference them without embedding values in source code.
All configuration-variable values are encrypted at rest with AES-256-GCM. Marking a variable sensitive additionally omits its saved plaintext from configuration read responses. Variable names and descriptions remain visible, so keep secrets in the value field. Configuration edits record which variable names and fields changed without recording their values in the configuration audit event.
An HTTP Action references a variable with `{{ config.CRM_API_KEY }}`. A Code Action reads it as `config.CRM_API_KEY`. Unlike managed OAuth credentials, these values are available to the Action at runtime. Do not print them, return them as outputs, or interpolate them into conversation prompts. Marking a value sensitive does not make arbitrary Action code safe to log it.
### Rotate or Revoke Credentials
To rotate an OAuth client secret, edit the connection and enter the replacement secret. Leaving that field empty preserves the stored secret. Saving the replacement invalidates the cached token so a subsequent execution exchanges the new credentials.
For an API key, replace the sensitive configuration variable's value. Keep the same name to preserve Action references. Remove unused connections and variables, and revoke old credentials with the provider. Deleting a connection in Simple does not revoke a token that the provider has already issued.
## Test Authentication
First test the OAuth connection to confirm the credentials can obtain a token. Then run the Action's test panel to confirm that the selected connection, allowed origin, provider permissions, and API request work together. A successful token test alone does not prove access to a particular API endpoint.
| Symptom | What to check |
| - | - |
| Token endpoint rejects the connection | Client ID, replacement secret, Basic versus form-body authentication, scopes, and provider-specific settings. |
| Token test passes, API returns `401` or `403` | The fetch explicitly selects the intended connection, the origin is allowed, and the token has the API's required audience and permissions. |
| Action reports a missing connection | Select an existing connection from the same organization on the affected fetch. |
| Token endpoint is unavailable | Check the provider's availability and HTTPS token URL, then retry the test. |
## API Keys and Bearer Tokens
Use the header name and format required by the destination API. Common HTTP Action configurations are:
| Header | Value template |
| - | - |
| `Authorization` | `Bearer {{ config.API_KEY }}` |
| `X-API-Key` | `{{ config.API_KEY }}` |
| `api-key` | `{{ config.API_KEY }}` |
Choose the matching header for your provider; you do not need to send all three. A Code Action can set the same headers with `fetch`:
```typescript theme={null}
import { defineAction } from '@simple-ai-lab/sdk';
export default defineAction(async ({ config }) => {
const response = await fetch('https://api.example.com/account', {
headers: { Authorization: `Bearer ${config.API_KEY}` },
});
if (!response.ok) {
throw new Error(`Account lookup failed with status ${response.status}`);
}
const account = await response.json();
return { account };
});
```
If the provider requires a key in a query parameter or request body, use that location instead. Prefer headers when the provider supports them, and use HTTPS.
## Mutual TLS (mTLS)
mTLS authenticates the client with a certificate and its corresponding private key during the TLS connection. It can also be used with OAuth, as described in [OAuth mutual TLS](https://www.rfc-editor.org/rfc/rfc8705).
Actions currently do not expose customer-provided client certificates or private keys for outbound requests. Adding a certificate to an HTTP header or configuration variable does not enable mTLS.
To reach an API that requires mTLS, have the Action call an HTTPS endpoint in [your own middleware](/integrations/custom-code). Authenticate that request with a supported method, such as an API key, and configure your middleware to present the client certificate when it calls the destination API. Certificate provisioning, renewal, and private-key storage belong in that middleware.
## Other Authentication Methods
| Method | How to use it with Actions |
| - | - |
| **HTTP Basic** | Store the Base64-encoded `username:password` value in a configuration variable, then set `Authorization` to `Basic {{ config.BASIC_AUTH }}`. Base64 is encoding, not encryption; use HTTPS. |
| **JWT bearer tokens** | Send an existing JWT in the provider's required header, commonly `Authorization: Bearer ...`. If the provider requires you to issue or exchange a signed JWT, implement that step in code or middleware and manage token expiry. |
| **HMAC or signed requests** | Use a Code Action to generate the provider's required signature, timestamp, and headers for each request. Use middleware if the signing flow requires libraries or runtime features unavailable in Code Actions. |
| **Session cookies** | A Code Action can explicitly send a provider-issued cookie in a `Cookie` header. Your integration must handle login, renewal, and cookie storage; there is no persistent browser session between executions. |
| **Custom headers** | Add the provider's required header names and reference their values from configuration variables. This can include a combination of credentials and account identifiers. |
| **No authentication** | Omit authentication headers for public endpoints that do not require credentials. |
For calls to Simple AI itself, use the handler's `client`: it is already authenticated for your organization. External services still require their own credentials.
# Understand the AI Phone Call Lifecycle
Source: https://docs.usesimple.ai/call-flow
Follow a Simple AI call from pre-call preparation through the live conversation, real-time events, completion, analysis, and post-call workflows.
When a call is initiated, whether inbound or outbound, Simple AI follows a sequence of steps before, during, and after the conversation. This page walks through that flow and explains where your workflows can hook in.
## 1. Pre-call workflow
Before a call starts, Simple AI can run a pre-call workflow that prepares the agent with context about the caller. This is not just a simple HTTP ping — it is an opportunity to run a full sequence of lookups, business logic, and data enrichment so the agent starts the conversation fully informed.
If an agent has the `pre_call_webhook_url` configured and `pre_call_webhook_enabled` set to `true`, Simple AI triggers the workflow before the call is created. The payload includes information about the call and the agent:
```json theme={null}
{
"call": {
"from_number": "+15551234567",
"to_number": "+15557654321",
"direction": "inbound",
"timestamp": "2024-01-01T12:00:00Z"
},
"agent": {
"id": "agent-uuid",
"name": "Support Agent"
}
}
```
Your workflow can look up the caller in your CRM, check for open support tickets, load account details, apply routing rules, or run any other logic you need. It responds with a `parameters` object, and those parameters are merged into the call before it begins.
If `pre_call_webhook_auth_token` is present, it is sent in the `Authorization` header as a bearer token. The workflow times out after `pre_call_webhook_timeout` seconds (default is 5). Errors or timeouts are ignored and the call proceeds normally.
See [Workflows](/integrations/workflows) for detailed examples and setup guidance.
## 2. Call created
After the optional pre-call workflow, Simple AI creates the call record and connects the caller to the agent. At this point your application receives the `call.started` webhook (see [Webhooks](/webhooks)).
## 3. Call in progress
During the conversation, Simple AI can send `call.transcript_update` webhooks with incremental transcript data. These allow your application to react in real time — displaying a live transcript, monitoring for keywords, or triggering mid-call actions.
## 4. Call completed
When the call ends, Simple AI sends a `call.completed` webhook. This is the trigger for your post-call workflow — a full journey of follow-up actions like syncing to your CRM, sending follow-up messages, updating tickets, notifying your team, or feeding data into your analytics pipeline.
Post-call workflows can be as simple as a single API call or as complex as a multi-step orchestration that branches based on the call outcome, analyzer results, or tags. See [Workflows](/integrations/workflows) for examples.
## Summary
| Step | What Happens | Your Hook |
| - | - | - |
| **Pre-call** | Agent prepares for the conversation | Pre-call workflow — enrich context, run business rules, load caller data |
| **Call started** | Call is created and connected | `call.started` webhook |
| **In progress** | Conversation is happening | `call.transcript_update` webhooks for real-time monitoring |
| **Call completed** | Call ends, recording and transcript finalized | `call.completed` webhook — trigger your post-call workflow |
# Understand the Simple AI Call Object
Source: https://docs.usesimple.ai/call-object
Learn how Simple AI represents a phone call in the API, including its identifiers, status lifecycle, agent fields, transcript, and recording.
Every inbound and outbound call is represented by the same object in the API. This page explains the parts of it that are easy to misread: which identifier to use, what each status means, and how the agent fields relate to each other.
For the full list of fields, see [Get Call](/api-reference/calls/get-call).
## Identifiers
A call has one identifier, a UUID. It appears under different names depending on where you see it:
| Where | Field |
| - | - |
| [Create Call](/api-reference/calls/create-call) and [Cancel Call](/api-reference/calls/cancel-call) responses | `uuid` |
| [Get Call](/api-reference/calls/get-call) and [List Calls](/api-reference/calls/list-calls) | `id` and `simple_uuid` (the same value) |
| [Webhook](/webhooks) events | `call_id` |
| Paths such as `/calls/{uuid}` | `uuid` |
All of these hold the same value, so you can take the `uuid` from a create response or the `call_id` from a webhook and use it directly in any call endpoint.
To attach your own identifiers to a call, pass `external_identifiers` when you create it. You can then look the call up with [Find Call by Attribute](/api-reference/calls/find-by-attribute).
## Status
`status` tells you where a call is in its lifecycle.
| Status | Meaning |
| - | - |
| `enqueued` | An outbound call has been accepted and is waiting to be placed |
| `in_progress` | The call is being placed or the conversation is under way |
| `completed` | The call ended normally |
| `failed` | The call could not be placed or ended because of an error |
| `cancelled` | The call was cancelled before it started |
`completed`, `failed`, and `cancelled` are final. A call in one of these statuses does not change status again.
**Outbound calls** start as `enqueued` and move to `in_progress` when Simple AI places them. If your organization is already at its limit for simultaneous calls, new calls stay `enqueued` and are placed as earlier calls finish. You can [cancel](/api-reference/calls/cancel-call) a call while it is `enqueued`.
**Inbound calls** start as `in_progress`.
To see how an outbound call was answered, read `answered_by`: `human`, `voicemail`, `no_answer`, or `unknown`. It is `null` until that is known.
## Agent Fields
An agent has versions, and each version has a history of saved snapshots. A call records all three:
| Field | What it identifies |
| - | - |
| `agent_id` | The agent |
| `agent_branch` | The agent version the call ran on |
| `agent_branch_name` | The name of that version |
| `agent_commit_id` | The exact snapshot of that version used for the call |
These are `null` when the call is not associated with an agent.
The same IDs are used when you create and filter calls:
| To do this | Use |
| - | - |
| Create a call with an agent | `agent_id` |
| Create a call with a specific version of it | `agent_id` and `version_id` (the value returned as `agent_branch`) |
| List calls for an agent, across all its versions | the `agent_id` filter |
| List calls for one version | the `agent_version_id` filter (the value returned as `agent_branch`) |
When you create a call with `agent_id` and no `version_id`, the most recently updated version is used.
## Times
`created_at`, `updated_at`, `started_at`, and `ended_at` are ISO 8601 timestamps in UTC. `started_at` and `ended_at` are `null` until the call starts and ends. `duration` is the length of the call in seconds.
Each entry in `transcripts` has a `timestamp`, which is a Unix timestamp in seconds.
## Transcript, Recording, and Analysis
* **`transcripts`** — the conversation in order. Each entry has a `role`, the `text` that was said, and for tool calls a `function_name` and its `arguments`.
* **`recording_presigned_url`** — a temporary HTTPS link to the call audio, or `null` when there is no recording. Use the complete URL as returned, including its query parameters. Anyone with a valid link can access the recording until it expires, so request a fresh link each time rather than storing it.
* **`summary`** — a text summary of the call, when one is available.
* **`analyzers`** and **`analysis_results`** — the [analyzers](/analyzers) configured for the call and the results they have produced so far.
* **`tags`** — the [tags](/tags) applied to the call.
Recordings, summaries, and analysis results are produced after the call ends, so they can be missing when you first receive a `call.completed` webhook. Get the call again shortly afterwards to read them.
## Your Data on a Call
* **`params`** — the parameters passed when the call was created, plus any set during the call. See [Call Parameters](/calls#call-parameters).
* **`external_identifiers`** — the identifiers you attached when creating the call.
# Analyze Sentiment in AI Phone Calls
Source: https://docs.usesimple.ai/call-sentiment
Use automatic sentiment analysis to understand caller tone, compare sentiment across Simple AI calls, and identify conversations that need attention.
Call sentiment gives you an automatic read on how callers are feeling during a conversation. Simple AI analyzes both the audio tone and the text of what was said to surface emotional signals you can use for quality assurance, coaching, and reporting.
## How It Works
After a call completes, Simple AI automatically analyzes the conversation in two ways:
* **Audio sentiment** — detects emotion from the caller's voice, such as whether they sound angry, sad, neutral, or excited
* **Transcript sentiment** — evaluates the text of what the caller said to determine whether the overall tone is negative, neutral, or positive
No setup is required. Sentiment analysis runs automatically on completed calls where a recording or transcript is available.
## Where Sentiment Shows Up
### Call Detail View
When you open a call, the sentiment section shows:
* The **dominant sentiment** — the emotion that was most present during the call
* A **duration bar** showing how much of the call was spent in each emotional state
* A **timeline of notable events** — moments where the caller's tone shifted. You can click on an event to jump to that point in the recording
### Calls List
You can filter calls by sentiment to focus on the conversations that matter most:
* Filter by **audio sentiment** — angry, sad, neutral, or excited
* Filter by **transcript sentiment** — negative, neutral, or positive
This makes it easy to surface calls where the caller was frustrated, or find positive calls to use as coaching examples.
### Analytics
The analytics page includes sentiment charts that help you track trends over time:
* **Sentiment trend** — shows how average sentiment scores are changing over a selected period
* **Sentiment distribution** — breaks down what percentage of calls were negative, neutral, or positive
## Best Practices
* Use sentiment alongside analyzers and tags for a more complete picture of call quality
* Combine sentiment filters with other filters like agent, date range, or tags to narrow down to the calls that matter most
* Use the analytics charts to spot sentiment trends across agents or time periods
# Make and Receive AI-Powered Phone Calls
Source: https://docs.usesimple.ai/calls
Learn how Simple AI handles inbound and outbound phone calls, including personalization, lifecycle events, recordings, transcripts, and analysis.
Calls are the core of Simple AI. Every voice conversation — inbound or outbound — is a call. You can trigger calls from the API, the dashboard, or through [campaigns](/campaigns), and each call is recorded, transcribed, and available for analysis.
## Inbound Calls
Attach an agent to a phone number and every call to that number is handled by your AI. To set up inbound calls:
1. Buy or connect a phone number — see [Phone Numbers](/numbers)
2. Assign an agent to that number — see [Agents](/agents)
3. When someone calls, the agent picks up and follows its configured prompt and flow
You can also run a [pre-call webhook](/call-flow) before the conversation starts to inject dynamic parameters (like caller identity or account details) into the agent.
## Outbound Calls
Outbound calls can be triggered three ways:
* **API** — send a POST request to [Create Call](/api-reference/calls/create-call) with a phone number and agent
* **Dashboard** — place a call directly from the Simple AI web app
* **Campaigns** — reach a list of contacts automatically with scheduling and retries. See [Campaigns](/campaigns)
When making an outbound call, you can either reference an agent you have already configured or provide a prompt inline to create an agent on the fly.
```bash theme={null}
curl -X POST "https://api.usesimple.ai/api/v1/calls" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to_number": "+15551234567",
"agent_id": "123e4567-e89b-12d3-a456-426614174000",
"params": { "customer_name": "Dana" },
"idempotency_key": "appointment-1042-reminder"
}'
```
The response confirms the call is queued and gives you its identifier:
```json theme={null}
{
"success": true,
"status": "enqueued",
"uuid": "346fe020-e8c2-4abe-9d40-a4992b016bd6"
}
```
Use that `uuid` to follow the call:
```bash theme={null}
curl "https://api.usesimple.ai/api/v1/calls/346fe020-e8c2-4abe-9d40-a4992b016bd6" \
-H "Authorization: Bearer YOUR_API_KEY"
```
While a call is still `enqueued`, you can cancel it:
```bash theme={null}
curl -X POST "https://api.usesimple.ai/api/v1/calls/346fe020-e8c2-4abe-9d40-a4992b016bd6/cancel" \
-H "Authorization: Bearer YOUR_API_KEY"
```
If you do not pass `from_number_id`, Simple AI picks one of your phone numbers to call from. Passing an `idempotency_key` makes the request safe to retry: sending it again with the same key and body returns the original response instead of placing a second call.
**Compliance note**: Outbound calls must follow all rules and regulations in your jurisdiction, including FCC rules and any applicable local laws.
## Call Parameters
You can pass custom key-value parameters when creating a call. These parameters are available to the agent during the conversation and can be referenced in the prompt using `{{parameter_name}}` syntax.
Parameters are useful for personalizing calls — for example, passing in a customer name, order number, or account ID so the agent can reference it during the conversation.
## Call Lifecycle
A call's `status` moves from `enqueued` to `in_progress` and ends as `completed`, `failed`, or `cancelled`. See [Call Object](/call-object) for what each status means and how to read the rest of the call.
Every call follows this sequence:
1. **Pre-call webhook** (optional) — your server is notified before the call starts and can inject parameters
2. **Call started** — the call is created and the agent connects
3. **In progress** — the conversation happens, with real-time transcript updates available via [webhooks](/webhooks)
4. **Call completed** — the call ends, recording and transcript are finalized, and post-call [analyzers](/analyzers) run
For full details on each step, see [Call Flow](/call-flow).
## Call Data
After a call completes, Simple AI stores:
* **Recording** — the full audio of the conversation
* **Transcript** — a text transcript of what was said by each party
* **Duration** — how long the call lasted
* **Outcome** — whether the call was answered, went to voicemail, or failed
* **Analyzer results** — structured data extracted by your [analyzers](/analyzers)
* **Tags** — labels applied by your [tag](/tags) rules
* **Sentiment** — automatic [sentiment analysis](/call-sentiment) of the caller's tone
You can review this data in the dashboard, retrieve it through the [API](/api-reference/calls/get-call), or export it as CSV.
## Language Support
Language availability depends on your agent's selected voice and speech providers. Configure the language on the agent, and see the [Create Call API reference](/api-reference/calls/create-call) for values accepted when creating one-off calls.
## API Reference
* [Create a call](/api-reference/calls/create-call)
* [Get a call](/api-reference/calls/get-call)
* [Cancel a call](/api-reference/calls/cancel-call)
* [List calls](/api-reference/calls/list-calls)
* [Find call by attribute](/api-reference/calls/find-by-attribute)
* [Delete a call](/api-reference/calls/delete-call)
# Run Automated Outbound Calling Campaigns
Source: https://docs.usesimple.ai/campaigns
Create outbound calling campaigns with Simple AI agents, contact lists, schedules, concurrency controls, retries, and campaign-level reporting.
Campaigns let you reach a list of contacts using your agents. You can upload recipient data, define the journey, choose when the campaign should run, and control how follow-up attempts should work.
## What Campaigns Do
You can use campaigns to:
* contact a list of recipients with an agent
* upload recipient data from a CSV
* control when calls should happen
* retry contacts when a goal is not met
* pause and resume the journey when a callback is requested
## How to Create a Campaign
When you create a campaign, you first configure the campaign setup screen.
In this step, you define:
* a campaign name
* the phone number the campaign will use
* whether the campaign should follow your organization's business hours
* the journey steps
* retry and continue condition behavior
* callback detection settings
After that, you upload your recipient data and then choose whether to run the campaign now or schedule it for later.
If `Business Hours Only` is enabled, Simple AI uses the business hours configured for your organization and keeps outbound attempts and retry calls within those time windows.
## Campaign Journey
The campaign journey defines the sequence of actions for each recipient. Journeys are not limited to phone calls — you can mix voice, messaging, and integration steps to build multi-channel outreach flows.
### Available Step Types
| Step Type | What It Does |
| - | - |
| **Call** | Place an outbound call using one of your agents |
| **SMS** | Send a text message to the recipient |
| **Email** | Send an email to the recipient |
| **Slack** | Post a message or notification to a Slack channel |
| **Microsoft Teams** | Post a message or notification to a Teams channel |
| **Google Sheets** | Add or update a row in a Google Sheet |
| **Wait** | Pause the journey for a specified duration before the next step |
You can combine these steps in any order to build the journey that fits your use case. For example:
* Call → wait 2 hours → SMS follow-up → wait 1 day → second call attempt
* Call → log result to Google Sheets → send Slack alert if tagged "escalation"
* SMS intro → wait 30 minutes → call → email summary
Each step lets you define who and when. In practice, this means choosing which agent should place a call, what message to send, and when the step should run — including whether it should happen after the previous step, after the first step, or at a specific time.
The journey pauses automatically when a call is answered and the goal has been achieved.
## Retry Settings
Retries let you control what happens after the initial contact attempt.
You can configure:
* how many follow-up steps to include
* how long to wait between steps
* whether retries should continue when the goal was not met
* whether retries should stop when the contact has already responded successfully
By default, retries are mainly driven by unsuccessful call outcomes, such as voicemail, no answer, busy, or other failed attempts.
If AI retries are enabled, Simple AI also analyzes answered calls to determine whether the goal was met. If the call was answered but the goal was not met, the system can continue the retry process for that recipient.
## Callback Detection
If callback detection is enabled and a contact asks to be called back at a specific time, the system can schedule that callback and pause the journey until then.
This helps campaigns feel more natural and prevents follow-up attempts from continuing when the caller has already requested a better time.
## Uploading Campaign Data
Campaign uploads use a CSV file with one row per recipient.
During setup, you map each CSV column to the variables defined in the agent builder so each recipient's row can be used during calls.
Your CSV must include:
* a `phone_number` column
* one row for each person you want to contact
If your agent uses parameters, those parameters can also be included as additional CSV columns so the agent can use them during calls.
You can also include extra columns as identifiers to help map campaign data back to your own systems later.
## Starting and Scheduling Campaigns
Once your campaign data is uploaded, you can either:
* run the campaign immediately
* schedule it for a later date and time
When scheduling a campaign, you choose the date, time, and timezone for when outreach should begin.
If a campaign is scheduled, you can later reschedule it or unschedule it before it runs.
## Reviewing and Exporting Data
Once campaign calls start rolling out, you can review the results directly in Simple AI.
This includes campaign call data such as [tags](/tags), [analyzers](/analyzers), transcripts, and other call attributes so you can understand what happened across the campaign.
When you are ready, you can also export this data as a CSV and use it in your own reporting or operational workflows.
## Best Practices
Campaigns work best when your uploaded data matches the parameters your agents expect to use during calls.
To get the most out of your campaign uploads, it also helps to create [tags](/tags) and [analyzers](/analyzers). This makes it easier to organize campaign calls, track outcomes, and pull structured insights from the conversations your campaign generates.
# Run TypeScript Actions During AI Phone Calls
Source: https://docs.usesimple.ai/code-actions
Run custom TypeScript in an isolated worker so Simple AI agents can validate data, call services, transform results, and control call workflows.
Code actions let you write TypeScript that runs inside Simple AI when an agent invokes the action — no server to deploy or host. Your code executes in a fresh, isolated Cloudflare Worker with the [Simple AI SDK](/sdk/quickstart), the OpenAI SDK, Zod, and libphonenumber-js bundled. The Simple AI SDK is authenticated for your organization automatically.
Use a code action when the logic is more than one HTTP call — combining lookups, applying business rules, or reading and writing [custom data](/api-reference/custom-data/query) — but you don't want to run your own middleware server (for that, see [Custom Code](/integrations/custom-code)).
## Writing a Code Action
Create an Action in the dashboard and choose the **Code** backend. Default-export a function; `defineAction` from the SDK adds typing and an authenticated client:
```typescript theme={null}
import { defineAction } from '@simple-ai-lab/sdk';
export default defineAction(async ({ inputs, config, client }) => {
const { records } = await client.data.query({
tableName: 'customers',
filters: [{ column: 'email', values: [inputs.email] }],
});
if (records.length === 0) {
return { found: false };
}
return { found: true, plan: records[0].plan };
});
```
Your function receives:
* **`inputs`** — the Action's declared inputs, already validated against its input schema
* **`config`** — your organization's configuration variables (for API keys and settings)
* **`client`** — the [Simple AI SDK](/sdk/quickstart), authenticated for your organization automatically; you never handle an API key
The value you return maps to the Action's declared outputs through output mappings rooted at `result` — for example `{{ result.plan }}`.
## Authentication
Use [Authentication](/authentication) to set up managed OAuth connections, store credentials securely, and authenticate requests with API keys, bearer tokens, or other methods. Choose an OAuth connection explicitly on each fetch that needs authentication. The guide explains that per-request choice, secure credential storage, and managed token renewal.
## Steering the conversation
A code action invoked from a workflow can decide where the conversation goes next. Call `workflow.transitionTo` with the name of the node to run:
```typescript theme={null}
import { defineAction } from '@simple-ai-lab/sdk';
export default defineAction(async ({ inputs, client, workflow }) => {
const account = await lookupAccount(client, inputs.email);
if (account.delinquent) {
workflow.transitionTo({
nodeName: 'Collections',
message: 'Let me get you to the right team.',
reason: 'account is delinquent',
});
}
return { plan: account.plan };
});
```
* **`nodeName`** — the node to run next, exactly as it is named in the Agent. Any node in the workflow is reachable; you do not need an edge from the node that invoked the action.
* **`message`** — optional. A short sentence the agent speaks as it moves.
* **`reason`** — optional. Recorded with the transition on the call.
`transitionTo` returns normally: it records where to go next, and the rest of your handler still runs. A transition comes in addition to the result, so the example above both maps `plan` to its outputs and moves the conversation. Calling it more than once keeps the last request, and an action that throws transitions nowhere. If the named node does not exist in the workflow, the transition is logged and ignored, and the agent follows its own edges instead.
### Capabilities
Code actions run in more places than workflows — per-turn actions, automatic before/after-call actions, and data sync all execute the same code, but there is no live conversation to steer. Each invocation states what it allows, and your handler reads it as `metadata.capabilities`:
```typescript theme={null}
import { ActionCapability, defineAction } from '@simple-ai-lab/sdk';
export default defineAction(async ({ inputs, metadata, workflow }) => {
const account = await lookupAccount(inputs.email);
if (account.delinquent && metadata.capabilities.includes(ActionCapability.WORKFLOW_TRANSITION)) {
workflow.transitionTo({ nodeName: 'Collections' });
}
return { delinquent: account.delinquent };
});
```
`WORKFLOW_TRANSITION` is granted when the action runs from a workflow — as an action node or an agent tool call. Calling `transitionTo` without it fails the action with a `MissingCapabilityError`, so check `metadata.capabilities` first if the same action also runs outside workflows.
## Calling an LLM
A code action can call an LLM through Simple AI's LLM router with `llm.infer`, without an LLM provider key of its own. It takes an [OpenAI Responses API](https://developers.openai.com/api/reference/resources/responses/methods/create) request body:
```typescript theme={null}
import { defineAction } from '@simple-ai-lab/sdk';
export default defineAction(async ({ inputs, llm }) => {
const response = await llm.infer({
model: '',
instructions: 'Summarize the caller notes in one sentence.',
input: inputs.notes,
});
const summary = response.output
.flatMap((item) => item.content ?? [])
.filter((part) => part.type === 'output_text')
.map((part) => part.text)
.join('');
return { summary };
});
```
* **`model`** — a router ladder slug, not a vendor model ID. Ask Simple AI support which slugs to use.
* Everything else is an ordinary Responses API body, such as `input`, `instructions`, `text`, or `temperature`. Requests are never streamed.
* The response comes back with its `output` typed, as above; other Responses API fields are there untyped.
* If the router refuses the request, `infer` throws `SimpleAIError` with its HTTP `status`, `code`, and message.
LLM calls are available in code actions during conversations, automatic before/after-call actions, data sync, and editor test runs. Authentication is provided automatically.
## Rules and Limits
* **TypeScript or JavaScript** in the Cloudflare Workers runtime. Use standard web APIs such as `fetch`, `Request`, and `Response`; Node.js APIs are not available.
* The importable packages are `@simple-ai-lab/sdk`, `openai`, `zod`, and `libphonenumber-js`. They are bundled when you save.
* `libphonenumber-js` parses, validates, and formats phone numbers (for example to E.164 before dialing). The default entry point carries minimal metadata; import `libphonenumber-js/max` when you need `getType()` or other extended-metadata lookups.
* The Simple AI SDK is authenticated automatically. To call an LLM, use [`llm.infer`](#calling-an-llm), or store your own OpenAI API key in an organization configuration variable and pass it to the OpenAI client from `config`.
* **Timeout**: configurable per action, 1–60 seconds (default 30). Voice conversations feel best well under 10 seconds — configure filler speech on the agent for longer actions.
* **Network requests**: up to 100 subrequests per execution.
* **Result size**: the returned value must serialize to JSON of at most 1 MB.
* **Logs**: `console.log` / `console.error` output is captured and shown in test runs and call transcripts — it never pollutes the result.
* Each execution runs in a **fresh Worker isolate**; nothing persists between runs. Persist state in custom data instead.
## Testing
The Action editor's test panel runs your code in a real Worker isolate: fill in sample inputs, press **Run test**, and inspect the returned value (click fields to map them as outputs), stdout/stderr logs, and execution timings. Syntax errors are rejected when you save; runtime errors surface in the test result with the thrown error's name and message.
A test run supports `llm.infer`. It has no conversation attached, so `workflow.transitionTo` fails with `MissingCapabilityError`. Guard workflow transitions with a `metadata.capabilities` check to keep the action testable from the editor, and exercise transitions on a test call.
# Build Custom Charts for AI Call Analytics
Source: https://docs.usesimple.ai/custom-charts
Build custom charts from Simple AI call data to track outcomes, analyzer results, tags, sentiment, and operational trends across conversations.
The dashboard can be found under **Insights > Dashboard** (or by pressing ⌘+K and searching "Dashboard"). At the top are built-in charts you get out of the box, like call volume and agent activity.
At the bottom of that page you can create **custom charts** based on your own personalized data about your calls. Charts let you track metrics over time, compare categories, and filter to specific subsets of calls.
You can set up custom Analyzers and Parameters for you calls from the [Calls](/calls) page. These extract structured data from each call — things like call\_status, refund amount, customer sentiment, and more. Custom charts are built on top of this data.
## Custom Charts
Charts are built from three building blocks: a **measure**, a **dimension** (optional), and a **filter** (optional).
### Measure
A measure is the value you want to aggregate. Each datapoint on the chart represents a bucket of calls. If you group by **daily**, each datapoint is a single day in which you may have received many calls. The measure defines how those calls are reduced to a single number.
For example:
* `SUM(.refund_amount)` — total refund amount for that time period
* `AVG(.satisfaction_score)` — average satisfaction score for that time period
* `COUNT(.call_status)` — number of calls that had a call\_status value
* `COUNT_DISTINCT(.call_status)` — number of unique call\_status values (e.g. "completed", "voicemail", etc.)
* `COUNT(*)` — total number of calls, regardless of any specific field. Useful when you just want to count calls that match a filter.
### Dimension
A dimension splits your measure into separate series. Instead of one line on your chart, you get one line per distinct value of the dimension.
For example, adding `.call_status` as a dimension turns a single "total refund amount" line into separate lines for `completed`, `voicemail`, `no_answer`, etc. There is a special dimension **Agent** which splits your data into a series per call agent
Dimensions are optional. Without one, your chart shows a single series.
If a call does not have a value for the dimension field, it is grouped under a special `__NONE__` series so it still contributes to the chart rather than being silently dropped.
Here is an example of the same data no dimension, a dimension with display "Stacked Bar", and a dimension with display "Line"
### Filter
A filter restricts which calls are included before any aggregation happens. For example, "only include calls where `.customer_sentiment` is `positive`."
Filters narrow the data — they do not create additional series. You can combine multiple filters and they are ANDed together.
### Data Sources
Charts pull from two data sources:
* **Params** — custom parameters you on each call
* **Analyzers** — values extracted by your post-call analyzers
Each chart uses one source. You cannot mix params and analyzers in a single chart.
### Chart Types
#### Timeseries
Timeseries charts show how a measure changes over time. The x-axis is time (divided into buckets like hours or days), and the y-axis is the aggregated value.
Use timeseries charts when you want to answer questions like:
* "How many calls are we getting per day?"
* "What is the average satisfaction score per week?"
* "How does refund volume compare across call\_statuss over time?"
You choose the bucket size (e.g. 1 week, 1 day) and date range when viewing the chart.
#### Distribution
Distribution charts show the breakdown of a single value. The x-axis is categories (for strings and booleans) or histogram bins (for numbers), and the y-axis is count.
Use distribution charts when you want to answer questions like:
* "What percentage of calls end in each call\_status?"
* "What does the call duration distribution look like?"
* "How many calls were transferred vs not?"
# Create Dynamic AI Agent Prompts with Liquid
Source: https://docs.usesimple.ai/dynamic-prompts
Use Liquid templates to personalize Simple AI agent prompts with variables, conditions, loops, reusable content, and controlled A/B variations.
Beyond simple `{{parameter}}` substitution, agent prompts support [Liquid](https://liquidjs.com) templating. Liquid lets you go past dropping a value into a sentence: you can branch on conditions, loop over lists, transform values with filters, and even run randomized A/B tests across calls, all from inside the prompt editor, with no code.
If you only need to insert a value (a customer name, an order ID), stick with the plain `{{parameter}}` syntax described in [Prompting](/prompting). Reach for the Liquid features below when the prompt needs to *decide* something.
## How it works
Your prompt is re-rendered on **every turn** of the conversation, not once at the start. To keep a branch stable across the call, seed it from a value that's constant for the whole call. `call.started_at` is fixed for the call's lifetime, so anything derived from it resolves the same way on every turn.
(`now` is derived from `call.started_at`, so it is also constant for the call rather than advancing with wall-clock time.) Times are evaluated in **UTC**.
## Available variables
You can reference the following inside your prompt.
### Call parameters
Any value you pass when creating a call, via the [API](/calls) or a [campaign](/campaigns) CSV, is available at the top level by its name:
```liquid theme={null}
You are calling {{customer_name}} about order {{order_id}}.
Their email on file is {{customer_email}}.
```
### The `call` object
Metadata about the current call is always available under `call`:
| Variable | Description | Example |
| - | - | - |
| `call.call_id` | Unique ID for this call | `8f3c…` |
| `call.from_number` | The number the call is from | `+15551234567` |
| `call.to_number` | The number the call is to | `+15559876543` |
| `call.direction` | `inbound` or `outbound` | `outbound` |
| `call.started_at` | Call start time, ISO 8601 (UTC) | `2026-01-16T10:30:00.000Z` |
```liquid theme={null}
{% if call.direction == "inbound" %}
The customer called you. Greet them and ask how you can help.
{% else %}
You are calling the customer. Introduce yourself first.
{% endif %}
```
### `now`
`now` is the call start time pre-formatted as a human-readable string, ready to speak:
```liquid theme={null}
For reference, the current date and time is {{now}}.
```
```text theme={null}
For reference, the current date and time is Friday, January 16th, 2026 10:30:00 AM UTC.
```
Use `now` when you want to **tell the agent the date/time**. Use `call.started_at` when you want a **raw value to compute with**, such as math, comparisons, or seeding (see A/B testing below). `now` is already formatted into prose, so filters like `date` won't work on it.
## Liquid basics
### Variables: `assign`
Compute a value once and reuse it throughout the prompt:
```liquid theme={null}
{% assign company = "Downtown Medical" %}
Thanks for calling {{company}}. This is {{agent_name}} from {{company}}.
```
Filters can only be used inside `{% assign %}` and `{{ output }}`, **not** inside an `{% if %}` or `{% case %}` condition. If you need to test a transformed value, assign it first, then compare the variable:
```liquid theme={null}
{% assign branch = call.started_at | date: "%s" | modulo: 2 %}
{% if branch == 0 %}…{% else %}…{% endif %}
```
### Conditionals: `if` / `elsif` / `else`
```liquid theme={null}
{% if account_tier == "premium" %}
Offer to connect them to a priority specialist.
{% elsif account_tier == "standard" %}
Help them directly with their request.
{% else %}
Ask which account they are calling about.
{% endif %}
```
### Multiple branches: `case` / `when`
Cleaner than a long `if`/`elsif` chain when you are switching on one value:
```liquid theme={null}
{% case region %}
{% when "US" %}Quote prices in US dollars.
{% when "UK" %}Quote prices in British pounds.
{% else %}Ask the customer which currency they prefer.
{% endcase %}
```
### Filters
Filters transform values with a `|`. A few useful ones:
```liquid theme={null}
{{customer_name | capitalize}} → Jane
{{order_id | upcase}} → 7BB0A946
{{call.started_at | date: "%A"}} → Friday
{{price | times: 1.08}} → apply 8% tax
```
All [standard Liquid filters](https://liquidjs.com/filters/overview.html) are available (`upcase`, `downcase`, `capitalize`, `date`, `plus`, `minus`, `times`, `modulo`, `append`, `default`, and more).
## A/B testing prompts across calls
A powerful use of Liquid is running **randomized variations** of a prompt without any code or statefulness. The pattern: derive a stable number from the call start time, then branch on it.
```liquid theme={null}
{% assign seed = call.started_at | date: "%s" %}
{% assign variant = seed | modulo: 3 %}
{% case variant %}
{% when 0 %}Open the call warmly: "Hi there! Thanks so much for calling."
{% when 1 %}Open the call briefly: "Hi, how can I help today?"
{% when 2 %}Open the call formally: "Good day. How may I assist you?"
{% endcase %}
```
How it works:
1. `call.started_at | date: "%s"` converts the call start time to a Unix timestamp (seconds): an ever-changing integer.
2. `| modulo: 3` reduces it to `0`, `1`, or `2`, spreading calls roughly evenly across three variants.
3. `case`/`when` selects the matching variant.
Because `call.started_at` is fixed for the life of the call, every part of the prompt that references `variant` resolves to the **same branch**: the agent stays consistent within a single call, while different calls land on different variants.
**Always seed from `call.started_at`, not `now`.** `now` is a formatted prose string and won't run through the `date` filter. `call.started_at` is the raw timestamp the math needs.
### Scaling to many variants
The same pattern extends to as many branches as you like: just raise the modulo. This is how teams test 10–30 script variations (different openers, tones, or full personas) from a single agent:
```liquid theme={null}
{% assign branch = call.started_at | date: "%s" | modulo: 20 %}
# Active Persona
{% case branch %}
{% when 0 %}You are warm and cheerful. Use light filler words.
{% when 1 %}You are polite and reserved. Keep responses brief.
{% when 2 %}You are tired but helpful, at the end of a long shift.
{% comment %} …branches 3 through 19… {% endcomment %}
{% endcase %}
```
You can reuse the same `branch` variable in multiple `case` blocks throughout the prompt (opener, acknowledgments, sign-off), so a single call presents one coherent persona end to end.
## Best practices
* **Keep logic shallow.** A couple of branches makes a prompt adaptive; deeply nested conditionals make it hard to read and test. If a prompt needs many distinct flows, consider [flow-based agents](/agents) with a node per scenario instead.
* **Provide a fallback.** Always include an `{% else %}` (or a `{% when %}` default) so an unexpected value never leaves a blank section in the prompt.
* **Guard against missing values** with the `default` filter: `{{customer_name | default: "there"}}`.
* **Test each variant.** With A/B branches, place several test calls and confirm each branch reads correctly: a typo in one `when` only shows up when that branch is selected.
* **Watch for silent passthrough.** If your Liquid has a syntax error, the prompt is used **as written**: the raw `{% … %}` tags will appear in the agent's instructions instead of being evaluated. If you see template tags leaking into agent behavior, check your syntax.
* **Review the rendered prompt.** The surest way to verify your logic is to make a real call and inspect what the agent actually received.
## Where Liquid works
Liquid is evaluated in your agent's prompt: both the main instructions and the per-node prompts of [flow-based agents](/agents), so each node can branch on the same call data.
# Connect Simple AI to CRM and Helpdesk Platforms
Source: https://docs.usesimple.ai/integrations/built-in
Use built-in Simple AI integrations to connect agents with CRM, contact center, helpdesk, productivity, and customer-support platforms.
Simple AI has built-in integrations with popular business platforms. These require minimal setup — connect your account, configure the integration, and your agents can start using it.
## Contact Center
Contact center platforms have their own section: see [CCaaS Integrations](/integrations/ccaas/overview) for Five9, Genesys, NiCE CXone, Talkdesk, RingCentral, and the rest.
[Five9](/integrations/ccaas/five9) is the deepest of these — beyond SIP call routing it adds call recording sync, live agent transfer, and chat transfer, and it is configured from **Settings > Integrations > Five9** in the dashboard.
## Knowledge Base Imports
Simple AI can import content from your existing helpdesk platform so your agents have immediate access to your support content without manual re-entry.
### Zendesk
Import articles directly from your Zendesk Help Center into a Simple AI knowledge base.
**What you can do:**
* Automatically sync help center articles into a Simple AI knowledge base
* Keep your agents up to date with the same content your support team uses
* Use imported content alongside custom articles and uploaded files
**Setup:**
1. Create a new knowledge base in the Simple AI dashboard
2. Choose **Import from Zendesk**
3. Enter your Zendesk subdomain (e.g., `yourcompany` for yourcompany.zendesk.com)
4. Simple AI imports your published articles
### Kustomer
Import articles from your Kustomer knowledge base into Simple AI.
**What you can do:**
* Automatically sync knowledge base articles from Kustomer
* Give your agents access to existing support documentation
* Combine Kustomer content with other knowledge sources
**Setup:**
1. Create a new knowledge base in the Simple AI dashboard
2. Choose **Import from Kustomer**
3. Enter your Kustomer subdomain (e.g., `yourcompany` for yourcompany.kustomer.com)
4. Simple AI imports your articles
## CRM Integrations
### Salesforce
Connect Simple AI with Salesforce to keep your CRM in sync with call activity.
**What you can do:**
* Log calls and outcomes as activities in Salesforce
* Sync contact and account data so your agents have context during calls
* Trigger workflows in Salesforce based on call results
**Setup:**
Contact your account team at [support@usesimple.ai](mailto:support@usesimple.ai) to set up the Salesforce integration. Configuration varies based on your Salesforce edition and the data you want to sync.
### Microsoft Dynamics 365
Connect Simple AI with Dynamics 365 to sync call data and contact records.
**What you can do:**
* Log call activity and outcomes in Dynamics 365
* Look up contact and account details during calls
* Keep your Dynamics records updated with the latest call data
**Setup:**
Contact your account team at [support@usesimple.ai](mailto:support@usesimple.ai) to set up the Dynamics 365 integration.
## E-Commerce
### Shopify
Connect Simple AI with Shopify so your agents can access order, product, and customer data during calls.
**What you can do:**
* **Order lookup** — agents can look up order status, tracking numbers, and delivery estimates during a call
* **Customer lookup** — pull up customer profiles, order history, and account details by phone number or email
* **Returns and exchanges** — initiate returns or exchanges directly from a call based on your Shopify policies
* **Product information** — answer questions about product availability, pricing, and variants
**Setup:**
Contact your account team at [support@usesimple.ai](mailto:support@usesimple.ai) to connect your Shopify store. Configuration includes which data your agents can access and what actions they can perform.
## Communication and Notifications
### Slack
Send call summaries, alerts, and escalation notifications to your Slack workspace.
**What you can do:**
* **Post-call summaries** — automatically post call summaries, outcomes, and analyzer results to a Slack channel after each call
* **Escalation alerts** — notify a channel or individual when a call needs human follow-up
* **Campaign updates** — get notified when campaigns start, complete, or hit issues
* **Real-time alerts** — trigger Slack messages based on call events like negative sentiment or failed transfers
**Setup:**
Contact your account team at [support@usesimple.ai](mailto:support@usesimple.ai) to connect your Slack workspace and configure which channels receive which notifications.
### Microsoft Teams
Send call notifications and summaries to Microsoft Teams channels, and integrate with Teams telephony.
**What you can do:**
* **Post-call summaries** — automatically post call outcomes and key details to a Teams channel
* **Escalation alerts** — notify your team when a call requires human attention
* **Telephony integration** — route calls between Simple AI and Teams Phone System via SIP (see [SIP Integration](/sip-integration))
* **Agent handoff** — transfer calls from your AI agent directly to a Teams user
**Setup:**
Contact your account team at [support@usesimple.ai](mailto:support@usesimple.ai) to connect your Teams workspace. For telephony integration via SIP, see [SIP Integration](/sip-integration).
## Automation
### Zapier
Connect Simple AI to thousands of apps through Zapier without writing any code.
**What you can do:**
* **Trigger Zaps from call events** — start a Zapier workflow when a call completes, a tag is applied, or an analyzer extracts a specific result
* **Post-call workflows** — automatically create CRM records, send emails, update spreadsheets, or notify your team after calls
* **Campaign triggers** — kick off Zapier workflows based on campaign outcomes
* **Connect to any app** — Zapier supports thousands of apps, so you can connect Simple AI to tools that do not have a native integration
**Common Zap examples:**
* Call completes → create a HubSpot contact with call details
* Negative sentiment detected → send an email to the support manager
* Analyzer extracts a qualified lead → add a row to Google Sheets
* Call tagged "follow-up needed" → create a task in Asana or Monday.com
**Setup:**
Contact your account team at [support@usesimple.ai](mailto:support@usesimple.ai) to set up the Zapier integration. Once connected, you can build Zaps using Simple AI triggers and actions in the Zapier dashboard.
## SMS and Messaging
### Attentive
Connect Simple AI with Attentive for SMS marketing event tracking.
**What you can do:**
* Track custom events from call outcomes in Attentive
* Trigger SMS marketing flows based on call results
* Sync user and phone number data between Simple AI and Attentive
**Setup:**
Contact your account team at [support@usesimple.ai](mailto:support@usesimple.ai) to configure the Attentive integration.
## Requesting New Integrations
If you need an integration that is not listed here or in [CCaaS Integrations](/integrations/ccaas/overview), reach out to your account team or contact [support@usesimple.ai](mailto:support@usesimple.ai). Simple AI's [HTTP tool system](/integrations/http) also lets you connect to any service with a REST API without waiting for a native integration.
# Connect Simple AI to 8x8 Contact Center
Source: https://docs.usesimple.ai/integrations/ccaas/8x8
Integrate Simple AI with 8x8 Contact Center over SIP for AI call automation, inbound and outbound routing, and transfers to human agents.
Simple AI connects to 8x8 over a SIP trunk. 8x8 Contact Center keeps routing, queues, and agent state; Simple AI handles the conversations routed to it and hands them back for live agent escalation.
## What You Can Do
* **Inbound routing** — 8x8 answers the call, runs IVR and queue routing, then transfers it to Simple AI's SIP endpoint.
* **Outbound campaigns** — the 8x8 dialer handles pacing and compliance, and transfers connected calls to Simple AI.
* **Direct outbound** — Simple AI places calls through the trunk with your existing caller ID.
* **Live agent escalation** — Simple AI transfers back to an 8x8 queue, agent, or external number.
* **Context on transfer** — call metadata rides along in `X-` prefixed SIP headers for screen pop and CRM lookup.
## How It Connects
```text theme={null}
Customer → Carrier → 8x8 (IVR / queue) → Simple AI
← escalation back ←
```
1. 8x8 receives the call and runs your IVR script and routing rules.
2. A transfer sends the call to Simple AI over the SIP trunk.
3. Simple AI runs the conversation and transfers back when escalation is needed.
4. 8x8 queues the returned call and delivers it to an agent with the transferred context.
## Setup
1. Configure a SIP trunk from 8x8 to Simple AI's SIP endpoint, and allowlist our signaling and media IPs.
2. Give your account team the 8x8 endpoint, auth details, and the numbers involved in E.164 format.
3. Point the IVR or queue rules that should use the AI at the transfer destination, and set a return destination for escalation.
4. Test inbound, outbound, and escalation, verifying caller ID, audio, DTMF, and agent-side context.
Your account team configures the Simple AI side. Contact [support@usesimple.ai](mailto:support@usesimple.ai) to get started.
## Related
* [CCaaS Integrations](/integrations/ccaas/overview)
* [SIP Integration](/sip-integration)
# Connect Simple AI to Amazon Connect
Source: https://docs.usesimple.ai/integrations/ccaas/amazon-connect
Connect Simple AI to Amazon Connect with a contact flow transfer or an Amazon Chime SDK Voice Connector SIP trunk, for AI call routing and live agent escalation.
Simple AI connects to Amazon Connect in one of two ways: a contact flow transfers the call to a number that routes to Simple AI, or you peer a SIP trunk to Simple AI through an **Amazon Chime SDK Voice Connector**. Amazon Connect keeps routing, queues, agent state, and reporting either way.
Amazon Connect does not expose SIP trunking directly. On AWS, the Chime SDK Voice Connector is the SIP endpoint, and a Connect contact flow reaches it the same way it reaches any other destination.
## What You Can Do
* **Inbound routing** — a contact flow answers the call, runs your IVR logic, then hands the conversation to Simple AI.
* **Outbound campaigns** — Amazon Connect outbound campaigns handle pacing and compliance, and transfer connected calls to Simple AI.
* **Direct outbound** — Simple AI places calls through the Voice Connector with your existing caller ID.
* **Live agent escalation** — Simple AI transfers back to a number claimed in Amazon Connect, whose inbound flow routes the call to the right queue.
* **Your numbers, your caller ID** — calls run on numbers you provision in AWS rather than on Simple AI's default telephony.
## How It Connects
```text theme={null}
Customer → Amazon Connect (contact flow) → Chime SDK Voice Connector → Simple AI
← escalation back to a number ←
```
### Option 1: Contact Flow Transfer
The simplest path. A **Transfer to phone number** block sends the call to a number that routes to Simple AI. There is no SIP configuration on your side, and no Voice Connector to run.
Use this when Amazon Connect should stay the front door and you do not need your own numbers or trunk on the Simple AI side.
### Option 2: Chime SDK Voice Connector
Peer a Chime SDK Voice Connector to Simple AI over SIP. Numbers you provision in AWS then reach Simple AI directly over SIP, with TLS and SRTP available, and Simple AI can place outbound calls through the same trunk using those numbers as caller ID.
### What Neither Option Carries
Amazon Connect reaches Simple AI by dialing a number, in both options. A **Transfer to phone number** block places an ordinary telephone call, so the handoff to Simple AI is a telephone transfer even when the number belongs to a Voice Connector.
That has one consequence worth planning around: **Amazon Connect contact attributes do not travel with the call.** Simple AI reads context from `X-` prefixed SIP headers on the incoming INVITE, and Amazon Connect does not attach custom SIP headers to a transfer. Attributes your flow set stay inside Amazon Connect.
If the AI needs to know something the flow already learned, supply it out of band — have the flow invoke a Lambda that starts the call through the [calls API](/api-reference/calls/create-call) with `params`, or look the caller up from the AI side using their number. Platforms that can set SIP headers on transfer, such as Genesys and NiCE CXone, do not have this constraint.
The same applies to the [screen pop](/integrations/ccaas/screen-pop#amazon-connect): the flow that routes a handoff to your agents finds the Simple AI call by an identifier, so it needs one that Simple AI also holds.
The rest of this page covers the Voice Connector setup.
## Prerequisites
* An AWS account with access to the [Amazon Chime SDK console](https://console.aws.amazon.com/chime-sdk/).
* A phone number provisioned in Amazon Chime SDK, or the ability to order one.
* A Simple AI account with permission to manage phone numbers, and a voice agent already built.
* Simple AI's signaling networks, which you add to the Voice Connector's allowlist. Ask your account team or [support@usesimple.ai](mailto:support@usesimple.ai) for the current ranges.
SIP connectivity is available on Enterprise plans.
## Step 1: Create the Voice Connector
In the Amazon Chime SDK console, open **Voice Connectors** and create one:
| Setting | Value |
| - | - |
| **Encryption** | Enabled (recommended) |
| **Network type** | IPV4\_ONLY |
Encryption enabled means the Voice Connector speaks TLS for signaling and SRTP for media. Note which you chose — every later step on both sides has to match it.
From the Voice Connector's details page, save the **Outbound host name**. It looks like `abcdef1ghij2klmno3pqr.voiceconnector.chime.aws` and is the endpoint Simple AI sends outbound calls to.
## Step 2: Assign Phone Numbers
Open the Voice Connector's **Phone numbers** tab, choose **Assign from inventory**, select the numbers this trunk should serve, and confirm.
If your inventory is empty, order numbers first under **Phone number management → Orders → Provision phone numbers**.
Record every number in E.164 format, for example `+14155552671`. You will enter the same numbers in Simple AI, and they have to match exactly.
## Step 3: Enable Termination for Outbound Calls
Termination governs calls that Simple AI originates through the Voice Connector. Skip this step if you only need inbound.
On the Voice Connector's **Termination** tab:
1. Set **Termination** to enabled.
2. Under **Allowed hosts**, add Simple AI's signaling CIDRs. Calls from any other network are rejected.
3. Under **Calling plan**, select the countries Simple AI is allowed to call.
4. Optionally create **Credentials** — a username and password — for SIP digest authentication on top of the IP allowlist. Save both; you enter them in Simple AI in the next step.
## Step 4: Connect the Trunk in Simple AI
In the Simple AI dashboard, go to **SIP trunks → Connect SIP trunk** and fill in the form with your Voice Connector's values:
| Field | Value for Amazon Chime SDK |
| - | - |
| **Name** | Anything recognizable, for example `Amazon Connect` |
| **Call directions** | **Inbound and outbound**, or **Inbound only** if you skipped Step 3 |
| **Interop profile** | Generic SIP |
| **SIP endpoint** | The Voice Connector's **Outbound host name** |
| **Signaling transport** | **TLS** if Voice Connector encryption is enabled, otherwise TCP |
| **Outbound authentication** | **Digest username and password** if you created termination credentials, otherwise **Carrier IP allowlist** |
| **Carrier signaling source CIDRs** | The Chime SDK signaling network for your Voice Connector's region |
| **Media encryption (SRTP)** | **Require SRTP** if encryption is enabled, otherwise **Disabled — RTP** |
| **Numbers on this trunk** | The E.164 numbers you assigned in Step 2, one per line |
The signaling source CIDRs are what let inbound calls in — Simple AI only accepts SIP INVITEs from the networks you list here. Look up the SIP signaling subnet for your region in the AWS [Voice Connector network configuration](https://docs.aws.amazon.com/chime-sdk/latest/ag/network-config.html) reference. For example:
| Region | Signaling network |
| - | - |
| US West (Oregon) | `99.77.253.0/24` |
| US East (N. Virginia) | `3.80.16.0/23` |
Connection settings are fixed once the trunk is saved. To change the endpoint, credentials, or numbers, unassign agents from the numbers, disconnect the trunk, and reconnect it with the new settings.
Save the form. Simple AI provisions routes for each number in the background; the trunk's detail page shows **Configured** when a number is ready, and offers **Retry setup** if one fails.
## Step 5: Configure Origination for Inbound Calls
Origination governs calls arriving from the PSTN that the Voice Connector forwards to Simple AI.
On the Voice Connector's **Origination** tab, set **Origination status** to enabled, then add an inbound route:
| Setting | Value |
| - | - |
| **Host** | `sip.usesimple.ai` |
| **Port** | `5061` with TLS, or `5060` with TCP |
| **Protocol** | TLS if Voice Connector encryption is enabled, otherwise TCP |
| **Priority** | `1` |
| **Weight** | `1` |
Your Simple AI trunk's detail page shows the same inbound domain under **Finish inbound routing**, so you can copy it from there.
The Voice Connector sends the dialed number as the destination user in the SIP INVITE, which is how Simple AI matches the call to a number and its agent.
## Step 6: Assign Agents and Test
Open each connected number from the trunk's detail page, or from **Numbers**, and assign the voice agent that should answer it. Then place an inbound test call to that number — Amazon Chime SDK forwards it to Simple AI and the agent answers.
If the trunk also handles outbound, place a test call through the API using one of its numbers as the caller ID. `from_number_id` is the number's UUID, which you can read from its dashboard URL or from [List Numbers](/api-reference/numbers/list-numbers):
```bash theme={null}
curl -X POST "https://api.usesimple.ai/api/v1/calls" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "your-agent-uuid",
"to_number": "+15551234567",
"from_number_id": "your-phone-number-uuid"
}'
```
Before moving production traffic, verify caller ID, audio quality, DTMF, and a live agent escalation back into Amazon Connect.
## Routing Amazon Connect Calls to the Trunk
With the Voice Connector in place, point the contact flows that should use the AI at one of its numbers:
* **Inbound** — add a **Transfer to phone number** block to the flow, targeting a number on the Voice Connector.
* **Outbound campaigns** — configure the campaign's connected-call handling to transfer to a Voice Connector number, so Amazon Connect keeps pacing, DNC, and answering machine detection.
* **Escalation back** — claim a number in Amazon Connect, associate it with an inbound flow that transfers to the queue you want, and give Simple AI that number as the transfer destination. Transfer destinations are E.164 numbers or SIP URIs; a queue identifier is not dialable, so the queue has to sit behind a number.
## Troubleshooting
| Symptom | What to check |
| - | - |
| Inbound calls never reach Simple AI | Origination is enabled, the inbound route points at `sip.usesimple.ai` on the right port and protocol, and the number's status on the trunk is **Configured**. |
| Inbound calls are rejected | The Chime SDK signaling network for your region is in the trunk's **Carrier signaling source CIDRs**, and the dialed number matches a number on the trunk in E.164 exactly. |
| Outbound calls fail to connect | Termination is enabled, Simple AI's signaling CIDRs are in **Allowed hosts**, the destination country is in the calling plan, and any digest credentials match. |
| Call connects but there is no audio | Media encryption matches on both sides — **Require SRTP** with encryption enabled, **Disabled — RTP** without it. |
For SIP-level detail, enable SIP logging on the Voice Connector's **Logging** tab to get full message traces in CloudWatch.
## Related
* [CCaaS Integrations](/integrations/ccaas/overview)
* [SIP Integration](/sip-integration) — connection details, codecs, and call flows
* [Numbers](/numbers) — assigning agents to numbers
# Connect Simple AI to Avaya Contact Center
Source: https://docs.usesimple.ai/integrations/ccaas/avaya
Integrate Simple AI with Avaya Aura, Elite, or Experience Portal over SIP for automated call handling, contact center routing, and transfers.
Simple AI connects to Avaya over a SIP trunk from Session Manager, Experience Portal, or an SBC. Avaya keeps vectors, queues, and agent state; Simple AI handles the conversations routed to it and hands them back for live agent escalation.
Avaya Aura / Communication Manager with Elite, Avaya Experience Portal, and Avaya Experience Platform all connect the same way.
## What You Can Do
* **Inbound routing** — a vector or Experience Portal application answers the call, then transfers it to Simple AI's SIP endpoint.
* **Outbound campaigns** — the Avaya dialer handles pacing and compliance, and transfers connected calls to Simple AI.
* **Direct outbound** — Simple AI places calls through the trunk with your existing caller ID.
* **Live agent escalation** — Simple AI transfers back to a VDN, skill, or agent extension.
* **Context on transfer** — call metadata rides along in `X-` prefixed SIP headers for screen pop and CRM lookup.
## How It Connects
```text theme={null}
Customer → Carrier → Avaya (VDN / vector) → Session Manager → Simple AI
← escalation back ←
```
1. Avaya receives the call and runs your vector or Experience Portal application.
2. The call is transferred to Simple AI over the SIP trunk, typically via Session Manager or an SBC.
3. Simple AI runs the conversation and transfers back when escalation is needed.
4. Avaya queues the returned call to the target VDN or skill and delivers it to an agent with the transferred context.
## Setup
1. Add a SIP entity link and routing policy in Session Manager (or the equivalent SBC configuration) pointing at Simple AI's SIP endpoint, and allowlist our signaling and media IPs.
2. Give your account team your SIP endpoint, auth method, and the numbers involved in E.164 format.
3. Add the handoff to the vectors or Experience Portal applications that should use the AI, and a return VDN for escalation.
4. Test inbound, outbound, and escalation, verifying caller ID, audio, DTMF, and agent-side context.
Your account team configures the Simple AI side. Contact [support@usesimple.ai](mailto:support@usesimple.ai) to get started.
## Related
* [CCaaS Integrations](/integrations/ccaas/overview)
* [SIP Integration](/sip-integration)
# Connect Simple AI to Cisco Contact Center
Source: https://docs.usesimple.ai/integrations/ccaas/cisco
Integrate Simple AI with Cisco CUCM, UCCX, or Webex Contact Center over SIP for AI call automation, routing, and live-agent transfers.
Simple AI peers with Cisco over a SIP trunk. CUCM handles call control and UCCX handles queueing and agent delivery; Simple AI handles the conversations routed to it and hands them back for live agent escalation.
Cisco Unified Communications Manager (CUCM), Unified Contact Center Express (UCCX), Unified CCE, and Webex Contact Center all connect over SIP.
## What You Can Do
* **Inbound routing** — a CTI route point or IVR script answers the call, then transfers it to Simple AI over the trunk.
* **Outbound campaigns** — the Cisco dialer handles pacing and compliance, and transfers connected calls to Simple AI.
* **Direct outbound** — Simple AI places calls through the trunk with your existing caller ID.
* **Live agent escalation** — Simple AI transfers back to a CTI route point, queue, or agent extension.
* **Context on transfer** — call metadata rides along in `X-` prefixed SIP headers for screen pop and CRM lookup.
## How It Connects
```text theme={null}
Customer → Carrier → CUCM (route pattern) → UCCX (CTI route point) → Simple AI
← escalation back ←
```
1. CUCM receives the call and matches a route pattern.
2. A CTI route point or IVR script decides the AI should handle the call and transfers it to Simple AI over the SIP trunk.
3. Simple AI runs the conversation and transfers back when escalation is needed.
4. UCCX queues the returned call and delivers it to an agent with the transferred context.
## Setup
1. Create a SIP trunk in CUCM pointing at Simple AI's SIP endpoint, with a route pattern for the AI destination, and allowlist our signaling and media IPs.
2. Give your account team your SIP endpoint, auth method, and the numbers involved in E.164 format.
3. Add the handoff to the CTI route points or UCCX scripts that should use the AI, and a return destination for escalation.
4. Test inbound, outbound, and escalation, verifying caller ID, audio, DTMF, and agent-side context.
Your account team configures the Simple AI side. Contact [support@usesimple.ai](mailto:support@usesimple.ai) to get started.
## Related
* [CCaaS Integrations](/integrations/ccaas/overview)
* [SIP Integration](/sip-integration)
# Connect Simple AI to Dialpad Ai Contact Center
Source: https://docs.usesimple.ai/integrations/ccaas/dialpad
Integrate Simple AI with Dialpad Ai Contact Center over SIP for automated call handling, contact center routing, and live-agent transfers.
Simple AI connects to Dialpad over a SIP trunk. Dialpad keeps routing, contact center queues, and agent state; Simple AI handles the conversations routed to it and hands them back for live agent escalation.
## What You Can Do
* **Inbound routing** — Dialpad answers the call, runs IVR and routing, then transfers it to Simple AI's SIP endpoint.
* **Outbound campaigns** — your dialer handles pacing and compliance, and transfers connected calls to Simple AI.
* **Direct outbound** — Simple AI places calls through the trunk with your existing caller ID.
* **Live agent escalation** — Simple AI transfers back to a Dialpad contact center, department, or agent.
* **Context on transfer** — call metadata rides along in `X-` prefixed SIP headers for screen pop and CRM lookup.
## How It Connects
```text theme={null}
Customer → Carrier → Dialpad (IVR / contact center) → Simple AI
← escalation back ←
```
1. Dialpad receives the call and runs your IVR and routing rules.
2. A transfer sends the call to Simple AI over the SIP trunk.
3. Simple AI runs the conversation and transfers back when escalation is needed.
4. Dialpad routes the returned call to an agent with the transferred context.
## Setup
1. Configure SIP connectivity from Dialpad to Simple AI's SIP endpoint, and allowlist our signaling and media IPs.
2. Give your account team the Dialpad endpoint, auth details, and the numbers involved in E.164 format.
3. Point the IVR or contact center routing that should use the AI at the transfer destination, and set a return destination for escalation.
4. Test inbound, outbound, and escalation, verifying caller ID, audio, DTMF, and agent-side context.
Your account team configures the Simple AI side. Contact [support@usesimple.ai](mailto:support@usesimple.ai) to get started.
## Related
* [CCaaS Integrations](/integrations/ccaas/overview)
* [SIP Integration](/sip-integration)
# Connect Simple AI to Five9 Contact Center
Source: https://docs.usesimple.ai/integrations/ccaas/five9
Connect Simple AI with Five9 for inbound and outbound call routing, recording synchronization, AI automation, and transfers to live agents.
Five9 is Simple AI's deepest contact center integration. Beyond SIP call routing, Simple AI can sync Five9 call recordings for analysis and hand both calls and chats to Five9 agents with full context.
## What You Can Do
* **Inbound routing** — Five9 answers the call, runs IVR and routing, then transfers it to Simple AI.
* **Outbound campaigns** — the Five9 dialer manages pacing and compliance, and connected calls transfer to Simple AI.
* **Live agent transfer** — Simple AI transfers a call back into Five9, to a campaign, skill, or agent.
* **Chat transfer** — hand a chat conversation to a Five9 agent when the AI cannot resolve it.
* **Call recording sync** — import recordings of calls your human agents handled in Five9, so they get the same transcripts, analyzers, and scorecards as AI calls.
## How Context Travels
Simple AI reads the Five9 call identifiers off the inbound SIP INVITE, keeps them on the conversation, and sends routing instructions back on the SIP BYE when the AI transfers the call.
| Direction | Headers |
| - | - |
| Five9 → Simple AI (INVITE) | `X-CallANI`, `X-CallDNIS`, `X-CallID`, `X-CallSessionID` |
| Simple AI → Five9 (BYE) | `X-RouteReason`, `X-RouteType`, `X-RouteValue`, `X-MetaData` |
The route headers tell Five9 where to send the call after the AI ends its leg, and `X-MetaData` carries the context your agent's screen pop needs. Header values are filled in from call parameters, so a transfer can pass an order number, verified caller identity, or resolution attempt to the receiving agent.
Each header value is capped at 256 characters to stay well inside SIP message limits.
## Call Recording Sync
Five9 recordings are delivered to an S3 bucket that Simple AI provisions for your organization. Once a recording lands, Simple AI transcribes it, separates the speakers into agent and customer, enriches it with the Five9 call metadata, and makes it searchable and reportable alongside your AI conversations.
You can also backfill existing recordings already in the bucket.
## Setup
1. Go to **Settings > Integrations > Five9** in the Simple AI dashboard.
2. Connect your Five9 account credentials. Simple AI provisions the recording bucket and returns the credentials Five9 needs to write to it.
3. In Five9, configure recording delivery to that bucket.
4. For call routing, share your Five9 SIP details with your account team so the trunk and header mapping are configured on both sides.
5. Place a test call in each direction and confirm caller ID, audio, transfer routing, and the metadata on the agent's screen.
## Related
* [CCaaS Integrations](/integrations/ccaas/overview)
* [SIP Integration](/sip-integration)
# Connect Simple AI to Genesys Cloud and Engage
Source: https://docs.usesimple.ai/integrations/ccaas/genesys
Integrate Simple AI with Genesys Cloud or Genesys Engage over SIP for AI call automation, contact center routing, and live-agent transfers.
Simple AI connects to Genesys over a SIP trunk. Genesys keeps routing, queues, agent state, and reporting; Simple AI handles the conversations you route to it and hands them back when a person is needed.
Both Genesys Cloud (BYOC Cloud or BYOC Premises) and Genesys Engage / PureConnect work — Engage peers through SIP Server or an SBC.
## What You Can Do
* **Inbound routing** — an Architect flow answers the call, then transfers to Simple AI's SIP endpoint when the AI should handle it.
* **Outbound campaigns** — the Genesys dialer manages pacing, DNC, and answering machine detection, then transfers connected calls to Simple AI.
* **Direct outbound** — Simple AI places calls through the trunk with your existing caller ID.
* **Live agent escalation** — Simple AI transfers the call back to a Genesys queue, skill, or agent.
* **Context on transfer** — call metadata rides along in `X-` prefixed SIP headers for screen pop and CRM lookup.
* **Screen pop** — open a page beside the call that shows the agent the AI's summary, the data it collected, and the transcript. See [Screen Pop for AI Handoffs](/integrations/ccaas/screen-pop).
## How It Connects
```text theme={null}
Customer → Carrier → Genesys (Architect flow / queue) → Simple AI
← escalation back to queue ←
```
1. Genesys receives the call and runs its normal IVR and routing logic.
2. An Architect transfer sends the call to Simple AI over the trunk.
3. Simple AI runs the conversation and, when escalation is needed, transfers back to a Genesys destination.
4. Genesys queues the call and delivers it to an agent with the transferred context.
## Setup
1. Create an external trunk in Genesys (BYOC Cloud, BYOC Premises, or an Engage SIP trunk) pointing at Simple AI's SIP endpoint.
2. Allowlist Simple AI's signaling and media IPs, and give your account team the Genesys endpoint and auth details.
3. Add the transfer to Simple AI in the Architect flow that should hand off to the AI, and a return destination for escalation.
4. Test inbound, outbound, and escalation, checking caller ID, audio, DTMF, and the metadata the agent receives.
Your account team configures the Simple AI side. Contact [support@usesimple.ai](mailto:support@usesimple.ai) to get started.
## Related
* [CCaaS Integrations](/integrations/ccaas/overview)
* [SIP Integration](/sip-integration)
# Connect Simple AI to NiCE CXone
Source: https://docs.usesimple.ai/integrations/ccaas/nice-cxone
Integrate Simple AI with NiCE CXone using Studio scripts and a SIP backchannel for AI call handling, routing, and contact center transfers.
Simple AI connects to NiCE CXone through a SIP backchannel driven by Studio scripts. CXone stays in control of routing and agent delivery, and calls its scripts decide should be handled by the AI are placed on the backchannel to Simple AI.
## What You Can Do
* **Inbound routing** — a Studio script answers the call and places it on the SIP backchannel to Simple AI.
* **Outbound campaigns** — Personal Connection handles pacing and compliance, and connected calls are handed to Simple AI.
* **Direct outbound** — Simple AI places calls through the trunk with your caller ID.
* **Live agent escalation** — Simple AI signals the running Studio script, which routes the call to the right skill or agent.
* **Context on transfer** — metadata travels in `X-` prefixed SIP headers so the receiving agent's screen pop is populated.
* **Screen pop** — open a page beside the call that shows the agent the AI's summary, the data it collected, and the transcript. See [Screen Pop for AI Handoffs](/integrations/ccaas/screen-pop).
## How It Connects
```text theme={null}
Customer → Carrier → CXone (Studio script) → Simple AI
← Signal API escalation ←
```
1. A Studio script receives the call and runs your IVR and routing logic.
2. The script places the call on the SIP backchannel to Simple AI.
3. Simple AI runs the conversation.
4. For escalation, Simple AI signals the script through the CXone Signal API, and the script routes the call to a skill or agent.
Because escalation goes through the script rather than a blind transfer, your existing CXone routing rules, skills, and priorities stay authoritative.
## Setup
1. Configure the SIP backchannel between CXone and Simple AI's SIP endpoint, and allowlist our signaling and media IPs.
2. Add the handoff and escalation steps to the Studio scripts that should use the AI.
3. For outbound, point the relevant Personal Connection campaigns at the handoff script.
4. Test inbound, outbound, and escalation, verifying caller ID, audio, DTMF, and agent-side context.
Your account team configures the Simple AI side and the Signal API credentials. Contact [support@usesimple.ai](mailto:support@usesimple.ai) to get started.
## Related
* [CCaaS Integrations](/integrations/ccaas/overview)
* [SIP Integration](/sip-integration)
# Connect Simple AI to CCaaS Platforms
Source: https://docs.usesimple.ai/integrations/ccaas/overview
Integrate Simple AI with Five9, Genesys, NiCE CXone, Talkdesk, RingCentral, and other CCaaS platforms for routing, transfers, and call analysis.
Simple AI runs alongside your existing contact center. Your platform keeps ownership of routing, queues, agent state, workforce management, and reporting — Simple AI becomes another destination it can send conversations to, and a place it can take them back from.
Every integration is built on standard SIP, so any contact center platform that supports SIP trunking works. Some platforms have additional native support on top of SIP, such as recording ingestion or chat transfer.
## Supported Platforms
| Platform | How It Connects | Beyond SIP |
| - | - | - |
| [Five9](/integrations/ccaas/five9) | SIP trunk with Five9-specific call headers | Call recording sync, live agent transfer, chat transfer |
| [Genesys](/integrations/ccaas/genesys) | SIP trunk (Genesys Cloud BYOC or Engage SIP Server) | — |
| [NiCE CXone](/integrations/ccaas/nice-cxone) | SIP backchannel driven by Studio scripts | Agent escalation via the Signal API |
| [Talkdesk](/integrations/ccaas/talkdesk) | SIP trunk (BYOC) with Studio routing | — |
| [RingCentral](/integrations/ccaas/ringcentral) | SIP trunk (Cloud Connect or SIP peering) | — |
| [8x8](/integrations/ccaas/8x8) | SIP trunk with 8x8 Contact Center routing | — |
| [Amazon Connect](/integrations/ccaas/amazon-connect) | Contact flow transfer, or SIP via Chime SDK Voice Connector | — |
| [Avaya](/integrations/ccaas/avaya) | SIP trunk from Session Manager or Experience Portal | — |
| [Cisco](/integrations/ccaas/cisco) | SIP trunk peering with CUCM route patterns and UCCX CTI route points | — |
| [Twilio Flex](/integrations/ccaas/twilio-flex) | SIP trunk or TaskRouter-driven transfer | — |
| [Dialpad](/integrations/ccaas/dialpad) | SIP trunk with Dialpad Ai Contact Center routing | — |
| [Zoom Contact Center](/integrations/ccaas/zoom) | SIP trunk with Zoom flow routing | — |
Not on the list? Any SIP-compatible platform connects the same way — see [SIP Integration](/sip-integration).
## What the Integration Covers
* **Inbound to the AI** — your platform answers the call, runs IVR and routing, then transfers the conversation to Simple AI.
* **Outbound campaigns** — your dialer handles pacing, DNC compliance, and answering machine detection, then hands connected calls to Simple AI.
* **Direct outbound** — Simple AI places calls itself through your SIP trunk with your caller ID, with no contact center in the middle.
* **Live agent escalation** — Simple AI transfers back into your platform, to a specific queue, skill, agent, or external number.
* **Context on transfer** — call metadata travels with the transfer in `X-` prefixed SIP headers, so the receiving agent's screen pop and CRM record are already populated.
* **Screen pop** — open a page beside the call that shows the agent the AI's summary, the data it collected, and the transcript. See [Screen Pop for AI Handoffs](/integrations/ccaas/screen-pop).
* **Analytics in one place** — calls handled by Simple AI get the same transcripts, tags, analyzers, and scorecards as every other Simple AI conversation.
## Choosing a Flow
| If you want | Use |
| - | - |
| The contact center to stay the front door for every call | Inbound transfer to Simple AI |
| Bulk outreach with your existing compliance and pacing rules | Contact center dialer + transfer to Simple AI |
| The lowest possible connect time on outbound | Direct outbound through your SIP trunk |
| The AI to deflect and escalate only when it cannot resolve | Inbound transfer plus live agent escalation |
## Setup
Setup is the same shape on every platform, and your account team runs it with you:
1. **Share your platform details** — the platform and edition you run, your SIP endpoint or trunk, auth method (IP allowlist or SIP digest), and the numbers involved in E.164 format.
2. **Simple AI configures the trunk** — connection, authentication, number routing, codecs, and any platform-specific header mapping.
3. **You configure your platform** — point the relevant routing at Simple AI's SIP endpoint and allowlist our IPs.
4. **Test both directions** — verify caller ID, audio quality, DTMF, transfers, and the metadata that arrives with an escalation.
Contact [support@usesimple.ai](mailto:support@usesimple.ai) to start. CCaaS integration is available on Enterprise plans.
## Related
* [SIP Integration](/sip-integration) — connection details, codecs, and call flows
* [Built-In Integrations](/integrations/built-in) — CRM, helpdesk, and notification integrations
* [Workflows](/integrations/workflows) — pre-call and post-call automation around each conversation
# Connect Simple AI to RingCentral and RingEX
Source: https://docs.usesimple.ai/integrations/ccaas/ringcentral
Integrate Simple AI with RingCentral Contact Center and RingEX over SIP for AI-powered call handling, routing, and live-agent transfers.
Simple AI connects to RingCentral over a SIP trunk. RingCentral keeps routing, queues, and agent state; Simple AI handles the conversations routed to it and hands them back for live agent escalation.
Both RingCentral Contact Center and RingEX (for straightforward call routing) work over the same trunk.
## What You Can Do
* **Inbound routing** — RingCentral answers the call, runs IVR and queue routing, then transfers it to Simple AI's SIP endpoint.
* **Outbound campaigns** — the RingCentral dialer handles pacing and compliance, and transfers connected calls to Simple AI.
* **Direct outbound** — Simple AI places calls through the trunk with your existing caller ID.
* **Live agent escalation** — Simple AI transfers back to a RingCentral queue, extension, or external number.
* **Context on transfer** — call metadata rides along in `X-` prefixed SIP headers for screen pop and CRM lookup.
## How It Connects
```text theme={null}
Customer → Carrier → RingCentral (IVR / queue) → Simple AI
← escalation back ←
```
1. RingCentral receives the call and runs your IVR and routing rules.
2. A transfer sends the call to Simple AI over the SIP trunk.
3. Simple AI runs the conversation and transfers back when escalation is needed.
4. RingCentral queues the returned call and delivers it to an agent with the transferred context.
## Setup
1. Set up SIP connectivity — RingCentral Cloud Connect or SIP peering — pointing at Simple AI's SIP endpoint, and allowlist our signaling and media IPs.
2. Give your account team the RingCentral endpoint, auth details, and the numbers involved in E.164 format.
3. Point the IVR nodes or queue overflow rules that should use the AI at the transfer destination, and set a return destination for escalation.
4. Test inbound, outbound, and escalation, verifying caller ID, audio, DTMF, and agent-side context.
Your account team configures the Simple AI side. Contact [support@usesimple.ai](mailto:support@usesimple.ai) to get started.
## Related
* [CCaaS Integrations](/integrations/ccaas/overview)
* [SIP Integration](/sip-integration)
# Screen Pop for AI Handoffs to Your Contact Center
Source: https://docs.usesimple.ai/integrations/ccaas/screen-pop
Show your agents what the AI already learned, beside the call, the moment Simple AI hands a caller to a person.
When Simple AI hands a call to one of your agents, your contact center can open a page beside the call that shows the AI's summary of the call, the data it collected, and the live transcript.
## How It Works
The page lives at one address. The only part that changes per call is a token:
```text theme={null}
https://api.usesimple.ai/screen-pop#token=
```
The token goes after the `#`, as `token=`. That part of an address is written like a query string, and `token` is the one parameter the page reads. A browser never sends that part of an address to a server, so the token stays out of server logs on the way. The token names one call and stops working four hours after the call started. Anyone holding it can read that call's page until then, so treat it like a link to the call and do not store it longer than you need to.
Your call flow gets the token, keeps it in a call variable, and your platform's screen pop feature opens the address built from that variable. Nothing is stored in Simple AI. In the dashboard, **Configuration > CCaaS Handoffs** builds the address for your platform, lets you choose what the page shows, and previews it with one of your own calls.
## Choosing What It Shows
By default the page shows the summary, every piece of data the AI collected, and the transcript. Add options to the address to change that:
| Option | Meaning | Example |
| - | - | - |
| `show` | The sections to show: `summary`, `data`, `transcript` | `?show=summary,data` |
| `fields` | The collected fields to show, in order. Add `:Label` to name one | `?fields=order_number:Order,customer.name` |
```text theme={null}
https://api.usesimple.ai/screen-pop?show=summary,data&fields=order_number:Order,customer.name#token=
```
Options go before the `#`. After it, the page reads only `token`.
The options are fixed text in the address you configure, so different queues can show different things. They tidy the page; they do not restrict access. Anyone holding the token can open the call without them.
## Getting the Token
Every call returned by the [calls API](/api-reference/calls/get-call) carries a `screen_pop_token`. From a call flow, the usual request is [find by attribute](/api-reference/calls/find-by-attribute) with the identifier your platform sent Simple AI when it forwarded the caller. Simple AI keeps every `X-` header from that first SIP message as a call parameter named `sip.headers.`.
```bash theme={null}
curl "https://api.usesimple.ai/api/v1/calls/find_by_attribute?param_key=sip.headers.x_callsessionid¶m_value=&fields=screen_pop_token" \
-H "Authorization: Bearer "
```
## Platform Steps
### Five9
1. Add a Query module to the IVR script that calls find by attribute with `Call.session_id` and stores `screen_pop_token` in a call variable.
2. Create a connector and type the whole URL, `https://api.usesimple.ai/screen-pop#token=@.@`, with trigger **On Call Accepted** and execution mode **Use embedded tab for Web**. Leave the connector's Parameters table empty: Five9 adds parameters to the end of the URL, where they would run into the token.
3. Add the connector to the campaigns your agents take handoffs on.
### NiCE CXone
1. Before the script places the call to Simple AI, add your contact ID as a header with SIPPUTHEADER, for example `X-ContactID` = `{contactid}`.
2. After the call returns to the script, add a REST API action that calls find by attribute with `param_key=sip.headers.x_contactid` and keeps `screen_pop_token` in a variable.
3. After ONANSWER, add a POPURL action with the URL `https://api.usesimple.ai/screen-pop#token={token}` and PopDestination **ContactPanel**.
Agents who use CXone inside Salesforce get the page in a new browser tab; CXone does not embed outside pages there.
### Genesys Cloud
1. In Architect, get the token into a flow variable with a web services Data Action that calls find by attribute using the identifier you sent Simple AI.
2. Create a script with one Web Page component that fills the page, and set its source to a string variable marked as an input. Publish the script.
3. In the flow, before **Transfer to ACD**, add **Set Screen Pop**, choose the script, and set its input to `https://api.usesimple.ai/screen-pop#token=` followed by the token.
Genesys stores script inputs on the conversation, where supervisors can see them. The token expires, which is why it is safe to pass this way.
### Talkdesk
These steps assume the AI takes calls through Talkdesk's **Connect to Autopilot Voice** component, which sends Simple AI the Talkdesk interaction ID in an `X-Talkdesk-Interaction-Id` header and continues the flow at its **Escalation** exit when the AI hands the caller back.
1. In **Connections**, add a custom connection to `https://api.usesimple.ai` with authentication type **Header X API Key**, header name `Authorization`, and value `Bearer `. Give it an action that calls find by attribute with `param_key=sip.headers.x_talkdesk_interaction_id` and `fields=screen_pop_token`, taking the interaction ID as `param_value`, with `call.screen_pop_token` in its output schema.
2. In Studio, on the **Escalation** exit of Connect to Autopilot Voice, add an **Execute Action** step that runs the action with the call's interaction ID and keeps `call.screen_pop_token` in a flow variable named `screen_pop_token`. Select that variable in **Manage context** so automations can read it.
3. In **Automation Tools**, add a task with the trigger **An inbound call starts** and the action **Open URL**, with the URL `https://api.usesimple.ai/screen-pop#token={{context.screen_pop_token}}`.
To show the page inside the agent's Conversations app instead of in a browser tab, build a Workspace Designer card with an iframe whose source is the same address, reading the variable with `Context.getVariable("screen_pop_token")`.
### Amazon Connect
Amazon Connect sends Simple AI nothing to find a call by. A **Transfer to phone number** block places an ordinary phone call, with no SIP headers and no contact attributes (see [Amazon Connect](/integrations/ccaas/amazon-connect#what-neither-option-carries)). The lookup in step 1 therefore needs an identifier your flow gives Simple AI some other way; work that out with your account team before you build the rest.
1. In the flow of the number Simple AI hands calls back to, add an **AWS Lambda function** block with response validation set to **JSON**. The Lambda calls find by attribute with that identifier and `fields=screen_pop_token`, and returns the address as `screen_pop_url`: `https://api.usesimple.ai/screen-pop#token=` followed by `call.screen_pop_token`. The address has to be built in the Lambda, because a string map response only allows letters, digits, dashes, and underscores.
2. Add **Set contact attributes** to keep `$.External.screen_pop_url` as the user-defined attribute `screen_pop_url`.
3. Before **Transfer to queue**, add **Set event flow** with the event **Default flow for agent UI**, pointing at a flow whose **Show view** block shows the **Detail** view with a link in its attribute bar: `{"Label": "Simple AI", "Value": "Open handoff", "LinkType": "external", "Url": "$.Attributes.screen_pop_url"}`.
The agent who accepts the call opens the page from that link, in a new browser tab. Agents need the security profile permission **Agent Applications - Custom views - All** to see the view.
### Other Platforms
Any platform whose screen pop can build an address from a call variable works the same way: get the token from the API in the call flow, then open `https://api.usesimple.ai/screen-pop#token=`. The page can be embedded in any agent desktop or opened in a new tab.
## Related
* [CCaaS Integrations](/integrations/ccaas/overview)
* [Find a call by attribute](/api-reference/calls/find-by-attribute)
# Connect Simple AI to Talkdesk Contact Center
Source: https://docs.usesimple.ai/integrations/ccaas/talkdesk
Integrate Simple AI with Talkdesk through a BYOC SIP trunk and Studio routing for AI call automation and transfers into contact center queues.
Simple AI connects to Talkdesk over a SIP trunk. Talkdesk Studio decides which calls the AI handles, and Simple AI hands conversations back to Talkdesk routing when a person is needed.
## What You Can Do
* **Inbound routing** — a Studio flow answers the call and transfers it to Simple AI's SIP endpoint.
* **Outbound campaigns** — the Talkdesk dialer handles pacing and compliance, and transfers connected calls to Simple AI.
* **Direct outbound** — Simple AI places calls through the trunk with your existing caller ID.
* **Live agent escalation** — Simple AI transfers back to a Talkdesk ring group, skill, or agent.
* **Context on transfer** — call metadata rides along in `X-` prefixed SIP headers for screen pop and CRM lookup.
* **Screen pop** — when calls reach Simple AI through Connect to Autopilot Voice, open a page for the agent who takes the handoff that shows the AI's summary, the data it collected, and the transcript. See [Screen Pop for AI Handoffs](/integrations/ccaas/screen-pop#talkdesk).
## How It Connects
```text theme={null}
Customer → Carrier → Talkdesk (Studio flow) → Simple AI
← escalation back ←
```
1. Talkdesk receives the call and runs your Studio flow.
2. A transfer step sends the call to Simple AI over the SIP trunk.
3. Simple AI runs the conversation and transfers back when escalation is needed.
4. Talkdesk routes the returned call to an agent with the transferred context.
## Setup
1. Set up a Talkdesk BYOC SIP trunk pointing at Simple AI's SIP endpoint, and allowlist our signaling and media IPs.
2. Give your account team the Talkdesk trunk endpoint, auth details, and the numbers involved in E.164 format.
3. Add the handoff step to the Studio flows that should use the AI, and a return destination for escalation.
4. Test inbound, outbound, and escalation, verifying caller ID, audio, DTMF, and agent-side context.
Your account team configures the Simple AI side. Contact [support@usesimple.ai](mailto:support@usesimple.ai) to get started.
## Related
* [CCaaS Integrations](/integrations/ccaas/overview)
* [SIP Integration](/sip-integration)
# Connect Simple AI to Twilio Flex
Source: https://docs.usesimple.ai/integrations/ccaas/twilio-flex
Integrate Simple AI with Twilio Flex using SIP or TaskRouter-driven transfers for AI call automation and handoffs into contact center queues.
Simple AI connects to Twilio Flex either over a SIP trunk or by transferring calls from a TwiML flow. Flex keeps TaskRouter queues, worker state, and reporting; Simple AI handles the conversations routed to it and hands them back for live agent escalation.
## What You Can Do
* **Inbound routing** — your Studio or TwiML flow answers the call, then transfers it to Simple AI.
* **Outbound campaigns** — your dialer or campaign logic hands connected calls to Simple AI.
* **Direct outbound** — Simple AI places calls through your Twilio SIP trunk with your existing caller ID.
* **Live agent escalation** — Simple AI transfers back into Flex so TaskRouter can queue the call to a worker.
* **Context on transfer** — over SIP, metadata rides along in `X-` prefixed SIP headers; from a TwiML flow, context is passed as task attributes.
## How It Connects
```text theme={null}
Customer → Twilio (Studio / TwiML) → Simple AI
← escalation back ←
```
1. Twilio receives the call and runs your Studio or TwiML flow.
2. The flow transfers the call to Simple AI — via the SIP trunk or a dial to the AI destination.
3. Simple AI runs the conversation and transfers back when escalation is needed.
4. TaskRouter creates a task for the returned call and routes it to a worker with the transferred context.
## Setup
1. Choose SIP trunking or a TwiML transfer, and share the details with your account team.
2. For SIP, create a Twilio Elastic SIP Trunk pointing at Simple AI's SIP endpoint, and allowlist our signaling and media IPs.
3. Add the handoff to the flows that should use the AI, and a return destination for escalation.
4. Test inbound, outbound, and escalation, verifying caller ID, audio, DTMF, and agent-side context.
Your account team configures the Simple AI side. Contact [support@usesimple.ai](mailto:support@usesimple.ai) to get started.
## Related
* [CCaaS Integrations](/integrations/ccaas/overview)
* [SIP Integration](/sip-integration)
# Connect Simple AI to Zoom Contact Center
Source: https://docs.usesimple.ai/integrations/ccaas/zoom
Integrate Simple AI with Zoom Contact Center over SIP for AI-powered call automation, inbound and outbound routing, and human-agent transfers.
Simple AI connects to Zoom Contact Center over a SIP trunk. Zoom keeps flow routing, queues, and agent state; Simple AI handles the conversations routed to it and hands them back for live agent escalation.
## What You Can Do
* **Inbound routing** — a Zoom flow answers the call, runs your IVR logic, then transfers it to Simple AI's SIP endpoint.
* **Outbound campaigns** — your dialer handles pacing and compliance, and transfers connected calls to Simple AI.
* **Direct outbound** — Simple AI places calls through the trunk with your existing caller ID.
* **Live agent escalation** — Simple AI transfers back to a Zoom queue, flow, or agent.
* **Context on transfer** — call metadata rides along in `X-` prefixed SIP headers for screen pop and CRM lookup.
## How It Connects
```text theme={null}
Customer → Carrier → Zoom Contact Center (flow) → Simple AI
← escalation back ←
```
1. Zoom receives the call and runs your contact center flow.
2. A transfer step sends the call to Simple AI over the SIP trunk.
3. Simple AI runs the conversation and transfers back when escalation is needed.
4. Zoom queues the returned call and delivers it to an agent with the transferred context.
## Setup
1. Configure SIP connectivity from Zoom to Simple AI's SIP endpoint, and allowlist our signaling and media IPs.
2. Give your account team the Zoom endpoint, auth details, and the numbers involved in E.164 format.
3. Add the handoff to the flows that should use the AI, and a return destination for escalation.
4. Test inbound, outbound, and escalation, verifying caller ID, audio, DTMF, and agent-side context.
Your account team configures the Simple AI side. Contact [support@usesimple.ai](mailto:support@usesimple.ai) to get started.
## Related
* [CCaaS Integrations](/integrations/ccaas/overview)
* [SIP Integration](/sip-integration)
# Run Custom Backend Logic from AI Voice Agents
Source: https://docs.usesimple.ai/integrations/custom-code
Connect Simple AI agents to custom backend logic for authenticated lookups, business rules, data transformations, and actions during calls.
Custom code integrations let you run your own server-side logic as tools that agents call during conversations. Instead of pointing an agent at a single API endpoint, you build a middleware layer that can combine multiple data sources, apply business rules, transform responses, and handle complex orchestration — all behind a single tool the agent calls naturally.
## When to Use Custom Code
Use custom code when:
* A tool needs to call multiple APIs before returning a single response to the agent
* You need to transform, filter, or enrich data before the agent sees it
* Business rules, validation, or authorization logic should run server-side
* The integration requires custom authentication flows or token management
* You want to abstract complex backend systems behind a simple interface for the agent
For simple single-endpoint API calls, [HTTP integrations](/integrations/http) are easier and do not require your own server. For no-code automation, [Zapier](/integrations/built-in#zapier) may be a better fit.
## How It Works
1. You build an endpoint on your server that accepts requests from Simple AI
2. You create an [HTTP tool](/integrations/http) on your agent that points to your endpoint
3. During a call, the agent calls your endpoint like any other tool
4. Your server runs whatever logic you need — database queries, multi-API orchestration, business rules
5. Your server returns a clean JSON response that the agent uses to continue the conversation
Your endpoint is a normal web server. You can write it in any language, use any framework, and deploy it anywhere.
## Example: Combined Customer Lookup
Instead of giving the agent three separate tools (CRM lookup, billing check, ticket search), build one endpoint that combines all three:
```text theme={null}
Agent calls: POST https://your-server.com/api/simple-ai/customer-lookup
Body: { "phone_number": "+15551234567" }
Your server:
1. Queries your CRM for the customer record
2. Calls your billing API to get payment status and plan details
3. Calls your ticketing system for open support cases
4. Combines everything into a single response
Response:
{
"customer_name": "Sarah Johnson",
"account_id": "ACCT-789",
"plan": "Enterprise",
"payment_status": "current",
"open_tickets": [
{ "id": "T-456", "subject": "Billing discrepancy", "status": "open" }
]
}
```
The agent gets everything it needs in one call, and your backend handles the complexity.
## Example: Eligibility Check with Business Rules
An insurance agent needs to check whether a caller is eligible for a specific plan. The rules are complex and change frequently, so they live in your backend:
```text theme={null}
Agent calls: POST https://your-server.com/api/simple-ai/eligibility-check
Body: { "member_id": "MBR-123", "plan_code": "GOLD-2026" }
Your server:
1. Looks up the member in your enrollment system
2. Checks their current coverage, age, and state of residence
3. Applies the latest eligibility rules for the requested plan
4. Returns a clear yes/no with explanation
Response:
{
"eligible": true,
"reason": "Member meets all criteria for GOLD-2026",
"effective_date": "2026-05-01",
"monthly_premium": "$425.00"
}
```
The agent can communicate the result without understanding the underlying rules.
## Example: Order Action with Side Effects
A support agent needs to process a return. This involves multiple steps that should happen atomically on your backend:
```text theme={null}
Agent calls: POST https://your-server.com/api/simple-ai/process-return
Body: { "order_id": "ORD-789", "reason": "defective", "items": ["SKU-A", "SKU-B"] }
Your server:
1. Validates the order exists and is eligible for return
2. Creates the return in your fulfillment system
3. Initiates the refund in your payment processor
4. Sends a return shipping label to the customer's email
5. Updates the order status in your OMS
Response:
{
"return_id": "RET-456",
"refund_amount": "$89.99",
"refund_method": "original payment method",
"label_sent_to": "sarah@example.com",
"estimated_refund": "3-5 business days"
}
```
The agent tells the customer the return is processed and a label has been sent — your backend handled all the coordination.
## Building Your Endpoint
### Request Format
When the agent calls your tool, Simple AI sends a POST request with:
* **Headers** — whatever you configured on the HTTP tool (typically an auth token)
* **Body** — JSON with the parameters the agent filled in based on the conversation
### Response Format
Return a JSON object with the data the agent should communicate. Keep it concise — only include fields the agent will actually say out loud or use to make decisions.
### Error Handling
If your endpoint returns an error (4xx, 5xx) or times out, the agent handles it gracefully and continues the conversation. Return clear error messages when possible so the agent can tell the caller what went wrong:
```json theme={null}
{
"error": true,
"message": "Order ORD-789 is not eligible for return. The return window has expired."
}
```
## Best Practices
* **Keep responses concise** — the agent reads the response and communicates it to the caller. Large payloads with irrelevant fields slow everything down.
* **Use descriptive tool descriptions** — the agent decides when to call your tool based on its description. Be specific about the trigger conditions.
* **Handle errors clearly** — return human-readable error messages so the agent can explain the problem to the caller instead of saying "something went wrong."
* **Secure your endpoints** — use authentication headers and validate them on your server. Your endpoint will receive call data that may include sensitive information.
* **Log requests** — log incoming requests and outgoing responses so you can debug issues and audit what happened during calls.
* **Scope to the right nodes** — in [flow-based agents](/agents), assign your custom code tools only to the nodes where they are relevant.
# Connect AI Agents to REST APIs with HTTP Tools
Source: https://docs.usesimple.ai/integrations/http
Configure HTTP tools that let Simple AI agents authenticate with REST APIs, send structured requests, and use responses during conversations.
HTTP integrations let you connect your agent to any external service that has a REST API. You define the request details, and the agent calls the API during conversations when it needs to look up data or take an action.
## How It Works
When you create a custom HTTP tool, you configure:
* **URL** — the API endpoint your agent will call
* **Method** — GET, POST, PUT, PATCH, or DELETE
* **Headers** — authentication tokens, API keys, or custom headers
* **Body** — the JSON request body, with parameters the agent fills in
* **Description** — a plain-language explanation of when the agent should use this tool
* **Parameters** — the inputs the agent needs to provide (e.g., order ID, customer email)
During a call, the agent reads the description to decide when to use the tool, fills in the parameters based on the conversation, sends the request, and uses the response to continue the conversation.
## Setting Up an HTTP Tool
1. Go to the **Tools** tab on your agent in the dashboard
2. Click **Add Tool**
3. Fill in the request details:
### Example: Order Status Lookup
| Field | Value |
| - | - |
| **Name** | Look up order status |
| **Description** | Use this tool to check the status of a customer's order. Use it when the customer asks about their order or provides an order number. |
| **Method** | POST |
| **URL** | `https://api.yourcompany.com/orders/lookup` |
| **Headers** | `Authorization: Bearer your-api-key` |
| **Body** | `{"order_id": "{{order_id}}"}` |
### Example: Schedule Appointment
| Field | Value |
| - | - |
| **Name** | Schedule appointment |
| **Description** | Use this tool to book an appointment for the customer. Use it after collecting the customer's preferred date and time. |
| **Method** | POST |
| **URL** | `https://api.yourcompany.com/appointments` |
| **Headers** | `Authorization: Bearer your-api-key` |
| **Body** | `{"customer_name": "{{name}}", "date": "{{date}}", "time": "{{time}}"}` |
### Example: CRM Contact Lookup
| Field | Value |
| - | - |
| **Name** | Look up contact |
| **Description** | Use this tool to look up the customer in the CRM by phone number. Use it at the start of the call to identify the caller. |
| **Method** | GET |
| **URL** | `https://api.yourcompany.com/contacts?phone={{phone_number}}` |
| **Headers** | `Authorization: Bearer your-api-key` |
## Authentication
HTTP Actions can send API keys, bearer tokens, Basic authentication credentials, and custom authentication headers using organization configuration variables. For OAuth flows, signed requests, and APIs that require mTLS, see [Action authentication](/authentication) for setup options and current limitations.
## Response Handling
When the API responds, the agent reads the response body and uses the information to continue the conversation naturally. For example, if the order status API returns:
```json theme={null}
{
"order_id": "12345",
"status": "shipped",
"tracking_number": "1Z999AA10123456784",
"estimated_delivery": "2026-04-15"
}
```
The agent would tell the caller something like: "Your order 12345 has been shipped. The tracking number is 1Z999AA10123456784 and the estimated delivery is April 15th."
If the API returns an error or times out, the agent handles it gracefully — it will let the caller know it was unable to retrieve the information and continue the conversation.
## Extraction Fields
You can define extraction fields on a tool to pull specific values out of the API response. This is useful when you want the agent to focus on particular pieces of data rather than interpreting the entire response.
## Testing Tools
You can test your HTTP tools directly from the dashboard before using them in live calls. This lets you verify that the request format, authentication, and response handling work correctly.
## Best Practices
* **Write clear descriptions** — the description is how the agent knows when to use the tool. Be specific about the trigger ("when the customer provides an order number") rather than vague ("for order-related queries").
* **Return concise responses** — only include the data the agent needs to communicate. Large payloads slow down the conversation.
* **Use HTTPS** — always use encrypted endpoints to protect data in transit.
* **Handle errors in your API** — return clear error messages so the agent can communicate what went wrong.
* **Scope tools to nodes** — in [flow-based agents](/agents), assign each tool only to the nodes where it is relevant. This prevents the agent from using a tool at the wrong stage.
# Connect Simple AI to Your Business Systems
Source: https://docs.usesimple.ai/integrations/overview
Connect Simple AI agents to CRMs, contact centers, helpdesks, REST APIs, custom code, workflows, and thousands of applications through Zapier.
Simple AI integrates with your existing systems so your agents can look up data, take actions, and stay in sync with your business workflows during conversations.
There are five ways to connect Simple AI to external services:
## Built-In Integrations
Simple AI has native integrations with popular platforms that require minimal setup — just connect your account and start using them.
[View built-in integrations](/integrations/built-in)
| Integration | What It Does |
| - | - |
| **Five9** | Sync call recordings, transfer conversations to Five9 agents |
| **Zendesk** | Import knowledge base articles from Zendesk Help Center |
| **Kustomer** | Import knowledge base articles from Kustomer |
| **Salesforce** | Sync call data and contact records with Salesforce CRM |
| **Microsoft Dynamics** | Sync call data and contact records with Dynamics 365 |
| **Shopify** | Look up orders, customers, and product data during calls |
| **Slack** | Post call summaries and escalation alerts to channels |
| **Microsoft Teams** | Call notifications, summaries, and telephony integration |
| **Zapier** | Connect Simple AI to thousands of apps with no-code workflows |
| **Attentive** | Track call outcome events and trigger SMS marketing flows |
## CCaaS Integrations
Run Simple AI alongside your existing contact center platform. Your platform keeps routing, queues, agent state, and reporting; Simple AI handles the conversations you route to it and hands them back for live agent escalation.
[View CCaaS integrations](/integrations/ccaas/overview)
| Platform | Notes |
| - | - |
| [**Five9**](/integrations/ccaas/five9) | SIP routing, call recording sync, live agent and chat transfer |
| [**Genesys**](/integrations/ccaas/genesys) | SIP routing and agent handoff via Architect flows |
| [**NiCE CXone**](/integrations/ccaas/nice-cxone) | SIP backchannel driven by Studio scripts, escalation via the Signal API |
| [**Talkdesk**](/integrations/ccaas/talkdesk) | BYOC SIP trunk with Studio routing |
| [**RingCentral**](/integrations/ccaas/ringcentral) | Cloud Connect or SIP peering |
| [**8x8**](/integrations/ccaas/8x8) | SIP trunk with 8x8 Contact Center routing |
| [**Amazon Connect**](/integrations/ccaas/amazon-connect) | Contact flow transfer or Chime SDK Voice Connector |
| [**Avaya**](/integrations/ccaas/avaya) | SIP trunk from Session Manager or Experience Portal |
| [**Cisco**](/integrations/ccaas/cisco) | SIP peering with CUCM route patterns and UCCX route points |
| [**Twilio Flex**](/integrations/ccaas/twilio-flex) | SIP trunk or TaskRouter-driven transfer |
| [**Dialpad**](/integrations/ccaas/dialpad) | SIP trunk with Dialpad Ai Contact Center routing |
| [**Zoom Contact Center**](/integrations/ccaas/zoom) | SIP trunk with Zoom flow routing |
Any other SIP-compatible platform connects the same way — see [SIP Integration](/sip-integration).
## Custom HTTP Integrations
Connect your agent to any single REST API endpoint. You define the URL, authentication, request format, and when the agent should call it. No backend code required.
[Set up HTTP integrations](/integrations/http)
Best for:
* Simple lookups against a single API (order status, contact details, inventory check)
* Services with straightforward REST APIs that return clean JSON
* Quick setup when you do not need custom logic between the request and response
## Custom Code
Run your own server-side logic as agent tools. Build middleware endpoints that combine multiple data sources, apply business rules, run validation, and handle complex orchestration — all behind a single tool the agent calls during conversations.
[Set up custom code integrations](/integrations/custom-code)
Best for:
* Combining data from multiple APIs into a single response
* Applying business rules, eligibility checks, or authorization logic
* Processing actions with side effects (initiating returns, creating tickets, sending emails)
* Any integration that needs custom logic between the agent and your backend systems
## Pre-Call and Post-Call Workflows
Run full workflows before and after calls. Pre-call workflows prepare the agent with caller context before the first word is spoken. Post-call workflows trigger multi-step journeys — CRM updates, follow-up messages, ticket creation, team notifications — based on what happened on the call.
[Set up workflows](/integrations/workflows)
Best for:
* Loading caller identity, account data, and history before a call starts
* Orchestrating multi-step follow-up processes after a call ends
* Routing and business rule logic that runs outside the conversation itself
# Build Pre-Call and Post-Call Automation Workflows
Source: https://docs.usesimple.ai/integrations/workflows
Build Simple AI workflows that enrich calls before they begin and automate follow-up actions after completion using webhooks and custom logic.
Simple AI lets you run workflows before and after calls — not just simple HTTP requests, but full sequences of actions that can look up data, enrich context, run business rules, trigger downstream systems, and orchestrate multi-step processes.
## Pre-Call Workflows
Pre-call workflows run **before** a call starts. They give you a window to prepare for the conversation — looking up the caller, loading their account data, checking for open issues, running routing logic, or anything else that helps the agent start the call with full context.
Think of it as a journey that runs before the first word is spoken.
### What You Can Do
* **Look up the caller** — query your CRM, database, or identity provider by phone number to identify who is calling
* **Load account context** — pull in account status, plan details, recent orders, open tickets, or any other data the agent needs
* **Run routing logic** — decide which agent or flow to use based on the caller's profile, time of day, or business rules
* **Check for open issues** — see if the caller has an active support case and pass that context into the call
* **Enrich from multiple sources** — call multiple APIs in sequence or in parallel to build a complete picture before the agent picks up
* **Apply business rules** — flag VIP customers, check eligibility, validate accounts, or set priority levels
### How It Works
1. A call comes in to a number with an assigned agent
2. Simple AI triggers your pre-call workflow
3. Your workflow runs its logic — database lookups, API calls, business rules, whatever you need
4. Your workflow responds with a `parameters` object
5. Simple AI merges those parameters into the call
6. The agent starts the conversation with full context already loaded
### Example
Your pre-call workflow receives the incoming call details:
```json theme={null}
{
"call": {
"from_number": "+15551234567",
"to_number": "+15559876543",
"direction": "inbound",
"timestamp": "2026-04-12T14:30:00Z"
},
"agent": {
"id": "agent-uuid",
"name": "Support Agent"
}
}
```
Your workflow looks up the caller across your systems and responds:
```json theme={null}
{
"parameters": {
"customer_name": "Sarah Johnson",
"account_id": "ACCT-789",
"plan": "Enterprise",
"open_tickets": 2,
"last_call_summary": "Called about billing discrepancy on March invoice",
"vip": true
}
}
```
Now the agent knows who is calling, can greet them by name, reference their plan, pick up where the last conversation left off, and treat them as a VIP — all before the caller says a word.
### Configuration
Configure pre-call workflows on an agent via the dashboard or the [Update Agent API](/api-reference/agents/update-agent):
* `pre_call_webhook_url` — your workflow endpoint
* `pre_call_webhook_auth_token` — optional bearer token for authentication
* `pre_call_webhook_timeout` — how long to wait for a response (default: 5 seconds)
* `pre_call_webhook_enabled` — set to `true` to activate
If your workflow times out or returns an error, the call proceeds normally without the extra parameters.
See [Call Flow](/call-flow) for the full call lifecycle and where pre-call workflows fit in.
## Post-Call Workflows
Post-call workflows run **after** a call completes. They let you trigger a full sequence of downstream actions — not just a single notification, but an entire journey of follow-up steps.
### What You Can Do
* **Sync to your CRM** — create or update contact records, log call activity, attach transcripts and recordings
* **Send follow-ups** — trigger follow-up emails, SMS messages, or scheduled callbacks based on what happened on the call
* **Update tickets** — create, update, or resolve support tickets based on the call outcome
* **Route to humans** — alert your team when a call needs manual follow-up, assign tasks, or escalate to a manager
* **Feed analytics** — push call data, analyzer results, and sentiment scores into your data warehouse or BI tools
* **Run multi-step workflows** — chain together any combination of the above. A single call completion can kick off a CRM update, a follow-up email, a Slack notification, and a task assignment all at once
### Available Events
Simple AI fires two events that can trigger post-call workflows:
* **call.completed** — fires when a call ends. Use this to kick off your post-call workflow.
* **call.transcript\_update** — fires during the call with transcript updates. Use this for real-time monitoring or mid-call actions.
See [Webhooks](/webhooks) for setup details and payload formats.
### Example: Post-Call Journey
A typical post-call workflow might look like:
```text theme={null}
Call completes
→ Log the call in Salesforce as an activity on the contact record
→ If the call was tagged "follow-up needed":
→ Create a task in your project management tool
→ Send a Slack message to the account owner
→ Schedule a follow-up call in 48 hours
→ If the analyzer detected a qualified lead:
→ Update the lead stage in your CRM
→ Notify the sales team
→ Push the call transcript and sentiment data to your data warehouse
```
You can build this kind of orchestration using [Zapier](/integrations/built-in#zapier), [custom code](/integrations/custom-code) on your own server, or any workflow automation tool that accepts webhooks.
## Best Practices
* **Keep pre-call workflows fast** — the default timeout is 5 seconds. If you need to do heavy processing, return the essentials quickly and handle the rest asynchronously.
* **Return only what the agent needs** — do not send your entire customer record. Pick the fields the agent will actually use in the conversation.
* **Make post-call handlers idempotent** — due to retries, your server may receive the same event more than once. Processing the same event twice should be safe.
* **Log everything** — log incoming requests and outgoing responses so you can debug issues in your workflows.
* **Use authentication** — set an auth token on your pre-call workflow and validate it on your server to prevent unauthorized requests.
* **Think in journeys** — pre-call and post-call workflows are at their most powerful when you treat them as full orchestration points, not just single API calls. Chain together the actions that make sense for your business.
# Connect Simple AI to Business Apps with Zapier
Source: https://docs.usesimple.ai/integrations/zapier
Use Zapier to connect Simple AI call events and agent actions with thousands of business applications through no-code automated workflows.
Zapier lets you connect Simple AI to thousands of apps without writing any code. When something happens on a call, Simple AI can kick off a Zap that updates your CRM, sends a message, creates a task, or triggers any other app in the Zapier ecosystem.
This page covers how the Simple AI Zapier integration works, the triggers and actions it exposes, and how to build your first Zap.
## How It Works
A Zap is an automated workflow made up of a **trigger** and one or more **actions**:
* A **trigger** is the event that starts the Zap. Simple AI provides triggers that fire on call events — for example, when a call completes or a tag is applied.
* An **action** is what the Zap does in response. Actions can run in any connected app (create a HubSpot contact, send a Slack message, add a Google Sheets row) or in Simple AI itself.
When a trigger event happens, Simple AI sends the event data to Zapier, which then runs your configured actions. This lets you connect Simple AI to tools that do not have a native integration.
## Triggers
Simple AI triggers start a Zap when a call event occurs. Each trigger passes the relevant call data into the Zap so downstream actions can use it.
| Trigger | Fires when |
| - | - |
| **Call Completed** | A call finishes, with the transcript, duration, outcome, and analyzer results |
| **Tag Applied** | A tag is applied to a call (manually or by an analyzer) |
| **Analyzer Result** | An analyzer extracts a specific result from a call |
| **Campaign Event** | A campaign starts, completes, or a campaign call reaches an outcome |
Trigger payloads include call metadata such as the from/to numbers, agent, duration, transcript, tags, sentiment, and any structured data extracted by your analyzers.
## Actions
Simple AI actions let a Zap take action in Simple AI in response to an event from another app.
| Action | What it does |
| - | - |
| **Create Call** | Start an outbound call from Simple AI |
| **Add Tag** | Apply a tag to an existing call |
| **Create Note** | Add a note to a call record |
You can combine Simple AI triggers and actions with any of the thousands of apps Zapier supports.
## Common Zap Examples
* Call completes → create a HubSpot contact with the call details
* Negative sentiment detected → send an email to the support manager
* Analyzer extracts a qualified lead → add a row to Google Sheets
* Call tagged "follow-up needed" → create a task in Asana or Monday.com
* New row added to a spreadsheet → start an outbound call with Simple AI
## Setup
The Simple AI Zapier integration is enabled per account. Contact your account team at [support@usesimple.ai](mailto:support@usesimple.ai) to have it connected before building Zaps.
1. Contact your account team at [support@usesimple.ai](mailto:support@usesimple.ai) to set up the Zapier integration for your organization.
2. In the Zapier dashboard, create a new Zap and search for **Simple AI**.
3. Choose a Simple AI trigger (for example, **Call Completed**) or action.
4. Connect your Simple AI account when prompted. You will authenticate using an API key from **Settings > API Keys** in the Simple AI dashboard.
5. Configure the trigger and map the call data into your downstream actions.
6. Turn the Zap on.
Once connected, you can build as many Zaps as you need using Simple AI triggers and actions.
## Related
* [Integrations Overview](/integrations/overview) — all the ways to connect Simple AI
* [Custom Workflows and Webhooks](/integrations/workflows) — run pre-call and post-call workflows
* [Webhooks](/webhooks) — receive call events directly at your own endpoint
# Simple AI Documentation for Voice, Chat, and SMS
Source: https://docs.usesimple.ai/introduction
Learn how to build, deploy, and manage Simple AI agents for phone calls, SMS, and web chat, then connect them to your business systems.
## What is Simple AI?
Simple AI is a voice AI platform that lets you build, deploy, and manage AI-powered phone agents. Your agents can make outbound calls, answer inbound calls, send and receive SMS, and be embedded directly in your website — all from a single platform.
## What You Can Build
* **Outbound calling agents** that reach contacts with personalized conversations
* **Inbound support agents** that answer calls on your phone numbers 24/7
* **SMS agents** that hold text conversations alongside voice
* **Web chat agents** embedded in your website via the [web widget](/web-widget)
* **Outbound campaigns** that contact lists of recipients at scale with retry logic and scheduling
## How It Works
1. **Create an agent** — define the personality, prompt, tools, and knowledge your agent needs. Use a simple prompt for quick setups, or build a multi-step flow for complex scenarios. See [Agents](/agents).
2. **Connect a phone number** — buy a number through Simple AI or bring your own via [SIP integration](/sip-integration). See [Phone Numbers](/numbers).
3. **Make calls** — trigger outbound calls through the [API](/api-reference/calls/create-call), the dashboard, or [campaigns](/campaigns). Attach agents to numbers for inbound calls.
4. **Analyze results** — use [analyzers](/analyzers), [tags](/tags), and [sentiment analysis](/call-sentiment) to extract structured insights from every conversation.
5. **Integrate with your systems** — connect [tools](/tools) so agents can look up data, update records, and take actions during calls. Use [webhooks](/webhooks) to push real-time call events to your application.
## Key Concepts
| Concept | What It Does |
| - | - |
| [Agents](/agents) | Define how your AI behaves — prompt, voice, tools, and conversation flow |
| [Calls](/calls) | Inbound and outbound voice conversations handled by your agents |
| [Phone Numbers](/numbers) | Numbers your agents use to make and receive calls |
| [Campaigns](/campaigns) | Batch outbound calling with scheduling, retries, and goal tracking |
| [Tools](/tools) | API connections that let agents interact with external services during calls |
| [Knowledge Base](/knowledgebase) | Reference material your agents can access during conversations |
| [Analyzers](/analyzers) | Post-call analysis that extracts structured data from transcripts |
| [Tags](/tags) | Labels for organizing and filtering calls by outcome or topic |
| [Webhooks](/webhooks) | Real-time event notifications pushed to your application |
## Quick Start
The fastest way to get started is to create a call with an inline agent using the API:
```bash theme={null}
curl -X POST "https://api.usesimple.ai/api/v1/calls" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to_number": "+15551234567",
"prompt": "You are a friendly assistant calling to confirm an appointment."
}'
```
Simple AI picks one of your phone numbers to call from and responds as soon as the call is queued:
```json theme={null}
{
"success": true,
"status": "enqueued",
"uuid": "346fe020-e8c2-4abe-9d40-a4992b016bd6"
}
```
Use the `uuid` to [get the call](/api-reference/calls/get-call) and read its status, transcript, and recording. To call from a specific number, pass its ID as `from_number_id`.
You can create and manage API keys in the Simple AI dashboard under **Settings > API Keys** (`/organization/settings/api-keys`).
For more control, create a reusable agent first and then reference it by ID when making calls. See the [API reference](/api-reference/introduction) for the full set of endpoints.
## Getting Help
* **Email**: [support@usesimple.ai](mailto:support@usesimple.ai)
* **Enterprise**: [Contact us](https://form.typeform.com/to/uldus6IZ) for custom plans, SIP integration, and dedicated support
# Build Automated Customer Outreach Journeys
Source: https://docs.usesimple.ai/journeys
Design multi-step Simple AI outreach journeys, launch them for contact lists, branch on results, and monitor each recipient's progress through the flow.
Journeys let you design a multi-step outreach flow once and run it against any group of recipients. A journey is a visual flow — calls, texts, delays, and branching logic — that you build on a canvas and then execute against an imported list of contacts.
## Journeys vs. Runs
A **journey** is a reusable template: the flow you design on the canvas, plus the list of data fields each recipient needs.
A **run** is a single execution of that journey against a specific list of recipients. One journey can have many runs — a morning wave, an afternoon wave, a retry batch, a new market — each with its own recipients, start time, and status.
Everything you configure in the canvas is shared across runs. Everything that's specific to "who are we contacting and when" lives on the run.
## Inside a Journey
Each journey has three tabs:
| Tab | What It's For |
| - | - |
| **Overview** | Live metrics across the journey — calls placed, answer rate, average talk time |
| **Workflow** | The visual canvas where you build and edit the flow |
| **Runs** | The list of runs for this journey, plus the recipients and status for the selected run |
You move between these tabs while the journey is running. The canvas always reflects the current saved version of the flow; the Runs tab always reflects live execution.
## What You Can Do
* Design a flow on a drag-and-drop canvas using calls, texts, delays, and branches
* Define the data each recipient needs (customer name, account id, plan, etc.) as typed journey data fields
* Import a CSV of recipients and map each column to a journey data field
* Run the journey now, schedule it for later, or prepare it as a draft and launch when ready
* Branch on call outcomes (answered, voicemail, no answer), on recipient state, or on your own journey data
* Pause, resume, or cancel a run at any time
* Review the recipients in a run, see which node each one is on, and inspect what's happened so far
* Export the results as a CSV when you're ready to report on them
## How a Recipient Moves Through a Journey
Each recipient starts at the Start node and moves forward through the flow one step at a time.
* When they hit an **action** (a call or an SMS), the action is dispatched and the recipient waits for the result.
* When they hit a **delay**, they're parked until the wait completes.
* When they hit a **branch**, the conditions are evaluated and the recipient is routed down the matching path.
* When there's no next step, the recipient is finished.
Different recipients in the same run can be at different places in the flow at the same time. That's normal — the flow is the map, each recipient follows their own path.
## Getting Started
The typical first build looks like this:
1. Create a journey and give it a name.
2. Open the **Workflow** tab and drop a Call node after the Start node. Configure it with a phone number and an agent.
3. (Optional) Add a Time Delay and a second Call node for a follow-up attempt.
4. Save the workflow.
5. Open the **Runs** tab and create a new draft run.
6. Import a CSV of recipients into the draft.
7. Start the run now, or schedule it for later.
Each of these pieces has its own page:
* [Building a Journey](/journeys/building-a-journey) — designing the flow on the canvas
* [Start Blocks](/journeys/start-blocks) — how journeys get kicked off
* [Node Types](/journeys/node-types) — reference for every node you can drop onto the canvas
* [Journey Data](/journeys/journey-data) — defining per-recipient fields and using them in your flow
* [Runs](/journeys/runs) — creating, scheduling, and monitoring runs
# Build an Automated Outreach Journey
Source: https://docs.usesimple.ai/journeys/building-a-journey
Use the Simple AI visual canvas to assemble an automated outreach journey from triggers, calls, messages, conditions, delays, and data actions.
The Workflow tab is where you design the flow. It's a drag-and-drop canvas — nodes are the steps, and arrows connect them in the order a recipient moves through the journey.
## The Canvas
On the left of the canvas you'll see a **palette** of nodes you can drop in, grouped into three categories:
* **Actions** — Call, SMS
* **Delays** — Time Delay, Wait Until
* **Branches** — Branch
Drag a node onto the canvas, then connect it from the previous node's output. Each recipient enters at the Start node and follows the arrows until the flow ends.
The **Start** node is already on the canvas when you open a journey. It marks where every recipient enters the flow and can't be removed.
## Configuring a Node
Click a node to open its config panel. Each node has its own settings — for a Call node you pick the phone number and the agent, for an SMS node you write the message, for a delay you choose the duration, and so on. See [Node Types](/journeys/node-types) for a full reference.
Most config fields accept either a **fixed value** or an **expression** that pulls from journey data or from an earlier node's output. That's what makes journeys personalized — the same flow produces different calls for different recipients because their journey data is different. See [Journey Data](/journeys/journey-data) for how this works.
## Connecting Nodes
Every node has outputs that connect to the next node. For most nodes there's just one output — the flow moves forward when the step is done. Branch nodes have one output per condition, plus a default, so you can route recipients down different paths.
A journey doesn't have to be linear. You can fan out with branches, merge paths back together, or end different paths at different points.
## Saving
When you're happy with your flow, save the workflow. Saving publishes the current design so that future runs (and any currently running recipients that haven't yet reached a changed node) use the latest version.
Saving does not start a run. A journey only runs when you create a run in the Runs tab and start it.
## Journey Data
Before you add Call or SMS nodes that depend on per-recipient information, it's worth defining your journey data fields — things like `customer_name`, `account_id`, `plan`. These become the columns you'll map to when you import a CSV, and the values you can reference inside node configs and branch conditions.
See [Journey Data](/journeys/journey-data) for the full walkthrough.
# Personalize Outreach with Journey Data
Source: https://docs.usesimple.ai/journeys/journey-data
Use per-recipient journey data to personalize Simple AI calls and messages, evaluate conditions, and carry customer context through each step.
Journey data is how each recipient brings their own information into the flow. You define the fields the journey needs, your CSV provides the values for each recipient, and your nodes reference those values to personalize calls, messages, and branching.
## Defining Fields
Every journey has a **journey data schema** — a list of fields each recipient is expected to have. Each field has:
* A **name** — short, alphanumeric, the handle you reference in nodes (for example, `customer_name`, `account_id`, `plan`)
* A **type** — one of `string`, `number`, `boolean`, or `date`
* A **required** flag — whether every recipient must have this field set
Keep the schema focused. Only add fields you actually plan to use somewhere in the flow — in a Call node's agent parameters, in an SMS message, or as the subject of a Branch condition.
## Providing Values Per Recipient
When you import a CSV into a run, each row is a recipient. The column `phone_number` is always required. Every other column is mapped to one of your journey data fields.
If a field is marked required in the schema, every row must have a value for it. Rows missing required fields will be flagged during import.
## Using Journey Data in Nodes
Most node config fields accept two kinds of values:
* **Fixed value** — a literal value you type in. Same for every recipient. Example: a specific phone number the call should come from, or a fixed SMS message.
* **Expression** — a reference that gets resolved per recipient. This is how you personalize.
Expressions can pull from:
* Journey data — e.g. the `customer_name` field for the current recipient
* An earlier node's output — e.g. the status of a previous Call node
* Recipient state — e.g. the recipient's current retry stage
When a recipient reaches a node, Simple AI evaluates any expressions against that recipient's data and uses the resolved values for the action.
## Using Journey Data in Branches
Branch conditions work the same way. A condition compares a field (like a journey data field or a call outcome) to a value, using one of the supported operators. When a recipient hits the branch, their current data is plugged in and the matching path is chosen.
See [Node Types](/journeys/node-types#branch) for the full list of operators and what you can branch on.
## Tips
* **Name fields the way you'd write them.** `customer_name` and `account_id` are easier to read in configs than `cn` or `aid`.
* **Keep types honest.** Use `number` for numeric fields you'll do comparisons on (retry stage, tier level), `date` for dates, `boolean` for true/false flags. It makes branch conditions easier to reason about.
* **Required fields prevent silent failures.** If the journey can't do its job without a field, mark it required so bad rows get caught at import time instead of at call time.
# Outreach Journey Node Type Reference
Source: https://docs.usesimple.ai/journeys/node-types
Explore every Simple AI journey node for calls, messages, branching, delays, data updates, and actions, including when to use each node type.
Journeys are built from a small set of node types. Each node does one thing — place a call, send a text, wait, or branch — and together they make up the flow.
## Summary
| Node | Category | What It Does |
| - | - | - |
| **Start** | Branches | The entry point. Every recipient begins here. |
| **Call** | Actions | Places an outbound call with one of your agents. |
| **SMS** | Actions | Sends a text message. |
| **Time Delay** | Delays | Pauses the flow for a fixed amount of time. |
| **Wait Until** | Delays | Pauses the flow until a specific date or until a recurring window opens. |
| **Branch** | Branches | Routes recipients down different paths based on conditions. |
## Start
The Start node is the entry point for every recipient. It's on the canvas by default and can't be deleted. You choose how the journey gets triggered on the Start node — see [Start Blocks](/journeys/start-blocks).
## Call
Places an outbound call with one of your agents.
Configure:
* **Phone number** — the number the call will come from
* **Agent** — which agent handles the call
The call uses whatever agent you select, with the recipient's journey data available to the agent during the conversation. The node completes when the call ends, and its result (status, who answered, tags) is available to any downstream Branch node.
## SMS
Sends a text message to the recipient.
Configure:
* **Phone number** — the number the message is sent from
* **Message** — the message body
Both fields accept fixed text or an expression that pulls from journey data, so you can personalize the message per recipient.
### The Conversation Doesn't End at the First Send
An SMS node sends one outbound message — but it doesn't have to stop there. If the sending number has an AI agent attached, that agent picks up the thread from the moment the message goes out.
When the recipient replies, the agent reads the reply and decides what to do: draft a response, use one of its tools to look something up, or hand the conversation off to a human teammate. A back-and-forth can play out across many messages, minutes, or hours, with the agent holding the thread on its own.
In other words, a single SMS step in a journey can kick off a real two-way conversation — not just a one-way blast. And because voice and SMS share the same contact history, an agent that later answers a call from the same recipient can reference whatever was just discussed over text.
See [SMS](/sms) for the full breakdown of how agent-driven SMS works.
## Time Delay
Pauses the recipient on this node for a fixed amount of time before moving on.
Configure:
* **Amount** — a whole number
* **Unit** — minutes, hours, or days
Use this when you want a predictable gap between steps — "wait 30 minutes after the first call before sending a follow-up SMS," for example.
## Wait Until
Pauses the recipient on this node until a specific moment, rather than for a fixed duration. There are two modes:
**Specific date and time.** Hold the recipient until a given date and time. You can also specify the timezone to interpret that time in.
**Recurring window.** Hold the recipient until the next time a recurring window opens — for example, "until 9:00 AM on the next weekday." You pick the days of the week (weekdays, weekends, or a specific day) and the time of day. You can also specify the timezone.
Use Wait Until when you want the next step to happen at a sensible hour in the recipient's timezone, rather than immediately after the previous step finished.
## Branch
Routes recipients down different paths based on conditions you define. Each branch is a condition; if a recipient matches the condition, they go down that branch. If no condition matches, they go down the default branch.
Configure a list of conditions, each of which compares a field to a value using an operator. Available operators:
| Operator | Meaning |
| - | - |
| `equals` | Field equals the value |
| `not_equals` | Field does not equal the value |
| `contains` | Field contains the value as a substring |
| `starts_with` | Field starts with the value |
| `ends_with` | Field ends with the value |
| `is_empty` | Field has no value |
| `is_not_empty` | Field has a value |
You can branch on:
* **Journey data** — any field you defined in the journey data schema (customer tier, account type, region, etc.)
* **Recipient state** — the recipient's current status or retry stage
* **Call outcome** — the status, who answered, retry stage, or tags from a Call node earlier in the flow
Typical uses:
* Route recipients differently based on whether a previous call was answered by a person, hit voicemail, or went unanswered
* Send a different follow-up path for VIP customers vs. standard customers
* Skip the follow-up for anyone already tagged as "opted out"
Every branch node also has a **default** path that catches recipients who don't match any condition, so nobody falls through the cracks.
# Launch and Monitor Outreach Journey Runs
Source: https://docs.usesimple.ai/journeys/runs
Launch Simple AI journey runs, monitor recipient progress and outcomes, inspect failures, and manage active executions from the dashboard.
A **run** is a single execution of a journey against a specific list of recipients. You manage runs from the **Runs** tab on a journey. This is where you upload your contacts, start or schedule a run, and watch it play out.
## The Runs Tab
The Runs tab has two panels:
* **Left panel** — the list of every run for this journey, with status, recipient count, and create/schedule time
* **Right panel** — the selected run's recipients, with their current status and progress through the flow
At the top of the tab there's a **New Run** button to create a run, and an **Export** button to download results.
## Creating and Starting a Run
A run always starts as a **draft**. A draft run exists but hasn't begun processing yet — it's the staging area where you load recipients.
The flow is:
1. Click **New Run** and give the run a name.
2. Use **Import** to upload a CSV of recipients into the draft. The CSV must include a `phone_number` column; every other column maps to a [journey data field](/journeys/journey-data).
3. Review the imported recipients in the right panel.
4. Either click **Start Run** to begin immediately, or click **Schedule** to pick a date, time, and timezone for the run to begin on its own.
You can delete a draft run at any time before it starts.
## Run Statuses
As a run moves through its lifecycle, it passes through these statuses:
| Status | Meaning |
| - | - |
| **pending** | Draft run. Recipients can still be imported. Not yet launched. |
| **queued** | Scheduled to start at a future time. |
| **running** | Actively processing recipients. |
| **paused** | Temporarily stopped. Recipients hold their place. Can be resumed. |
| **completed** | Every recipient has reached the end of their path. |
| **failed** | The run hit a terminal error. |
| **cancelled** | Stopped manually. Remaining recipients won't be processed. |
You can pause, resume, or cancel a run from the run's controls.
## Recipients
Each recipient in a run has their own status reflecting where they are in the journey — pending, in progress, completed, failed, and so on. The right panel shows each recipient's phone number, current status, which node they're on, and the next time they're scheduled to do something.
Different recipients in the same run can be at different nodes at the same time. Some might be mid-call, others parked on a delay, others already finished — that's the journey executing recipient-by-recipient.
## Scheduling
When you schedule a run instead of starting it immediately, you pick:
* The **date and time** the run should begin
* The **timezone** that date and time are in
The run sits in the queued state until that moment, then flips to running automatically. You can reschedule or cancel a queued run at any time before it fires.
## Pausing and Resuming
Pausing a run freezes everyone in place. No new calls or messages are sent, and no delays tick down while the run is paused. Recipients that were mid-action (on an active call, for example) complete that action, but they won't advance to the next node until the run is resumed.
Resuming a run picks up exactly where it left off.
## Exporting Results
When a run has generated enough activity to review, use the **Export** button at the top of the Runs tab to download the results as a CSV. Exports let you bring the data into your own reporting or operations tools.
## Multiple Runs on One Journey
Nothing stops you from having several runs active at the same time on the same journey. A typical pattern:
* A **daytime run** launched in the morning for one batch of recipients
* A **follow-up run** launched later in the week for recipients from the first run that didn't answer
* A **test run** with a handful of internal numbers to validate changes to the flow
Each run has its own recipients and its own status, so you can manage them independently.
# Choose How an Outreach Journey Starts
Source: https://docs.usesimple.ai/journeys/start-blocks
Configure the start block that triggers a Simple AI journey from an uploaded list, API request, schedule, event, or another supported source.
Every journey begins at the **Start** node. The Start node determines how a run gets kicked off — whether you trigger it yourself, schedule it for later, or let an external system start it automatically.
The Start node sits at the top of the canvas and can't be removed.
## Trigger Types
### Start Immediately
The simplest trigger. You create a run, import a list of recipients, and click **Start Run**. Simple AI begins processing recipients right away.
Use this when:
* Your list is ready to go and you want to start now
* You're testing a journey with a small batch
* You're running a one-off outreach that doesn't need to wait
### Schedule for Later
Create a run, import recipients, and then pick a future date, time, and timezone. Simple AI holds the run in a scheduled state and starts it automatically when the scheduled time arrives.
Use this when:
* You want outreach to begin at a specific time of day in a specific timezone
* You're prepping runs for a campaign that launches tomorrow morning
* You need to coordinate a run with a business event (sale, product launch, appointment window)
You can reschedule or cancel a scheduled run any time before it starts.
### Webhook
A Webhook start block turns the journey into something an external system can trigger. Instead of manually creating a run and importing a CSV, you send recipient data to a dedicated URL whenever you want someone to enter the journey.
The idea is the same as a webhook trigger on any automation platform: you get a URL, your system hits it with a recipient payload, and the journey picks up from there.
This is the path for integrating Simple AI journeys with your own systems — your CRM, your booking tool, your backend — so recipients flow in automatically as events happen in those systems.
Webhook triggers are on the roadmap. Full details (URL format, payload shape, authentication, response) will be published when this capability ships.
## Which Trigger to Pick
* **One-off outreach to a known list:** Start Immediately.
* **Scheduled outreach that should begin at a specific time:** Schedule for Later.
* **Ongoing, system-driven outreach where recipients enter as events happen:** Webhook (once available).
A journey has one Start node. When you need a different trigger for the same flow, you can duplicate the journey and change the Start configuration on the copy.
# Build a Public Knowledge Base and Help Center
Source: https://docs.usesimple.ai/knowledge-base-help-center
Create searchable Simple AI knowledge bases, publish customer-facing help articles, organize content, and launch a branded public help center.
Use Simple AI knowledge bases to store product information, policies, troubleshooting content, and support answers that both your agents and your customers can use.
The feature has two parts:
* A private knowledge base in the Simple AI dashboard where your team creates and manages content
* A public help center that exposes published articles on your website or through the public API
## What You Can Add
Simple AI knowledge bases support several content sources:
* Text articles that you write directly in the editor
* File-based articles created from uploaded documents
* Website articles created from a URL
* Imported external knowledge bases from Kustomer or Zendesk
Each article gets its own slug, summary, publish state, and optional folder placement.
## Typical Workflow
1. Create a knowledge base for a specific product area, team, or use case.
2. Add articles as text, files, websites, or imported external content.
3. Organize articles into folders or sections.
4. Edit article content in Markdown.
5. Publish the articles you want customers to see.
6. Create a help center for that knowledge base.
7. Customize the help center branding and publish it.
8. Embed the help center on your site or fetch it over the public API.
## Article Publishing Rules
Public visibility depends on two separate publish states:
* The help center itself must be published
* The individual article must also be published
If either one is unpublished, the article will not appear in the public help center or public search results.
## What Customers See
Once published, your help center can expose:
* A home view with sections and article lists
* Search across published articles
* Individual article pages by slug
* Branded presentation with logo, favicon, welcome title, and custom CSS
Article content is authored in Markdown and rendered as HTML for the public help center.
## URL and Deep-Link Behavior
Published articles use article slugs for public navigation.
If you use the Simple AI help center embed, the widget syncs the selected section or article into the browser URL as:
```text theme={null}
?hc_section={section-slug}
?hc_article={article-slug}
```
That makes section and article links shareable and lets readers return directly to a specific published destination.
## Search Behavior
Public help center search only returns published content from published help centers.
Search is designed for customer-facing self-service, so draft content stays out of public results until you publish it.
## Best Practices
* Create separate knowledge bases for distinct products, teams, or support surfaces
* Use article titles that match the questions customers actually ask
* Keep one article focused on one topic so search results stay clear
* Publish only customer-safe content in public help centers
* Use sections to group related articles such as billing, onboarding, troubleshooting, and account management
## Next Step
If you want to embed your published help center or call the public endpoints directly, see the companion guide in `knowledge-base-help-center-public-api.mdx`.
# Embed a Help Center with the Simple AI Public API
Source: https://docs.usesimple.ai/knowledge-base-help-center-public-api
Embed a Simple AI help center on your website and use the public API to retrieve published categories, articles, HTML content, and search results.
Use the public help center API when you want to render published knowledge base content outside the Simple AI dashboard.
This guide covers:
* Embedding the Simple AI help center on your website
* Fetching published help center configuration and article data over HTTP
* Deep-linking into specific articles
## Publish Requirements
The public endpoints only return content when:
* The help center is published
* The article is published
Draft help centers and draft articles are intentionally excluded from the public API.
## Embed the Help Center
Add the hosted script to your site and pass your help center token:
```html theme={null}
```
### Optional Script Attributes
* `data-target="#selector"`: Render into a specific element instead of appending to `body`
* `data-api-base="https://..."`: Override the API base URL, mainly for development environments
### Render Into a Specific Container
```html theme={null}
```
## Embed Behavior
The hosted help center embed:
* Loads the published help center config and article tree
* Supports article search when search is enabled
* Opens articles by slug
* Syncs the selected section into the browser URL as `?hc_section={slug}`
* Syncs the selected article into the browser URL as `?hc_article={slug}`
* Uses Shadow DOM so embedded styles stay isolated from the host page
## Public API Base
All public help center endpoints are rooted at:
```text theme={null}
https://api.usesimple.ai/api/public/help-center
```
## Get a Published Help Center
Fetch the published help center configuration, section tree, and uncategorized article list.
```bash theme={null}
curl https://api.usesimple.ai/api/public/help-center/YOUR_HELP_CENTER_TOKEN
```
### Response Shape
```json theme={null}
{
"config": {
"name": "Acme Help Center",
"slug": "acme-help-center",
"logo_url": "https://...",
"welcome_title": "How can we help?",
"search_enabled": true
},
"sections": [
{
"uuid": "section_uuid",
"name": "Billing",
"slug": "billing",
"description": "Billing and invoices",
"position": 0,
"children": [],
"articles": [
{
"uuid": "article_uuid",
"name": "How refunds work",
"slug": "how-refunds-work",
"description": "Refund policy overview",
"summary": "Refund timing, eligibility, and processing details.",
"position": 0
}
]
}
],
"uncategorized_articles": []
}
```
## Get a Published Article
Fetch a single published article by slug.
```bash theme={null}
curl https://api.usesimple.ai/api/public/help-center/YOUR_HELP_CENTER_TOKEN/article/how-refunds-work
```
### JSON Response
```json theme={null}
{
"uuid": "article_uuid",
"name": "How refunds work",
"slug": "how-refunds-work",
"description": "Refund policy overview",
"summary": "Refund timing, eligibility, and processing details.",
"content": "# Refunds\n\nYour original markdown...",
"html": "Refunds
Your rendered HTML...
",
"section": {
"uuid": "section_uuid",
"name": "Billing",
"slug": "billing"
},
"updated_at": "2026-04-11T12:34:56.000Z"
}
```
### Request Article HTML Directly
If you only need the rendered article body, request HTML output instead of JSON:
```bash theme={null}
curl "https://api.usesimple.ai/api/public/help-center/YOUR_HELP_CENTER_TOKEN/article/how-refunds-work?format=html"
```
## Search Published Articles
Search published articles in a published help center.
```bash theme={null}
curl "https://api.usesimple.ai/api/public/help-center/YOUR_HELP_CENTER_TOKEN/search?q=refund&limit=10"
```
### Query Parameters
* `q` required: Search query string
* `limit` optional: Number of results to return, from `1` to `50`
### Search Response
```json theme={null}
{
"results": [
{
"uuid": "article_uuid",
"name": "How refunds work",
"slug": "how-refunds-work",
"description": "Refund policy overview",
"summary": "Refund timing, eligibility, and processing details.",
"section": {
"uuid": "section_uuid",
"name": "Billing",
"slug": "billing"
},
"score": 12.34
}
],
"total": 1
}
```
## Common Implementation Notes
* Public article routes are slug-based, so article slugs should be treated as the stable public identifier
* Public search only returns published articles
* Public article fetches return both raw Markdown (`content`) and rendered HTML (`html`)
* The embed uses the same public API described above
## When To Use the Public API vs the Embed
Use the hosted embed when you want the fastest setup and the default Simple AI help center experience.
Use the public API when you want to:
* Render the help center inside your own frontend
* Fully control layout and branding in your application
* Combine help center content with other product surfaces
# Add a Knowledge Base to Your AI Voice Agent
Source: https://docs.usesimple.ai/knowledgebase
Create a Simple AI knowledge base from documents and connected sources so agents can answer questions with accurate business information.
Knowledge bases let you store reference material that your agents can access during calls. Instead of cramming everything into the prompt, you can upload documents, write articles, or import content from external sources — and the agent pulls in the relevant information when it needs it.
## What You Can Add
Simple AI knowledge bases support several content sources:
* **Text** — write articles directly in the editor with relevant information
* **Files** — upload documents (PDFs, text files, etc.) and Simple AI extracts the content automatically
* **Websites** — provide a URL and Simple AI pulls the page content
* **External imports** — import an existing knowledge base from Kustomer or Zendesk
## How Agents Use Knowledge Bases
When an agent is connected to a knowledge base, it can search and retrieve relevant content during a conversation. For example, if a customer asks about your return policy and that policy is in the knowledge base, the agent finds and references it in its response.
You can attach multiple knowledge bases to a single agent. This is useful when different types of information live in different places — for example, one knowledge base for product details and another for support policies.
## Creating a Knowledge Base
1. Go to the **Knowledge Base** section in the dashboard
2. Create a new knowledge base and give it a name
3. Add content — text, files, websites, or external imports
4. Attach the knowledge base to the agents that need it
When you upload files, Simple AI automatically extracts and indexes the content. You can also manually edit any of the extracted fields or add more information.
## Help Center
If you want to make your knowledge base content available to customers directly, you can publish it as a public help center. This gives you a searchable, branded support site powered by the same content your agents use.
See [Knowledge Base and Help Center](/knowledge-base-help-center) for how to set up and publish a help center, and the [Help Center Public API](/knowledge-base-help-center-public-api) for embedding it on your website.
## Best Practices
* **Separate by topic** — create different knowledge bases for different areas (billing, product, onboarding) so you can attach only the relevant ones to each agent
* **Be specific** — add concrete facts, policies, and procedures. Avoid vague descriptions of processes
* **Keep content current** — update your knowledge bases when policies or product details change. Agents always use the latest version
* **Test with calls** — after adding content, make a few test calls to verify the agent retrieves and communicates the information correctly
## Video Tutorial
# Buy and Configure Phone Numbers for AI Agents
Source: https://docs.usesimple.ai/numbers
Learn how to search for, purchase, configure, and release phone numbers, then assign Simple AI agent versions to handle inbound calls.
Phone numbers are how your agents connect to the phone network. You can buy numbers directly through Simple AI, or bring your own numbers via [SIP integration](/sip-integration).
## Buying a Number
You can search for and purchase phone numbers from the Simple AI dashboard or through the API.
When searching for numbers, you can filter by:
* **Country** — the country the number is based in
* **Area code** — a specific local area code
* **Capabilities** — voice, SMS, or both
Once purchased, the number is immediately available for use.
## Assigning an Agent
After buying a number, assign an agent to it to handle inbound calls. When someone calls that number, the assigned agent picks up and follows its configured prompt and flow.
You can change the assigned agent at any time from the dashboard or via the [Assign Agent API](/api-reference/numbers/assign-agent).
A number can also be used for outbound calls without assigning an agent — just reference the number as the `from_number` when [creating a call](/api-reference/calls/create-call).
## Inbound Calls
When an agent is assigned to a number:
1. A call comes in to the number
2. Simple AI routes it to the assigned agent
3. The agent handles the conversation according to its configuration
4. Optionally, a [pre-call webhook](/call-flow) runs before the agent picks up, allowing you to inject parameters
If no agent is assigned, inbound calls will not be answered.
## Outbound Calls
Any number you own can be used as the caller ID for outbound calls. Pass it as the `from_number` when creating a call through the API or when setting up a [campaign](/campaigns).
## Releasing a Number
If you no longer need a number, you can release it. Released numbers are removed from your account and can no longer be used for inbound or outbound calls.
## SIP Integration
If you already have phone numbers with your own carrier, you do not need to buy numbers through Simple AI. Instead, connect your existing numbers via SIP — see [SIP Integration](/sip-integration) for details.
## API Reference
* [List numbers](/api-reference/numbers/list-numbers)
* [Get number](/api-reference/numbers/get-number)
* [Update number](/api-reference/numbers/update-number)
* [Search available numbers](/api-reference/numbers/search-numbers)
* [Buy number](/api-reference/numbers/buy-number)
* [Release number](/api-reference/numbers/release-number)
* [Assign agent to number](/api-reference/numbers/assign-agent)
# How to Write Prompts for AI Voice Agents
Source: https://docs.usesimple.ai/prompting
Learn how to write clear, reliable prompts for Simple AI voice agents, including role definition, conversation rules, tool use, and call handling.
The prompt is the most important part of your agent configuration. It tells the agent who it is, what it should do, and how it should behave during calls. A well-written prompt leads to natural, reliable conversations.
## Prompt Structure
A good agent prompt typically includes these sections:
### Background
Tell the agent who it is and set the context for the conversation.
```text theme={null}
Your name is Sarah. You are a scheduling assistant calling patients
to confirm their upcoming appointments at Downtown Medical Center.
```
### Goal
Clearly state what the agent should accomplish on the call.
```text theme={null}
Confirm the patient's appointment for {{appointment_date}} at {{appointment_time}}.
If they need to reschedule, collect their preferred date and time.
```
### Instructions
Provide step-by-step guidance on how the conversation should flow.
```text theme={null}
1. Greet the patient and introduce yourself.
2. Confirm the appointment date and time.
3. If they confirm, thank them and end the call.
4. If they need to reschedule, ask for their preferred date and time.
5. Confirm the new time and end the call.
```
### Behavioral Notes
Add rules that shape how the agent speaks and handles edge cases.
```text theme={null}
- Speak conversationally. Do not list information — use natural sentences.
- Do not use markdown or formatting. You are on a phone call.
- If placed on hold, wait patiently. Do not hang up.
- If you reach voicemail, leave a brief message with the appointment details.
```
## Using Parameters
You can reference dynamic values in your prompt using `{{parameter_name}}` syntax. These values are passed in when the call is created — either through the API or from a [campaign](/campaigns) CSV upload.
```text theme={null}
You are calling {{customer_name}} about order {{order_id}}.
Their email on file is {{customer_email}}.
```
For more than simple substitution — conditional logic, branching, and randomized A/B variation — see [Dynamic Prompts](/dynamic-prompts).
## Voice-Specific Tips
Voice agents behave differently from text-based AI. Keep these in mind:
### Reading Numbers
Numbers need to be read digit by digit for clarity. Instruct the agent to read them slowly:
```text theme={null}
When reading a phone number, say each digit individually with a pause between groups.
For example, read 5551234567 as "five, five, five — one, two, three — four, five, six, seven."
```
For prices, instruct the agent to spell them out:
```text theme={null}
When reading a price, say it in full. For example, "$1,295.50" should be read as
"one thousand, two hundred and ninety-five dollars and fifty cents."
```
### Spelling Names and Emails
Names and email addresses are often misheard on a phone call. Instruct the agent to offer to spell them:
```text theme={null}
When providing a name, say it first, then offer to spell it.
Spell one letter at a time with a brief pause between each letter.
```
### Handling Hold and Transfers
Agents may be placed on hold or transferred. Make this explicit:
```text theme={null}
If placed on hold, wait for the other person to return. Do not hang up.
If transferred to another person, introduce yourself and continue the conversation.
```
### Avoiding Unnatural Output
Phone calls should sound like natural speech, not text output:
```text theme={null}
Do not use markdown, bullet points, or parenthetical notes in your responses.
Never say words in parentheses like "(pause)" or "(laughs)".
Speak in natural, conversational sentences.
```
## Full Prompt Example
Here is a complete example prompt that incorporates the patterns above:
```text theme={null}
## Background
Your name is James. You are speaking on the phone with a customer
who you just called.
## Goal
Tell the customer about their upcoming delivery for order {{order_id}}
and confirm the delivery address.
## Instructions
1. Greet the customer and introduce yourself.
2. Let them know you are calling about their order.
3. Confirm the delivery address: {{delivery_address}}.
4. If the address is correct, thank them and end the call.
5. If they need to update the address, collect the new address
and read it back to confirm.
## Behavioral Notes
- Speak conversationally. Do not list out details — form natural sentences.
- Do not use markdown or formatting in your responses.
- Use casual filler words like "great" and "awesome" to sound natural.
- Do not use filler words when reading numbers or addresses.
- If placed on hold, wait patiently. Do not hang up.
- If you reach voicemail, leave a brief message with the order number
and ask them to call back.
- Read numbers slowly, one digit at a time.
```
## Best Practices
* **Be specific** — vague prompts produce vague conversations. Tell the agent exactly what to do in each situation.
* **Test with real calls** — make a few test calls after writing your prompt to hear how it sounds in practice.
* **Handle edge cases** — think about what happens if the person does not answer, asks to be called back, or asks an unexpected question.
* **Keep it focused** — one prompt should cover one use case. If you need different behavior for different scenarios, use [flow-based agents](/agents) with separate prompts per node.
* **Iterate** — prompts rarely work perfectly the first time. Listen to recordings, spot issues, and refine.
# Define Simple AI Agents as TypeScript Code
Source: https://docs.usesimple.ai/sdk/agents-as-code
Define action-first Simple AI agents in TypeScript, keep agent configuration in source control, and synchronize changes through the SDK and CLI.
Install the SDK and CLI in a pnpm project:
```bash theme={null}
pnpm add @simple-ai-lab/sdk zod
pnpm add -D typescript
pnpm add -g @simple-ai-lab/cli
```
For a new Agent, run `simple init`. To synchronize an existing production branch, run:
```bash theme={null}
simple login
simple pull --branch main
```
`agent.ts` is the desired state. `simple.lock.json` records the remote Agent, selected branch and head commit, graph identities, and immutable Action heads. Commit both files.
Pull refuses to overwrite local changes. Reconcile them first, or pass `--force` only when you intentionally want production to replace the local source. If `--branch` is omitted, the CLI follows the product's most-recently-updated branch selection.
## Action-first authoring
Define integrations with `action.http`, `action.code`, `action.customDataQuery`, `action.customDataSearch`, or `action.knowledgeBaseSearch`. Attach an Action to a prompt with `action.callable`, or execute it deterministically with an Action node or `action.run`.
```typescript theme={null}
import { action, defineAgent, node, prompt } from '@simple-ai-lab/sdk';
import { z } from 'zod';
const lookup = action.http({
key: 'lookup-account',
name: 'Lookup account',
description: 'Fetch an account',
input: z.object({
account_id: z.string().describe('The account identifier from the caller'),
}),
output: z.object({ account_name: z.string() }),
outputMappings: { account_name: '{{ httpResponse.body.name }}' },
request: {
method: 'GET',
url: 'https://example.com/accounts/{{ inputs.account_id }}',
},
});
export default defineAgent({
key: 'support',
name: 'Support',
branch: 'main',
llm: { modelUuid: 'your-llm-model-uuid' },
nodes: [
node.prompt({
key: 'root',
name: 'Root',
root: true,
prompt: prompt.inline('Help the caller.'),
actions: [
action.callable({
key: 'lookup-action',
action: lookup,
name: 'lookup_account',
description: 'Look up an account',
inputs: {
account_id: action.fromAgent('The account identifier'),
},
// Action output name -> Agent parameter name
outputParameters: { account_name: 'customer_account_name' },
}),
],
}),
],
});
```
New authoring never emits legacy tool assignments or HTTP graph nodes. A pull uses `defineAgentSnapshot(...)` when necessary to preserve every current Agent v2 node and legacy field losslessly; you can refactor that snapshot to the ergonomic API deliberately.
## Validate and publish
```bash theme={null}
simple typecheck
simple diff
simple publish --label "Improve account lookup"
```
Publish validates the complete project on the server before mutation, checks Agent and branch metadata plus the relevant Action heads, creates or revises changed Actions with retry-safe identities, and finally creates one Agent commit pinned to those exact versions. If remote state changed, publish stops and asks you to pull or upgrade instead of overwriting it.
`simple actions upgrade` rewrites a pulled `defineAgentSnapshot(...)` with the accepted remote Action values and version pins. For ergonomic `defineAgent(...)` source, update the Action definition to match the remote head first; the CLI refuses to make the lockfile disagree with source.
## Organization resources
Tags, analyzers, scorecards, and post-call workflows belong to the organization rather than to an Agent commit, and an Agent references them by UUID. The CLI creates and lists them directly:
```bash theme={null}
simple tags list
simple analyzers list
simple scorecards list
simple scorecards metrics list
simple post-call-workflows list
```
Each `list` prints ` ` and accepts `--json` for the raw payload, which is how you collect the UUIDs that `agent.ts`, a scorecard section, or a workflow filter needs.
Each resource is created from a JSON definition passed with `--file ` (`--file -` reads stdin), from command flags, or from both, with flags overriding the file. Definitions are camelCase, reference other resources by UUID, and are validated locally before anything is sent.
```bash theme={null}
simple tags create --name "Escalation" --description "Caller asked for a human" --color "#2563EB"
simple analyzers create --title "Sentiment" --prompt "Rate the caller sentiment" --output-enum positive,neutral,negative
simple scorecards metrics create --name "Greeted the caller" --criteria "The agent greets the caller by name"
```
`--output` covers `string`, `number`, and `boolean` analyzers and `--output-enum` covers enums; nested object and array outputs are authored in a definition file:
```json theme={null}
{
"title": "Resolution",
"prompt": "Summarize how the call was resolved",
"output": {
"type": "object",
"properties": [
{ "name": "resolved", "type": "boolean" },
{ "name": "reason", "type": "enum", "enum": ["fixed", "escalated", "abandoned"] }
]
},
"agentUuids": [""]
}
```
A scorecard, and each of its sections, can be restricted to calls carrying certain tags. `callTagUuids` is the shorthand for "any of these tags"; `callTagFilter` expresses the full rule in disjunctive normal form, where groups are ORed and the conditions inside a group are ANDed with `any_of`, `all_of`, or `none_of`:
```json theme={null}
{
"name": "Sales QA",
"agentUuids": [""],
"callTagFilter": [
[{ "operator": "all_of", "tagUuids": ["", ""] }]
],
"sections": [{ "name": "Opening", "metricUuids": [""] }]
}
```
A post-call workflow runs its actions in order against one Agent branch once a call ends, so it is always created from a file. Filter actions gate the remaining actions on an analyzer result, and action identities are generated unless the definition names them:
```json theme={null}
{
"name": "Email escalations to QA",
"enabled": true,
"actions": [
{ "type": "filter", "analyzerUuid": "", "resultPath": "escalated", "operator": "is_true" },
{ "type": "email", "to": "qa@example.com", "subject": "Escalated call", "body": "Review this call." }
]
}
```
```bash theme={null}
simple post-call-workflows create --file workflow.json --agent --branch main
```
Inside a pulled project the branch recorded in `simple.lock.json` is used, so `--agent` and `--branch` are only needed elsewhere. An `agentBranchUuid` in the definition pins the branch outright and overrides both.
## CLI authentication
`simple login` opens PropelAuth in your browser and uses Authorization Code + PKCE. When your account belongs to more than one organization, the CLI asks which organization to use. It stores the rotating refresh token with user-only permissions and refreshes short-lived access tokens automatically.
The CLI is OAuth-only and uses the signed-in user's existing Agent and Action permissions. The published CLI includes Simple's public OAuth client configuration and registered localhost callback. No client secret is used by the CLI.
# Manage Custom Data with the Simple AI SDK
Source: https://docs.usesimple.ai/sdk/data
Use the Simple AI TypeScript SDK to query, read, create, update, and delete records in custom data tables from application code.
`client.data` wraps the [custom data REST API](/api-reference/custom-data/query). All operations are scoped to your organization.
## Query (search)
Facet-filtered search over an indexed table. Returns at most 50 records per call:
```typescript theme={null}
const { records, total } = await client.data.query({
tableName: 'customers',
filters: [{ column: 'state', values: ['CA', 'NY'] }],
text: 'ada lovelace', // optional free-text relevance search
size: 25,
from: 0,
});
```
Type the records for convenience (types are not validated at runtime):
```typescript theme={null}
type Customer = { email: string; plan: string };
const { records } = await client.data.query({ tableName: 'customers' });
```
## Records
```typescript theme={null}
// Paginated listing from the relational store
const page = await client.data.list('customers', {
page: 1,
perPage: 50,
orderBy: 'created_at',
orderDirection: 'desc',
filters: { state: 'CA' }, // column equality filters
});
const { record } = await client.data.get('customers', 42);
const created = await client.data.create('customers', { email: 'ada@example.com', plan: 'pro' });
const updated = await client.data.update('customers', 42, { plan: 'enterprise' });
await client.data.delete('customers', 42);
await client.data.deleteAll('customers'); // removes every record — irreversible
```
## Bulk Upserts
Large imports go through asynchronous batches of up to 1,000 rows:
```typescript theme={null}
const { ingestBatchId } = await client.data.batchUpsert('customers', rows);
// Poll until the batch is applied
const status = await client.data.getBatch(ingestBatchId);
// status.status: 'queued' | 'processing' | 'succeeded' | 'failed'
```
Batch rows are **full-row replace** on key conflict: a column omitted from a row is written as `NULL`, not preserved. Always send the complete record.
## Facets
```typescript theme={null}
const facets = await client.data.facets('customers');
```
Returns the configured facet values for the table, matching the [facets endpoint](/api-reference/custom-data/get-facets).
# Install and Use the Simple AI TypeScript SDK
Source: https://docs.usesimple.ai/sdk/quickstart
Install the Simple AI TypeScript SDK, authenticate with an API key, and start working with custom data, code actions, and agents as code.
The Simple AI SDK (`@simple-ai-lab/sdk`) is a typed TypeScript client for the Simple AI API. It works in two places:
* **Inside [code actions](/code-actions)** — bundled when you save and authenticated automatically; just import it.
* **In your own applications** — install it from npm and authenticate with an API key.
* **In an Agent repository** — define action-first agents and publish them with the `simple` CLI.
## Inside a Code Action
No installation or keys are needed. Simple bundles the SDK when you save the Action, and the handler's `client` is ready to use:
```typescript theme={null}
import { defineAction } from '@simple-ai-lab/sdk';
export default defineAction(async ({ inputs, client }) => {
const { records, total } = await client.data.query({
tableName: 'orders',
text: inputs.orderNumber,
size: 5,
});
return { total, orders: records };
});
```
## In Your Own Application
```bash theme={null}
npm install @simple-ai-lab/sdk
```
Create an API key in the dashboard under **Settings → API Keys** (`/organization/settings/api-keys`), then:
```typescript theme={null}
import { SimpleAI } from '@simple-ai-lab/sdk';
// Reads SIMPLE_API_KEY from the environment by default
const client = new SimpleAI();
// Or pass it explicitly
const explicit = new SimpleAI({ apiKey: process.env.MY_SIMPLE_KEY });
```
## Error Handling
Every non-2xx API response throws a `SimpleAIError` with the HTTP `status`, a machine-readable `code` when available, and the parsed response `body`:
```typescript theme={null}
import { SimpleAI, SimpleAIError } from '@simple-ai-lab/sdk';
const client = new SimpleAI();
try {
await client.data.get('customers', 42);
} catch (e) {
if (e instanceof SimpleAIError && e.status === 404) {
// record does not exist
} else {
throw e;
}
}
```
## What's Included Today
* **`client.data`** — full custom data access: [query, records, bulk upserts, and tables](/sdk/data)
* **`defineAction`** — typed code-action handlers, including [steering the conversation](/code-actions#steering-the-conversation) to another node
* **`defineAgent`** — typed Agent v2 graphs, settings, prompts, extraction schemas, and resource references
* **`action.*`** — HTTP, code, custom-data, and knowledge-base Actions
* **`simple` CLI** — [pull, typecheck, diff, and publish an Agent branch](/sdk/agents-as-code)
# Connect Simple AI to Contact Centers with SIP
Source: https://docs.usesimple.ai/sip-integration
Connect Simple AI to your carrier or contact center over SIP for inbound routing, outbound calling, transfers, and existing phone numbers.
If your organization already has a carrier, phone numbers, or a contact center platform, you can connect Simple AI directly to your existing infrastructure via SIP. Simple AI will send and receive calls through your systems instead of its default telephony — keeping your branded caller ID, compliance setup, and reporting in place.
## What You Can Do
* Route outbound AI calls through your carrier with your caller ID
* Forward inbound calls from your phone system or contact center to Simple AI
* Transfer calls between Simple AI and live agents on your platform
* Use your existing phone numbers without porting
## Compatible Platforms
Simple AI connects via standard SIP trunks. If your system supports SIP, it works.
### Contact Centers
| Platform | Details |
| - | - |
| [**NiCE CXone**](/integrations/ccaas/nice-cxone) | SIP backchannel via Studio scripts. Inbound routing, outbound campaigns via Personal Connection, live agent escalation via Signal API. |
| [**Cisco CUCM / UCCX**](/integrations/ccaas/cisco) | SIP trunk peering with route patterns and CTI route points. Call queuing and agent handoff. |
| [**Five9**](/integrations/ccaas/five9) | Conversation handoff and external call recording ingestion. |
| [**Genesys**](/integrations/ccaas/genesys) | SIP-based call routing and agent handoff. |
| [**Talkdesk**](/integrations/ccaas/talkdesk) | BYOC SIP trunk with Studio routing and agent handoff. |
| [**RingCentral**](/integrations/ccaas/ringcentral) | Cloud Connect or SIP peering with queue-based handoff. |
| [**8x8**](/integrations/ccaas/8x8) | SIP trunk with 8x8 Contact Center routing and agent handoff. |
| [**Amazon Connect**](/integrations/ccaas/amazon-connect) | Contact flow transfer, or SIP via a Chime SDK Voice Connector. |
| [**Avaya**](/integrations/ccaas/avaya) | SIP trunk from Session Manager or Experience Portal, with VDN and skill handoff. |
| [**Twilio Flex**](/integrations/ccaas/twilio-flex) | Elastic SIP Trunk or TaskRouter-driven transfer. |
| [**Dialpad**](/integrations/ccaas/dialpad) | SIP trunk with Dialpad Ai Contact Center routing. |
| [**Zoom Contact Center**](/integrations/ccaas/zoom) | SIP trunk with Zoom flow routing and agent handoff. |
Any other SIP-compatible CCaaS platform will work the same way. Per-platform setup details live in [CCaaS Integrations](/integrations/ccaas/overview).
### PBX Systems
Cisco CUCM, Asterisk / FreePBX, 3CX, Avaya, Microsoft Teams (via SBC), or any SIP-compliant PBX.
### Carriers
Any SIP trunk provider — Twilio, Vonage, Bandwidth, Telnyx, or your own. Simple AI is carrier-agnostic.
## Outbound Calls
Simple AI places calls through your SIP trunk. The AI agent is on the line from the moment the customer picks up.
1. Create a call via the API with a phone number that's on your SIP trunk
2. Simple AI sends the SIP INVITE to your carrier
3. Your carrier dials the customer with your branded number
4. Customer answers, conversation starts immediately
```bash theme={null}
curl -X POST https://api.usesimple.ai/api/v1/calls \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"agent_id": "your-agent-uuid",
"to_number": "+15551234567",
"from_number_id": "your-phone-number-uuid"
}'
```
Pass the ID of a phone number that's configured on your SIP trunk as `from_number_id`. Simple AI routes through the correct trunk automatically.
## Inbound Calls
Your carrier or contact center forwards calls to Simple AI's SIP endpoint. Simple AI picks up, runs the AI conversation, and can transfer to a live agent when needed.
```text theme={null}
Customer → Your Carrier → Contact Center (optional) → Simple AI
```
The contact center controls routing — it decides when a call goes to the AI vs. a human. Simple AI can hand the call back for live agent escalation at any point.
## Contact Center Flows
### Inbound Through a Contact Center
Your contact center receives the call, runs IVR and routing, then SIP-transfers to Simple AI when the AI should handle it. Simple AI can transfer back for live agent escalation.
### Outbound Campaigns
Your contact center's dialer handles pacing, DNC compliance, and answering machine detection. Connected calls get SIP-transferred to Simple AI for the conversation. Use this for bulk outreach where the contact center manages the campaign.
### Direct Outbound
For single calls (API-triggered, CRM callbacks), go direct through your carrier's SIP trunk. No contact center in the middle. This is the fastest path — zero transfer delay.
## Connection Details
| Setting | Value |
| - | - |
| **Transport** | TCP or TLS (TLS recommended) |
| **Audio Codecs** | G.711 u-law (PCMU), G.711 A-law (PCMA), Opus |
| **DTMF** | RFC 2833 |
| **Number Format** | E.164 |
| **Auth** | IP-based allowlisting or SIP digest (RFC 3261) |
| **Custom Headers** | `X-` prefixed headers for passing metadata (call IDs, CRM context) |
Contact your account team for your dedicated SIP endpoint and credentials.
## Setup
1. **Share your SIP details** — carrier endpoint, auth credentials or IPs, and phone numbers (E.164)
2. **We configure the trunk** — connection, auth, number routing, codec preferences
3. **You configure your carrier** — allowlist our IPs (outbound) or point your routing to our endpoint (inbound)
4. **Test** — place a test call in each direction to verify caller ID, audio, and transfers
Your account team handles the setup. Reach out at [support@usesimple.ai](mailto:support@usesimple.ai) to get started.
## FAQ
**Do I need to port my numbers?** No. Your numbers stay with your carrier. Simple AI routes calls through your SIP trunk.
**Can I mix SIP and default telephony?** Yes. Numbers on a SIP trunk route through your carrier. Everything else uses Simple AI's default telephony.
**Is there additional cost?** SIP integration is available on Enterprise plans. Contact your account team.
**Does call recording still work?** Yes. Recording works the same regardless of how the call is routed.
**Can the AI transfer to a live agent?** Yes — blind transfer or attended transfer to any SIP destination (agents, queues, IVR, external numbers).
# Send and Receive SMS with Simple AI Agents
Source: https://docs.usesimple.ai/sms
Learn how Simple AI agents, human teammates, and automated journeys send and receive SMS messages while preserving conversation context.
Simple AI supports SMS alongside voice, so your agents and your team can reach contacts over text as well as phone calls. SMS in Simple AI is built to work the same way voice does, and the two channels can share context on the same contact.
## What You Can Do with SMS
SMS in Simple AI supports four main use cases:
* an AI agent can hold a full SMS conversation on its own
* a human teammate can send and receive SMS directly from the dashboard
* SMS steps can be used inside campaign journeys
* context is shared between phone calls and SMS on the same contact
You can use any combination of these. A conversation that starts as an inbound call can continue over SMS, a journey can mix call steps and SMS steps, and a human can take over an AI agent's SMS thread at any time.
## AI Agent SMS Conversations
You can configure an agent to handle SMS the same way you configure it to handle voice calls. When a contact texts a number attached to an SMS-enabled agent, the agent can read the incoming message, decide how to respond, and send replies.
Agents support two modes of sending SMS:
* **AI-generated replies** — the agent drafts and sends responses on its own, based on its prompt, tools, and knowledge base
* **User-written replies** — a human teammate writes the outgoing message, while the conversation still lives in the same thread the agent has access to
Both modes can be used in the same conversation. A common pattern is to let the agent handle most of the thread and have a human step in for specific messages, then hand the conversation back to the agent.
Agents can also use tools during an SMS conversation, so the same tools you configure for voice calls — looking up a customer, scheduling an appointment, updating a CRM — are available over text.
## Human-Sent SMS
Your team can send and receive SMS directly from the Simple AI dashboard without involving an agent at all.
Human SMS is useful when:
* a teammate needs to follow up on a call manually
* a conversation needs a human touch that should not be automated
* you want to send a one-off message to a contact
Human and AI messages show up in the same conversation thread, so there is a single history per contact regardless of who sent each message.
For replies your team sends often, see [Snippets](/snippets) — a shared library of pre-written messages a teammate can insert by typing a slash.
## SMS in Journeys
SMS can be used as a step inside a journey, alongside calls and other actions. See [Campaigns](/campaigns) for more on how journeys work.
## Shared Context Between Calls and SMS
SMS and voice calls are not isolated channels. When a contact has both a call history and an SMS history with your organization, Simple AI treats them as part of the same conversation with that contact.
In practice, this means:
* an agent answering a call can reference what was said in a recent SMS thread
* an agent responding to an SMS can reference what happened on an earlier call
* a human teammate sees calls and messages together in the contact's history
* a journey can pause on one channel and resume on another
This makes it possible to build flows that feel like a single conversation even when they span multiple channels and multiple days.
## Inbound SMS
You can attach an agent to a phone number to handle inbound SMS, similar to how you attach an agent to handle inbound calls. When a message comes in, the agent decides how to respond and the thread becomes part of the contact's history.
If no agent is attached, inbound SMS still lands in the dashboard so your team can respond manually.
## Outbound SMS
Outbound SMS can be sent from:
* the dashboard, by a human teammate
* an agent, as part of an ongoing conversation
* a campaign journey step
* the API, for programmatic sending
As with outbound calls, outbound SMS must follow all rules and regulations in your jurisdiction, including carrier requirements and opt-out handling.
## Language Support
SMS supports the same languages Simple AI supports for voice. If you need a language that is not currently supported, please contact us.
## Getting Started
SMS capabilities are enabled per account. If you do not see SMS options in your dashboard and would like to use them, reach out to [support@usesimple.ai](mailto:support@usesimple.ai) or your account team to have SMS enabled for your organization.
# Reply Faster with Saved SMS Snippets
Source: https://docs.usesimple.ai/snippets
Build a shared library of pre-written SMS replies and insert them into any conversation by typing a slash, so your team answers common questions consistently.
Snippets are short, pre-written text messages your team can drop into an SMS reply instead of typing the same thing again. If you have used canned responses or saved replies in another messaging tool, snippets work the same way.
Every snippet belongs to your organization, so anyone on your team sees the same library and your customers get the same wording no matter who answers.
## What Snippets Are For
Snippets suit any message your team sends over and over:
* appointment confirmations and reschedule options
* "running late" notices
* answers to common questions about pricing, financing, or hours
* opt-out acknowledgements
* follow-ups after a missed call
A snippet is plain text. Once it lands in the message box you can edit it like anything else you typed, so you can personalize a line before sending it.
## Insert a Snippet While Replying
Open an SMS conversation and type `/` in the reply box. The snippet list opens straight away.
Keep typing to narrow the list. Simple AI matches what you type against snippet names first, then against the message text, so `/conf` finds a snippet named "Appointment confirmed" as well as one whose body mentions confirming.
Press `Enter` and the snippet replaces what you typed. Your cursor lands at the end of the message, ready for you to edit or send.
### Keyboard Shortcuts
| Key | What It Does |
| - | - |
| `/` | Open the snippet list |
| Any letters | Filter the list |
| `↑` and `↓` | Move through the list |
| `Enter` or `Tab` | Insert the highlighted snippet |
| `Esc` | Close the list and keep what you typed |
| `Cmd`+`Enter` | Send the message, as usual |
The slash only opens the list at the start of a message or after a space, so ordinary text such as `https://example.com` or `24/7` is unaffected.
### The Snippets Button
If you would rather use the mouse, click **Snippets** next to the paperclip below the message box. It opens the same list and inserts the snippet wherever your cursor is.
Opening the list this way never changes your draft. If you close it without choosing a snippet, your message stays exactly as you left it.
## Where You Can Use Snippets
Snippets are available anywhere your team writes an SMS:
* replying to an existing SMS conversation
* starting a new SMS conversation
They work the same in the shared inbox and in the conversation view, for both teammates and supervisors.
Snippets are currently available for SMS conversations. They do not appear in chat or email composers.
## Create and Manage Snippets
Go to **Configuration → Snippets** to see your organization's library. Snippets are listed alphabetically, which is the same order your team sees when inserting one.
Click **New snippet** to add one. A snippet needs two things:
* **Name** — what your team types after the slash to find it. Short, specific names work best.
* **Body** — the message that gets inserted.
The counter under the message shows how long the snippet is. Snippet names must be unique within your organization, so you cannot end up with two entries called "Appointment confirmed".
To change a snippet, use the pencil icon on its row. To remove one, use the trash icon and confirm. Deleting a snippet only stops your team from inserting it in future; messages you have already sent are never changed.
Teammates pick up your changes without reloading the dashboard, so a conversation that is already open shows the updated library shortly after you save.
## Using Placeholders
Snippets are inserted exactly as written. Simple AI does not fill in customer details automatically, so a common practice is to mark the parts that need editing with square brackets:
```
Hi! Your appointment is confirmed for [date] at [time].
Reply here if anything changes.
```
Your teammate replaces the bracketed parts before sending. Brackets are just a convention that makes the gaps easy to spot; you can use any marker your team prefers.
## Limits
| Limit | Value |
| - | - |
| Snippet name | 80 characters |
| Snippet message | 1,600 characters |
| Snippets per organization | 200 |
The message limit matches the maximum length of a single SMS, so any snippet you save fits in one message on its own.
## Who Can Use and Manage Snippets
* **Anyone who can open an inbox and reply to an SMS** can insert snippets. No extra setup is needed.
* **Teammates who can edit your organization's configuration** can create, edit, and delete them. This is the same access that covers settings such as business hours and agent teams.
Every change to a snippet is recorded in your organization's audit log with who made it and what changed, so you can see how the library has evolved.
## Tips for Writing Good Snippets
* **Name them the way you would search for them.** "Reschedule options" is easier to recall mid-conversation than "Template 4".
* **Keep each snippet to one job.** Several short snippets beat one long message your team has to trim every time.
* **Write them to be edited.** Leave the details that change in brackets rather than guessing at a default.
* **Review them periodically.** Prices, hours, and offers move; the library should move with them.
* **Start small.** Six to ten snippets covering your most common replies deliver most of the benefit.
## Getting Started
If SMS is enabled for your organization, open **Configuration → Snippets** and add your first one. As soon as it is saved, your team can reach it by typing `/` in any SMS reply.
If you do not see SMS options in your dashboard and would like to use them, reach out to [support@usesimple.ai](mailto:support@usesimple.ai) or your account team.
# Organize and Filter AI Phone Calls with Tags
Source: https://docs.usesimple.ai/tags
Create Simple AI call tags, apply them automatically from conversation outcomes, and use them to organize, filter, and report on calls.
Tags let you organize calls in a way that matches your workflow. You can use them to label calls by outcome, topic, team, or follow-up status so they are easier to review and filter later.
## What Tags Do
You can use tags to:
* label calls with meaningful business context
* group similar calls together
* support filtering and reporting in your own workflows
* make handoffs and follow-up easier for your team
Common examples include:
* `qualified lead`
* `follow up needed`
* `billing`
* `support`
* `vip customer`
## How to Add Tags
Before you can attach a tag to a call, you first create the tag for your organization.
When you create a tag, you define:
* a name
* a description
* a color
* an agent
This is useful when you want to create a consistent set of labels that your team can reuse across many calls.
For example, you might create tags such as `sales`, `refund-request`, or `priority-follow-up`.
## How to Apply Tags to a Call
After a tag has been created, Simple AI will automatically apply relevant tags to calls based on the agents where the tag is enabled. After a call is completed, you also have the option to manually add or remove tags.
This allows you to label calls after they happen so that you can sort, review, and analyze them more easily.
## Rerun Tags on Previous Calls
If you create a new tag or update how a tag should be applied, you can rerun tag application on previous calls.
This is useful when:
* you added a new tag and want to apply it to older calls
* you changed your tagging rules and want past calls to reflect the updated logic
* you want to backfill tags for reporting or review workflows
When you rerun a tag, Simple AI reprocesses eligible calls from the date range you select.
## How Reruns Work
Reruns are supported for previous calls and can be applied across a date range.
Depending on how the tag is configured, the rerun may apply to:
* all calls in the selected date range
* calls associated with the agents where that tag is enabled
If you are using reruns, it is a good idea to choose a clear date range so you only reprocess the calls you actually want to update.
## When to Use Tags
Tags are helpful when you want to track outcomes or workflows that matter to your team.
Examples include:
* marking calls that need a callback
* separating support conversations from sales conversations
* identifying calls that should be reviewed internally
* tracking campaign or workflow outcomes
## Best Practices
To keep tags useful over time, it helps to use a small set of consistent labels instead of creating many overlapping versions of the same tag.
For example, choose one naming style and use it consistently:
* `follow-up-needed` instead of mixing `follow up`, `FollowUp`, and `callback`
Clear descriptions also make it easier for teammates to understand when each tag should be used.
Adding specific descriptions about when a tag should be applied also helps Simple AI place tags on the right calls.
For example, instead of describing a tag as `Billing`, you might describe it as `Apply this tag when the caller is asking about charges, invoices, refunds, or payment issues`.
## Video Demo
### Tag Creation
### Manual Tagging and Filtering
## API Reference
The following endpoints are available for working with tags:
* [List tags](/api-reference/call-tags/list-tags)
* [Create tag](/api-reference/call-tags/create-tag)
* [Update call tags](/api-reference/call-tags/update-call-tags)
# Connect AI Voice Agents to APIs and Tools
Source: https://docs.usesimple.ai/tools
Give Simple AI agents tools to retrieve information and take actions during conversations through built-in capabilities and external APIs.
Tools let your agents interact with external services and perform actions during a conversation. When an agent has tools configured, it can decide when to call them based on what is happening in the call.
## How Tools Work
Each tool is an HTTP request that the agent can trigger mid-conversation. When you create a tool, you define:
* **URL** — the endpoint the agent will call
* **Method** — GET, POST, PUT, PATCH, or DELETE
* **Headers** — any authentication headers or custom headers the request needs
* **Body format** — the JSON body template for the request
* **Description** — a plain-language explanation of when and why the agent should use this tool
* **Parameters** — the inputs the agent needs to provide when calling the tool
The description is important. It tells the agent when to invoke the tool, so write it clearly. For example: "Use this tool to look up the status of an order when the customer provides an order number."
## Example: Order Status Lookup
A support agent needs to check order status during calls. You would create a tool with:
* **URL**: `https://api.yourcompany.com/orders/lookup`
* **Method**: POST
* **Headers**: `Authorization: Bearer your-api-key`
* **Body**: `{"order_id": "{{order_id}}"}`
* **Description**: "Look up the status of a customer's order. Use this when the customer asks about their order or provides an order number."
During the call, when a customer says "Can you check on my order 12345?", the agent recognizes it should use this tool, sends the request, and reads the response back to the caller.
## Built-In Tools
In addition to custom API tools, agents have access to built-in capabilities:
### DTMF / IVR Navigation
The agent can press keys on the phone keypad. This is useful when your agent is calling into a system that has an IVR menu (press 1 for billing, press 2 for support, etc.).
### End Call
The agent can hang up and end the call when the conversation is complete or when instructed to do so.
### Call Transfer
The agent can transfer the call to another phone number or agent — either a blind transfer or an attended transfer.
## Tool Responses
When a tool returns a response, the agent reads the response body and uses it to continue the conversation. Make sure your API returns clear, readable data — the agent will try to communicate the results naturally to the caller.
If a tool call fails (timeout, error response), the agent handles it gracefully and continues the conversation.
## Best Practices
* **Write clear descriptions** — the agent relies on the description to know when to use each tool. Vague descriptions lead to the tool being used at the wrong time or not at all.
* **Keep responses concise** — return only the data the agent needs to communicate to the caller. Large payloads slow down the response.
* **Use tools per node** — in [flow-based agents](/agents), assign tools only to the nodes where they are relevant. This prevents the agent from using a tool at the wrong stage of the conversation.
* **Test with real calls** — make a few test calls to verify the agent uses the tool at the right moment and communicates the response naturally.
## Creating Tools
You can create and edit tools from the **Tools** tab of an agent in the Simple AI dashboard.
# Embed the Simple AI Chat Widget on Your Website
Source: https://docs.usesimple.ai/web-widget
Add the Simple AI chat widget to your website, configure its appearance and behavior, and control conversations through the JavaScript API.
Use the web widget to add chat to your website and route messages to your configured widget agents.
## Install
```html theme={null}
```
You can find the widget token in Dashboard -> Widgets -> your widget -> Embed.
## Layout Modes
Set layout in Dashboard -> Widgets -> Appearance -> Layout, or pass `layout` in the script.
* `bubble`: Default floating launcher bubble.
* `tab`: Vertical side tab launcher.
* If `layout` is not provided, it defaults to `bubble`.
## Constructor Options
* `widgetToken` (required): Widget token from Dashboard.
* `layout` (optional): `bubble` or `tab` (default: `bubble`).
* `bottomSpacing` (optional): Distance in pixels from the page bottom (default: `28`).
### Bottom Spacing Example
```html theme={null}
```
## JavaScript API
After creating a widget instance, you can control it programmatically:
* `widget.init()`: Initialize the widget and load config from your widget token.
* `widget.open()`: Open chat UI (also makes a hidden widget visible).
* `widget.close()`: Close chat UI.
* `widget.toggle()`: Toggle between open and closed.
* `widget.show()`: Show widget UI if hidden.
* `widget.hide()`: Hide widget UI.
* `widget.identify(params)`: Identify a logged-in user and link chat activity to their contact.
* `widget.resetIdentity()`: Clear the stored identity (for example, on logout).
* `widget.destroy()`: Remove widget DOM and event listeners.
## Identify Logged-In Users
Use `identify()` when a visitor is signed into your product. Simple links widget conversations and activity to that contact so agents and teammates see the right person.
### Without identity verification
If identity verification is not enabled on your widget, you can identify users with just their ID and optional profile fields:
```html theme={null}
```
`identify()` can be called before or after `init()`. If you call it early, the widget queues the request and sends it once initialization finishes.
### Identify parameters
| Field | Required | Description |
| - | - | - |
| `id` | Yes | Your stable user ID (primary key). This is the value signed when using HMAC. |
| `email` | No | User email address. |
| `name` | No | Display name. |
| `phone` | No | Phone number (E.164 recommended). |
| `hmac` | When identity verification is enabled | Hex-encoded HMAC-SHA256 of `id` (see below). |
| `metadata` | No | Additional key/value attributes to store on the contact. |
### Conversation linkage
Once `identify()` has resolved, every **new** conversation the visitor starts is linked to that contact. Conversations that are already open are not re-linked: the contact is attached when the conversation is created and stays fixed for its lifetime.
While an `identify()` request is being verified, the widget briefly pauses conversation loading and sending. This prevents a previously stored user identity from being used during an account switch. If verification fails, the previous identity remains cleared and the widget continues anonymously.
When a linked conversation is created, its contact is set and the agent's template parameters are populated from the contact record as stored on Simple's side, never directly from the values you passed to `identify()`:
| Parameter | Description |
| - | - |
| `{{contact.uuid}}` | The contact's UUID. |
| `{{contact.email}}` | Email, lowercased and trimmed. |
| `{{contact.name}}` | Display name, trimmed. |
| `{{contact.phone_number}}` | Phone number. |
| `{{contact.external_id}}` | The `id` you passed to `identify()`. |
The `contact` object is always present on a linked conversation and each field is `null` when unknown. On an anonymous chat, `{{contact}}` itself is `null`. Simple owns this object: values passed in `context`, and updates made by the agent's own extraction steps, never overwrite it.
Simple also sets the flat working values `{{email}}`, `{{name}}`, `{{phone_number}}` and `{{external_id}}`, but only when the contact has a value for them. These are the parameters existing agents typically reference, and the agent's own extraction steps may update them later in the conversation, so use `{{contact.*}}` when you need the verified value.
Parameter precedence, lowest to highest: widget default params, then `context` passed from the page, then the verified contact.
If the contact cannot be resolved for the widget's organization, the conversation is created anonymously and a warning is logged on Simple's side; chat keeps working.
Calling `resetIdentity()`, or identifying as a different user, discards the conversation stored in the visitor's browser that was created under the previous identity, so the next message starts a fresh, correctly attributed conversation. Conversations started anonymously are kept.
## Authenticated Identity with HMAC
For production apps, enable identity verification so visitors cannot spoof another user's identity from the browser.
When identity verification is enabled on a widget, every `identify()` call must include a valid HMAC. Simple rejects identify requests that omit the HMAC or pass an invalid signature.
### How it works
1. Your backend signs the user's ID with your widget identity verification secret.
2. Your frontend receives that HMAC from your backend (for example, as part of the session payload).
3. Your page calls `widget.identify({ id, hmac, ... })`.
4. Simple verifies the HMAC before accepting the identity.
**Never put the identity verification secret in frontend code.** Only your server should generate the HMAC.
### Get your widget secret
Each widget has its own identity verification secret. In the Simple dashboard, open **Widgets**, select the widget, and go to **Install → Identity verification**. Choose **Reveal**, then copy the secret into your backend's environment variables or secrets manager.
Identity verification is off by default. Finish sending HMACs from your backend before turning on **Require verified identities** and saving the widget; otherwise, unsigned `identify()` calls will be rejected.
### Generate the HMAC on your server
The HMAC is **HMAC-SHA256** of the user `id`, using your identity verification secret as the key. The digest must be hex-encoded.
#### Node.js
```javascript theme={null}
const crypto = require('crypto');
function generateUserHmac(userId, identityVerificationSecret) {
return crypto
.createHmac('sha256', identityVerificationSecret)
.update(userId)
.digest('hex');
}
// Example inside a session/API response:
const hmac = generateUserHmac(currentUser.id, process.env.SIMPLE_IDENTITY_VERIFICATION_SECRET);
```
#### Python
```python theme={null}
import hashlib
import hmac
import os
def generate_user_hmac(user_id: str, identity_verification_secret: str) -> str:
return hmac.new(
identity_verification_secret.encode("utf-8"),
user_id.encode("utf-8"),
hashlib.sha256,
).hexdigest()
# Example inside a session/API response:
user_hmac = generate_user_hmac(current_user.id, os.environ["SIMPLE_IDENTITY_VERIFICATION_SECRET"])
```
#### Ruby
```ruby theme={null}
require "openssl"
def generate_user_hmac(user_id, identity_verification_secret)
OpenSSL::HMAC.hexdigest("SHA256", identity_verification_secret, user_id)
end
# Example inside a session/API response:
hmac = generate_user_hmac(current_user.id, ENV.fetch("SIMPLE_IDENTITY_VERIFICATION_SECRET"))
```
The string you sign must be exactly the same `id` string you pass to `identify()`.
### Pass the HMAC to the widget
```html theme={null}
```
### Recommended end-to-end flow
1. Reveal the widget's identity verification secret in **Install → Identity verification** and store it on your backend.
2. Your backend loads the signed-in user and generates `hmac = HMAC-SHA256(user.id, secret)`.
3. Your page renders (or fetches) `id`, profile fields, and `hmac`.
4. Initialize the widget and call `identify()` with those values.
5. Confirm every authenticated `identify()` call includes a valid HMAC, then enable **Require verified identities** and save the widget.
6. On logout, call `resetIdentity()`. Besides clearing the stored identity, this discards the conversation the browser had open under that user, so the next person on a shared device cannot pick up their chat.
```html theme={null}
```
### Security notes
* Sign only the user `id` (not email/name/phone). Profile fields are traits; the HMAC authenticates identity.
* Keep the identity verification secret server-side only (environment variable or secrets manager).
* Secrets are unique per widget. An HMAC generated with one widget's secret cannot authenticate a user on another widget.
* If a secret is exposed, disable identity verification for that widget and contact Simple support to replace it before re-enabling verification.
* If identity verification is enabled and the HMAC is missing or invalid, identify fails and the visitor stays anonymous.
## Tab Layout Example
```html theme={null}
```
# Receive Real-Time AI Call Events with Webhooks
Source: https://docs.usesimple.ai/webhooks
Subscribe to Simple AI webhook events for call starts, transfers, transcript updates, and completed calls, and verify, retry, and deduplicate deliveries.
Webhooks let you receive real-time notifications when events happen during calls. Simple AI sends HTTP POST requests to your server so your application can react to call events as they occur — and trigger full workflows in response.
## Available Events
| Event | Sent when |
| - | - |
| [`call.started`](/api-reference/webhooks/call-started) | A call is created and the agent is connecting |
| [`call.transcript_update`](/api-reference/webhooks/transcript-update) | A new entry is added to the call transcript |
| [`call.transferred`](/api-reference/webhooks/call-transferred) | The agent transfers the call |
| [`call.completed`](/api-reference/webhooks/call-completed) | The call has finished |
Webhooks are not sent for test calls.
## Setting Up Webhooks
You can create webhooks in the dashboard under **Configuration > Webhooks** (`/organization/settings/webhooks`), which opens the webhook portal, or through the [API](/api-reference/webhooks/create). When you create a webhook, you provide:
* **URL** — the endpoint on your server that will receive the events
* **Events** — which event types you want to subscribe to. If you do not choose any, the endpoint receives all of them.
Simple AI will send a POST request to your URL whenever a subscribed event occurs.
## Webhook Format
Every event is a JSON object that names the event and the call it belongs to:
```json theme={null}
{
"call_id": "123e4567-e89b-12d3-a456-426614174000",
"event_type": "call.completed"
}
```
| Field | Description |
| - | - |
| `call_id` | The UUID of the call this event relates to |
| `event_type` | The event that occurred |
`call.started` also includes `direction`, `from_number`, and `to_number`. See each event's reference page for its full payload.
Events are notifications, not snapshots. To read the call's status, transcript, recording, or analysis, [get the call](/api-reference/calls/get-call) using the `call_id` from the event.
Each request also carries three headers:
| Header | Description |
| - | - |
| `svix-id` | A unique identifier for the message. It stays the same when a delivery is retried. |
| `svix-timestamp` | When the delivery attempt was made, as a Unix timestamp in seconds |
| `svix-signature` | The signature to verify, described below |
## Verifying Signatures
Every webhook is signed with a secret that is unique to your endpoint. Verify the signature before you process an event so that you only act on requests sent by Simple AI.
Find the signing secret for an endpoint in the webhook portal, under **Configuration > Webhooks** in the dashboard. It starts with `whsec_`.
Verify against the raw request body, before any JSON parsing. Parsing and re-serializing the body changes it and causes verification to fail.
```typescript TypeScript theme={null}
import { Webhook } from 'svix';
const webhook = new Webhook(process.env.SIMPLE_WEBHOOK_SECRET);
// rawBody is the request body as a string, exactly as received
const event = webhook.verify(rawBody, {
'svix-id': request.headers['svix-id'],
'svix-timestamp': request.headers['svix-timestamp'],
'svix-signature': request.headers['svix-signature'],
});
```
```python Python theme={null}
import os
from svix.webhooks import Webhook
webhook = Webhook(os.environ["SIMPLE_WEBHOOK_SECRET"])
# raw_body is the request body as bytes or a string, exactly as received
event = webhook.verify(raw_body, {
"svix-id": request.headers["svix-id"],
"svix-timestamp": request.headers["svix-timestamp"],
"svix-signature": request.headers["svix-signature"],
})
```
`verify` returns the parsed event and throws if the signature does not match or the timestamp is too old.
To verify without a library, compute an HMAC-SHA256 over `{svix-id}.{svix-timestamp}.{raw body}` using the base64-decoded part of the secret after `whsec_`, base64-encode the result, and compare it to the signatures in `svix-signature`. That header holds one or more space-separated entries in the form `v1,`; accept the request if any of them matches.
## Pre-Call and Post-Call Workflows
Webhooks are the building blocks for full pre-call and post-call workflows. Rather than just reacting to a single event, you can use them to trigger entire journeys — looking up caller data before a call, or orchestrating a multi-step follow-up process after a call ends.
See [Workflows](/integrations/workflows) for detailed guidance on building pre-call and post-call workflows, including examples of multi-step orchestration.
## Retry Policy
If your endpoint is unavailable or returns a non-2xx status code, Simple AI retries the delivery with exponential backoff. A retried delivery has the same `svix-id` as the original.
## Best Practices
* **Respond quickly** — return a 2xx status code as soon as you receive the webhook. If you need to do heavy processing, queue the work and respond immediately.
* **Handle duplicates** — due to retries, you may receive the same event more than once. Record the `svix-id` of each message you process and skip any you have already seen.
* **Verify signatures** — always check the webhook signature to confirm the request is authentic.
* **Do not rely on order** — events for a call can arrive out of order. Get the call to read its current state.
* **Think in workflows** — a `call.completed` event does not have to map to a single action. Use it as the trigger for a full post-call journey that branches based on tags, analyzer results, or call outcome.
* **Monitor failures** — if your endpoint starts returning errors, webhooks will be retried but eventually dropped. The webhook portal lists each delivery attempt with the response your endpoint returned, and lets you resend a message.
## API Reference
* [Create webhook](/api-reference/webhooks/create)
* [List webhooks](/api-reference/webhooks/list-webhooks)
* [Delete webhook](/api-reference/webhooks/delete)