# GHL → 3CX Bridge — Setup & Testing Guide
## Option A: GHL public number → PSTN forward → 3CX DID → Queue → CSRs

---

## Architecture recap

```
Customer dials GHL support number
        ↓  (PSTN call forward)
3CX DID (provided by Telnyx/Vonage/DIDWW)
        ↓  (SIP trunk)
3CX PBX — Support Queue — CSR agents
        ↓  (webhook POST)
Your PHP server → GHL CRM (contacts, tags, logs)
```

No Twilio. No extra bridge layer.

---

## PART 1 — Get a SIP DID for 3CX (replaces Twilio)

You need a real phone number from a SIP provider that connects to 3CX.
Recommended: **Telnyx** (cheapest, excellent 3CX support).

### Steps (Telnyx)

1. Sign up at https://telnyx.com
2. Go to **Numbers → Search** — buy a number in your area code
3. Go to **Voice → SIP Trunks → Create SIP Trunk**
   - Name: `3cx-support-trunk`
   - Origination: add `sip:YOUR_3CX_IP:5060` (or your 3CX FQDN)
   - Set **Concurrent call limit** to 10+ (no busy signals)
4. Assign the number you bought to this SIP trunk
5. Note down: the DID number (e.g. +1 202 555 0100) — this is `3CX_DID_NUMBER`

---

## PART 2 — 3CX Configuration

### 2a. Add Telnyx as a SIP trunk in 3CX

