{"slug": "build-an-auditable-ai-phone-workflow-with-telnyx-missions", "title": "Build an Auditable AI Phone Workflow with Telnyx Missions", "summary": "Telnyx published an open-source Python sample, missions-audit, that builds an auditable AI phone workflow on its AI Missions API. The sample creates a durable mission, starts a run, submits an ordered three-step plan, advances step statuses with typed events, optionally schedules an AI-assisted phone call, and renders a timestamped audit report from fresh server state. The design treats the audit trail as part of the workflow's API contract rather than something reconstructed later from process logs.", "body_md": "An AI phone workflow can return a successful response and still leave a difficult question unanswered: **what actually happened?**\n\nIf a clinic says an assistant called a patient, confirmed an appointment, and completed the outreach plan, a reviewer may need more than application logs. They need the objective, the approved plan, every observed state transition, the assistant associated with the run, and the final result in one durable record.\n\nThe open-source [`missions-audit`](https://github.com/team-telnyx/telnyx-code-examples/tree/main/missions-audit) sample shows one way to build that record with Python and Telnyx AI Missions. It creates a mission, starts a run, submits a plan, advances step statuses, logs typed events, optionally schedules a real AI-assisted phone call, and renders the resulting ledger as a timestamped audit report.\n\nThe important idea is simple: **the audit trail is part of the workflow's API contract, not a story reconstructed later from process logs.**\n\nA mission is the durable objective. A run is one execution of that objective. Each run has an ordered plan, and each plan contains steps whose statuses change as work progresses.\n\nThe sample combines five related resources:\n\n| Resource | Purpose | \n|---|---|\n| Mission | Stores the durable objective | \n| Run | Represents one execution and its final result | \n| Plan and steps | Describe the intended work and its progress | \n| Typed events | Record the chronological audit timeline | \n| Agent link | Identifies the Telnyx AI assistant associated with the run | \n\nThis produces a queryable record of what was planned, what the worker observed, and how the run ended.\n\nThe project separates the workflow into five small programs:\n\n`create_mission.py` creates the durable mission.`start_run.py` starts one execution of that mission.`plan_steps.py` submits an ordered three-step plan.`execute.py` advances the run, steps, and event timeline.`audit.py` reads fresh server state and renders the report.\nThe default plan is:\n\n```\nprovision_assistant\nschedule_outreach_call\nverify_outcome\n```\n\nThose names describe the live workflow, but the project is safe by default. More on that distinction shortly.\n\nThe sample uses raw HTTP requests against `https://api.telnyx.com/v2`. Missions responses are wrapped in a `data` envelope and use identifiers such as `mission_id` and `run_id`.\n\nCreating the durable objective starts with `POST /ai/missions`:\n\n```\nmission = post(\"/ai/missions\", {\n    \"name\": \"Clinic outreach audit\",\n    \"description\": \"Verify clinic contacts can be reached\",\n    \"instructions\": (\n        \"Verify the contact is reachable, confirm the appointment \"\n        \"slot briefly, and stay polite.\"\n    ),\n    \"execution_mode\": \"external\",\n})\n```\n\nThe worker is external, so the application controls execution while the Missions API keeps the durable record.\n\nOne execution is created with:\n\n```\nrun = post(f\"/ai/missions/{mission_id}/runs\", {})\n```\n\nThe run begins in `pending`. The worker can advance it through `running` to `succeeded` or `failed`. The API also supports `paused` and `cancelled` states for operator intervention.\n\nThe plan uses client-selected `step_id` values, descriptions, and one-based sequence numbers:\n\n```\nsteps = [\n    {\n        \"step_id\": \"provision_assistant\",\n        \"description\": \"Create or select the outreach assistant.\",\n        \"sequence\": 1,\n    },\n    {\n        \"step_id\": \"schedule_outreach_call\",\n        \"description\": \"Create one scheduled phone-call event.\",\n        \"sequence\": 2,\n    },\n    {\n        \"step_id\": \"verify_outcome\",\n        \"description\": \"Confirm the event reached a terminal status.\",\n        \"sequence\": 3,\n    },\n]\n\npost(f\"/ai/missions/{mission_id}/runs/{run_id}/plan\", {\"steps\": steps})\n```\n\nSteps move from `pending` to `in_progress`, then to `completed` or `failed`. A `skipped` state is available for intentional no-ops.\n\nThe most useful pattern in the sample is not an endpoint. It is a rule enforced by `execute.py`:\n\nRecord every state change the worker observes, and never invent a platform outcome.\n\nWhen a step starts, the worker updates the step and writes a `step_started` event. When it completes, the worker writes the corresponding status and `step_completed` event. Failures become both a failed step and a `step_failed` event.\n\n``` python\ndef run_step(step_id, work):\n    update_step(step_id, \"in_progress\")\n    log_event(\"step_started\", f\"Step {step_id} started\", step_id)\n\n    try:\n        result = work()\n    except Exception as exc:\n        update_step(step_id, \"failed\")\n        log_event(\n            \"step_failed\",\n            f\"Step {step_id} failed: {exc}\",\n            step_id,\n        )\n        raise\n\n    update_step(step_id, \"completed\")\n    log_event(\"step_completed\", f\"Step {step_id} completed\", step_id)\n    return result\n```\n\nIn live mode, the worker polls the scheduled-event API every five seconds, with a hard ten-minute cap. Each newly observed event status becomes a `status_change` mission event. A request failure or timeout becomes an explicit `error` event and causes the verification step to fail.\n\nThat means the ledger never reports that a call completed merely because the worker expected it to complete.\n\n`DEMO_MODE=true` is the default.\n\nIn demo mode, the sample exercises the mission, run, plan, step, and event APIs without creating an assistant, creating a scheduled event, placing a call, or linking an agent. It records a custom event with this meaning:\n\n```\n{\n  \"demo_mode\": true,\n  \"note\": \"no assistant created, no call placed\"\n}\n```\n\nThe run's result summary says the same thing. The simulated walk is useful for learning the ledger mechanics, but it cannot be mistaken for evidence of a real phone call.\n\nSet `DEMO_MODE=false` to exercise the live path. Live mode requires placeholder-backed environment variables for a destination you control and a phone number attached to the assistant:\n\n```\nTELNYX_API_KEY=YOUR_TELNYX_API_KEY\nDEMO_MODE=false\nDESTINATION_NUMBER=<E164_TEST_NUMBER>\nASSISTANT_PHONE_NUMBER=<E164_ASSISTANT_NUMBER>\n```\n\nThe live worker can create or reuse an assistant, schedule the phone-call event, poll its status, record the latest call attempt, and link the assistant to the mission run. The Assistants and scheduled-events APIs return bare JSON objects rather than the Missions API's `data` envelope, so the sample handles those response shapes separately.\n\n`audit.py` waits for the run to reach `succeeded`, `failed`, or `cancelled`, with the same ten-minute safety cap. It then performs fresh reads of the mission, run, plan, events, and linked agents.\n\nThe report is therefore built from the server's current view rather than from the worker's in-memory state:\n\n```\nMISSIONS AUDIT REPORT\n\nMission\n  mission_id: ...\n  name: Clinic outreach audit\n\nRun\n  status: succeeded\n  result_summary: ...\n\nSteps\n  provision_assistant     completed\n  schedule_outreach_call  completed\n  verify_outcome          completed\n\nEvent timeline\n  step_started\n  step_completed\n  status_change\n  custom\n\nLinked Telnyx agents\n  run_id=... telnyx_agent_id=...\n```\n\nIf the report reaches its polling cap before the run becomes terminal, it prints a warning, performs a final fresh read, renders the current state, and exits unsuccessfully. It does not silently present an unfinished run as complete.\n\nClone the repository and install its dependencies:\n\n```\ngit clone https://github.com/team-telnyx/telnyx-code-examples.git\ncd telnyx-code-examples/missions-audit\n\npython -m venv .venv\nsource .venv/bin/activate\npip install -r requirements.txt\n\ncp .env.example .env\n```\n\nAdd your Telnyx API key to `.env`. Keep `DEMO_MODE=true` for the side-effect-free walkthrough.\n\nRun the included smoke test first:\n\n```\npytest smoke_test.py -q\n```\n\nThen run the workflow, passing the identifiers printed by each step:\n\n```\npython create_mission.py\npython start_run.py --mission-id <MISSION_ID>\npython plan_steps.py --mission-id <MISSION_ID> --run-id <RUN_ID>\npython execute.py --mission-id <MISSION_ID> --run-id <RUN_ID>\npython audit.py --mission-id <MISSION_ID> --run-id <RUN_ID>\n```\n\nFor live mode, use only test numbers you control and configure the required assistant phone number before running `execute.py`.\n\nThe clinic example is only one use case. The same structure works anywhere an automated workflow must be replayable:\n\nThe reusable design is a durable objective, an explicit plan, typed events, independently queryable state, and a final report assembled from fresh reads.\n\nAn audit trail is most useful when it is designed before the first action runs. With Missions, plans, typed events, and agent linkage treated as API resources, \"what happened?\" becomes a query instead of an investigation.", "url": "https://wpnews.pro/news/build-an-auditable-ai-phone-workflow-with-telnyx-missions", "canonical_source": "https://dev.to/sonam_gupta_dba00ec9473e5/build-an-auditable-ai-phone-workflow-with-telnyx-missions-1g7e", "published_at": "2026-10-21 18:26:34+00:00", "updated_at": "2026-10-21 18:40:03.274568+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "ai-products"], "entities": ["Telnyx", "Telnyx AI Missions", "missions-audit", "GitHub"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/build-an-auditable-ai-phone-workflow-with-telnyx-missions", "markdown": "https://wpnews.pro/news/build-an-auditable-ai-phone-workflow-with-telnyx-missions.md", "text": "https://wpnews.pro/news/build-an-auditable-ai-phone-workflow-with-telnyx-missions.txt", "jsonld": "https://wpnews.pro/news/build-an-auditable-ai-phone-workflow-with-telnyx-missions.jsonld"}}