# Telnyx Missions

> Track agent activities using the Telnyx AI Missions API. Use this skill when executing multi-step tasks that should be logged and tracked. Supports creating voice/SMS agents, scheduling calls, and retrieving conversation insights. Use when tasks involve calling people, sending SMS, or any substantial tracked work.

- Skill: `team-telnyx/telnyx-missions` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add team-telnyx/telnyx-missions`
- Raw SKILL.md: https://api.skillmd.com/api/skills/team-telnyx/telnyx-missions/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: team-telnyx (https://skillmd.com/u/team-telnyx)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/team-telnyx/telnyx-missions

---


# Telnyx AI Missions

Track multi-step agent activities using the Telnyx AI Missions API. Create voice/SMS assistants, schedule calls, and retrieve conversation insights.

---

# 🛑 GUARDRAILS — Actions Requiring Explicit User Permission

**The following actions are NEVER allowed without explicit user approval.** Do not proceed with any of these — even if the mission plan implies them — until the user has reviewed and confirmed.

These guardrails apply to **all contexts**: interactive sessions, cron-triggered runs, sub-agent executions, and any automated workflow that uses this skill.

## Prohibited Without Permission

1. **Remove a connection from a phone number** — Never unassign or change the connection profile on a phone number without user review. This can break live call routing.

2. **Create, edit, or delete an AI assistant** — Assistants are shared resources. Creating new ones, modifying instructions/tools/voice on existing ones, or deleting them requires explicit approval. (Reusing an existing assistant as-is is fine.)

3. **Create or edit TeXML apps or other connections** — Missions should never need to create or modify TeXML applications, SIP connections, FQDN connections, or any other connection type. If a mission plan seems to require this, stop and ask the user — the approach is wrong.

4. **Schedule a cron job** — Never create, modify, or enable a cron job (OpenClaw cron, system cron, or any scheduled automation) without user review. This includes cron jobs for polling, retries, or follow-up actions.

## Enforcement

- **Before executing any of the above:** pause, describe what you intend to do and why, and wait for explicit approval.
- **If running via cron or automation:** the cron-triggered agent must also follow these guardrails. Automation does not grant implicit permission. If a guardrailed action is needed, notify the user and wait — do not proceed unattended.
- **If in doubt:** ask. It is always better to pause and confirm than to take an irreversible action.

---

## Setup

The Python script `telnyx_api.py` handles all API calls:

```bash
# Set your API key
export TELNYX_API_KEY="your_key_here"

# Run commands using the script
python3 {baseDir}/scripts/telnyx_api.py <command> [args...]

# Or create an alias for convenience
alias missions="python3 {baseDir}/scripts/telnyx_api.py"
```

**Note:** All command examples in this document use `python telnyx_api.py` for brevity. Replace with the full path `python3 {baseDir}/scripts/telnyx_api.py` or use the alias above.

---

This skill enables you to track your work using the Telnyx AI Missions API, including making phone calls and sending SMS messages through AI assistants.

---

# ⚠️ CRITICAL: SAVE STATE FREQUENTLY ⚠️

**You MUST save your progress after EVERY significant action.** If the session crashes or restarts, unsaved work is LOST.

## Two-Layer Persistence: Memory + Events

Always save to BOTH:
1. **Local Memory** (`.missions_state.json`) - Fast, survives restarts
2. **Events API** (cloud) - Permanent audit trail, survives local file loss

## When to Save (After EVERY action!)

| Action | Save Memory | Log Event |
|--------|-------------|-----------|
| Web search returns results | ✅ append-memory | ✅ log-event (tool_call) |
| Found a contractor/lead | ✅ append-memory | ✅ log-event (custom) |
| Created assistant | ✅ save-memory | ✅ log-event (custom) |
| Assigned phone number | ✅ save-memory | ✅ log-event (custom) |
| Scheduled a call/SMS | ✅ append-memory | ✅ log-event (custom) |
| Call completed | ✅ save-memory | ✅ log-event (custom) |
| Got quote/insight | ✅ save-memory | ✅ log-event (custom) |
| Made a decision | ✅ save-memory | ✅ log-event (message) |
| Step started | ✅ save-memory | ✅ update-step (in_progress) + log-event (step_started) |
| Step completed | ✅ save-memory | ✅ update-step (completed) + log-event (step_completed) |
| Step failed | ✅ save-memory | ✅ update-step (failed) + log-event (error) |
| Error occurred | ✅ save-memory | ✅ log-event (error) |

## Memory Commands (Local Backup)

```bash
# Save a single value
python telnyx_api.py save-memory "<slug>" "key" '{"data": "value"}'

# Append to a list (great for collecting multiple items)
python telnyx_api.py append-memory "<slug>" "contractors" '{"name": "ABC Co", "phone": "+1234567890"}'

# Retrieve memory
python telnyx_api.py get-memory "<slug>"           # Get all memory
python telnyx_api.py get-memory "<slug>" "key"     # Get specific key
```

## Event Commands (Cloud Backup)

```bash
# Log an event (step_id is REQUIRED - links event to a plan step)
python telnyx_api.py log-event <mission_id> <run_id> <type> "<summary>" <step_id> '[payload_json]'

