# Connect Twilio

Connect Twilio to EspoCRM for texts (SMS/MMS) and, if Twilio is your phone system, for calls with a softphone or the browser phone.

Texts always go through Twilio. Calls can go through Twilio too, if you don't use [3CX](https://docs.sapiensandmachines.com/en/espocrm-voip/connect-3cx). In the
addresses below, replace `https://crm.example.com` with your **Site URL**.

## Connect the account

:::steps
1. Open **Administration → Integrations → S+M EspoCRM VOIP · Twilio** and tick the box to turn it on.
2. Fill in **Twilio Account SID** and **Twilio Auth Token** (Twilio console → Account → API keys & tokens), and the
   **Default country code** (`1` for the US and Canada).
3. Save. Your Twilio numbers are added under **Administration → S+M EspoCRM VOIP → Twilio Numbers**.
:::

## Texts (SMS/MMS)

### Choose who uses each number

Open **Twilio Numbers**. Numbers are added automatically when you save the Twilio settings and once a day; nothing you
set is overwritten. For each number:

- **Status**: *Inactive* for a number this CRM shouldn't use (its texts are ignored and nobody can send from it).
- **Default number for new texts**.
- **Users who can send texts** and **Teams who can send texts**. Administrators can always send. Everyone who can see
  a Contact, Lead or Account can still read its texts.
- **Alert first (no assigned user)** and **Also alert on every text** (see [Daily use](https://docs.sapiensandmachines.com/en/espocrm-voip/daily-use#who-is-alerted)).
- **Twilio Messaging Service SID** (optional, `MG…`): send this number's texts through that service.

> [!IMPORTANT]
> Everyone allowed to send from a number sees all conversations through it, including with people who aren't in the
> CRM. Choose each number's users and teams with that in mind.

### Point the number at EspoCRM

:::steps
1. In the Twilio console, open **Phone Numbers → Manage → Active numbers → your number → Messaging configuration**.
   Note where its texts go today: a number sends its texts to one place only.
2. Set **A message comes in** to **Webhook** `https://crm.example.com/api/v1/SmVoip/twilio/sms`, HTTP POST. Save.
:::

Delivery reports need nothing more: EspoCRM sends their address with each text.

### Opt-outs

When someone replies **STOP** (or STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT, REVOKE, OPTOUT), Twilio answers them and
EspoCRM marks the number **Opted out**: no texts can be sent to it until they reply **START** (or YES, UNSTOP).
HELP and INFO are answered by Twilio.

### Texts from workflows

Under **Administration → SMS**, choose the SMS provider **S+M EspoCRM VOIP (Twilio)** so EspoCRM workflows can send
texts, from the default number.

## Calls through Twilio

Skip this part if your calls run on 3CX.

### 1. A SIP domain for the softphones

:::steps
1. In the Twilio console, **Voice → SIP domains → Credential lists**: create a list (for example `Company phones`)
   with a username and a strong password for each person, for example `jsmith`.
2. **SIP domains → Create SIP Domain**, for example `yourcompany.sip.twilio.com`:
   - *Voice Authentication → Credential lists*: your list.
   - *SIP Registration*: **Enabled**, with the same list.
   - *A call comes in*: the outgoing Studio flow (step 3), once it exists.
   - *Call status changes*: `https://crm.example.com/api/v1/SmVoip/twilio/status`, POST.
3. If 911 must work from the softphones, set an emergency address in Twilio.
:::

In EspoCRM's Twilio settings, fill in **SIP domain** and **Caller ID for the Call button** (your Twilio number, for
example `+18885551234`), and optionally **Record calls made with the Call button**. On each User, set **SIP Username**
to their username on the SIP domain (`jsmith`).

### 2. Incoming calls: a Studio flow

In **Studio → Flows**, create a flow (for example `Incoming`) from scratch. From the trigger's **Incoming Call** exit:

:::steps
1. **Make HTTP Request** `crm_ringing`: POST, *Form URL Encoded*, URL
   `https://crm.example.com/api/v1/SmVoip/twilio/ringing`, with the parameters `CallSid` = `{{trigger.call.CallSid}}`,
   `From` = `{{trigger.call.From}}`, `To` = `{{trigger.call.To}}` and `SipUser` = `jsmith`. Connect both *Success* and
   *Fail* to the next widget, so calls still ring if the CRM is down.
2. **Connect Call To** `ring_user`: **SIP Endpoint** `sip:jsmith@yourcompany.sip.twilio.com` (username and password
   empty), Caller ID `{{trigger.call.From}}`, a timeout such as 30 seconds, and **Record Call** on if you want
   recordings.
3. **Split Based On** `answered`, from *Connected Call Ended*, on `{{widgets.ring_user.DialCallStatus}}`: if *Equal
   To* `completed`, a **Say/Play** goodbye; otherwise a **Say/Play** greeting, then **Record Voicemail** (maximum
   length 120 seconds).
4. **Publish**.
:::

Then, on the number's **Voice configuration**: *A call comes in* → **Studio Flow** → your flow, and *Call status
changes* → `https://crm.example.com/api/v1/SmVoip/twilio/status`, HTTP POST.

### 3. Outgoing calls: a second Studio flow

Create a flow (for example `Outgoing`). From the trigger's **Incoming Call** exit (a call from a softphone arrives
at the SIP domain as an incoming call):

