# Twilio Operating Runbook
**Cold Bore Ballistics LLC — Confidential**
Last updated: 2026-06-06

> This document contains step-by-step procedures for day-to-day Twilio operations. For resource names and SIDs, see `Twilio_Architecture_Reference.md`. For disaster recovery and full rebuild, see `Twilio_Rebuild_Guide.md`.

---

## Prerequisites

### Console Access
1. Open a browser and go to `https://console.twilio.com`
2. Log in with `jason@coldboreballisticsllc.com`
3. To switch between accounts (CBB Corporate / ScopeDOPE), click the account name dropdown in the top-left corner of the Console

### API Access
All API operations require three values from `Twilio_Architecture_Reference.md`:
- Account SID (`AC...`)
- API Key SID (`SK...`) — current SID is in the Architecture Reference
- API Key Secret — stored in `kv-cbb-shared-prod` → `twilio-cbb-claude-code-cli-secret`

Set these in your shell before running any API commands:
```bash
export TWILIO_ACCOUNT_SID=AC26cc215c2e2f0425984bffbe7050a0e0
export TWILIO_API_KEY=<SK...>   # current SID from Twilio_Architecture_Reference.md
export TWILIO_API_SECRET=$(az keyvault secret show \
  --vault-name kv-cbb-shared-prod \
  --name twilio-cbb-claude-code-cli-secret \
  --query value --output tsv)
```

---

## Procedure: Purchase a Phone Number

### Via Console
1. Log in to Console and ensure you are in the correct account (CBB Corporate for 800 number)
2. In the left navigation, click **Phone Numbers** → **Manage** → **Buy a number**
3. Set filters:
   - Country: United States
   - Number type: Toll-free (for 800 number) or Local
   - Capabilities: Voice + SMS (check both)
4. Search for available numbers
5. Click **Buy** next to the desired number
6. Confirm purchase — $2.00/month for toll-free, $1.15/month for local
7. After purchase, update `Twilio_Architecture_Reference.md` — add the number to the Phone Numbers section

### Via API
```bash
# Search available toll-free numbers
curl -s -X GET "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/AvailablePhoneNumbers/US/TollFree.json?SmsEnabled=true&VoiceEnabled=true" \
  -u "$TWILIO_API_KEY:$TWILIO_API_SECRET"

# Purchase a specific number (replace +18XXXXXXXXX with chosen number)
curl -s -X POST "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/IncomingPhoneNumbers.json" \
  -u "$TWILIO_API_KEY:$TWILIO_API_SECRET" \
  --data-urlencode "PhoneNumber=+18XXXXXXXXX" \
  --data-urlencode "FriendlyName=CBB Corporate - 800 Number" \
  --data-urlencode "SmsMethod=POST" \
  --data-urlencode "VoiceMethod=POST"
```

---

## Procedure: Configure Call Forwarding on 800 Number

After purchasing the 800 number, configure it to forward inbound calls to Jason's personal cell.

1. In Console, go to **Phone Numbers** → **Manage** → **Active numbers**
2. Click the 800 number
3. Under **Voice & Fax** → **A call comes in** → select **Redirect to** → enter personal cell number in E.164 format (e.g., `+1XXXXXXXXXX`)
4. Click **Save**

For outbound caller ID masking (UC-04), calls placed through the Twilio number will automatically display the 800 number as caller ID — no additional configuration required.

---

## Procedure: Add a Number to the Messaging Service

After purchasing a phone number, add it to the CBB Corporate Messaging Service for compliant A2P SMS.

```bash
curl -s -X POST "https://messaging.twilio.com/v1/Services/MGfed49ffbd4095c927e22b49670b0a48d/PhoneNumbers" \
  -u "$TWILIO_API_KEY:$TWILIO_API_SECRET" \
  --data-urlencode "PhoneNumberSid=<PN_SID_of_purchased_number>"
```

The `PhoneNumberSid` (`PN...`) is returned when the number is purchased, or can be retrieved:
```bash
curl -s -X GET "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/IncomingPhoneNumbers.json" \
  -u "$TWILIO_API_KEY:$TWILIO_API_SECRET"
```

---

## Procedure: Rotate an API Key

Use this procedure when an API key is compromised or when rotating credentials on schedule.

> **WARNING:** Do NOT rotate the primary Auth Token unless a key is compromised — Auth Token rotation invalidates ALL API keys simultaneously.

1. Create a new API key first (before deleting the old one):
```bash
# Via Console: Settings → API Keys & Auth Tokens → Create API Key → Standard
# Copy both the SID (SK...) and Secret immediately — Secret is shown only once
```

2. Update all systems using the old key to use the new key SID + secret

3. Verify the new key works:
```bash
curl -s -X GET "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID.json" \
  -u "<new_SK...>:<new_secret>"
```

4. Delete the old key:
```bash
curl -s -X DELETE "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Keys/<old_SK...>.json" \
  -u "<new_SK...>:<new_secret>"
```

5. Update `Twilio_Architecture_Reference.md` — remove old key entry, add new key entry with date