# Event types: tool_call, custom, message, error, step_started, step_completed
# step_id: Use the step_id from your plan (e.g., "research", "setup", "calls")
#          Use "-" if event doesn't belong to a specific step
```

## Example: Complete Save Pattern

After finding a contractor via web search, do BOTH:

```bash
# 1. Save to local memory (fast recovery)
python telnyx_api.py append-memory "find-window-washers" "contractors_found" '{"name": "ABC Cleaning", "phone": "+13125551234", "source": "google search"}'

# 2. Log to events API with step_id (permanent cloud record linked to plan step)
python telnyx_api.py log-event "$MISSION_ID" "$RUN_ID" custom "Found contractor: ABC Cleaning +13125551234" "research" '{"contractor": "ABC Cleaning", "phone": "+13125551234", "source": "google search"}'
```

After scheduling a call:

```bash
# 1. Local memory
python telnyx_api.py append-memory "find-window-washers" "calls_scheduled" '{"event_id": "evt_123", "contractor": "ABC Cleaning", "time": "2024-12-01T15:00:00Z"}'

# 2. Cloud event with step_id
python telnyx_api.py log-event "$MISSION_ID" "$RUN_ID" custom "Scheduled call to ABC Cleaning for 3:00 PM" "calls" '{"scheduled_event_id": "evt_123", "contractor": "ABC Cleaning", "scheduled_for": "2024-12-01T15:00:00Z"}'
```

After getting a quote from a call:

```bash
# 1. Local memory
python telnyx_api.py save-memory "find-window-washers" "quotes" '{"ABC Cleaning": {"amount": 350, "available": "next week"}}'

# 2. Cloud event with step_id
python telnyx_api.py log-event "$MISSION_ID" "$RUN_ID" custom "Call completed: ABC Cleaning quoted $350" "calls" '{"contractor": "ABC Cleaning", "quote": 350, "availability": "next week", "conversation_id": "conv_xyz"}'
```

## Best Practices

1. **Save IMMEDIATELY** - Don't wait, don't batch
2. **Save to BOTH** - Memory (local) AND Events (cloud)
3. **Be verbose** - More data saved = easier recovery
4. **Include context** - Timestamps, sources, IDs
5. **Save partial results** - Something is better than nothing
6. **Save before risky operations** - Before long API calls or waits

---

## When to Use This Skill

This skill has two modes: **full missions** (tracked, multi-step) and **simple calls** (one-off, no mission overhead). Pick the right one.

### Use a Full Mission When:
- The task involves **multiple calls or SMS** (batch outreach, surveys, sweeps)
- You need a **complete audit trail** with events, plans, and state tracking
- The task is **multi-step** and takes significant effort across phases
- **Retries and failure tracking** matter
- You need to **compare results** across multiple calls

Examples:
- "Find me window washing contractors in Chicago, call them and negotiate rates"
- "Contact all leads in this list and schedule demos"
- "Call 10 weather stations and find the hottest one"

### Do NOT Use a Mission When:
- The task is a **single outbound call** — just create an assistant (or reuse one) and schedule the call directly
- It's a **one-off SMS** — schedule it and done
- The task doesn't need tracking, plans, or state recovery
- You'd be creating a mission with one step and one call — that's overengineering

**For simple calls, just:**
```bash
# Reuse or create an assistant
python telnyx_api.py list-assistants --name=<relevant>
# Schedule the call
python telnyx_api.py schedule-call <assistant_id> <to> <from> <datetime> <mission_id> <run_id> <step_id>
# Poll for completion
python telnyx_api.py get-event <assistant_id> <event_id>
# Get insights
python telnyx_api.py get-insights <conversation_id>
```

No mission, no run, no plan. Keep it simple.

## Required Setup

The Python script `telnyx_api.py` handles all API calls. Check that `TELNYX_API_KEY` environment variable is set:

```bash
python telnyx_api.py check-key
```

# State Persistence

The script automatically manages state in `.missions_state.json`. This survives restarts and supports multiple concurrent missions.

## State Commands

```bash
# List all active missions
python telnyx_api.py list-state

# Get state for a specific mission
python telnyx_api.py get-state "find-window-washing-contractors"

