phone-agent
Physical name: morpheus-sam-phone-agent. Source: lambdas/phone_agent/.
Three unrelated scheduled jobs share this function because they share its integrations: Twilio, Slack, and the two outbound queues.
| Aspect | Value |
|---|---|
Handler |
|
Timeout |
120 s |
Memory |
256 MB |
Reserved concurrency |
2 |
Layers |
common, data-layer, bigquery |
Triggers |
3 EventBridge schedules, all enabled |
It has no SQS trigger, but the dispatcher supports the SQS path so one can be added
without touching handler.py.
Registered events
detail-type |
Handler | Purpose |
|---|---|---|
|
|
Twilio line-type intelligence for pending |
|
|
Slack reminders for tasks due within 24 h, plus the escalation ladder |
|
|
Amazon seller campaign re-engagement |
Phone.LineIntelPending
Full rules: Phone line intelligence.
Selects target_phone_numbers rows where syncTwilio is false, capped at
PHONE_AGENT_BATCH_SIZE. Per number:
-
GETTwilio Lookup v2 withFields=line_type_intelligence,line_status. -
Update the row with country code, carrier, MCC/MNC, line type, risk level,
isBlocked, andsyncTwilio = True. -
Build HubSpot properties
phone_carrier_nameandphone_line_type. -
Resolve a
hubspotContactIdthrough the row’scontactId. -
Publish
Contact.UpdatetoCrmSyncQueuewithtarget_phone_number_idset, somorpheus-hubspotstamps the row after a successful PATCH.
Returns immediately with an ERROR when CRM_SYNC_QUEUE_URL is unset.
Risk tiers above medium — nonFixedVoip (high), premium, voicemail, pager
(critical) — set isBlocked = True, keeping the number out of future dialling.
syncTwilio is the idempotence marker: a number is looked up exactly once, which
matters because Twilio Lookup is billed per request.
Phone.TaskDeadlineReminder
Full rules: escalation ladder.
Two phases per invocation, both gated on SLACK_BOT_TOKEN and
SALES_SLACK_CHANNEL_ID being set:
- Phase 1 — reminders
-
Pending tasks due within 24 hours, grouped by rep, posted as one Slack channel message. An
Activityrow of typereminderis inserted per task as a best-effort audit trail. - Phase 2 — escalation
-
Tasks past their deadline by a day or more, escalated one stage per run with a 3-second sleep between tasks for Slack rate limits:
Stage Action Next 0
Public channel warning mentioning the rep
1
1
Slack DM to the rep +
Email.SendInternaltoEmailAgentQueue2
2
Mark the task
failed3
3
Terminal — return
—
At stage 3, a
FollowUpTaskSyncEventwith statusfailedis published toCrmSyncQueuewhen the task has ahubspotTaskId.
A per-task failure is logged and the loop continues.
Phone.CampaignTargetReEngagement
Full rules: Amazon seller campaign.
Queries v_amazon_seller_contact_engagement_summary in BigQuery, computes priority in
Python, resolves ownership, and publishes one Contact.Update per row to
CrmSyncQueue.
Priority:
| Condition | call_priority |
|---|---|
AI-owned last engagement within |
4 |
AI-owned last engagement older, or none |
3 |
Last engagement owner is not the AI |
1 |
This is the only handler in the platform that aborts the whole batch when round-robin returns no rep, rather than continuing — every remaining row would otherwise be enrolled with no assignee.
Returns early with a warning when BIGQUERY_CREDENTIALS or CRM_SYNC_QUEUE_URL is
unset.
Services
| Module | Responsibility |
|---|---|
|
Twilio Lookup v2 via |
|
|
|
|
|
|
|
|
|
Two publishers exist because the two queues differ. |
Configuration
| Setting | Default | Purpose |
|---|---|---|
|
(template) |
Shared secret |
|
(template) |
Tenant scope |
|
|
Required by line-intel and campaign re-engagement |
|
|
Stage-1 escalation email; skipped with a warning when unset |
|
|
Numbers per Twilio run |
|
|
Channel for reminders and escalation |
|
|
PhoneAgentSecrets: DATABASE_URL, TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN (all
validated non-empty), plus optional SLACK_BOT_TOKEN and BIGQUERY_CREDENTIALS.
Because the Twilio credentials are validated non-empty, this function will not cold-start without them — even for the two handlers that never call Twilio.
IAM
-
SQSSendMessagePolicyonCrmSyncQueue -
SQSSendMessagePolicyonEmailAgentQueue -
secretsmanager:GetSecretValueonAgentcoreSecret
No Bedrock access — this function runs no agents.
Tests
cd lambdas && uv run pytest tests/unit/test_phone_agent_twilio_lookup.py -v
cd lambdas && uv run pytest tests/unit/test_campaign_reengagement.py -v
Operating notes
-
AI_OWNER_IDhere duplicatesDIALER_FALLBACK_HUBSPOT_OWNER_IDinmorpheus_hubspot/utils/constants.py. Both are"86817215"and both must change together. -
Empty
SLACK_BOT_TOKENdisables both reminder phases with a warning; nothing else breaks. -
Empty
BIGQUERY_CREDENTIALSdisables campaign re-engagement only. -
To re-run Twilio for a number, clear its
syncTwilioflag:UPDATE target_phone_numbers SET "syncTwilio" = false WHERE "phoneNumber" = '+1302…'; -
All three schedules currently use
rate(…)values from development. See EventBridge schedules.