---

## Procedure: Create a New Subaccount

Use this procedure when a new product line requires its own Twilio account for cost isolation.

1. Confirm the new product line is active and actually requires Twilio resources — do not create placeholder subaccounts
2. Create the subaccount:
```bash
CBB_AUTH=$(az keyvault secret show \
  --vault-name kv-cbb-shared-prod \
  --name twilio-cbb-auth-token \
  --query value --output tsv)

curl -s -X POST "https://api.twilio.com/2010-04-01/Accounts.json" \
  -u "AC26cc215c2e2f0425984bffbe7050a0e0:$CBB_AUTH" \
  --data-urlencode "FriendlyName=<Product Name>"
```
3. **Immediately copy and store the Auth Token** from the response — it is shown only once at creation
4. Create a dedicated API Key for the new subaccount (use the subaccount's own SID + Auth Token to authenticate):
```bash
# Console: switch to the new subaccount → Settings → API Keys → Create API Key
```
5. Update `Twilio_Architecture_Reference.md` — add the new subaccount to the Account Structure section

---

## Procedure: Suspend or Close a Subaccount

> **WARNING:** Closing a subaccount is permanent after 30 days. Suspension is reversible. Choose carefully.

### Suspend (reversible)
```bash
CBB_AUTH=$(az keyvault secret show \
  --vault-name kv-cbb-shared-prod \
  --name twilio-cbb-auth-token \
  --query value --output tsv)

curl -s -X POST "https://api.twilio.com/2010-04-01/Accounts/<subaccount_SID>.json" \
  -u "AC26cc215c2e2f0425984bffbe7050a0e0:$CBB_AUTH" \
  --data-urlencode "Status=suspended"
```

### Close (permanent after 30 days)
```bash
CBB_AUTH=$(az keyvault secret show \
  --vault-name kv-cbb-shared-prod \
  --name twilio-cbb-auth-token \
  --query value --output tsv)

curl -s -X POST "https://api.twilio.com/2010-04-01/Accounts/<subaccount_SID>.json" \
  -u "AC26cc215c2e2f0425984bffbe7050a0e0:$CBB_AUTH" \
  --data-urlencode "Status=closed"
```

---

## Procedure: Check Account Balance

```bash
curl -s -X GET "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Balance.json" \
  -u "$TWILIO_API_KEY:$TWILIO_API_SECRET"
```

---

## Procedure: Add Funds to Account

1. Log in to Console → **Admin** → **Account billing**
2. Click **Add funds**
3. Enter amount and confirm payment method
4. Funds are available immediately

Auto-recharge can be configured here as well — recommended threshold: $10 trigger, $25 top-up.

---

## Procedure: Set Up 10DLC Registration (before Club SaaS SMS)

10DLC (10-digit long code) registration is required by US carriers before sending A2P SMS at volume. This must be completed before Club SaaS match day SMS goes live.

**Step 1 — Register your brand:**
1. Console → **Messaging** → **Regulatory Compliance** → **A2P 10DLC** → **Register a brand**
2. Enter business details:
   - Legal business name: Cold Bore Ballistics LLC
   - Business type: Private for-profit
   - EIN: (CBB EIN — see Legal_and_Trademarks/)
   - Website: `https://www.coldboreballisticsllc.com`
   - Business contact: `jason@coldboreballisticsllc.com`
3. One-time fee: $4.00
4. Approval typically takes 1–3 business days

**Step 2 — Register a campaign:**
1. Console → **Messaging** → **Regulatory Compliance** → **A2P 10DLC** → **Register a campaign**
2. Select the registered brand
3. Select use case: **Mixed** (covers notifications, reminders, alerts)
4. Provide sample messages (match reminders, score notifications, cancellations)
5. Monthly fee: $15.00
6. Approval typically takes 1–5 business days

**Step 3 — Link campaign to Messaging Service:**
1. In Console, navigate to the Messaging Service (`MGfed49ff...`)
2. Under **Compliance** → assign the approved 10DLC campaign

---

## Procedure: Monitor Usage and Costs

```bash
# Get usage summary for current month
curl -s -X GET "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Usage/Records/ThisMonth.json" \
  -u "$TWILIO_API_KEY:$TWILIO_API_SECRET" | python3 -m json.tool
```

Console view: **Monitor** → **Usage** → **Usage Records** — filter by date range and category.

---

## Procedure: Update IVR Script

> **Pre-change step:** Before editing the IVR, run `cbb-utils/twilio/snapshot_account.sh` and commit the output with a message describing what you are about to change. See the cbb-utils/twilio README for the commit pattern.

Use when the greeting text or menu options need to change.

1. Log in to Console and switch to the **ScopeDOPE subaccount** (`AC7cc6b6571c9b0907b73b783a5422a2c4`)
2. Navigate to **Explore Products → Developer Tools → Studio → Flows**
3. Click **ScopeDOPE IVR**
4. Click the widget whose text needs to change (e.g., `main_menu_say` for the greeting)
5. Edit the **Say** field with the new text
6. Click **Save** on the widget
7. Click **Publish** (top right) — changes are not live until published
8. Update `Twilio_Architecture_Reference.md` if the routing map or any SID changes
9. Test by calling `+18773673707`

> **Note:** The voicemail prompt text ("Please leave your message after the tone...") appears in each `voicemail_prompt_*` widget — there are 6 of them (1, 2, 3, 4, 91, 92). Update all six if changing the voicemail prompt.

---

## Procedure: Rotate SendGrid API Key for IVR Function

Use when the `twilio-ivr` SendGrid key needs rotation. See also `SendGrid_Operating_Runbook.md` — Procedure: Rotate the twilio-ivr API Key.

1. Create a new restricted SendGrid key (Mail Send only) — see `SendGrid_Operating_Runbook.md`
2. Store the new key value in Key Vault:
```bash
az keyvault secret set \
  --vault-name kv-cbb-shared-prod \
  --name sendgrid-twilio-ivr-secret \
  --value "<new-key-value>"
```
3. Update the Twilio Function environment variable:
```bash
# Get current variable SID
curl -s "https://serverless.twilio.com/v1/Services/ZSfb518af22cef2c341dccd311a4d29d81/Environments/ZEa0ad80e6327ffb7bb3a3bb31db28469c/Variables" \
  -u "AC7cc6b6571c9b0907b73b783a5422a2c4:<auth-token>"

# Update the SENDGRID_API_KEY variable (replace ZV... with actual SID)
curl -s -X POST "https://serverless.twilio.com/v1/Services/ZSfb518af22cef2c341dccd311a4d29d81/Environments/ZEa0ad80e6327ffb7bb3a3bb31db28469c/Variables/ZV200e9a3e467bd88ae1f6aea01dcd915e" \
  -u "AC7cc6b6571c9b0907b73b783a5422a2c4:<auth-token>" \
  --data-urlencode "Value=<new-key-value>"
```
4. Redeploy the Function — trigger a new build and deployment (see `Twilio_Rebuild_Guide.md` for full Function redeploy steps)
5. Update `SendGrid_Architecture_Reference.md` with the new key ID
6. Delete the old SendGrid key

---

## Procedure: Test IVR End-to-End

Run after any IVR change, deployment, or key rotation.

1. Call `+18773673707` from a personal cell
2. Verify the greeting plays correctly with the Alice voice
3. Test each path:
   - Press 1 → leave voicemail → confirm email arrives at `phone@scope-dope.com` with subject `[Bug Report] Voicemail from +1XXXXXXXXXX`
   - Press 2 → leave voicemail → confirm `[Feature Request]` at `phone@scope-dope.com`
   - Press 3 → leave voicemail → confirm `[Support]` at `phone@scope-dope.com`
   - Press 4 → leave voicemail → confirm `[Business Inquiry]` at `phone@coldboreballisticsllc.com`
   - Press 9 → press 1 → leave voicemail → confirm `[ScopeDOPE General]` at `phone@scope-dope.com`
   - Press 9 → press 2 → leave voicemail → confirm `[CBB General]` at `phone@coldboreballisticsllc.com`
   - Press 9 → wait (timeout) → confirm routes to ScopeDOPE general voicemail
4. Verify navigation: Press 0 replays current menu; Press * returns to main menu from any level
5. Verify email headers: check raw headers for `X-CBB-Source`, `X-CBB-Called`, `X-CBB-Category`, `X-CBB-Option`
6. Verify transcription arrives (may take 30–60 seconds after recording)

---

## Procedure: Enable 2FA on Console Account

> **CBB-99 — Done.** 2FA is already enabled on the Twilio Console account. This procedure is retained for reference in the event 2FA needs to be re-enrolled (e.g., new device, authenticator app migration).

1. Log in to Console as `jason@coldboreballisticsllc.com`
2. Click your avatar (top right) → **Profile**
3. Under **Two-factor authentication** → **Enable** (or **Re-enroll**)
4. Choose **Authenticator app** (Microsoft Authenticator is already in use for M365)
5. Scan QR code with authenticator app
6. Enter verification code to confirm
7. Save backup codes in a secure location
8. Update `Twilio_Architecture_Reference.md` if status changes

---

## Document Control

| Field | Value |
|---|---|
| **Document owner** | `jschwefel@coldboreballisticsllc.com` |
| **Classification** | Confidential (03_-_Confidential/) |
| **Location** | `OneDrive/CBB_Corporate/03_-_Confidential/Infrastructure/Twilio/Twilio_Operating_Runbook.md` |
| **Cross-references** | `Twilio_Architecture_Reference.md`; `Twilio_Rebuild_Guide.md`; `SendGrid_Operating_Runbook.md` |
| **Created** | 2026-06-06 |
| **Last updated** | 2026-06-06 |
| **Next full review due** | 2027-06-06 |

---

## Revision Control Block

| Rev | Date | Changed By | Reviewed By | Approved By | Summary |
|-----|------|------------|-------------|-------------|---------|
| 0.9 | 2026-06-06 | jschwefel | — | — | Initial release |