# Remove a mission from state
python telnyx_api.py remove-state "find-window-washing-contractors"
```

---

# Core Workflow

## Phase 1: Initialize Tracking

### Step 1.1: Create a Mission

```bash
python telnyx_api.py create-mission "Brief descriptive name" "Full description of the task"
```

**Save the returned `mission_id`** - you'll need it for all subsequent calls.

### Step 1.2: Start a Run

```bash
python telnyx_api.py create-run <mission_id> '{"original_request": "The exact user request", "context": "Any relevant context"}'
```

**Save the returned `run_id`**.

### Step 1.3: Create a Plan

Before executing, outline your plan:

```bash
python telnyx_api.py create-plan <mission_id> <run_id> '[
  {"step_id": "step_1", "description": "Research contractors online", "sequence": 1},
  {"step_id": "step_2", "description": "Create voice agent for calls", "sequence": 2},
  {"step_id": "step_3", "description": "Schedule calls to each contractor", "sequence": 3},
  {"step_id": "step_4", "description": "Monitor call completions", "sequence": 4},
  {"step_id": "step_5", "description": "Analyze results and select best options", "sequence": 5}
]'
```

### Step 1.4: Set Run to Running

```bash
python telnyx_api.py update-run <mission_id> <run_id> running
```

### High-Level Alternative: Initialize Everything at Once

Use the `init` command to create mission, run, plan, and set status in one step:

```bash
python telnyx_api.py init "Find window washing contractors" "Find contractors in Chicago, call them, negotiate rates" "User wants window washing quotes" '[
  {"step_id": "research", "description": "Find contractors online", "sequence": 1},
  {"step_id": "setup", "description": "Create voice agent", "sequence": 2},
  {"step_id": "calls", "description": "Schedule and make calls", "sequence": 3},
  {"step_id": "analyze", "description": "Analyze results", "sequence": 4}
]'
```

This also automatically resumes if a mission with the same name already exists.

---

## Phase 2: Voice/SMS Agent Setup

When your task requires making calls or sending SMS, create an AI assistant first.

### Step 2.1: Create a Voice/SMS Assistant

**For phone calls:**
```bash
python telnyx_api.py create-assistant "Contractor Outreach Agent" "You are calling on behalf of [COMPANY]. Your goal is to [SPECIFIC GOAL]. Be professional and concise. Collect: [WHAT TO COLLECT]. If they cannot talk now, ask for a good callback time." "Hi, this is an AI assistant calling on behalf of [COMPANY]. Is this [BUSINESS NAME]? I am calling to inquire about your services. Do you have a moment?" '["telephony"]'
```

**For SMS:**
```bash
python telnyx_api.py create-assistant "SMS Outreach Agent" "You send SMS messages to collect information. Keep messages brief and professional." "Hi! I am reaching out on behalf of [COMPANY] regarding [PURPOSE]. Could you please reply with [REQUESTED INFO]?" '["messaging"]'
```

**Save the returned `assistant_id`**.

### Step 2.2: Find and Assign a Phone Number

#### 2.2.1: List Available Phone Numbers

```bash
python telnyx_api.py list-phones --available
```

Or get the first available one directly:

```bash
python telnyx_api.py get-available-phone
```

**If no phone numbers are available, STOP and inform the user:**
> "No available phone numbers found. You need to purchase phone numbers from Telnyx at https://portal.telnyx.com before I can make calls."

#### 2.2.2: Get Assistant's Connection ID

```bash
# For voice calls
python telnyx_api.py get-connection-id <assistant_id> telephony

# For SMS
python telnyx_api.py get-connection-id <assistant_id> messaging
```

#### 2.2.3: Assign Phone Number to Assistant

```bash
# For voice calls
python telnyx_api.py assign-phone <phone_number_id> <connection_id> voice

# For SMS
python telnyx_api.py assign-phone <phone_number_id> <connection_id> sms
```

### High-Level Alternative: Setup Agent in One Step

Use the `setup-agent` command to create assistant and assign phone number:

```bash
python telnyx_api.py setup-agent "find-window-washing-contractors" "Contractor Caller" "You are calling to get quotes for commercial window washing. Ask about: rates per floor, availability, insurance. Be professional." "Hi, I am calling to inquire about your commercial window washing services. Do you have a moment to discuss rates?"
```

This automatically:
- Creates the assistant with telephony features
- **Links the agent to the mission run** (if mission_id and run_id are in state)
- Finds an available phone number
- Assigns it to the assistant
- Saves all IDs to the state file

### Step 2.3: Link Agent to Mission Run

**IMPORTANT**: After creating an assistant, you MUST link it to the mission run. This allows the system to track which agents are working on which missions.

**If using `setup-agent` command**: Linking is done automatically when mission_id and run_id are in the state.

**If setting up manually**:
```bash
python telnyx_api.py link-agent <mission_id> <run_id> <assistant_id>
```

You can also list and unlink agents:
```bash
# List all agents linked to a run
python telnyx_api.py list-linked-agents <mission_id> <run_id>