:::steps
1. **Make HTTP Request** like `crm_ringing` above, without `SipUser`. *Success* and *Fail* → next.
2. **Connect Call To**: **Single Number** with the value below, and your Twilio number as Caller ID.
3. **Publish**, and choose this flow as the SIP domain's *A call comes in*.
:::

```text
{{trigger.call.To | remove: "sip:" | split: "@" | first}}
```

Dial numbers from the softphone as `+1XXXXXXXXXX`. Twilio's call log shows two rows per call; EspoCRM logs one Call.

### 4. The softphone

In Zoiper (or any SIP softphone), add a SIP account: domain `yourcompany.sip.twilio.com`, the person's username and
password, transport UDP. It must show **Registered**. On a computer, make it the default app for phone links.

> [!WARNING]
> On a mobile phone, use a softphone with push notifications (for example Zoiper Pro). Free apps fall asleep in the
> background and calls go straight to voicemail.

### 5. The browser phone (optional)

Answer and make calls inside EspoCRM with the computer's microphone.

:::steps
1. Twilio console → **Account → API keys & tokens → Create API key** (type Standard). Copy the **SID** (`SK…`) and
   the **Secret** (shown once) into **API Key SID (browser phone)** and **API Key Secret (browser phone)**.
2. **Voice → Manage → TwiML apps → Create**: *Voice Request URL* = your outgoing flow's webhook URL (click the flow's
   trigger to see it), POST; *Status callback URL* = `https://crm.example.com/api/v1/SmVoip/twilio/status`. Copy its
   **SID** (`AP…`) into **TwiML App SID (browser phone)**.
3. To ring the browser and the softphone together: in the incoming flow, add a **TwiML Redirect** widget, POST, URL
   `https://crm.example.com/api/v1/SmVoip/twilio/dial?user=jsmith&timeout=30&record=1&flow=FW…` (`FW…` is the flow's
   SID; `record=0` not to record). Connect `crm_ringing` to it instead of `ring_user`, connect its **Return** exit to
   the `answered` split, and change the split to `{{widgets.<redirect widget name>.DialCallStatus}}`. Publish.
:::

Each person then turns it on in EspoCRM: **Calls** in the top bar → **Turn on the browser phone**, and allows the
microphone.

## Test

1. Text the Twilio number from your mobile: within seconds a *New text from …* pop-up appears. Reply from EspoCRM.
2. Reply **STOP**: EspoCRM shows *Opted out*. Reply **START** to undo.
3. With Twilio calls: call the number from a phone that's in the CRM; the softphone rings and the pop-up shows the
   record. Hang up: a held "Inbound call" is logged.

If nothing happens, see [Troubleshooting](https://docs.sapiensandmachines.com/en/espocrm-voip/troubleshooting#twilio).
