--- name: edge-merge-shift-coverage title: "Edge Merge Shift Coverage" description: "Manager texts need a closer tonight." language: python framework: flask telnyx_products: [Edge Compute, Voice, Messaging] integrations: [Merge HRIS] channel: [voice, sms] --- # Edge Merge Shift Coverage > Also known as: shift coverage, schedule filling, employee availability, shift management. Manager texts need a closer tonight. Edge worker checks HRIS schedule via Merge, calls available employees in priority order, negotiates, confirms via SMS to both parties. ## Why Telnyx This example runs SMS, outbound voice calls, and AI inference over Telnyx's **AI Communications Infrastructure** -- a single private global network where messaging, programmable voice, and AI live together instead of being stitched across separate vendors. The inbound manager text, the priority-ordered employee calls with TTS negotiation, and the confirmation SMS to both parties all flow through one Telnyx account and one set of credentials, so a shift-coverage workflow stays low-latency and easy to operate end to end. ## Telnyx API Endpoints Used - **Call Control: Gather Using Speak**: `POST /v2/calls/{id}/actions/gather_using_speak` -- [API reference](https://developers.telnyx.com/api/call-control/gather-using-speak) - **Call Control: Speak (TTS)**: `POST /v2/calls/{id}/actions/speak` -- [API reference](https://developers.telnyx.com/api/call-control/speak) - **Call Control: Hangup**: `POST /v2/calls/{id}/actions/hangup` -- [API reference](https://developers.telnyx.com/api/call-control/hangup) - **AI Inference**: `POST /v2/ai/chat/completions` -- [API reference](https://developers.telnyx.com/api/inference/chat-completions) - **Send Message (SMS)**: `POST /v2/messages` -- [API reference](https://developers.telnyx.com/api/messaging/send-message) - **Create Outbound Call**: `POST /v2/calls` -- [API reference](https://developers.telnyx.com/api/call-control/create-call) ## Telnyx Webhook Events This app handles these webhook events ([Call Control docs](https://developers.telnyx.com/docs/api/v2/call-control)): - `call.answered` -- Call connected, app begins interaction with greeting - `call.gather.ended` -- Caller input received (speech or DTMF), app processes response - `call.speak.ended` -- TTS playback finished, transitions to next action - `call.hangup` -- Call ended, cleans up session state and logs outcome ## Environment Variables Copy `.env.example` to `.env` and fill in: | Variable | Type | Example | Required | Description | Where to get it | |----------|------|---------|----------|-------------|-----------------| | `TELNYX_API_KEY` | `string` | `KEY0123456789` | **yes** | Telnyx API v2 key | [Portal](https://portal.telnyx.com/api-keys) | | `TELNYX_PHONE_NUMBER` | `string` | `+18005551234` | **yes** | Telnyx phone number | [Portal](https://portal.telnyx.com/numbers/my-numbers) | | `TELNYX_CONNECTION_ID` | `string` | `149440475714` | **yes** | Call Control app ID | [Portal](https://portal.telnyx.com/call-control/applications) | | `MERGE_API_KEY` | `string` | `your-key` | **yes** | Merge.dev API key | [Merge](https://app.merge.dev/keys) | | `MERGE_ACCOUNT_TOKEN` | `string` | `your-token` | **yes** | Merge linked account token | [Merge](https://app.merge.dev) | | `AI_MODEL` | `string` | `moonshotai/Kimi-K2.6` | no | AI model | [Models](https://developers.telnyx.com/docs/inference/models) | | `PORT` | `integer` | `5000` | no | Server port | -- | ## Setup ```bash git clone https://github.com/team-telnyx/telnyx-code-examples.git cd telnyx-code-examples/edge-merge-shift-coverage-python cp .env.example .env pip install -r requirements.txt python app.py ``` ### Webhook Configuration 1. Expose your local server: `ngrok http 5000` 2. Configure in [Telnyx Portal](https://portal.telnyx.com/call-control/applications): - Call Control Application -> Webhook URL -> `https://.ngrok.io/webhooks/voice` ## API Reference ### `GET /shifts` Shifts. ```bash curl http://localhost:5000/shifts ``` **Response:** ```json {"status": "ok", "service": "edge-merge-shift-coverage"} ``` ### `GET /health` Health. ```bash curl http://localhost:5000/health ``` **Response:** ```json {"status": "ok", "service": "edge-merge-shift-coverage"} ``` ## Webhook Endpoints ### `POST /webhooks/voice` Telnyx sends call events here. Your app processes them and responds with the next action. **Events handled:** - `call.answered` -- Call connected, app begins interaction with greeting - `call.gather.ended` -- Caller input received (speech or DTMF), app processes response - `call.speak.ended` -- TTS playback finished, transitions to next action - `call.hangup` -- Call ended, cleans up session state and logs outcome **Example payload:** ```json { "data": { "event_type": "call.initiated", "id": "evt_123", "payload": { "call_control_id": "v3:abc123", "direction": "incoming", "from": "+12125551234", "to": "+18005559876" } } } ``` ## All Endpoints | Method | Path | Purpose | |--------|------|---------| | `POST` | `/webhooks/sms` | Sms | | `POST` | `/webhooks/voice` | Voice | | `GET` | `/shifts` | Shifts | | `GET` | `/health` | Health | ## Troubleshooting | Issue | Likely cause | Fix | |-------|--------------|-----| | `401 invalid signature` on `/webhooks/sms` or `/webhooks/voice` | `TELNYX_PUBLIC_KEY` is missing, wrong, or the request was tampered with | Set `TELNYX_PUBLIC_KEY` to the Ed25519 public key from [Portal -> Keys & Credentials](https://portal.telnyx.com); it must be the base64 value, not the API key. | | Webhooks never arrive / call never starts | The webhook URL is not reachable from Telnyx, or `ngrok` is down | Re-run `ngrok http 5000`, then point the Call Control Application webhook URL at `https://.ngrok.io/webhooks/voice`. The manager-text SMS profile must point at `/webhooks/sms`. | | App exits or calls fail with auth errors | A required env var is unset (`TELNYX_API_KEY`, `TELNYX_PHONE_NUMBER`, `TELNYX_CONNECTION_ID`) | Confirm `.env` is filled in from `.env.example`; `TELNYX_CONNECTION_ID` must be the Call Control Application ID used to place outbound calls. | | `"Calling 0 available employees now."` | Merge returned no employees with phone numbers | Verify `MERGE_API_KEY` and `MERGE_ACCOUNT_TOKEN`, that the linked HRIS account has `ACTIVE` employees, and that those records include phone numbers. | ## Related Examples - [shift-fill-engine-python](https://raw.githubusercontent.com/team-telnyx/telnyx-code-examples/main/shift-fill-engine-python/README.md) -- standalone shift-fill workflow without the Edge/Merge layer - [edge-merge-ai-receptionist-python](https://raw.githubusercontent.com/team-telnyx/telnyx-code-examples/main/edge-merge-ai-receptionist-python/README.md) -- sibling Edge + Merge HRIS example using voice and AI - [route-phone-calls-to-ai-agent-python](https://raw.githubusercontent.com/team-telnyx/telnyx-code-examples/main/route-phone-calls-to-ai-agent-python/README.md) -- Call Control routing and AI-driven voice interaction - [merge-employee-hotline-python](https://raw.githubusercontent.com/team-telnyx/telnyx-code-examples/main/merge-employee-hotline-python/README.md) -- employee-facing voice line backed by Merge HRIS data ## Resources - [Telnyx Developer Docs](https://developers.telnyx.com) - [Call Control quickstart](https://developers.telnyx.com/docs/voice/call-control) - [AI Inference docs](https://developers.telnyx.com/docs/inference) - [Merge.dev API docs](https://docs.merge.dev) - [Telnyx Portal](https://portal.telnyx.com)