# Unlink an agent from a run
python telnyx_api.py unlink-agent <mission_id> <run_id> <assistant_id>
```

### Step 2.4: Log the Setup

```bash
python telnyx_api.py log-event <mission_id> <run_id> custom "Created voice assistant and assigned phone number" "setup" '{"assistant_id": "<assistant_id>", "phone_number": "+15551234567", "type": "telephony"}'
```

---

## Phase 3: Research & Data Gathering

Search for the information you need (contractors, leads, etc.):

1. Use web search tools if available
2. Use any specialized tools provided for the task
3. Log each search as an event with step_id

```bash
python telnyx_api.py log-event <mission_id> <run_id> tool_call "Searching for window washing contractors in Chicago" "research" '{"tool": "WebSearch", "query": "commercial window washing contractors Chicago"}'
```

---

## Phase 4: Scheduling Calls/SMS

### Business Hours Consideration

**CRITICAL**: Before scheduling calls, consider business hours:
- Typical business hours: 9 AM - 5 PM local time
- If current time is outside business hours, schedule for next business day
- `scheduled_at_fixed_datetime` must be in the future (at least 1 minute from now)

### Step 4.1: Schedule a Phone Call

```bash
python telnyx_api.py schedule-call <assistant_id> "+15551234567" "+15559876543" "2024-12-01T14:30:00Z" <mission_id> <run_id> <step_id>
```

**Save the returned `scheduled_event_id`**.

### Step 4.2: Schedule an SMS

```bash
python telnyx_api.py schedule-sms <assistant_id> "+15551234567" "+15559876543" "2024-12-01T14:30:00Z" "Hi! I am reaching out on behalf of [COMPANY] to inquire about your window cleaning rates for commercial buildings. Could you share your pricing?" <mission_id> <run_id> <step_id>
```

### Step 4.3: Log Each Scheduled Event

```bash
python telnyx_api.py log-event <mission_id> <run_id> custom "Scheduled call to ABC Window Cleaning for 2:30 PM" "calls" '{"scheduled_event_id": "<event_id>", "contractor": "ABC Window Cleaning", "phone": "+15551234567", "scheduled_for": "2024-12-01T14:30:00Z"}'
```

---

## Phase 5: Monitoring Call Completion

After a call is scheduled, you need to poll for completion.

### Step 5.1: Check Scheduled Event Status

```bash
python telnyx_api.py get-event <assistant_id> <scheduled_event_id>
```

### Event Status Values

The event-level `status` tracks the overall lifecycle:

| Status | Meaning | Action |
|--------|---------|--------|
| `pending` | Waiting for scheduled time | Wait and check again later |
| `in_progress` | Call/SMS in progress | Check again in a few minutes |
| `completed` | Finished successfully | Get conversation_id, fetch insights |
| `failed` | Failed after retries | Consider rescheduling |

### Call Status Values (Phone Calls Only)

The `call_status` field provides the telephony-level outcome. **This is the most important field for deciding what to do next.**

| call_status | Meaning | Action |
|-------------|---------|--------|
| `ringing` | Phone is ringing, not yet answered | Still in progress — wait and poll again in 1-2 minutes |
| `in-progress` | Call is active, conversation ongoing | Still in progress — poll again in 2-3 minutes |
| `completed` | Call connected and finished normally | Success — get `conversation_id`, fetch insights |
| `no-answer` | Phone rang but nobody picked up | **Retryable** — reschedule for a different time |
| `busy` | Line is busy | **Retryable** — reschedule in 10-15 minutes, line may free up quickly |
| `canceled` | Call was canceled | Check if you canceled it; if not, may need to reschedule |
| `failed` | Call failed (network/system error) | **Retryable** — reschedule after a short backoff (5-10 minutes) |

### Step 5.2: Polling Strategy

**When to start polling**: A few minutes after `scheduled_at_fixed_datetime`

**Polling intervals based on call_status**:
- `ringing` → poll again in 1-2 minutes (call may connect any moment)
- `in-progress` → poll again in 2-3 minutes (conversation is happening)
- `pending` (event status) → poll every 5 minutes until scheduled time passes
- `no-answer` / `busy` / `failed` → stop polling, handle retry immediately
- `completed` → done, proceed to fetch insights

**Timeout**: If still `ringing` or `in-progress` after 30 minutes, treat as failed.

### Step 5.3: Handle Retryable Call Statuses

When `call_status` is `no-answer`, `busy`, or `failed`, the call can be retried:

1. Update the call tracker with the failure reason
2. Check retry count — by default, retry up to 3 times (unless user specifies otherwise)
3. Schedule a retry at a different time based on the failure type:
   - `busy` → retry in 10-15 minutes (line may free up)
   - `no-answer` → retry in 30 minutes to 2 hours (try a different time of day)
   - `failed` → retry in 5-10 minutes (transient error)
4. Log the failure and retry as events

```bash
# Update tracker
python telnyx_api.py save-memory "<slug>" "call_tracker" '{"+15551234567": {"status": "no_answer", "attempts": 1, "call_status": "no-answer", "next_retry": "2024-12-02T10:00:00Z"}}'

# Log the failure
python telnyx_api.py log-event "$MISSION_ID" "$RUN_ID" custom "Call not answered (busy), scheduling retry #2" "calls" '{"phone": "+15551234567", "call_status": "busy", "attempt": 1}'

# Schedule retry
python telnyx_api.py schedule-call <assistant_id> "+15551234567" "+15559876543" "2024-12-02T10:00:00Z" <mission_id> <run_id> <step_id>
```

---

## Phase 6: Getting Conversation Insights

Once a call completes and you have a `conversation_id`, retrieve the conversation insights.

**IMPORTANT**: Always use insights to get the call summary. Do NOT fetch raw conversation messages - insights provide a structured summary of the conversation outcome.

### Step 6.1: Get Conversation Insights

```bash
python telnyx_api.py get-insights <conversation_id>
```

### Step 6.2: Poll Until Insight is Complete

The insight may not be immediately ready after the call ends. **You must poll until the insight status is "completed".**

**Polling strategy:**
- Check immediately after getting the `conversation_id`
- If status is NOT "completed", wait 10 seconds and retry
- Continue polling until status is "completed" or 20 minutes have passed
- Only use the insight data when status is "completed"

**Example polling flow:**
```bash
# First attempt
python telnyx_api.py get-insights "conv_xyz"
# Output: Insight status: in_progress