1. Log into **3CX Web Management Console** (https://YOUR_3CX_HOST:5001)
2. Go to **SIP Trunks → Add SIP Trunk**
3. Select **Generic SIP** (or search for Telnyx template)
4. Fill in:
   - **Registrar/Proxy**: `sip.telnyx.com`
   - **Authentication ID**: your Telnyx SIP username (from Telnyx portal → SIP Trunk → Credentials)
   - **Password**: your Telnyx SIP password
   - **Number of simultaneous calls**: 10
5. Under **Inbound rules**, add:
   - **DID number**: the Telnyx number you bought (digits only, no + or spaces)
   - **Destination**: `800` (your support queue extension — create it next)
6. Save and wait for trunk status to show **OK/Registered**

### 2b. Create the Support Queue

1. Go to **Call Queues → Add Queue**
2. Set:
   - **Extension**: `800`
   - **Queue name**: `Support`
   - **Ring strategy**: `Round Robin`
   - **No answer**: `Keep in queue` ← critical, NOT voicemail
   - **Max wait time**: `300 seconds`
   - **Hold music**: upload an MP3 or use default
   - **Polling interval**: `10 seconds`
3. Under **Agents**, add your CSR extensions (e.g. 101, 102, 103)
4. Save

### 2c. Create CSR extensions

For each support agent:
1. Go to **Users → Add User**
2. Set extension (101, 102, 103…), name, email
3. Under **Phone**, choose **3CX App** (desktop or mobile)
4. Send the welcome email — agent installs the 3CX app and logs in

### 2d. Configure webhook (3CX → your PHP server)

1. Go to **Settings → Integrations → Webhooks** (or **Call Events** depending on your version)
2. Add a new webhook:
   - **URL**: `https://yourdomain.com/webhook/receive.php`
   - **Events**: check all — Call Start, Call Answer, Call End, Call Missed, Call Queue
   - **Secret / Header**: add header `X-3CX-Secret: YOUR_SECRET_STRING`
     (must match `3CX_WEBHOOK_SECRET` in your config)
3. Save and test with the built-in test button if available

> **3CX Cloud (hosted)**: Webhooks are under **Settings → CRM Integration → Custom**.
> **3CX Self-hosted**: Check **Settings → General → Webhooks**.

---

## PART 3 — GHL Configuration

### 3a. Get your GHL API credentials

1. Log into GHL → go to **Settings → Integrations → API Keys**
2. Create a new API key — copy it as `GHL_API_KEY`
3. Your Location ID is in the URL: `app.gohighlevel.com/location/LOCATION_ID_HERE/`
   Copy it as `GHL_LOCATION_ID`

### 3b. Set call forwarding on your support number

1. Go to **Settings → Phone Numbers**
2. Find your support number (the one customers call)
3. Click **Edit / Configure**
4. Under **Incoming Calls**, set action to: **Forward Call**
5. Forward number: enter the Telnyx DID you bought (e.g. `+12025550100`)
6. **Disable voicemail** on this number — set to "Disconnected" or "No voicemail"
7. Save

> GHL will now forward every inbound call via PSTN to 3CX's DID.
> The call costs a small PSTN forwarding fee per minute, but you keep GHL as the brand number.

### 3c. Create a "Missed Call" workflow (optional but recommended)

1. Go to **Automation → Workflows → New Workflow**
2. Trigger: **Tag Added** → tag name: `Missed Call`
3. Actions:
   - Send SMS to contact: "Sorry we missed your call! We'll call you back shortly."
   - Assign to user / notify team
   - (Optional) create task for follow-up
4. Save and activate
5. Copy the workflow ID from the URL and paste it into `CallEventProcessor.php` → `WORKFLOWS['missed_call']`

---

## PART 4 — Deploy the PHP bridge

### 4a. Server requirements

- PHP 8.1+
- HTTPS (required for 3CX webhooks)
- Public IP / domain

### 4b. Install

```bash
git clone <your-repo> /var/www/ghl-3cx-bridge
cd /var/www/ghl-3cx-bridge
composer install
cp .env.example .env
nano .env   # fill in all values
```

### 4c. Configure your web server

**Nginx** — add to your server block:
```nginx
location /webhook {
    root /var/www/ghl-3cx-bridge;
    try_files $uri $uri/ /webhook/receive.php?$query_string;
    fastcgi_pass unix:/run/php/php8.1-fpm.sock;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
```

**Apache** — add a `.htaccess` in `/webhook/`:
```apache
Options -Indexes
<Files "receive.php">
    Require all granted
</Files>
```

---

## PART 5 — Testing

### Step 1: Verify connectivity
```bash
php ghl/verify_forward.php
```
All four checks should pass before proceeding.

### Step 2: Simulate 3CX events (no real call needed)
```bash
# Test a full answered call
php sip/simulate_3cx_events.php full

# Test a missed call
php sip/simulate_3cx_events.php missed

# Test 3 concurrent calls
php sip/simulate_3cx_events.php concurrent
```

After each simulation, log into GHL → Contacts → search for `+12025550100` and verify:
- Contact was created
- Correct tags were added
- Call activity note appeared on the timeline

### Step 3: Real call test (single)

1. Make sure at least one CSR is logged into the 3CX app and set to **Available**
2. Dial your GHL support number from any phone
3. Verify the call routes: GHL → Telnyx DID → 3CX queue → CSR app rings
4. Answer on the CSR app
5. Hang up after 10 seconds
6. Check GHL contact — tags + call log should appear within 5 seconds

### Step 4: Concurrent call test

1. Have 2 phones call the support number simultaneously
2. Both should queue — neither should get a busy signal
3. If only 1 CSR is available, one call connects, the other holds with music
4. When the first call ends, the queued call connects automatically

### Step 5: All-agents-busy test

1. Put all CSR extensions on DND or offline in the 3CX app
2. Dial the support number
3. After the configured max wait time (300s), the call should either:
   - Stay in queue (correct — if `no_answer_action = queue`)
   - NOT go to voicemail
4. In GHL, you should see a `Missed Call` tag on the contact

---

## Troubleshooting

| Symptom | Likely cause | Fix |
|---|---|---|
| Busy signal on inbound | Telnyx concurrent limit too low | Increase in Telnyx SIP trunk settings |
| GHL forwards but 3CX doesn't ring | SIP trunk not registered | Check trunk status in 3CX — recheck credentials |
| Webhook not firing | 3CX can't reach your server | Check firewall, HTTPS cert, webhook URL |
| GHL contact not created | API key wrong or rate limited | Check GHL_API_KEY and location ID |
| Calls go to voicemail | GHL forwarding not disabled | Disable voicemail on the GHL number |
| Tags not appearing | Webhook secret mismatch | Check 3CX_WEBHOOK_SECRET matches header |

---

## Log monitoring

```bash
# Watch live call events
tail -f logs/calls.log

# Check for errors
grep ERROR logs/calls.log
```