# Wait 10 seconds, try again
python telnyx_api.py get-insights "conv_xyz"
# Output: Insight status: in_progress

# Wait 10 seconds, try again
python telnyx_api.py get-insights "conv_xyz"
# Output: Insight: Customer quoted $350 for a 10-story building...
```

### Step 6.3: Rename the Conversation

By default, all conversations are named "Voice Assistant Conversation" which is useless for reviewing in the portal. **Rename each conversation** with a meaningful label after it completes:

```bash
python telnyx_api.py rename-conversation "<conv_id>" "Call 1: ABC Window Cleaning - $350/visit"
```

**Best practice:** Include the call sequence number, target name, and key outcome in the name. This makes conversations instantly identifiable when browsing the mission in the Telnyx portal.

### Step 6.4: Log the Insight

```bash
python telnyx_api.py log-event <mission_id> <run_id> custom "Call completed with ABC Window Cleaning - quoted $350" "calls" '{"conversation_id": "<conv_id>", "contractor": "ABC Window Cleaning", "outcome": "success", "quote": "$350", "availability": "next week", "notes": "Willing to negotiate for recurring contracts"}'
```

---

## Phase 7: Complete the Mission

### ⚠️ MANDATORY Completion Checklist

**Every mission MUST complete ALL of these before it can be considered done.** Skipping any of these leaves the mission in a broken state in the Telnyx portal.

| # | Action | Command | Why |
|---|--------|---------|-----|
| 1 | **Update ALL plan step statuses** | `update-step <mid> <rid> <step_id> completed` | Steps show in portal — "pending" means it looks unfinished |
| 2 | **Log events for every completed call** | `log-event ... custom "Call completed" <step_id> '{"conversation_id": "..."}'` | Creates audit trail linking conversations to the mission |
| 3 | **Set run result_summary** | Included in `complete` or `update-run` | Human-readable summary visible in portal |
| 4 | **Set run result_payload** | Included in `complete` or `update-run` | Structured data for programmatic consumption |
| 5 | **Mark run as succeeded/failed** | `complete` or `update-run <mid> <rid> succeeded` | Closes the run |

**The `complete` command handles #3, #4, and #5 in one call** — but you still need to do #1 and #2 yourself.

```bash
# 1. Update every step status (do this THROUGHOUT the mission, not just at the end)
python telnyx_api.py update-step <mission_id> <run_id> "setup" "completed"
python telnyx_api.py update-step <mission_id> <run_id> "calls" "completed"
python telnyx_api.py update-step <mission_id> <run_id> "analyze" "completed"

# 2. Complete the run with summary and payload
python telnyx_api.py complete "<slug>" <mission_id> <run_id> \
  "Contacted 5 contractors, received 4 quotes. Best: ABC Cleaning ($350)" \
  '{"contractors_contacted": 5, "quotes_received": 4, "recommended": [{"name": "ABC Cleaning", "quote": 350}]}'
```

**Common mistake:** Logging `step_completed` events but NOT calling `update-step`. These are separate — events are the audit log, step status is the progress tracker. You need BOTH.

### Step 7.1: Analyze Results

After all calls complete:
1. Compare quotes and outcomes
2. Select best options based on criteria
3. Prepare summary for user

### Step 7.2: Complete the Run

Use the `complete` command to set result_summary, result_payload, mark the run as succeeded, and clean up state in one step:

```bash
python telnyx_api.py complete "find-window-washing-contractors" <mission_id> <run_id> "Contacted 5 contractors, received 4 quotes. Best options: ABC Cleaning ($350) and XYZ Windows ($380)." '{"contractors_contacted": 5, "quotes_received": 4, "recommended": [{"name": "ABC Cleaning", "quote": 350}, {"name": "XYZ Windows", "quote": 380}]}'
```

Or use `update-run` directly with all fields:

```bash
python telnyx_api.py update-run <mission_id> <run_id> succeeded "Human-readable summary here" '{"structured": "payload"}'
```

The `complete` command also removes the mission from the state file.

---

# Event Logging Reference

**Log EVERY action as an event for complete audit trail.** Events are stored in the cloud and provide permanent backup even if local files are lost.

## CRITICAL: Update Step Status (Not Just Events!)

**You MUST update the plan step status via `update-step` when starting or completing each step.** Logging events alone does NOT change the step status — the client tracks progress by looking at step statuses, not events.

```bash
# When STARTING a step:
python telnyx_api.py update-step "$MISSION_ID" "$RUN_ID" "research" "in_progress"
python telnyx_api.py log-event "$MISSION_ID" "$RUN_ID" step_started "Starting: Research contractors" "research"

# When COMPLETING a step:
python telnyx_api.py update-step "$MISSION_ID" "$RUN_ID" "research" "completed"
python telnyx_api.py log-event "$MISSION_ID" "$RUN_ID" step_completed "Completed: Research contractors" "research"

# When a step FAILS:
python telnyx_api.py update-step "$MISSION_ID" "$RUN_ID" "calls" "failed"
python telnyx_api.py log-event "$MISSION_ID" "$RUN_ID" error "Failed: Could not reach any contractors" "calls"

# To SKIP a step:
python telnyx_api.py update-step "$MISSION_ID" "$RUN_ID" "setup" "skipped"
```

**Always call `update-step` BEFORE `log-event`** — this ensures the step status is correct even if the event logging fails.

## IMPORTANT: step_id is Required

**step_id is a required parameter** - it links events to your plan steps, enabling tracking of which activities belong to which phase.

```bash
# With step_id (links to plan step)
python telnyx_api.py log-event "$MISSION_ID" "$RUN_ID" custom "Found contractor" "research" '{"name": "ABC"}'

# Use "-" if event doesn't belong to a specific step
python telnyx_api.py log-event "$MISSION_ID" "$RUN_ID" custom "General note" "-" '{"note": "value"}'
```

The step_id should match one of the step_id values from your plan (e.g., "research", "setup", "calls", "analyze").

| Action | Step Status Update | Event Type | step_id | Example Summary |
|--------|-------------------|------------|---------|-----------------|
| Starting a plan step | `update-step ... in_progress` | `step_started` | step_id | "Starting: Research contractors" |
| Completing a step | `update-step ... completed` | `step_completed` | step_id | "Completed: Research contractors" |
| Step failed | `update-step ... failed` | `error` | step_id | "Failed: Could not reach contractors" |
| Web search | — | `tool_call` | "research" | "Searching for window cleaning contractors" |
| Creating assistant | — | `custom` | "setup" | "Created voice assistant: ast_123" |
| Scheduling call | — | `custom` | "calls" | "Scheduled call to ABC for 2:30 PM" |
| Call completed | — | `custom` | "calls" | "Call completed with ABC - got quote $350" |
| Call failed | — | `error` | "calls" | "Call to XYZ not answered after 3 attempts" |
| Decision made | — | `message` | "analyze" | "Selected ABC and XYZ as top choices" |

---

# Quick Reference: All Commands

```bash
# Check setup
python telnyx_api.py check-key

# Missions
python telnyx_api.py create-mission <name> <instructions>
python telnyx_api.py get-mission <mission_id>
python telnyx_api.py list-missions

# Runs
python telnyx_api.py create-run <mission_id> <input_json>
python telnyx_api.py get-run <mission_id> <run_id>
python telnyx_api.py update-run <mission_id> <run_id> <status>
python telnyx_api.py list-runs <mission_id>

# Plan
python telnyx_api.py create-plan <mission_id> <run_id> <steps_json>
python telnyx_api.py get-plan <mission_id> <run_id>
python telnyx_api.py update-step <mission_id> <run_id> <step_id> <status>
# status: pending, in_progress, completed, skipped, failed

# Events (step_id is REQUIRED - use "-" if no specific step)
python telnyx_api.py log-event <mission_id> <run_id> <type> <summary> <step_id> [payload_json]
python telnyx_api.py list-events <mission_id> <run_id>

# Assistants
python telnyx_api.py list-assistants [--name=<filter>] [--page=<n>] [--size=<n>]
python telnyx_api.py create-assistant <name> <instructions> <greeting> [options_json]
python telnyx_api.py get-assistant <assistant_id>
python telnyx_api.py update-assistant <assistant_id> <updates_json>
python telnyx_api.py get-connection-id <assistant_id> [telephony|messaging]

# Phone Numbers
python telnyx_api.py list-phones [--available]
python telnyx_api.py get-available-phone
python telnyx_api.py assign-phone <phone_id> <connection_id> [voice|sms]

# Scheduled Events
python telnyx_api.py schedule-call <assistant_id> <to_phone> <from_phone> <datetime> <mission_id> <run_id> [step_id] [dynamic_variables_json]
python telnyx_api.py schedule-sms <assistant_id> <to_phone> <from_phone> <datetime> <text> [mission_id] [mission_run_id] [step_id] [dynamic_variables_json]
python telnyx_api.py get-event <assistant_id> <event_id>
python telnyx_api.py cancel-scheduled-event <assistant_id> <event_id>
python telnyx_api.py list-events-assistant <assistant_id>

# Insights (conversation results - POLL until status is "completed"!)
python telnyx_api.py get-insights <conversation_id>
python telnyx_api.py rename-conversation <conversation_id> <name>

# Insight Templates (CRUD)
python telnyx_api.py create-insight <name> <instructions> [options_json]  # options: json_schema, webhook
python telnyx_api.py get-insight <insight_id>
python telnyx_api.py list-insights
python telnyx_api.py update-insight <insight_id> <updates_json>

# Insight Groups
python telnyx_api.py create-insight-group <name> [options_json]  # options: description, webhook
python telnyx_api.py get-insight-group <group_id>
python telnyx_api.py list-insight-groups
python telnyx_api.py update-insight-group <group_id> <updates_json>
python telnyx_api.py assign-insight <group_id> <insight_id>
python telnyx_api.py unassign-insight <group_id> <insight_id>

# Mission Run Agents (linking agents to runs)
python telnyx_api.py link-agent <mission_id> <run_id> <telnyx_agent_id>
python telnyx_api.py list-linked-agents <mission_id> <run_id>
python telnyx_api.py unlink-agent <mission_id> <run_id> <telnyx_agent_id>

# State Management
python telnyx_api.py list-state
python telnyx_api.py get-state <slug>
python telnyx_api.py remove-state <slug>

# Memory (SAVE OFTEN!)
python telnyx_api.py save-memory <slug> <key> <value_json>
python telnyx_api.py get-memory <slug> [key]
python telnyx_api.py append-memory <slug> <key> <item_json>

# High-Level Workflows
python telnyx_api.py init <name> <instructions> <request> [steps_json]
python telnyx_api.py setup-agent <slug> <name> <instructions> <greeting>
python telnyx_api.py complete <slug> <mission_id> <run_id> <summary> [payload_json]
```

---

# Complete Example: Window Washing Contractors

```bash
# 1. Initialize the mission (creates mission, run, plan, sets to running)
python telnyx_api.py init "Find window washing contractors" \
  "Find contractors in Chicago, call them, negotiate rates, select best two" \
  "Find me window washing contractors in Chicago" \
  '[{"step_id": "research", "description": "Find contractors online", "sequence": 1}, {"step_id": "setup", "description": "Create voice agent", "sequence": 2}, {"step_id": "calls", "description": "Schedule and make calls", "sequence": 3}, {"step_id": "analyze", "description": "Analyze results", "sequence": 4}]'

# Output: Created mission: mis_abc123
#         Created run: run_def456

# 2. Get the mission slug and IDs from state
python telnyx_api.py get-state "find-window-washing-contractors"

# 3. Mark research step as in_progress and start working
python telnyx_api.py update-step "mis_abc123" "run_def456" "research" "in_progress"
python telnyx_api.py log-event "mis_abc123" "run_def456" step_started "Starting: Find contractors online" "research"

# 4. Setup voice agent (creates assistant, links to run, assigns phone number)
python telnyx_api.py update-step "mis_abc123" "run_def456" "setup" "in_progress"
python telnyx_api.py setup-agent "find-window-washing-contractors" \
  "Contractor Caller" \
  "You are calling to get quotes for commercial window washing. Ask about: rates per floor, availability, insurance. Be professional." \
  "Hi, I am calling to inquire about your commercial window washing services. Do you have a moment to discuss rates?"

# Output: Created assistant: ast_xyz789
#         Linked agent ast_xyz789 to run run_def456
#         Found available: +15559876543
#         Assigned phone number 123456

python telnyx_api.py update-step "mis_abc123" "run_def456" "setup" "completed"
python telnyx_api.py log-event "mis_abc123" "run_def456" step_completed "Completed: Voice agent setup" "setup"

# 5. Get agent phone from state
AGENT_PHONE=$(python telnyx_api.py get-state "find-window-washing-contractors" | python -c "import sys,json; print(json.load(sys.stdin).get('agent_phone',''))")
ASSISTANT_ID=$(python telnyx_api.py get-state "find-window-washing-contractors" | python -c "import sys,json; print(json.load(sys.stdin).get('assistant_id',''))")

# 6. After research, SAVE to memory AND log events with step_id (CRITICAL!)
python telnyx_api.py append-memory "find-window-washing-contractors" "contractors_found" '{"name": "ABC Cleaning", "phone": "+13125551234", "source": "web search"}'
python telnyx_api.py log-event "mis_abc123" "run_def456" custom "Found contractor: ABC Cleaning" "research" '{"name": "ABC Cleaning", "phone": "+13125551234"}'

python telnyx_api.py append-memory "find-window-washing-contractors" "contractors_found" '{"name": "XYZ Windows", "phone": "+13125555678", "source": "web search"}'
python telnyx_api.py log-event "mis_abc123" "run_def456" custom "Found contractor: XYZ Windows" "research" '{"name": "XYZ Windows", "phone": "+13125555678"}'

# 7. Complete research step, start calls step
python telnyx_api.py update-step "mis_abc123" "run_def456" "research" "completed"
python telnyx_api.py log-event "mis_abc123" "run_def456" step_completed "Completed: Found 2 contractors" "research"
python telnyx_api.py update-step "mis_abc123" "run_def456" "calls" "in_progress"
python telnyx_api.py log-event "mis_abc123" "run_def456" step_started "Starting: Schedule calls" "calls"

# 8. Schedule calls
python telnyx_api.py schedule-call "$ASSISTANT_ID" "+13125551234" "$AGENT_PHONE" "2024-12-01T15:00:00Z" "$MISSION_ID" "$RUN_ID" "$STEP_ID"

# Output: Scheduled call: evt_abc123

# 9. SAVE scheduled event to memory AND log event with step_id (CRITICAL!)
python telnyx_api.py append-memory "find-window-washing-contractors" "calls_scheduled" '{"event_id": "evt_abc123", "contractor": "ABC Cleaning", "scheduled_for": "2024-12-01T15:00:00Z"}'
python telnyx_api.py log-event "mis_abc123" "run_def456" custom "Scheduled call to ABC Cleaning for 3:00 PM" "calls" '{"scheduled_event_id": "evt_abc123", "contractor": "ABC Cleaning"}'

# 10. Poll for completion (after scheduled time)
python telnyx_api.py get-event "$ASSISTANT_ID" "evt_abc123"

# Output: Status: completed, conversation_id: conv_xyz

# 11. Get insights - POLL UNTIL STATUS IS "completed"
python telnyx_api.py get-insights "conv_xyz"
# Output: Insight status: in_progress
# (wait 10 seconds and retry)

python telnyx_api.py get-insights "conv_xyz"
# Output: Insight status: in_progress
# (wait 10 seconds and retry)

python telnyx_api.py get-insights "conv_xyz"
# Output: Insight: Customer quoted $350 for a 10-story building. Available next week.
# (status is now "completed" - proceed with the insight data)

# 12. SAVE call results to memory AND log event with step_id (CRITICAL!)
python telnyx_api.py save-memory "find-window-washing-contractors" "call_results" '{"ABC Cleaning": {"status": "completed", "conversation_id": "conv_xyz", "quote": 350, "availability": "next week"}}'
python telnyx_api.py log-event "mis_abc123" "run_def456" custom "Call completed: ABC Cleaning quoted $350, available next week" "calls" '{"contractor": "ABC Cleaning", "quote": 350, "conversation_id": "conv_xyz"}'

# 13. Complete calls step, start analyze step
python telnyx_api.py update-step "mis_abc123" "run_def456" "calls" "completed"
python telnyx_api.py log-event "mis_abc123" "run_def456" step_completed "Completed: All calls done" "calls"
python telnyx_api.py update-step "mis_abc123" "run_def456" "analyze" "in_progress"
python telnyx_api.py log-event "mis_abc123" "run_def456" step_started "Starting: Analyze results" "analyze"

# 14. Complete the mission (mark analyze step done first)
python telnyx_api.py update-step "mis_abc123" "run_def456" "analyze" "completed"
python telnyx_api.py log-event "mis_abc123" "run_def456" step_completed "Completed: Analysis done" "analyze"
python telnyx_api.py complete "find-window-washing-contractors" "mis_abc123" "run_def456" \
  "Found 2 best contractors: ABC ($350) and XYZ ($380)" \
  '{"recommended": ["ABC Cleaning", "XYZ Windows"]}'

# Output: Updated run run_def456: succeeded
#         Mission 'find-window-washing-contractors' completed successfully
```

---

# ⚠️ BEFORE CREATING ANYTHING: Review Existing Resources

**Always check what already exists before creating new assistants, insights, or insight groups.** Reuse is better than duplication.

### Pre-flight Checklist

Run these commands at the start of every mission to inventory what's available:

```bash
# 1. Search for existing assistants by name — maybe one already fits your use case
python telnyx_api.py list-assistants --name=Weather
python telnyx_api.py list-assistants  # or list all (paginated)
python telnyx_api.py list-assistants --page=2  # next page

# 2. List existing insight templates — reuse structured insights across missions
python telnyx_api.py list-insights

# 3. List existing insight groups — you may only need to add an insight to an existing group
python telnyx_api.py list-insight-groups

# 4. List available phone numbers — check what's already assigned vs free
python telnyx_api.py list-phones --available
```

**All list commands are paginated.** If you have many resources, page through with `--page=N`. The assistant name filter does substring matching — use it to quickly find relevant assistants instead of scrolling through pages.

### Decision Flow

#### ⚠️ CRITICAL: Reuse Without Modification

**The rule is: reuse existing resources IF you can use them as-is. Do NOT modify existing assistants, insights, or insight groups that may be in use by other missions or users.** Editing a shared resource (e.g., changing an assistant's instructions or an insight's schema) can silently break unrelated workflows that depend on the current configuration.

**Safe to reuse without modification:**
- An existing assistant whose instructions, tools, voice, and settings already match your needs exactly
- An existing insight template whose schema/instructions already extract what you need
- The default "Summary" insight (always reuse this — never recreate it)
- An existing insight group that already contains the insights you need

**When to create new instead of reusing:**
- You need different instructions, tools, voice, or model → **create a new assistant**
- You need a different extraction schema → **create a new insight template**
- You need a different combination of insights → **create a new insight group**
- The existing resource is "close but needs tweaks" → **create new, don't modify the existing one**

**For dynamic context between calls** (e.g., Class 3 Sequential Negotiation, where you inject "best quote so far" into each call), use **dynamic variables passed via the scheduled events API** rather than modifying the assistant. Define variable placeholders in the assistant's instructions (e.g., `{{best_quote}}`) and pass the values at schedule time. This keeps the assistant immutable while varying context per call.

1. **Assistants:** Search for existing assistants. If one matches your

…(truncated)
