From eed80ec97dffb0b646ddde1bfec388f7072b0d88 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 16 Jun 2026 19:21:40 -0700 Subject: [PATCH 1/3] feat: add twilio plugin --- README.md | 1 + plugins/twilio/LICENSE.twilio-developer-kit | 21 + plugins/twilio/README.md | 34 + plugins/twilio/index.ts | 32 + plugins/twilio/package.json | 21 + .../skills/twilio-account-setup/SKILL.md | 270 ++++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../SKILL.md | 217 ++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-agent-connect/SKILL.md | 551 +++++++++++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-ai-agent-architect/SKILL.md | 309 +++++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-call-recordings/SKILL.md | 280 ++++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-cli-reference/SKILL.md | 237 +++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-compliance-onboarding/SKILL.md | 238 +++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-compliance-traffic/SKILL.md | 258 +++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-conference-calls/SKILL.md | 319 +++++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-content-template-builder/SKILL.md | 195 ++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-conversation-intelligence/SKILL.md | 618 +++++++++++++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-conversation-orchestrator/SKILL.md | 635 ++++++++++++++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-conversations-classic-api/SKILL.md | 598 +++++++++++++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-customer-memory/SKILL.md | 370 ++++++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../SKILL.md | 199 ++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-debugging-observability/SKILL.md | 424 ++++++++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../SKILL.md | 114 ++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio/skills/twilio-email-send/SKILL.md | 198 ++++++ .../twilio-email-send/assets/icon-large.png | Bin 0 -> 9590 bytes .../twilio-email-send/assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-enterprise-knowledge/SKILL.md | 353 ++++++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-iam-auth-setup/SKILL.md | 251 +++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../SKILL.md | 177 +++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-lookup-phone-intelligence/SKILL.md | 161 +++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../SKILL.md | 192 ++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-messaging-channel-advisor/SKILL.md | 115 ++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-messaging-overview/SKILL.md | 134 ++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-messaging-services/SKILL.md | 321 +++++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-messaging-webhooks/SKILL.md | 254 +++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../SKILL.md | 173 +++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-numbers-senders/SKILL.md | 222 ++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-organizations-setup/SKILL.md | 186 +++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-rcs-messaging/SKILL.md | 227 +++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../SKILL.md | 207 ++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-reliability-patterns/SKILL.md | 290 ++++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-security-api-auth/SKILL.md | 167 +++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-security-compliance-hipaa/SKILL.md | 148 ++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-security-hardening/SKILL.md | 166 +++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-send-message/SKILL.md | 199 ++++++ .../twilio-send-message/assets/icon-large.png | Bin 0 -> 9590 bytes .../twilio-send-message/assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-sendgrid-account-setup/SKILL.md | 120 ++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../SKILL.md | 260 +++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-sendgrid-email-send/SKILL.md | 180 +++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-sendgrid-email-settings/SKILL.md | 92 +++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../SKILL.md | 137 ++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-sendgrid-inbound-parse/SKILL.md | 103 +++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-sendgrid-suppressions/SKILL.md | 103 +++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-sendgrid-webhooks/SKILL.md | 140 ++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-sms-isv-setup/SKILL.md | 265 ++++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-sms-send-message/SKILL.md | 207 ++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-taskrouter-routing/SKILL.md | 417 ++++++++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../skills/twilio-verify-send-otp/SKILL.md | 283 ++++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-voice-conversation-relay/SKILL.md | 208 ++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-voice-outbound-calls/SKILL.md | 302 +++++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio/skills/twilio-voice-twiml/SKILL.md | 351 ++++++++++ .../twilio-voice-twiml/assets/icon-large.png | Bin 0 -> 9590 bytes .../twilio-voice-twiml/assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-webhook-architecture/SKILL.md | 425 ++++++++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-whatsapp-manage-senders/SKILL.md | 201 ++++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes .../twilio-whatsapp-send-message/SKILL.md | 168 +++++ .../assets/icon-large.png | Bin 0 -> 9590 bytes .../assets/icon-small.png | Bin 0 -> 5559 bytes 170 files changed, 14044 insertions(+) create mode 100644 plugins/twilio/LICENSE.twilio-developer-kit create mode 100644 plugins/twilio/README.md create mode 100644 plugins/twilio/index.ts create mode 100644 plugins/twilio/package.json create mode 100644 plugins/twilio/skills/twilio-account-setup/SKILL.md create mode 100644 plugins/twilio/skills/twilio-account-setup/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-account-setup/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-agent-augmentation-architect/SKILL.md create mode 100644 plugins/twilio/skills/twilio-agent-augmentation-architect/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-agent-augmentation-architect/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-agent-connect/SKILL.md create mode 100644 plugins/twilio/skills/twilio-agent-connect/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-agent-connect/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-ai-agent-architect/SKILL.md create mode 100644 plugins/twilio/skills/twilio-ai-agent-architect/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-ai-agent-architect/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-call-recordings/SKILL.md create mode 100644 plugins/twilio/skills/twilio-call-recordings/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-call-recordings/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-cli-reference/SKILL.md create mode 100644 plugins/twilio/skills/twilio-cli-reference/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-cli-reference/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-compliance-onboarding/SKILL.md create mode 100644 plugins/twilio/skills/twilio-compliance-onboarding/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-compliance-onboarding/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-compliance-traffic/SKILL.md create mode 100644 plugins/twilio/skills/twilio-compliance-traffic/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-compliance-traffic/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-conference-calls/SKILL.md create mode 100644 plugins/twilio/skills/twilio-conference-calls/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-conference-calls/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-content-template-builder/SKILL.md create mode 100644 plugins/twilio/skills/twilio-content-template-builder/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-content-template-builder/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-conversation-intelligence/SKILL.md create mode 100644 plugins/twilio/skills/twilio-conversation-intelligence/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-conversation-intelligence/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-conversation-orchestrator/SKILL.md create mode 100644 plugins/twilio/skills/twilio-conversation-orchestrator/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-conversation-orchestrator/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-conversations-classic-api/SKILL.md create mode 100644 plugins/twilio/skills/twilio-conversations-classic-api/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-conversations-classic-api/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-customer-memory/SKILL.md create mode 100644 plugins/twilio/skills/twilio-customer-memory/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-customer-memory/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-customer-support-architect/SKILL.md create mode 100644 plugins/twilio/skills/twilio-customer-support-architect/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-customer-support-architect/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-debugging-observability/SKILL.md create mode 100644 plugins/twilio/skills/twilio-debugging-observability/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-debugging-observability/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-email-deliverability-advisor/SKILL.md create mode 100644 plugins/twilio/skills/twilio-email-deliverability-advisor/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-email-deliverability-advisor/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-email-send/SKILL.md create mode 100644 plugins/twilio/skills/twilio-email-send/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-email-send/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-enterprise-knowledge/SKILL.md create mode 100644 plugins/twilio/skills/twilio-enterprise-knowledge/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-enterprise-knowledge/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-iam-auth-setup/SKILL.md create mode 100644 plugins/twilio/skills/twilio-iam-auth-setup/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-iam-auth-setup/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-identity-verification-advisor/SKILL.md create mode 100644 plugins/twilio/skills/twilio-identity-verification-advisor/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-identity-verification-advisor/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-lookup-phone-intelligence/SKILL.md create mode 100644 plugins/twilio/skills/twilio-lookup-phone-intelligence/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-lookup-phone-intelligence/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-marketing-promotions-advisor/SKILL.md create mode 100644 plugins/twilio/skills/twilio-marketing-promotions-advisor/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-marketing-promotions-advisor/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-messaging-channel-advisor/SKILL.md create mode 100644 plugins/twilio/skills/twilio-messaging-channel-advisor/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-messaging-channel-advisor/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-messaging-overview/SKILL.md create mode 100644 plugins/twilio/skills/twilio-messaging-overview/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-messaging-overview/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-messaging-services/SKILL.md create mode 100644 plugins/twilio/skills/twilio-messaging-services/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-messaging-services/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-messaging-webhooks/SKILL.md create mode 100644 plugins/twilio/skills/twilio-messaging-webhooks/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-messaging-webhooks/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-notifications-alerts-advisor/SKILL.md create mode 100644 plugins/twilio/skills/twilio-notifications-alerts-advisor/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-notifications-alerts-advisor/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-numbers-senders/SKILL.md create mode 100644 plugins/twilio/skills/twilio-numbers-senders/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-numbers-senders/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-organizations-setup/SKILL.md create mode 100644 plugins/twilio/skills/twilio-organizations-setup/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-organizations-setup/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-rcs-messaging/SKILL.md create mode 100644 plugins/twilio/skills/twilio-rcs-messaging/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-rcs-messaging/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-regulatory-compliance-bundles/SKILL.md create mode 100644 plugins/twilio/skills/twilio-regulatory-compliance-bundles/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-regulatory-compliance-bundles/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-reliability-patterns/SKILL.md create mode 100644 plugins/twilio/skills/twilio-reliability-patterns/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-reliability-patterns/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-security-api-auth/SKILL.md create mode 100644 plugins/twilio/skills/twilio-security-api-auth/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-security-api-auth/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-security-compliance-hipaa/SKILL.md create mode 100644 plugins/twilio/skills/twilio-security-compliance-hipaa/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-security-compliance-hipaa/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-security-hardening/SKILL.md create mode 100644 plugins/twilio/skills/twilio-security-hardening/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-security-hardening/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-send-message/SKILL.md create mode 100644 plugins/twilio/skills/twilio-send-message/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-send-message/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-sendgrid-account-setup/SKILL.md create mode 100644 plugins/twilio/skills/twilio-sendgrid-account-setup/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-sendgrid-account-setup/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-sendgrid-deliverability-advisor/SKILL.md create mode 100644 plugins/twilio/skills/twilio-sendgrid-deliverability-advisor/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-sendgrid-deliverability-advisor/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-sendgrid-email-send/SKILL.md create mode 100644 plugins/twilio/skills/twilio-sendgrid-email-send/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-sendgrid-email-send/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-sendgrid-email-settings/SKILL.md create mode 100644 plugins/twilio/skills/twilio-sendgrid-email-settings/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-sendgrid-email-settings/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-sendgrid-engagement-quality/SKILL.md create mode 100644 plugins/twilio/skills/twilio-sendgrid-engagement-quality/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-sendgrid-engagement-quality/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-sendgrid-inbound-parse/SKILL.md create mode 100644 plugins/twilio/skills/twilio-sendgrid-inbound-parse/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-sendgrid-inbound-parse/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-sendgrid-suppressions/SKILL.md create mode 100644 plugins/twilio/skills/twilio-sendgrid-suppressions/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-sendgrid-suppressions/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-sendgrid-webhooks/SKILL.md create mode 100644 plugins/twilio/skills/twilio-sendgrid-webhooks/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-sendgrid-webhooks/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-sms-isv-setup/SKILL.md create mode 100644 plugins/twilio/skills/twilio-sms-isv-setup/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-sms-isv-setup/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-sms-send-message/SKILL.md create mode 100644 plugins/twilio/skills/twilio-sms-send-message/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-sms-send-message/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-taskrouter-routing/SKILL.md create mode 100644 plugins/twilio/skills/twilio-taskrouter-routing/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-taskrouter-routing/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-verify-send-otp/SKILL.md create mode 100644 plugins/twilio/skills/twilio-verify-send-otp/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-verify-send-otp/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-voice-conversation-relay/SKILL.md create mode 100644 plugins/twilio/skills/twilio-voice-conversation-relay/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-voice-conversation-relay/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-voice-outbound-calls/SKILL.md create mode 100644 plugins/twilio/skills/twilio-voice-outbound-calls/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-voice-outbound-calls/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-voice-twiml/SKILL.md create mode 100644 plugins/twilio/skills/twilio-voice-twiml/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-voice-twiml/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-webhook-architecture/SKILL.md create mode 100644 plugins/twilio/skills/twilio-webhook-architecture/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-webhook-architecture/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-whatsapp-manage-senders/SKILL.md create mode 100644 plugins/twilio/skills/twilio-whatsapp-manage-senders/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-whatsapp-manage-senders/assets/icon-small.png create mode 100644 plugins/twilio/skills/twilio-whatsapp-send-message/SKILL.md create mode 100644 plugins/twilio/skills/twilio-whatsapp-send-message/assets/icon-large.png create mode 100644 plugins/twilio/skills/twilio-whatsapp-send-message/assets/icon-small.png diff --git a/README.md b/README.md index a400e81c..98ab780a 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Each plugin lives in `plugins/`. The directory name is the install keyword | `mac-notify` | macOS notifications when a Cline run completes. | | `nanobanana` | Image generation through OpenRouter and Gemini image models. | | `speak` | Speaks completed Cline replies with ElevenLabs text to speech. | +| `twilio` | Twilio and SendGrid workflow skills plus the Twilio docs MCP. | | `typescript-lsp` | TypeScript language service `goto_definition` support. | | `weather-metrics` | Demo weather tool plus runtime metrics hooks. | | `web-search` | Exa-backed web search as a Cline tool. | diff --git a/plugins/twilio/LICENSE.twilio-developer-kit b/plugins/twilio/LICENSE.twilio-developer-kit new file mode 100644 index 00000000..18b91fa1 --- /dev/null +++ b/plugins/twilio/LICENSE.twilio-developer-kit @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Twilio + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/plugins/twilio/README.md b/plugins/twilio/README.md new file mode 100644 index 00000000..226394c3 --- /dev/null +++ b/plugins/twilio/README.md @@ -0,0 +1,34 @@ +# Twilio + +Twilio bundles workflow skills for building with Twilio and SendGrid, plus the public Twilio docs MCP server for documentation and API-schema lookup. + +## What It Adds + +- 55 Twilio and SendGrid skills covering SMS, WhatsApp, RCS, Voice, Verify, Lookup, Messaging Services, regulatory onboarding, webhooks, Conversation Orchestrator, Conversation Intelligence, Customer Memory, SendGrid sending, deliverability, inbound parse, suppressions, and event webhooks. +- `twilio-docs`, a streamable HTTP MCP server at `https://mcp.twilio.com/docs` for semantic Twilio documentation search and API operation retrieval. The docs MCP does not require authentication. +- A safety rule for live messaging, email, voice, verification, compliance, and customer-data workflows. + +## Requirements + +- Cline with plugin MCP support. +- A Twilio account and credentials such as `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN` for live Twilio API work. +- A SendGrid account and `SENDGRID_API_KEY` for SendGrid email workflows. +- Valid sender identities, phone numbers, WhatsApp senders, regulatory registrations, webhook URLs, and user consent for the channels you plan to use. + +## Trust Boundaries + +Installing this plugin registers only the unauthenticated Twilio documentation MCP and bundled local skills. It does not send messages, place calls, create resources, read account data, or contact Twilio or SendGrid APIs with user credentials during setup. + +Review live sends, calls, OTPs, compliance changes, number purchases or releases, credential changes, webhook exposure, and production-traffic changes before allowing Cline to execute them. + +## Install + +```bash +cline plugin install twilio +``` + +For local development: + +```bash +cline plugin install ./plugins/twilio --cwd . +``` diff --git a/plugins/twilio/index.ts b/plugins/twilio/index.ts new file mode 100644 index 00000000..a1c1fd02 --- /dev/null +++ b/plugins/twilio/index.ts @@ -0,0 +1,32 @@ +import type { AgentPlugin } from "@cline/sdk" + +const twilioSafetyRule = [ + "Twilio and SendGrid workflows can send messages and email, place calls, verify users, change account configuration, handle personal data, and affect production traffic.", + "Do not send SMS, WhatsApp, RCS, MMS, email, voice calls, OTPs, webhooks, or production traffic, change compliance or registration settings, buy or release numbers, rotate credentials, mutate account resources, or delete customer data without explicit user approval.", + "Treat TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, Twilio API keys and secrets, SENDGRID_API_KEY, phone numbers, email addresses, message bodies, recordings, transcripts, verification codes, webhook payloads, and customer data as sensitive.", + "Before implementing messaging, voice, email, or verification workflows, confirm consent, destination, region, environment, sender identity, expected cost, and applicable regulatory or compliance requirements.", +].join("\n") + +const plugin: AgentPlugin = { + name: "twilio", + manifest: { + capabilities: ["mcp", "skills", "rules"], + }, + setup(api) { + api.registerMcpServer({ + name: "twilio-docs", + transport: { + type: "streamableHttp", + url: "https://mcp.twilio.com/docs", + }, + }) + + api.registerRule({ + id: "twilio-safety", + source: "twilio", + content: twilioSafetyRule, + }) + }, +} + +export default plugin diff --git a/plugins/twilio/package.json b/plugins/twilio/package.json new file mode 100644 index 00000000..b0a30cba --- /dev/null +++ b/plugins/twilio/package.json @@ -0,0 +1,21 @@ +{ + "name": "twilio", + "version": "0.0.0", + "private": true, + "type": "module", + "description": "Cline plugin that bundles Twilio and SendGrid workflow skills with the Twilio docs MCP.", + "cline": { + "plugins": [ + { + "paths": [ + "./index.ts" + ], + "capabilities": [ + "mcp", + "skills", + "rules" + ] + } + ] + } +} diff --git a/plugins/twilio/skills/twilio-account-setup/SKILL.md b/plugins/twilio/skills/twilio-account-setup/SKILL.md new file mode 100644 index 00000000..07a79999 --- /dev/null +++ b/plugins/twilio/skills/twilio-account-setup/SKILL.md @@ -0,0 +1,270 @@ +--- +name: twilio-account-setup +description: > + Create and configure a Twilio account from scratch. Covers free trial signup, + trial limitations, getting credentials (Account SID and Auth Token), buying + a phone number, verifying recipient numbers for trial use, SDK installation, + first API call, subaccount management (creation, inheritance, credential + isolation, limits), and enabling specific products (AI Assistants, + Conversations, Verify, ConversationRelay, WhatsApp). Use this skill before + any other Twilio skill if you do not yet have a Twilio account or need to + enable a product. For Organization-level governance (SSO, SCIM, multi-team), + see `twilio-organizations-setup`. +--- + +## Overview + +Every Twilio skill requires an active Twilio account and credentials. This skill covers the one-time setup steps that are prerequisites for all other Twilio skills. + +--- + +## Quickstart + +1. Sign up at [twilio.com/try-twilio](https://www.twilio.com/try-twilio) -- enter name, email, password +2. Verify your email and personal phone number +3. Get your credentials from [Console > Account > API keys & tokens](https://console.twilio.com/us1/account/keys-credentials/api-keys): + +```bash +export TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +export TWILIO_AUTH_TOKEN=your_auth_token +``` + +4. Buy a phone number at [Console > Phone Numbers > Buy a number](https://console.twilio.com/us1/develop/phone-numbers/search). Confirm the account, region, sender type, and expected cost before buying a number. + +5. Install the SDK and send your first message. Confirm the destination, sender, body, environment, and user consent before running a live send: + +Python +```bash +pip install twilio +``` +```python +import os +from twilio.rest import Client + +client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"]) +message = client.messages.create( + to="+15558675310", # must be verified on trial accounts + from_="+15017122661", # your Twilio number + body="Hello from Twilio!" +) +print(f"Sent: {message.sid}") +``` + +Node.js +```bash +npm install twilio +``` +```node +const twilio = require("twilio"); +const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); + +const message = await client.messages.create({ + to: "+15558675310", // must be verified on trial accounts + from: "+15017122661", // your Twilio number + body: "Hello from Twilio!", +}); +console.log(`Sent: ${message.sid}`); +``` + +You're ready to use any Twilio skill. Trial accounts have restrictions -- see Constraints below. + +--- + +## Key Patterns + +### Verify Recipient Numbers (trial accounts only) + +Trial accounts can only send to verified phone numbers (up to 5 per account). + +1. Go to [Console > Phone Numbers > Verified Caller IDs](https://console.twilio.com/us1/develop/phone-numbers/verified-caller-ids) +2. Click Add a new Caller ID and verify via SMS code + +Verified numbers work across both messaging and voice. Remove this restriction by upgrading your account. + +### Enable Specific Products + +Some products require explicit activation: + +| Product | How to enable | +|---------|--------------| +| AI Assistants | [Console > Explore Products > AI Assistants](https://console.twilio.com/us1/develop/ai-assistants) > Get started | +| Conversations | [Console > Conversations > Manage > Overview](https://console.twilio.com/us1/develop/conversations/manage/overview) > Enable Conversations | +| Verify | [Console > Verify > Services](https://console.twilio.com/us1/verify/services) > Create new | +| WhatsApp (sandbox) | [Console > Messaging > Try it out > Send a WhatsApp message](https://console.twilio.com/us1/develop/sms/try-it-out/whatsapp-learn) | +| ConversationRelay | [Console > Voice > ConversationRelay](https://console.twilio.com/us1/voice/conversation-relay) > complete onboarding form | + +### SDK Installation + +| Language | Install | SDK package | +|----------|---------|-------------| +| Python | `pip install twilio` | `twilio` | +| Node.js | `npm install twilio` | `twilio` | +| Java | Maven/Gradle | `com.twilio.sdk:twilio` | +| C# | `dotnet add package Twilio` | `Twilio` | +| Ruby | `gem install twilio-ruby` | `twilio-ruby` | +| PHP | `composer require twilio/sdk` | `twilio/sdk` | +| Go | `go get github.com/twilio/twilio-go` | `twilio-go` | + +### Initialize the Client + +Always load credentials from environment variables -- never hardcode them. + +Python +```python +import os +from twilio.rest import Client + +client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"]) +``` + +Node.js +```node +const twilio = require("twilio"); +const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); +``` + +For production, use API Keys instead of Auth Token. See `twilio-iam-auth-setup`. + +### Twilio CLI Setup + +The CLI is useful for quick operations and local webhook testing. + +```bash +# Install (macOS) +brew tap twilio/brew && brew install twilio + +# Install (npm -- all platforms) +npm install -g twilio-cli + +# Login (creates an API key automatically) +# Confirm this account-level credential change before running. +twilio login + +# Verify setup +twilio phone-numbers:list +``` + +The CLI stores profiles for switching between accounts: +```bash +# List profiles +twilio profiles:list + +# Switch active profile +twilio profiles:use my-project + +# Use environment variables instead of profiles +export TWILIO_ACCOUNT_SID=ACxxxxxxxx +export TWILIO_AUTH_TOKEN=xxxxxxxx +``` + +Precedence: `--profile` flag > environment variables > active profile. + +### Accounts and Subaccounts + +Creating a new Twilio account: Accounts can only be created from the Console UI - there is no API for creating top-level accounts. A new account is automatically created when a user signs up. Additional accounts can be created from Console > My Accounts (or "View all accounts"). + +To see all your accounts: Console > My Accounts shows all accounts and subaccounts you have access to. For Organization-wide visibility, see Console > Admin > Accounts (requires Organization admin role). See `twilio-organizations-setup` for Organization-level governance. + +### Subaccounts + +Subaccounts are child accounts under your main (parent) account. Use them for multi-tenant apps, per-customer isolation, or team separation. + +How they differ from the parent account: +- Resources (numbers, calls, messages) are isolated - a subaccount cannot see the parent's resources or other subaccounts' resources +- Billing is consolidated to the parent - a single Twilio balance for all subaccounts +- Voice and SMS permissions inherit from the parent +- Phone numbers can be transferred between parent and subaccounts + +Create via Console: Console > My Accounts > Create Subaccount + +Create via API: + +Python +```python +subaccount = client.api.accounts.create(friendly_name="Customer A") +print(f"Subaccount SID: {subaccount.sid}") +# Store securely - auth_token is only shown at creation time +# e.g., secrets_manager.store("subaccount_auth_token", subaccount.auth_token) +``` + +Node.js +```javascript +const subaccount = await client.api.accounts.create({ friendlyName: "Customer A" }); +console.log(`Subaccount SID: ${subaccount.sid}`); +``` + +### Subaccount Credential Isolation + +Always use the subaccount's own credentials (API Keys or Auth Token) when accessing subaccount resources - do NOT use the parent account's credentials as a shortcut. + +Python - access subaccount resources +```python +# Correct: use subaccount credentials +sub_client = Client(subaccount.sid, subaccount.auth_token) +call = sub_client.calls.create( + to="+15558675310", + from_="+15017122661", # number owned by this subaccount + url="https://yourapp.com/voice" +) + +# Also correct: parent credentials with subaccount SID (v2010 API only) +parent_client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"]) +calls = parent_client.api.accounts(subaccount.sid).calls.list() +``` + +Critical: Resources on separate subdomains (`studio.twilio.com`, `taskrouter.twilio.com`) require subaccount-specific credentials. Parent account credentials will not work on these subdomains. + +### Subaccount Limits + +- Default limit: 1,000 subaccounts per parent account +- Trial accounts: Can create only 1 subaccount - upgrade to create more +- At the limit: Contact Twilio Support with your use case to request an increase +- Closing: Set status to `closed` via API or Console. Closed subaccounts are automatically deleted after 30 days +- Suspension cascade: Suspending the parent account automatically suspends ALL subaccounts + +### Upgrade from Trial + +1. Click Upgrade at the top of the Console, or go to [Console > Admin > Account billing](https://console.twilio.com/us1/admin/billing) +2. Provide name, address, and payment details +3. Your trial phone number carries over; trial balance does not + +--- + +## Trial Restrictions at a Glance + +| Feature | Trial | Upgraded | +|---------|-------|----------| +| Phone numbers | 1 | Unlimited | +| Send to unverified numbers | No | Yes | +| Outbound message prefix | Yes (visible to recipient) | No | +| Verified caller IDs | Up to 5 | Not needed | +| A2P 10DLC registration | No | Yes | +| Daily WhatsApp messages | 50 | Unlimited | +| ConversationRelay | No | Yes (after onboarding) | +| Voice: outbound calls | Domestic only | International | + +--- + +## CANNOT + +- Cannot create top-level accounts via API - Only Console UI. A new account is created at signup; additional accounts from Console > My Accounts. +- Cannot create more than 1 subaccount on trial - Upgrade your account first, then you can create up to 1,000. +- Cannot access subdomain resources with parent credentials - Studio, TaskRouter, and other subdomain APIs require subaccount-specific credentials. Parent credentials return auth errors. +- Cannot undo a closed subaccount after 30 days - Closed subaccounts are permanently deleted. Suspension is reversible; closure is not. +- Cannot transfer trial balance to a paid account - Trial credits are forfeited on upgrade. +- Cannot send to unverified numbers on trial - Only verified Caller IDs (up to 5) can receive messages or calls. +- Auth Token rotation invalidates ALL API keys - This is a one-way door. Use API Keys from day one. See `twilio-security-api-auth`. +- API Key secrets shown only once at creation - Store them immediately. Cannot be retrieved afterward. +- AI Assistants and ConversationRelay require approval - Limited access products. Activation is not instant. + +--- + +## Next Steps + +- Organization governance (SSO, SCIM, multi-team): `twilio-organizations-setup` +- Secure credential management and API Keys: `twilio-security-api-auth` +- Send your first SMS: `twilio-sms-send-message` +- Send your first WhatsApp message: `twilio-whatsapp-send-message` +- Receive incoming messages: `twilio-messaging-webhooks` +- US SMS compliance (A2P 10DLC): `twilio-compliance-onboarding` +- Webhook setup: `twilio-webhook-architecture` diff --git a/plugins/twilio/skills/twilio-account-setup/assets/icon-large.png b/plugins/twilio/skills/twilio-account-setup/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-account-setup/assets/icon-small.png b/plugins/twilio/skills/twilio-account-setup/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Planning skill for augmenting human agents with real-time AI + intelligence. Qualifies the developer's use case across coaching, + compliance, QA, and routing to recommend the right Conversation Intelligence + Conversation Memory + + TaskRouter architecture. Handles both "I want to add AI coaching to + my call center" and "configure Conversation Intelligence operators for script adherence." +tier: discover +--- + +## Role + +You are a Human Agent Augmentation Advisor. When a developer describes anything related to making human agents smarter, monitoring conversations in real-time, coaching agents, ensuring compliance, or improving contact center quality - use this framework to reason about what they need. + +## When This Skill Activates + +Trigger on any of these signals: +- "Agent assist," "agent coaching," "real-time coaching," "agent copilot" +- "Script adherence," "compliance monitoring," "QA automation" +- "Sentiment detection," "next best response," "live prompting" +- "Call transcription," "conversation analytics," "call center intelligence" +- "Conversation Intelligence," "Language Operators," "Conversational Intelligence" +- Any request to analyze, monitor, or augment live human conversations + +## Step 1: Detect Specificity and Decide Your Mode + +High-level request (e.g., "I want AI to help my agents perform better"): +-> DISCOVERY MODE. Walk through Steps 2-4 to understand what "better" means. + +Mid-level request (e.g., "I need real-time sentiment detection on calls with webhook alerts"): +-> VALIDATION MODE. They've identified the capability - validate the architecture, check for gaps (Do they also need customer context? Recording for post-call?), recommend skills. + +Specific implementation request (e.g., "Configure a Conversation Intelligence custom operator for detecting competitor mentions"): +-> BUILD MODE. Proceed with the relevant Product skill. Quick context check: Is Conversation Intelligence provisioned? Is Conversation Orchestrator linked? Are they aware of the operator lifecycle gotchas? + +## Step 2: Qualify Intent - The 5 Essential Questions + +1. What does "augmentation" mean for your agents? + - Real-time coaching: Live suggestions/prompts appearing on the agent's screen during a call + - Compliance monitoring: Automated detection of script deviations, regulatory violations, disclosure requirements + - Post-call QA: Automated scoring and review of completed conversations (replacing manual sampling) + - Intelligent routing: Using AI signals to send calls to the right specialist + +2. What channels are your agents handling? + - Voice calls only -> Transcription + Conversation Intelligence operators on audio stream + - Voice + messaging -> Conversation Orchestrator for unified conversation tracking + Conversation Intelligence across both + - Messaging only -> Conversation Intelligence operators on text (no transcription needed) + +3. What's your existing contact center infrastructure? + - Twilio Flex -> Native integration path (Flex Agent Copilot replatforming onto Conversation Intelligence) + - Other CCaaS (Genesys, Five9, NICE) -> Webhook-based integration, more custom glue + - Custom-built -> Full flexibility but more setup + +4. Do you need customer context surfaced to agents? + - No (agents look up context themselves) -> Skip Conversation Memory + - Yes (show customer history, preferences, past issues on accept) -> Add Conversation Memory + +5. What's your call volume and budget sensitivity? + - Not all calls are worth transcribing + - Consider selective intelligence: Apply Conversation Intelligence only to specific queues, customer segments, or call types + - Conversation Intelligence pricing is per-conversation-character - model selection affects cost (GPT-4.1-nano for speed/cost vs. GPT-5.2 for quality) + +## Step 3: Assess Sophistication - The Capability Ladder + +### Level 1: Listen - Transcription & Recording +Developer says: "I want to transcribe calls for review and analysis." +Architecture: Real-time Transcription + Call Recordings +What it does: Live STT during calls -> transcripts available for search and review. Recordings stored for compliance and playback. +Key decisions: +- Engine: Google (wider language support) vs Deepgram (better accuracy, lower latency) +- Track: Inbound audio, outbound audio, or both +- Recording method: `` for simplicity, or Recordings REST API for control +Relevant bundled skills: `twilio-call-recordings` + +### Level 2: Coach - Real-Time Intelligence +Developer says: "I want to detect sentiment, prompt agents with next-best-response, or monitor script adherence live." +Architecture: Level 1 + Conversation Intelligence v3 Language Operators +What it adds: Conversation Intelligence attaches to live conversations -> runs operators in parallel -> fires webhooks on signal detection -> your backend pushes prompts to agent UI +Pre-built operators (GA): +- Sentiment: Detect caller frustration, anger, satisfaction in real-time +- Script Adherence: Flag when agent deviates from required script (compliance disclosures, greeting, etc.) +- Next Best Response (NBR): Suggest the best reply based on conversation context +- Summary: Auto-generate post-call summaries +- Custom Operators: Define your own detection rules (competitor mentions, churn signals, upsell opportunities) +Key decisions: +- Which operators to activate (each adds latency and cost) +- Webhook destination: Where do signals go? (Flex plugin, custom dashboard, Slack alert) +- Model profile: Speed (GPT-4.1-nano, lower cost) vs quality (GPT-5.2, higher accuracy) +Relevant bundled skills: + `twilio-conversation-intelligence` + +### Level 3: Context - Customer Memory for Agents +Developer says: "When the agent picks up, I want them to see who this customer is and their full history." +Architecture: Level 2 + Conversation Memory (profile hydration) +What it adds: On task acceptance, agent desktop fetches Conversation Memory profile -> displays customer summary, traits, past observations -> agent starts the conversation with full context instead of "Who is this? What do you need?" +Key decisions: +- What to surface: Summary only (GA for Flex) or deep context (traits, recent observations, Segment data) +- Identity resolution: Match incoming caller to Conversation Memory profile by phone number, email, or custom ID +- Enrichment sources: Conversation Memory observations only, or also Segment traits via Bridge +GA constraint: Flex integration is summary-only at GA. Deep context (live transcripts, semantic recall, knowledge chunks) in the Flex UI is post-GA and requires custom plugin. +Relevant bundled skills: + `twilio-customer-memory`, `twilio-conversation-orchestrator` + +### Level 4: Route - Intelligence-Driven Routing +Developer says: "I want AI signals to determine which agent gets the call - not just FIFO." +Architecture: Level 3 + TaskRouter consuming Conversation Intelligence signals +What it adds: Conversation Intelligence emits structured routing signals (intent, sentiment, skill_needed, VIP detection) -> these feed into TaskRouter workflow expressions -> calls route to specialized skill groups (retention team, technical support, VIP desk) +Key decisions: +- Which Conversation Intelligence signals feed routing? (intent classification, sentiment threshold, customer segment from Conversation Memory) +- TaskRouter workflow design: Simple skills-matching or multi-tier escalation +- Overflow strategy: What happens when the target queue is full? +Relevant bundled skills: + `twilio-taskrouter-routing` + +## Step 4: Qualify Context + +### Existing Infrastructure +- Flex customer: Leverage Flex Agent Copilot (being replatformed onto Conversation Intelligence). Tightest integration path. +- Other CCaaS: You'll integrate via webhooks. Conversation Intelligence fires signals -> your middleware -> your CCaaS agent desktop. More work but fully functional. +- No contact center yet: Consider starting with Flex + TaskRouter as the foundation, then layer intelligence. + +### Customer Profile + +ISV (building augmentation for multiple clients): +- Per-client Conversation Intelligence operator configurations +- Separate Conversation Memory stores per client (max 15 per account) +- White-label considerations for agent UI + +Enterprise: +- Compliance operators are likely mandatory (regulated industries: finance, healthcare, insurance) +- Selective intelligence to control cost at scale +- Integration with existing QA workflows (Calabrio, Verint, etc.) +- No ngrok for webhook delivery - deploy to production infrastructure + +SMB: +- Start at Level 2 - sentiment + summary operators give immediate value +- Skip Conversation Memory initially - add when agent "amnesia" becomes a pain point +- Use pre-built operators before investing in custom ones + +## Architectural Warnings + +These affect which capabilities to recommend and how to set expectations - implementation details are in the Product skills. + +- Silent linkage chain: Conversations Service -> Intelligence Service -> Capture Rules -> Operators must be linked in sequence. Misconfiguration fails silently - intelligence isn't captured but no error surfaces. +- Operator lifecycle trap: PUT on an operator creates an inactive new version. No activation endpoint exists - must delete and POST a new one. Plan operator changes as delete+recreate, not update. +- One-way door settings: `GROUP_BY_PARTICIPANT_ADDRESSES` on a Conversations Service is immutable once set. Removing a capture rule stops ALL capture for that service. +- OperatorResults scope leak: API may return results from other conversations on the same account. Always filter by `conversation_id`. +- Dashboard vs. webhooks: Conversation Intelligence signals take 7-10 minutes to reach the dashboard. For real-time coaching, rely on webhook delivery - not dashboard polling. +- Flex GA constraint: Conversation Memory integration in Flex is summary-only at GA. Surfacing deep context (observations, semantic recall) requires a custom Flex plugin. +- Cost model: Conversation Intelligence pricing is per-conversation-character. Model selection (GPT-4.1-nano for speed/cost vs. GPT-5.2 for quality) directly affects bill. Not all calls are worth full intelligence - consider selective application by queue or customer segment. +- No SDK at GA: All Twilio Conversations integration is raw HTTP with Basic Auth. The bundled twilio-docs MCP helps look up Twilio documentation and API schemas; direct Conversation Memory and Conversation Orchestrator integration still requires SDK or raw HTTP calls. + +## Decision Rules + +### Transcription Engine Selection +- Google STT: Wider language support, good for international contact centers. Choose when multi-lingual support is the priority. +- Deepgram: Lower latency, better accuracy for English. Choose for English-primary contact centers or noisy environments. +- Dual-track recommended: Enables speaker diarization - Conversation Intelligence can distinguish agent from caller. Single-track reduces script adherence and sentiment accuracy. +- Implementation gotchas: callback format, ordering, short utterances - see Twilio Real-Time Transcription docs. + +### Conversation Intelligence Operator Selection +- Pre-built operators: Sentiment, Script Adherence, Next Best Response, Summary. Start here - immediate value, no custom configuration. +- Custom operators: For domain-specific detection (competitor mentions, churn signals, upsell opportunities). Three types: text-generation, classification, extraction. +- Selective application: Not all calls warrant full intelligence. Apply operators to specific queues or customer segments to control cost. +- Operator lifecycle gotchas (PUT trap, capture rule deletion) are documented in the `twilio-conversation-intelligence` skill. + +### Recording Method Selection +- Use `` when: Simple two-party call recording. Minimal setup. +- Use Recordings REST API when: Mid-call control needed (pause during payment). Dual-channel recording for QA. +- Use `` when: Recording must start before `` (e.g., ConversationRelay AI side). +- Use Conference `record` when: Multi-party calls. +- Critical: `` (standalone verb) is voicemail-style - NOT for recording calls. +- PCI: Never record card numbers. Use `` verb. PCI Mode is IRREVERSIBLE and account-wide. +- Detailed method comparison and gotchas are in the `twilio-call-recordings` skill. + +## GA Constraints (May 2026) + +What works: +- Conversation Intelligence v3 real-time operators (sentiment, script adherence, NBR, custom) ✅ +- Conversation Memory profile storage and Recall ✅ +- TaskRouter with custom routing signals ✅ +- Call recordings and real-time transcription ✅ + +What requires custom code: +- Flex Agent Copilot: Being replatformed onto Conversation Intelligence. Early stages - expect custom plugin work. +- Aggregated insights: No native dashboards. API-only - pipe to Tableau, PowerBI, Looker. +- Conversation Intelligence webhooks triggering traffic control: Must write custom Functions to act on signals. + +What does NOT work at GA: +- AI copilot silently listening during human conversation (Conversation Orchestrator participant modes) +- Supervisor whisper/barge via Conversation Orchestrator (use existing Flex/Conference patterns) +- Native "Next Best Action" auto-execution (operator suggests, human/backend decides) +- Automated intervention pausing outbound campaigns (planned) + +## Output Format + +After qualifying the developer, recommend: + +``` +Recommended Architecture: [Brief plain-language description of the recommended approach - e.g., "AI-augmented voice agent with real-time transcription, sentiment analysis, and agent assist suggestions via Twilio Flex."] + +Reference Skills: +- twilio-call-recordings (if recording needed) +- twilio-conversation-intelligence (if transcription and AI insights needed) +- twilio-customer-memory (if persistent customer context needed) +- twilio-conversation-orchestrator (if multi-step orchestration needed) +- twilio-taskrouter-routing (if intelligent routing needed) +- twilio-voice-insights (for call quality diagnostics) +- twilio-sendgrid-email-send (if post-call summary emails needed) + +Setup Skills: +- twilio-account-setup - if developer needs help with credentials or account structure +- twilio-iam-auth-setup - if developer asks about API key scoping or security +- twilio-webhook-architecture - if developer needs help designing or securing webhook endpoints + +Guardrail Skills: +- twilio-security-hardening (always) +- twilio-debugging-observability (always - Voice Insights, Event Streams, error triage) +``` diff --git a/plugins/twilio/skills/twilio-agent-augmentation-architect/assets/icon-large.png b/plugins/twilio/skills/twilio-agent-augmentation-architect/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-agent-augmentation-architect/assets/icon-small.png b/plugins/twilio/skills/twilio-agent-augmentation-architect/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Connect third-party AI agents (OpenAI, Bedrock, LangChain, Microsoft Foundry) + to Twilio's communication channels using the Twilio Agent Connect SDK. Covers + identity resolution, memory and context management via Conversation Memory, + conversation orchestration via Conversation Orchestrator, multi-channel + handling (Voice, SMS, RCS, WhatsApp, Chat), and AI-to-human escalation. + Use this skill when integrating an existing LLM agent with Twilio services. +--- + +# Twilio Agent Connect + +## Overview + +Twilio Agent Connect (TAC) is a Python and TypeScript SDK that integrates third-party LLM agentic applications with Twilio's communication technologies. TAC provides middleware for identity resolution, memory/context management (via Conversation Memory), conversation orchestration (via Conversation Orchestrator), and multi-channel handling (Voice, SMS, RCS, WhatsApp, Chat). + +Key Architecture Principle: TAC is not an agent runtime itself - it's middleware that enables existing LLM applications (OpenAI Agents SDK, Bedrock, LangChain, Microsoft Foundry, etc.) to leverage Twilio Conversations services. + +## Product Context + +### Core Twilio Conversations Services + +TAC integrates with three core Twilio Conversations services: + +1. Conversation Memory (Memory Store) - Persistent user context and memory management + - Profile storage with traits and attributes + - Observation and summary storage + - Session history with full conversation context + - Identity resolution (profile lookup by phone/email) + +2. Conversation Orchestrator - Multi-channel conversation lifecycle management + - Unified conversation API across all channels + - Participant management + - Communication routing + - Conversation grouping and configuration + +3. Enterprise Knowledge - Knowledge base integration + - Semantic search across knowledge bases + - RAG (Retrieval-Augmented Generation) support + - Knowledge chunk retrieval with relevance scoring + +### Supported Channels + +TAC provides built-in support for: +- Voice - ConversationRelay (WebSocket-based real-time voice) +- SMS - Text messaging +- RCS - Rich Communication Services +- WhatsApp - WhatsApp Business messaging +- Chat - Web chat integrations + +All channels support both inbound (customer-initiated) and outbound (agent-initiated) conversations. + +### ConversationRelay-Only Mode + +TAC supports a simplified "ConversationRelay-only" mode for getting started with voice conversations without requiring Conversation Orchestrator or Conversation Memory setup. This mode provides: +- TwiML generation +- WebSocket protocol handling +- Voice conversation lifecycle management +- Callback-based message processing + +## Installation + +### Python SDK + +Requirements: Python 3.10+ + +```bash +# Using uv (recommended) +uv add git+https://github.com/twilio/twilio-agent-connect-python.git + +# With server support (includes FastAPI and uvicorn for TACFastAPIServer) +uv add git+https://github.com/twilio/twilio-agent-connect-python.git --extra server + +# Using pip +pip install git+https://github.com/twilio/twilio-agent-connect-python.git +pip install "git+https://github.com/twilio/twilio-agent-connect-python.git[server]" +``` + +### TypeScript SDK + +Requirements: Node.js 22.13+ + +```bash +# Clone and build (not yet published to npm) +git clone https://github.com/twilio/twilio-agent-connect-typescript.git +cd twilio-agent-connect-typescript +npm install +npm run build +``` + +## Quick Start + +### Multi-Channel Agent with OpenAI (Python) + +```python +from dotenv import load_dotenv +from openai import AsyncOpenAI +from tac import TAC, TACConfig +from tac.adapters.openai import with_tac_memory +from tac.channels.sms import SMSChannel +from tac.channels.voice import VoiceChannel +from tac.server import TACFastAPIServer + +load_dotenv() + +tac = TAC(config=TACConfig.from_env()) +voice_channel = VoiceChannel(tac) +sms_channel = SMSChannel(tac) +openai_client = AsyncOpenAI() + +conversation_history = {} +SYSTEM_INSTRUCTIONS = ( + "You are a customer service agent speaking with a user over voice or SMS. " + "Keep responses short and conversational - a sentence or two. " + "Do not use markdown, asterisks, bullets, or emojis; your words will be " + "spoken aloud or sent as plain text." +) + +async def handle_message_ready(user_message, context, memory_response): + conv_id = context.conversation_id + + if conv_id not in conversation_history: + conversation_history[conv_id] = [] + conversation_history[conv_id].append({"role": "user", "content": user_message}) + + # Inject conversation memory and profile into OpenAI client + client = with_tac_memory(openai_client, memory_response, context) + + response = await client.responses.create( + model="gpt-5.4-mini", + instructions=SYSTEM_INSTRUCTIONS, + input=conversation_history[conv_id] + ) + + llm_response = response.output_text + conversation_history[conv_id].append({"role": "assistant", "content": llm_response}) + + return llm_response + +tac.on_message_ready(handle_message_ready) +TACFastAPIServer(tac=tac, voice_channel=voice_channel, messaging_channels=[sms_channel]).start() +``` + +### Multi-Channel Agent with OpenAI (TypeScript) + +```typescript +import { config } from 'dotenv'; +import OpenAI from 'openai'; +import { + TAC, + TACConfig, + VoiceChannel, + SMSChannel, + TACServer, + MemoryPromptBuilder, +} from 'twilio-agent-connect'; + +config(); + +const openai = new OpenAI(); +const tac = await TAC.create({ config: TACConfig.fromEnv() }); +const voiceChannel = new VoiceChannel(tac); +const smsChannel = new SMSChannel(tac); + +tac.registerChannel(voiceChannel); +tac.registerChannel(smsChannel); + +const conversationHistory: Record = {}; + +const SYSTEM_INSTRUCTIONS = + 'You are a customer service agent speaking with a user over voice or SMS. ' + + 'Keep responses short and conversational - a sentence or two. ' + + 'Do not use markdown, asterisks, bullets, or emojis; your words will be ' + + 'spoken aloud or sent as plain text.'; + +tac.onMessageReady(async ({ conversationId, message, memory, session }) => { + const convId = conversationId as string; + + if (!conversationHistory[convId]) { + conversationHistory[convId] = []; + } + + const memoryContext = MemoryPromptBuilder.build(memory, session); + const systemPrompt = SYSTEM_INSTRUCTIONS + (memoryContext ? `\n\n${memoryContext}` : ''); + + conversationHistory[convId].push({ role: 'user', content: message }); + + const response = await openai.chat.completions.create({ + model: 'gpt-5.4-mini', + messages: [ + { role: 'system', content: systemPrompt }, + ...conversationHistory[convId], + ], + }); + + const llmResponse = response.choices[0]?.message?.content ?? ''; + conversationHistory[convId].push({ role: 'assistant', content: llmResponse }); + + return llmResponse; +}); + +const server = new TACServer(tac); +await server.start(); +``` + +## Configuration + +### Required Environment Variables + +```bash +# Twilio Account Credentials +TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +TWILIO_AUTH_TOKEN=your_auth_token +TWILIO_API_KEY=SKxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +TWILIO_API_SECRET=your_api_key_secret + +# Conversation Configuration +TWILIO_CONVERSATION_CONFIGURATION_ID=conv_configuration_xxxx + +# Phone Number +TWILIO_PHONE_NUMBER=+1234567890 + +# Server Configuration (for Voice) +TWILIO_VOICE_PUBLIC_DOMAIN=your-domain.ngrok.io +``` + +### Optional Memory Configuration + +```bash +# Conversation Memory (optional) +TWILIO_MEMORY_STORE_ID=mem_service_xxxx +TWILIO_TRAIT_GROUPS=Contact,Preferences +``` + +## Cloud Platform Integrations + +### AWS Integration + +Package: `twilio-agent-connect-aws` + +Connect AWS agent services to Twilio channels: + +```bash +# With Strands SDK +pip install twilio-agent-connect-aws[strands,server] + +# With Bedrock Agents +pip install twilio-agent-connect-aws[bedrock,server] + +# With Bedrock AgentCore +pip install twilio-agent-connect-aws[agentcore,server] +``` + +Features: +- StrandsConnector - AWS Strands SDK integration with per-conversation agent isolation +- BedrockConnector - AWS Bedrock Agents (console-created agents) +- BedrockAgentCoreConnector - AWS Bedrock AgentCore (custom agent code deployment) + +Repository: https://github.com/twilio/twilio-agent-connect-aws + +### Microsoft/Azure Integration + +Package: `twilio-agent-connect-microsoft` (formerly `tac-azure`) + +Connect Microsoft Foundry agents to Twilio channels: + +```bash +# With Agent Framework +pip install twilio-agent-connect-microsoft[agent-framework,server] + +# With Voice Live +pip install twilio-agent-connect-microsoft[voice-live,server] +``` + +Features: +- AgentFrameworkConnector - Microsoft Agent Framework integration + - Supports Foundry Hosted Agents, Foundry Prompt Agents, Azure OpenAI (Responses API, Chat Completions) + - Pluggable session persistence (in-memory, file, Cosmos DB) + - Memory context injection and lifecycle hooks +- VoiceLiveConnector - Voice Live API integration + - Text-in / text-streaming-out over WebSocket + - STT and TTS handled by Twilio ConversationRelay + - Native interrupt handling via Voice Live `response.cancel` + - Server-side conversation state management + - Tool execution with async handlers + +Repository: https://github.com/twilio/twilio-agent-connect-microsoft + +## Key Features + +### Memory Management + +Automatic integration with Twilio Conversation Memory for persistent user context: +- Profile retrieval with traits +- Observation and summary storage +- Session history with full message context +- Automatic profile lookup by phone/email + +### Conversation Lifecycle + +Automatic tracking of conversation sessions and state: +- Multi-channel conversation initialization +- Participant management +- Conversation status tracking +- Graceful cleanup on conversation end + +### Message Flow + +1. Webhook/Connection Received - Twilio sends webhook (messaging) or WebSocket connection (voice) +2. Channel Processing - Channel validates and processes the incoming event +3. Memory Retrieval - TAC optionally retrieves user memories and profile from Conversation Memory +4. Callback Invoked - Your `on_message_ready` callback receives user message, context, and optional memory response +5. Response Handling - Your callback returns a response string that TAC routes to the appropriate channel + +### Outbound Conversations + +TAC supports agent-initiated conversations across all channels: +- Programmatic conversation creation +- Participant addition +- Message sending +- Full conversation lifecycle management + +## Voice-Specific Features + +### ConversationRelay Protocol + +TAC handles the full ConversationRelay WebSocket protocol: +- TwiML generation for inbound calls +- WebSocket connection management +- Message parsing and validation +- Automatic conversation initialization +- Status callback handling + +### Voice Live API (Microsoft Integration) + +The Voice Live connector provides: +- Text-in / text-streaming-out interface +- STT (Speech-to-Text) handled by Twilio +- TTS (Text-to-Speech) handled by Twilio +- Server-side interrupt handling +- No local session management required + +## Messaging-Specific Features + +### SMS Channel + +- Idempotency-based deduplication using Twilio's `i-twilio-idempotency-token` header +- Fire-and-forget webhook processing with immediate 200 response +- Automatic conversation initialization +- Profile retrieval per message + +### Multi-Channel Support + +TAC provides unified handling across SMS, RCS, WhatsApp, and Chat: +- Single `on_message_ready` callback for all channels +- Automatic channel detection and routing +- Per-channel response formatting + +## Advanced Features + +### Conversation Intelligence Integration + +Process Conversation Intelligence operator results to create observations and summaries: + +```python +from tac.core.config import ConversationIntelligenceConfig + +config = TACConfig.from_env() +config.conversation_intelligence_config = ConversationIntelligenceConfig( + configuration_id="your_ci_configuration_id", + observation_operator_sid="LY...", + summary_operator_sid="LY...", +) + +@app.post("/ci-webhook") +async def ci_webhook_handler(request: Request): + payload = await request.json() + result = await tac.process_conversation_intelligence_event(payload) + return result.model_dump() +``` + +### Custom Tools + +TAC provides built-in tools for common operations: +- Memory recall +- Knowledge base search +- Studio Flow handoff (human escalation) +- Message sending + +You can also create custom tools using the `@function_tool` decorator: + +```python +from tac.tools import function_tool + +@function_tool() +def send_email(recipient: str, subject: str, body: str) -> bool: + """ + Sends an email to a recipient. + + Args: + recipient: Email address + subject: Email subject + body: Email body + + Returns: + True on success, False on failure + """ + # Implementation here + return True +``` + +### Adapter Pattern + +TAC provides adapters for automatic memory injection into LLM runtimes: + +Python OpenAI Adapter: +```python +from tac.adapters.openai import with_tac_memory + +client = with_tac_memory(openai_client, memory_response, context) +# Memory and profile automatically injected into system messages +``` + +TypeScript Memory Prompt Builder: +```typescript +import { MemoryPromptBuilder } from 'twilio-agent-connect'; + +const memoryContext = MemoryPromptBuilder.build(memory, session); +const systemPrompt = SYSTEM_INSTRUCTIONS + `\n\n${memoryContext}`; +``` + +## Documentation Links + +- Quickstart Guide: https://www.twilio.com/docs/platform/tac/quickstart +- Overview Documentation: https://www.twilio.com/docs/platform/tac/overview +- Python SDK: https://github.com/twilio/twilio-agent-connect-python +- TypeScript SDK: https://github.com/twilio/twilio-agent-connect-typescript +- AWS Integration: https://github.com/twilio/twilio-agent-connect-aws +- Microsoft Integration: https://github.com/twilio/twilio-agent-connect-microsoft + +## Setup Wizard + +TAC includes a web-based setup wizard to automatically create required Twilio services: + +```bash +# Python SDK +git clone https://github.com/twilio/twilio-agent-connect-python.git +cd twilio-agent-connect-python +make setup # Opens http://localhost:8080 +``` + +The wizard creates: +- Conversation Memory store +- Conversation Configuration +- Generates `.env` file with all required credentials + +## Common Use Cases + +### Customer Support Agent + +Build an AI-powered customer support agent with: +- Multi-channel support (voice, SMS, WhatsApp) +- Persistent customer memory and context +- Knowledge base integration +- Human handoff capability + +### Outbound Campaign Agent + +Create an agent that initiates conversations: +- Schedule outbound calls or messages +- Personalized messaging based on customer profile +- Conversation tracking and analytics + +### Voice IVR Replacement + +Replace traditional IVR with conversational AI: +- Natural language understanding +- Context-aware responses +- Seamless handoff to human agents + +### Multi-Language Support + +Build globally accessible agents: +- Automatic language detection +- Multi-language conversation memory +- Localized responses + +## Best Practices + +### Error Handling + +TAC provides lenient error handling: +- Profile lookup failures fall back to Conversation Orchestrator API +- Memory retrieval failures continue without exceptions +- All errors logged with appropriate severity levels + +### Performance Optimization + +- Use immediate 200 responses for webhooks to prevent retries +- Enable conversation deduplication for high-traffic applications +- Leverage conversation grouping for related interactions + +### Security + +- Never commit API keys or tokens to version control +- Use environment variables for all credentials +- Implement webhook signature validation (Twilio SDK provides helpers) +- Use HTTPS for all webhook endpoints + +### Testing + +- Use ngrok for local webhook testing +- Test each channel independently before multi-channel deployment +- Implement logging for debugging webhook processing +- Use TAC's built-in logging with channel-specific logger names + +## Troubleshooting + +### Common Issues + +Memory not retrieving: +- Verify `TWILIO_MEMORY_STORE_ID` is set +- Check profile_id is present in webhook data +- Enable DEBUG logging: `TWILIO_TAC_LOG_LEVEL=DEBUG` + +Voice not connecting: +- Verify `TWILIO_VOICE_PUBLIC_DOMAIN` is accessible +- Check TwiML endpoint returns valid XML +- Ensure WebSocket endpoint is reachable +- Verify Conversation Configuration is active + +Duplicate messages: +- Ensure webhook returns 200 immediately +- Verify idempotency token is passed to channel +- Check deduplication capacity is sufficient + +Channel isolation issues: +- Verify each channel has distinct conversation sessions +- Check `configuration_id` filtering is enabled +- Ensure conversation status is properly tracked + +## Version Requirements + +- Python SDK: Python 3.10+ +- TypeScript SDK: Node.js 22.13+ +- Twilio SDK: twilio>=9.8.3 + +## License + +MIT License - see repository LICENSE files for details. diff --git a/plugins/twilio/skills/twilio-agent-connect/assets/icon-large.png b/plugins/twilio/skills/twilio-agent-connect/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-agent-connect/assets/icon-small.png b/plugins/twilio/skills/twilio-agent-connect/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Planning skill for AI-powered conversational agents. Qualifies the + developer's use case across outcome sophistication, entry point, and + customer profile to recommend the right Twilio Conversations architecture and + implementation skills. Handles both high-level requests ("build me a + voice AI assistant") and specific ones ("integrate ConversationRelay + with my OpenAI backend"). +tier: discover +--- + +## Role + +You are an AI Agent Architecture Advisor. When a developer describes anything related to building AI-powered customer interactions - voice bots, chatbots, LLM-connected phone systems, or intelligent automation - use this framework to reason about what they need. + +## When This Skill Activates + +Trigger on any of these signals: +- "AI agent," "voice bot," "chatbot," "virtual assistant," "LLM + phone" +- "ConversationRelay," "speech-to-text," "text-to-speech," "real-time voice" +- "AI customer service," "automated support," "conversational AI" +- "Conversation Memory," "Conversation Intelligence," "Conversation Orchestrator," "TAC," "Agent Connect" +- Any request to connect an LLM (OpenAI, Claude, Gemini) to Twilio Voice or Messaging + +## Step 1: Detect Specificity and Decide Your Mode + +Before anything else, assess how specific the developer's request is: + +High-level request (e.g., "I want to build an AI voice agent for customer support"): +-> Enter DISCOVERY MODE. Walk through Steps 2-4 to qualify their needs before recommending. + +Mid-level request (e.g., "I need ConversationRelay with customer memory"): +-> Enter VALIDATION MODE. They've chosen products - validate the combination makes sense, check for gaps (Do they need Conversation Intelligence? Have they considered escalation?), then recommend Product skills. + +Specific implementation request (e.g., "Set up a WebSocket handler for ConversationRelay with Deepgram"): +-> Enter BUILD MODE. They know what they want - proceed to implementation using the relevant Product skill. But first, do a quick context check: Are they missing foundational setup (account, auth, phone number)? Are they aware of the CANNOT constraints? + +## Step 2: Qualify Intent - The 5 Essential Questions + +If you lack answers to these, ask before recommending. You don't need all 5 upfront - gather organically through conversation. + +1. What outcome are you trying to achieve? + - Autonomous customer service (ordering, FAQ, booking) + - Outbound AI calling (reminders, surveys, collections) + - Voice AI for internal tools (agents, copilots) + - Conversational commerce (sales, upsell) + +2. Which channels? + - Voice only -> ConversationRelay + - Voice + SMS/WhatsApp -> ConversationRelay + Conversation Orchestrator for cross-channel + - Chat/messaging only -> Conversation Orchestrator + your LLM (no ConversationRelay needed) + - Omnichannel -> Full Twilio Conversations stack + +3. Do you need the agent to remember customers across sessions? + - No (stateless, each call is independent) -> Skip Conversation Memory + - Yes (returning customers, order history, preferences) -> Add Conversation Memory + +4. Do you need real-time supervision or analytics? + - No -> Skip Conversation Intelligence + - Yes (compliance monitoring, sentiment detection, churn risk) -> Add Conversation Intelligence + +5. Will the AI ever need to hand off to a human? + - No (fully autonomous) -> No TaskRouter needed + - Yes (escalation for complex issues) -> Add TaskRouter + design escalation payload + +## Step 3: Assess Sophistication - The Capability Ladder + +Walk the developer up this ladder based on their answers. Each level adds products and complexity. Stop at the level that matches their stated outcome. + +### Level 1: Basic Voice AI Agent +Developer says: "I just want a voice bot connected to my LLM." +Architecture: ConversationRelay + WebSocket server + LLM API +What it does: Phone call -> Twilio transcribes speech -> sends text to your WebSocket -> you call your LLM -> return text -> Twilio speaks response +Products: ConversationRelay (managed STT/TTS) +Implementation paths: +- Fast path (recommended): `twilio-agent-connect` - Python/TypeScript SDK, multi-channel support (Voice, SMS, RCS, WhatsApp, Chat), automatic memory integration, OpenAI adapter +- Microsoft Azure deployment: `twilio-agent-connect-microsoft` - Microsoft Agent Framework connector (Foundry Hosted/Prompt Agents, Azure OpenAI), Voice Live connector with native interrupts +- AWS deployment: `twilio-agent-connect-aws` - Strands SDK connector, Bedrock Agents connector, Bedrock AgentCore connector +- Custom path: `twilio-voice-conversation-relay` + `twilio-voice-twiml` - Manual WebSocket server, full control + +### Level 2: + Customer Memory +Developer says: "I want it to remember who's calling and their history." +Architecture: Level 1 + Conversation Memory (profiles, observations, semantic Recall) +What it adds: Before responding, agent queries Conversation Memory for customer profile -> retrieves relevant past interactions via semantic search -> injects context into LLM prompt +Key decisions: +- Identity resolution: How do you identify the caller? (phone number, email, account ID) +- Memory scope: What should be remembered? (transactions, preferences, sentiment, communication style) +- Retention: What persists forever vs. what gets summarized over time? +Implementation: +- With TAC SDK: Automatic memory retrieval built-in (configure `MEMORY_STORE_ID` env var) +- Without TAC SDK: Manual Conversation Memory API integration via `twilio-customer-memory` skill + +### Level 3: + Real-Time Intelligence +Developer says: "I want to detect sentiment, monitor compliance, or trigger actions mid-conversation." +Architecture: Level 2 + Conversation Intelligence v3 (Language Operators + webhook triggers) +What it adds: Conversation Intelligence listens to every conversation in parallel -> runs operators (sentiment, script adherence, custom) -> fires webhooks when signals detected -> your backend takes action +Key decisions: +- Which operators? Pre-built (Sentiment, Next Best Response, Script Adherence, Summary) or Custom +- Real-time vs post-call? Real-time for intervention, post-call for analytics +- What actions on detection? Webhook to your backend, Twilio Function trigger, log for review +Relevant bundled skills: + `twilio-conversation-intelligence` + +### Level 4: + Human Escalation +Developer says: "When the AI can't handle it, I want it to route to the right human agent." +Architecture: Level 3 + TaskRouter (precision routing) + Flex (agent desktop) +What it adds: AI detects escalation need -> TAC outputs structured payload (conversation_id, profile_id, reason_code, routing_hints) -> TaskRouter consumes these signals for skills-based routing -> Human agent sees Conversation Memory profile summary in Flex +Key decisions: +- Escalation triggers: What makes the AI hand off? (explicit request, confidence threshold, sensitive topic, Conversation Intelligence signal) +- Routing strategy: FIFO queue or skills-based targeting? (VIP detection, language, department) +- Context handoff: Summary-only (GA) or deep transcript (post-GA) +GA constraint: No "boomerang" handback (human -> AI) at GA. No AI copilot mode during human conversation. +Relevant bundled skills: + `twilio-taskrouter-routing` + +## Architectural Warnings + +These affect which products to recommend and how to set expectations - implementation details are in the Product skills. + +- Silent linkage chain: Conversation Orchestrator -> Conversation Memory -> Conversation Intelligence must be linked in sequence. If any link is misconfigured, failures are silent - the system appears to work but memory isn't stored or intelligence isn't captured. This is the #1 debugging time sink. +- SDK availability: Twilio Agent Connect SDK (Python 3.10+ and TypeScript/Node.js 22.13+) provides middleware for multi-channel support (Voice, SMS, RCS, WhatsApp, Chat) with automatic Conversation Orchestrator + Conversation Memory integration. Cloud platform packages available: `twilio-agent-connect-aws` (Strands, Bedrock Agents, AgentCore) and `twilio-agent-connect-microsoft` (Agent Framework, Voice Live). ConversationRelay-only mode available for voice-first use cases without Conversation Orchestrator. +- One-way door settings: `GROUP_BY_PARTICIPANT_ADDRESSES` on a Conversations Service cannot be changed once set. Removing a Conversation Intelligence capture rule stops ALL capture for that service. +- Operator lifecycle trap: Updating a Conversation Intelligence operator via PUT creates an inactive new version with no activation endpoint. Must delete and recreate. +- Dashboard latency: Conversation Intelligence signals take 7-10 minutes to appear in the console dashboard. Use webhook delivery for real-time action. +- Tunnel reliability: Dead ngrok tunnels cause silent webhook delivery failure. For production, deploy to cloud infrastructure. + +## Step 4: Qualify Context - Entry Point & Customer Profile + +### Entry Point: Pure AI or Hybrid? +- Pure AI agent (no humans in the loop): Levels 1-3 are your world. Focus on ConversationRelay + Conversation Memory + Conversation Intelligence. +- Hybrid (AI handles tier-1, humans handle complex): You need Level 4. Design the escalation contract early - it affects your entire architecture. + +### Customer Profile: How does this change the recommendation? + +ISV (building for multiple clients): +- Multi-tenant Conversation Memory: Separate Memory Stores per client (max 15 per account) +- Per-client Conversation Intelligence operator configs +- Compliance: Each client may have different retention policies +- Likely needs Segment Bridge for client CRM integration + +Enterprise: +- No ngrok: Must use production-grade tunneling or deploy to cloud (dead ngrok tunnels are a common debugging time-sink) +- Compliance operators: Script adherence and regulatory monitoring likely required +- Segment Bridge: Bidirectional sync with existing CDP +- Custom operators: Enterprise-specific detection rules + +SMB / Startup: +- Start at Level 1, prove value, then add levels +- Use managed defaults - don't over-engineer memory or intelligence upfront +- Quickstart path: Twilio Agent Connect SDK + OpenAI -> multi-channel working demo in under an hour +- Use setup wizard in SDK repos for automated Memory and Conversation Orchestrator configuration + +### Regulatory Context +- TCPA: AI voice agents making outbound calls require prior express consent. Automated/prerecorded voice = strict consent rules. Quiet hours (8am-9pm recipient local time). +- HIPAA: If the AI agent handles PHI (healthcare), BAA with Twilio required. Recording encryption mandatory. Minimize PHI in TTS output. API key rotation. +- PCI DSS: If AI agent collects payment info, use `` verb. Never let LLM process or log card numbers. PCI Mode is IRREVERSIBLE and account-wide. +- GDPR: EU call recording requires explicit consent. Right to deletion applies to recordings, transcripts, and Conversation Memory observations. +- FDCPA: AI agents for debt collection must include Mini-Miranda disclosure. Max 7 attempts per debt per 7-day window. Developer must enforce - Twilio does not. + +### Tech Stack Considerations +- ConversationRelay WebSocket server: Deploy behind load balancer for redundancy. Configure `action` URL on `` for graceful fallback to DTMF IVR on disconnect. +- LLM provider failover: WebSocket server should detect LLM timeouts and fall back to secondary provider or scripted response. +- Session state persistence: Persist conversation history to Sync, Redis, or DynamoDB for WebSocket reconnection scenarios. +- Functions scaling: 30 concurrent executions/service, 10-second timeout. Status callbacks at 50 concurrent calls = 300 invocations. Use thin-receiver pattern or external compute. +- Multi-region: Twilio processes calls in closest region. Use `TWILIO_EDGE` for explicit control. Co-locate WebSocket server with Twilio region for lowest latency. + +## Decision Rules + +### Twilio Agent Connect SDK vs Manual Integration + +Use Twilio Agent Connect SDK when: +- Building a new Voice or SMS AI agent from scratch +- Want fastest time-to-value with batteries-included approach +- Need multi-channel support (Voice + SMS) from one codebase +- Customer Memory is a core requirement +- Team is comfortable with Python 3.9+ or TypeScript/Node.js 22.13.0+ +- Don't need access to low-level ConversationRelay protocol events + +Use Manual Integration when: +- Need full control over WebSocket lifecycle and protocol handling +- Building advanced features not yet in SDK (interrupt handling in Python, handoff callbacks in Python) +- Integrating into existing WebSocket server infrastructure +- Need to customize beyond SDK's callback model +- Voice-only and need access to raw ConversationRelay events (setup, DTMF, etc.) + +Key difference: Twilio Agent Connect is middleware that abstracts channel complexity. Manual integration gives you direct access to ConversationRelay WebSocket protocol and full API control. + +### Cloud Platform Selection (TAC SDK) + +If using Twilio Agent Connect SDK, choose the right integration package for your infrastructure: + +Use core TAC SDK (`twilio-agent-connect`) when: +- Deploying on any infrastructure (cloud-agnostic) +- Using OpenAI or Anthropic APIs directly +- Need maximum flexibility in LLM provider choice +- Don't need cloud-native agent orchestration + +Use Azure integration (`tac-azure`) when: +- Deploying on Azure infrastructure (App Service, Container Apps, AKS) +- Using Azure AI Foundry for agent management +- Want Azure OpenAI with Microsoft Agent Framework orchestration +- Need Azure-native session storage (CosmosDB) +- Using Azure Voice Live for low-latency streaming + +Use AWS integration (`tac-aws`) when: +- Deploying on AWS infrastructure (ECS, Fargate, EKS, Lambda) +- Using AWS Bedrock models (Claude, Titan, etc.) +- Want AWS-managed agent runtime (Strands, Bedrock AgentCore) +- Using Bedrock Agents console for agent configuration +- Need AWS-native orchestration and knowledge base integration + +### ConversationRelay vs Media Streams +- Use ConversationRelay when: You want managed STT/TTS, fast time-to-value, JSON text protocol. This is the default choice for 90% of voice AI use cases. +- Use Media Streams when: You need raw audio access, custom STT/TTS pipeline, audio processing (noise cancellation, speaker diarization), or full bidirectional audio control. +- CANNOT: Mix ConversationRelay and Media Streams on the same call. Choose one. +- CANNOT (ConversationRelay): Access raw audio, auto-reconnect WebSocket, change voice mid-session (only language), handle SMS/messaging (voice only), record via ConversationRelay itself (use separate `` before ``). + +### STT/TTS Provider Selection +- Deepgram: Best real-time accuracy, lowest latency. Supports nova-3-general model. Default recommendation. +- Google: Widest language coverage. Use when multi-lingual support is the priority. +- ElevenLabs: Best voice quality and naturalness. Use for customer-facing premium experiences. Requires account enablement. +- Amazon Polly: Cost-effective for high volume. Fewer voice options. +- Multi-lingual: The supported language set is the INTERSECTION of your chosen STT and TTS providers. Check compatibility before committing. + +### When to Add Conversation Memory +- Add if: Customer calls back and should be recognized. Personalization matters. You need to recall past interactions. +- Skip if: Every call is independent (hotline, one-time surveys). Stateless is simpler. +- Key gotcha (TypeScript SDK): Voice Memory has a known bug (userMemory hardcoded to undefined for voice). Use manual `retrieveMemory()` workaround. Python SDK works correctly. + +### When to Add Conversation Intelligence +- Add if: You need real-time supervision, compliance monitoring, or coaching signals. +- Skip if: Pure autonomous agent with no monitoring needs. Add it later when you need analytics. +- Key gotcha: Operator updates via PUT create an inactive new version - there is no activation endpoint. You must recreate the operator to apply changes. +- Key gotcha: OperatorResults may return results from other conversations. Filter by conversation_id explicitly. + +## GA Constraints (May 2026) + +What works: +- ConversationRelay: Full STT/TTS/WebSocket pipeline ✅ +- Conversation Memory: Profiles, observations, summaries, semantic Recall, identity resolution ✅ +- Conversation Intelligence v3: Real-time Language Operators, webhook triggers ✅ +- TAC escalation: Structured payload to TaskRouter ✅ + +What requires custom code: +- Cross-channel binding: Must explicitly pass ConversationId (no automatic stitching) +- Subject discrimination: Developer must build query normalization (Conversation Orchestrator can't separate topics) +- Channel switching context: Must manually hydrate context via Conversation Memory Recall + +What does NOT work at GA: +- Boomerang handback (human -> AI return) +- AI copilot mode during human conversations +- Primary channel governance / turn-taking +- Delegated authority / scoped tokens (planned) +- Outbound orchestration (planned) +- Native dashboards (API-only, pipe to your own BI tools) + +## SDK Options + +Twilio Agent Connect SDK (Recommended for most use cases): +- Middleware SDK available in Python and TypeScript (Public Beta) +- Handles ConversationRelay + Conversation Orchestrator + Conversation Memory integration automatically +- Unified callback model for Voice and SMS channels +- Automatic memory retrieval (when configured) +- Setup wizard for Memory Store and Conversation Service creation +- Use `twilio-agent-connect` skill for implementation guidance + +Raw API Integration (Advanced/Custom use cases): +- Direct HTTP calls to Conversation Memory, Conversation Orchestrator, Conversation Intelligence APIs +- Required for advanced features not yet in SDK +- More flexibility but more integration complexity +- Use product-specific skills: `twilio-customer-memory`, `twilio-conversation-orchestrator`, `twilio-conversation-intelligence` + +Always recommend `twilio-debugging-observability` guardrail skill alongside any Twilio Conversations implementation. + +## Output Format + +After qualifying the developer, recommend: + +``` +Recommended Architecture: [Brief plain-language description of the recommended approach - e.g., "AI voice agent using Agent Connect with long-term memory via Customer Memory API and Conversation Orchestrator for multi-step task handling."] + +Implementation Path: +- Fast path (recommended): Use Twilio Agent Connect SDK -> Install `twilio-agent-connect` skill + - Handles Voice + SMS channels + - Automatic memory integration when configured + - Python 3.9+ or Node.js 22.13.0+ + - Setup wizard for Memory Store and Conversation Service creation + +- Custom path (advanced): Manual integration -> Install individual product skills below + +Product Skills (for custom/advanced implementations): +- twilio-voice-conversation-relay (voice AI - manual WebSocket server) +- twilio-customer-memory (manual memory integration) +- twilio-conversation-intelligence (Conversation Intelligence webhook processing) +- twilio-taskrouter-routing (human escalation routing) +- twilio-conversation-orchestrator (conversation orchestration) +- twilio-media-streams (if custom STT/TTS needed instead of ConversationRelay) +- twilio-sendgrid-email-send (post-interaction email summaries) + +Setup Skills: +- twilio-account-setup - if developer needs help with credentials or account structure +- twilio-iam-auth-setup - if developer asks about API key scoping or security +- twilio-numbers-senders - number type selection affects throughput and compliance timelines; use when choosing between local, toll-free, or short code +- twilio-webhook-architecture - if developer needs help designing or securing webhook endpoints (especially for enterprise - tunnel alternatives) + +Guardrail Skills: +- twilio-security-hardening (always) +- twilio-debugging-observability (always - error triage, Event Streams, Voice Insights) +- twilio-reliability-patterns (for production deployment) +``` diff --git a/plugins/twilio/skills/twilio-ai-agent-architect/assets/icon-large.png b/plugins/twilio/skills/twilio-ai-agent-architect/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-ai-agent-architect/assets/icon-small.png b/plugins/twilio/skills/twilio-ai-agent-architect/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 zcmeHKg;&&3xBU$vAtfLTNOwpoEg;McDcvP7G)N2~rL=S-AvH8gNOwpJjC2Y}Nr=SI z15!`ld+YrXZ+&;&efK_R-8}2A8&R6-io}Gpga7~#D=R^@|FXtE;A8(SbAH>`0{|h1 z0|cV!VC4(|KG`{G-nzPTR1sqx-hTNbf}UW%D(!n@JM={cT439I;9;PbX#PG651AIU zh&u!amk3M@gTpBAgS0^Y&qIR9L$zLgv{sl^dE@Jg-rU4|m0nq}LwjJp`>!_OVC|Fu9rw}w_`49?>=~@1V$-Q7vxhBF%2=%osz?_XCsdw4!d+Ve zD;U9)lG?LTxAS?&f#$_BEkK37HWFT6n1sO^MVlHLMH>Z$?g}*TyiQzImKI@tG^Z_g zWR+;rMf1zzIZ#&y#f&Y6vGIZskZc4o1)@o_W|;vt*jp#Yi*=c&N9}9`I5e+IN+cvC zI7~@UP}A|1Gg9F|Kp?|K4*2IT|(OksWjt!WpAEZ)_UlV7IGfsX64L{}+P+s14vH=NJoL_pfux!>k(NcQL0 zScy2gd4O&($2j&t7uG!BzlyUIOo|WWGZ9u`Jx;`#1Q<7h&MfW^;=QvFo5$mkBmRgN zmxr4T(62si2KYmFSMPzbFx^}kz*yH~AL1=${2qDhNG$H>j)|hy_(so(>}fVgF6Fv4 zDJk*T<)10xS4K3-ztbepC76+q8*dut{)OfFkT&c<-fH~mDeozRf(7}DcX}4|^OUw- zqtya*gjHb_YcG8GvhmO%s5Qw8@Dho6P*pGFkn5T#7Xrvb?IR`Ov4UgCWBnUq3saMzk}1;#MUbM=J#km*5z`DBGUU&_mA*2FZ6hQS6R$tB&?g{Nl?5XYHob$y;>hv0|K69H8XW*wc2zTnq z>HpEEw!U?w?c>}|F9VDXRCQDT+&wb80A1q*&`tN#9AcNmbzw6_D2~iYd4oW*Z8} zuw&L^6N2clsIVp+6L<8*B#ExRqP$5woRaz_dEHW+5(0){0 zR-~>Rr!=ChQ#4YHT{Kk`QF zi{m4>jDIF8^95f~0A#;(ba;5scsLyOIju5TnpH{#VlVmB)tIL;<8_GW3x9!9mhH!1 zlNPz>SiE%(;@XqjQ=TbyKJU~EP`d5FGV3;DhFK=aNAy5o?+b$AiSYP!xqj8?Cn~z^ z{_JVm!IK2V{=?O^!S2<^lAFE5Z-+g2%6M{lXm}p-Q0v+1I+UrG4V4v_+3A(ea5sINZ4Ulu=Gx$@3Gxjq+AC$a)KTthLp*v?CfkeysW}6C+MMdEIus(-EW|Ri1$x`D;nP zCiaAO9X4A=Pui&$P;*WRbCFHaVyLzW|JkntcA**x0z+bbO_P`!WH94O6^ij3bF{L1 zXVG%*zI+@Hkgd{Ji>+Gbxmeh)V&pRNy7J2MIJ>vHcSHRlT+B7hYRpv-Gx-<@I%4M? zL2qj>d#_c`quv#0GweMyRv}ZtC9<8N7~CKCJ9a=JLQxDlR_SJ5K`N~(cVAX@2B7}b5RX1`6qA4SQ|z_ts-=B9x`9iT?c7=2q|0;C>tV>oaN-YR09w?<0%S?OzdN6XZL|4eUStZ!nGNldAVs z%=cq{csG2jt+17~{Ni?J-(v1-_fEh6AeuB)ITfPEuU9!UJR?=ULTAC)>^r_=p&F;6 zW;t>NS%P>wN;@s?dj1|Ju_Cc!4A5qLxpF1)b9dlge|S%Lf})S2Lc%@9eTRC7SFf~P zcwEA>-i=Isg~$K67}(DmrTUq+pl|H7hHx|Wx!!3)tdJlNr6=AdBWgFL_R(dFT+X=r znpc_U-{XtoCq_SxcI(KLwzloHEp&=q-VQpPP3hTLk5MAovqz-b++6lsT25%T?fBVV zg3-)KRxwopF>7Px{o8Q#TwP2PWM(~zaEtpAr#UUzxww|4x%anIEGFAlG z?=~H{GFH?4YPa++baLBS{hk#sCa~n*=5^VK+24hrBJM@>hNENqVq=#rS3b06`C3CoPvc5?bJd@rd_-ka?&?m*X2#BP12HjE0&nY*>L>&*1gHY|{5~BX5Avc6 zes~0XB)lx`$PLsv)L+}5@Q(HFK(j7ujBdZ_3gEi?cvrKVf-c3o?{NSA7Z4`w-O`j(wLyRB&{JDo2LN#C<($)7r?-wT|7_2; zXCh#X_wEaSoH@L{IhwB!A;M;T)v|Yc(BaO7leoMtg(sNtdp}Q%Hh5zvnn`VNT>oMC zNfJ+e=ac6X-&H~CtHa3wZ_74KPGfbr$w)agBKs{B$c?3cyklEZ{+7d!^&Tx$K862+ z+P{*y;3A$RK_$Suf|oiOw&td>qMP2%axqEt>kNdfSv-?@3|f@TbH^DlAh zhQ}-U6kqLXM5_$fc(om-!p^afAzmw|C9c8|Ihkw zb{%4E1%P|U%1}97!`>Xj9SDUY zX%hg7^gBDSnrlLKgci;Y$?fe;u6PAy;RN|5;0lg+j4_dF2=H|lE!TE`mT}KwmRpnB zz?LnZ-Jq5HCd)KV1tq)0!BuV8N*C@s=+(@R%md57i)I&SMf_s(o?wN=5jRDbm^?_* zn%&1Q_=QlBb33JFw1PPV5it4^!V2g5<^@$1aU!n_9kJ>5Gn9|EKp+mz`LGg`C>>o} zKu*LfQ|L>I&!1Hq<=flaCsuZGs;{5gRwFBcDL0|>H_PvLJhndyw9rjd_cgzy+VMKF#XXV}^xw@s#Kw7AXgUq0IL8Xv!d8 z32U~;CZ9#O#Wis^wjL~vB98!OKBNU#BzHaKCnjS#mzAbS3%oOxc7ESVLcl55+;lzc zt7Q@XVO0lfbwc@^wegrImzhcUkk9B>bzqZ!zBGE+D}+$-*Pp12>2Mx({5;yJS%g)7 z_3*s{wF?38cZG`r!#gbxhIS$%?aF2FR zuD;_6JWzI%&9fFW9nP25=&rD{`0drzJ27&Zye1|}OaDDFFeyozMKrV`?;VIXRA_-2 z0)$%Q6tF7j*<;jfoUSpQ$O*Li6E5$EpLzDLVK@`v&BiB8hBORW!@ZPX<}-9NIm&Nb!`x zxhF*xjplgMrmGWpL4XkAG#bKKzsB1=F2h*X0454I-UjkZ*|RQbz4Y1da( zDqe==`D`bO*ic&=yiFt@5D_Km^0_R!)JmDq9VMb`$L&Ff`oKE2?x5j zW<+SXv!GWP!S5gX+r$sR{HHnRT3O9o>pun&AE{Edq*NAL0-nj43_R2nkB+wEeAn*L z!NA_RDE?x4$h;Vp@W`j7@Fd$xCfHOYW?}fIQ-Z8Ka=%Hu)hhiKTgipBYgNi{CMkRC zmjRb&qMV^W&ise5FgbljTgJpCF2Q-XvwW(DqcQH{N$y(#V!(v<#)@Mrh5Ny0WT}E} z$7^)k4guy%V4^~8!F$yb&3*qtbiSkSCCoju)GogMIJA9$$StckGEeh?@pS+-h!%~P z;uA8;slqGJ;3Jc@w|&la=O{mr;Ng2--oF=d-L{e$2*LPrsr{kucVjuDz+8niTr7ra zr$ow5m_5D~+~YxA)?f?N)^0{hoCu{c8s0XVd|Pp9!lzC9f^ep5pr{hics!e~v!NS3 zlV0lcwtM)_rlI%`CYEiYt?sNUrtqv@r&z2da5U|=$Br@sUvfWcscV9PTC4h=#4&e_ zT|uDr?YBb2jTKM}{vj*4-=G#7x5l6`s~c#7Iu&aOXXOseztNB7kvC~SylvZ*=zSD) z52uEogmV5Pd6DfFcI}4`nWWZQ-=6A4*_(Ksfclv17OwDf%-{3UcN9!6ZuzfMcvo~BqKbas#zHsLb z4P7pJ)Zsd4!Z7UPs#JI@Tuk^ex5>d5#a!s3A}W0+n5y&g%_nzl2ISD`RZRSyY2>VZ zB}vQ5>EZq*Cq~pO&6BZQ(AIu2U(m{4^TsT%!OINkcy0z0YiO_Df2+2*Z*|%InRRmU zUOlB+YC{*b^x%VQ8Kde!zmlmBx^@Eim8HAIc~Ml9M`*u_g3X&LmJ4E9sFnMe;iNIm z?SXl4)zdwita7vPOu3W20RPz%3?h_%Q)cE;7*9ChJ%tJScFwS@_F-J``({4Rx88E1 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Record Twilio voice calls correctly. Covers the critical distinction + between Record verb (voicemail) and Dial record (call recording), + dual-channel for QA, mid-call pause for PCI, Conference recording, and + the ConversationRelay workaround. Use this skill whenever you need to + capture call audio for compliance, QA, or analytics. +--- + +## Overview + +Twilio offers multiple recording methods. Choosing the wrong one is the #1 developer mistake in voice - using `` when you mean `` produces voicemail behavior instead of call recording. + +| Method | What it does | Use when | +|--------|-------------|----------| +| `` verb | Records the CALLER only (voicemail-style) | Leaving a message, capturing input | +| `` | Records BOTH parties on a call | Call recording for two-party calls | +| `` | Starts a recording alongside other verbs | ConversationRelay, multi-verb flows | +| Conference `record` | Records the conference mix | Multi-party calls | +| Recordings REST API | Programmatic control mid-call | Pause during payment (PCI) | + +--- + +## Prerequisites + +- Twilio account with a voice-capable phone number - see `twilio-account-setup` +- `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN` - see `twilio-iam-auth-setup` +- SDK: `pip install twilio` / `npm install twilio` +- A webhook endpoint for recording status callbacks +- Compliance check: Recording consent requirements vary by jurisdiction - see `twilio-compliance-traffic` + +--- + +## Quickstart + +### Record a Two-Party Call (Most Common) + +Use `` - NOT ``. + +Python (Flask) +```python +from flask import Flask, request +from twilio.twiml.voice_response import VoiceResponse + +app = Flask(__name__) + +@app.route("/voice", methods=["POST"]) +def incoming_call(): + response = VoiceResponse() + response.say("This call may be recorded for quality assurance.") + dial = response.dial( + record="record-from-answer-dual", # dual-channel: agent on one, caller on other + recording_status_callback="https://yourapp.com/recording-status" + ) + dial.number("+15558675310") # agent's phone + return str(response) +``` + +Node.js (Express) +```node +app.post("/voice", (req, res) => { + const response = new VoiceResponse(); + response.say("This call may be recorded for quality assurance."); + const dial = response.dial({ + record: "record-from-answer-dual", + recordingStatusCallback: "https://yourapp.com/recording-status", + }); + dial.number("+15558675310"); + res.type("text/xml").send(response.toString()); +}); +``` + +### Handle the Recording Status Callback + +> Security: Validate `X-Twilio-Signature` on recording callbacks in production. Without validation, attackers could POST fake recording URLs to your endpoint. + +Python (Flask) +```python +@app.route("/recording-status", methods=["POST"]) +def recording_status(): + recording_sid = request.form["RecordingSid"] + recording_url = request.form["RecordingUrl"] + call_sid = request.form["CallSid"] + status = request.form["RecordingStatus"] # "completed", "failed" + duration = request.form.get("RecordingDuration", 0) + + if status == "completed": + # Store recording reference + save_recording(call_sid, recording_sid, recording_url, duration) + + return "", 200 +``` + +--- + +## Key Patterns + +### Recording Modes for `` + +| Mode | What's recorded | Use case | +|------|----------------|----------| +| `record-from-answer` | Single channel, both parties mixed | Simple recording | +| `record-from-answer-dual` | Dual channel - caller on left, agent on right | QA (separate agent/caller audio) | +| `record-from-ringing` | Records from ring, not answer | Capture ring time + full call | +| `record-from-ringing-dual` | Dual channel from ring | QA with ring time | + +Always use `dual` for QA and analytics. Dual-channel lets speech analytics tools (like Conversation Intelligence) distinguish agent from caller. + +### Conference Recording + +Record multi-party calls via the Conference: + +Python +```python +response = VoiceResponse() +dial = response.dial() +dial.conference( + "support-room-123", + record="record-from-start", # Records from when conference starts + recording_status_callback="https://yourapp.com/conf-recording-status" +) +``` + +Note: Conference recording captures the main audio mix. Coach/whisper audio is NOT included. See `twilio-conference-calls`. + +### ConversationRelay Recording + +Critical: `record:true` on the REST API call is silently ignored with ConversationRelay. No error. No recording. + +Correct approach: Use `` in TwiML before ``: + +Python +```python +@app.route("/voice", methods=["POST"]) +def voice(): + response = VoiceResponse() + response.say("This call may be recorded.") + + # Start recording BEFORE connecting ConversationRelay + start = Start() + start.recording( + recording_status_callback="https://yourapp.com/recording-status", + recording_status_callback_event="completed" + ) + response.append(start) + + # Now connect ConversationRelay + connect = Connect() + connect.conversation_relay(url="wss://yourapp.com/ws/voice") + response.append(connect) + + return str(response) +``` + +Node.js +```node +app.post("/voice", (req, res) => { + const response = new VoiceResponse(); + response.say("This call may be recorded."); + + const start = response.start(); + start.recording({ + recordingStatusCallback: "https://yourapp.com/recording-status", + recordingStatusCallbackEvent: "completed", + }); + + const connect = response.connect(); + connect.conversationRelay({ url: "wss://yourapp.com/ws/voice" }); + + res.type("text/xml").send(response.toString()); +}); +``` + +### Mid-Call Pause for PCI Compliance + +Pause recording when a customer provides payment information: + +Python +```python +def pause_recording_for_payment(call_sid, recording_sid): + """Pause recording during credit card capture.""" + client.calls(call_sid).recordings(recording_sid).update( + status="paused" + ) + +def resume_recording(call_sid, recording_sid): + """Resume recording after payment processed.""" + client.calls(call_sid).recordings(recording_sid).update( + status="in-progress" + ) +``` + +Node.js +```node +async function pauseForPayment(callSid, recordingSid) { + await client.calls(callSid).recordings(recordingSid).update({ + status: "paused", + }); +} + +async function resumeRecording(callSid, recordingSid) { + await client.calls(callSid).recordings(recordingSid).update({ + status: "in-progress", + }); +} +``` + +PCI DSS: Never record card numbers. Use Twilio's `` verb when possible. If collecting verbally, pause recording for the duration. PCI Mode is IRREVERSIBLE and account-wide - use a sub-account if only some calls need PCI. + +### Accessing Recordings + +Python +```python +# List recordings for a specific call +recordings = client.recordings.list(call_sid=call_sid) + +for recording in recordings: + print(f"SID: {recording.sid}") + print(f"Duration: {recording.duration}s") + print(f"URL: https://api.twilio.com{recording.uri.replace('.json', '.mp3')}") + +# Download a recording +import requests as req +audio = req.get( + f"https://api.twilio.com/2010-04-01/Accounts/{account_sid}/Recordings/{recording_sid}.mp3", + auth=(account_sid, auth_token) +) +with open("recording.mp3", "wb") as f: + f.write(audio.content) + +# Delete a recording (GDPR right to deletion) +client.recordings(recording_sid).delete() +``` + +### Recording Storage & Retention + +| Feature | Default | Notes | +|---------|---------|-------| +| Storage location | Twilio cloud | Can configure external storage (S3, GCS) | +| Retention | Indefinite | Delete manually via API or set auto-delete policy | +| Formats | WAV (default), MP3 | Request MP3 by appending `.mp3` to URL | +| Encryption | At rest | Additional encryption with PCI Mode | + +--- + +## Common Errors + +| Symptom | Cause | Fix | +|---------|-------|-----| +| Recording captures only caller (no agent) | Used `` verb instead of `` | Switch to `` | +| No recording at all | Used REST API `record:true` with ConversationRelay | Use `` in TwiML | +| Recording is empty / silent | Webhook endpoint unreachable, recording never started | Check StatusCallback URL reachability | +| Recording has both parties on same channel | Used `record-from-answer` (mono) | Use `record-from-answer-dual` for separate channels | +| Coach audio missing from conference recording | Expected behavior - coach audio isn't in the mix | Record coach's call leg separately | + +--- + +## CANNOT + +- `recordingTrack` has no observable effect via TwiML - The `` TwiML parameter `recordingTrack` does not isolate tracks. Use the Recordings REST API with `recordingTrack` for actual track isolation. +- Cannot start API recordings on ConversationRelay calls - REST API `record:true` is silently ignored ("not eligible for recording"). Must use `` before `` in TwiML. +- Cannot pause/resume recordings via TwiML - Only available via the REST API (`update` with `status="paused"` or `status="in-progress"`). +- Cannot get dual-channel conference recordings - Conference recording is always mono (mixed). +- Cannot get dual-channel from Calls API without explicit param - `Record=true` defaults to mono. Must specify `recordingChannels: 'dual'`. +- Cannot transcribe PCI-mode recordings - Recordings created while PCI mode was enabled cannot be transcribed, even after PCI is disabled. +- Cannot use `` verb for call recording - `` captures the caller only (voicemail-style). Use `` or `` for call recording. +- Cannot capture coach/whisper audio in conference recordings - Supervisor whisper is excluded from the mix +- Cannot reverse PCI Mode - PCI Mode is irreversible and account-wide. Once enabled, all recordings are encrypted. +- Cannot auto-delete recordings without configuration - Recordings are retained indefinitely unless you configure auto-deletion +- Cannot avoid larger file sizes with dual-channel - Dual-channel recordings are ~2x the size of mono. Factor into storage costs. + +--- + +## Next Steps + +- Conference calls: `twilio-conference-calls` +- Agent routing: `twilio-taskrouter-routing` +- Compliance: `twilio-compliance-traffic` +- Debug recording issues: `twilio-debugging-observability` diff --git a/plugins/twilio/skills/twilio-call-recordings/assets/icon-large.png b/plugins/twilio/skills/twilio-call-recordings/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-call-recordings/assets/icon-small.png b/plugins/twilio/skills/twilio-call-recordings/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Twilio CLI reference for managing Twilio resources from the terminal. + Covers installation, credential profiles, phone number provisioning, + sending SMS and email, webhook configuration, local development with + a tunneling service, debugging with watch and logs, serverless deployment, and + plugin ecosystem. Use when the developer asks to "just do it", + "set this up", "run a command", mentions "CLI", "command line", or + "terminal", or when an AI agent can execute a task directly instead + of writing application code. +--- + +## Overview + +The Twilio CLI lets you manage Twilio resources, send messages, configure webhooks, and deploy serverless functions directly from the terminal. AI coding agents can use CLI commands to provision resources and test integrations without writing application code. + +Install: + +| Platform | Command | +|----------|---------| +| macOS | `brew tap twilio/brew && brew install twilio` | +| Windows | `scoop bucket add twilio-scoop https://github.com/twilio/scoop-twilio-cli && scoop install twilio-cli` | +| Linux (apt) | See `twilio-cli/getting-started/install` for repo setup, then `apt install twilio` | +| npm | `npm install -g twilio-cli` | + +Update: `brew upgrade twilio` / `scoop update twilio-cli` / `npm install -g twilio-cli@latest` + +--- + +## Authentication & Profiles + +```bash +# First-time login - creates a local API key stored as a profile +twilio login + +# Named profiles for multiple accounts +twilio profiles:create --account-sid ACxxx --auth-token xxx --profile staging +# Avoid --auth-token as a plain argument - it will be stored in shell history. Use TWILIO_AUTH_TOKEN env var instead. +twilio profiles:list +twilio profiles:use staging + +# Run a single command against a different profile +twilio api:core:messages:list -p production + +# Subaccount access +twilio api:core:messages:list --account-sid ACxxx_SUBACCOUNT +``` + +Credential priority: explicit `-p`/`--account-sid` flag > env vars (`TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN`) > active profile. + +--- + +## Phone Numbers + +```bash +# Search available numbers (US local, area code 415) +twilio api:core:available-phone-numbers:local:list --country-code US --area-code 415 + +# Purchase a number +twilio api:core:incoming-phone-numbers:create --phone-number "+14155551234" + +# List owned numbers +twilio phone-numbers:list + +# Set webhooks on a number +twilio phone-numbers:update +14155551234 \ + --sms-url "https://example.com/sms" \ + --voice-url "https://example.com/voice" +``` + +--- + +## Send SMS + +```bash +twilio api:core:messages:create \ + --from "+14155551234" \ + --to "+14155556789" \ + --body "Your order has shipped." + +# List recent messages +twilio api:core:messages:list --to "+14155556789" --limit 10 +``` + +--- + +## Send Email (SendGrid) + +Requires `SENDGRID_API_KEY` environment variable. + +```bash +# Configure defaults +twilio email:set --from "noreply@example.com" --subject "Default Subject" + +# Send email +twilio email:send \ + --to "user@example.com" \ + --subject "Invoice attached" \ + --text "Please find your invoice." \ + --attachment ./invoice.pdf + +# Pipe output as email body +ps aux | twilio email:send --to "ops@example.com" --subject "Process list" +``` + +--- + +## Webhook Development + +```bash +# Set webhook URLs on a number +twilio phone-numbers:update +14155551234 --sms-url "https://your-tunnel-url.example.com/sms" + +# Emulate webhook events locally (requires plugin) +twilio plugins:install @twilio-labs/plugin-webhook +twilio webhook:invoke http://localhost:3000/sms --type sms +``` + +Local development: The CLI rejects `localhost` URLs directly. Tunneling services such as ngrok are not bundled with twilio-cli. install one separately, then set the public tunnel URL as your webhook. + + ngrok is NOT included in the CLI - install separately: npm install -g ngrok + or via https://ngrok.com + - Start tunnel: ngrok http 3000, then use the provided URL for webhook + configuration +--- + +## Debugging & Monitoring + +```bash +# Debug logging on any command (logs to stderr) +twilio api:core:messages:create --from +14155551234 --to +14155556789 --body "test" -l debug + +# Real-time monitoring (requires plugin) +twilio plugins:install @twilio-labs/plugin-watch +twilio watch # stream debugger alerts, calls, messages in real time + +# Output formatting +twilio api:core:messages:list -o json # JSON output +twilio api:core:messages:list -o tsv # tab-separated +twilio api:core:messages:list --properties sid,status,direction # select columns +twilio api:core:messages:list --limit 200 # override default 50-record cap +``` + +--- + +## Serverless Deployment + +```bash +twilio plugins:install @twilio-labs/plugin-serverless + +# Create a new Functions project +twilio serverless:init my-project --template blank +cd my-project + +# Local development +twilio serverless:start # serves functions at localhost:3000 + +# Deploy to Twilio +twilio serverless:deploy +``` + +--- + +## Plugins + +```bash +twilio plugins:install +twilio plugins:list +twilio plugins:remove +``` + +| Plugin | What it does | +|--------|-------------| +| `@twilio-labs/plugin-serverless` | Develop and deploy Twilio Functions and Assets | +| `@twilio-labs/plugin-dev-phone` | Test SMS/Voice without a real phone | +| `@twilio-labs/plugin-watch` | Real-time monitoring of debugger alerts, calls, messages | +| `@twilio-labs/plugin-token` | Generate client-side SDK tokens (Voice, Chat, Video) | +| `@twilio-labs/plugin-assets` | Upload static resources | +| `@twilio-labs/plugin-webhook` | Emulate webhook events for local testing | +| `@twilio-labs/plugin-flex` | Create, build, deploy Flex plugins | + +--- + +## Regional & Edge Routing + +```bash +# Login to a specific region +twilio login --region au1 --edge sydney + +# Set edge globally +twilio config:set --edge=sydney + +# Or via env vars +export TWILIO_REGION=au1 +export TWILIO_EDGE=sydney +``` + +Supported: `au1`/`sydney`, `ie1`/`dublin`, `jp1`/`tokyo`. Each region uses a different Auth Token from your US1 credentials. Find yours in the Twilio Console under API keys & tokens, selecting your target region. + +--- + +## Configuration + +```bash +twilio config:list # show all settings +twilio config:set --edge=sydney # set default edge +twilio config:set --require-profile-input # prompt before using active profile +``` + +Config stored at `~/.twilio-cli/config.json`. + +Syntax notes: +- Commands use spaces by default, using colon also works: `twilio api core messages create` = `twilio api:core:messages:create` +- `twilio [COMMAND] --help` for any command's options +- Multi-line: use `\` for line continuation + +--- + +## CANNOT + +- Default list limit is 50 records - always pass `--limit` for larger result sets. +- API timeout is 30 seconds - long-running operations may fail silently. +- Cannot use localhost URLs for webhooks - use a tunneling service, such as ngrok, installed separately. +- No autocomplete on Windows - only bash/zsh supported. +- ngrok is not bundled with twilio-cli, install separately +- Cannot send Twilio Email (comms API) via CLI - `twilio email:send` uses SendGrid only. For Twilio Email, use the REST API directly. + +--- + +## Next Steps + +- Account setup and API keys: `twilio-account-setup`, `twilio-iam-auth-setup` +- Webhook architecture and signature validation: `twilio-webhook-architecture` +- Debugging and observability: `twilio-debugging-observability` +- Send SMS via API/SDK: `twilio-send-message` +- SendGrid email setup: `twilio-sendgrid-account-setup` diff --git a/plugins/twilio/skills/twilio-cli-reference/assets/icon-large.png b/plugins/twilio/skills/twilio-cli-reference/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-cli-reference/assets/icon-small.png b/plugins/twilio/skills/twilio-cli-reference/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Registrations required BEFORE Twilio traffic works. Covers messaging + programs (A2P 10DLC, toll-free verification, WhatsApp WABA, RCS, short + code, alphanumeric sender) and voice trust programs (STIR/SHAKEN, Voice + Integrity, Branded Calling, CNAM). Each number/sender type has its own + program - registration blocks traffic until complete. +--- + +## Overview + +Most Twilio channels require registration or approval before traffic flows. Skipping this step is the #1 onboarding mistake - developers build first, then discover messages are blocked or calls labeled as spam. + +Lifecycle: Choose numbers/senders (`twilio-numbers-senders`) -> Register them (this skill) -> Follow traffic rules (`twilio-compliance-traffic`) + +--- + +## Decision Tree: What Do I Need to Register? + +### Messaging Programs + +| Sender type | Registration program | Timeline | Docs | +|-------------|---------------------|----------|------| +| US local (10DLC) | A2P 10DLC - brand + campaign | Brand: minutes. Campaign: 10-15 business days | [Overview](https://www.twilio.com/docs/messaging/compliance/a2p-10dlc) \| [Quickstart](https://www.twilio.com/docs/messaging/compliance/a2p-10dlc/quickstart) | +| US toll-free | Toll-free verification | 3-5 business days | [Console onboarding](https://www.twilio.com/docs/messaging/compliance/toll-free/console-onboarding) \| [Requirements](https://help.twilio.com/articles/5377174717595-Toll-Free-Message-Verification-for-US-Canada) | +| US short code | Pre-approved at purchase | 8-12 weeks provisioning | [Guidelines by country](https://www.twilio.com/en-us/guidelines/short-code) \| [What is a short code?](https://help.twilio.com/articles/223182068-What-is-a-Messaging-Short-Code-) | +| WhatsApp | WABA + Meta Business Verification | Minutes (sender) + weeks (Meta verification) | [Self sign-up](https://www.twilio.com/docs/whatsapp/self-sign-up) \| [Getting started](https://help.twilio.com/articles/360007721954-Getting-Started-with-Twilio-for-WhatsApp) | +| RCS | Google + carrier approval | 4-6 weeks minimum, longer multi-region | [RCS onboarding](https://www.twilio.com/docs/rcs/onboarding) \| [Compliance guide](https://help.twilio.com/articles/49174994355355-RCS-Compliance-Onboarding-Guide) | +| Alpha Sender ID | Registration in some countries | Varies by country | [How to register](https://help.twilio.com/articles/20153208099611-How-to-Register-an-Alphanumeric-Sender-ID) | +| International numbers | Regulatory bundle (many countries) | Varies | [Getting started](https://www.twilio.com/docs/phone-numbers/regulatory/getting-started) \| [How to submit](https://help.twilio.com/articles/8338625205147-How-to-Submit-a-Regulatory-Bundle-for-Phone-Number-Regulatory-Compliance) | +| Twilio Verify | Exempt - no registration needed | Immediate | - | + +### Voice Trust Programs + +| Program | What it does | Vetting timeline | Docs | +|---------|-------------|-----------------|------| +| STIR/SHAKEN | Level A attestation = trusted caller ID | 24hr (Business Profile) + 72hr (Trust Product) | [Overview](https://www.twilio.com/docs/voice/trusted-calling-with-shakenstir) \| [Onboarding](https://www.twilio.com/docs/voice/trusted-calling-with-shakenstir/shakenstir-onboarding) | +| Voice Integrity | Registers numbers with carriers to remediate spam labels | 24-48hr (profile) + 24-48hr (remediation) | [Overview](https://www.twilio.com/docs/voice/spam-monitoring-with-voiceintegrity) \| [Onboarding](https://www.twilio.com/docs/voice/spam-monitoring-with-voiceintegrity/voice-integrity-onboarding) | +| Branded Calling (US) | Verified name + logo on mobile caller ID | Public Beta (T-Mobile, Verizon) | [Overview](https://www.twilio.com/docs/voice/branded-calling) \| [FAQ](https://help.twilio.com/articles/22312096414363-Branded-Calls-FAQ) | +| Branded Calling (Non-US) | Verified caller ID branding for international numbers | Availability varies by country/carrier | [Overview](https://www.twilio.com/docs/voice/branded-calling) | +| CNAM | Business name on outbound caller ID | 48-72hr propagation | [Overview](https://www.twilio.com/docs/voice/brand-your-calls-using-cnam) \| [Getting started](https://help.twilio.com/articles/360051670533-Getting-Started-with-CNAM-Caller-ID) | + +Voice trust priority: STIR/SHAKEN first (required for Level A attestation) -> Voice Integrity (spam label remediation) -> Branded Calling (mobile only, beta) -> CNAM (simplest, lowest impact). All voice programs require an approved Trust Hub Business Profile as prerequisite. + +--- + +## A2P 10DLC - Deep Dive + +A2P 10DLC is the most common program and the most common source of onboarding delays. + +### Registration Flow + +1. Create Customer Profile - Business identity in Trust Hub (required for all programs) +2. Register Brand - EIN, business name, address, website. TCR typically approves within minutes. Respond to OTP verification within 24 hours. [Brand best practices](https://help.twilio.com/articles/4405758341659-A2P-10DLC-Brand-Approval-Best-Practices) +3. Register Campaign - Use case, 2+ sample messages, opt-in proof, privacy policy. Review takes 10-15 business days. [Campaign best practices](https://help.twilio.com/articles/11847054539547-A2P-10DLC-Campaign-Approval-Best-Practices) +4. Associate phone numbers - Link numbers to campaign via Messaging Service + +### Message Flow (Opt-In Documentation) + +The message flow field is the #1 reason campaigns get rejected. Reviewers click your links and follow your opt-in steps. If submitting via API, the field must be 40-2049 characters. + +4 required elements: +1. Description of opt-in method(s) with clear language inviting users to sign up (no pre-checked boxes) +2. Message frequency (e.g., "Up to 4 msgs/month") +3. "Message and data rates may apply" disclosure +4. Link(s) to opt-in image/mockup (must be publicly accessible) + +Example of a strong message flow: +> "Customers opt in by texting JOIN to 55555, or by checking the SMS opt-in box during checkout at shop.acme.com. The checkout page displays: 'Check this box to receive exclusive deals via text. Up to 4 msgs/month. Message and data rates may apply. Reply STOP to opt out. Reply HELP for help. Privacy Policy: acme.com/privacy. Terms: acme.com/tc.' In-store signage also promotes keyword opt-in with full disclosures. Screenshot of signage: [Google Drive link]" + +How to document opt-in by scenario: +| Scenario | What to provide | +|----------|----------------| +| Public website form | URL to your sign-up page | +| Form behind login/paywall | Screenshot uploaded to Google Drive/OneDrive (set to "anyone with link"), include public link | +| Verbal/phone opt-in | Full script of what you say and how customer confirms consent | +| Paper form | Scan/photograph the form, upload publicly, include link | +| Text keyword campaign | Screenshot of marketing materials showing keyword, upload publicly | + +All links must be publicly accessible. Non-English disclosures need a translated version included. + +### Consent Requirements + +Three tiers of consent (CTIA guidelines): + +| Tier | Required for | How to obtain | +|------|-------------|---------------| +| Implied consent | Transactional messages (order confirmations, account alerts) | Customer provides phone number during a transaction | +| Express consent | Informational messages (appointment reminders, service updates) | Customer actively opts in (checkbox, keyword, form) | +| Express written consent | Marketing/promotional messages | Signed consent with brand name, message frequency, "Msg & data rates apply," opt-out instructions | + +Critical rules: +- Consent is per-campaign. Signing up for order updates does NOT grant consent for promotions. Separate opt-ins required. +- Consent must be voluntary. If customers must opt in to messaging to complete a purchase or create an account, the registration will be rejected. +- Brand name must appear in the consent disclosure - generic "you agree to receive texts" is insufficient. + +### Privacy Policy & Terms and Conditions + +Both are required. Registrations without them are rejected. + +Privacy policy must include: +- What data you collect and how it's used +- That mobile information will NOT be shared with third parties for marketing (CTIA requirement) + +Terms and conditions must include: +- Program/brand name and description +- "Message and data rates may apply" +- Message frequency or recurring message disclosure +- Customer support contact information +- HELP and STOP opt-out instructions (displayed in bold) +- Link to privacy policy +- "Carriers are not liable for any delayed or undelivered messages" + +Pro tip: Create messaging-specific privacy policies and terms rather than updating your main company documents. Dedicated policies are easier to keep current if requirements change. + +### Mixed Use Case Campaigns + +If you send both marketing and transactional messages (e.g., order confirmations AND promotions), use the Mixed campaign use case: + +- Select "Mixed" as the campaign use case during registration +- Allows 2-5 sub-use cases within one campaign (e.g., Customer Care, Marketing, Account Notification, 2FA, PSA) +- Describe each sub-use case clearly in the campaign description +- Sample messages must cover each declared sub-use case + +Do NOT register separate campaigns for each message type unless they use different phone numbers or have different opt-in flows. Mixed is the intended solution for multi-purpose messaging from the same sender. + +### Campaign Rejection Gotchas + +| Field | Common mistake | Correct approach | +|-------|---------------|-----------------| +| Campaign description | Vague ("We send texts") | Specific ("Order confirmation and shipping updates for e-commerce purchases") | +| Sample messages | Don't match description or missing opt-out | Must reflect declared use case + include opt-out in every sample | +| Opt-in description | "Users sign up on our website" | "Users check SMS consent checkbox during account registration at checkout.example.com" with link to screenshot | +| URL shorteners | Using bit.ly links | Public URL shorteners are forbidden - use branded/vanity domains | +| Privacy policy | States data IS shared | Must state data is NOT shared with third parties | +| Links | Behind login or not accessible | All links must be publicly accessible to reviewers | +| Consent | Single opt-in covering all message types | Each sub-use case in a Mixed campaign still needs its own documented opt-in method | +| Mixed campaign | Leaving sub-use cases undescribed | Each sub-use case must be explained in description | + +Failed campaigns can now be edited directly in Console (API editing is private beta). + +### Registration Tiers + +| Tier | Daily segment limit (T-Mobile) | Notes | +|------|-------------------------------|-------| +| Sole Proprietor | ~1,000/day | Console only, 1 campaign, 1 number | +| Low-Volume Standard | ~2,000/day | Requires EIN | +| Standard | 2,000+ (scales with Trust Score) | Requires verified EIN | +| High-volume (secondary vetting) | 200,000+/day | [Secondary vetting](https://help.twilio.com/articles/4403988619163-Secondary-Vetting-for-A2P-10DLC) | + +Russell 3000 companies qualify for 200,000 segments/day automatically. + +### Common Errors + +| Error | Meaning | Fix | +|-------|---------|-----| +| `30034` | Message from unregistered number | Complete A2P registration | +| `30007` | Message filtered as spam | Check opt-in compliance and content | +| Brand rejected | Business info doesn't match EIN records | Tax ID and business name must match exactly | + +--- + +## Toll-Free Verification + +Required for US/Canada toll-free SMS. Simpler than A2P 10DLC. + +- Submit via Console (Active Numbers -> Regulatory Information tab) or [API](https://www.twilio.com/docs/messaging/compliance/toll-free/api-onboarding) +- Requires: paid account, Customer Profile, business name, website, use case description, sample message, opt-in type +- Unverified toll-free numbers cannot send SMS to US/Canada - status shows "Restricted" +- If rejected: resubmit within 7 days for priority review. After 7 days, number reverts to Restricted and resubmission goes to back of queue +- ISVs must have an approved Primary Business Profile before submitting for secondary customers +- 527 political organizations require Campaign Verify tokens before Console submission +- Don't use multiple toll-free numbers for the same use case ("snowshoeing") + +Docs: [Console onboarding](https://www.twilio.com/docs/messaging/compliance/toll-free/console-onboarding) | [Why rejected?](https://help.twilio.com/articles/9321443984155-Why-Was-My-Toll-Free-Verification-Rejected-) + +--- + +## WhatsApp WABA Registration + +### Self-Signup Flow (Direct Customers) + +1. Console -> Messaging -> Senders -> WhatsApp Senders -> "Create new sender" +2. Select phone number (Twilio or non-Twilio - must not already be registered with WhatsApp) +3. Click "Continue with Facebook" -> Meta Embedded Signup popup +4. Create or select Meta Business Portfolio +5. Create or select WABA (all senders on same Twilio account must share one WABA) +6. Set display name, category, description - Meta reviews display name post-registration +7. Phone verification via OTP (SMS or voice) +8. Registration completes within minutes + +### Post-Registration Requirements + +- Meta Business Verification required before production messaging - can take several weeks +- If display name rejected by Meta, messaging is limited to 250 messages/24 hours +- Outbound messages require pre-approved Message Templates (submitted to Meta, 24-48hr approval) +- Free-form messages only within 24-hour service window after customer initiates + +### ISV Path + +Enroll in Meta's Tech Provider Program to onboard customers. Different flow from self-signup. + +Docs: [Self sign-up](https://www.twilio.com/docs/whatsapp/self-sign-up) | [WhatsApp hub](https://www.twilio.com/docs/whatsapp) + +--- + +## RCS Onboarding + +4-6 weeks minimum. RCS has a detailed 7-part compliance process covering sender profile, privacy/ToS, eligibility, campaign details, opt-in/consent, sample messages, and common rejection reasons. + +See `twilio-rcs-messaging` for the full onboarding guide, sending patterns, and device support. + +Quick summary: Create RCS Sender in Console -> complete compliance submission -> Twilio specialist reviews -> Google + carrier approval -> add to Messaging Service -> go live. + +Docs: [RCS onboarding](https://www.twilio.com/docs/rcs/onboarding) | [Compliance guide](https://help.twilio.com/articles/49174994355355-RCS-Compliance-Onboarding-Guide) | [Regional availability](https://www.twilio.com/docs/rcs/regional) + +--- + +## CANNOT + +- Cannot skip A2P registration for US 10DLC - Mandatory for all senders, no exceptions for small volume +- Cannot register Sole Proprietor A2P via API - Console only +- Cannot combine unrelated use cases without Mixed campaign - Use the "Mixed" use case category to register 2-5 sub-use cases under one campaign +- Cannot require A2P registration for Verify traffic - Twilio Verify is exempt from A2P registration +- Cannot use voice trust programs without Trust Hub - All voice programs require an approved Trust Hub Primary Customer Profile +- Cannot use Branded Calling on landlines - Mobile-only. US: Public Beta (T-Mobile, Verizon). Non-US: availability varies by country and carrier - check eligibility for your specific numbers. Use CNAM for landlines. + +--- + +## Next Steps + +- Channel overview and onboarding guide: `twilio-messaging-overview` +- Choose the right number type first: `twilio-numbers-senders` +- Follow traffic rules after registration: `twilio-compliance-traffic` +- Set up Messaging Services for number pools: `twilio-messaging-services` +- Send SMS after registration: `twilio-sms-send-message` +- Secure your account: `twilio-security-hardening` diff --git a/plugins/twilio/skills/twilio-compliance-onboarding/assets/icon-large.png b/plugins/twilio/skills/twilio-compliance-onboarding/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-compliance-onboarding/assets/icon-small.png b/plugins/twilio/skills/twilio-compliance-onboarding/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Rules you must follow for Twilio messaging and voice traffic. Covers + TCPA (consent tiers, quiet hours, DNC), GDPR (EU consent, right to + deletion), PCI DSS (payment recording, Pay verb), HIPAA (BAA, PHI), + FDCPA (debt collection limits), CAN-SPAM, WhatsApp policies, + SHAKEN/STIR, and consent management patterns. Use this skill proactively + when developers have working traffic to ensure they follow the rules. +--- + +## Overview + +Compliance failures block sends, get numbers suspended, and expose your customer to legal liability. This skill covers the ongoing rules that apply to live traffic - what you can send, when, and to whom. + +Lifecycle: Choose numbers (`twilio-numbers-senders`) -> Register them (`twilio-compliance-onboarding`) -> Follow traffic rules (this skill) -> Secure everything (`twilio-security-hardening`) + +For registrations required before traffic works (A2P 10DLC, toll-free verification, WhatsApp/RCS sender approval, voice trust programs), see `twilio-compliance-onboarding`. + +--- + +## TCPA (Telephone Consumer Protection Act) + +Applies to all US voice calls and SMS. + +### Consent Requirements + +| Communication type | Consent required | Notes | +|-------------------|-----------------|-------| +| Informational SMS (order updates) | Prior express consent | Providing phone number during transaction usually qualifies | +| Marketing SMS | Prior express written consent | Must be clear and conspicuous, separate from T&C | +| Manual voice calls | None for existing business relationship | 18-month window | +| Autodialed / prerecorded voice | Prior express consent (informational) or written (marketing) | AI voice agents typically count as autodialed and must disclose who is calling | +| Emergency / fraud alerts | No consent required | Must be genuinely urgent | + +### Quiet Hours + +- 8:00 AM - 9:00 PM in the recipient's local time zone +- Applies to telemarketing and non-emergency calls +- Your application must determine the recipient's time zone - Twilio does not enforce this +- Use `twilio-lookup-phone-intelligence` to determine carrier/region for time zone inference + +### Do Not Call + +- Maintain an internal Do Not Call list +- Honor opt-outs within 10 business days (best practice: immediately) +- Scrub against the National Do Not Call Registry for telemarketing + +--- + +## GDPR (EU/EEA) + +### Consent for Communications + +| Basis | When it applies | Requirements | +|-------|----------------|-------------| +| Explicit consent | Marketing messages, new customer outreach | Must be freely given, specific, informed, unambiguous. Pre-checked boxes do NOT qualify. | +| Legitimate interest | Transactional messages, existing customer relationship | Requires documented balancing test. Must offer opt-out. | +| Contractual necessity | Order confirmations, shipping updates | Directly related to contract performance | + +### Right to Deletion + +Applies to ALL data stored by your application via Twilio: +- Call recordings and transcripts +- SMS/messaging logs +- Conversation Memory observations and profiles +- Conversation Intelligence operator results +- Customer profiles in your database + +Implementation: Build a deletion endpoint that removes data from all systems. Twilio retains message logs for 400 days - you can delete recordings via API but cannot delete message logs from Twilio's system before the retention window. + +### Call Recording Consent + +- EU calls require explicit consent before recording, or a documented legitimate interest basis +- Play a recording notice at the start of every call: `This call may be recorded for quality assurance.` +- Store consent records with timestamp + +--- + +## PCI DSS (Payment Card Industry) + +### Never Record Card Numbers + +- If recording calls, pause recording during payment: + +Python +```python +# Pause recording when customer gives card number +client.calls(call_sid).recordings(recording_sid).update(status="paused") + +# Use verb instead of collecting card numbers verbally +response = VoiceResponse() +response.pay( + payment_connector="stripe_connector", + charge_amount="49.99", + currency="usd", + status_callback="https://yourapp.com/pay-status" +) +``` + +- Never let an LLM process, log, or repeat card numbers +- Never store card numbers in Conversation Memory observations or Conversation Intelligence transcripts + +### PCI Mode Warning + +PCI Mode is IRREVERSIBLE and account-wide. Once enabled: +- All recordings are encrypted +- Transcript access is restricted +- Cannot be disabled - ever + +Recommendation: If you need PCI compliance for one use case, create a separate sub-account. See `twilio-account-setup`. + +--- + +## HIPAA (Healthcare) + +### Requirements + +- BAA required: Execute a Business Associate Agreement with Twilio before handling PHI +- Recording encryption: Mandatory for any call recording containing PHI +- PHI minimization in TTS: Don't speak full patient details via ``. Use minimum necessary information. +- API key rotation: Regular rotation required. See `twilio-iam-auth-setup` +- Access controls: Restrict who can access recordings and transcripts + +### Safe Notification Content + +| Channel | Safe | Unsafe | +|---------|------|--------| +| SMS | "Your appointment is tomorrow at 2pm" | "Your appointment with Dr. Smith for diabetes follow-up" | +| Voice IVR | "Press 1 to confirm your upcoming appointment" | "Press 1 to confirm your cardiology appointment" | +| Email | Can include more detail if encrypted/authenticated | Never send PHI in subject line | + +--- + +## FDCPA / Regulation F (Debt Collection) + +### Requirements + +- Mini-Miranda disclosure required on every communication: "This is an attempt to collect a debt and any information obtained will be used for that purpose." +- Call attempt limits: Max 7 call attempts per debt per 7-day rolling window +- Voicemail: Must include disclosure or use limited-content message (name, phone number, request to call back - no mention of debt) +- SMS consent: Requires separate consent from voice consent +- Time restrictions: Same as TCPA quiet hours (8am-9pm local time) +- Developer responsibility: Twilio does NOT enforce FDCPA limits. Your application must track attempt counts and timing. + +Python +```python +# Track call attempts per debt +def can_attempt_call(debt_id, db): + seven_days_ago = datetime.now() - timedelta(days=7) + attempts = db.count_attempts(debt_id, since=seven_days_ago) + return attempts < 7 + +# Include Mini-Miranda in IVR +response = VoiceResponse() +response.say("This is an attempt to collect a debt and any information obtained will be used for that purpose.") +response.pause(length=1) +response.say("Please press 1 to speak with a representative.") +response.gather(num_digits=1, action="/handle-keypress") +``` + +--- + +## WhatsApp Compliance + +### Template Requirements +- Outbound messages require pre-approved Message Templates (submitted to Meta, 24-48 hour approval) +- Free-form messages only within 24-hour service window after customer initiates +- Template rejections: vague descriptions, missing variables, promotional language in utility templates + +### Quality Rating +- WhatsApp enforces quality scoring - too many blocks/reports = rate limited or suspended +- Monitor quality in WhatsApp Manager dashboard +- Opt-in required before sending any WhatsApp messages + +### Opt-In Best Practices +- Collect WhatsApp-specific consent (separate from SMS consent) +- Clearly state what types of messages will be sent +- Provide easy opt-out (reply STOP) + +--- + +## CAN-SPAM (Email) + +- Physical mailing address required in every marketing email +- One-click unsubscribe required (SendGrid handles automatically via List-Unsubscribe header) +- Honor unsubscribe within 10 business days +- Subject line must not be misleading +- "From" address must be accurate + +See `twilio-sendgrid-email-send` for SendGrid-specific compliance features. + +--- + +## SHAKEN/STIR (Caller ID Verification) + +### Attestation Levels + +| Level | Meaning | Caller ID display | +|-------|---------|-------------------| +| A (Full) | Carrier vouches for caller identity and right to use number | Green checkmark ✅ | +| B (Partial) | Carrier vouches for caller but not number ownership | Neutral display | +| C (Gateway) | Carrier knows where call entered network, nothing else | May show "Spam Likely" | + +- Only Level A produces a trusted caller ID display +- Affects answer rates significantly for outbound campaigns +- E.164 formatting required for proper attestation +- Twilio signs outbound calls automatically when you own the number + +--- + +## Consent Management Pattern + +### Store Consent Records + +```python +# Minimum consent record +consent_record = { + "phone": "+15558675310", + "channel": "sms", # sms, voice, whatsapp, email + "consent_type": "marketing", # marketing, transactional, debt_collection + "consent_method": "web_form", # web_form, verbal, paper, api + "consent_timestamp": "2026-04-13T14:30:00Z", + "consent_source": "checkout_page", # where consent was collected + "ip_address": "203.0.113.42", # for web consent + "opted_out": False, + "opt_out_timestamp": None +} +``` + +### Opt-Out Handling + +- Process STOP/CANCEL/UNSUBSCRIBE/END/QUIT keywords immediately +- Messaging Services handle keyword opt-out automatically for SMS +- For voice: maintain your own Do Not Call list +- For WhatsApp: handle via webhook when user blocks +- For email: SendGrid manages suppression lists automatically + +--- + +## CANNOT + +- Cannot rely on Twilio to enforce compliance rules - Your application must implement TCPA, GDPR, PCI, and other rules. Twilio provides tools, not enforcement. +- Cannot apply A2P 10DLC registration outside the US - Other countries have their own regimes +- Cannot use public link shorteners (bit.ly, tinyurl, goo.gl, short.io, etc.) - Messages with public short links are categorically filtered by carriers. Use a branded/vanity short domain (e.g., `go.yourcompany.com`) configured in your Messaging Service. Twilio's shared `twil.io` domain is not sufficient - you must register your own branded domain in Console under Messaging > Link Shortening. +- Cannot reverse PCI Mode - Irreversible and account-wide once enabled +- Cannot fully clear message logs via GDPR deletion - Twilio retains internal message logs for 400 days regardless of deletion requests +- Cannot assume regulations are static - Compliance requirements change. Verify current regulations before launch. +- Cannot apply this skill's guidance outside US/EU - India TRAI DLT, Brazil LGPD, Australia Spam Act, and other jurisdictions require additional research + +--- + +## Next Steps + +- Registration before traffic works: `twilio-compliance-onboarding` +- WhatsApp sender setup: `twilio-whatsapp-manage-senders` +- Credential security: `twilio-iam-auth-setup` +- Account structure for PCI isolation: `twilio-account-setup` diff --git a/plugins/twilio/skills/twilio-compliance-traffic/assets/icon-large.png b/plugins/twilio/skills/twilio-compliance-traffic/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-compliance-traffic/assets/icon-small.png b/plugins/twilio/skills/twilio-compliance-traffic/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Build multi-party calls using Twilio Conference. Covers warm transfer, + cold transfer, coaching (whisper), hold vs mute, participant modes, and + supervisor barge. Use this skill for any contact center, support line, + or scenario requiring transfers, holds, or multi-party calls. +--- + +## Overview + +Conference is the foundation of contact center call handling. The key insight: every call that might need a transfer should start as a Conference, not a direct ``. A Conference supports hold, transfer, coaching, and recording - a direct Dial does not. + +``` +Caller ──-> Conference Room ←── Agent + ↑ + Supervisor (coach mode: speaks to agent only) +``` + +Contact center best practice: Every multi-agent call should use Conference, not direct Dial. + +--- + +## Prerequisites + +- Twilio account with a voice-capable phone number - see `twilio-account-setup` +- `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN` - see `twilio-iam-auth-setup` +- SDK: `pip install twilio` / `npm install twilio` +- For agent routing: TaskRouter - see `twilio-taskrouter-routing` + +--- + +## Quickstart + +Step 1 - Put the inbound caller into a Conference + +When a call comes in, place the caller into a named Conference room. + +Python (Flask) +```python +from flask import Flask, request +from twilio.twiml.voice_response import VoiceResponse + +app = Flask(__name__) + +@app.route("/voice", methods=["POST"]) +def incoming_call(): + call_sid = request.form["CallSid"] + response = VoiceResponse() + dial = response.dial() + dial.conference( + f"room-{call_sid}", + start_conference_on_enter=True, + end_conference_on_exit=False, # Keep conference alive when caller disconnects (for wrap-up) + wait_url="http://twimlets.com/holdmusic?Bucket=com.twilio.music.classical", + status_callback="https://yourapp.com/conference-events", + status_callback_event="join leave", + record="record-from-start" + ) + return str(response) +``` + +Node.js (Express) +```node +app.post("/voice", (req, res) => { + const callSid = req.body.CallSid; + const response = new VoiceResponse(); + const dial = response.dial(); + dial.conference( + `room-${callSid}`, + { + startConferenceOnEnter: true, + endConferenceOnExit: false, + waitUrl: "http://twimlets.com/holdmusic?Bucket=com.twilio.music.classical", + statusCallback: "https://yourapp.com/conference-events", + statusCallbackEvent: "join leave", + record: "record-from-start", + } + ); + res.type("text/xml").send(response.toString()); +}); +``` + +Step 2 - Connect an agent to the same Conference + +After TaskRouter assigns a worker, dial the agent into the conference: + +> Security: Never interpolate untrusted user input into inline `twiml=` strings. Use the SDK's `VoiceResponse` builder for any dynamic content. + +Python +```python +# Called from your assignment callback or agent connect logic +def connect_agent(conference_name, agent_phone): + client.calls.create( + to=agent_phone, + from_="+15551234567", # your Twilio number + twiml=f''' + + {conference_name} + + ''', + status_callback="https://yourapp.com/agent-call-status" + ) +``` + +Node.js +```node +async function connectAgent(conferenceName, agentPhone) { + await client.calls.create({ + to: agentPhone, + from: "+15551234567", + twiml: `${conferenceName}`, + statusCallback: "https://yourapp.com/agent-call-status", + }); +} +``` + +--- + +## Key Patterns + +### Warm Transfer + +Put caller on hold -> dial new agent into Conference -> original agent briefs new agent -> original agent drops. + +Python +```python +def warm_transfer(conference_sid, original_agent_call_sid, new_agent_phone, conference_name): + # Step 1: Put caller on hold (hold = hears music, can't hear agents) + caller_participant = client.conferences(conference_sid) \ + .participants(caller_call_sid) \ + .update(hold=True) + + # Step 2: Dial new agent into the same conference + client.calls.create( + to=new_agent_phone, + from_="+15551234567", + twiml=f'{conference_name}', + status_callback="https://yourapp.com/transfer-agent-status" + ) + + # Step 3: Original agent briefs new agent (caller is on hold, can't hear) + # ... agents talk ... + + # Step 4: Take caller off hold + client.conferences(conference_sid) \ + .participants(caller_call_sid) \ + .update(hold=False) + + # Step 5: Original agent leaves + client.conferences(conference_sid) \ + .participants(original_agent_call_sid) \ + .update(status="completed") # Removes from conference +``` + +### Cold Transfer + +Simpler - just redirect the caller to a new agent without briefing. + +Python +```python +def cold_transfer(conference_sid, original_agent_call_sid, new_agent_phone, conference_name): + # Remove original agent + client.conferences(conference_sid) \ + .participants(original_agent_call_sid) \ + .update(status="completed") + + # Dial new agent into conference + client.calls.create( + to=new_agent_phone, + from_="+15551234567", + twiml=f'{conference_name}' + ) +``` + +### Hold vs Mute + +| Feature | Hold | Mute | +|---------|------|------| +| Participant hears | Hold music | Everything (but can't speak) | +| Other participants hear | Nothing from held party | Nothing from muted party | +| Use when | Transfer briefing, agent lookup | Quick aside (agent mutes self to cough) | +| API | `hold=True` | `muted=True` | + +```python +# Hold - plays music to the held participant +client.conferences(conf_sid).participants(participant_sid).update(hold=True) +client.conferences(conf_sid).participants(participant_sid).update(hold=False) + +# Mute - silences the participant but they still hear +client.conferences(conf_sid).participants(participant_sid).update(muted=True) +client.conferences(conf_sid).participants(participant_sid).update(muted=False) +``` + +Critical distinction: Hold plays music. Mute just silences. Using mute when you mean hold exposes agent-side conversations to the caller. + +### Coaching (Supervisor Whisper) + +Supervisor joins the Conference and can speak to the agent only - the caller cannot hear the supervisor. + +Python +```python +def add_coach(conference_sid, supervisor_phone, conference_name): + """Add supervisor as coach - speaks to agent only, caller can't hear.""" + client.calls.create( + to=supervisor_phone, + from_="+15551234567", + twiml=f''' + + {conference_name} + + ''' + ) +``` + +Node.js +```node +async function addCoach(conferenceSid, supervisorPhone, conferenceName, agentCallSid) { + await client.calls.create({ + to: supervisorPhone, + from: "+15551234567", + twiml: ` + + ${conferenceName} + + `, + }); +} +``` + +Coach behavior: +- Supervisor hears both caller and agent +- Supervisor can speak to agent only (caller cannot hear) +- Coach audio is NOT captured in conference recording - record separately if needed +- To switch from coach to barge (speak to everyone), update the participant + +### Supervisor Barge + +Supervisor joins and speaks to everyone - useful for escalation or takeover. + +```python +def barge_in(conference_sid, supervisor_phone, conference_name): + """Supervisor joins as full participant - everyone hears them.""" + client.calls.create( + to=supervisor_phone, + from_="+15551234567", + twiml=f'{conference_name}' + ) +``` + +### Participant Management + +```python +# List all participants in a conference +participants = client.conferences(conference_sid).participants.list() +for p in participants: + print(f"CallSid: {p.call_sid}, Muted: {p.muted}, Hold: {p.hold}") + +# Remove a participant +client.conferences(conference_sid).participants(call_sid).update(status="completed") + +# End the entire conference +client.conferences(conference_sid).update(status="completed") +``` + +--- + +## Gotchas + +### 1. Conference Requires 2+ Participants to "Exist" + +A Conference with only one participant is in a waiting state. The single participant hears hold music. API calls to the Conference may behave unexpectedly until a second participant joins. + +### 2. Coach Audio Not in Recording + +Conference recordings capture the main audio mix only. Coach/whisper audio is NOT recorded. If you need to record coaching sessions for QA, add a separate recording on the supervisor's call leg. + +### 3. endConferenceOnExit Behavior + +If `endConferenceOnExit=True` for any participant, the conference ends when they leave - dropping all other participants. Set this carefully: +- Caller: Usually `False` (so agents can wrap up) +- Agent: Usually `False` (so caller can be transferred) +- Supervisor: Always `False` + +### 4. Conference Name Is Account-Scoped + +Conference names must be unique within your account at any given time. Use a unique identifier (like CallSid) in the name to prevent collisions: +```python +conference_name = f"room-{call_sid}" # unique per call +``` + +--- + +## CANNOT + +- Cannot use `` inside a Conference - DTMF goes into the audio mix, not a handler. Gather before joining the conference. +- Cannot rely on speaker events for app logic - Speaker events fire too frequently to be actionable in real-time routing. +- Cannot get post-flight participant data from REST API - Completed conferences return empty participant lists. Use Voice Insights for historical data. +- Coach audio is NOT in the conference recording - Supervisor whisper audio is excluded from the recorded mix. Record the supervisor's call leg separately if needed. +- Cannot filter Insights list endpoint by `processing_state` - Must fetch by Conference SID directly. +- Cannot use PII in `friendlyName` - Compliance requirement, not just a suggestion. +- Cannot create a conference with 0 call legs and get Insights data - Insights requires at least 1 participant call attempt. +- Cannot poll Insights immediately after conference end - Takes 15-30+ minutes for data to appear, even for `in_progress` state. +- Cannot exceed 250 participants per conference - Hard limit +- Cannot pre-add phone numbers to a conference - Participants must be active calls +- Cannot use a private URL for hold music - Hold music URL must be publicly accessible +- Cannot get per-participant recordings from conference recording - Recording is per-conference (mono mixed). Use dual-channel recording for QA - see `twilio-call-recordings` + +--- + +## Next Steps + +- Route calls to agents: `twilio-taskrouter-routing` +- Record calls: `twilio-call-recordings` +- IVR before conferencing: `twilio-voice-twiml` +- AI agent with escalation: `twilio-voice-conversation-relay` diff --git a/plugins/twilio/skills/twilio-conference-calls/assets/icon-large.png b/plugins/twilio/skills/twilio-conference-calls/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-conference-calls/assets/icon-small.png b/plugins/twilio/skills/twilio-conference-calls/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Create, manage, and send message templates using Twilio's Content API. + Covers template creation for WhatsApp, SMS, RCS, and MMS; variable usage; + WhatsApp Meta approval; and sending templates via ContentSid. Use this skill + when building structured messages that require pre-approval or consistent + formatting across channels. +--- + +## Overview + +The Content API creates channel-agnostic templates identified by a `ContentSid` (`HX...`). WhatsApp templates must be approved by Meta before use outside the 24-hour service window. + +--- + +## Prerequisites + +- Twilio account - New to Twilio? See `twilio-account-setup` +- For WhatsApp templates: active WhatsApp sender + - See `twilio-whatsapp-send-message` (sandbox) or `twilio-whatsapp-manage-senders` (production) +- Environment variables: + - `TWILIO_ACCOUNT_SID` + - `TWILIO_AUTH_TOKEN` + - See `twilio-iam-auth-setup` for credential setup and best practices +- SDK: `pip install twilio` / `npm install twilio` + +--- + +## Quickstart + +Step 1 - Create a template via Console (simplest) + +Console > Messaging > Content Template Builder > Create new. Use `{{1}}`, `{{2}}` for variables. Save to get a `ContentSid`. + +Step 2 - Send the template + +Python +```python +import os +from twilio.rest import Client + +client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"]) + +message = client.messages.create( + from_="whatsapp:+14155238886", + to="whatsapp:+15558675310", + content_sid="HXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + content_variables='{"1": "Sarah", "2": "March 28", "3": "10:00 AM"}' +) +print(message.sid) +``` + +Node.js +```node +const twilio = require("twilio"); +const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); + +const message = await client.messages.create({ + from: "whatsapp:+14155238886", + to: "whatsapp:+15558675310", + contentSid: "HXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + contentVariables: JSON.stringify({ "1": "Sarah", "2": "March 28", "3": "10:00 AM" }), +}); +``` + +--- + +## Key Patterns + +### Create a Template via API + +Python +```python +template = client.content.v1.contents.create( + friendly_name="appointment-reminder", + language="en", + types={ + "twilio/text": { + "body": "Hi {{1}}, your appointment is on {{2}} at {{3}}. Reply YES to confirm." + } + } +) +print(template.sid) # HXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +``` + +Node.js +```node +const template = await client.content.v1.contents.create({ + friendlyName: "appointment-reminder", + language: "en", + types: { + "twilio/text": { + body: "Hi {{1}}, your appointment is on {{2}} at {{3}}. Reply YES to confirm.", + }, + }, +}); +console.log(template.sid); +``` + +### Submit for WhatsApp Approval + +Python +```python +approval = client.content.v1 \ + .contents(template.sid) \ + .approval_requests \ + .create(name="appointment-reminder", category="UTILITY") +print(approval.status) # PENDING +``` + +Node.js +```node +const approval = await client.content.v1 + .contents(templateSid) + .approvalRequests.create({ name: "appointment-reminder", category: "UTILITY" }); +console.log(approval.status); +``` + +Categories: `UTILITY`, `MARKETING`, `AUTHENTICATION` + +### Check Approval Status + +Python +```python +content = client.content.v1.contents(template.sid).fetch() +print(content.approval_requests.status) # APPROVED | REJECTED | PENDING +``` + +Node.js +```node +const content = await client.content.v1.contents(templateSid).fetch(); +console.log(content.approvalRequests.status); +``` + +Approval typically takes under 1 hour. + +### List and Delete Templates + +Python +```python +for template in client.content.v1.contents.list(): + print(template.sid, template.friendly_name, template.language) + +client.content.v1.contents("HXxxxxxxxxxx").delete() +``` + +Node.js +```node +const templates = await client.content.v1.contents.list(); +templates.forEach(t => console.log(t.sid, t.friendlyName, t.language)); + +await client.content.v1.contents("HXxxxxxxxxxx").remove(); +``` + +### Supported Content Types + +| Type | `types` key | Channels | +|------|------------|---------| +| Plain text | `twilio/text` | All | +| Media (image, video) | `twilio/media` | WhatsApp, MMS, RCS | +| Quick reply buttons | `twilio/quick-reply` | WhatsApp, RCS | +| Call-to-action buttons | `twilio/call-to-action` | WhatsApp, RCS | +| List picker | `twilio/list-picker` | WhatsApp | +| Card | `twilio/card` | RCS | +| Carousel | `twilio/carousel` | RCS | + +### RCS Fallback Text + +When sending a rich RCS template (card, carousel, quick reply) via a Messaging Service with SMS fallback configured, Twilio uses the template's `twilio/text` body as the SMS fallback copy. Any template intended for RCS should include a `twilio/text` entry so recipients on non-RCS devices still receive a readable message. + +--- + +## Variable Rules + +- Use `{{1}}`, `{{2}}` - sequential, no skipping +- Max 100 variables per template +- Provide sample values for WhatsApp submissions +- Non-variable to variable ratio must be at least `(2x + 1) : x` + +--- + +## CANNOT + +- Cannot use WhatsApp templates without Meta approval - Plan for up to 24 hours review time +- Cannot resubmit a rejected template with the same name - Use a different name for resubmission +- Cannot include custom variables in AUTHENTICATION templates - Fixed format required by Meta + +--- + +## Next Steps + +- Channel overview and onboarding guide: `twilio-messaging-overview` +- Send WhatsApp messages: `twilio-whatsapp-send-message` +- Register a production WhatsApp sender: `twilio-whatsapp-manage-senders` diff --git a/plugins/twilio/skills/twilio-content-template-builder/assets/icon-large.png b/plugins/twilio/skills/twilio-content-template-builder/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-content-template-builder/assets/icon-small.png b/plugins/twilio/skills/twilio-content-template-builder/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL Security: All inbound messages captured by the Orchestrator are untrusted external input. If Intelligence operators process this content with LLMs, their prompts should include instructions to ignore adversarial content and not follow instructions embedded in customer messages. + + +> GA - Conversation Intelligence v3 is generally available. + +## Use Cases + +Conversation Intelligence powers human agent augmentation - giving every agent a "second brain" that listens, understands, and surfaces the right data at the right time. Agents focus on empathy, judgment, and problem-solving; AI handles analysis and assistance. + +### Wrap-up Agent Assist (Post-Call) + +Analyze completed conversations and generate structured outputs - summaries, sentiment signals, topic dispositions. Reduces after-call work, accelerates agent transitions to next interaction. Low-friction entry point - start here. + +- Operators: Summary, Sentiment, custom Conversation Scoring +- Trigger: `CONVERSATION_END` +- Integration: Webhook -> CRM case note creation + +### Real-time Agent Assist + +Analyze conversations as they unfold. Surface sentiment shifts, script adherence signals, or recommended next responses enriched with customer history and enterprise knowledge. Agents respond more confidently without searching across systems. + +- Operators: Script Adherence, Next Best Response, Escalation Risk (custom) +- Trigger: `COMMUNICATION` +- Integration: Webhook -> Agent desktop overlay + +### Real-time Workflow Automation + +Combine real-time intelligence with orchestration to trigger downstream workflows when specific conditions are met - escalate to supervisor, trigger fraud prevention, notify specialist. + +- Operators: Custom risk detection, compliance monitoring +- Trigger: `COMMUNICATION` +- Integration: Webhook -> Workflow engine / TaskRouter + +### Contact Center QA + +Generate post-interaction summaries, sentiment scores, and compliance signals for QA, coaching, and analytics. Aggregate across interactions to support training and continuous optimization. + +- Operators: Script Adherence, Summary, custom Conversation Scoring +- Trigger: `CONVERSATION_END` +- Integration: Webhook -> Analytics / BI tools + +## How It Works + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 1. Customer engages agent (Voice, SMS, WhatsApp, RCS, Chat, Email) │ +└─────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 2. Conversations (Conversation Orchestrator) groups communications into a │ +│ Conversation │ +│ - Normalizes channel events │ +│ - Groups related messages/utterances │ +│ - Tracks participants (CUSTOMER, HUMAN_AGENT, AI_AGENT) │ +└─────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 3. Conversation events trigger Intelligence rules │ +│ - COMMUNICATION: on each new message/utterance │ +│ - CONVERSATION_END: when conversation closes │ +│ - CONVERSATION_INACTIVE: when conversation goes idle │ +└─────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 4. Language Operators analyze the conversation │ +│ - Twilio-authored: Sentiment, Summary, NBR, Script Adherence │ +│ - Custom: domain-specific analysis with your prompts │ +│ - Context: enriched with Customer Memory + Enterprise Knowledge │ +└─────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 5. Results delivered via webhook + REST API │ +│ - Real-time: Agent desktop, workflow triggers │ +│ - Post-call: CRM notes, QA systems, analytics │ +│ - Aggregated: Conversational Insights for cross-conversation analysis │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +Key insight: Real-time and post-conversation intelligence use the same underlying model. Start with low-friction post-call summaries, then progressively introduce real-time assist using the same components. + +## Scope + +### CAN + +- Analyze conversations in real-time (per-message) and post-conversation (at close/inactive) via Language Operators +- Use 4 Twilio-authored operators: Sentiment, Summary, Next Best Response, Script Adherence +- Create custom Language Operators with natural language prompts and structured output (TEXT, JSON, CLASSIFICATION) - `EXTRACTION` is a read-only format returned by some Twilio-authored operators; it cannot be set on custom operators you create +- Define up to 5 rules per Intelligence Configuration, each with 1-5 operators (minimum 1 required), 0 or 1 trigger, and 0-2 webhook actions +- Throttle real-time triggers with `count` parameter (run every N communications, min 1, max 20) +- Deliver results via webhook (POST) and query historically via REST API +- Track conversations across SMS, Voice, RCS, WhatsApp, Chat, and Email channels via Conversation Orchestrator (Conversations v2) integration +- Create custom operators with parameters (`{{parameters.name}}` syntax), including knowledge base references (`KNOWLEDGE_BASE_AND_SOURCE_IDS` type - value format: `knowledge_base_id:knowledge_source_id`) +- Enrich operators with Customer Memory (`context.memory.enabled: true`) and Enterprise Knowledge (`context.knowledge.bases: [...]`) at the rule or operator level +- Add `trainingExamples` (input/output pairs) to custom operators to improve accuracy +- Pin a specific operator version in a rule via `operators[].version`; omit to use latest +- Query OperatorResults filtered by `intelligenceConfigurationId`, `conversationId`, or `operatorId` +- Query operator versions and fetch specific version details +- Delete Intelligence Configurations, custom Operators, and individual OperatorResults via REST API +- Use ETag/If-Match headers for optimistic locking on Operator updates (returns 412 on mismatch) - ETag is not supported on Configuration updates +- Filter Conversations by `status`, `channels`, `createdAtBefore`/`createdAtAfter`, `channelId`, `intelligenceConfigurationIds`, `operatorIds` +- Authenticate with both Account SID/Auth Token and API Key/Secret +- Define rules without a trigger (trigger is optional per spec; runs on all events if omitted) + +### CANNOT + +- JSON-only API - All v3 endpoints require `Content-Type: application/json`. Form-encoded bodies return HTTP 415 with error 20422. +- No standalone operation - v3 requires Conversation Orchestrator (Conversations v2) for conversation capture. You cannot feed raw messages or recordings into v3 directly. +- No per-message sentiment - Sentiment is conversation-level, accumulating across all messages. A conversation with one positive and one negative message returns "mixed", not separate results per message. +- No deleting Twilio-authored operators - DELETE returns 404 "Operator not found" for Twilio-authored operators, not 403. They are not treated as "yours" to delete. +- No editing Twilio-authored operator prompts - Twilio-authored operators have `prompt: null` when retrieved via GET. The prompt is hidden and not configurable. +- No more than 2 actions per rule - The API enforces `size must be between 0 and 2` for actions. +- No more than 5 rules per configuration - API enforces `size must be between 0 and 5`. +- No PCI or HIPAA compliance - Conversation Intelligence v3 is not PCI compliant or HIPAA Eligible. Do not use for payment data or protected health information. +- No GET/PUT/DELETE on v3 Conversations - The Conversations endpoint is read-only (GET list, GET by ID). Conversation lifecycle is controlled by Conversation Orchestrator, not by the Intelligence API. +- No unsupported JSON schema features - The following are rejected in `outputSchema`: `minLength`/`maxLength` (strings), `patternProperties` (objects), `uniqueItems` (arrays). Use basic types only. +- Cannot use PUT to update a live configuration - PUT creates an inactive version with no activation API. Operators silently stop returning results. Workaround: DELETE the configuration and POST to recreate it. +- Silent Memory Store linkage failures - If `memoryStoreId` points to a deleted or invalid store, capture still works but identity resolution and extraction silently fail with no error. Implement periodic health checks to verify Memory Store linkage is functioning. + +## Quick Decision + +| Need | Use | Why | +|------|-----|-----| +| Real-time agent assist during live calls/chats | v3 + COMMUNICATION trigger + Next Best Response operator | Real-time webhook delivery per utterance | +| Post-call QA scoring | v3 + CONVERSATION_END trigger + Script Adherence operator | Runs once at conversation close, returns detailed score | +| Conversation sentiment tracking | v3 + Sentiment operator (Twilio-authored) | Conversation-level classification: positive/negative/neutral/mixed | +| Post-call recording transcription + analysis | v2 Voice Intelligence (existing) | v3 does not ingest recordings directly - v2 pipeline handles recording->transcript->operators | +| Custom domain-specific analysis | v3 + Custom operator with JSON output | Define prompt, parameters, structured output schema | +| Cross-channel conversation history | Conversations v2 (Conversation Orchestrator) alone | v3 adds analysis on top; Conversation Orchestrator handles capture and history | +| Simple keyword extraction | v3 + Custom operator (EXTRACTION format) | Structured extraction with custom prompt | + +## Decision Frameworks + +### v2 (Voice Intelligence) vs v3 (Conversation Intelligence) + +| Dimension | v2 (Voice Intelligence) | v3 (Conversation Intelligence) | +|-----------|------------------------|-----------------------------------| +| Input source | Recording SIDs (audio) | Conversation Orchestrator conversations (text/transcriptions) | +| Channels | Voice only | SMS, Voice, RCS, WhatsApp, Chat, Email | +| Operator management | Console only (attach to Intelligence Service) | REST API (full CRUD on custom operators) | +| Trigger model | Post-transcription (async) | Real-time (per-message) or post-conversation | +| Result delivery | Webhook (`voice_intelligence_transcript_available`) | Webhook (per-rule action) + REST query | +| SDK support | `client.intelligence.v2.transcripts` | Twilio Node.js SDK supported | +| SID prefix | `GA` (service), `GT` (transcript), `LY` (operator) | `intelligence_configuration_*`, `intelligence_operator_*` | +| Status | GA | GA | +| Coexistence | Works alongside v3 | Works alongside v2 | + +Use v2 when: You need post-call transcription from recordings, or need GA stability. +Use v3 when: You need real-time analysis, cross-channel support, or API-managed custom operators. + +### Real-Time vs Post-Conversation + +| Factor | COMMUNICATION trigger | CONVERSATION_END trigger | CONVERSATION_INACTIVE trigger | +|--------|----------------------|-------------------------|-------------------------------| +| When it fires | On each new message/utterance | When conversation closes | When conversation goes idle | +| Latency | Near real-time | Seconds after close | After inactive timeout | +| Use cases | Agent assist, escalation detection, live compliance | QA scoring, summaries, CRM updates | Idle conversation follow-up | +| Operator context | Accumulating - sees all messages so far | Complete conversation | Messages up to inactivity point | +| Throttling | `count` parameter (every N messages) | N/A | N/A | +| Cost implication | Runs per message (more executions) | Runs once per conversation | Runs once per inactivity event | + +### Operator Version Lifecycle + +| Status | Behavior | When | +|--------|----------|------| +| `PREVIEW` | Normal execution, restricted visibility | Internal/testing versions | +| `ACTIVE` | Normal execution, full availability | Production-ready versions | +| `DEPRECATED` | Executes with Warn event via Watch | Migration window - update to newer version | +| `RETIRED` | Hard failure, Error logged in Watch | Must update Intelligence Configuration manually | + +### Custom Operator Output Formats + +| Format | Use When | Result Shape | Schema Support | +|--------|----------|-------------|----------------| +| TEXT | Free-form analysis, summaries, translations | `{"text": "..."}` | Auto-generated (not customizable) | +| JSON | Structured extraction with custom fields | User-defined via `outputSchema` | Full JSON Schema (max 100 props, 10 nesting levels, 1000 enum values max) | +| CLASSIFICATION | Category labeling (sentiment, intent, topic) | `{"label": "..."}` | Auto-generated | +| EXTRACTION | Returned by some Twilio-authored operators only - cannot be set on custom operators | `{"entities": [{"text": "...", "label": "..."}]}` | N/A (read-only) | + +### Twilio-Authored Operator Reference + +Ready-to-use operators maintained by Twilio. Use these IDs directly in rules - no custom prompt required. + +| Operator | ID | Best Trigger | Use Case | +|----------|-----|--------------|----------| +| Sentiment | `intelligence_operator_01kcrvw16kfa88qvgrfmr7y151` | COMMUNICATION | Real-time sentiment tracking (positive/negative/neutral/mixed) | +| Summary | `intelligence_operator_01kcv35pnkeysaf6z6cqtbpegn` | CONVERSATION_END | Post-call conversation summary | +| Next Best Response | `intelligence_operator_01kea27sy7ffsafmtsfp17nzx4` | COMMUNICATION | Real-time agent assist with suggested responses | +| Script Adherence | `intelligence_operator_01kf34tcyefpyb1t4m0nbd8rxg` | CONVERSATION_END | QA scoring for script compliance | + +Note: Twilio-authored operators have `author: "TWILIO"` and `prompt: null` when retrieved via GET. Prompts are hidden and not configurable. Use custom operators if you need control over the prompt. + +## Integration Patterns + +Code samples use raw `fetch()` for clarity, but the Twilio Node.js SDK is also supported for v3. + +### Authentication Helper + +```javascript +const INTELLIGENCE_V3_BASE = 'https://intelligence.twilio.com/v3'; + +function getAuthHeaders() { + const credentials = Buffer.from( + `${process.env.TWILIO_ACCOUNT_SID}:${process.env.TWILIO_AUTH_TOKEN}` + ).toString('base64'); + return { + 'Authorization': `Basic ${credentials}`, + 'Content-Type': 'application/json', + }; +} +``` + +### Create Intelligence Configuration with Rules + +```javascript +// Step 1: Create configuration (empty rules initially) +const configResponse = await fetch( + `${INTELLIGENCE_V3_BASE}/ControlPlane/Configurations`, + { + method: 'POST', + headers: getAuthHeaders(), + body: JSON.stringify({ + displayName: 'Customer Support Analytics', + description: 'Real-time sentiment + post-call summary', + rules: [], + }), + } +); +const config = await configResponse.json(); +// config.id = "intelligence_configuration_..." + +// Step 2: Add rules via PUT (replaces all rules) +const updateResponse = await fetch( + `${INTELLIGENCE_V3_BASE}/ControlPlane/Configurations/${config.id}`, + { + method: 'PUT', + headers: getAuthHeaders(), + body: JSON.stringify({ + displayName: 'Customer Support Analytics', + rules: [ + { + operators: [ + { id: 'intelligence_operator_01kcrvw16kfa88qvgrfmr7y151' }, // Sentiment + ], + triggers: [{ on: 'COMMUNICATION' }], + actions: [ + { type: 'WEBHOOK', method: 'POST', url: 'https://your-app.com/realtime-results' }, + ], + }, + { + operators: [ + { id: 'intelligence_operator_01kcv35pnkeysaf6z6cqtbpegn' }, // Summary + ], + triggers: [{ on: 'CONVERSATION_END' }], + actions: [ + { type: 'WEBHOOK', method: 'POST', url: 'https://your-app.com/post-call-results' }, + ], + }, + ], + }), + } +); +const updatedConfig = await updateResponse.json(); +// updatedConfig.version = 2 (auto-incremented) +``` + +### Link to Conversation Orchestrator Conversation Configuration + +```javascript +// Intelligence config must be linked to a Conversation Orchestrator conversation config +// See conversation-orchestrator skill for full Conversation Orchestrator setup +const convConfigResponse = await fetch( + 'https://conversations.twilio.com/v2/ControlPlane/Configurations', + { + method: 'POST', + headers: getAuthHeaders(), + body: JSON.stringify({ + displayName: 'Support Config', + memoryStoreId: 'mem_store_...', // Required - create via Memory API first + conversationGroupingType: 'GROUP_BY_PARTICIPANT_ADDRESSES', + intelligenceConfigurationIds: [config.id], + channelSettings: { + SMS: { + statusTimeouts: { inactive: 5, closed: 10 }, + captureRules: [{ from: '*', to: '+1XXXXXXXXXX', metadata: {} }], + }, + }, + }), + } +); +``` + +### Consume Operator Results + +```javascript +// Query all results for an intelligence configuration +const resultsResponse = await fetch( + `${INTELLIGENCE_V3_BASE}/OperatorResults?intelligenceConfigurationId=${config.id}`, + { headers: getAuthHeaders() } +); +const results = await resultsResponse.json(); + +for (const operatorResult of results.items) { + console.log(`Operator: ${operatorResult.operator.id}`); + console.log(`Format: ${operatorResult.outputFormat}`); + console.log(`Payload: ${JSON.stringify(operatorResult.result)}`); // e.g. { text: "..." } or { label: "..." } + console.log(`Conversation: ${operatorResult.conversationId}`); + console.log(`Trigger: ${operatorResult.executionDetails.trigger.on}`); + // Context that was actually used at runtime (single source of truth): + console.log(`Memory profile: ${operatorResult.executionDetails.resolvedContext?.memory?.profileId}`); + console.log(`Knowledge sources: ${JSON.stringify(operatorResult.executionDetails.resolvedContext?.knowledge?.sources)}`); + // Cost/perf metadata: + console.log(`Model: ${operatorResult.metadata.system.resolvedModel}, latencyMs: ${operatorResult.metadata.system.latencyMs}`); +} +``` + +### Paginate Through Results + +All list endpoints (`/OperatorResults`, `/Conversations`, `/Operators`, `/Configurations`) use cursor-based pagination. Default page size is 50; maximum is 1000. + +```javascript +async function* getAllOperatorResults(configId) { + let pageToken = undefined; + do { + const url = new URL(`${INTELLIGENCE_V3_BASE}/OperatorResults`); + url.searchParams.set('intelligenceConfigurationId', configId); + url.searchParams.set('pageSize', '1000'); + if (pageToken) url.searchParams.set('pageToken', pageToken); + + const response = await fetch(url, { headers: getAuthHeaders() }); + const data = await response.json(); + + yield* data.items; + pageToken = data.meta?.nextToken; // null/undefined when no more pages + } while (pageToken); +} + +// Usage: +for await (const result of getAllOperatorResults(config.id)) { + console.log(result.operator.id, result.result); +} +``` + +### Enable Customer Memory and Enterprise Knowledge on a Rule + +```javascript +// Context is configured at the rule level (not the operator level) +rules: [ + { + operators: [{ id: 'intelligence_operator_01kea27sy7ffsafmtsfp17nzx4' }], // NBR + triggers: [{ on: 'COMMUNICATION' }], + actions: [{ type: 'WEBHOOK', method: 'POST', url: 'https://your-app.com/nbr' }], + context: { + memory: { enabled: true }, // inject customer profile from Memory Store + knowledge: { + bases: ['knowledge_base_id_here'], // inject enterprise KB articles + }, + }, + }, +] +``` + +### Create a Custom Operator with Training Examples and Parameters + +```javascript +const operatorResponse = await fetch( + `${INTELLIGENCE_V3_BASE}/ControlPlane/Operators`, + { + method: 'POST', + headers: getAuthHeaders(), + body: JSON.stringify({ + displayName: 'Escalation Risk Detector', + prompt: `Analyze this conversation between a customer and agent. +Product context: {{parameters.productName}} +Classify escalation risk as LOW, MEDIUM, or HIGH based on customer frustration signals.`, + outputFormat: 'CLASSIFICATION', + parameters: { + productName: { type: 'STRING', required: true, description: 'Product line being discussed' }, + // KNOWLEDGE_BASE_AND_SOURCE_IDS parameters are passed as "kb_id:source_id" at rule time + knowledgeContext: { type: 'KNOWLEDGE_BASE_AND_SOURCE_IDS', required: false }, + }, + trainingExamples: [ + { + input: 'Customer: This is the third time I have called about this issue', + output: 'HIGH', + }, + { + input: 'Customer: Thanks, I think that might work', + output: 'LOW', + }, + ], + }), + } +); +const operator = await operatorResponse.json(); +// Pin this operator to a specific version in your rule: +// operators: [{ id: operator.id, version: operator.version }] +``` + +## Gotchas + +### Setup + +1. Memory Store is required for Conversation Orchestrator: You cannot create a Conversations v2 Configuration without a `memoryStoreId`. The Memory API returns `"memoryStoreId: must not be null"` (error 20001). Create the Memory Store first via `POST memory.twilio.com/v1/ControlPlane/Stores`. + +2. JSON-only API: All v3 endpoints require `Content-Type: application/json`. Form-encoded bodies return HTTP 415 with error 20422 ("does not support this payload format"). This matches Conversation Orchestrator but differs from most Twilio APIs. + +3. v2 and v3 coexist independently: Creating v3 configurations does not affect v2 Intelligence Services (`GA*` SIDs). Both are accessible on the same account simultaneously. They share the `intelligence.twilio.com` host but use different URL paths (`/v2/Services` vs `/v3/ControlPlane/Configurations`). + +### Configuration + +4. PUT creates an inactive version - operators stop returning results: When you PUT to update an Intelligence Configuration, the new version is created in an inactive state. There is no activation API to make it live. Your operators will silently stop producing results. Workaround: DELETE the configuration and POST to recreate it with the updated rules/operators. This is the only reliable way to update a live configuration. + +5. PUT replaces all rules: Updating a configuration replaces the entire `rules` array. There is no PATCH or per-rule update. Always include all rules in the PUT body, not just the changed one. + +6. Config version auto-increments: Each PUT bumps the `version` field. Conversation Orchestrator conversation configs use `version` for optimistic locking, but Intelligence configs accept PUT without version checks. + +7. Rules get their own IDs: When rules are created via PUT, each gets an auto-generated ID (`intelligence_configurationrule_*`). These IDs appear in OperatorResult references but are not user-settable. + +8. Trigger count parameter throttles execution: Setting `{"on":"COMMUNICATION","parameters":{"count":3}}` runs the operator every 3 messages instead of every message. + +### Runtime + +9. Dual capture rules cause duplicate processing: If both inbound (`from: *, to: +1XXX`) and outbound (`from: +1XXX, to: *`) capture rules match the same SMS, Conversation Orchestrator creates two Communications for one message, and Intelligence produces two OperatorResults. Use unidirectional capture rules unless you specifically want both. + +10. Twilio number is auto-typed HUMAN_AGENT: In Conversation Orchestrator conversations, the Twilio number is automatically assigned `type: "HUMAN_AGENT"` and the external number gets `type: "CUSTOMER"` with automatic memory profile resolution (`mem_profile_*`). + +11. Sentiment accumulates across messages: The Sentiment operator analyzes the full conversation context, not individual messages. After a positive message, sentiment was "positive". After adding a negative message to the same conversation, it became "mixed". + +12. Near real-time delivery for COMMUNICATION trigger: Results are delivered via webhook shortly after each utterance - the full pipeline is Conversation Orchestrator capture -> Intelligence trigger -> Operator execution -> webhook delivery. + +13. Custom operator TEXT output auto-wraps: Custom operators with `outputFormat: "TEXT"` always return `{"text": "..."}` regardless of the prompt. The outputSchema is auto-generated and not customizable for TEXT format. + +### Observability + +14. conversationConfigurationId returns "unused": The v3 Conversations endpoint returns `"conversationConfigurationId": "unused"` instead of the actual Conversation Orchestrator config ID. Use `intelligenceConfigurationIds` array instead for linking. + +15. No isTwilioAuthored field: Twilio-authored operators are distinguished by `author: "TWILIO"`, custom operators by `author: "SELF"`. There is no boolean `isTwilioAuthored` field. + +16. Twilio-authored operator prompts are hidden: GET on a Twilio-authored operator returns `prompt: null`. You cannot inspect or modify the system prompt. Custom operators return the full prompt. + +17. Two separate metadata sections: OperatorResults carry both `metadata.system` and `executionDetails.resolvedContext` - they serve different purposes: + - `metadata.system`: cost and performance - `resolvedModel` (LLM used), `latencyMs`, `inputCharacters`/`outputCharacters` (billing units), `inputTruncated` + - `executionDetails.resolvedContext`: what context was actually injected at runtime - `memory` (`profileId`, `memoryStoreId`) and `knowledge` (`sources`: array of `{baseId, sourceId}`). This is the single source of truth for context resolution. + +### Error Handling + +18. Error codes are consistent: v3 uses Twilio standard error codes: `20001` (bad request/validation), `20404` (not found), `20422` (unsupported format), `70001` (operator validation). All include `userError: true` and descriptive messages. + +19. Invalid operator ID gives specific error: Using a non-existent operator ID in a rule returns 400 with code `70001` and message identifying the exact invalid operator. + +### JSON Schema + +20. All JSON schema fields are required by default - and Twilio auto-sets this: Twilio automatically sets `additionalProperties: false` and marks all provided fields as required in `outputSchema`. Do not add `required` or `additionalProperties` yourself - Twilio overwrites any values you provide. The practical consequence: if the LLM cannot populate a field, the operator execution fails. Use union types for nullable fields: `"type": ["string", "null"]`. + +21. Nullable field pattern: To make a field optional/nullable in JSON output, use array type union: +```json +{ + "outputSchema": { + "type": "object", + "properties": { + "requiredField": { "type": "string" }, + "optionalField": { "type": ["string", "null"] } + } + } +} +``` +This allows the operator to return `null` for fields where the LLM has insufficient context. + +21. Unsupported JSON schema features cause silent or hard failures: The following JSON Schema features are NOT supported in `outputSchema` and will be rejected. Stick to `type`, `enum`, `properties`, `items`, `anyOf`, `$defs`/`$ref`. + - Strings: `minLength`, `maxLength` + - Objects: `patternProperties`, `unevaluatedProperties`, `propertyNames`, `minProperties`, `maxProperties` + - Arrays: `unevaluatedItems`, `contains`, `minContains`, `maxContains`, `uniqueItems` + +22. `executionDetails.context` was removed: Older docs and some live responses may show `executionDetails.context`. This field was removed in a breaking change. Use `executionDetails.resolvedContext` - it contains `memory` (profileId, memoryStoreId) and `knowledge` (array of baseId/sourceId pairs). + +23. Operator result query param is `intelligenceConfigurationId` (not `intelligenceConfiguration`): The REST API filter parameter for listing OperatorResults by config is `intelligenceConfigurationId`. Using the shorter form returns unfiltered results. + +24. `KNOWLEDGE_BASE_AND_SOURCE_IDS` parameter values must use colon-separated format: When passing a knowledge base parameter to an operator at rule time, the value must be formatted as `"knowledge_base_id:knowledge_source_id"`. Passing just the KB ID or using any other separator returns a validation error. Only plaintext KB sources are supported. + +25. `KNOWLEDGE_BASE_AND_SOURCE_IDS` parameters do not support `default`: Unlike `STRING`, `INTEGER`, `NUMBER`, and `BOOLEAN` parameter types which all allow a `default` value, `KNOWLEDGE_BASE_AND_SOURCE_IDS` does not. Defining a `default` on a KB parameter will be ignored or rejected. + +26. Each rule requires at least 1 operator: The `operators` array on a rule has `minItems: 1`. Submitting a rule with an empty operators array on create or update returns a 400 validation error. + +27. List endpoints are paginated - don't assume you got all results: All list endpoints (`/OperatorResults`, `/Conversations`, `/Operators`, `/Configurations`) return a max of 50 items by default (max 1000 with `pageSize`). The response `meta.nextToken` is non-null when more pages exist. Always paginate when querying production data sets. + +## Conversational Insights (Cross-Conversation Analytics) + +Where the Intelligence API gives you per-conversation OperatorResults, the Insights API v3 is the query layer for aggregating across thousands of conversations - grouping, filtering, and counting by dimensions like sentiment, channel, language, and operator output. + +Base URL: `https://insights.twilio.com` + +> Public Beta - Insights v3 is currently in public beta. The query schema is subject to change. + +### When to Use Insights vs Intelligence REST API + +| Goal | Use | +|------|-----| +| Get the result for a specific conversation | Intelligence API: `GET /v3/OperatorResults?conversationId=...` | +| Count conversations by sentiment over time | Insights API: query with `OperatorResult.Value` dimension | +| Find all conversations where agent went off-script | Insights API: filter on `OperatorResult.Value` | +| Build a sentiment trend dashboard | Insights API: group by `DateCreated` + `OperatorResults` | +| Discover available metrics and dimensions | Insights API: `GET /v3/InsightsDomains/Conversations/Metadata` | + +### Endpoints + +| Method | Path | Purpose | +|--------|------|---------| +| `POST` | `/v3/InsightsDomains/Conversations/Query` | Execute a semantic query, returns first page | +| `GET` | `/v3/InsightsDomains/Conversations/Query?pageToken=...` | Fetch subsequent pages | +| `GET` | `/v3/InsightsDomains/Conversations/Metadata` | Discover available cubes, measures, dimensions | + +Same Basic Auth as Intelligence API. JSON-only (`Content-Type: application/json`). + +### Query a Sentiment Distribution + +```javascript +const INSIGHTS_BASE = 'https://insights.twilio.com'; + +const response = await fetch( + `${INSIGHTS_BASE}/v3/InsightsDomains/Conversations/Query`, + { + method: 'POST', + headers: getAuthHeaders(), // same helper as Intelligence API + body: JSON.stringify({ + domain: 'Conversations', + query: { + measures: ['Conversation.Count'], + dimensions: ['OperatorResults', 'Channels', 'DateCreated'], + filters: [{ + op: 'AND', + expressions: [ + { op: 'IN', field: 'OperatorResult.Value', values: ['positive', 'negative'] }, + ], + }], + orderBy: [{ field: 'OperatorResults.CreatedDate', direction: 'DESC' }], + }, + }), + } +); +const data = await response.json(); +// data.items = [{ Id: 'conv1', OperatorResults: 'positive', Channels: ['voice'], ... }] +// data.meta.nextToken - use for next page (null if last page) +``` + +Query fields: + +| Field | Description | +|-------|-------------| +| `query.measures` | What to aggregate - e.g. `"Conversation.Count"`, `"OperatorResult.Count"` | +| `query.dimensions` | What to group by - e.g. `"OperatorResults"`, `"Channels"`, `"Languages"`, `"DateCreated"` | +| `query.filters` | Nested filter tree with `op` + `expressions`. Filter ops: `AND`, `OR`, `EQ`, `NE`, `GT`, `LT`, `IN` | +| `query.orderBy` | Sort by field + `ASC`/`DESC` | + +### Pagination + +POST returns the first page. Subsequent pages use GET with `pageToken`. Stop when `meta.nextToken` is null. + +```javascript +async function* queryAll(queryBody) { + const first = await fetch(`${INSIGHTS_BASE}/v3/InsightsDomains/Conversations/Query`, + { method: 'POST', headers: getAuthHeaders(), body: JSON.stringify(queryBody) } + ).then(r => r.json()); + yield* first.items; + + let nextToken = first.meta?.nextToken; + while (nextToken) { + const page = await fetch( + `${INSIGHTS_BASE}/v3/InsightsDomains/Conversations/Query?pageToken=${nextToken}`, + { headers: getAuthHeaders() } + ).then(r => r.json()); + yield* page.items; + nextToken = page.meta?.nextToken; + } +} +``` + +### Discover Available Dimensions and Measures + +```javascript +const meta = await fetch( + `${INSIGHTS_BASE}/v3/InsightsDomains/Conversations/Metadata`, + { headers: getAuthHeaders() } +).then(r => r.json()); + +for (const cube of meta.cubes) { + console.log('Measures:', cube.measures.map(m => m.name)); + console.log('Dimensions:', cube.dimensions.map(d => d.name)); +} +``` + +Known dimensions: `DateCreated`, `OperatorResults`, `OperatorResult.Value`, `Channels`, `Languages`, `Conversation.AccountSid` + +Known measures: `Conversation.Count`, `OperatorResult.Count` + +### Insights CANNOT +- Return raw OperatorResult payloads - use Intelligence `GET /v3/OperatorResults` for that +- Write anything - read-only +- Query in real-time - data mart has indexing lag vs. the live Intelligence API +- Query domains other than `Conversations` + +## Related Resources + +- [Conversation Orchestrator Skill](../twilio-conversation-orchestrator/SKILL.md) - Conversation Orchestrator setup: Memory Store, Conversation Configuration, capture rules, participant types +- [Twilio Conversations (unified stack)](../twilio-conversation-orchestrator/SKILL.md) - End-to-end integration guide for Conversation Orchestrator + Conversation Memory + Intelligence v3 pipeline + diff --git a/plugins/twilio/skills/twilio-conversation-intelligence/assets/icon-large.png b/plugins/twilio/skills/twilio-conversation-intelligence/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-conversation-intelligence/assets/icon-small.png b/plugins/twilio/skills/twilio-conversation-intelligence/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Configure automatic conversation capture and routing with Twilio Conversation + Orchestrator. Covers Configuration creation, channel capture rules, grouping + types, status timeouts, Memory Store linkage, Intelligence linkage, and + conversation lifecycle. Use this skill to automatically capture SMS, voice, + WhatsApp, RCS, and web chat traffic into unified conversations without + manually creating conversations or participants. +--- + +# Conversation Orchestrator + +Decision-making guide for Twilio's Conversation Orchestrator (Conversations v2) - automatic conversation capture and routing across Voice, SMS, WhatsApp, RCS, and web chat. Covers Configurations, capture rules, grouping types, channel settings, status timeouts, and linkage to Conversation Memory and Conversation Intelligence. + +> GA - Conversation Orchestrator is generally available. + +## Use Cases + +Conversation Orchestrator powers automatic conversation capture and integration with existing voice implementations - replacing manual conversation creation with either: +- Passive ingestion: Declarative capture rules that automatically capture traffic as it flows +- Active ingestion: TwiML parameters or API calls that route existing implementations into conversations + +Note: Active and passive ingestion can be configured on a per-channel basis. For example, you can use passive capture rules for SMS while using active TwiML parameters for voice calls. + +## ⚠️ CRITICAL: Voice Double Billing Warning + +> WARNING: You can be charged for STT (speech-to-text) twice on the same call if misconfigured. + +If you are using voice with ConversationRelay or Transcription in TwiML: + +We do not recommend using passive voice capture rules (`captureRules`) in your Configuration when using active TwiML. + +See the full [Voice Double Billing Warning](#️-critical-voice-double-billing-warning-1) section below for details. + +### Unified Customer Context + +Capture all channels (voice, SMS, WhatsApp, RCS, CHAT (via Conversation API (classic))) into a single conversation thread per customer. Conversation Memory resolves identity across channels and maintains persistent context. Start here - this is the most common pattern. + +- Grouping: `GROUP_BY_PROFILE` +- Channels: SMS + VOICE + WHATSAPP + RCS + CHAT (via Conversation API (classic)) +- Linkage: Memory Store (identity resolution) + Intelligence (analysis) + +### Channel-Isolated Analytics + +Keep voice transcripts separate from SMS threads for per-channel analysis. Intelligence operators run independently on each channel's conversation. + +- Grouping: `GROUP_BY_PARTICIPANT_ADDRESSES_AND_CHANNEL_TYPE` +- Channels: SMS + VOICE (separate conversations) +- Linkage: Intelligence (per-channel operators) + +### Agent Connect Integration + +Capture conversations for AI-to-human escalation via Agent Connect (TAC SDK). Uses address-pair grouping required by the SDK. + +- Grouping: `GROUP_BY_PARTICIPANT_ADDRESSES` +- Channels: SMS or VOICE +- Linkage: Memory Store + Intelligence + Agent Connect + +### Post-Conversation Memory Extraction + +Automatically extract observations from conversations into Conversation Memory. Opt-in - configure once, every conversation feeds the memory loop. + +- Config: `memoryExtractionEnabled: true` +- Trigger: INACTIVE and/or CLOSED lifecycle transitions (configurable) +- Result: Observations and summaries written to linked Memory Store profiles + +## How It Works + +### Passive Ingestion (Capture Rules) + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 1. Inbound/outbound traffic arrives (SMS, Voice, WhatsApp, RCS) │ +└─────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 2. Capture rules match on phone number patterns │ +│ - from/to with wildcards (e.g., from: *, to: +15551234567) │ +│ - Per-channel rules (SMS, VOICE, WHATSAPP, RCS) │ +│ - Metadata filters (callType for CLIENT/SIP) │ +└─────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 3. Conversation auto-created (or existing one matched via grouping) │ +│ - GROUP_BY_PROFILE: merge by Memory Profile identity │ +│ - GROUP_BY_PARTICIPANT_ADDRESSES: merge by address pair │ +│ - GROUP_BY_..._AND_CHANNEL_TYPE: separate per channel │ +└─────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 4. Linked services activate │ +│ - Memory Store: identity resolution, profile auto-creation │ +│ - Intelligence: operators fire per Communication or at close │ +│ - Status timeouts: ACTIVE -> INACTIVE -> CLOSED lifecycle │ +└─────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 5. On conversation close │ +│ - Memory extraction: observations written to Memory Store │ +│ - CONVERSATION_END Intelligence operators fire (Summary, etc.) │ +│ - Status callbacks delivered (if configured) │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +### Active Ingestion (TwiML or API) + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 1. Voice call arrives OR you create conversation via API │ +└─────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 2a. TwiML: Pass conversationConfiguration or conversationId parameter │ +│ - │ +│ - │ +│ 2b. API: POST to /v2/Conversations with participants │ +└─────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 3. Conversation created or matched based on parameter │ +│ - conversationConfiguration: uses grouping rules │ +│ - conversationId: routes to specific conversation │ +└─────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 4. Voice transcription or API communications added │ +│ - Same linked services activate (Memory Store, Intelligence) │ +│ - Same lifecycle: ACTIVE -> INACTIVE -> CLOSED │ +└─────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 5. On conversation close │ +│ - Memory extraction: observations written to Memory Store │ +│ - CONVERSATION_END Intelligence operators fire (Summary, etc.) │ +│ - Status callbacks delivered (if configured) │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +## Scope + +### CAN + +- Automatically capture SMS, voice, WhatsApp, RCS, and CHAT (via Conversation API (classic)) into Conversations via passive ingestion (capture rules) OR work with existing voice implementations via active ingestion (TwiML parameters: conversationConfiguration, conversationId) +- Merge multiple channels into one Conversation thread via `GROUP_BY_PROFILE` +- Link Memory Store for automatic identity resolution and observation extraction +- Link multiple Intelligence Configurations for real-time and post-conversation analysis +- Bridge Conversation API (classic) Services for browser SDK chat via `conversationsV1Bridge` +- Configure per-channel capture rules with wildcard matching +- Set independent timeout policies per channel +- Add `statusCallbacks` for webhook notifications on conversation state changes +- Pass `conversationConfiguration` or `conversationId` in `` or `` TwiML to create or route to a conversation (Active TwiML mode) - both parameters supported in both TwiML verbs +- Close conversations explicitly via PATCH to trigger Memory extraction and CONVERSATION_END operators +- List and filter Conversations by status, channel, and date range +- Read Communications (messages + voice utterances) within a Conversation +- Authenticate with Account SID/Auth Token or API Key/Secret + +### CANNOT + +- Cannot update Configurations with PATCH - PUT only, full replacement. Omitting fields deletes them. Always re-fetch before updating. +- Cannot exceed 10 Configurations per account - Hard limit at GA. Each config supports up to 100 capture rules per channel. Delete unused configs to make room. Plan Configuration topology for large phone number portfolios accordingly (e.g., 101 numbers for one channel requires 2 configs). +- Cannot change grouping type after creation - `conversationGroupingType` is immutable on a Configuration. Create a new config if you need a different grouping. +- Cannot capture CLIENT or SIP voice calls without explicit callType metadata - PSTN is captured by default. Browser (Client SDK) and SIP calls require `metadata.callType` in capture rules. +- Not recommended to combine passive VOICE capture rules with active TwiML voice (ConversationRelay or Transcription with conversation parameters) - You will be double-charged for STT. The system does not prevent this configuration, but it is not recommended. Passive voice capture uses Real-Time Transcription (RTT) under the hood. If you pass `conversationConfiguration` or `conversationId` in `` or `` TwiML, you are using active ingestion which has its own STT engine. Both engines will run = double STT billing. Use active TwiML (pass conversation parameters) OR passive capture rules (captureRules), not both for the same traffic. See the [Voice Double Billing Warning](#️-critical-voice-double-billing-warning) section above. +- Cannot detect failed Memory linkage - If `memoryStoreId` points to a deleted or invalid store, capture still works but identity resolution and extraction silently fail. See `twilio-debugging-observability`. +- Cannot filter Intelligence operators by participant type - Operators fire on ALL Communications (customer and agent). Use the operator prompt to specify which participant to analyze. +- Cannot extract Memory observations mid-conversation (ACTIVE state) - Extraction is opt-in and can fire on INACTIVE and/or CLOSED lifecycle transitions, but not while the conversation is ACTIVE. For real-time Memory writes during an active conversation, post Observations directly via `twilio-customer-memory`. +- Cannot have conversations pick up config changes retroactively - Conversations pin the Configuration version at creation time. Close existing conversations to apply updated rules. +- Cannot use the POST response to get the Configuration ID - Creation returns 202 with an operation. Poll the operation's `statusUrl` until `status` is `COMPLETED`, then retrieve the configuration ID from the operation result. +- No standalone operation - Requires a Memory Store because Conversation Orchestrator uses profiles for identity resolution. `memoryStoreId` is mandatory when creating a Configuration. +- JSON-only API - All Conversation Orchestrator endpoints require `Content-Type: application/json`. Form-encoded bodies are rejected. + +## Quick Decision + +| Need | Use | Why | +|------|-----|-----| +| Already have ConversationRelay or Transcription voice implementation | Pass `conversationConfiguration` or `conversationId` in TwiML (Active ingestion) - do NOT add passive VOICE capture rules | More granular control over which calls are captured. Avoids double STT billing. | +| Capture all messaging into unified customer conversations | Configuration with passive capture rules + Memory Store + `GROUP_BY_PROFILE` | Automatic capture with cross-channel identity resolution | +| Keep voice and SMS conversations separate | Configuration with `GROUP_BY_PARTICIPANT_ADDRESSES_AND_CHANNEL_TYPE` | Channel-isolated threads for per-channel analytics | +| Auto-extract customer observations from conversations | Set `memoryExtractionEnabled: true` on Configuration | Triggers on conversation close, writes to linked Memory Store | +| Analyze conversations with Intelligence operators | Link `intelligenceConfigurationIds` on Configuration | Operators fire per Communication or at conversation close | +| Capture browser voice calls (Client SDK) | Add VOICE capture rule with `metadata.callType: "CLIENT"` | PSTN-only by default; CLIENT needs explicit rule | +| Capture CHAT (via Conversation API (classic)) | Set `conversationsV1Bridge.serviceId` on Configuration | CHAT flows through a Conversations (v1) Service bridged into Orchestrator | + +## ⚠️ CRITICAL: Voice Double Billing Warning + +> WARNING: You can be charged for STT (speech-to-text) twice on the same call if misconfigured. + +We do not recommend using passive voice capture rules (`captureRules`) in your Configuration when using active TwiML. + +When you pass `conversationConfiguration` or `conversationId` in your TwiML: +- `` - Active TwiML mode +- `` - Active TwiML mode +- `` - Active TwiML mode +- `` - Active TwiML mode + +You are using active ingestion. Your voice is already being captured and transcribed. + +What causes double billing: +- Passive voice capture rules use Real-Time Transcription (RTT) under the hood +- ConversationRelay/Transcription use their own STT engines +- If both are active on the same call = you pay for BOTH STT engines + +Correct configuration for active voice (TwiML): +```json +{ + "channelSettings": { + "VOICE": { + "statusTimeouts": null // ✅ Define channel settings + // ❌ NO captureRules - omit this field entirely + } + } +} +``` + +When to use passive voice capture rules: +- Human agent calls WITHOUT ConversationRelay or Transcription TwiML +- You want automatic capture with no TwiML changes + +When to use active voice (TwiML parameters): +- AI voice agents with ConversationRelay +- Adding transcription to existing API-created conversations +- Any scenario where you're already passing conversation parameters in TwiML + +## Decision Frameworks + +### Conversation Grouping + +The `conversationGroupingType` on your configuration controls how new traffic groups into conversations. + +| Type | Behavior | When to use | +|------|----------|-------------| +| `GROUP_BY_PARTICIPANT_ADDRESSES_AND_CHANNEL_TYPE` | Separate conversations per channel. SMS and Voice between the same numbers create different conversations. | The default. Keeps channels separate. | +| `GROUP_BY_PARTICIPANT_ADDRESSES` | Same conversation across channels when participants share an address. | Omnichannel on the same addresses - customer can switch between SMS and Voice seamlessly. | +| `GROUP_BY_PROFILE` | Groups by customer profile. The same customer from different devices or channels goes to one conversation. | Preferred for production. Recommended when channels use different addresses (chat and voice). | + +Immutable after creation. Choose before creating the Configuration. To change grouping, create a new Configuration. + +### Supported Channels + +Conversation Orchestrator supports voice, SMS, RCS, and WhatsApp channels. You can also bring Chat traffic in through the Conversations API (classic) bridge. + +| Channel | Address Format | Example | Ingestion Modes | +|---------|---------------|---------|-----------------| +| Voice (PSTN) | E.164 phone number | `+15559876543` | Passive and active | +| Voice (CLIENT) | Client identity string | `agent-1` | Passive and active | +| Voice (PUBLIC_SIP) | SIP URI or E.164 phone number | `sip:user@example.com` | Passive and active | +| SMS | E.164 phone number | `+15551234567` | Passive and active | +| RCS | E.164 phone number | `+15551234567` | Passive and active | +| WhatsApp | E.164 phone number | `+15551234567` | Passive and active | +| Chat | Identity string | `user123` | Conversations API (classic) bridge only | + +### Channel Configuration Details + +Voice: +- Use `callType` metadata in passive capture rules to distinguish call types: + - `PSTN` - Standard phone calls over the public network + - `CLIENT` - In-app calls using Twilio Voice SDK + - `PUBLIC_SIP` - Calls over a SIP interface +- When you save voice capture rules, Conversation Orchestrator automatically provisions call filtering and Real-Time Transcription +- Each TTS fragment is a separate communication (3-5 per agent response) +- Voice communications have `content.type` of `TRANSCRIPTION` +- Warning: For dynamic or numerous client identities, use active TwiML instead of passive capture rules. Don't use wildcard identities with `CLIENT` call type. + +SMS: +- Bidirectional capture rules needed (from: phone, to: * AND from: *, to: phone) +- Communications have `content.type` of `TEXT` +- Includes `deliveryStatus` in recipients array +- Recommended timeouts: `inactive: 10, closed: 60` + +RCS: +- Same pattern as SMS +- Text body captured; media attachments are not added to conversations +- Recommended timeouts: `inactive: 10, closed: 60` + +WhatsApp: +- E.164 format addresses (with or without `whatsapp:` prefix) +- Text and template messages supported +- Media attachments on inbound/outbound messages are not added to conversations +- Recommended timeouts: `inactive: 10, closed: 60` + +Chat (via Conversations API (classic)): +- Only available through Conversations API (classic) bridge +- Uses customer-defined identity strings +- Configure `conversationsV1Bridge.serviceId` on Configuration +- Classic `ConversationSid` carried on address as `channelId` +- Recommended timeouts: `inactive: 15, closed: 60` + +## Integration Patterns + +Code samples use raw `fetch()` for clarity. All Conversation Orchestrator APIs use Basic Auth - see `twilio-iam-auth-setup`. + +### Authentication Helper + +```javascript +const CONVERSATIONS_V2_BASE = 'https://conversations.twilio.com/v2'; + +function getAuthHeaders() { + const credentials = Buffer.from( + `${process.env.TWILIO_ACCOUNT_SID}:${process.env.TWILIO_AUTH_TOKEN}` + ).toString('base64'); + return { + 'Authorization': `Basic ${credentials}`, + 'Content-Type': 'application/json', + }; +} +``` + +### Create a Configuration + +```javascript +const configResponse = await fetch( + `${CONVERSATIONS_V2_BASE}/ControlPlane/Configurations`, + { + method: 'POST', + headers: getAuthHeaders(), + body: JSON.stringify({ + displayName: 'my-app-config', + description: 'Production conversation config', + conversationGroupingType: 'GROUP_BY_PROFILE', + memoryStoreId: 'mem_store_...', // Required - create via Memory API first + memoryExtractionEnabled: true, + channelSettings: { + SMS: { + captureRules: [ + { from: '+15551234567', to: '*', metadata: {} }, + { from: '*', to: '+15551234567', metadata: {} }, + ], + statusTimeouts: { inactive: 10, closed: 60 }, + }, + VOICE: { + captureRules: [ + { from: '*', to: '+15551234567', metadata: {} }, + ], + }, + }, + }), + } +); +// May return 202 without config ID - poll GET /ControlPlane/Configurations to find by displayName +``` + +```python +import os, requests + +account_sid = os.environ["TWILIO_ACCOUNT_SID"] +auth_token = os.environ["TWILIO_AUTH_TOKEN"] +twilio_phone = os.environ["TWILIO_PHONE_NUMBER"] + +config = requests.post( + "https://conversations.twilio.com/v2/ControlPlane/Configurations", + auth=(account_sid, auth_token), + json={ + "displayName": "my-app-config", + "description": "Production conversation config", + "conversationGroupingType": "GROUP_BY_PROFILE", + "memoryStoreId": "mem_store_...", + "memoryExtractionEnabled": True, + "channelSettings": { + "SMS": { + "captureRules": [ + {"from": twilio_phone, "to": "*", "metadata": {}}, + {"from": "*", "to": twilio_phone, "metadata": {}} + ], + "statusTimeouts": {"inactive": 10, "closed": 60} + }, + "VOICE": { + "captureRules": [ + {"from": "*", "to": twilio_phone, "metadata": {}} + ] + } + } + } +).json() +``` + +### Update a Configuration (PUT - Full Replacement) + +```javascript +// Step 1: Fetch current config (ALWAYS re-fetch before updating) +const current = await fetch( + `${CONVERSATIONS_V2_BASE}/ControlPlane/Configurations/${configId}`, + { headers: getAuthHeaders() } +).then(r => r.json()); + +// Step 2: Modify the field you need +current.channelSettings.VOICE.captureRules.push( + { from: '*', to: '+15551234567', metadata: { callType: 'CLIENT' } } +); + +// Step 3: PUT the complete object back +await fetch( + `${CONVERSATIONS_V2_BASE}/ControlPlane/Configurations/${configId}`, + { + method: 'PUT', + headers: getAuthHeaders(), + body: JSON.stringify(current), + } +); +``` + +```python +# Fetch current config +current = requests.get( + f"https://conversations.twilio.com/v2/ControlPlane/Configurations/{config_id}", + auth=(account_sid, auth_token) +).json() + +# Modify and PUT the whole thing back +current["channelSettings"]["VOICE"]["captureRules"].append( + {"from": "*", "to": twilio_phone, "metadata": {"callType": "CLIENT"}} +) + +requests.put( + f"https://conversations.twilio.com/v2/ControlPlane/Configurations/{config_id}", + auth=(account_sid, auth_token), + json=current +) +``` + +### Link Intelligence Configuration + +```javascript +// Fetch current config, add Intelligence, PUT back +const current = await fetch( + `${CONVERSATIONS_V2_BASE}/ControlPlane/Configurations/${configId}`, + { headers: getAuthHeaders() } +).then(r => r.json()); + +current.intelligenceConfigurationIds = [intelligenceConfigId]; + +await fetch( + `${CONVERSATIONS_V2_BASE}/ControlPlane/Configurations/${configId}`, + { + method: 'PUT', + headers: getAuthHeaders(), + body: JSON.stringify(current), + } +); +``` + +### Read Conversations and Communications + +```javascript +// List active conversations +const conversations = await fetch( + `${CONVERSATIONS_V2_BASE}/Conversations?Status=ACTIVE&PageSize=10`, + { headers: getAuthHeaders() } +).then(r => r.json()); + +for (const conv of conversations.conversations ?? []) { + // Note: List view has minimal data. For full details, fetch individual conversation + console.log(`Conversation: ${conv.id}, Created: ${conv.createdAt || 'N/A'}`); + + // List communications (messages + voice utterances) + const comms = await fetch( + `${CONVERSATIONS_V2_BASE}/Conversations/${conv.id}/Communications`, + { headers: getAuthHeaders() } + ).then(r => r.json()); + + for (const comm of comms.communications ?? []) { + // Use optional chaining - channel and body may be undefined in list view + console.log(`[${comm.channel ?? 'N/A'}] ${comm.body ?? 'N/A'}`); + } +} +``` + +```python +conversations = requests.get( + "https://conversations.twilio.com/v2/Conversations", + auth=(account_sid, auth_token), + params={"Status": "ACTIVE", "PageSize": 10} +).json() + +for conv in conversations.get("conversations", []): + conv_id = conv["id"] + # Note: List view has minimal data. Use .get() for defensive access + print(f"Conversation: {conv_id}, Created: {conv.get('createdAt', 'N/A')}") + + comms = requests.get( + f"https://conversations.twilio.com/v2/Conversations/{conv_id}/Communications", + auth=(account_sid, auth_token) + ).json() + + for comm in comms.get("communications", []): + # Use .get() - channel and body may be missing in list view + print(f" [{comm.get('channel', 'N/A')}] {comm.get('body', 'N/A')}") +``` + +### Close a Conversation + +Closing triggers Memory extraction (if enabled) and CONVERSATION_END Intelligence operators. + +```javascript +await fetch( + `${CONVERSATIONS_V2_BASE}/Conversations/${convId}`, + { + method: 'PATCH', + headers: getAuthHeaders(), + body: JSON.stringify({ status: 'CLOSED' }), + } +); +``` + +```python +requests.patch( + f"https://conversations.twilio.com/v2/Conversations/{conv_id}", + auth=(account_sid, auth_token), + json={"status": "CLOSED"} +) +``` + +### Voice Integration Patterns + +Active TwiML (recommended for AI voice agents): Pass `conversationConfiguration` on `` to create a new conversation. Do NOT add passive VOICE `captureRules` - this avoids double STT billing. See the [Voice Double Billing Warning](#️-critical-voice-double-billing-warning) section above. + +```xml + + + + + +``` + +Still define VOICE in `channelSettings` for lifecycle/timeouts - just omit `captureRules`: +```json +{ + "channelSettings": { + "VOICE": { + "statusTimeouts": null + } + } +} +``` + +Attach voice to an existing conversation (Real-Time Transcription): Use `` with `conversationId` to add a voice call's transcription to a conversation you created via API: + +```xml + + + + + Welcome to support. How can I help you today? + +``` + +Passive voice capture (human agent calls): Use VOICE `captureRules` to automatically capture calls without TwiML changes. Appropriate for human agent scenarios where ConversationRelay is not used: +```json +{ + "VOICE": { + "captureRules": [ + { "from": "*", "to": "+15551234567", "metadata": {} } + ] + } +} +``` + +> Warning: Do NOT combine passive VOICE capture rules with active TwiML voice. See the [Voice Double Billing Warning](#️-critical-voice-double-billing-warning) section above. + +## Gotchas + +### Setup + +1. Memory Store is required. You cannot create a Configuration without a `memoryStoreId`. Create the Memory Store first via `twilio-customer-memory`. + +2. JSON-only API. All Conversation Orchestrator endpoints require `Content-Type: application/json`. Form-encoded bodies are rejected. This matches Intelligence v3 but differs from most Twilio APIs. + +3. Async creation. POST to `/ControlPlane/Configurations` returns 202 with an operation. Poll the operation's `statusUrl` until `status` is `COMPLETED`, then retrieve the configuration ID from the operation result. + +### Configuration + +4. PUT replaces everything. The most common bug: fetching a config, modifying one field, PUTting back - but forgetting to include `channelSettings` or `memoryStoreId`. The API accepts the PUT and silently removes the omitted fields. Always re-fetch, modify, PUT. + +5. Grouping type is immutable. `conversationGroupingType` cannot be changed after creation. To switch grouping, create a new Configuration and close conversations on the old one. + +6. 10 Configuration limit per account. Hard limit at GA (up to 100 capture rules per channel per config). Delete unused Configurations to make room. For customers with large phone number portfolios, partition numbers across multiple Configurations. + +7. CLIENT voice capture is opt-in. Browser-originated calls via the Twilio Client SDK are not captured by default VOICE rules. You need a separate capture rule with `"metadata": {"callType": "CLIENT"}`. SIP calls similarly need `{"callType": "PUBLIC_SIP"}`. PSTN is the only type captured by default. + +8. `conversationConfiguration` (no "Id" suffix) is the correct TwiML attribute name. The attribute on `` and `` is `conversationConfiguration`, NOT `conversationConfigurationId`. The incorrect name is silently ignored (unrecognized TwiML attributes produce no error), resulting in no conversation being created. + +### Runtime + +9. Timeout precedence across channels. If a customer is on a voice call and sends an SMS, both channels are active in the same Conversation (with `GROUP_BY_PROFILE`). When the voice call ends, the SMS channel's timeout still governs - the Conversation won't close until the SMS timeout expires. Channel close events are proposals, not commands. + +10. Config versioning pins at creation. Intelligence rules and capture rules are pinned to the Configuration version at conversation creation time. Upgrading Intelligence (adding operators, changing rules) doesn't affect existing conversations. Close active conversations to pick up the new version. + +11. ConversationRelay TTS fragmentation. ConversationRelay writes one Communication per TTS fragment, not per complete utterance. A single agent response may produce 3-5 Communications. Intelligence operators fire per Communication, so operator cost scales with fragment count. + +12. Overly broad wildcard VOICE rules match multiple call types. A rule `{"from": "*", "to": "*", "metadata": {"callType": "PSTN"}}` will match all PSTN calls in your account, not just those to/from specific numbers. If you also have CLIENT capture rules, each call could match multiple rules, leading to unexpected conversation grouping. Always use specific `from` or `to` addresses to limit rule scope. + +13. Active TwiML voice and passive capture rules cause double STT billing. See the [Voice Double Billing Warning](#️-critical-voice-double-billing-warning) section for full details. Do not use passive VOICE `captureRules` when passing conversation parameters in TwiML. + +### Observability + +14. Silent Memory linkage failure. If `memoryStoreId` points to a deleted or invalid store, capture still works but identity resolution and extraction silently fail. No error is returned. See `twilio-debugging-observability`. + +15. No participant type filtering for Intelligence. Operators fire on ALL Communications - customer messages AND agent responses. There is no config-level filter. Use the operator prompt to specify which participant to analyze. + +16. Memory extraction is opt-in and fires on INACTIVE and/or CLOSED. Extraction does not run automatically - it must be enabled. It can be configured to fire on the INACTIVE transition, the CLOSED transition, or both. It does NOT fire while a conversation is ACTIVE. For mid-conversation Memory writes, post directly to the Observations endpoint via `twilio-customer-memory`. + +17. List endpoints return partial data. When listing Conversations or Communications via GET `/Conversations` or `/Conversations/{id}/Communications`, response objects are missing fields that are present when fetching individual resources. Missing fields include `dateCreated` (list) vs `createdAt` (single GET), `channels`, `body`, and `channel`. Always use defensive field access (`conv?.createdAt` or `conv.get('createdAt')`) and fetch individual resources if you need complete data. Example: +```javascript +// List returns partial data +const list = await fetch(`${BASE}/Conversations?PageSize=10`); +for (const conv of list.conversations) { + console.log(conv.dateCreated); // undefined + console.log(conv.createdAt); // also undefined in list view + + // Fetch full details if needed + const full = await fetch(`${BASE}/Conversations/${conv.id}`); + console.log(full.createdAt); // ✅ present (note: 'createdAt' not 'dateCreated') +} +``` + +## Related Resources + +- [Conversation Intelligence Skill](../twilio-conversation-intelligence/SKILL.md) - Intelligence Configuration, Language Operators, real-time and post-conversation analysis +- [Customer Memory Skill](../twilio-customer-memory/SKILL.md) - Memory Store, profiles, traits, observations, Recall +- [ConversationRelay Skill](../twilio-voice-conversation-relay/SKILL.md) - Voice AI agent setup with WebSocket streaming +- [Agent Connect Skill](../twilio-agent-connect/SKILL.md) - AI-to-human escalation via TAC SDK +- [Debugging Skill](../twilio-debugging-observability/SKILL.md) - Error triage, Event Streams, linkage chain verification diff --git a/plugins/twilio/skills/twilio-conversation-orchestrator/assets/icon-large.png b/plugins/twilio/skills/twilio-conversation-orchestrator/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-conversation-orchestrator/assets/icon-small.png b/plugins/twilio/skills/twilio-conversation-orchestrator/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Build multi-channel messaging experiences using Twilio Conversations (classic) API. + Covers creating conversations, adding participants (SMS, WhatsApp, chat), + sending messages, and handling webhooks. Use this skill to manage persistent + multi-party or multi-channel conversations beyond single-message SMS/WhatsApp. +--- + +## Overview + +Conversations (classic) API provides persistent, multi-channel threads where participants on SMS, WhatsApp, and web chat can message together. Unlike single-message APIs, Conversations maintains history and supports multi-agent access. + +Note: This is the Conversations (classic) API (v1). + +--- + +## Prerequisites + +- Twilio account with Conversations (classic) enabled + - New to Twilio? See `twilio-account-setup` + - Enable at: [Console > Conversations > Manage > Overview](https://console.twilio.com/us1/develop/conversations/manage/overview) > Enable Conversations +- Environment variables: + - `TWILIO_ACCOUNT_SID` + - `TWILIO_AUTH_TOKEN` + - See `twilio-iam-auth-setup` for credential setup and best practices +- SDK: `pip install twilio` / `npm install twilio` +- For SMS/WhatsApp participants: a Twilio number assigned to a Conversations Service + +--- + +## Setup: Create a Conversation Service (classic) + +A Conversation Service is the parent configuration container for all your conversations in the classic API. You need one before creating conversations with SMS/WhatsApp participants. + +Python +```python +import os +from twilio.rest import Client + +client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"]) + +# Create a Conversation Service +service = client.conversations.v1.services.create( + friendly_name="Customer Support Service" +) +print(f"Service SID: {service.sid}") +``` + +Node.js +```node +const twilio = require("twilio"); +const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); + +// Create a Conversation Service +const service = await client.conversations.v1.services.create({ + friendlyName: "Customer Support Service" +}); +console.log(`Service SID: ${service.sid}`); +``` + +Next: Assign your Twilio phone number to this service at [Console > Conversations > Manage > Services](https://console.twilio.com/us1/develop/conversations/manage/services) > Select your service > Add phone number. + +--- + +## Quickstart + +Python +```python +import os +from twilio.rest import Client + +client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"]) + +# Create a conversation (use default service or specify service_sid) +conversation = client.conversations.v1.conversations.create( + friendly_name="Customer Support - Order #12345" +) + +# Add an SMS participant +client.conversations.v1 \ + .conversations(conversation.sid) \ + .participants \ + .create( + messaging_binding_address="+15558675310", + messaging_binding_proxy_address="+15017122661" + ) + +# Send a message +client.conversations.v1 \ + .conversations(conversation.sid) \ + .messages \ + .create(body="Hello! How can I help you today?", author="support-agent") +``` + +Node.js +```node +const twilio = require("twilio"); +const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); + +// Create a conversation (use default service or specify serviceSid) +const conversation = await client.conversations.v1.conversations.create({ + friendlyName: "Customer Support - Order #12345", +}); + +// Add an SMS participant +await client.conversations.v1 + .conversations(conversation.sid) + .participants.create({ + messagingBindingAddress: "+15558675310", + messagingBindingProxyAddress: "+15017122661", + }); + +// Send a message +await client.conversations.v1 + .conversations(conversation.sid) + .messages.create({ body: "Hello! How can I help you today?", author: "support-agent" }); +``` + +--- + +## Key Patterns + +### Add Participants by Channel + +WhatsApp participant - Python +```python +client.conversations.v1 \ + .conversations(conversation.sid) \ + .participants \ + .create( + messaging_binding_address="whatsapp:+15558675310", + messaging_binding_proxy_address="whatsapp:+14155238886" + ) +``` + +WhatsApp participant - Node.js +```node +await client.conversations.v1 + .conversations(conversationSid) + .participants.create({ + messagingBindingAddress: "whatsapp:+15558675310", + messagingBindingProxyAddress: "whatsapp:+14155238886", + }); +``` + +Chat participant (web/mobile) - Python +```python +client.conversations.v1 \ + .conversations(conversation.sid) \ + .participants \ + .create(identity="user-123") +``` + +Chat participant (web/mobile) - Node.js +```node +await client.conversations.v1 + .conversations(conversationSid) + .participants.create({ identity: "user-123" }); +``` + +### Send Media (All Channels) + +Python +```python +# Send a message with media +client.conversations.v1 \ + .conversations(conversation.sid) \ + .messages \ + .create( + body="Check out this image!", + author="support-agent", + media_url="https://example.com/image.jpg" + ) + +# Multiple media URLs (up to 10) +client.conversations.v1 \ + .conversations(conversation.sid) \ + .messages \ + .create( + body="Here are the documents", + author="support-agent", + media_url=[ + "https://example.com/doc1.pdf", + "https://example.com/doc2.pdf" + ] + ) +``` + +Node.js +```node +// Send a message with media +await client.conversations.v1 + .conversations(conversationSid) + .messages.create({ + body: "Check out this image!", + author: "support-agent", + mediaUrl: "https://example.com/image.jpg" + }); + +// Multiple media URLs (up to 10) +await client.conversations.v1 + .conversations(conversationSid) + .messages.create({ + body: "Here are the documents", + author: "support-agent", + mediaUrl: [ + "https://example.com/doc1.pdf", + "https://example.com/doc2.pdf" + ] + }); +``` + +Media must be publicly accessible URLs. Supported: JPG, PNG, GIF, PDF, vCard. Max 10 URLs per message. Works across all channels: SMS (as MMS), WhatsApp, and chat participants all receive media. + +### Add Multiple Participants + +Python +```python +# Add multiple SMS participants to a conversation +participant_numbers = [ + "+15558675310", + "+15558675311", + "+15558675312" +] + +twilio_number = "+15017122661" + +for phone_number in participant_numbers: + client.conversations.v1 \ + .conversations(conversation.sid) \ + .participants \ + .create( + messaging_binding_address=phone_number, + messaging_binding_proxy_address=twilio_number + ) +``` + +Node.js +```node +// Add multiple SMS participants to a conversation +const participantNumbers = [ + "+15558675310", + "+15558675311", + "+15558675312" +]; + +const twilioNumber = "+15017122661"; + +for (const phoneNumber of participantNumbers) { + await client.conversations.v1 + .conversations(conversationSid) + .participants.create({ + messagingBindingAddress: phoneNumber, + messagingBindingProxyAddress: twilioNumber + }); +} +``` + +### Fetch Message History + +Python +```python +# Get all messages from a conversation +messages = client.conversations.v1 \ + .conversations(conversation.sid) \ + .messages \ + .list(limit=50) + +for message in messages: + print(f"{message.author}: {message.body}") +``` + +Node.js +```node +// Get all messages from a conversation +const messages = await client.conversations.v1 + .conversations(conversationSid) + .messages + .list({ limit: 50 }); + +messages.forEach(message => { + console.log(`${message.author}: ${message.body}`); +}); +``` + +### List Conversations + +Python +```python +# List all conversations +conversations = client.conversations.v1.conversations.list(limit=20) + +for conv in conversations: + print(f"{conv.friendly_name} - {conv.sid}") + +# Filter by state +active_conversations = client.conversations.v1.conversations.list( + state="active", + limit=20 +) +``` + +Node.js +```node +// List all conversations +const conversations = await client.conversations.v1.conversations.list({ limit: 20 }); + +conversations.forEach(conv => { + console.log(`${conv.friendlyName} - ${conv.sid}`); +}); + +// Filter by state +const activeConversations = await client.conversations.v1.conversations.list({ + state: "active", + limit: 20 +}); +``` + +### Remove Participants + +Python +```python +# Remove a participant by participant SID +client.conversations.v1 \ + .conversations(conversation.sid) \ + .participants(participant_sid) \ + .delete() + +# Find and remove by phone number +participants = client.conversations.v1 \ + .conversations(conversation.sid) \ + .participants \ + .list() + +for p in participants: + if p.messaging_binding and p.messaging_binding.get("address") == "+15558675310": + client.conversations.v1 \ + .conversations(conversation.sid) \ + .participants(p.sid) \ + .delete() +``` + +Node.js +```node +// Remove a participant by participant SID +await client.conversations.v1 + .conversations(conversationSid) + .participants(participantSid) + .remove(); + +// Find and remove by phone number +const participants = await client.conversations.v1 + .conversations(conversationSid) + .participants + .list(); + +for (const p of participants) { + if (p.messagingBinding?.address === "+15558675310") { + await client.conversations.v1 + .conversations(conversationSid) + .participants(p.sid) + .remove(); + } +} +``` + +### Close/Complete Conversations + +Python +```python +# Close a conversation (marks it inactive) +client.conversations.v1 \ + .conversations(conversation.sid) \ + .update(state="closed") + +# Delete a conversation completely +client.conversations.v1 \ + .conversations(conversation.sid) \ + .delete() +``` + +Node.js +```node +// Close a conversation (marks it inactive) +await client.conversations.v1 + .conversations(conversationSid) + .update({ state: "closed" }); + +// Delete a conversation completely +await client.conversations.v1 + .conversations(conversationSid) + .remove(); +``` + +### Handle Incoming Messages (Webhook) + +Configure at Console > Conversations > Manage > Global Webhooks. + +> Security: Always validate the `X-Twilio-Signature` header in production to confirm requests originate from Twilio. See `twilio-webhook-architecture` for validation patterns. + +Python (Flask) +```python +from twilio.request_validator import RequestValidator + +@app.route("/conversations/webhook", methods=["POST"]) +def conversations_webhook(): + validator = RequestValidator(os.environ["TWILIO_AUTH_TOKEN"]) + if not validator.validate(request.url, request.form, request.headers.get("X-Twilio-Signature", "")): + return "", 403 + + event_type = request.form.get("EventType") + conversation_sid = request.form.get("ConversationSid") + author = request.form.get("Author") + + if event_type == "onMessageAdded" and author != "support-agent": + client.conversations.v1.conversations(conversation_sid).messages.create( + body="Thanks - an agent will be with you shortly.", + author="support-bot" + ) + return "", 204 +``` + +Node.js (Express) +```node +app.post("/conversations/webhook", async (req, res) => { + const valid = twilio.validateRequest( + process.env.TWILIO_AUTH_TOKEN, + req.headers["x-twilio-signature"], + `https://${req.headers.host}${req.originalUrl}`, + req.body + ); + if (!valid) return res.status(403).send("Forbidden"); + + const { EventType, ConversationSid, Author } = req.body; + if (EventType === "onMessageAdded" && Author !== "support-agent") { + await client.conversations.v1 + .conversations(ConversationSid) + .messages.create({ body: "Thanks - an agent will be with you shortly.", author: "support-bot" }); + } + res.sendStatus(204); +}); +``` + +--- + +## Advanced Context + +### Message Delivery Status + +Track message delivery through webhooks. Configure at Console > Conversations > Manage > Global Webhooks. + +Available delivery events: +- `onMessageAdded` - Message created +- `onMessageUpdated` - Message status changed +- `onDeliveryUpdated` - Delivery receipt received (SMS/WhatsApp only) + +Python (Flask) +```python +@app.route("/conversations/webhook", methods=["POST"]) +def delivery_webhook(): + event_type = request.form.get("EventType") + + if event_type == "onDeliveryUpdated": + delivery_status = request.form.get("DeliveryStatus") + message_sid = request.form.get("MessageSid") + print(f"Message {message_sid}: {delivery_status}") + # Status values: sent, delivered, failed, undelivered + + return "", 204 +``` + +Node.js (Express) +```node +app.post("/conversations/webhook", (req, res) => { + const { EventType, DeliveryStatus, MessageSid } = req.body; + + if (EventType === "onDeliveryUpdated") { + console.log(`Message ${MessageSid}: ${DeliveryStatus}`); + // Status values: sent, delivered, failed, undelivered + } + + res.sendStatus(204); +}); +``` + +### Conversation Attributes (Metadata) + +Store custom metadata on conversations (order IDs, customer info, tags). + +Python +```python +# Set attributes when creating +conversation = client.conversations.v1.conversations.create( + friendly_name="Customer Support - Order #12345", + attributes='{"order_id": "12345", "priority": "high", "customer_tier": "gold"}' +) + +# Update attributes on existing conversation +client.conversations.v1 \ + .conversations(conversation.sid) \ + .update(attributes='{"order_id": "12345", "status": "resolved"}') + +# Read attributes +conv = client.conversations.v1.conversations(conversation.sid).fetch() +import json +attrs = json.loads(conv.attributes) +print(f"Order ID: {attrs['order_id']}") +``` + +Node.js +```node +// Set attributes when creating +const conversation = await client.conversations.v1.conversations.create({ + friendlyName: "Customer Support - Order #12345", + attributes: JSON.stringify({ orderId: "12345", priority: "high", customerTier: "gold" }) +}); + +// Update attributes on existing conversation +await client.conversations.v1 + .conversations(conversationSid) + .update({ attributes: JSON.stringify({ orderId: "12345", status: "resolved" }) }); + +// Read attributes +const conv = await client.conversations.v1.conversations(conversationSid).fetch(); +const attrs = JSON.parse(conv.attributes); +console.log(`Order ID: ${attrs.orderId}`); +``` + +### Participant Attributes (Metadata) + +Store metadata on individual participants (role, name, account info). + +Python +```python +# Set attributes when adding participant +client.conversations.v1 \ + .conversations(conversation.sid) \ + .participants \ + .create( + messaging_binding_address="+15558675310", + messaging_binding_proxy_address="+15017122661", + attributes='{"name": "John Doe", "role": "customer", "account_id": "A123"}' + ) + +# Update participant attributes +client.conversations.v1 \ + .conversations(conversation.sid) \ + .participants(participant_sid) \ + .update(attributes='{"role": "vip_customer", "satisfaction": "high"}') +``` + +Node.js +```node +// Set attributes when adding participant +await client.conversations.v1 + .conversations(conversationSid) + .participants.create({ + messagingBindingAddress: "+15558675310", + messagingBindingProxyAddress: "+15017122661", + attributes: JSON.stringify({ name: "John Doe", role: "customer", accountId: "A123" }) + }); + +// Update participant attributes +await client.conversations.v1 + .conversations(conversationSid) + .participants(participantSid) + .update({ attributes: JSON.stringify({ role: "vip_customer", satisfaction: "high" }) }); +``` + +--- + +## Limits + +| Limit | Value | +|-------|-------| +| Participants per conversation | 1,000 | +| Messages per conversation | Unlimited (older messages may be archived) | +| Message retention | Configurable (default: indefinite) | + +--- + +## CANNOT + +- Cannot add SMS participants without a Twilio number - Number must be assigned to a Conversations (classic) Service +- Cannot send WhatsApp messages outside the 24-hour window - Subject to service window rules. See `twilio-whatsapp-send-message` +- Cannot use chat participants without Access Tokens - Client-side SDK auth required. See `twilio-iam-auth-setup` +- Cannot use WhatsApp Groups API - Deprecated April 2020. Use Conversations (classic) API instead. +- Conversations v1 (classic) is in maintenance mode - Consider Conversations v2 API for new projects with enhanced features and scalability. + +--- + +## Next Steps + +- WhatsApp setup and rules: `twilio-whatsapp-send-message` +- SMS setup: `twilio-sms-send-message` +- Access Tokens for chat clients: `twilio-iam-auth-setup` +- Webhook security: `twilio-webhook-architecture` diff --git a/plugins/twilio/skills/twilio-conversations-classic-api/assets/icon-large.png b/plugins/twilio/skills/twilio-conversations-classic-api/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-conversations-classic-api/assets/icon-small.png b/plugins/twilio/skills/twilio-conversations-classic-api/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 zcmeHKg;&&3xBU$vAtfLTNOwpoEg;McDcvP7G)N2~rL=S-AvH8gNOwpJjC2Y}Nr=SI z15!`ld+YrXZ+&;&efK_R-8}2A8&R6-io}Gpga7~#D=R^@|FXtE;A8(SbAH>`0{|h1 z0|cV!VC4(|KG`{G-nzPTR1sqx-hTNbf}UW%D(!n@JM={cT439I;9;PbX#PG651AIU zh&u!amk3M@gTpBAgS0^Y&qIR9L$zLgv{sl^dE@Jg-rU4|m0nq}LwjJp`>!_OVC|Fu9rw}w_`49?>=~@1V$-Q7vxhBF%2=%osz?_XCsdw4!d+Ve zD;U9)lG?LTxAS?&f#$_BEkK37HWFT6n1sO^MVlHLMH>Z$?g}*TyiQzImKI@tG^Z_g zWR+;rMf1zzIZ#&y#f&Y6vGIZskZc4o1)@o_W|;vt*jp#Yi*=c&N9}9`I5e+IN+cvC zI7~@UP}A|1Gg9F|Kp?|K4*2IT|(OksWjt!WpAEZ)_UlV7IGfsX64L{}+P+s14vH=NJoL_pfux!>k(NcQL0 zScy2gd4O&($2j&t7uG!BzlyUIOo|WWGZ9u`Jx;`#1Q<7h&MfW^;=QvFo5$mkBmRgN zmxr4T(62si2KYmFSMPzbFx^}kz*yH~AL1=${2qDhNG$H>j)|hy_(so(>}fVgF6Fv4 zDJk*T<)10xS4K3-ztbepC76+q8*dut{)OfFkT&c<-fH~mDeozRf(7}DcX}4|^OUw- zqtya*gjHb_YcG8GvhmO%s5Qw8@Dho6P*pGFkn5T#7Xrvb?IR`Ov4UgCWBnUq3saMzk}1;#MUbM=J#km*5z`DBGUU&_mA*2FZ6hQS6R$tB&?g{Nl?5XYHob$y;>hv0|K69H8XW*wc2zTnq z>HpEEw!U?w?c>}|F9VDXRCQDT+&wb80A1q*&`tN#9AcNmbzw6_D2~iYd4oW*Z8} zuw&L^6N2clsIVp+6L<8*B#ExRqP$5woRaz_dEHW+5(0){0 zR-~>Rr!=ChQ#4YHT{Kk`QF zi{m4>jDIF8^95f~0A#;(ba;5scsLyOIju5TnpH{#VlVmB)tIL;<8_GW3x9!9mhH!1 zlNPz>SiE%(;@XqjQ=TbyKJU~EP`d5FGV3;DhFK=aNAy5o?+b$AiSYP!xqj8?Cn~z^ z{_JVm!IK2V{=?O^!S2<^lAFE5Z-+g2%6M{lXm}p-Q0v+1I+UrG4V4v_+3A(ea5sINZ4Ulu=Gx$@3Gxjq+AC$a)KTthLp*v?CfkeysW}6C+MMdEIus(-EW|Ri1$x`D;nP zCiaAO9X4A=Pui&$P;*WRbCFHaVyLzW|JkntcA**x0z+bbO_P`!WH94O6^ij3bF{L1 zXVG%*zI+@Hkgd{Ji>+Gbxmeh)V&pRNy7J2MIJ>vHcSHRlT+B7hYRpv-Gx-<@I%4M? zL2qj>d#_c`quv#0GweMyRv}ZtC9<8N7~CKCJ9a=JLQxDlR_SJ5K`N~(cVAX@2B7}b5RX1`6qA4SQ|z_ts-=B9x`9iT?c7=2q|0;C>tV>oaN-YR09w?<0%S?OzdN6XZL|4eUStZ!nGNldAVs z%=cq{csG2jt+17~{Ni?J-(v1-_fEh6AeuB)ITfPEuU9!UJR?=ULTAC)>^r_=p&F;6 zW;t>NS%P>wN;@s?dj1|Ju_Cc!4A5qLxpF1)b9dlge|S%Lf})S2Lc%@9eTRC7SFf~P zcwEA>-i=Isg~$K67}(DmrTUq+pl|H7hHx|Wx!!3)tdJlNr6=AdBWgFL_R(dFT+X=r znpc_U-{XtoCq_SxcI(KLwzloHEp&=q-VQpPP3hTLk5MAovqz-b++6lsT25%T?fBVV zg3-)KRxwopF>7Px{o8Q#TwP2PWM(~zaEtpAr#UUzxww|4x%anIEGFAlG z?=~H{GFH?4YPa++baLBS{hk#sCa~n*=5^VK+24hrBJM@>hNENqVq=#rS3b06`C3CoPvc5?bJd@rd_-ka?&?m*X2#BP12HjE0&nY*>L>&*1gHY|{5~BX5Avc6 zes~0XB)lx`$PLsv)L+}5@Q(HFK(j7ujBdZ_3gEi?cvrKVf-c3o?{NSA7Z4`w-O`j(wLyRB&{JDo2LN#C<($)7r?-wT|7_2; zXCh#X_wEaSoH@L{IhwB!A;M;T)v|Yc(BaO7leoMtg(sNtdp}Q%Hh5zvnn`VNT>oMC zNfJ+e=ac6X-&H~CtHa3wZ_74KPGfbr$w)agBKs{B$c?3cyklEZ{+7d!^&Tx$K862+ z+P{*y;3A$RK_$Suf|oiOw&td>qMP2%axqEt>kNdfSv-?@3|f@TbH^DlAh zhQ}-U6kqLXM5_$fc(om-!p^afAzmw|C9c8|Ihkw zb{%4E1%P|U%1}97!`>Xj9SDUY zX%hg7^gBDSnrlLKgci;Y$?fe;u6PAy;RN|5;0lg+j4_dF2=H|lE!TE`mT}KwmRpnB zz?LnZ-Jq5HCd)KV1tq)0!BuV8N*C@s=+(@R%md57i)I&SMf_s(o?wN=5jRDbm^?_* zn%&1Q_=QlBb33JFw1PPV5it4^!V2g5<^@$1aU!n_9kJ>5Gn9|EKp+mz`LGg`C>>o} zKu*LfQ|L>I&!1Hq<=flaCsuZGs;{5gRwFBcDL0|>H_PvLJhndyw9rjd_cgzy+VMKF#XXV}^xw@s#Kw7AXgUq0IL8Xv!d8 z32U~;CZ9#O#Wis^wjL~vB98!OKBNU#BzHaKCnjS#mzAbS3%oOxc7ESVLcl55+;lzc zt7Q@XVO0lfbwc@^wegrImzhcUkk9B>bzqZ!zBGE+D}+$-*Pp12>2Mx({5;yJS%g)7 z_3*s{wF?38cZG`r!#gbxhIS$%?aF2FR zuD;_6JWzI%&9fFW9nP25=&rD{`0drzJ27&Zye1|}OaDDFFeyozMKrV`?;VIXRA_-2 z0)$%Q6tF7j*<;jfoUSpQ$O*Li6E5$EpLzDLVK@`v&BiB8hBORW!@ZPX<}-9NIm&Nb!`x zxhF*xjplgMrmGWpL4XkAG#bKKzsB1=F2h*X0454I-UjkZ*|RQbz4Y1da( zDqe==`D`bO*ic&=yiFt@5D_Km^0_R!)JmDq9VMb`$L&Ff`oKE2?x5j zW<+SXv!GWP!S5gX+r$sR{HHnRT3O9o>pun&AE{Edq*NAL0-nj43_R2nkB+wEeAn*L z!NA_RDE?x4$h;Vp@W`j7@Fd$xCfHOYW?}fIQ-Z8Ka=%Hu)hhiKTgipBYgNi{CMkRC zmjRb&qMV^W&ise5FgbljTgJpCF2Q-XvwW(DqcQH{N$y(#V!(v<#)@Mrh5Ny0WT}E} z$7^)k4guy%V4^~8!F$yb&3*qtbiSkSCCoju)GogMIJA9$$StckGEeh?@pS+-h!%~P z;uA8;slqGJ;3Jc@w|&la=O{mr;Ng2--oF=d-L{e$2*LPrsr{kucVjuDz+8niTr7ra zr$ow5m_5D~+~YxA)?f?N)^0{hoCu{c8s0XVd|Pp9!lzC9f^ep5pr{hics!e~v!NS3 zlV0lcwtM)_rlI%`CYEiYt?sNUrtqv@r&z2da5U|=$Br@sUvfWcscV9PTC4h=#4&e_ zT|uDr?YBb2jTKM}{vj*4-=G#7x5l6`s~c#7Iu&aOXXOseztNB7kvC~SylvZ*=zSD) z52uEogmV5Pd6DfFcI}4`nWWZQ-=6A4*_(Ksfclv17OwDf%-{3UcN9!6ZuzfMcvo~BqKbas#zHsLb z4P7pJ)Zsd4!Z7UPs#JI@Tuk^ex5>d5#a!s3A}W0+n5y&g%_nzl2ISD`RZRSyY2>VZ zB}vQ5>EZq*Cq~pO&6BZQ(AIu2U(m{4^TsT%!OINkcy0z0YiO_Df2+2*Z*|%InRRmU zUOlB+YC{*b^x%VQ8Kde!zmlmBx^@Eim8HAIc~Ml9M`*u_g3X&LmJ4E9sFnMe;iNIm z?SXl4)zdwita7vPOu3W20RPz%3?h_%Q)cE;7*9ChJ%tJScFwS@_F-J``({4Rx88E1 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Store and retrieve customer context using Twilio Conversation Memory. + Covers Memory Store provisioning, profile management, traits, observations, + conversation summaries, and semantic Recall. Use this skill to give AI agents + or human agents persistent memory of customer interactions across sessions + and channels. +--- + +## Overview + +Conversation Memory gives your application persistent customer memory. Observations (what happened) and traits (who the customer is) are written automatically from conversations flowing through Conversation Orchestrator/Orchestrator - or posted directly if you run your own extraction. Retrieve relevant context via Recall before responding. + +``` +Conversation Orchestrator/Orchestrator conversation -> auto-extracted observations & summaries -> Memory Store +Your App -> Recall -> relevant context injected into LLM prompt +``` + +All Conversation Memory APIs are on `memory.twilio.com`. Observations, traits, profiles, summaries - everything is on the same host. + +Auth: Basic Auth - `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN`. + +--- + +## Prerequisites + +- Twilio account with Conversation Memory access (requires enablement) + - New to Twilio? See `twilio-account-setup` +- `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN` - see `twilio-iam-auth-setup` +- Memory Store must be created before creating a Conversations Service in Conversation Orchestrator/Orchestrator - the store SID is required in the conversation config +- For conversation orchestration: `twilio-conversation-orchestrator` + +--- + +## Quickstart + +### Step 1 - Create a Memory Store + +Do this before setting up Conversation Orchestrator/Orchestrator. The Memory Store SID goes into your conversation service config. + +Python +```python +import os, requests + +account_sid = os.environ["TWILIO_ACCOUNT_SID"] +auth_token = os.environ["TWILIO_AUTH_TOKEN"] + +store = requests.post( + "https://memory.twilio.com/v1/Services", + auth=(account_sid, auth_token), + json={ + "uniqueName": "my-app-memory", + "friendlyName": "My App Memory Store" + } +).json() + +memory_store_sid = store["sid"] +print(memory_store_sid) +``` + +Node.js +```javascript +const accountSid = process.env.TWILIO_ACCOUNT_SID; +const authToken = process.env.TWILIO_AUTH_TOKEN; + +const store = await fetch("https://memory.twilio.com/v1/Services", { + method: "POST", + headers: { + "Authorization": "Basic " + btoa(`${accountSid}:${authToken}`), + "Content-Type": "application/json", + }, + body: JSON.stringify({ + uniqueName: "my-app-memory", + friendlyName: "My App Memory Store", + }), +}).then(r => r.json()); + +const memoryStoreSid = store.sid; +``` + +Use `memory_store_sid` when creating your Conversations Service in Conversation Orchestrator/Orchestrator. The two must be linked for automatic observation and summary extraction to work. + +### Step 2 - Profiles + +Profiles are created automatically when conversations flow through Conversation Orchestrator/Orchestrator - the conversation config determines how participants are resolved into profiles. You can also create or enrich profiles manually using traits. + +Create a profile manually with traits: + +Python +```python +profile = requests.post( + f"https://memory.twilio.com/v1/Services/{memory_store_sid}/Profiles", + auth=(account_sid, auth_token), + json={ + "traits": { + "Contact": { + "phone": "+15558675310", + "firstName": "Alyssa", + "lastName": "Mock", + "email": "alyssa@example.com" + } + } + } +).json() + +profile_id = profile["id"] +``` + +Node.js +```javascript +const profile = await fetch( + `https://memory.twilio.com/v1/Services/${memoryStoreSid}/Profiles`, + { + method: "POST", + headers: { + "Authorization": "Basic " + btoa(`${accountSid}:${authToken}`), + "Content-Type": "application/json", + }, + body: JSON.stringify({ + traits: { + Contact: { + phone: "+15558675310", + firstName: "Alyssa", + lastName: "Mock", + email: "alyssa@example.com", + } + } + }), + } +).then(r => r.json()); + +const profileId = profile.id; +``` + +Look up a profile by phone number (for inbound calls where you only have the caller's number): + +Python +```python +lookup = requests.post( + f"https://memory.twilio.com/v1/Services/{memory_store_sid}/Profiles/Lookup", + auth=(account_sid, auth_token), + json={"idType": "phone", "value": "+15558675310"} +).json() + +profile_id = lookup["profiles"][0]["id"] if lookup.get("profiles") else None +``` + +### Step 3 - Observations + +Observations are extracted automatically from conversations when a conversation becomes inactive or is closed, based on your conversation config. You don't need to write them manually for Conversation Orchestrator-managed conversations. + +If you run your own extraction (custom pipeline outside Conversation Orchestrator), post results directly: + +Python +```python +requests.post( + f"https://memory.twilio.com/v1/Services/{memory_store_sid}/Profiles/{profile_id}/Observations", + auth=(account_sid, auth_token), + json={ + "observations": [ + { + "content": "Customer asked about order #4521. Wants expedited shipping. Prefers SMS updates.", + "source": "custom_extraction", + "occurredAt": "2026-04-20T14:30:00Z", + "conversationIds": [conversation_sid] + } + ] + } +) +``` + +Node.js +```javascript +await fetch( + `https://memory.twilio.com/v1/Services/${memoryStoreSid}/Profiles/${profileId}/Observations`, + { + method: "POST", + headers: { + "Authorization": "Basic " + btoa(`${accountSid}:${authToken}`), + "Content-Type": "application/json", + }, + body: JSON.stringify({ + observations: [{ + content: "Customer asked about order #4521. Wants expedited shipping. Prefers SMS updates.", + source: "custom_extraction", + occurredAt: new Date().toISOString(), + conversationIds: [conversationSid], + }] + }), + } +); +``` + +Batch up to 10 observations in one request. + +### Step 4 - Recall Context Before Responding + +Recall runs hybrid lexical + semantic search and returns the most relevant observations and summaries for an LLM prompt. + +Recommended: pass a `conversationId` from Conversation Orchestrator/Orchestrator. Recall builds a contextually relevant query from the active conversation automatically - no need to craft one yourself. + +Python +```python +recall = requests.post( + f"https://memory.twilio.com/v1/Services/{memory_store_sid}/Profiles/{profile_id}/Recall", + auth=(account_sid, auth_token), + json={ + "conversationId": orchestrator_conversation_sid, + "observationsLimit": 10, + "summariesLimit": 3, + } +).json() + +observations = "\n".join(o["content"] for o in recall.get("observations", [])) +summaries = "\n".join(s["content"] for s in recall.get("summaries", [])) + +system_prompt = f"""You are a helpful support agent. + +Customer history: +{observations} + +Recent summaries: +{summaries}""" +``` + +Node.js +```javascript +const recall = await fetch( + `https://memory.twilio.com/v1/Services/${memoryStoreSid}/Profiles/${profileId}/Recall`, + { + method: "POST", + headers: { + "Authorization": "Basic " + btoa(`${accountSid}:${authToken}`), + "Content-Type": "application/json", + }, + body: JSON.stringify({ + conversationId: orchestratorConversationSid, + observationsLimit: 10, + summariesLimit: 3, + }), + } +).then(r => r.json()); + +const context = [ + ...recall.observations.map(o => o.content), + ...recall.summaries.map(s => s.content), +].join("\n"); +``` + +Other Recall modes: + +| Mode | How | When to use | +|------|-----|-------------| +| Conversation ID (recommended) | `"conversationId": orchestrator_sid` | Active Conversation Orchestrator/Orchestrator conversation - query is generated from conversation context | +| Custom query | `"query": "your question"` | Custom pipelines outside Conversation Orchestrator, or when you need precise control over relevance | +| No query | Omit both `query` and `conversationId` | Returns most recent observations in chronological order - useful for loading history at session start | + +--- + +## Key Patterns + +### Trait Groups + +Traits are organized into named groups. The `Contact` group is the standard identity anchor - its fields are promoted to profile identifiers for lookup. + +| Group | Fields | Use | +|-------|--------|-----| +| `Contact` | phone, email, firstName, lastName | Identity anchor - always include | +| `Account` | accountNumber, tier, region | Business account data | +| `Support` | disposition, caseId, lastIssueType | Support history | + +Define your own groups for domain-specific data. + +### Summaries + +Summaries are written automatically at conversation close or when a conversation goes inactive, based on your conversation config - the same trigger as observations. You can also write them manually: + +Python +```python +requests.post( + f"https://memory.twilio.com/v1/Services/{memory_store_sid}/Profiles/{profile_id}/ConversationSummaries", + auth=(account_sid, auth_token), + json={ + "conversationId": conversation_sid, + "content": "Customer called about order #4521. Resolved: approved expedited upgrade.", + "source": "manual" + } +) +``` + +Summaries are returned in the `summaries` array of Recall results. + +### Voice Agent Integration + +Retrieve memory at call start, store observations at call end. For voice AI agents on ConversationRelay. + +Python (WebSocket handler) +```python +async def handle_call(websocket): + setup = json.loads(await websocket.recv()) + caller = setup.get("from", "unknown") + + # Look up profile by caller phone + lookup = requests.post( + f"https://memory.twilio.com/v1/Services/{MEMORY_STORE_SID}/Profiles/Lookup", + auth=(ACCOUNT_SID, AUTH_TOKEN), + json={"idType": "phone", "value": caller} + ).json() + profiles = lookup.get("profiles", []) + profile_id = profiles[0]["id"] if profiles else None + + context = "" + if profile_id: + recall = requests.post( + f"https://memory.twilio.com/v1/Services/{MEMORY_STORE_SID}/Profiles/{profile_id}/Recall", + auth=(ACCOUNT_SID, AUTH_TOKEN), + json={"observationsLimit": 5, "summariesLimit": 2} + ).json() + context = "\n".join(o["content"] for o in recall.get("observations", [])) + + system_prompt = f"You are a helpful agent.\n\nCustomer history:\n{context}" if context else "You are a helpful agent." + + # ... handle conversation ... + + # Store observation at end if running custom extraction + if profile_id: + requests.post( + f"https://memory.twilio.com/v1/Services/{MEMORY_STORE_SID}/Profiles/{profile_id}/Observations", + auth=(ACCOUNT_SID, AUTH_TOKEN), + json={"observations": [{"content": call_summary, "source": "voice_agent", "conversationIds": [orchestrator_conversation_sid]}]} + ) +``` + +### Multi-Tenant (ISV) Pattern + +Use one Memory Store per client. The `uniqueName` doubles as a namespace. + +```python +# At client onboarding +store = requests.post( + "https://memory.twilio.com/v1/Services", + auth=(account_sid, auth_token), + json={"uniqueName": f"client-{client_id}", "friendlyName": client_name} +).json() +# Store store["sid"] in your tenant config - pass it to Conversation Orchestrator conversation service setup +``` + +--- + +## CANNOT + +- Cannot create Conversation Orchestrator before Memory Store - Create Memory Store first. Its SID is required when creating the Conversations Service. Reversing this order breaks the linkage. +- Cannot extract observations mid-conversation - Automatic extraction happens on conversation close or inactive. For real-time writing, post directly to the Observations endpoint. +- Cannot read observations immediately after write - Eventual consistency. Allow ~2 seconds after write before querying Recall. +- Cannot exceed 15 Memory Stores per account - ISVs with more than 15 tenants should use sub-accounts +- Cannot detect misconfigured linkages - If Memory Store is not correctly linked in Conversation Orchestrator config, observations are silently not extracted. See `twilio-debugging-observability`. +- Cannot recover deleted profiles - Profile deletion is irreversible, permanent +- Cannot exceed 20 observations per Recall query - `observationsLimit` max 20, default 5. `summariesLimit` and `communicationsLimit` similar. +- Cannot batch more than 10 observations per request - Hard limit on batch writes + +--- + +## Next Steps + +- Set up Conversation Orchestrator conversations: `twilio-conversation-orchestrator` +- Add real-time intelligence: `twilio-conversation-intelligence` +- Enterprise knowledge retrieval (scripts, offers, policies): `twilio-enterprise-knowledge` +- Voice agent setup: `twilio-voice-conversation-relay` +- Debug integration issues: `twilio-debugging-observability` diff --git a/plugins/twilio/skills/twilio-customer-memory/assets/icon-large.png b/plugins/twilio/skills/twilio-customer-memory/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-customer-memory/assets/icon-small.png b/plugins/twilio/skills/twilio-customer-memory/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Planning skill for building customer service and support systems. + Qualifies the developer's needs across the support ladder + (self-service -> AI agents -> contact center), channel mix, and scale + to recommend the right Twilio architecture. Handles both "build me a + call center" and "add an IVR to my existing support line." +tier: discover +--- + +## Role + +You are a Customer Support Architecture Advisor. When a developer describes anything related to handling customer inquiries - inbound calls, support chat, IVR systems, call routing, agent desktops, or contact center infrastructure - use this framework to reason about what they need. + +## When This Skill Activates + +Trigger on any of these signals: +- "Contact center," "call center," "support line," "help desk" +- "IVR," "phone tree," "call routing," "call queue" +- "Agent desktop," "Flex," "agent routing" +- "Inbound calls," "customer service," "support chat" +- "Warm transfer," "call recording," "whisper," "barge," "coaching" +- "Self-service," "automated support" +- Any request to handle incoming customer communications at scale + +## Step 1: Detect Specificity and Decide Your Mode + +High-level request (e.g., "I need to build a customer support system"): +-> DISCOVERY MODE. Walk through Steps 2-4. This is a big architectural decision. + +Mid-level request (e.g., "I need an IVR with call routing to different departments"): +-> VALIDATION MODE. They've described a pattern - validate the approach, recommend Studio vs custom TwiML, check if they need TaskRouter or simple `` routing. + +Specific implementation request (e.g., "Create a TwiML Bin that plays a greeting and gathers digits"): +-> BUILD MODE. Proceed with the relevant Product skill. Quick check: Are they building a one-off or something that should scale? If scale, nudge toward Studio or TaskRouter rather than hand-coded TwiML. + +## Step 2: Qualify Intent - The 6 Essential Questions + +1. Inbound, outbound, or both? + - Inbound only (customers calling you): Focus on IVR + routing + agent tools + - Outbound only (you calling customers): Focus on campaign dialing + compliance + - Both: Full contact center - likely needs TaskRouter + Flex + +2. Which channels do customers use to reach you? + - Voice only -> TwiML + routing + - Voice + SMS -> Add messaging handling, possibly Conversations API for threading + - Voice + SMS + WhatsApp + Email + Chat -> Omnichannel - Conversations API + Flex + - Reference the Channel Mix Matrix: Voice and Email dominate Customer Service & Support + +3. What's your call/message volume? + - Low (< 50/day): Simple TwiML + `` may suffice + - Medium (50-500/day): TaskRouter for fair distribution + basic reporting + - High (500+/day): Full TaskRouter + Flex + real-time monitoring + queue management + +4. Do you need self-service automation? + - Simple menu ("Press 1 for billing"): TwiML `` + `` + - Complex multi-step flow: Twilio Studio (no-code, recommended by SEs over custom state machines) + - AI-powered self-service: -> Hand off to `twilio-ai-agent-architect` Planner skill + +5. Do you need agent tooling (desktop, CRM integration)? + - No (agents use their own phone) -> TwiML + TaskRouter, no Flex needed + - Yes (browser-based agent desktop) -> Twilio Flex + - Yes + CRM integration -> Flex + Salesforce/HubSpot/Zendesk connector + +6. What happens during transfers and holds? + - Simple cold transfer -> `` to another number + - Warm transfer (introduce caller to next agent) -> Conference API + - Coaching/whisper/barge (supervisor listens, coaches agent) -> Conference with participant modes + +## Step 3: Assess Sophistication - The Support Ladder + +### Level 1: Self-Service Automation +Developer says: "I want an automated phone menu / IVR." +Architecture: TwiML (``, ``, ``) or Twilio Studio +Key decision - Studio vs Custom TwiML: +- Use Studio when: Non-developers need to modify flows. Multi-step logic with branching. Rapid prototyping. SEs strongly recommend this over hand-coded state machines. +- Use custom TwiML when: Developer team wants full code control. Flows are simple (< 3 levels). Need dynamic behavior from external APIs. +- Use TwiML Bins when: Static responses only. No logic. Fastest to deploy. +Relevant bundled skills: `twilio-voice-twiml` + +### Level 2: AI-Powered Self-Service +Developer says: "I want AI to handle the easy questions before routing to humans." +Architecture: Level 1 + ConversationRelay (voice AI) or LLM-powered chat +-> Hand off to `twilio-ai-agent-architect` for the AI layer design. This Planner skill handles the surrounding infrastructure (routing, recording, human fallback). +Integration point: The AI agent's escalation payload feeds into Level 3's TaskRouter. + +### Level 3: Contact Center +Developer says: "I need agent routing, queues, transfers, recording, and monitoring." +Architecture: TaskRouter + Conference + Recordings + (optionally) Flex +TaskRouter (the core of any Twilio contact center): +- Workers = your agents (with attributes: skills, languages, department) +- Task Queues = logical groups (billing, technical, VIP) +- Workflows = routing rules (if skill=billing AND language=es, route to Spanish billing queue) +- Reservations = agent accepts/rejects the task + +Conference (for call orchestration): +- Every call should be a Conference, not a direct `` - this enables warm transfer, hold, coaching +- Hold vs Mute: Hold plays music and the held party can't hear. Mute silences one party but they still hear. Critical distinction. +- Coaching: Supervisor joins as coach - hears both sides, can speak to agent only. Coach audio is NOT in the conference recording. + +Recordings: +- Record every call for QA: `` for dual-channel (agent on one channel, caller on other) +- `` verb is NOT for recording calls - it's voicemail-style. This is the #1 mistake developers make. +- For mid-call control (pause during credit card), use the Recordings REST API + +Relevant bundled skills: `twilio-taskrouter-routing`, `twilio-conference-calls`, `twilio-call-recordings` + +### Level 4: Intelligent Contact Center +Developer says: "I want AI analytics, real-time coaching, and customer context for my agents." +-> Hand off to `twilio-agent-augmentation-architect` for the intelligence layer. This Planner skill provides the contact center foundation that augmentation builds on. + +## Step 4: Qualify Context + +### Existing Infrastructure +- Greenfield (building from scratch): Start with Studio (self-service) + TaskRouter (routing) + Conference (transfers). Add Flex if browser-based desktop needed. +- Existing phone system / PBX: Consider Elastic SIP Trunking to connect existing infrastructure to Twilio. Or migrate incrementally - route overflow to Twilio first. +- Existing Flex deployment: Focus on what to add (TaskRouter workflows, Conference patterns, recordings) rather than rebuilding. + +### CRM Integration +- Salesforce: Flex has native Salesforce connector. Alternatively, use Studio + Twilio Functions to push/pull data. +- HubSpot: Webhook-based integration via Functions. No native connector. +- Zendesk: Flex plugin available. Ticket creation on call completion. +- ServiceNow: REST API integration via Functions. Common in enterprise. +- 3-5 questions determine integration success - qualify the CRM early. + +### Regulatory & Compliance Context +- TCPA: Quiet hours (8am-9pm recipient local time). Prior express consent required for autodialed/prerecorded calls. Applies to outbound contact center campaigns. +- PCI DSS: Never record credit card numbers. Use `` verb for payment. If recording during payment, pause recording with Recordings REST API. PCI Mode is IRREVERSIBLE and account-wide - create a separate sub-account if needed. +- HIPAA: Requires BAA with Twilio. Recording encryption mandatory. Transcript access restrictions. API key rotation. PHI in IVR prompts must be minimized. +- FDCPA / Regulation F (Debt Collection): Max 7 call attempts per debt per 7-day rolling window. Mini-Miranda disclosure required on every communication. Voicemail must include disclosure or use limited-content message. SMS requires separate consent from voice consent. Developer must track all this - Twilio does not enforce. +- GDPR: EU call recording requires explicit consent or legitimate interest basis. Right to deletion applies to recordings and transcripts. +- SHAKEN/STIR: Three attestation levels (A/B/C). Only A produces green checkmark on caller ID. Affects answer rates for outbound. E.164 formatting required. + +### Tech Stack Considerations +- Existing CCaaS (Genesys, Five9, NICE): Webhook-based integration. Consider incremental migration - handle overflow or specific queues via Twilio first. +- SIP Infrastructure: Elastic SIP Trunking for PBX interconnect. TLS and SRTP configuration. E.164 dialplan requirements. +- Serverless constraints: Twilio Functions: 30 concurrent executions/service, 10-second timeout, 256 MB memory. Status callbacks multiply load (50 concurrent calls × 6 callbacks = 300 invocations). Use thin-receiver pattern or external compute for high-volume. +- Multi-region: Twilio processes calls in closest region by default. Use `TWILIO_EDGE` for explicit region control. Configure `voiceFallbackUrl` and `smsFallbackUrl` on phone numbers for HA. + +### Scale & Architecture +- < 10 agents: TaskRouter with simple workflow, single queue. No Flex needed - agents can use phone. +- 10-50 agents: TaskRouter with skills-based routing, multiple queues. Flex recommended for desktop. +- 50+ agents: Full Flex deployment, multi-skill workflows, real-time queue monitoring, supervisor tools. Consider `twilio-agent-augmentation-architect` for intelligence layer. +- Status callback resilience at scale: Use `{CallSid}-{CallStatus}` composite key for idempotent processing. Implement thin-receiver pattern - receive -> queue -> 200 OK immediately -> async processing. Thundering herd: timeouts trigger retries, doubling/tripling callback volume. + +## Decision Rules + +### Studio vs Functions vs Custom Code +- Use Studio when: Non-developers need to modify IVR flows. Multi-step branching logic with conditional routing. Rapid prototyping or frequent flow changes. You want visual debugging and versioning. SEs recommend this for most IVR use cases. +- Use Functions when: You need tight programmatic control over every call state transition. Heavy external API integration mid-flow (CRM lookups, payment processing). Sub-second latency requirements where Studio's orchestration overhead matters. Your team is developer-heavy and prefers code over visual tools. +- Use TaskRouter (not custom code) for routing: Skills-based matching, queue management, reservation lifecycle. Always use for multi-agent setups. Common mistake: developers reinvent TaskRouter in Node.js - don't. +- Functions scaling constraint: 30 concurrent executions per service, 10-second timeout. At 50+ simultaneous calls with status callbacks (6 per call = 300 invocations), you exceed the limit. Use the thin-receiver pattern: receive callback -> write to queue -> return 200 immediately -> process asynchronously. + +### Conference Patterns +- Every multi-agent call should use Conference, not direct Dial +- Warm transfer: Put caller on hold in Conference -> dial new agent into same Conference -> brief -> drop original agent +- Gotcha: Conference requires ≥2 participants to exist. API state can be misleading for single-participant conferences. +- Gotcha: Coach audio is NOT captured in conference recordings. Record separately if needed. + +### TaskRouter Gotchas +- Hyphens in worker attribute names break expressions silently +- `HAS` operator on non-array attributes silently matches nothing (no error - tasks sit in queue forever) +- Reservation timeout -> worker moves to offline Activity -> fewer available workers -> deeper backlog -> positive feedback loop (cascade failure) +- Activity `available` flag updates return 200 OK but may not change the value + +## Output Format + +After qualifying the developer, recommend: + +``` +Recommended Architecture: [Brief plain-language description of the recommended approach - e.g., "Omnichannel support with Flex, SMS and WhatsApp channels, and Task Router for skill-based routing."] + +Reference Skills: +- twilio-voice-twiml (always for voice support) +- twilio-voice-outbound-calls (if outbound calling needed) +- twilio-sms-send-message (if SMS support channel) +- twilio-messaging-webhooks (if inbound SMS) +- twilio-email-send (if email channel with Twilio Account SID + Auth Token) or twilio-sendgrid-email-send (if email channel with SendGrid API key) +- twilio-conversations-classic-api (if omnichannel threading) +- twilio-taskrouter-routing (if multi-agent routing needed) +- twilio-conference-calls (if transfers/coaching needed) +- twilio-call-recordings (if recording needed) + +Cross-reference Planner Skills: +- twilio-ai-agent-architect (if AI self-service layer needed) +- twilio-agent-augmentation-architect (if intelligent contact center needed) + +Setup Skills: +- twilio-account-setup - if developer needs help with credentials or account structure +- twilio-iam-auth-setup - if developer asks about API key scoping or security +- twilio-numbers-senders - number type selection affects throughput and compliance timelines; use when choosing between local, toll-free, or short code +- twilio-webhook-architecture - if developer needs help designing or securing webhook endpoints + +Guardrail Skills: +- twilio-security-hardening (always) +- twilio-reliability-patterns (especially for high-volume - 429 backoff) +- twilio-debugging-observability (Voice Insights for call quality) +``` diff --git a/plugins/twilio/skills/twilio-customer-support-architect/assets/icon-large.png b/plugins/twilio/skills/twilio-customer-support-architect/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-customer-support-architect/assets/icon-small.png b/plugins/twilio/skills/twilio-customer-support-architect/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Debug Twilio integrations and set up production observability. Covers the + Console Debugger, Monitor Alerts API, Event Streams for error log streaming, + status callback tracking, common error codes, and a systematic debugging + workflow. Use this skill whenever a Twilio integration produces errors, + messages fail to deliver, calls drop unexpectedly, or you need to set up + monitoring for a production deployment. +--- + +## Overview + +Twilio provides several layers of debugging and observability: the Console Debugger for interactive troubleshooting, the Monitor REST API for programmatic alert queries, Event Streams for real-time error streaming, and status callbacks for per-resource delivery tracking. This skill covers the systematic approach to diagnosing issues and setting up production monitoring. + + +--- + +## Prerequisites + +- Twilio account with `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN` -- see `twilio-iam-auth-setup` +- SDK: `pip install twilio requests` / `npm install twilio` +- For Event Streams: a publicly accessible HTTPS endpoint or AWS Kinesis stream + +--- + +## Quickstart + +Check for recent errors on your account using the Monitor Alerts API. + +Python +```python +import os +from twilio.rest import Client + +client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"]) + +alerts = client.monitor.alerts.list(log_level="error", limit=10) +for alert in alerts: + print(f"{alert.date_created}: [{alert.error_code}] {alert.alert_text}") +``` + +Node.js +```node +const twilio = require("twilio"); +const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); + +const alerts = await client.monitor.alerts.list({ logLevel: "error", limit: 10 }); +alerts.forEach(a => { + console.log(`${a.dateCreated}: [${a.errorCode}] ${a.alertText}`); +}); +``` + +--- + +## Key Patterns + +### 1. Systematic Debugging Workflow + +When something fails, work through these layers in order: + +``` +1. Check status callbacks FIRST + (Did your endpoint receive delivery/call status? What error code?) + | +2. Check the resource directly via REST API + (GET /Messages/{sid} or /Calls/{sid} - current state + error_code) + | +3. Check number reputation / sender registration + (Is the number spam-flagged? Is A2P 10DLC registered? Toll-free verified?) + | +4. Check the Console Debugger for webhook/TwiML errors + (Console > Monitor > Errors - shows HTTP request/response details) + | +5. Check your webhook endpoint + (Is it reachable? Responding within 15s? Returning valid TwiML/200?) + | +6. Query Monitor Alerts API or Event Streams + (For patterns across many messages/calls, or historical analysis) +``` + +Why status callbacks first: Status callbacks tell you the exact error code for the specific message or call that failed. The Console Debugger aggregates errors across your account and may not surface the one you're looking for. Start specific, then broaden. + +Number reputation checklist: +- SMS 30007 (carrier filtering) -> Check A2P 10DLC registration status, content for spam triggers +- SMS 30034 -> Sender not registered for A2P 10DLC - register brand + campaign +- Calls going to voicemail / "Spam Likely" -> Check STIR/SHAKEN attestation, Voice Integrity status (see `twilio-numbers-senders`) +- Toll-free SMS blocked -> Check toll-free verification status + +Rule of thumb: If status callbacks show `delivered` but the user says they didn't receive it, the issue is on the carrier/device side (not Twilio). If the Console Debugger shows no errors at all, the problem is in your application (webhook, TwiML, business logic). + +### 2. Console Debugger + +The [Console Debugger](https://console.twilio.com/us1/monitor/logs/debugger) shows errors and warnings for your account in real time. + +Each entry includes: +- The exact error or warning that occurred +- Potential causes and suggested solutions +- The full HTTP request and response for the associated webhook + +Configure a Debugger webhook for real-time alerting: + +Console > Monitor > Logs > Debugger > (gear icon) > set Callback URL + +Debugger webhook POST parameters: + +| Parameter | Description | +|---|---| +| `Sid` | Debugger event identifier | +| `AccountSid` | Account that generated the event | +| `Level` | `Error` or `Warning` | +| `Timestamp` | ISO 8601 time | +| `Payload` | JSON: `resource_sid`, `error_code`, `more_info`, `webhook` (full request/response) | + +Python (Flask) -- debugger webhook handler +```python +import json, os +from flask import Flask, request +from twilio.request_validator import RequestValidator + +app = Flask(__name__) +validator = RequestValidator(os.environ["TWILIO_AUTH_TOKEN"]) + +@app.route("/debugger", methods=["POST"]) +def debugger_event(): + sig = request.headers.get("X-Twilio-Signature", "") + if not validator.validate(request.url, request.form, sig): + return "Forbidden", 403 + level = request.form.get("Level") + payload = json.loads(request.form.get("Payload", "{}")) + error_code = payload.get("error_code") + resource_sid = payload.get("resource_sid") + msg = payload.get("more_info", {}).get("msg", "") + print(f"[{level}] Error {error_code} on {resource_sid}: {msg}") + return "", 204 +``` + +Node.js (Express) -- debugger webhook handler +```node +const express = require("express"); +const twilio = require("twilio"); +const app = express(); +app.use(express.urlencoded({ extended: false })); + +app.post("/debugger", (req, res) => { + const valid = twilio.validateRequest( + process.env.TWILIO_AUTH_TOKEN, + req.headers["x-twilio-signature"], + `https://${req.headers.host}${req.originalUrl}`, + req.body + ); + if (!valid) return res.status(403).send("Forbidden"); + const payload = JSON.parse(req.body.Payload || "{}"); + const { error_code, resource_sid } = payload; + const msg = payload.more_info?.msg || ""; + console.log(`[${req.body.Level}] Error ${error_code} on ${resource_sid}: ${msg}`); + res.sendStatus(204); +}); +``` + +### 3. Monitor Alerts API + +The Monitor REST API (`monitor.twilio.com/v1/Alerts`) provides programmatic access to error and warning logs. Individual alert instances include the full HTTP request and response data. + +Python -- query alerts with date filtering +```python +from datetime import datetime, timedelta + +# Alerts from the last 24 hours +start = datetime.utcnow() - timedelta(days=1) +alerts = client.monitor.alerts.list( + start_date=start, + log_level="error", + limit=50 +) + +for alert in alerts: + print(f"{alert.date_created} [{alert.error_code}]") + # Fetch full details including HTTP request/response + detail = client.monitor.alerts(alert.sid).fetch() + print(f" Request URL: {detail.request_url}") + print(f" Response body: {detail.response_body}") +``` + +Node.js -- query alerts with date filtering +```node +const startDate = new Date(Date.now() - 24 * 60 * 60 * 1000); +const alerts = await client.monitor.alerts.list({ + startDate, + logLevel: "error", + limit: 50, +}); + +for (const alert of alerts) { + console.log(`${alert.dateCreated} [${alert.errorCode}]`); + const detail = await client.monitor.alerts(alert.sid).fetch(); + console.log(` Request URL: ${detail.requestUrl}`); + console.log(` Response body: ${detail.responseBody}`); +} +``` + +Retention: Enterprise accounts: 13 months. Free accounts: 30 days. + +### 4. Monitor Events API + +The Events resource (`monitor.twilio.com/v1/Events`) tracks all changes to Twilio resources -- phone number provisioning, account settings, recording access, API key creation, and more. + +Python -- audit recent account changes +```python +events = client.monitor.events.list(limit=20) +for event in events: + print(f"{event.event_date}: {event.event_type}") + print(f" Resource: {event.resource_type} ({event.resource_sid})") + print(f" Actor: {event.actor_type} ({event.actor_sid}) from {event.source_ip_address}") +``` + +Each event captures: event type, resource, actor (who triggered it), source (API / Console / Twilio admin), and IP address. + +Use cases: +- Audit who changed a phone number's webhook URL +- Track API key creation and deletion +- Detect unexpected configuration changes +- Feed events into a SIEM for security monitoring + +### 5. Event Streams for Error Log Streaming + +For production monitoring, stream errors to your infrastructure in real time using Event Streams. The Twilio SDK does not wrap Event Streams -- use `requests` / `fetch` directly. + +Python -- set up error log streaming to a webhook +```python +import os, requests + +account_sid = os.environ["TWILIO_ACCOUNT_SID"] +auth_token = os.environ["TWILIO_AUTH_TOKEN"] + +# Step 1: Create a webhook sink +sink = requests.post( + "https://events.twilio.com/v1/Sinks", + auth=(account_sid, auth_token), + data={ + "Description": "Error monitoring sink", + "SinkType": "webhook", + "SinkConfiguration": '{"destination": "https://yourapp.com/twilio-errors", "method": "POST"}' + } +).json() + +# Step 2: Subscribe to error log events +subscription = requests.post( + "https://events.twilio.com/v1/Subscriptions", + auth=(account_sid, auth_token), + data={ + "Description": "Error log subscription", + "SinkSid": sink["sid"], + "Types": '[{"type": "com.twilio.error-logs.error.logged"}]' + } +).json() + +print(f"Sink: {sink['sid']}, Subscription: {subscription['sid']}") +``` + +Sink types: `webhook`, `kinesis`, `segment` + +Useful event types for observability: + +| Event type | Description | +|---|---| +| `com.twilio.error-logs.error.logged` | All errors and warnings on account | +| `com.twilio.messaging.message.delivered` | Message delivered successfully | +| `com.twilio.messaging.message.undelivered` | Message delivery failed | +| `com.twilio.voice.insights.call-summary` | Post-call quality and status summary | + +### 6. Status Callback Monitoring + +Status callbacks are the most granular observability mechanism -- they fire for individual resource state changes. + +Message delivery tracking: +```python +# Attach when sending +message = client.messages.create( + to="+15558675310", from_="+15017122661", body="Hello!", + status_callback="https://yourapp.com/msg-status" +) +``` + +Call lifecycle tracking: +```python +call = client.calls.create( + to="+15558675310", from_="+15017122661", + url="https://yourapp.com/voice", + status_callback="https://yourapp.com/call-status", + status_callback_event=["initiated", "ringing", "answered", "completed"] +) +``` + +Recording completion tracking: +```python +# In TwiML +response = VoiceResponse() +response.record( + recording_status_callback="https://yourapp.com/recording-status", + recording_status_callback_event="completed absent failed" +) +``` + +Recording status values: `in-progress`, `completed`, `absent`, `failed`. The `RecordingUrl` is available when status is `completed`. + +### 7. Debugging Webhooks + +When Twilio can't reach your webhook or receives an error, the problem is often in your infrastructure. + +Common causes and fixes: + +| Symptom | Likely cause | Fix | +|---|---|---| +| Error 11200 in Debugger | Webhook URL returned non-200 / unreachable | Verify endpoint is live: `curl -I https://yourapp.com/sms` | +| Error 11205 | HTTP connection failure (port closed, refused, firewall) | Verify server is running and port is open: `curl -I https://yourapp.com/sms` | +| Error 12100 | TwiML document could not be parsed | Check for debug output, BOM characters, or malformed XML | +| Parameters missing after redirect | HTTP 301/302 strips POST body | Fix URL to avoid redirect (add/remove `www.`, use HTTPS directly) | +| Webhook works locally but not deployed | Tunnel expired or firewall | Use `curl` from an external host to test | +| Intermittent failures | ngrok session expired / recycled | Deploy to a stable host for anything beyond quick tests | + +Test webhooks manually: +```bash +# Simulate an inbound SMS webhook +curl -X POST https://yourapp.com/sms \ + -d "From=+15551234567" \ + -d "To=+15559876543" \ + -d "Body=Test message" \ + -d "MessageSid=SM00000000000000000000000000000000" +``` + +Browser testing: Visit your webhook URL in Firefox -- it highlights XML errors in the response. + +### 8. Common Error Codes + +| Code | Name | Cause | Fix | +|---|---|---|---| +| 11200 | HTTP retrieval failure | Twilio cannot reach your webhook URL | Check URL, DNS, firewall, SSL cert | +| 11205 | HTTP connection failure | Webhook endpoint refused connection | Verify server is running and port is open | +| 11751 | Media download failure | MMS media URL unreachable | Check media URL accessibility | +| 12100 | Document parse failure | TwiML is not valid XML | Validate XML; remove debug output | +| 12200 | Schema compliance failure | TwiML verbs/attributes are invalid | Check TwiML reference for correct syntax | +| 20003 | Authentication error | Invalid Account SID or Auth Token | Verify credentials in environment | +| 21211 | Invalid `To` number | Number not in E.164 format | Use `+` country code + number | +| 21608 | Unverified number (trial) | Trial accounts can only send to verified numbers | Verify number or upgrade account | +| 30003 | Unreachable destination | Carrier cannot deliver message | Check number validity; retry later | +| 30006 | Landline or unreachable | Destination is a landline | Use voice channel instead | +| 30007 | Carrier filtering | Message filtered by carrier | Review content; register for A2P 10DLC | +| 30008 | Unknown error | Carrier returned generic error | Retry; contact support if persistent | + +Full error reference: https://www.twilio.com/docs/api/errors + +### 9. Querying Resource State Directly + +When you need the current state of a message or call (not waiting for a callback): + +Python +```python +# Check message delivery status +message = client.messages("SMxxxxxxxxxx").fetch() +print(f"Status: {message.status}, Error: {message.error_code}") + +# Check call status +call = client.calls("CAxxxxxxxxxx").fetch() +print(f"Status: {call.status}, Duration: {call.duration}") +``` + +Node.js +```node +const message = await client.messages("SMxxxxxxxxxx").fetch(); +console.log(`Status: ${message.status}, Error: ${message.errorCode}`); + +const call = await client.calls("CAxxxxxxxxxx").fetch(); +console.log(`Status: ${call.status}, Duration: ${call.duration}`); +``` + +### 10. CLI Debugging + +The Twilio CLI supports debug logging: + +```bash +# Verbose output for any CLI command +twilio api:core:messages:list --limit 5 -l debug + +# Log levels: debug, info, warn, error +``` + +Debug output goes to stderr, so you can pipe normal output while still seeing diagnostics. + +--- + +## Monitoring Checklist + +Set up before going to production: + +| What to monitor | How | Alert threshold | +|---|---|---| +| Webhook errors | Debugger webhook or Event Streams (`com.twilio.error-logs.error.logged`) | Any error | +| Message delivery failures | Status callback `failed`/`undelivered` | > 2% failure rate | +| Call completion rate | Status callback `completed` vs total | < 95% completion | +| Webhook response time | Your APM (DataDog, New Relic) | p95 > 5 seconds | +| 429 rate limit hits | Count in your backoff handler | > 5% of requests | +| Account configuration changes | Monitor Events API | Any unexpected change | +| Recording failures | Recording status callback `failed`/`absent` | Any failure | + +--- + +## CANNOT + +- Cannot fetch more than 10,000 alerts per request - Use date range filters for large accounts +- Cannot get full HTTP request/response from alert list - Only available when fetching a single alert by SID +- Cannot combine multiple filters on Events API - One additional field (ResourceSid, ActorSid, SourceIpAddress) plus date range per request +- Cannot delete an Event Streams sink before its subscription - Must delete the subscription first +- Cannot guarantee status callback delivery or order - Best-effort. Use composite keys for idempotency. +- Cannot rely on a static error code list - New error codes are added without notice. Always link to the full reference rather than hardcoding. + +--- + +## Next Steps + +- Webhook architecture: `twilio-webhook-architecture` +- Scale webhook handling: `twilio-reliability-patterns` +- Compliance monitoring: `twilio-compliance-traffic` +- Credential security: `twilio-iam-auth-setup` diff --git a/plugins/twilio/skills/twilio-debugging-observability/assets/icon-large.png b/plugins/twilio/skills/twilio-debugging-observability/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-debugging-observability/assets/icon-small.png b/plugins/twilio/skills/twilio-debugging-observability/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 zcmeHKg;&&3xBU$vAtfLTNOwpoEg;McDcvP7G)N2~rL=S-AvH8gNOwpJjC2Y}Nr=SI z15!`ld+YrXZ+&;&efK_R-8}2A8&R6-io}Gpga7~#D=R^@|FXtE;A8(SbAH>`0{|h1 z0|cV!VC4(|KG`{G-nzPTR1sqx-hTNbf}UW%D(!n@JM={cT439I;9;PbX#PG651AIU zh&u!amk3M@gTpBAgS0^Y&qIR9L$zLgv{sl^dE@Jg-rU4|m0nq}LwjJp`>!_OVC|Fu9rw}w_`49?>=~@1V$-Q7vxhBF%2=%osz?_XCsdw4!d+Ve zD;U9)lG?LTxAS?&f#$_BEkK37HWFT6n1sO^MVlHLMH>Z$?g}*TyiQzImKI@tG^Z_g zWR+;rMf1zzIZ#&y#f&Y6vGIZskZc4o1)@o_W|;vt*jp#Yi*=c&N9}9`I5e+IN+cvC zI7~@UP}A|1Gg9F|Kp?|K4*2IT|(OksWjt!WpAEZ)_UlV7IGfsX64L{}+P+s14vH=NJoL_pfux!>k(NcQL0 zScy2gd4O&($2j&t7uG!BzlyUIOo|WWGZ9u`Jx;`#1Q<7h&MfW^;=QvFo5$mkBmRgN zmxr4T(62si2KYmFSMPzbFx^}kz*yH~AL1=${2qDhNG$H>j)|hy_(so(>}fVgF6Fv4 zDJk*T<)10xS4K3-ztbepC76+q8*dut{)OfFkT&c<-fH~mDeozRf(7}DcX}4|^OUw- zqtya*gjHb_YcG8GvhmO%s5Qw8@Dho6P*pGFkn5T#7Xrvb?IR`Ov4UgCWBnUq3saMzk}1;#MUbM=J#km*5z`DBGUU&_mA*2FZ6hQS6R$tB&?g{Nl?5XYHob$y;>hv0|K69H8XW*wc2zTnq z>HpEEw!U?w?c>}|F9VDXRCQDT+&wb80A1q*&`tN#9AcNmbzw6_D2~iYd4oW*Z8} zuw&L^6N2clsIVp+6L<8*B#ExRqP$5woRaz_dEHW+5(0){0 zR-~>Rr!=ChQ#4YHT{Kk`QF zi{m4>jDIF8^95f~0A#;(ba;5scsLyOIju5TnpH{#VlVmB)tIL;<8_GW3x9!9mhH!1 zlNPz>SiE%(;@XqjQ=TbyKJU~EP`d5FGV3;DhFK=aNAy5o?+b$AiSYP!xqj8?Cn~z^ z{_JVm!IK2V{=?O^!S2<^lAFE5Z-+g2%6M{lXm}p-Q0v+1I+UrG4V4v_+3A(ea5sINZ4Ulu=Gx$@3Gxjq+AC$a)KTthLp*v?CfkeysW}6C+MMdEIus(-EW|Ri1$x`D;nP zCiaAO9X4A=Pui&$P;*WRbCFHaVyLzW|JkntcA**x0z+bbO_P`!WH94O6^ij3bF{L1 zXVG%*zI+@Hkgd{Ji>+Gbxmeh)V&pRNy7J2MIJ>vHcSHRlT+B7hYRpv-Gx-<@I%4M? zL2qj>d#_c`quv#0GweMyRv}ZtC9<8N7~CKCJ9a=JLQxDlR_SJ5K`N~(cVAX@2B7}b5RX1`6qA4SQ|z_ts-=B9x`9iT?c7=2q|0;C>tV>oaN-YR09w?<0%S?OzdN6XZL|4eUStZ!nGNldAVs z%=cq{csG2jt+17~{Ni?J-(v1-_fEh6AeuB)ITfPEuU9!UJR?=ULTAC)>^r_=p&F;6 zW;t>NS%P>wN;@s?dj1|Ju_Cc!4A5qLxpF1)b9dlge|S%Lf})S2Lc%@9eTRC7SFf~P zcwEA>-i=Isg~$K67}(DmrTUq+pl|H7hHx|Wx!!3)tdJlNr6=AdBWgFL_R(dFT+X=r znpc_U-{XtoCq_SxcI(KLwzloHEp&=q-VQpPP3hTLk5MAovqz-b++6lsT25%T?fBVV zg3-)KRxwopF>7Px{o8Q#TwP2PWM(~zaEtpAr#UUzxww|4x%anIEGFAlG z?=~H{GFH?4YPa++baLBS{hk#sCa~n*=5^VK+24hrBJM@>hNENqVq=#rS3b06`C3CoPvc5?bJd@rd_-ka?&?m*X2#BP12HjE0&nY*>L>&*1gHY|{5~BX5Avc6 zes~0XB)lx`$PLsv)L+}5@Q(HFK(j7ujBdZ_3gEi?cvrKVf-c3o?{NSA7Z4`w-O`j(wLyRB&{JDo2LN#C<($)7r?-wT|7_2; zXCh#X_wEaSoH@L{IhwB!A;M;T)v|Yc(BaO7leoMtg(sNtdp}Q%Hh5zvnn`VNT>oMC zNfJ+e=ac6X-&H~CtHa3wZ_74KPGfbr$w)agBKs{B$c?3cyklEZ{+7d!^&Tx$K862+ z+P{*y;3A$RK_$Suf|oiOw&td>qMP2%axqEt>kNdfSv-?@3|f@TbH^DlAh zhQ}-U6kqLXM5_$fc(om-!p^afAzmw|C9c8|Ihkw zb{%4E1%P|U%1}97!`>Xj9SDUY zX%hg7^gBDSnrlLKgci;Y$?fe;u6PAy;RN|5;0lg+j4_dF2=H|lE!TE`mT}KwmRpnB zz?LnZ-Jq5HCd)KV1tq)0!BuV8N*C@s=+(@R%md57i)I&SMf_s(o?wN=5jRDbm^?_* zn%&1Q_=QlBb33JFw1PPV5it4^!V2g5<^@$1aU!n_9kJ>5Gn9|EKp+mz`LGg`C>>o} zKu*LfQ|L>I&!1Hq<=flaCsuZGs;{5gRwFBcDL0|>H_PvLJhndyw9rjd_cgzy+VMKF#XXV}^xw@s#Kw7AXgUq0IL8Xv!d8 z32U~;CZ9#O#Wis^wjL~vB98!OKBNU#BzHaKCnjS#mzAbS3%oOxc7ESVLcl55+;lzc zt7Q@XVO0lfbwc@^wegrImzhcUkk9B>bzqZ!zBGE+D}+$-*Pp12>2Mx({5;yJS%g)7 z_3*s{wF?38cZG`r!#gbxhIS$%?aF2FR zuD;_6JWzI%&9fFW9nP25=&rD{`0drzJ27&Zye1|}OaDDFFeyozMKrV`?;VIXRA_-2 z0)$%Q6tF7j*<;jfoUSpQ$O*Li6E5$EpLzDLVK@`v&BiB8hBORW!@ZPX<}-9NIm&Nb!`x zxhF*xjplgMrmGWpL4XkAG#bKKzsB1=F2h*X0454I-UjkZ*|RQbz4Y1da( zDqe==`D`bO*ic&=yiFt@5D_Km^0_R!)JmDq9VMb`$L&Ff`oKE2?x5j zW<+SXv!GWP!S5gX+r$sR{HHnRT3O9o>pun&AE{Edq*NAL0-nj43_R2nkB+wEeAn*L z!NA_RDE?x4$h;Vp@W`j7@Fd$xCfHOYW?}fIQ-Z8Ka=%Hu)hhiKTgipBYgNi{CMkRC zmjRb&qMV^W&ise5FgbljTgJpCF2Q-XvwW(DqcQH{N$y(#V!(v<#)@Mrh5Ny0WT}E} z$7^)k4guy%V4^~8!F$yb&3*qtbiSkSCCoju)GogMIJA9$$StckGEeh?@pS+-h!%~P z;uA8;slqGJ;3Jc@w|&la=O{mr;Ng2--oF=d-L{e$2*LPrsr{kucVjuDz+8niTr7ra zr$ow5m_5D~+~YxA)?f?N)^0{hoCu{c8s0XVd|Pp9!lzC9f^ep5pr{hics!e~v!NS3 zlV0lcwtM)_rlI%`CYEiYt?sNUrtqv@r&z2da5U|=$Br@sUvfWcscV9PTC4h=#4&e_ zT|uDr?YBb2jTKM}{vj*4-=G#7x5l6`s~c#7Iu&aOXXOseztNB7kvC~SylvZ*=zSD) z52uEogmV5Pd6DfFcI}4`nWWZQ-=6A4*_(Ksfclv17OwDf%-{3UcN9!6ZuzfMcvo~BqKbas#zHsLb z4P7pJ)Zsd4!Z7UPs#JI@Tuk^ex5>d5#a!s3A}W0+n5y&g%_nzl2ISD`RZRSyY2>VZ zB}vQ5>EZq*Cq~pO&6BZQ(AIu2U(m{4^TsT%!OINkcy0z0YiO_Df2+2*Z*|%InRRmU zUOlB+YC{*b^x%VQ8Kde!zmlmBx^@Eim8HAIc~Ml9M`*u_g3X&LmJ4E9sFnMe;iNIm z?SXl4)zdwita7vPOu3W20RPz%3?h_%Q)cE;7*9ChJ%tJScFwS@_F-J``({4Rx88E1 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Deliverability advisor for the Twilio Email API specifically. Use ONLY when + the developer explicitly mentions Twilio Email, comms.twilio.com, or a + Twilio (non-SendGrid) email program. For all other deliverability questions + - including generic ones - use twilio-sendgrid-deliverability-advisor. +tier: discover +--- + +## Role + +You are an Email Deliverability Advisor for the Twilio Email API. This skill is a work in progress - Twilio Email deliverability tooling is more limited than SendGrid's. Apply general email best practices and flag where SendGrid-specific guidance does not apply. + +## When This Skill Activates + +Use when a developer is on the Twilio Email API (`comms.twilio.com`) and asks about: +- Emails going to spam, not reaching inbox, or getting blocked +- Bounce rates, spam complaints, domain authentication +- How to improve deliverability + +Do NOT use for SendGrid - use `twilio-sendgrid-deliverability-advisor` instead. + +--- + +## Step 0: Identify Platform + +Check for platform signals before proceeding: + +| Signal | Platform | Action | +|--------|----------|--------| +| Mentions `comms.twilio.com`, Account SID, or Auth Token | Twilio Email | Proceed | +| API key starts with `SG.` | SendGrid | Redirect | +| Mentions `app.sendgrid.com` | SendGrid | Redirect | +| No signal | Unknown | Ask | + +If SendGrid: Stop. Respond: "For SendGrid deliverability, use the `twilio-sendgrid-deliverability-advisor` skill - it has SendGrid-specific tooling like SEQ scores, IP warmup schedules, and blocklist guidance." + +If unclear: Ask exactly this before proceeding: +> "Are you using Twilio Email (Twilio Account SID / Auth Token, endpoint at comms.twilio.com) or SendGrid (API key starting with `SG.`, dashboard at app.sendgrid.com)?" + +--- + +## Known Constraints + +Twilio Email does not expose the same deliverability tooling as SendGrid: +- No Engagement Quality Score (SEQ) +- No IP pool management UI +- No Email Address Validation API (requires a separate SendGrid account) +- Dedicated IP is not available on the standard Twilio Email API - contact Twilio Sales for enterprise options + +--- + +## Foundation Checklist (applies to all email programs) + +### Authentication (do these first) + +| Protocol | What it does | Required? | +|----------|-------------|----------| +| SPF | Authorizes sending servers for your domain | Yes | +| DKIM | Cryptographic signature proving message integrity | Yes | +| DMARC | Policy for SPF/DKIM failures (none/quarantine/reject) | Required for >5,000 msgs/day (Gmail, Yahoo, Microsoft, Apple); >1,000/day for Orange | + +Configure domain authentication via the Twilio Console. SPF and DKIM are required at all volumes. DMARC thresholds vary by provider - see table above. + +### List Hygiene + +- Never buy email lists +- Use double opt-in for marketing lists +- Remove hard bounces immediately after each send +- Reconfirm subscribers inactive > 6 months + +### Thresholds + +| Metric | Healthy | Warning | Critical | +|--------|---------|---------|----------| +| Hard bounce rate | < 1% | 1-2% | > 2% | +| Spam complaint rate | < 0.08% | 0.08-0.1% | > 0.1% | + +--- + +## Platform Limitations and Where to Get Help + +The Twilio Email API has less built-in deliverability tooling than SendGrid. When you hit these limits, use the resources below: + +| Question | Where to go | +|----------|-------------| +| What delivery stats are available? | Twilio Console -> Monitor -> Logs, or configure Event Webhooks via Console | +| Bounce and spam complaint data? | Event Webhooks are the primary signal; the Console provides basic send stats. For detailed per-message events, contact [Twilio Support](https://help.twilio.com/) to confirm current webhook event types | +| New domain warmup requirements? | No platform-enforced warmup schedule (unlike SendGrid's 41-day automated warmup). Follow manual warmup best practices in the Foundation Checklist above | +| Dedicated IP availability? | Not available on standard plans - contact Twilio Sales for enterprise options | +| Which delivery events are exposed? | Contact [Twilio Support](https://help.twilio.com/) for current webhook event schema; standard email events (delivered, bounced, failed) are typically available | + +When in doubt: Open a ticket at [help.twilio.com](https://help.twilio.com/) - deliverability questions on the Twilio Email API require platform-specific support that this skill cannot fully replace. + +--- + +## Output Format + +After diagnosing, respond with: + +``` +Diagnosis: [Acute / Gradual / Proactive] +Root Cause: [Most likely issue based on symptoms] + +Immediate Actions: +1. [Highest priority fix] +2. [Second fix] +3. [Third fix] + +Skills to Install: +- twilio-email-send - if developer needs help sending email via Twilio (Account SID / Auth Token) +- twilio-sendgrid-deliverability-advisor - if you discover the sender is using SendGrid instead +``` diff --git a/plugins/twilio/skills/twilio-email-deliverability-advisor/assets/icon-large.png b/plugins/twilio/skills/twilio-email-deliverability-advisor/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-email-deliverability-advisor/assets/icon-small.png b/plugins/twilio/skills/twilio-email-deliverability-advisor/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Use when the caller has Twilio credentials (Account SID + Auth Token or + API Key SID + Secret) and needs to send email via comms.twilio.com/v1/Emails. + This is Twilio-native email - NOT SendGrid. Do NOT use if the caller has a + SendGrid API key (SG.-prefix) - use twilio-sendgrid-email-send instead. + Covers single sends, batch sends up to 10,000 recipients, Liquid + personalization, operation tracking, and error handling. +--- + +## Overview + +> Agent safety: Always confirm recipients, subject, and content with the user before sending. Email is irreversible once delivered. Never send email autonomously without explicit user approval - especially for batch sends to multiple recipients. + +Twilio Email is a separate product from SendGrid. Both send email, but they use different APIs, credentials, templating languages, and endpoints. If you have a SendGrid API key (`SG.`-prefix), use `twilio-sendgrid-email-send` instead. + +| | Twilio Email (this skill) | SendGrid | +|---|---|---| +| Base URL | `https://comms.twilio.com/v1/emails` | `https://api.sendgrid.com/v3/mail/send` | +| Auth | Twilio Account SID + Auth Token (or API Key SID + Secret) | SendGrid API key (`SG.`-prefix) | +| Templating | Liquid (`{{variable}}`) | Handlebars (`{{variable}}`) | +| Max recipients/request | 10,000 | 1,000 | +| Max message size | 10MB (including attachments) | 30MB | +| Status tracking | Operation resource (poll `operationLocation`) | Event Webhooks (async POST) | +| Console | console.twilio.com | app.sendgrid.com | + +--- + +## Prerequisites + +- A Twilio account - see `twilio-account-setup` for signup and credentials +- A Verified Sender: an approved domain identity configured in the Twilio console that must match the `from` address domain +- Compliance with regional anti-spam regulations (CAN-SPAM, GDPR) + +For a complete setup guide, see the Email Onboarding guide in the Twilio console. + +--- + +## Authentication + +The API uses Basic Authentication with either: +- Account SID + Auth Token +- API Key SID + API Key Secret + +These are standard Twilio credentials - the same ones used for SMS, Voice, and other Twilio APIs. + +--- + +## Send a Simple Email + +`POST https://comms.twilio.com/v1/Emails` + +The endpoint is asynchronous - it returns `202 Accepted` with an `operationId`, not a delivery confirmation. + +```bash +curl -X POST "https://comms.twilio.com/v1/Emails" \ + --header "Content-Type: application/json" \ + --data '{ + "from": { + "address": "support@example.com", + "name": "Support Team" + }, + "to": [ + { + "address": "john.doe@example.com", + "name": "John Doe" + } + ], + "content": { + "subject": "Your subject line", + "html": "

Your message content in HTML format.

", + "text": "Your message content in plain text." + } + }' \ + -u $TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN +``` + +Response (`202 Accepted`): +```json +{ + "operationId": "...", + "operationLocation": "https://comms.twilio.com/v1/Emails/Operations/..." +} +``` + +Poll `operationLocation` to track delivery status. + +--- + +## Batch Sending + +Send the same message to multiple recipients in a single request by adding entries to the `to` array. Maximum 10,000 recipients per request. + +```json +{ + "from": { + "address": "support@example.com", + "name": "Support Team" + }, + "to": [ + { + "address": "john.doe@example.com", + "name": "John Doe" + }, + { + "address": "jane.smith@example.com", + "name": "Jane Smith" + } + ], + "content": { + "subject": "Your subject line", + "html": "

Your message content in HTML format.

", + "text": "Your message content in plain text." + } +} +``` + +--- + +## Liquid Personalization + +Use Liquid templating in the `content.subject`, `content.html`, and `content.text` fields. For each variable referenced (e.g. `{{firstName}}`), provide a matching key in the `variables` object for every recipient in the `to` array. + +```json +{ + "from": { + "address": "noreply@example.com", + "name": "Support Team" + }, + "to": [ + { + "address": "alice@example.com", + "name": "Alice", + "variables": {"firstName": "Alice", "orderId": "123"} + }, + { + "address": "bob@example.com", + "name": "Bob", + "variables": {"firstName": "Bob", "orderId": "456"} + } + ], + "content": { + "subject": "Hi {{firstName}}, your order update", + "html": "

Hi {{firstName}}, order #{{orderId}} has shipped.

", + "text": "Hi {{firstName}}, order #{{orderId}} has shipped." + } +} +``` + +Ensure every recipient has all referenced variables defined. + +--- + +## Operation Tracking + +After submitting a send, use the Operation resource to monitor batch status. + +1. Submit email via `POST /v1/emails` - response includes `operationId` and `operationLocation` +2. Poll status via `GET` to the `operationLocation` URI +3. The operation tracks progress for the entire batch + +This is especially important for large recipient lists where processing is not instantaneous. + +--- + +## Error Codes + +| Status Code | Description | Action | +|-------------|-------------|--------| +| 202 | Accepted | Request accepted, Operation created. Poll `operationLocation` for status. | +| 400 | Bad Request | Malformed or ambiguous request content. Check JSON payload. | +| 401 | Unauthorized | Verify Account SID and Auth Token / API Key are correct. | +| 429 | Too Many Requests | Rate limited. Back off and retry. | +| 500 | Internal Server Error | Twilio server-side issue. Retry with backoff. | +| 503 | Service Unavailable | Temporarily unavailable. Retry after a short delay. | + +Validation errors return as many issues as possible in a single response to help debug quickly. + +--- + +## CANNOT + +- Cannot use SendGrid API keys - Twilio Email uses Twilio Account SID + Auth Token or API Key SID + Secret. `SG.`-prefix keys do not work. Use `twilio-sendgrid-email-send` for SendGrid. +- Cannot send more than 10,000 recipients per request - Split into multiple requests for larger lists. +- Cannot exceed 10MB per message - Total size including attachments must be under 10MB (smaller than SendGrid's 30MB limit). +- Cannot use Unicode in the `from` field - Unicode encoding is not supported for sender addresses. +- Cannot use Handlebars templating - Twilio Email uses Liquid, not Handlebars. If you see `{{#if}}` or `{{#each}}`, that's Handlebars/SendGrid syntax. +- Cannot get synchronous delivery confirmation - The API is async. `202 Accepted` means queued, not delivered. Poll the Operation resource for status. +- Tags total length cannot exceed 10,000 bytes - Combined length of all tags on a request is limited. + +--- + +## Next Steps + +- Account setup and credentials: `twilio-account-setup` +- SendGrid email (separate product): `twilio-sendgrid-email-send` +- SMS sending: `twilio-sms-send-message` diff --git a/plugins/twilio/skills/twilio-email-send/assets/icon-large.png b/plugins/twilio/skills/twilio-email-send/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 zcmaKScT`i)^LFSEdRKu+=tvb1f`kr|NEHx4iXbhNPz6GhVnB*O=q*N?A_~$$sx+k< z1eD%;lP(}4{w|;MegA#`$jLc#_wLTl%-wmOoryIv(4hxk1A{;ydL%;27z83Q0Dk(^ zlz_yuJGvYAqwzvm_<}%^{Fgrx%xhJD5QrCq)KWLa;D5fH@t*1#j-FHY+xbj4U_#e+ zd#qVnp45$Bny0;wS)4m!gIZ6!!kC<@asF2vNrj3GnG9(d=LpU&Ck#ZnzSI84w6f?& zoKY}!^GKNN;nd0QoI1kYKXrq;t?Ycw+(vLBwtj~6qoVHwd-zQ9t52p+TT}m0hR>i{ zLKF}FU~3i?z``ya9x^=qF$)smL+xX}i^ZR-LnO58KvC&(*bXGon7M~ewQK(+LHlI| zMTI)k2z@vw_=X}?RBAIHU!YhyoqtPrt2q?&mO|uB5owff+qTe%F|RCna!8>|5-Wij zTXUr>GwOq=7A<%!9LCf~*Lf^tuMXwmWg}vf`}`^A4O)i_w+X`J&`|EOOfG8^!~-K3 ztL8!4Yopc&x1vj#w4St|qaFgXJiZKBkmvmbXz@5HhFn{YMg(t=EFD4__gY7!fSB)f z+g>4?$kePGz2H(oTA{`?WdFV(MqLEYfHOLWv^O~j!mSBR65EDAtEn8<6s$z#vMJ6y*b&`ABmMk5y`ad<3!38-CVNLVlQ8Bp3|x1^I$e|BPV4cgSU_GF4MxMr!PN zUdLGMGOdv`Qo%?q7~3&%bHFZzFhSO%v$bM1g-gr#^2gS&OQ^LA+;(Rsf0FO6lVSwp z=XdK_v$bTdY?qA;=a`HxB9D^gYwDGw;u?$92Q++Ah(>^cVQn3Z-W`y9Oqv_lA`jS^ zRv<}{D&PiR>o{rF%@9w9sA_0a?-3;bA+Z9FSs`gtp32yPdo&QH;g5l5zCZ#5oN{15$ zUTYX+HG&zorK>v3OP>T%g4dXV*yD*rRXsL{CO*J^2@HD0E`n$OXqAF1EM;moggArR2h5F~Fw+Nn6rSyB-PDGO4taQN8QDB22FgC0>2P{@@28(Orcxb&$}DBx0(^AbR% zM01jlTt0(qwoxVr*%a%JEW=pkNDN>Ulyr}g^+C2&9*Si+jSKXT%XQY*tHYw#*@qUU zF)7>Ayq2fo_konJNY)Hgm46h>@MS5@BDnyGP)L)|t)GlOQa^wQ$E#tF`XKFxdW{w; zE=j4t)7@!a3?X1L`U$eCqqk2H>N*WNRa%J)PRS2){4Joc@y#(wI*?@H5EP!!3eJsRbn+V0F4j%eB?RSFD-G>D2YQ=glHQ5B+Lv zX5F}$Q;V=Bi5sEh+SR&dr%;@f8F`f?-&=cf^%jcHHExhQTPu1~G3EmN_!Pt6Z_qlC zrA+WsQnh5CR|LK0tC6^ap0!f}egcwOSX=gnCGgd0EoubPFb^w{-*imA$$*S04qhli z-N`??!C7)LcS}3bxR(i=Hj7f4jxTmwNVBQ@RQuDbk7EIY`;4ona*I5onv39@q#Rq^ zFoRhrdv-xoyHW^_>U*C(i^ebxaKP|x?D2`*p=y!6Kt!av}+JKh`_VIgdDhv&L9uy@5Yqqb} z1+8L>xfYhAc{nYgm`qwnr5sAQyjGhw2Vdw9Y@&lpVI5y1>PsQplUh*^a?K5foitC@YG0R|;CW z+Sc;iK{56@o2IX{HpLaRiHtD5G>?qh?sv;)uIQuZTGqbOA^*y6t+g*^Yx>ez%Dn=; z5Y2I@v1xrV+E3RejO~5$mTl1}yFJZoM`$FNu3p)maXx7%D6rc~sDzKA-4gr#D^r8| z`1MgbCyieboX-fL0g~= zd({0CcH!U1r)zswC2Bnq+%s$WRt3j2b58u>kVRLtIBVH6Til)ftNB6Y=YQgf!ZJA2b56c|V7@+AajMimgT zCSVOwqLZbO9Fk8!obZw(uJH)A#$#g{uiU|)!=|4?$44*(R#J10DlowMDrC<>Tln=G z_+p9%=v*WR2bw;j9zdMqW>;SnAdO#F!F}Jm-*s2%G2VTv#YvoTxUCr^hE?$%^G~_W zM+iW8>NdiUDGKZO1t*^#@hb9aP(~C@UX0f9%iJ5Q>^_F=CTE~Nh~zk+5Kn#~rxJU# z^snDJgs~pUpMbvDh^-szH>ZyQ$=@EN6qw%38}d{NWYc`n)TH)stIxZc!swXFj5bf~ z{K_Eb@7D08O1%nom4_xFdJ6+2=jzdE%{A-4cl{&v&Q8Ad z4yuBB%4pwCyUo@7jL~8%udekHT19)1Og|(dk)M4T{$`X5gHA@TpZWF>trd@Utq)0a z%v-B#$F3H>BNz*YI^CBGNZr6FWOGoX`6df!K)WR8ue7*6jO)Ld@Q?{5*`tu(nPCLk zX!Vr&Bt6Mlaz;xrbuLY}fB8C=F<51ouP9(z)jaYYY+z=lh!9ief3b3B3a2SB_dBVM zCmHo)Y~n7AwJ)?3IGxMtn4crAIWSy@4nv?%9*InOvpKUb0;ws-vTW6Mu_0q^C@A|@ zsPB}KvghxoVgohwu%d&!U?zQDOy`^crxSQTO@0rWqjshLlSK3mOZw}It36HfO2W4| z3|yX7;@sT&o!y{s;OkO*hj|Zv+alpF>qkN=(UHnn{wZ=c$TrNbUD7>t2_jV>!lqf%A@!rEt-xjC0Y?=DL9XF>R&2 zxvsR%j`}su#}&dCw5J0uHU(o;DmTmj;yf3TxoX^uyE-1t$=ZUos73GD_J8_;o+U%p z?}~S8f3D*KA2@&FhU(<78ui*|7AwflLoO6l2tPtKSr_EeIo;d(jLY+>?|yK|-NtQQHYxX^ zAHNdzTalOv_E@$!C%>l+AG$5J7AXT|%$RbZ2|HM~UL`u&-DESnpDd%z%~n(483UW= z2j8$GIrZ6YT;F6B*5@NKrNyEg!~HP!F3;GJVaj5;L(vbl3*JYWNNy??g;dmdM`Ft~ z4{&!?o*0Sr@jn`*J^2FGZm1)Xh?T3DgF&efd zv=E!zgbUtdO(a);uhy)hiPCzW)ytb)M_{Mq4@>NBtX?0`YP23+ub*Mcx{;o;lCo&B zw1zXvmPm`DEzC+nja@b`MfqXQqic~~4-e(Lub2t`Wh+w_>}_J4@RiDT;qp?uoM845 zE4?+FK|{>ieF;D>DhvVp_&{DGImZ5~^eqO>z1!vcF#S-GTQ^tG;Osh*=$^E>S73#V z4!`z?uUvR2rSKf8@WI(&_iJ;ORdES+XN)pvvUfd|*KHoKT8G>zXC{`m@Z5lNO;0F~ z)U{th`=8yc*2OUcgA#Fo4bybL?m0(|1dcRmd!{n}8EkeL%IK;*zZ$=^PyGpqY)*r5 z{O1pLZu?j@kUg4=ZulsoUW6@Y`iiAC`9!04K3YffAx-hv`KrcLIP0F>KX$M)Ts$%M zbI#X|Vt zLT#u|j?lvE23(p|*OqTK&!2^R3u#X^lN1HYuQNbPs=vwSK_@D&6AoZW7gc1<;iOBr!>r>{;3{JS&~+~hW;a^@CH^zG;KDQFRG5@ z7G_vJkEA$iWaCcLpn*mjOOk^A12+9R5eX!Omfm`yH&v{L9@S=%G-97+gW@t`HB`Wi z)$q_JI<{Fd`sSn6$VBjcdv=U)?HD$Wn{-IfgOjw8x}xe1#ht-KSdX!!LX5ezRnlIC zQYfg&_HsNtut!aVDWU%#jExxZcXIezxtKS{B&A5T4qVD*{SRv?K59b{(2V{{=3Fz_ z`My5SJz)3bYdXbWY$26-_2Gjp5c>tcMG-(RHM6HCzhK=hxkHd7Sv2wkI-Dg~D`Z?d zyd_S@vaSkQp@a|WT{_5p=j8;9ApoKFriWftt6cU?{qG=sx{@4zXq?UI%AKs=)6bsEZJnN(oo<%V?~?Bj7JU31@55SN z=I?lXOM>cBne0J`l{$>w`teyIJ7>Eh)z`{0k<(HJv|1o7*vmRTG;j__VIDK~IX&sM ze)JnfGhw11ww+9P&;mG3=;~hQ68HP;A*R9VA2)#{MR(y*;C5QXWHt-^T8V${Ki648 zW(qE@j|+-pq+oKYPP;%e7XGR_ajnPH%NC_8U93Nm>et2OQp?Og_X>LdHrP}1b{u@~ z>M+wF)qcT!ra^JdYm2{cMvNAk01=1kC^3;NGXY$|9vf5q4wWImM-Yt{nm2@Xhc9tE z_nCH$*e5TgQVfYR?{jy~Y1QbQ70{g0{+I(jjk^4n+yD(dYHXZ9t*%o(0DlfcEz3#W z%Z_P&BI!=zK*pqh^aIVzRi1xlizBqC+A2+lP&6YdJ)oK1{Ij;N6of&>S8)QRYIdti^jt$oz-$0(F z@4Ana+~G%zpf4Pnhq?f6PbgA<`nLJAUJK)9N-x~ z=O3>27aaJ>rXh<3f#}=RNRqV^z)Pil^-99TQou5-FD)PmiPr?aCM?cw=^l&*s>Kc# zhGM@1mp;_cTlror8VG$6JeJ9r_5UaCD)a(?2$M!My~1xq*0m;n^z!GK2Kt_7hl>lh+m(2@x;nQe`BLaUlU-JhtI~yfb2qoT$03Ilm348CWpi*|&Fo-JYcTKDg;sLi z>n-%!(b;;fG^QedfLnv@c?Hdrs1Z`uYI60QsqH7$@}q*uNnSEqtMP!>b|F!$g})9I z&|Eb}Yy+AQ&~)(A^>p$>Ts!SLfDFm^Y^D;go!glGs7*o$asqgi5QElUnNk%d~W4HbY>dET2{ru)Jfh;54x5EE?<7%NYeXi>T46 zc1X6Z4J}2hv0hMs3^E53YJnpRYd0DsXFqJ|Sy1Eo8b(55Cu1%!y0#$^6hiV;k|EGi zIn9NOiEJOg25_qZ`VIr#mwbe>%;T~*&D!Qdn~>srOAMzLFooOxazg1i#|XL3KFwJ_ zi+QI10m0%B<$m3wz`;*eU(gV%UFB%V3kkGsP$8@{> zp6@y~%Bn7eBCHI-3Sm4ROyrU`SPrr+E5G0LWJTTUtD%3}``*`romdd2^l%CNB}m)j z3>t_KJmJy9C`Kpmzy!wR^8{xJbNas%*0_b|J-@t5apTI+fHV8c&?iAbTW;#q#es3#0^m+(e!5DkGjrQND4a5ghgyKY76tsZ_ zU2!e|CaH!9&sQ_6jL6U51!OOVoPKc%=fCp430Z_=w&uD}EzKf$NT(ksM3`j~EXAjP z9mA0EdrEPMQ-Q*oxx03*`^O3M|&&f7&7&Q~~j z=KVG%zPTJ^@B1FdcKiJj+mJttO~bOpG#dt>qMkz2{uJgT#2{2f3Vf);)BQeW`OU?COQ+%hlmK=h6S2bAf z6Ahk|Siog@dyVMz<3}N<;kglt%PaEIxQZKa0-M3c+LL+@9&d$S!I=>`)44|%>;(z# zUl#rBbI&?N*}Q+QCjfkc6=QAnO;EoZYyVq}qx;gen#XG#WY#PO<$(t8wF?sJNGS8Y zzSxD`xL!N}>^BrB>c-_oUe0=tQ?9k1n^UT(^E|W;?T1FGau#5f+qQ#SR<`4WVf!vZr#3*61jpp(!!LPV z7c22Lr(B$Nx$Ky{S~7ezO5oQ%(^e31yidTLbi#L}y8gGv!5K1+-x9fW|7grH0OH)?D+TbrL@Vy+8&9aR6$F;N+SwA|9q zcG-Gr;Q($RLL`|T+#V0JKa!A`OTV?@S^E=(EDNp%cNWoJUT{YDXHZEc zEn63Am1v~8H&C#{RDR zm+@OlDd3Du$*MmB{OSBK%xY(dZsWy@##GFW%L+v6RMK4z{n_u|m)y^(67~%|K(eL~ z%pl=^nDLdw3rF_Y?>>&QkH2AV-mzcML#^m~h&oW&TtBru9SD3{dz7d2Dn{ z;TNC(#7kb|buo#-t6i%m($fVErsIn3R$yD{VY=(7%L7yyX2P29!j@IN` zD$r4~QKJHFwml5w`)s>E`Q4Ka!D>$X>Qc#-1GZr|}x0ma3@|I8GC+V@%pTRqwy{QPUucUQmP20>FMLA`ryFw$R$2*^d z+<~Ib$FCHVL3lMIkN(+jwOMo%x&X+%1An7x%m3(vd|q`{ixnuR0EeLV-m=7tl?UWf zBUPwXgr-~swT-V^5-Xx_i(JFI(`x)_OerIA=wHJRL(nRRX0K3BO8eCgQfzDY0ZDuR z^wD{M8&o&#Uol!o;9+pC8hnA9WSlqQE*I$ho^!mY{_-0_{uI4KJ@xPHhF{N{=`SIs zo%me9sk0e%^4E^>;od@lQ+FK;1d}!_V!4^99BygI2UR%b(GXP84BmB#@;cw?aGk9e zyJ2sU=xjNJxs!zfd)j9k*|5eeqe;D^WNWiaO4)Kxz=_b$MV5gHD}ITb*uhDMRNHh_12!_A8bv(?mtH5mUHt!pk zRAh!!jL#tvnTV#032>O^K^ABH4Lr;2Yx(&{4#$E5fdVVXF;9#=)XW8IVglR8A;P%T z2`S&~C>kUO^HptDXGNh_DC&anI zH>LY!DAAj2erR}N8{73*AzShrTB?GhT2gW{)(b4kaetN%LtryZp&O=8EkHf}`Hr(m zc7g5WwB-Gk*a64IpcNl^o7>dAyAoKz;3ru!+rvxbkz?7$!N$ExG-zsX-N=@l`#Vim zT|*^aak==1fSf8$c>nHVLBnQ{=lBO@gPuD3l3L+(BY3P53#WcwVWd$yG0G(C*HM66 z9)?AMPEGF9ab@ydsSeR7J}KulxrjyNfGlWex}X?x@HRID&)+_inz#_8BbXuykfgls z&?kSuKl<6Vt6MJ!EZc_yt2mU1-%!Y=@8SL|mKC&oJ>#*bbGk9Sq3-3|)V;X88^K50 zX}>t$Wzq>aP`!ASgB*TSWvAonHkdT@=bl1K5WdY;!=G1=?w^C--()_PIcgnEnuXnldq^i|EmsLG`5h87-14JA4iCN%M6dqz(PxkGoAg`vUjf z_+h9eD1dGT$+H}20m78K$59Ru?|43QeQU#KGxoBM2i&ZVvgoU&hpONj+i0Uja~L_a zSfv*MHWFU+k?Ys33NI}KJ{u>7c|%8&8uNrvUD@~hdKkQ5Re`wcvR8cWqNa+X{3XO&PlcXHk{vdP64plF#>XE zW24E~uRHLn$kqlH(f&cav5QRO!nY7jUBXClC?I)U`HK%DBmwCLs4?bm#h1Eeii!2YG8}w;;pw!sz@c`?Lz7iFcr-`>E}LG>Xrk?u7u9l zUZmw>V#casx8=pKKSYi+`1#^*?WpZmNlI$7dWqvfpQ{A{f~?Mr#umMBvfZf?^#J6O zfLxY^ZAc8qc5lO4hRrk4ZHSzO$sm03YPK^uvjqHT#4mPPd>7V!z(LeU(VT_dix;=S>_@}j5s=vu;z{_CaHlaX%cjk_uE_%&vr zWZvQOKIyV%{?@1^uC$5BMBId8tqijVxrRc$x1Nkio2`sA-L0Vu z9#J zm0@WeYG$2d+PnG+<{P#4ly4!&h2VX8lV4b!mhFT^jsa@Bw>&>ZodAk-Q=* z^V1Dr;GmHjk_yg?T>;u8qDiAnXuu*a$g3$*B3XYm7fkPvA3>&D6RoIxvncbtu0`C( z_(Vu_{M|>ie}CTLLbpNom1H_X>09czk1A^i2uq;zgikEliP{c+nAiC@se-Bl|64cL z2*$%}cTU$vWvz?ohVxSkb$TxWGN{cukGbC6Sc#6x??;k6>I+ZemNlFY9*gY8D@U5E z$OLS#f}KfwV)3%%o@soptZ|MSmmM`)M+P=@Ykm`?B5IX-fGs)@uvgF+6lI;luXeC1 zyq^9NF70jJClxWLzfQ?UB+-PTyTIxqkC?DNg|77XB07*Z$>*qO zujq4$EdCij>SbCj@r`>d;T=dxUYiYbo0St$vz=EY?#uVX`H)1l1%U)6bsi|@%~D_g zySF>c@a~!R2Vj8``l!?=kSN4dPmbBHb^p^E!U@ VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-email-send/assets/icon-small.png b/plugins/twilio/skills/twilio-email-send/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Add knowledge retrieval to AI agents using Twilio's Enterprise Knowledge + product. Enterprise Knowledge is a centralized, searchable repository of your + organization's documents, websites, and content - FAQs, support policies, + warranty terms, product catalogs. Current models don't have access to how you + run your business today. Enterprise Knowledge gives agents a way to query this + repository during a conversation and ground their responses in your actual + approved source material. This skill covers provisioning a Knowledge Base and + uploading knowledge sources from web URLs, PDFs, and raw text, and running + semantic search to retrieve relevant chunks at runtime. Enterprise Knowledge is + shared across your organization - it captures what your organization knows and + how it is meant to run. It is distinct from Conversation Memory + (twilio-customer-memory), which is scoped to individual end-customers and + captures what you know about a specific person. The two are designed to be + combined: enterprise content for business practices, customer memory for + personalization. +--- + +## Overview + +Enterprise Knowledge gives AI and human agents access to your organization's actual source material during a conversation - FAQs, warranty policies, support scripts, product catalogs. Models trained on general data don't know how your business operates today; Enterprise Knowledge closes that gap by letting agents query a searchable repository of your approved content and inject accurate, up-to-date answers rather than hallucinated ones. + +``` +Your content (web/PDF/text) -> Knowledge Base -> Indexed chunks +Agent query -> Search -> Ranked chunks -> Inject into LLM prompt +``` + +Enterprise Knowledge is shared across your organization and captures institutional content: how your products work, what your policies say, what your agents are supposed to do. It is distinct from Conversation Memory, which is scoped to individual end-customers. The two are designed to be combined - enterprise content for accuracy and business practices, customer memory for personalization. + +Auth: Basic Auth - `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN`. + +--- + +## Prerequisites + +- Twilio account with Enterprise Knowledge access (requires enablement) + - New to Twilio? See `twilio-account-setup` +- `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN` - see `twilio-iam-auth-setup` + +--- + +## Quickstart + +### Step 1 - Create a Knowledge Base + +Knowledge Bases are containers for knowledge sources. Creation is async - returns 202, poll the `Location` header until `status: ACTIVE`. + +Python +```python +import os, requests, time + +account_sid = os.environ["TWILIO_ACCOUNT_SID"] +auth_token = os.environ["TWILIO_AUTH_TOKEN"] + +res = requests.post( + "https://memory.twilio.com/v1/ControlPlane/KnowledgeBases", + auth=(account_sid, auth_token), + json={ + "displayName": "product-docs", # alphanumeric + hyphens only + "description": "Product documentation for customer support agents" + } +) + +operation_url = res.headers["Location"] + +# Poll until ready +while True: + kb = requests.get(operation_url, auth=(account_sid, auth_token)).json() + if kb.get("status") == "ACTIVE": + kb_id = kb["id"] + break + if kb.get("status") == "FAILED": + raise Exception("Knowledge Base creation failed") + time.sleep(2) + +print(kb_id) +``` + +Node.js +```javascript +const accountSid = process.env.TWILIO_ACCOUNT_SID; +const authToken = process.env.TWILIO_AUTH_TOKEN; +const authHeader = "Basic " + btoa(`${accountSid}:${authToken}`); + +const res = await fetch("https://memory.twilio.com/v1/ControlPlane/KnowledgeBases", { + method: "POST", + headers: { + "Authorization": authHeader, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + displayName: "product-docs", + description: "Product documentation for customer support agents", + }), +}); + +const operationUrl = res.headers.get("Location"); + +let kbId; +while (true) { + const kb = await fetch(operationUrl, { + headers: { "Authorization": authHeader }, + }).then(r => r.json()); + if (kb.status === "ACTIVE") { kbId = kb.id; break; } + if (kb.status === "FAILED") throw new Error("Knowledge Base creation failed"); + await new Promise(r => setTimeout(r, 2000)); +} +``` + +### Step 2 - Add a Knowledge Source + +Three source types: Web (crawl a URL), File (upload PDF/CSV/Markdown/text), Text (inline raw text). + +#### Web source + +```python +knowledge = requests.post( + f"https://knowledge.twilio.com/v1/KnowledgeBases/{kb_id}/Knowledge", + auth=(account_sid, auth_token), + json={ + "name": "Product Documentation", + "description": "Public product docs", + "source": { + "type": "Web", + "url": "https://docs.example.com", + "crawlDepth": 3, # 1-10, default 2 + "crawlPeriod": "WEEKLY" # WEEKLY | BIWEEKLY | MONTHLY | NEVER + } + } +).json() + +knowledge_id = knowledge["id"] +``` + +#### File source (PDF, CSV, Markdown, TSV, plain text - max 16MB) + +```python +# Step 1: Create the source - returns a presigned upload URL +knowledge = requests.post( + f"https://knowledge.twilio.com/v1/KnowledgeBases/{kb_id}/Knowledge", + auth=(account_sid, auth_token), + json={ + "name": "Company Handbook", + "source": { + "type": "File", + "fileName": "handbook.pdf", + "fileSize": 2048576, + "mimeType": "application/pdf" + } + } +).json() + +knowledge_id = knowledge["id"] +upload_url = knowledge["source"]["importUrl"] # presigned S3 URL + +# Step 2: PUT file to presigned URL - no auth header, URL is already signed +with open("handbook.pdf", "rb") as f: + requests.put(upload_url, data=f, headers={"Content-Type": "application/pdf"}) +``` + +#### Text source (inline content, max 185,000 chars) + +```python +knowledge = requests.post( + f"https://knowledge.twilio.com/v1/KnowledgeBases/{kb_id}/Knowledge", + auth=(account_sid, auth_token), + json={ + "name": "Refund Policy", + "source": { + "type": "Text", + "content": "Our refund policy: customers may return items within 30 days..." + } + } +).json() +``` + +### Step 3 - Wait for Processing + +Knowledge sources are processed asynchronously. Poll until `status` is `COMPLETED`. + +```python +def wait_for_knowledge(kb_id, knowledge_id): + while True: + k = requests.get( + f"https://knowledge.twilio.com/v1/KnowledgeBases/{kb_id}/Knowledge/{knowledge_id}", + auth=(account_sid, auth_token) + ).json() + if k["status"] == "COMPLETED": + return k + if k["status"] == "FAILED": + raise Exception(f"Knowledge processing failed: {k}") + time.sleep(3) + +wait_for_knowledge(kb_id, knowledge_id) +``` + +Statuses: `SCHEDULED` -> `QUEUED` -> `PROCESSING` -> `COMPLETED` / `FAILED` + +### Step 4 - Search and Inject into LLM + +Python +```python +results = requests.post( + f"https://knowledge.twilio.com/v1/KnowledgeBases/{kb_id}/Search", + auth=(account_sid, auth_token), + json={ + "query": "How do I reset my password?", + "top": 5, # max 20 + "knowledgeIds": [knowledge_id] # optional - search specific sources + } +).json() + +chunks = "\n\n".join(c["content"] for c in results.get("chunks", [])) + +system_prompt = f"""You are a helpful support agent. + +Relevant knowledge: +{chunks} + +Answer the customer's question using only the above content.""" +``` + +Node.js +```javascript +const results = await fetch( + `https://knowledge.twilio.com/v1/KnowledgeBases/${kbId}/Search`, + { + method: "POST", + headers: { + "Authorization": authHeader, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + query: userMessage, + top: 5, + knowledgeIds: [knowledgeId], + }), + } +).then(r => r.json()); + +const chunks = results.chunks.map(c => c.content).join("\n\n"); +const systemPrompt = `You are a helpful support agent.\n\nRelevant knowledge:\n${chunks}`; +``` + +--- + +## Key Patterns + +### Combine Enterprise Knowledge with Conversation Memory Recall + +For the best agent responses, combine both: Enterprise Knowledge for company content, Recall for individual customer history. + +Python +```python +# Run both in parallel +recall_res = requests.post( + f"https://memory.twilio.com/v1/Services/{MEMORY_STORE_SID}/Profiles/{profile_id}/Recall", + auth=(account_sid, auth_token), + json={"query": user_query, "observationsLimit": 5} +) +search_res = requests.post( + f"https://knowledge.twilio.com/v1/KnowledgeBases/{KB_ID}/Search", + auth=(account_sid, auth_token), + json={"query": user_query, "top": 3} +) + +customer_history = "\n".join(o["content"] for o in recall_res.json().get("observations", [])) +knowledge_chunks = "\n\n".join(c["content"] for c in search_res.json().get("chunks", [])) + +system_prompt = f"""Customer history: +{customer_history} + +Relevant documentation: +{knowledge_chunks}""" +``` + +### Refresh Stale Web Sources + +Re-crawl a web source without changing its config: + +```python +requests.patch( + f"https://knowledge.twilio.com/v1/KnowledgeBases/{kb_id}/Knowledge/{knowledge_id}?refresh=true", + auth=(account_sid, auth_token), + json={} +) +# Returns 202 - source re-queued for processing +``` + +### Filter Search to Specific Sources + +When your knowledge base has multiple sources (scripts, FAQs, policies), target search to the relevant one: + +```python +results = requests.post( + f"https://knowledge.twilio.com/v1/KnowledgeBases/{kb_id}/Search", + auth=(account_sid, auth_token), + json={ + "query": "cancellation policy", + "top": 5, + "knowledgeIds": [policy_knowledge_id] + } +).json() +``` + +Omit `knowledgeIds` to search across all sources in the knowledge base. + +### Inspect Processed Chunks + +To audit what got indexed from a source: + +```python +chunks = requests.get( + f"https://knowledge.twilio.com/v1/KnowledgeBases/{kb_id}/Knowledge/{knowledge_id}/Chunks", + auth=(account_sid, auth_token), + params={"pageSize": 50} +).json() + +for chunk in chunks["chunks"]: + print(chunk["content"][:100]) +``` + +--- + +## CANNOT + +- Cannot add sources before Knowledge Base is active - Creation is async (returns 202). Poll `Location` header until `status: ACTIVE`. +- Cannot use one host for all operations - Management is on `memory.twilio.com`; sources and search are on `knowledge.twilio.com`. Wrong host returns 404. +- Cannot include auth header when uploading to presigned URL - `importUrl` is already signed. Adding your auth header will fail. +- Cannot use expired presigned URLs - `uploadExpiration` is typically 1 hour. Upload promptly. +- Cannot search before processing completes - Web crawl and file indexing are async (seconds to minutes). Poll status first. +- Cannot use high crawl depth without performance impact - `crawlDepth` 1-10, default 2. Higher depths dramatically increase processing time. +- Cannot exceed 16MB per file upload - Hard limit +- Cannot exceed 185,000 characters per text source - Hard limit +- Cannot retrieve more than 20 search results per query - `top-K` max is 20 +- Cannot use spaces or underscores in `displayName` - Alphanumeric and hyphens only (`^[a-zA-Z0-9-]+$`) +- Cannot use Knowledge for customer-specific context - Knowledge is shared across all customers. Use `twilio-customer-memory` for per-customer context. +- Cannot retry FAILED sources - Delete and recreate. No retry endpoint. Check chunk count after `COMPLETED` to verify extraction. + +--- + +## Next Steps + +- Per-customer context: `twilio-customer-memory` - combine with Enterprise Knowledge for full agent context (company knowledge + individual customer history) +- Conversation Intelligence operators with enterprise context: `twilio-conversation-intelligence` - feed Enterprise Knowledge chunks into Conversation Intelligence operators to give them business context. Examples: + - Script Adherence: index your approved call scripts as a knowledge source; the operator can evaluate agent compliance against the retrieved script for the current conversation type + - Custom upsell classifier: index product offers, pricing tiers, or eligibility rules; a custom classification operator can use retrieved offer details to detect upsell opportunities mid-conversation + - Next Best Response: retrieved policy or FAQ chunks injected alongside the operator prompt improve suggestion quality +- Wire into a voice AI agent: `twilio-voice-conversation-relay` +- TAC SDK integration: `twilio-agent-connect` +- Debug integration issues: `twilio-debugging-observability` diff --git a/plugins/twilio/skills/twilio-enterprise-knowledge/assets/icon-large.png b/plugins/twilio/skills/twilio-enterprise-knowledge/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 zcmaKScT`i)^LFSEdRKu+=tvb1f`kr|NEHx4iXbhNPz6GhVnB*O=q*N?A_~$$sx+k< z1eD%;lP(}4{w|;MegA#`$jLc#_wLTl%-wmOoryIv(4hxk1A{;ydL%;27z83Q0Dk(^ zlz_yuJGvYAqwzvm_<}%^{Fgrx%xhJD5QrCq)KWLa;D5fH@t*1#j-FHY+xbj4U_#e+ zd#qVnp45$Bny0;wS)4m!gIZ6!!kC<@asF2vNrj3GnG9(d=LpU&Ck#ZnzSI84w6f?& zoKY}!^GKNN;nd0QoI1kYKXrq;t?Ycw+(vLBwtj~6qoVHwd-zQ9t52p+TT}m0hR>i{ zLKF}FU~3i?z``ya9x^=qF$)smL+xX}i^ZR-LnO58KvC&(*bXGon7M~ewQK(+LHlI| zMTI)k2z@vw_=X}?RBAIHU!YhyoqtPrt2q?&mO|uB5owff+qTe%F|RCna!8>|5-Wij zTXUr>GwOq=7A<%!9LCf~*Lf^tuMXwmWg}vf`}`^A4O)i_w+X`J&`|EOOfG8^!~-K3 ztL8!4Yopc&x1vj#w4St|qaFgXJiZKBkmvmbXz@5HhFn{YMg(t=EFD4__gY7!fSB)f z+g>4?$kePGz2H(oTA{`?WdFV(MqLEYfHOLWv^O~j!mSBR65EDAtEn8<6s$z#vMJ6y*b&`ABmMk5y`ad<3!38-CVNLVlQ8Bp3|x1^I$e|BPV4cgSU_GF4MxMr!PN zUdLGMGOdv`Qo%?q7~3&%bHFZzFhSO%v$bM1g-gr#^2gS&OQ^LA+;(Rsf0FO6lVSwp z=XdK_v$bTdY?qA;=a`HxB9D^gYwDGw;u?$92Q++Ah(>^cVQn3Z-W`y9Oqv_lA`jS^ zRv<}{D&PiR>o{rF%@9w9sA_0a?-3;bA+Z9FSs`gtp32yPdo&QH;g5l5zCZ#5oN{15$ zUTYX+HG&zorK>v3OP>T%g4dXV*yD*rRXsL{CO*J^2@HD0E`n$OXqAF1EM;moggArR2h5F~Fw+Nn6rSyB-PDGO4taQN8QDB22FgC0>2P{@@28(Orcxb&$}DBx0(^AbR% zM01jlTt0(qwoxVr*%a%JEW=pkNDN>Ulyr}g^+C2&9*Si+jSKXT%XQY*tHYw#*@qUU zF)7>Ayq2fo_konJNY)Hgm46h>@MS5@BDnyGP)L)|t)GlOQa^wQ$E#tF`XKFxdW{w; zE=j4t)7@!a3?X1L`U$eCqqk2H>N*WNRa%J)PRS2){4Joc@y#(wI*?@H5EP!!3eJsRbn+V0F4j%eB?RSFD-G>D2YQ=glHQ5B+Lv zX5F}$Q;V=Bi5sEh+SR&dr%;@f8F`f?-&=cf^%jcHHExhQTPu1~G3EmN_!Pt6Z_qlC zrA+WsQnh5CR|LK0tC6^ap0!f}egcwOSX=gnCGgd0EoubPFb^w{-*imA$$*S04qhli z-N`??!C7)LcS}3bxR(i=Hj7f4jxTmwNVBQ@RQuDbk7EIY`;4ona*I5onv39@q#Rq^ zFoRhrdv-xoyHW^_>U*C(i^ebxaKP|x?D2`*p=y!6Kt!av}+JKh`_VIgdDhv&L9uy@5Yqqb} z1+8L>xfYhAc{nYgm`qwnr5sAQyjGhw2Vdw9Y@&lpVI5y1>PsQplUh*^a?K5foitC@YG0R|;CW z+Sc;iK{56@o2IX{HpLaRiHtD5G>?qh?sv;)uIQuZTGqbOA^*y6t+g*^Yx>ez%Dn=; z5Y2I@v1xrV+E3RejO~5$mTl1}yFJZoM`$FNu3p)maXx7%D6rc~sDzKA-4gr#D^r8| z`1MgbCyieboX-fL0g~= zd({0CcH!U1r)zswC2Bnq+%s$WRt3j2b58u>kVRLtIBVH6Til)ftNB6Y=YQgf!ZJA2b56c|V7@+AajMimgT zCSVOwqLZbO9Fk8!obZw(uJH)A#$#g{uiU|)!=|4?$44*(R#J10DlowMDrC<>Tln=G z_+p9%=v*WR2bw;j9zdMqW>;SnAdO#F!F}Jm-*s2%G2VTv#YvoTxUCr^hE?$%^G~_W zM+iW8>NdiUDGKZO1t*^#@hb9aP(~C@UX0f9%iJ5Q>^_F=CTE~Nh~zk+5Kn#~rxJU# z^snDJgs~pUpMbvDh^-szH>ZyQ$=@EN6qw%38}d{NWYc`n)TH)stIxZc!swXFj5bf~ z{K_Eb@7D08O1%nom4_xFdJ6+2=jzdE%{A-4cl{&v&Q8Ad z4yuBB%4pwCyUo@7jL~8%udekHT19)1Og|(dk)M4T{$`X5gHA@TpZWF>trd@Utq)0a z%v-B#$F3H>BNz*YI^CBGNZr6FWOGoX`6df!K)WR8ue7*6jO)Ld@Q?{5*`tu(nPCLk zX!Vr&Bt6Mlaz;xrbuLY}fB8C=F<51ouP9(z)jaYYY+z=lh!9ief3b3B3a2SB_dBVM zCmHo)Y~n7AwJ)?3IGxMtn4crAIWSy@4nv?%9*InOvpKUb0;ws-vTW6Mu_0q^C@A|@ zsPB}KvghxoVgohwu%d&!U?zQDOy`^crxSQTO@0rWqjshLlSK3mOZw}It36HfO2W4| z3|yX7;@sT&o!y{s;OkO*hj|Zv+alpF>qkN=(UHnn{wZ=c$TrNbUD7>t2_jV>!lqf%A@!rEt-xjC0Y?=DL9XF>R&2 zxvsR%j`}su#}&dCw5J0uHU(o;DmTmj;yf3TxoX^uyE-1t$=ZUos73GD_J8_;o+U%p z?}~S8f3D*KA2@&FhU(<78ui*|7AwflLoO6l2tPtKSr_EeIo;d(jLY+>?|yK|-NtQQHYxX^ zAHNdzTalOv_E@$!C%>l+AG$5J7AXT|%$RbZ2|HM~UL`u&-DESnpDd%z%~n(483UW= z2j8$GIrZ6YT;F6B*5@NKrNyEg!~HP!F3;GJVaj5;L(vbl3*JYWNNy??g;dmdM`Ft~ z4{&!?o*0Sr@jn`*J^2FGZm1)Xh?T3DgF&efd zv=E!zgbUtdO(a);uhy)hiPCzW)ytb)M_{Mq4@>NBtX?0`YP23+ub*Mcx{;o;lCo&B zw1zXvmPm`DEzC+nja@b`MfqXQqic~~4-e(Lub2t`Wh+w_>}_J4@RiDT;qp?uoM845 zE4?+FK|{>ieF;D>DhvVp_&{DGImZ5~^eqO>z1!vcF#S-GTQ^tG;Osh*=$^E>S73#V z4!`z?uUvR2rSKf8@WI(&_iJ;ORdES+XN)pvvUfd|*KHoKT8G>zXC{`m@Z5lNO;0F~ z)U{th`=8yc*2OUcgA#Fo4bybL?m0(|1dcRmd!{n}8EkeL%IK;*zZ$=^PyGpqY)*r5 z{O1pLZu?j@kUg4=ZulsoUW6@Y`iiAC`9!04K3YffAx-hv`KrcLIP0F>KX$M)Ts$%M zbI#X|Vt zLT#u|j?lvE23(p|*OqTK&!2^R3u#X^lN1HYuQNbPs=vwSK_@D&6AoZW7gc1<;iOBr!>r>{;3{JS&~+~hW;a^@CH^zG;KDQFRG5@ z7G_vJkEA$iWaCcLpn*mjOOk^A12+9R5eX!Omfm`yH&v{L9@S=%G-97+gW@t`HB`Wi z)$q_JI<{Fd`sSn6$VBjcdv=U)?HD$Wn{-IfgOjw8x}xe1#ht-KSdX!!LX5ezRnlIC zQYfg&_HsNtut!aVDWU%#jExxZcXIezxtKS{B&A5T4qVD*{SRv?K59b{(2V{{=3Fz_ z`My5SJz)3bYdXbWY$26-_2Gjp5c>tcMG-(RHM6HCzhK=hxkHd7Sv2wkI-Dg~D`Z?d zyd_S@vaSkQp@a|WT{_5p=j8;9ApoKFriWftt6cU?{qG=sx{@4zXq?UI%AKs=)6bsEZJnN(oo<%V?~?Bj7JU31@55SN z=I?lXOM>cBne0J`l{$>w`teyIJ7>Eh)z`{0k<(HJv|1o7*vmRTG;j__VIDK~IX&sM ze)JnfGhw11ww+9P&;mG3=;~hQ68HP;A*R9VA2)#{MR(y*;C5QXWHt-^T8V${Ki648 zW(qE@j|+-pq+oKYPP;%e7XGR_ajnPH%NC_8U93Nm>et2OQp?Og_X>LdHrP}1b{u@~ z>M+wF)qcT!ra^JdYm2{cMvNAk01=1kC^3;NGXY$|9vf5q4wWImM-Yt{nm2@Xhc9tE z_nCH$*e5TgQVfYR?{jy~Y1QbQ70{g0{+I(jjk^4n+yD(dYHXZ9t*%o(0DlfcEz3#W z%Z_P&BI!=zK*pqh^aIVzRi1xlizBqC+A2+lP&6YdJ)oK1{Ij;N6of&>S8)QRYIdti^jt$oz-$0(F z@4Ana+~G%zpf4Pnhq?f6PbgA<`nLJAUJK)9N-x~ z=O3>27aaJ>rXh<3f#}=RNRqV^z)Pil^-99TQou5-FD)PmiPr?aCM?cw=^l&*s>Kc# zhGM@1mp;_cTlror8VG$6JeJ9r_5UaCD)a(?2$M!My~1xq*0m;n^z!GK2Kt_7hl>lh+m(2@x;nQe`BLaUlU-JhtI~yfb2qoT$03Ilm348CWpi*|&Fo-JYcTKDg;sLi z>n-%!(b;;fG^QedfLnv@c?Hdrs1Z`uYI60QsqH7$@}q*uNnSEqtMP!>b|F!$g})9I z&|Eb}Yy+AQ&~)(A^>p$>Ts!SLfDFm^Y^D;go!glGs7*o$asqgi5QElUnNk%d~W4HbY>dET2{ru)Jfh;54x5EE?<7%NYeXi>T46 zc1X6Z4J}2hv0hMs3^E53YJnpRYd0DsXFqJ|Sy1Eo8b(55Cu1%!y0#$^6hiV;k|EGi zIn9NOiEJOg25_qZ`VIr#mwbe>%;T~*&D!Qdn~>srOAMzLFooOxazg1i#|XL3KFwJ_ zi+QI10m0%B<$m3wz`;*eU(gV%UFB%V3kkGsP$8@{> zp6@y~%Bn7eBCHI-3Sm4ROyrU`SPrr+E5G0LWJTTUtD%3}``*`romdd2^l%CNB}m)j z3>t_KJmJy9C`Kpmzy!wR^8{xJbNas%*0_b|J-@t5apTI+fHV8c&?iAbTW;#q#es3#0^m+(e!5DkGjrQND4a5ghgyKY76tsZ_ zU2!e|CaH!9&sQ_6jL6U51!OOVoPKc%=fCp430Z_=w&uD}EzKf$NT(ksM3`j~EXAjP z9mA0EdrEPMQ-Q*oxx03*`^O3M|&&f7&7&Q~~j z=KVG%zPTJ^@B1FdcKiJj+mJttO~bOpG#dt>qMkz2{uJgT#2{2f3Vf);)BQeW`OU?COQ+%hlmK=h6S2bAf z6Ahk|Siog@dyVMz<3}N<;kglt%PaEIxQZKa0-M3c+LL+@9&d$S!I=>`)44|%>;(z# zUl#rBbI&?N*}Q+QCjfkc6=QAnO;EoZYyVq}qx;gen#XG#WY#PO<$(t8wF?sJNGS8Y zzSxD`xL!N}>^BrB>c-_oUe0=tQ?9k1n^UT(^E|W;?T1FGau#5f+qQ#SR<`4WVf!vZr#3*61jpp(!!LPV z7c22Lr(B$Nx$Ky{S~7ezO5oQ%(^e31yidTLbi#L}y8gGv!5K1+-x9fW|7grH0OH)?D+TbrL@Vy+8&9aR6$F;N+SwA|9q zcG-Gr;Q($RLL`|T+#V0JKa!A`OTV?@S^E=(EDNp%cNWoJUT{YDXHZEc zEn63Am1v~8H&C#{RDR zm+@OlDd3Du$*MmB{OSBK%xY(dZsWy@##GFW%L+v6RMK4z{n_u|m)y^(67~%|K(eL~ z%pl=^nDLdw3rF_Y?>>&QkH2AV-mzcML#^m~h&oW&TtBru9SD3{dz7d2Dn{ z;TNC(#7kb|buo#-t6i%m($fVErsIn3R$yD{VY=(7%L7yyX2P29!j@IN` zD$r4~QKJHFwml5w`)s>E`Q4Ka!D>$X>Qc#-1GZr|}x0ma3@|I8GC+V@%pTRqwy{QPUucUQmP20>FMLA`ryFw$R$2*^d z+<~Ib$FCHVL3lMIkN(+jwOMo%x&X+%1An7x%m3(vd|q`{ixnuR0EeLV-m=7tl?UWf zBUPwXgr-~swT-V^5-Xx_i(JFI(`x)_OerIA=wHJRL(nRRX0K3BO8eCgQfzDY0ZDuR z^wD{M8&o&#Uol!o;9+pC8hnA9WSlqQE*I$ho^!mY{_-0_{uI4KJ@xPHhF{N{=`SIs zo%me9sk0e%^4E^>;od@lQ+FK;1d}!_V!4^99BygI2UR%b(GXP84BmB#@;cw?aGk9e zyJ2sU=xjNJxs!zfd)j9k*|5eeqe;D^WNWiaO4)Kxz=_b$MV5gHD}ITb*uhDMRNHh_12!_A8bv(?mtH5mUHt!pk zRAh!!jL#tvnTV#032>O^K^ABH4Lr;2Yx(&{4#$E5fdVVXF;9#=)XW8IVglR8A;P%T z2`S&~C>kUO^HptDXGNh_DC&anI zH>LY!DAAj2erR}N8{73*AzShrTB?GhT2gW{)(b4kaetN%LtryZp&O=8EkHf}`Hr(m zc7g5WwB-Gk*a64IpcNl^o7>dAyAoKz;3ru!+rvxbkz?7$!N$ExG-zsX-N=@l`#Vim zT|*^aak==1fSf8$c>nHVLBnQ{=lBO@gPuD3l3L+(BY3P53#WcwVWd$yG0G(C*HM66 z9)?AMPEGF9ab@ydsSeR7J}KulxrjyNfGlWex}X?x@HRID&)+_inz#_8BbXuykfgls z&?kSuKl<6Vt6MJ!EZc_yt2mU1-%!Y=@8SL|mKC&oJ>#*bbGk9Sq3-3|)V;X88^K50 zX}>t$Wzq>aP`!ASgB*TSWvAonHkdT@=bl1K5WdY;!=G1=?w^C--()_PIcgnEnuXnldq^i|EmsLG`5h87-14JA4iCN%M6dqz(PxkGoAg`vUjf z_+h9eD1dGT$+H}20m78K$59Ru?|43QeQU#KGxoBM2i&ZVvgoU&hpONj+i0Uja~L_a zSfv*MHWFU+k?Ys33NI}KJ{u>7c|%8&8uNrvUD@~hdKkQ5Re`wcvR8cWqNa+X{3XO&PlcXHk{vdP64plF#>XE zW24E~uRHLn$kqlH(f&cav5QRO!nY7jUBXClC?I)U`HK%DBmwCLs4?bm#h1Eeii!2YG8}w;;pw!sz@c`?Lz7iFcr-`>E}LG>Xrk?u7u9l zUZmw>V#casx8=pKKSYi+`1#^*?WpZmNlI$7dWqvfpQ{A{f~?Mr#umMBvfZf?^#J6O zfLxY^ZAc8qc5lO4hRrk4ZHSzO$sm03YPK^uvjqHT#4mPPd>7V!z(LeU(VT_dix;=S>_@}j5s=vu;z{_CaHlaX%cjk_uE_%&vr zWZvQOKIyV%{?@1^uC$5BMBId8tqijVxrRc$x1Nkio2`sA-L0Vu z9#J zm0@WeYG$2d+PnG+<{P#4ly4!&h2VX8lV4b!mhFT^jsa@Bw>&>ZodAk-Q=* z^V1Dr;GmHjk_yg?T>;u8qDiAnXuu*a$g3$*B3XYm7fkPvA3>&D6RoIxvncbtu0`C( z_(Vu_{M|>ie}CTLLbpNom1H_X>09czk1A^i2uq;zgikEliP{c+nAiC@se-Bl|64cL z2*$%}cTU$vWvz?ohVxSkb$TxWGN{cukGbC6Sc#6x??;k6>I+ZemNlFY9*gY8D@U5E z$OLS#f}KfwV)3%%o@soptZ|MSmmM`)M+P=@Ykm`?B5IX-fGs)@uvgF+6lI;luXeC1 zyq^9NF70jJClxWLzfQ?UB+-PTyTIxqkC?DNg|77XB07*Z$>*qO zujq4$EdCij>SbCj@r`>d;T=dxUYiYbo0St$vz=EY?#uVX`H)1l1%U)6bsi|@%~D_g zySF>c@a~!R2Vj8``l!?=kSN4dPmbBHb^p^E!U@ VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-enterprise-knowledge/assets/icon-small.png b/plugins/twilio/skills/twilio-enterprise-knowledge/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Set up and manage Twilio authentication credentials: Auth Tokens, API keys + (Standard, Main, Restricted), Access Tokens for client-side SDKs, and + credential rotation. Use this skill as a prerequisite foundation before + making any Twilio API calls. +--- + +## Overview + +Twilio supports multiple authentication methods. For most developers: use Auth Token for local prototyping, then move to API Keys in production. + +| Method | Use for | Security | +|--------|---------|----------| +| Account SID + Auth Token | Local prototyping, initial testing | Full account access - avoid in production | +| Account SID + API Key (Standard) + Secret | All production code | Recommended - revocable, no access to /Accounts or /Keys | +| Account SID + API Key (Restricted) + Secret | Fine-grained production access | Best - limit to specific resources only | +| Account SID + API Key (Main) + Secret | Account management automation | Full access like Auth Token, but revocable | + +For beginners / vibe-coders: Start with Auth Token to get your first API call working, then create a Standard API Key before deploying anything. The key difference: if an API Key leaks, you revoke just that key. If your Auth Token leaks, your entire account is exposed until you rotate it. + +--- + +## Prerequisites + +- Twilio account - see `twilio-account-setup` if you don't have one +- Access to the [Twilio Console](https://console.twilio.com) + +--- + +## Quickstart + +Find your Account SID and Auth Token in the Console dashboard. + +Python +```python +import os +from twilio.rest import Client + +client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"]) +``` + +Node.js +```node +const client = require("twilio")( + process.env.TWILIO_ACCOUNT_SID, + process.env.TWILIO_AUTH_TOKEN +); +``` + +Never commit Auth Token to version control or use in production. + +--- + +## Key Patterns + +### API Keys (production) + +Create: Console > Account > API keys & tokens > Create API key + +| Key type | Access | Use case | +|----------|--------|----------| +| Standard | All resources except /Accounts and /Keys endpoints | Default for production apps | +| Restricted | Only the specific resources you grant | Multi-tenant apps, microservices, least-privilege | +| Main | Full account access (like Auth Token) | Account management automation (Console-only creation) | + +After creation, copy the API Key SID (`SK...`) and Secret - the secret is shown only once. + +Python +```python +client = Client( + os.environ["TWILIO_API_KEY"], # SK... + os.environ["TWILIO_API_SECRET"], + os.environ["TWILIO_ACCOUNT_SID"] # required as third argument +) +``` + +Node.js +```node +const client = require("twilio")( + process.env.TWILIO_API_KEY, + process.env.TWILIO_API_SECRET, + { accountSid: process.env.TWILIO_ACCOUNT_SID } +); +``` + +### Restricted API Keys + +Restricted keys grant access only to specific Twilio API resources you define. Use them for least-privilege access in production. + +Create via the v1 IAM API (not the v2010 /Keys.json endpoint - see CANNOT section): + +Python +```python +key = client.iam.v1.api_key.create( + account_sid=os.environ["TWILIO_ACCOUNT_SID"], + friendly_name="messaging-only-key", + key_type="restricted", + policy={ + "allow": [ + "/2010-04-01/Accounts/{AccountSid}/Messages*" + ] + } +) +# Store key.sid and key.secret securely - secret shown only once +``` + +Example permission patterns: +| Permission | Grants access to | +|-----------|-----------------| +| `/2010-04-01/Accounts/{AccountSid}/Messages*` | Send and read messages | +| `/2010-04-01/Accounts/{AccountSid}/Calls*` | Make and manage calls | +| `/v2/Services/*/Verifications*` | Verify API only | + +Docs: [Restricted API keys](https://www.twilio.com/docs/iam/api-keys/restricted-api-keys) + +### Test Credentials + +Make API calls without charges or sending real messages. Find at Console > Account > API keys & tokens > Test credentials. + +Python +```python +client = Client( + os.environ["TWILIO_TEST_ACCOUNT_SID"], + os.environ["TWILIO_TEST_AUTH_TOKEN"] +) +``` + +Node.js +```node +const client = require("twilio")( + process.env.TWILIO_TEST_ACCOUNT_SID, + process.env.TWILIO_TEST_AUTH_TOKEN +); +``` + +Magic test numbers: +- `+15005550006` - valid, can receive messages +- `+15005550001` - invalid number (triggers error 21211) +- `+15005550007` - number that cannot receive SMS (triggers error 21612) + +### Auth Token Rotation + +Rotate your Auth Token if it's been exposed or as periodic security hygiene. Twilio uses a secondary token promotion model: + +1. Console > Account > API keys & tokens > Request a secondary Auth Token +2. Update your application to use the secondary token +3. Once confirmed working, promote the secondary to primary +4. The old primary token is immediately invalidated + +Python +```python +# Promote secondary Auth Token to primary via API +from twilio.rest import Client + +client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"]) +account = client.api.accounts(os.environ["TWILIO_ACCOUNT_SID"]).update( + auth_token_promotion="promote" +) +``` + +Important: Auth Token rotation invalidates all active sessions using that token. Plan the switchover to minimize downtime. + +API Keys cannot be rotated - if an API Key is compromised, delete it and create a new one: +- Console > Account > API keys & tokens > select key > Delete +- Or via API: `client.keys(key_sid).delete()` + +Docs: [Auth Token REST API](https://www.twilio.com/docs/iam/api/authtoken) + +### Access Tokens (client-side SDKs) + +Short-lived JWTs for authenticating browser/mobile clients (Voice JS SDK, Conversations SDK, Video SDK). Generate server-side and pass to the client. + +Python +```python +from twilio.jwt.access_token import AccessToken +from twilio.jwt.access_token.grants import VoiceGrant + +token = AccessToken( + os.environ["TWILIO_ACCOUNT_SID"], + os.environ["TWILIO_API_KEY"], + os.environ["TWILIO_API_SECRET"], + identity="user-123", + ttl=3600 +) +token.add_grant(VoiceGrant(outgoing_application_sid="APxxxx")) +print(token.to_jwt()) +``` + +Node.js +```node +const { AccessToken } = require("twilio").jwt; +const { VoiceGrant } = AccessToken; + +const token = new AccessToken( + process.env.TWILIO_ACCOUNT_SID, + process.env.TWILIO_API_KEY, + process.env.TWILIO_API_SECRET, + { identity: "user-123", ttl: 3600 } +); +token.addGrant(new VoiceGrant({ outgoingApplicationSid: "APxxxx" })); +console.log(token.toJwt()); +``` + +Available grant types: `VoiceGrant`, `VideoGrant`, `ChatGrant` (Conversations), `SyncGrant` + +### Environment Variable Reference + +```bash +TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +# Option 1: Auth Token (testing only) +TWILIO_AUTH_TOKEN=your_auth_token + +# Option 2: API Key (production) +TWILIO_API_KEY=SKxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +TWILIO_API_SECRET=your_api_secret + +# Test credentials +TWILIO_TEST_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +TWILIO_TEST_AUTH_TOKEN=your_test_auth_token +``` + +--- + +## CANNOT + +- Standard keys cannot access /Accounts or /Keys endpoints - Returns error 20003 (401). Must use Auth Token or Main API Key for account management. +- No restricted key creation via v2010 API - The v2010 `/Keys.json` endpoint silently ignores `KeyType=restricted` and `Policy` parameters, creating a standard key instead. Use the v1 IAM API. +- Restricted keys cannot generate Access Tokens - Only Standard and Main keys can create client SDK tokens. +- No individual Access Token revocation - Tokens are valid until expiration (max 24h). To revoke early, delete the API key that issued them. +- Subaccount credentials cannot access parent or sibling resources - Each subaccount has its own Auth Token and API Keys. Use the subaccount's own credentials to access its resources - never the parent account's credentials. +- API Keys cannot be rotated - No key rotation API exists. To replace a compromised key: create a new key, update your app, then delete the old key. +- PKCV is an advanced feature for compliance-heavy industries - Public Key Client Validation adds client-certificate-style auth. Incompatible with Flex, Studio, and TaskRouter. Once enforcement is enabled, Auth Token authentication is disabled (one-way door). See [PKCV docs](https://www.twilio.com/docs/iam/pkcv) - consider this only if your security team requires mutual TLS-equivalent authentication. +- Test credentials work with only 4 endpoints - Messages, Calls, IncomingPhoneNumbers, and Lookups. All other endpoints return 403. +- API Key Secret shown only at creation - Cannot be retrieved afterward. If lost, create a new key. +- FriendlyName max 64 characters for keys - 65+ characters returns error 70001. +- Restricted keys limited to 100 permissions per key - Exceeding this limit is rejected at creation. +- Cannot create Main API Keys via REST API - Console only +- Cannot set Access Token TTL beyond 24 hours - Maximum lifetime is 24h +- Cannot use test credentials with real numbers - Test credentials only work with test magic numbers + +--- + +## Next Steps + +- Account setup and phone numbers: `twilio-account-setup` +- Security best practices (credential management, key rotation): `twilio-security-hardening` +- Restricted API keys (fine-grained permissions): [Docs](https://www.twilio.com/docs/iam/api-keys/restricted-api-keys) +- Auth Token rotation: [REST API](https://www.twilio.com/docs/iam/api/authtoken) diff --git a/plugins/twilio/skills/twilio-iam-auth-setup/assets/icon-large.png b/plugins/twilio/skills/twilio-iam-auth-setup/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-iam-auth-setup/assets/icon-small.png b/plugins/twilio/skills/twilio-iam-auth-setup/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Planning skill for identity verification and fraud prevention. + Qualifies the developer's needs across authentication method, + channel selection, fraud risk level, and user experience to + recommend the right Twilio Verify + Lookup architecture. Handles + login, signup, password reset, and risk-adaptive verification. +tier: discover +--- + +## Role + +You are an Identity & Verification Architecture Advisor. When a developer describes anything related to verifying user identity, preventing fraud, implementing 2FA/MFA, or validating phone numbers - use this framework to reason about what they need. + +## When This Skill Activates + +Trigger on any of these signals: +- "OTP," "verification code," "2FA," "MFA," "two-factor" +- "Phone verification," "email verification," "device verification," "identity verification" +- "Fraud prevention," "phone validation," "number lookup" +- "Passwordless," "magic link," "passkey," "TOTP," "authenticator app" +- "Account signup," "login verification," "password reset," "account recovery" +- Any request to verify a user is who they claim to be + +## Step 1: Detect Specificity and Decide Your Mode + +High-level request (e.g., "I need to add phone verification to my signup flow"): +-> DISCOVERY MODE. Channel, fraud risk, and UX matter - qualify first. + +Mid-level request (e.g., "Send an OTP via SMS and verify it"): +-> VALIDATION MODE. Clear approach - check if they've considered fraud (SMS pumping), fallback channels, and rate limiting. + +Specific implementation request (e.g., "Call the Verify API to start a verification with channel=sms"): +-> BUILD MODE. Proceed with `twilio-verify-send-otp`. Quick check: Are they using Verify (highly recommended) or rolling their own OTP logic? If custom, strongly recommend Verify - it handles rate limiting, code generation, expiry, and fraud protection so you don't have to. + +## Step 2: Qualify Intent - The 4 Essential Questions + +1. What are you verifying and when? + - Account signup (new user registration) -> Phone/email/device verification + - Login (returning user authentication) -> 2FA/MFA, phone verification, device verification + - Password reset / account recovery -> Identity confirmation (these are the same flow - verify identity before allowing reset) + - High-value transaction (payment, account change) -> Step-up verification + +2. What channels can you reach the user on? + - SMS -> Most common. Universal reach. + - Email -> Good for account verification. Less real-time. + - WhatsApp -> Growing. Good for international users already on WhatsApp. Cost-effective for high-traffic countries. + - Voice -> Accessibility fallback. Automated call reads the code. + - Push notification -> Best UX (one-tap approve). Requires your mobile app with Verify Push SDK. + - TOTP (authenticator app) -> No network dependency. User must have set up app (Google Authenticator, Authy). + - Passkeys -> Newest. Phishing-resistant. Requires WebAuthn browser support. + +3. What's your fraud risk level? + - Low (basic signup confirmation): SMS OTP is fine + - Medium (financial account, PII access): Add Lookup line type intelligence before sending OTP + - High (payment authorization, KYC-regulated business): Line type intelligence + SIM swap check + step-up to Push or TOTP + +4. What does your user base look like? + - US/Canada primarily -> SMS works well. Consider toll-free for cost. + - International -> WhatsApp may have better delivery rates and lower cost than SMS in high-traffic countries. + - Mobile app users -> Push verification is the best UX (no code to type) + - Enterprise / high-security -> TOTP or Passkeys (no phone network dependency) + +## Step 3: Assess Sophistication - The Verification Ladder + +### Level 1: Basic OTP Verification +Developer says: "I need to send a code and verify it." +Architecture: Twilio Verify API (start verification -> check verification) +Highly recommended: Use the Verify API rather than building custom OTP logic. Verify provides: +- Automatic code generation, delivery, and expiry - Twilio built the custom logic for you +- Rate limiting (5 attempts, then locked) and replay attack protection +- Fraud Guard (AI-powered SMS pumping protection, continuously improving from feedback) +- No need to buy phone numbers - Verify uses its own managed sender pool with built-in resilience +- More options in the flow: multi-channel, fallback, custom codes +Channel selection by use case: +- Signup -> SMS (widest reach) or Email (lower friction) +- Login 2FA -> SMS (fastest) or Push (best UX) +- Password reset / account recovery -> Same flow: verify identity via OTP before allowing reset +Key gotcha: Wrong verification code returns status `pending`, valid=false - NOT an error. The 6th consecutive wrong attempt throws error 60202. +Relevant bundled skills: `twilio-verify-send-otp` + +### Level 2: Multi-Channel with Fallback +Developer says: "I want to try SMS first, then fall back to voice if it doesn't arrive." +Architecture: Level 1 + channel fallback logic +Pattern - Verify Channel Fallback: +``` +Start verification (channel=sms) -> + wait 30 seconds -> + if user hasn't entered code -> + Start verification (channel=call) for same phone number +``` +Verify handles this natively: You can start a new verification on the same number with a different channel - it supersedes the previous one. +Channel priority recommendation: +1. Push (if user has your app - zero friction, one-tap) +2. SMS (universal, fast) +3. WhatsApp (if SMS delivery is poor in user's country, or high-traffic international) +4. Voice (accessibility fallback - automated call reads code) +5. Email (if no phone number available) +Relevant bundled skills: Same as Level 1 - fallback is logic you build around the Verify API + +### Level 3: Risk-Adaptive Verification +Developer says: "I want to check fraud risk before sending a code, and adjust the verification method based on risk." +Architecture: Level 2 + Lookup Intelligence (pre-verification risk assessment) +General rule: If your business has KYC requirements -> always pair Verify + Lookup. +Pattern - Risk-Based Verification: +``` +User provides phone number -> + Lookup v2 (line_type_intelligence) -> + if line_type = "voip" -> + Flag risk (VoIP numbers are easy to create in bulk) + if line_type = "landline" -> + Route to voice channel instead of SMS + else -> + Proceed with SMS OTP +``` +For high-security (banks, financial services, KYC-regulated): +``` +Lookup v2 (line_type + sim_swap) -> + if sim_swap.last_sim_swap.swapped_in_period = true -> + Block SMS, require Push or TOTP or in-person verification +``` +Lookup Intelligence packages: +- Line Type Intelligence: Is this a mobile, landline, or VoIP number? VoIP = higher risk. This is the bare minimum for risk-based verification. +- SIM Swap: Has this number recently changed SIM cards? Recent swap = high risk. Use for banks and KYC-regulated businesses. +- SMS Pumping Risk: Is this number associated with SMS traffic pumping? Score 0-100. +- Caller Name (CNAM): Who is this number registered to? Match against provided name. +- Identity Match: Does the name + address + DOB match public records? +Key decision: Which Lookup packages to use? Each has a per-lookup cost. Recommend: +- Minimum (all production apps): Line Type Intelligence +- KYC / financial: Line Type + SIM Swap +- High-security: Line Type + SIM Swap + Identity Match +Relevant bundled skills: + `twilio-lookup-phone-intelligence` + +### Cost Optimization by Geography +OTP delivery cost varies dramatically by country. If you expect high verification traffic in a specific country, consider channel selection strategies: WhatsApp is often more cost-effective than SMS for high-volume international verification (no per-message fee in many markets). + +## Decision Rules + +### Verify API vs Programmable Messaging API - Verify Highly Recommended +- Developers frequently try to implement OTP manually using the Programmable Messaging API (generate random code -> send via API -> store in DB -> compare) +- Twilio Verify is a fully managed user authentication solution: automatic code generation and validation, retries, expiry, replay attack protection, Fraud Guard, multi-channel delivery (SMS, WhatsApp, RCS, Voice, Email), and per-verification analytics +- Pricing: [Per confirmed verification + channel fee](https://www.twilio.com/en-us/verify/pricing) vs per-message +- Programmable Messaging API only recommended when you need full control over message content or custom routing logic + +### Fraud Guard and SMS Pumping Protection - Always Enable +- Always recommend enabling Fraud Guard on every Verify Service (included, no extra cost) +- Always recommend SMS pumping protection for any verification flow +- SMS pumping fraud can cost $10,000+ in a single attack - these protections are non-negotiable +- Enable in Console: Verify Service -> Fraud Guard -> Enable + +### When to Use Lookup BEFORE Verify +- Recommended for signup (validate the number is real before sending a code) +- Recommended for high-value transactions (check line type; add SIM swap for KYC businesses) +- Optional for routine 2FA (if you trust the number from prior verification) + +## Output Format + +After qualifying the developer, recommend: + +``` +Recommended Architecture: [Brief plain-language description of the recommended approach - e.g., "SMS OTP via Twilio Verify with Lookup line type intelligence for pre-verification fraud screening."] + +Reference Skills: +- twilio-verify-send-otp (always - core verification) +- twilio-lookup-phone-intelligence (if Level 3+ - fraud risk assessment) +- twilio-sms-send-message (if account admin notifications) +- twilio-sendgrid-email-send (if password reset emails or account admin - recommended) + +Setup Skills: +- twilio-account-setup - if developer needs help with credentials or account structure +- twilio-iam-auth-setup - if developer asks about API key scoping or security + +Guardrail Skills: +- twilio-security-hardening (always - credential management, never expose Verify Service SID) +- twilio-reliability-patterns (retry logic for verification delivery) +``` diff --git a/plugins/twilio/skills/twilio-identity-verification-advisor/assets/icon-large.png b/plugins/twilio/skills/twilio-identity-verification-advisor/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 zcmaKScT`i)^LFSEdRKu+=tvb1f`kr|NEHx4iXbhNPz6GhVnB*O=q*N?A_~$$sx+k< z1eD%;lP(}4{w|;MegA#`$jLc#_wLTl%-wmOoryIv(4hxk1A{;ydL%;27z83Q0Dk(^ zlz_yuJGvYAqwzvm_<}%^{Fgrx%xhJD5QrCq)KWLa;D5fH@t*1#j-FHY+xbj4U_#e+ zd#qVnp45$Bny0;wS)4m!gIZ6!!kC<@asF2vNrj3GnG9(d=LpU&Ck#ZnzSI84w6f?& zoKY}!^GKNN;nd0QoI1kYKXrq;t?Ycw+(vLBwtj~6qoVHwd-zQ9t52p+TT}m0hR>i{ zLKF}FU~3i?z``ya9x^=qF$)smL+xX}i^ZR-LnO58KvC&(*bXGon7M~ewQK(+LHlI| zMTI)k2z@vw_=X}?RBAIHU!YhyoqtPrt2q?&mO|uB5owff+qTe%F|RCna!8>|5-Wij zTXUr>GwOq=7A<%!9LCf~*Lf^tuMXwmWg}vf`}`^A4O)i_w+X`J&`|EOOfG8^!~-K3 ztL8!4Yopc&x1vj#w4St|qaFgXJiZKBkmvmbXz@5HhFn{YMg(t=EFD4__gY7!fSB)f z+g>4?$kePGz2H(oTA{`?WdFV(MqLEYfHOLWv^O~j!mSBR65EDAtEn8<6s$z#vMJ6y*b&`ABmMk5y`ad<3!38-CVNLVlQ8Bp3|x1^I$e|BPV4cgSU_GF4MxMr!PN zUdLGMGOdv`Qo%?q7~3&%bHFZzFhSO%v$bM1g-gr#^2gS&OQ^LA+;(Rsf0FO6lVSwp z=XdK_v$bTdY?qA;=a`HxB9D^gYwDGw;u?$92Q++Ah(>^cVQn3Z-W`y9Oqv_lA`jS^ zRv<}{D&PiR>o{rF%@9w9sA_0a?-3;bA+Z9FSs`gtp32yPdo&QH;g5l5zCZ#5oN{15$ zUTYX+HG&zorK>v3OP>T%g4dXV*yD*rRXsL{CO*J^2@HD0E`n$OXqAF1EM;moggArR2h5F~Fw+Nn6rSyB-PDGO4taQN8QDB22FgC0>2P{@@28(Orcxb&$}DBx0(^AbR% zM01jlTt0(qwoxVr*%a%JEW=pkNDN>Ulyr}g^+C2&9*Si+jSKXT%XQY*tHYw#*@qUU zF)7>Ayq2fo_konJNY)Hgm46h>@MS5@BDnyGP)L)|t)GlOQa^wQ$E#tF`XKFxdW{w; zE=j4t)7@!a3?X1L`U$eCqqk2H>N*WNRa%J)PRS2){4Joc@y#(wI*?@H5EP!!3eJsRbn+V0F4j%eB?RSFD-G>D2YQ=glHQ5B+Lv zX5F}$Q;V=Bi5sEh+SR&dr%;@f8F`f?-&=cf^%jcHHExhQTPu1~G3EmN_!Pt6Z_qlC zrA+WsQnh5CR|LK0tC6^ap0!f}egcwOSX=gnCGgd0EoubPFb^w{-*imA$$*S04qhli z-N`??!C7)LcS}3bxR(i=Hj7f4jxTmwNVBQ@RQuDbk7EIY`;4ona*I5onv39@q#Rq^ zFoRhrdv-xoyHW^_>U*C(i^ebxaKP|x?D2`*p=y!6Kt!av}+JKh`_VIgdDhv&L9uy@5Yqqb} z1+8L>xfYhAc{nYgm`qwnr5sAQyjGhw2Vdw9Y@&lpVI5y1>PsQplUh*^a?K5foitC@YG0R|;CW z+Sc;iK{56@o2IX{HpLaRiHtD5G>?qh?sv;)uIQuZTGqbOA^*y6t+g*^Yx>ez%Dn=; z5Y2I@v1xrV+E3RejO~5$mTl1}yFJZoM`$FNu3p)maXx7%D6rc~sDzKA-4gr#D^r8| z`1MgbCyieboX-fL0g~= zd({0CcH!U1r)zswC2Bnq+%s$WRt3j2b58u>kVRLtIBVH6Til)ftNB6Y=YQgf!ZJA2b56c|V7@+AajMimgT zCSVOwqLZbO9Fk8!obZw(uJH)A#$#g{uiU|)!=|4?$44*(R#J10DlowMDrC<>Tln=G z_+p9%=v*WR2bw;j9zdMqW>;SnAdO#F!F}Jm-*s2%G2VTv#YvoTxUCr^hE?$%^G~_W zM+iW8>NdiUDGKZO1t*^#@hb9aP(~C@UX0f9%iJ5Q>^_F=CTE~Nh~zk+5Kn#~rxJU# z^snDJgs~pUpMbvDh^-szH>ZyQ$=@EN6qw%38}d{NWYc`n)TH)stIxZc!swXFj5bf~ z{K_Eb@7D08O1%nom4_xFdJ6+2=jzdE%{A-4cl{&v&Q8Ad z4yuBB%4pwCyUo@7jL~8%udekHT19)1Og|(dk)M4T{$`X5gHA@TpZWF>trd@Utq)0a z%v-B#$F3H>BNz*YI^CBGNZr6FWOGoX`6df!K)WR8ue7*6jO)Ld@Q?{5*`tu(nPCLk zX!Vr&Bt6Mlaz;xrbuLY}fB8C=F<51ouP9(z)jaYYY+z=lh!9ief3b3B3a2SB_dBVM zCmHo)Y~n7AwJ)?3IGxMtn4crAIWSy@4nv?%9*InOvpKUb0;ws-vTW6Mu_0q^C@A|@ zsPB}KvghxoVgohwu%d&!U?zQDOy`^crxSQTO@0rWqjshLlSK3mOZw}It36HfO2W4| z3|yX7;@sT&o!y{s;OkO*hj|Zv+alpF>qkN=(UHnn{wZ=c$TrNbUD7>t2_jV>!lqf%A@!rEt-xjC0Y?=DL9XF>R&2 zxvsR%j`}su#}&dCw5J0uHU(o;DmTmj;yf3TxoX^uyE-1t$=ZUos73GD_J8_;o+U%p z?}~S8f3D*KA2@&FhU(<78ui*|7AwflLoO6l2tPtKSr_EeIo;d(jLY+>?|yK|-NtQQHYxX^ zAHNdzTalOv_E@$!C%>l+AG$5J7AXT|%$RbZ2|HM~UL`u&-DESnpDd%z%~n(483UW= z2j8$GIrZ6YT;F6B*5@NKrNyEg!~HP!F3;GJVaj5;L(vbl3*JYWNNy??g;dmdM`Ft~ z4{&!?o*0Sr@jn`*J^2FGZm1)Xh?T3DgF&efd zv=E!zgbUtdO(a);uhy)hiPCzW)ytb)M_{Mq4@>NBtX?0`YP23+ub*Mcx{;o;lCo&B zw1zXvmPm`DEzC+nja@b`MfqXQqic~~4-e(Lub2t`Wh+w_>}_J4@RiDT;qp?uoM845 zE4?+FK|{>ieF;D>DhvVp_&{DGImZ5~^eqO>z1!vcF#S-GTQ^tG;Osh*=$^E>S73#V z4!`z?uUvR2rSKf8@WI(&_iJ;ORdES+XN)pvvUfd|*KHoKT8G>zXC{`m@Z5lNO;0F~ z)U{th`=8yc*2OUcgA#Fo4bybL?m0(|1dcRmd!{n}8EkeL%IK;*zZ$=^PyGpqY)*r5 z{O1pLZu?j@kUg4=ZulsoUW6@Y`iiAC`9!04K3YffAx-hv`KrcLIP0F>KX$M)Ts$%M zbI#X|Vt zLT#u|j?lvE23(p|*OqTK&!2^R3u#X^lN1HYuQNbPs=vwSK_@D&6AoZW7gc1<;iOBr!>r>{;3{JS&~+~hW;a^@CH^zG;KDQFRG5@ z7G_vJkEA$iWaCcLpn*mjOOk^A12+9R5eX!Omfm`yH&v{L9@S=%G-97+gW@t`HB`Wi z)$q_JI<{Fd`sSn6$VBjcdv=U)?HD$Wn{-IfgOjw8x}xe1#ht-KSdX!!LX5ezRnlIC zQYfg&_HsNtut!aVDWU%#jExxZcXIezxtKS{B&A5T4qVD*{SRv?K59b{(2V{{=3Fz_ z`My5SJz)3bYdXbWY$26-_2Gjp5c>tcMG-(RHM6HCzhK=hxkHd7Sv2wkI-Dg~D`Z?d zyd_S@vaSkQp@a|WT{_5p=j8;9ApoKFriWftt6cU?{qG=sx{@4zXq?UI%AKs=)6bsEZJnN(oo<%V?~?Bj7JU31@55SN z=I?lXOM>cBne0J`l{$>w`teyIJ7>Eh)z`{0k<(HJv|1o7*vmRTG;j__VIDK~IX&sM ze)JnfGhw11ww+9P&;mG3=;~hQ68HP;A*R9VA2)#{MR(y*;C5QXWHt-^T8V${Ki648 zW(qE@j|+-pq+oKYPP;%e7XGR_ajnPH%NC_8U93Nm>et2OQp?Og_X>LdHrP}1b{u@~ z>M+wF)qcT!ra^JdYm2{cMvNAk01=1kC^3;NGXY$|9vf5q4wWImM-Yt{nm2@Xhc9tE z_nCH$*e5TgQVfYR?{jy~Y1QbQ70{g0{+I(jjk^4n+yD(dYHXZ9t*%o(0DlfcEz3#W z%Z_P&BI!=zK*pqh^aIVzRi1xlizBqC+A2+lP&6YdJ)oK1{Ij;N6of&>S8)QRYIdti^jt$oz-$0(F z@4Ana+~G%zpf4Pnhq?f6PbgA<`nLJAUJK)9N-x~ z=O3>27aaJ>rXh<3f#}=RNRqV^z)Pil^-99TQou5-FD)PmiPr?aCM?cw=^l&*s>Kc# zhGM@1mp;_cTlror8VG$6JeJ9r_5UaCD)a(?2$M!My~1xq*0m;n^z!GK2Kt_7hl>lh+m(2@x;nQe`BLaUlU-JhtI~yfb2qoT$03Ilm348CWpi*|&Fo-JYcTKDg;sLi z>n-%!(b;;fG^QedfLnv@c?Hdrs1Z`uYI60QsqH7$@}q*uNnSEqtMP!>b|F!$g})9I z&|Eb}Yy+AQ&~)(A^>p$>Ts!SLfDFm^Y^D;go!glGs7*o$asqgi5QElUnNk%d~W4HbY>dET2{ru)Jfh;54x5EE?<7%NYeXi>T46 zc1X6Z4J}2hv0hMs3^E53YJnpRYd0DsXFqJ|Sy1Eo8b(55Cu1%!y0#$^6hiV;k|EGi zIn9NOiEJOg25_qZ`VIr#mwbe>%;T~*&D!Qdn~>srOAMzLFooOxazg1i#|XL3KFwJ_ zi+QI10m0%B<$m3wz`;*eU(gV%UFB%V3kkGsP$8@{> zp6@y~%Bn7eBCHI-3Sm4ROyrU`SPrr+E5G0LWJTTUtD%3}``*`romdd2^l%CNB}m)j z3>t_KJmJy9C`Kpmzy!wR^8{xJbNas%*0_b|J-@t5apTI+fHV8c&?iAbTW;#q#es3#0^m+(e!5DkGjrQND4a5ghgyKY76tsZ_ zU2!e|CaH!9&sQ_6jL6U51!OOVoPKc%=fCp430Z_=w&uD}EzKf$NT(ksM3`j~EXAjP z9mA0EdrEPMQ-Q*oxx03*`^O3M|&&f7&7&Q~~j z=KVG%zPTJ^@B1FdcKiJj+mJttO~bOpG#dt>qMkz2{uJgT#2{2f3Vf);)BQeW`OU?COQ+%hlmK=h6S2bAf z6Ahk|Siog@dyVMz<3}N<;kglt%PaEIxQZKa0-M3c+LL+@9&d$S!I=>`)44|%>;(z# zUl#rBbI&?N*}Q+QCjfkc6=QAnO;EoZYyVq}qx;gen#XG#WY#PO<$(t8wF?sJNGS8Y zzSxD`xL!N}>^BrB>c-_oUe0=tQ?9k1n^UT(^E|W;?T1FGau#5f+qQ#SR<`4WVf!vZr#3*61jpp(!!LPV z7c22Lr(B$Nx$Ky{S~7ezO5oQ%(^e31yidTLbi#L}y8gGv!5K1+-x9fW|7grH0OH)?D+TbrL@Vy+8&9aR6$F;N+SwA|9q zcG-Gr;Q($RLL`|T+#V0JKa!A`OTV?@S^E=(EDNp%cNWoJUT{YDXHZEc zEn63Am1v~8H&C#{RDR zm+@OlDd3Du$*MmB{OSBK%xY(dZsWy@##GFW%L+v6RMK4z{n_u|m)y^(67~%|K(eL~ z%pl=^nDLdw3rF_Y?>>&QkH2AV-mzcML#^m~h&oW&TtBru9SD3{dz7d2Dn{ z;TNC(#7kb|buo#-t6i%m($fVErsIn3R$yD{VY=(7%L7yyX2P29!j@IN` zD$r4~QKJHFwml5w`)s>E`Q4Ka!D>$X>Qc#-1GZr|}x0ma3@|I8GC+V@%pTRqwy{QPUucUQmP20>FMLA`ryFw$R$2*^d z+<~Ib$FCHVL3lMIkN(+jwOMo%x&X+%1An7x%m3(vd|q`{ixnuR0EeLV-m=7tl?UWf zBUPwXgr-~swT-V^5-Xx_i(JFI(`x)_OerIA=wHJRL(nRRX0K3BO8eCgQfzDY0ZDuR z^wD{M8&o&#Uol!o;9+pC8hnA9WSlqQE*I$ho^!mY{_-0_{uI4KJ@xPHhF{N{=`SIs zo%me9sk0e%^4E^>;od@lQ+FK;1d}!_V!4^99BygI2UR%b(GXP84BmB#@;cw?aGk9e zyJ2sU=xjNJxs!zfd)j9k*|5eeqe;D^WNWiaO4)Kxz=_b$MV5gHD}ITb*uhDMRNHh_12!_A8bv(?mtH5mUHt!pk zRAh!!jL#tvnTV#032>O^K^ABH4Lr;2Yx(&{4#$E5fdVVXF;9#=)XW8IVglR8A;P%T z2`S&~C>kUO^HptDXGNh_DC&anI zH>LY!DAAj2erR}N8{73*AzShrTB?GhT2gW{)(b4kaetN%LtryZp&O=8EkHf}`Hr(m zc7g5WwB-Gk*a64IpcNl^o7>dAyAoKz;3ru!+rvxbkz?7$!N$ExG-zsX-N=@l`#Vim zT|*^aak==1fSf8$c>nHVLBnQ{=lBO@gPuD3l3L+(BY3P53#WcwVWd$yG0G(C*HM66 z9)?AMPEGF9ab@ydsSeR7J}KulxrjyNfGlWex}X?x@HRID&)+_inz#_8BbXuykfgls z&?kSuKl<6Vt6MJ!EZc_yt2mU1-%!Y=@8SL|mKC&oJ>#*bbGk9Sq3-3|)V;X88^K50 zX}>t$Wzq>aP`!ASgB*TSWvAonHkdT@=bl1K5WdY;!=G1=?w^C--()_PIcgnEnuXnldq^i|EmsLG`5h87-14JA4iCN%M6dqz(PxkGoAg`vUjf z_+h9eD1dGT$+H}20m78K$59Ru?|43QeQU#KGxoBM2i&ZVvgoU&hpONj+i0Uja~L_a zSfv*MHWFU+k?Ys33NI}KJ{u>7c|%8&8uNrvUD@~hdKkQ5Re`wcvR8cWqNa+X{3XO&PlcXHk{vdP64plF#>XE zW24E~uRHLn$kqlH(f&cav5QRO!nY7jUBXClC?I)U`HK%DBmwCLs4?bm#h1Eeii!2YG8}w;;pw!sz@c`?Lz7iFcr-`>E}LG>Xrk?u7u9l zUZmw>V#casx8=pKKSYi+`1#^*?WpZmNlI$7dWqvfpQ{A{f~?Mr#umMBvfZf?^#J6O zfLxY^ZAc8qc5lO4hRrk4ZHSzO$sm03YPK^uvjqHT#4mPPd>7V!z(LeU(VT_dix;=S>_@}j5s=vu;z{_CaHlaX%cjk_uE_%&vr zWZvQOKIyV%{?@1^uC$5BMBId8tqijVxrRc$x1Nkio2`sA-L0Vu z9#J zm0@WeYG$2d+PnG+<{P#4ly4!&h2VX8lV4b!mhFT^jsa@Bw>&>ZodAk-Q=* z^V1Dr;GmHjk_yg?T>;u8qDiAnXuu*a$g3$*B3XYm7fkPvA3>&D6RoIxvncbtu0`C( z_(Vu_{M|>ie}CTLLbpNom1H_X>09czk1A^i2uq;zgikEliP{c+nAiC@se-Bl|64cL z2*$%}cTU$vWvz?ohVxSkb$TxWGN{cukGbC6Sc#6x??;k6>I+ZemNlFY9*gY8D@U5E z$OLS#f}KfwV)3%%o@soptZ|MSmmM`)M+P=@Ykm`?B5IX-fGs)@uvgF+6lI;luXeC1 zyq^9NF70jJClxWLzfQ?UB+-PTyTIxqkC?DNg|77XB07*Z$>*qO zujq4$EdCij>SbCj@r`>d;T=dxUYiYbo0St$vz=EY?#uVX`H)1l1%U)6bsi|@%~D_g zySF>c@a~!R2Vj8``l!?=kSN4dPmbBHb^p^E!U@ VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-identity-verification-advisor/assets/icon-small.png b/plugins/twilio/skills/twilio-identity-verification-advisor/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Look up phone number intelligence via Twilio Lookup v2 API. Covers number + validation, line type detection (mobile/landline/VoIP), SIM swap detection, + caller name, identity match, and SMS pumping risk scoring. Use this skill + to validate numbers or assess fraud risk before sending messages or calls. +--- + +## Overview + +Twilio Lookup validates phone numbers and provides optional intelligence packages. Basic validation is free; data packages (line type, SIM swap, etc.) are paid per lookup. + +--- + +## Prerequisites + +- Twilio account (free trial works for basic lookups) + - New to Twilio? See `twilio-account-setup` +- Environment variables: + - `TWILIO_ACCOUNT_SID` + - `TWILIO_AUTH_TOKEN` + - See `twilio-iam-auth-setup` for credential setup and best practices +- SDK: `pip install twilio` / `npm install twilio` + +--- + +## Quickstart + +Python +```python +import os +from twilio.rest import Client + +client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"]) + +phone = client.lookups.v2.phone_numbers("+15108675310").fetch() + +print(phone.valid) # True / False +print(phone.phone_number) # +15108675310 (E.164) +print(phone.national_format) # (510) 867-5310 +print(phone.country_code) # US +``` + +Node.js +```node +const twilio = require("twilio"); +const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); + +const phone = await client.lookups.v2.phoneNumbers("+15108675310").fetch(); + +console.log(phone.valid); +console.log(phone.phoneNumber); +console.log(phone.nationalFormat); +``` + +If `valid` is `false`, check `phone.validationErrors` for the reason. + +--- + +## Key Patterns + +### Line Type Intelligence (paid) + +Identifies mobile, landline, VoIP, toll-free, etc. + +Python +```python +phone = client.lookups.v2.phone_numbers("+15108675310").fetch( + fields="line_type_intelligence" +) +print(phone.line_type_intelligence) +# {'type': 'mobile', 'carrier_name': 'T-Mobile USA', ...} +``` + +Node.js +```node +const phone = await client.lookups.v2.phoneNumbers("+15108675310").fetch({ + fields: "line_type_intelligence", +}); +console.log(phone.lineTypeIntelligence); +``` + +Line types: `mobile`, `landline`, `voip`, `toll-free`, `fixedVoip`, `nonFixedVoip`, `personal`, `payphone`, `unknown` + +### Multiple Packages in One Request + +Python +```python +phone = client.lookups.v2.phone_numbers("+15108675310").fetch( + fields="line_type_intelligence,sim_swap,caller_name" +) +``` + +Node.js +```node +const phone = await client.lookups.v2.phoneNumbers("+15108675310").fetch({ + fields: "line_type_intelligence,sim_swap,caller_name", +}); +``` + +### Validate Before Sending + +Python +```python +phone = client.lookups.v2.phone_numbers("+invalid").fetch() +if not phone.valid: + print(f"Invalid number: {phone.validation_errors}") + # Handle gracefully - do not attempt to send +``` + +Node.js +```node +const phone = await client.lookups.v2.phoneNumbers("+invalid").fetch(); +if (!phone.valid) { + console.log("Invalid number:", phone.validationErrors); +} +``` + +### Available Data Packages + +| Package | `fields` value | Coverage | Use case | +|---------|---------------|----------|----------| +| Line Type Intelligence | `line_type_intelligence` | Worldwide | Route by line type; block VoIP | +| Caller Name | `caller_name` | US only | Show caller ID | +| SIM Swap | `sim_swap` | Select regions | Fraud detection | +| Identity Match | `identity_match` | Select regions | Verify ownership | +| SMS Pumping Risk | `sms_pumping_risk` | Worldwide | Fraud prevention | +| Reassigned Number | `reassigned_number` | US only | Check if recycled | + +--- + +## Common Errors + +| Code | Meaning | Fix | +|------|---------|-----| +| 20404 | Phone number not found | Number may be invalid or unsupported format | +| 60601 | Data package not available for this region | Check regional coverage before requesting package | + +--- + +## CANNOT + +- Cannot look up by name or address - Input is always a phone number. No reverse search. +- Cannot get caller_name for non-US numbers - Returns error 60600 (out of coverage). +- Cannot detect conditional call forwarding - Only unconditional forwarding is detected, and only for UK carriers. +- Cannot guarantee SIM swap data without carrier registration - Returns error 60606 until carrier approval is in place. +- Cannot use Reassigned Number outside the US - US-only dataset with monthly update cadence. +- Cannot get real-time SMS pumping scores - Scores are statistical models, not live traffic analysis. +- Cannot write data - Lookup v2 is read-only (GET only). The one exception is Line Type Override (POST/DELETE). +- Cannot batch multiple phone numbers in one request - One number per API call. Loop for bulk lookups. +- Cannot avoid billing on caller_name requests that return no data - Billed per request regardless of whether data is returned. +- Cannot use Phone Number Quality Score or Pre-fill without provisioning - Returns 60606. Contact Twilio sales to enable. +- Cannot guarantee deliverability from a valid lookup - A valid number may still be unreachable (carrier issues, ported, etc.) + +--- + +## Next Steps + +- Send SMS after validation: `twilio-sms-send-message` +- Send OTP after validation: `twilio-verify-send-otp` diff --git a/plugins/twilio/skills/twilio-lookup-phone-intelligence/assets/icon-large.png b/plugins/twilio/skills/twilio-lookup-phone-intelligence/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-lookup-phone-intelligence/assets/icon-small.png b/plugins/twilio/skills/twilio-lookup-phone-intelligence/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Planning skill for marketing and promotional messaging. Use when a + developer is figuring out how to set up or architect a marketing + campaign on Twilio - channel selection, compliance readiness, + audience size, geography, and delivery tracking. Handles open-ended + requests like "how do I set up a WhatsApp marketing campaign" or + "what's the best way to run promotional SMS." Skip this skill when + the developer already knows what they want and is asking for API + specs or implementation details. +tier: discover +--- + +## Role + +You are a Marketing & Promotions Architecture Advisor. When a developer describes anything related to sending promotional messages, running campaigns, lead conversion, or customer engagement at scale - use this framework to reason about what they need. + +## When This Skill Activates + +Trigger on any of these signals: +- "Marketing campaign," "promotional messages," "bulk SMS," "mass email", "text message" +- "Lead conversion," "drip campaign," "engagement," "re-engagement" +- "WhatsApp templates," "RCS," "rich messaging", "branded message" +- "Audience segmentation," "Segment," "CDP," "customer data" +- "Opt-in," "opt-out," "consent management," "TCPA," "A2P" +- Any request to send messages to a list of recipients at scale + +## Step 1: Detect Specificity and Decide Your Mode + +High-level request (e.g., "I want to send promotional messages to my customers"): +-> DISCOVERY MODE. Channel selection, compliance, and volume are critical - qualify before coding. + +> Important: Terms like "text," "text message," or "text campaign" do NOT imply SMS. They could mean SMS, WhatsApp, or RCS. Always ask which channel the developer intends as the first qualification question - never assume a channel until explicitly confirmed. + +Mid-level request (e.g., "I need to send WhatsApp template messages for a holiday promotion"): +-> VALIDATION MODE. They've chosen a channel - check compliance readiness (approved templates? sender registration?), volume expectations, and tracking needs. + +Specific implementation request (e.g., "Send an SMS via Messaging Service with a StatusCallback"): +-> BUILD MODE. Proceed with the Product skill. Quick check: Are they US-based and need A2P 10DLC? Are they using a Messaging Service (recommended) or raw `from` number? + +## Step 2: Qualify Intent - The 6 Essential Questions + +1. What are you promoting? + - Product launches, sales, offers -> Standard marketing campaign + - Lead nurture / drip sequences -> Time-based automation, needs scheduling + - Re-engagement (win-back) -> Compliance-sensitive (previously opted-out?) + - Event-driven (cart abandonment, browse behavior) -> Needs real-time triggers, likely Segment integration + +2. Which channel(s)? + > Channel selection: If the developer hasn't confirmed a specific channel, invoke `twilio-messaging-channel-advisor` - it qualifies content type, geography, brand requirements, and use case to recommend the right channel mix. "Text," "text message," or "text campaign" defaults to SMS but may not be optimal - the channel advisor will surface better alternatives (RCS for rich/branded US sends, WhatsApp for LATAM/APAC) before committing to an architecture. + + Quick reference for confirmed channels: + - SMS/MMS -> Highest open rates (98%), immediate. Best for time-sensitive offers. US requires A2P 10DLC compliance. + - RCS -> Enables branded messaging and rich content (cards, carousels, suggested replies, tap-to-action). Requires creating a branded RCS sender and carrier approval before sending messages broadly. Can send to allowlisted test devices. Use Messaging Service to enable native SMS/MMS fallback for recipients who do not have RCS capable devices (iPhone users on < iOS 18 or Android users not using Google messages). + - Email -> Highest volume capacity, lowest per-message cost. Best for rich content (images, HTML). Use `twilio-email-send` (Twilio Account SID + Auth Token, comms.twilio.com) or `twilio-sendgrid-email-send` (SendGrid API key, SG.-prefix). + - WhatsApp -> Dominant internationally (India, Brazil, Europe). Requires pre-approved templates for outbound. 24-hour service window for free-form replies. + - Multi-channel -> Most campaigns should use 2+ channels. Email for initial reach, SMS for urgency, WhatsApp for international. + +3. What's your audience size and send frequency? + - < 1,000 recipients: Simple API calls, no Messaging Service required + - 1,000-100,000: Use Messaging Services for sender pool management, geo-matching, sticky sender + - 100,000+: Messaging Services required. Rate limiting critical. Expect 429 errors - implement exponential backoff with ±10% jitter. + +4. What geography? + - US-only -> A2P 10DLC registration required for SMS. Toll-free verification for lower volume. + - Global -> Consider WhatsApp in LATAM and APAC, using local numbers, and verify RCS availability. Use Geomatch within Messaging Services for simplified routing. + +5. Do you have a CDP or CRM? + - Segment -> Native integration for audience building + event triggers + Reverse ETL + - Salesforce/HubSpot -> Webhook-based integration via Twilio Functions + - Custom database -> Direct API calls with your own audience management + - None -> Start simple - CSV upload or direct API calls + +6. How do you track success? + - Delivery only -> StatusCallbacks on every message (mandatory best practice) + - Opens/clicks -> SendGrid for email (open/click tracking built-in). SMS link shortening + tracking via Messaging Services. + - Conversions -> Segment for attribution, event tracking through the funnel + +## Step 3: Assess Sophistication - The Campaign Ladder + +### Level 1: Single-Channel Blast +Developer says: "I need to send a promotional SMS/email to a list." +Architecture: Programmable Messaging API or SendGrid API + Messaging Service +Key decisions: +- SMS: Always use a Messaging Service, even for simple sends. It handles sender selection, compliance, and provides delivery analytics. +- Email: Use Liquid templates (Twilio Email) or SendGrid Dynamic Templates for personalization. Don't hard-code HTML. +- Track every message: Include StatusCallback URL on every send. +Relevant bundled skills: `twilio-sms-send-message` and/or `twilio-email-send` (Account SID + Auth Token -> comms.twilio.com) or `twilio-sendgrid-email-send` (SendGrid API key, SG.-prefix), `twilio-messaging-services` + +### Level 2: Multi-Channel Campaign +Developer says: "I want to reach customers on their preferred channel." +Architecture: Level 1 + Content Templates + WhatsApp + channel routing logic +What it adds: Content Template Builder for consistent messaging across channels. WhatsApp templates (require Meta approval - plan 24-48 hours). Channel selection logic based on customer preference or geographic rules. +Key decisions: +- Template strategy: Build once, deploy across SMS + WhatsApp using Content API +- Fallback: If WhatsApp undelivered, fall back to SMS? (Design the retry chain) +- Personalization: Use template variables for customer name, order details, offer codes +Relevant bundled skills: + `twilio-whatsapp-send-message`, `twilio-whatsapp-manage-senders`, `twilio-content-template-builder` + +### Level 3: Data-Driven Engagement +Developer says: "I want to trigger messages based on customer behavior and segment audiences." +Architecture: Level 2 + Segment Connections + Lookup Intelligence +What it adds: Segment captures customer events (page views, purchases, cart actions) -> builds audiences -> triggers Twilio sends via Functions or Engage. Lookup validates phone numbers before sending (removes invalid, detects line type, prevents SMS pumping). +Key decisions: +- Segment source: Where do events originate? (Web, mobile app, backend, data warehouse) +- Trigger logic: Real-time (event-triggered) vs batch (scheduled audience sync) +- Reverse ETL: Push Segment audiences to Twilio for targeting, pull delivery data back to Segment for attribution +- Phone validation: Always validate before bulk sends - saves money and protects sender reputation +Relevant bundled skills: + `twilio-lookup-phone-intelligence` + +## Step 4: Qualify Context - Compliance + +This is non-negotiable. Compliance failures block sends. + +### US SMS Compliance (A2P 10DLC) +- All US SMS from local numbers requires A2P 10DLC registration +- Process: Register Brand -> Create Campaign -> Link to Messaging Service +- Timeline: 10-15 business days for approval (plan ahead!) +- Tier-based throughput: Sole proprietor gets very low throughput. Standard/high-volume requires verified brand. +- ISV note: ISVs commonly struggle with compliance - missing mandatory fields, submitting incorrect data. Automate validation of required fields. +- Alternative: Toll-free numbers for lower volume (faster verification, 3-5 days) +- Alternative: Short codes for highest throughput (expensive, 8-12 week provisioning) + +### WhatsApp Compliance +- Outbound requires pre-approved Message Templates (submitted to Meta) +- Free-form messages only within 24-hour service window after customer initiates +- Opt-in required before sending. WhatsApp enforces quality scoring - too many blocks = rate limited. + +### Email Compliance (CAN-SPAM, GDPR) +- Physical address required in every marketing email +- One-click unsubscribe required (SendGrid handles automatically) +- GDPR: Explicit consent required for EU recipients. Track consent timestamps. + +### Consent Management +- Implement opt-in/opt-out at the application level +- Store consent records with timestamp, channel, and method +- Honor opt-out within 10 business days (US) or immediately (best practice) +- Use `twilio-compliance-traffic` guardrail skill for detailed patterns + +Relevant bundled skills: `twilio-compliance-onboarding` (for US SMS) + +## Decision Rules + +### Channel Selection Framework +| Factor | SMS | Email | WhatsApp | +|--------|-----|-------|----------| +| Time-sensitive | ✅ Best | ❌ Slow open | ⚠️ Good if user is active | +| Rich content | ❌ Text + link | ✅ HTML, images | ✅ Media, buttons, cards | +| Cost per message | $$$ | $ | $$ | +| Compliance burden | High (A2P) | Medium (CAN-SPAM) | Medium (templates) | +| International | ⚠️ Expensive | ✅ Global | ✅ Dominant in many markets | +| Open rate | ~98% | ~20% | ~85% | + +### Messaging Services - Always Use Them +Even for simple sends. Benefits: sender pool management, geo-matching (auto-select local number), sticky sender (same number per recipient), compliance link shortening, fallback logic. + +### Rate Limiting +- High-volume sends WILL hit 429 errors. This is expected, not a bug. +- Implement exponential backoff with ±10% jitter in every dispatch loop. +- Messages Per Second limits vary by number type: local phone numbers (~1 SMS/sec), toll-free (~30/sec), short code (~100/sec), RCS (100 / sec). +- Use Messaging Services sender pool to multiply throughput across numbers. + +## Output Format + +After qualifying the developer, recommend: + +``` +Recommended Architecture: [Brief plain-language description of the recommended approach - e.g., "Single-channel WhatsApp campaign using pre-approved templates and a Messaging Service for delivery tracking."] + +Reference Skills: +- twilio-messaging-channel-advisor (if channel not yet confirmed - qualifies SMS vs RCS vs WhatsApp) +- twilio-sms-send-message (if SMS channel) +- twilio-rcs-messaging (if RCS channel) +- twilio-email-send (if email channel, Twilio creds - Account SID + Auth Token) or twilio-sendgrid-email-send (if SendGrid API key, SG.-prefix) +- twilio-whatsapp-send-message (if WhatsApp channel) +- twilio-whatsapp-manage-senders (if WhatsApp production) +- twilio-messaging-services (always for SMS/RCS at scale) +- twilio-compliance-onboarding (if US SMS) +- twilio-content-template-builder (if multi-channel templates) +- twilio-lookup-phone-intelligence (if bulk sends - validate first) + +Setup Skills: +- twilio-account-setup - if developer needs help with credentials or account structure +- twilio-iam-auth-setup - if developer asks about API key scoping or security +- twilio-numbers-senders - number type selection affects throughput and compliance timelines; use when developer needs to choose between local, toll-free, or short code + +Guardrail Skills: +- twilio-compliance-traffic (always for marketing) +- twilio-reliability-patterns (always for bulk sends - 429 backoff) +- twilio-security-hardening (credential management) +``` diff --git a/plugins/twilio/skills/twilio-marketing-promotions-advisor/assets/icon-large.png b/plugins/twilio/skills/twilio-marketing-promotions-advisor/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-marketing-promotions-advisor/assets/icon-small.png b/plugins/twilio/skills/twilio-marketing-promotions-advisor/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 zcmeHKg;&&3xBU$vAtfLTNOwpoEg;McDcvP7G)N2~rL=S-AvH8gNOwpJjC2Y}Nr=SI z15!`ld+YrXZ+&;&efK_R-8}2A8&R6-io}Gpga7~#D=R^@|FXtE;A8(SbAH>`0{|h1 z0|cV!VC4(|KG`{G-nzPTR1sqx-hTNbf}UW%D(!n@JM={cT439I;9;PbX#PG651AIU zh&u!amk3M@gTpBAgS0^Y&qIR9L$zLgv{sl^dE@Jg-rU4|m0nq}LwjJp`>!_OVC|Fu9rw}w_`49?>=~@1V$-Q7vxhBF%2=%osz?_XCsdw4!d+Ve zD;U9)lG?LTxAS?&f#$_BEkK37HWFT6n1sO^MVlHLMH>Z$?g}*TyiQzImKI@tG^Z_g zWR+;rMf1zzIZ#&y#f&Y6vGIZskZc4o1)@o_W|;vt*jp#Yi*=c&N9}9`I5e+IN+cvC zI7~@UP}A|1Gg9F|Kp?|K4*2IT|(OksWjt!WpAEZ)_UlV7IGfsX64L{}+P+s14vH=NJoL_pfux!>k(NcQL0 zScy2gd4O&($2j&t7uG!BzlyUIOo|WWGZ9u`Jx;`#1Q<7h&MfW^;=QvFo5$mkBmRgN zmxr4T(62si2KYmFSMPzbFx^}kz*yH~AL1=${2qDhNG$H>j)|hy_(so(>}fVgF6Fv4 zDJk*T<)10xS4K3-ztbepC76+q8*dut{)OfFkT&c<-fH~mDeozRf(7}DcX}4|^OUw- zqtya*gjHb_YcG8GvhmO%s5Qw8@Dho6P*pGFkn5T#7Xrvb?IR`Ov4UgCWBnUq3saMzk}1;#MUbM=J#km*5z`DBGUU&_mA*2FZ6hQS6R$tB&?g{Nl?5XYHob$y;>hv0|K69H8XW*wc2zTnq z>HpEEw!U?w?c>}|F9VDXRCQDT+&wb80A1q*&`tN#9AcNmbzw6_D2~iYd4oW*Z8} zuw&L^6N2clsIVp+6L<8*B#ExRqP$5woRaz_dEHW+5(0){0 zR-~>Rr!=ChQ#4YHT{Kk`QF zi{m4>jDIF8^95f~0A#;(ba;5scsLyOIju5TnpH{#VlVmB)tIL;<8_GW3x9!9mhH!1 zlNPz>SiE%(;@XqjQ=TbyKJU~EP`d5FGV3;DhFK=aNAy5o?+b$AiSYP!xqj8?Cn~z^ z{_JVm!IK2V{=?O^!S2<^lAFE5Z-+g2%6M{lXm}p-Q0v+1I+UrG4V4v_+3A(ea5sINZ4Ulu=Gx$@3Gxjq+AC$a)KTthLp*v?CfkeysW}6C+MMdEIus(-EW|Ri1$x`D;nP zCiaAO9X4A=Pui&$P;*WRbCFHaVyLzW|JkntcA**x0z+bbO_P`!WH94O6^ij3bF{L1 zXVG%*zI+@Hkgd{Ji>+Gbxmeh)V&pRNy7J2MIJ>vHcSHRlT+B7hYRpv-Gx-<@I%4M? zL2qj>d#_c`quv#0GweMyRv}ZtC9<8N7~CKCJ9a=JLQxDlR_SJ5K`N~(cVAX@2B7}b5RX1`6qA4SQ|z_ts-=B9x`9iT?c7=2q|0;C>tV>oaN-YR09w?<0%S?OzdN6XZL|4eUStZ!nGNldAVs z%=cq{csG2jt+17~{Ni?J-(v1-_fEh6AeuB)ITfPEuU9!UJR?=ULTAC)>^r_=p&F;6 zW;t>NS%P>wN;@s?dj1|Ju_Cc!4A5qLxpF1)b9dlge|S%Lf})S2Lc%@9eTRC7SFf~P zcwEA>-i=Isg~$K67}(DmrTUq+pl|H7hHx|Wx!!3)tdJlNr6=AdBWgFL_R(dFT+X=r znpc_U-{XtoCq_SxcI(KLwzloHEp&=q-VQpPP3hTLk5MAovqz-b++6lsT25%T?fBVV zg3-)KRxwopF>7Px{o8Q#TwP2PWM(~zaEtpAr#UUzxww|4x%anIEGFAlG z?=~H{GFH?4YPa++baLBS{hk#sCa~n*=5^VK+24hrBJM@>hNENqVq=#rS3b06`C3CoPvc5?bJd@rd_-ka?&?m*X2#BP12HjE0&nY*>L>&*1gHY|{5~BX5Avc6 zes~0XB)lx`$PLsv)L+}5@Q(HFK(j7ujBdZ_3gEi?cvrKVf-c3o?{NSA7Z4`w-O`j(wLyRB&{JDo2LN#C<($)7r?-wT|7_2; zXCh#X_wEaSoH@L{IhwB!A;M;T)v|Yc(BaO7leoMtg(sNtdp}Q%Hh5zvnn`VNT>oMC zNfJ+e=ac6X-&H~CtHa3wZ_74KPGfbr$w)agBKs{B$c?3cyklEZ{+7d!^&Tx$K862+ z+P{*y;3A$RK_$Suf|oiOw&td>qMP2%axqEt>kNdfSv-?@3|f@TbH^DlAh zhQ}-U6kqLXM5_$fc(om-!p^afAzmw|C9c8|Ihkw zb{%4E1%P|U%1}97!`>Xj9SDUY zX%hg7^gBDSnrlLKgci;Y$?fe;u6PAy;RN|5;0lg+j4_dF2=H|lE!TE`mT}KwmRpnB zz?LnZ-Jq5HCd)KV1tq)0!BuV8N*C@s=+(@R%md57i)I&SMf_s(o?wN=5jRDbm^?_* zn%&1Q_=QlBb33JFw1PPV5it4^!V2g5<^@$1aU!n_9kJ>5Gn9|EKp+mz`LGg`C>>o} zKu*LfQ|L>I&!1Hq<=flaCsuZGs;{5gRwFBcDL0|>H_PvLJhndyw9rjd_cgzy+VMKF#XXV}^xw@s#Kw7AXgUq0IL8Xv!d8 z32U~;CZ9#O#Wis^wjL~vB98!OKBNU#BzHaKCnjS#mzAbS3%oOxc7ESVLcl55+;lzc zt7Q@XVO0lfbwc@^wegrImzhcUkk9B>bzqZ!zBGE+D}+$-*Pp12>2Mx({5;yJS%g)7 z_3*s{wF?38cZG`r!#gbxhIS$%?aF2FR zuD;_6JWzI%&9fFW9nP25=&rD{`0drzJ27&Zye1|}OaDDFFeyozMKrV`?;VIXRA_-2 z0)$%Q6tF7j*<;jfoUSpQ$O*Li6E5$EpLzDLVK@`v&BiB8hBORW!@ZPX<}-9NIm&Nb!`x zxhF*xjplgMrmGWpL4XkAG#bKKzsB1=F2h*X0454I-UjkZ*|RQbz4Y1da( zDqe==`D`bO*ic&=yiFt@5D_Km^0_R!)JmDq9VMb`$L&Ff`oKE2?x5j zW<+SXv!GWP!S5gX+r$sR{HHnRT3O9o>pun&AE{Edq*NAL0-nj43_R2nkB+wEeAn*L z!NA_RDE?x4$h;Vp@W`j7@Fd$xCfHOYW?}fIQ-Z8Ka=%Hu)hhiKTgipBYgNi{CMkRC zmjRb&qMV^W&ise5FgbljTgJpCF2Q-XvwW(DqcQH{N$y(#V!(v<#)@Mrh5Ny0WT}E} z$7^)k4guy%V4^~8!F$yb&3*qtbiSkSCCoju)GogMIJA9$$StckGEeh?@pS+-h!%~P z;uA8;slqGJ;3Jc@w|&la=O{mr;Ng2--oF=d-L{e$2*LPrsr{kucVjuDz+8niTr7ra zr$ow5m_5D~+~YxA)?f?N)^0{hoCu{c8s0XVd|Pp9!lzC9f^ep5pr{hics!e~v!NS3 zlV0lcwtM)_rlI%`CYEiYt?sNUrtqv@r&z2da5U|=$Br@sUvfWcscV9PTC4h=#4&e_ zT|uDr?YBb2jTKM}{vj*4-=G#7x5l6`s~c#7Iu&aOXXOseztNB7kvC~SylvZ*=zSD) z52uEogmV5Pd6DfFcI}4`nWWZQ-=6A4*_(Ksfclv17OwDf%-{3UcN9!6ZuzfMcvo~BqKbas#zHsLb z4P7pJ)Zsd4!Z7UPs#JI@Tuk^ex5>d5#a!s3A}W0+n5y&g%_nzl2ISD`RZRSyY2>VZ zB}vQ5>EZq*Cq~pO&6BZQ(AIu2U(m{4^TsT%!OINkcy0z0YiO_Df2+2*Z*|%InRRmU zUOlB+YC{*b^x%VQ8Kde!zmlmBx^@Eim8HAIc~Ml9M`*u_g3X&LmJ4E9sFnMe;iNIm z?SXl4)zdwita7vPOu3W20RPz%3?h_%Q)cE;7*9ChJ%tJScFwS@_F-J``({4Rx88E1 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Planning skill that helps the developer pick the right Twilio + messaging channel - SMS, MMS, RCS, or WhatsApp - for a given use + case. Qualifies intent across content type, geography, use case + (marketing / notifications / OTP / support), cost model, and brand + presence. Use when the developer asks "which channel should I use", + "SMS vs RCS vs WhatsApp", mentions a country or region, asks about + branded messaging, rich content, or fallback - and proactively when + the developer says "send SMS" or "text" but the use case (rich + content, international reach, branded experience, marketing campaign, + transactional notification) would benefit from a different or + multi-channel approach. Also invoke alongside + twilio-marketing-promotions-advisor or + twilio-notifications-alerts-advisor whenever the developer has not + yet confirmed a specific channel. +tier: discover +--- + +## Role + +You are a Messaging Channel Advisor. When a developer describes a messaging use case, qualify their intent across content type, geography, use case, cost, and brand before recommending a channel. Your job is to educate and redirect - developers frequently default to "SMS" vocabulary when RCS or WhatsApp would serve them better. + +Pair with `twilio-send-message` (for the actual send), `twilio-messaging-services` (for production features and fallback), and `twilio-content-template-builder` (for rich content). + +--- + +## Qualifying Questions + +### 1. What content are you sending? +- Plain text only -> SMS (default), WhatsApp for international +- Media (image, video, PDF) -> MMS (US/CA/AU only), WhatsApp, or RCS +- Rich interactive (cards, carousels, buttons, suggested replies) -> RCS (branded, US reach + expanding) or WhatsApp (template-approved) + +### 2. Where are your recipients? +- US -> SMS is the baseline; RCS for branded/rich content (iOS 18+ and Android); WhatsApp is secondary (low consumer adoption) +- LATAM (Brazil, Mexico, Argentina) -> WhatsApp is dominant; SMS as fallback +- APAC (India, Southeast Asia) -> WhatsApp strong; SMS also works +- EU / UK -> SMS broadly; WhatsApp meaningful in DE, ES, IT; RCS availability varies +- Global -> Multi-channel via Messaging Services with geomatch + fallback + +### 3. What's the use case? +- Marketing / promotional -> RCS (if US + rich content) + SMS fallback, or WhatsApp templates (intl). See `twilio-marketing-promotions-advisor`. +- Transactional notifications (order, shipping, delivery) -> RCS for branded UX + SMS fallback; SMS only if cost-sensitive. See `twilio-notifications-alerts-advisor`. +- OTP / verification codes -> Prefer `twilio-verify-send-otp`. Verify handles rate limits, retries, and fraud protection. Works across SMS, WhatsApp, RCS, push, TOTP. +- Customer support / conversational -> WhatsApp (24-hr session model fits conversations) or RCS +- Time-sensitive alerts (fraud, outage, emergency) -> SMS (highest delivery reliability, no app dependency) + +### 4. What's your cost model tolerance? +- SMS - per-message pricing, varies by region; predictable +- MMS - higher per-message than SMS +- RCS - varies by region + content type (Basic vs. Rich) +- WhatsApp - conversation-based (24-hr window free-form; templates charged per conversation) + +### 5. Does brand presence matter? +- Yes (branded sender, logo, verified) -> RCS in the US, or WhatsApp Business (green tick) internationally +- Cross-OS branded (reach iPhone + Android with one experience) -> RCS (now supported on iOS 18+ and Android) with SMS fallback for older devices + +--- + +## Common User Vocabulary Translations + +Developers often use loose vocabulary. Translate before recommending. + +| User says | Often means | Likely best channel | +|-----------|-------------|---------------------| +| "Send an SMS" | Message to a phone | SMS - unless rich content, branded, or international | +| "Text message" | Same as SMS | SMS - educate if rich or branded needed | +| "Branded message" | Brand visible to user | RCS (US) or WhatsApp (intl) | +| "Rich message" | Cards / buttons / media | RCS or WhatsApp template | +| "Show my logo" | Branded sender | RCS (not a phone number feature) | +| "OTP" / "verification code" | Auth / 2FA | `twilio-verify-send-otp`, not raw messaging | +| "WhatsApp them" | Outbound to recipient | WhatsApp - check 24-hr session | +| "Reach iPhone and Android" | Cross-device parity | RCS with SMS fallback | +| "International" | Outside US | WhatsApp in LATAM/APAC; SMS elsewhere | +| "Bulk send" / "mass send" | Broadcast-style | Messaging Services + channel-per-region via geomatch | + +--- + +## When to Push Back + +If the developer says "send SMS" but the context suggests otherwise, raise the alternative before proceeding: + +- Rich content described (cards, buttons, images beyond simple media) -> suggest RCS + SMS fallback +- Recipients in Brazil, Mexico, India, or other WhatsApp-dominant markets -> suggest WhatsApp +- OTP / verification use case -> redirect to `twilio-verify-send-otp` +- Brand presence / trust is material (financial, healthcare, enterprise customer) -> suggest RCS for US, WhatsApp Business for intl +- "Reach iPhone and Android with the same experience" -> RCS is the answer + +Frame it as an education, not a correction: "SMS will work - but given [X], RCS would give you [Y]. Would you like to use RCS with SMS fallback?" + +--- + +## Output Format + +When you recommend a channel, include: +1. Primary channel and why it fits +2. Fallback channel (if applicable) and how to configure it +3. Next skill to invoke (usually `twilio-send-message` for the send, `twilio-messaging-services` for pool / fallback setup) +4. Trade-offs the developer should know (cost, setup time, approval requirements) + +--- + +## Next Steps + +- Send the message: `twilio-send-message` +- Channel overview and unified API: `twilio-messaging-overview` +- Sender pools + RCS->SMS fallback: `twilio-messaging-services` +- Rich content templates: `twilio-content-template-builder` +- RCS-specific onboarding and rich cards: `twilio-rcs-messaging` +- WhatsApp-specific onboarding: `twilio-whatsapp-send-message`, `twilio-whatsapp-manage-senders` +- OTP / verification flows: `twilio-verify-send-otp` +- Marketing-specific planner: `twilio-marketing-promotions-advisor` +- Notifications-specific planner: `twilio-notifications-alerts-advisor` diff --git a/plugins/twilio/skills/twilio-messaging-channel-advisor/assets/icon-large.png b/plugins/twilio/skills/twilio-messaging-channel-advisor/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-messaging-channel-advisor/assets/icon-small.png b/plugins/twilio/skills/twilio-messaging-channel-advisor/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Twilio Messaging channel overview and onboarding guide. Covers all + channels (SMS, WhatsApp, RCS, Facebook Messenger), the unified + Messages API, channel selection guidance, and the recommended setup + sequence from first message to production monitoring. Start here + before choosing a specific messaging channel. +--- + +## Overview + +Twilio's Messaging platform sends and receives messages across multiple channels through a single API. All channels use `client.messages.create()` - only the address format changes. + +| Channel | Address format | Rich media | Template required? | Reach | Skill | +|---------|---------------|------------|-------------------|-------|-------| +| SMS/MMS | `+15551234567` (E.164) | MMS: images/video, US/CA/AU only | No | Global (180+ countries) | `twilio-sms-send-message` | +| WhatsApp | `whatsapp:+15551234567` | Yes (images, docs, video, location) | Outside 24hr window: yes | 190+ countries | `twilio-whatsapp-send-message` | +| RCS | Same number (via Messaging Service) | Yes (rich cards, carousels, video) | No | US + expanding globally | `twilio-rcs-messaging` | +| Facebook Messenger | `messenger:{page-scoped-id}` | Yes | No | Global | - | + +--- + +## Which Channel Should I Use? + +| If you need to... | Use | Why | +|-------------------|-----|-----| +| Reach any phone number | SMS | Universal - works on every phone, no app needed | +| Send rich media globally | WhatsApp | Images, docs, video work worldwide (MMS is US/CA/AU only) | +| Send time-sensitive alerts (OTP, outages) | SMS | Highest open rates, no app dependency | +| Reach opted-in audience at lower cost | WhatsApp | No per-message fee in many markets | +| Run marketing campaigns across channels | Start with `twilio-marketing-promotions-advisor` | Planner skill handles channel mix, compliance, and fallback | +| Send transactional notifications | Start with `twilio-notifications-alerts-advisor` | Planner skill handles urgency-based channel routing | + +Not sure? Use a Planner skill first - it will qualify your use case and recommend the right channel combination. + +--- + +## Unified API + +All channels share `messages.create()`. The only difference is the address format in `from` and `to`. + +Python +```python +import os +from twilio.rest import Client + +client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"]) + +# SMS +sms = client.messages.create( + from_="+15017122661", + to="+15558675310", + body="Your order shipped." +) + +# WhatsApp - same API, prefixed addresses +whatsapp = client.messages.create( + from_="whatsapp:+15017122661", + to="whatsapp:+15558675310", + body="Your order shipped." +) +``` + +Node.js +```javascript +const client = require("twilio")(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); + +// SMS +const sms = await client.messages.create({ + from: "+15017122661", + to: "+15558675310", + body: "Your order shipped.", +}); + +// WhatsApp - same API, prefixed addresses +const whatsapp = await client.messages.create({ + from: "whatsapp:+15017122661", + to: "whatsapp:+15558675310", + body: "Your order shipped.", +}); +``` + +--- + +## Onboarding Sequence + +Set up messaging in this order. Each step builds on the previous. + +### Step 1: Foundation +Get a Twilio number, send your first SMS. +- `twilio-account-setup` -> `twilio-sms-send-message` + +### Step 2: Sender Strategy +Create a Messaging Service and add your numbers to a sender pool. Use `messagingServiceSid` instead of `from` for all production sends - this enables geo-match, sticky sender, and unlocks compliance features. +- `twilio-messaging-services` + +### Step 3: Compliance +Register for A2P 10DLC (required for US SMS traffic). Set up opt-out handling. +- `twilio-compliance-onboarding` -> `twilio-compliance-traffic` + +### Step 4: Protect +Enable SMS pumping protection (prevents artificial traffic inflation) and compliance toolkit (quiet hours, reassigned number detection) on your Messaging Service. +- `twilio-messaging-services` (Compliance Toolkit + SMS Pumping Protection sections) + +### Step 5: Add Channels +Add WhatsApp or other channels. Use Content Templates for cross-channel message formatting. +- `twilio-whatsapp-send-message` -> `twilio-content-template-builder` + +### Step 6: Monitor +Enable intelligent alerts for error pattern detection. Set up status callbacks for delivery tracking. +- `twilio-messaging-services` (Intelligent Alerts section) -> `twilio-messaging-webhooks` + +--- + +## CANNOT + +- Cannot send cross-channel in a single API call - Each `messages.create()` targets one channel. For multi-channel fallback, implement sequencing in your application (e.g., try SMS, on failure send WhatsApp). +- Cannot use WhatsApp free-form messages outside the 24-hour window - After 24 hours since the user's last inbound message, you must use a pre-approved template. See `twilio-whatsapp-send-message`. +- Cannot send MMS outside US, Canada, and Australia - MMS is only supported on US/CA/AU numbers. For international rich media, use WhatsApp. +- Cannot use SMS pumping protection or compliance toolkit without a Messaging Service - These features are configured per Messaging Service. Raw `from` number sends don't get these protections. +- Cannot mix channels in a single Messaging Service sender pool - A Messaging Service manages phone numbers for SMS/MMS. WhatsApp senders are configured separately. +- Cannot guarantee delivery on any channel - SMS can be carrier-filtered, WhatsApp can queue-timeout (4 hours). Always implement status callbacks to track delivery. + +--- + +## Next Steps + +- Send SMS: `twilio-sms-send-message` +- Send RCS: `twilio-rcs-messaging` +- Send WhatsApp: `twilio-whatsapp-send-message` +- Set up sender pools and production features: `twilio-messaging-services` +- Channel selection for marketing: `twilio-marketing-promotions-advisor` +- Channel selection for notifications: `twilio-notifications-alerts-advisor` diff --git a/plugins/twilio/skills/twilio-messaging-overview/assets/icon-large.png b/plugins/twilio/skills/twilio-messaging-overview/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-messaging-overview/assets/icon-small.png b/plugins/twilio/skills/twilio-messaging-overview/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Create and configure Twilio Messaging Services for production messaging. + Covers sender pools, geo-match, sticky sender, message scheduling, + compliance toolkit, SMS pumping protection, link shortening, and + intelligent alerts. Use this skill when setting up production-ready + messaging infrastructure. +--- + +## Overview + +A Messaging Service groups senders (phone numbers, short codes, toll-free numbers) with shared configuration. Send via `messagingServiceSid` instead of a specific `from` number - Twilio picks the best sender automatically. + +Use a Messaging Service for all production sends. Beyond sender pools, it unlocks compliance toolkit, SMS pumping protection, link shortening, message scheduling, and intelligent alerts. For channel selection guidance, see `twilio-messaging-overview`. + +--- + +## Prerequisites + +- Twilio account with at least one SMS-capable phone number + - New to Twilio? See `twilio-account-setup` +- Environment variables: + - `TWILIO_ACCOUNT_SID` + - `TWILIO_AUTH_TOKEN` + - See `twilio-iam-auth-setup` for credential setup and best practices +- SDK: `pip install twilio` / `npm install twilio` + +--- + +## Quickstart + +Python +```python +import os +from twilio.rest import Client + +client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"]) + +# Step 1: Create the service +service = client.messaging.v1.services.create( + friendly_name="Production Notifications Service" +) +print(service.sid) # MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - save as MESSAGING_SERVICE_SID + +# Step 2: Add a phone number +client.messaging.v1 \ + .services(service.sid) \ + .phone_numbers \ + .create(phone_number_sid="PNxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") + +# Step 3: Send via the service +message = client.messages.create( + messaging_service_sid=service.sid, + to="+15558675310", + body="Your order has shipped." +) +print(message.sid) +``` + +Node.js +```node +const twilio = require("twilio"); +const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); + +// Step 1: Create the service +const service = await client.messaging.v1.services.create({ + friendlyName: "Production Notifications Service", +}); +console.log(service.sid); + +// Step 2: Add a phone number +await client.messaging.v1 + .services(service.sid) + .phoneNumbers.create({ phoneNumberSid: "PNxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }); + +// Step 3: Send via the service +const message = await client.messages.create({ + messagingServiceSid: service.sid, + to: "+15558675310", + body: "Your order has shipped.", +}); +console.log(message.sid); +``` + +--- + +## Key Patterns + +### Create Service with Webhooks and Features + +Python +```python +service = client.messaging.v1.services.create( + friendly_name="Marketing Campaigns", + inbound_request_url="https://yourapp.com/sms/inbound", + status_callback="https://yourapp.com/sms/status", + sticky_sender=True, + area_code_geomatch=True, + validity_period=14400 +) +``` + +Node.js +```node +const service = await client.messaging.v1.services.create({ + friendlyName: "Marketing Campaigns", + inboundRequestUrl: "https://yourapp.com/sms/inbound", + statusCallback: "https://yourapp.com/sms/status", + stickySender: true, + areaCodeGeomatch: true, + validityPeriod: 14400, +}); +``` + +### Optional Features + +| Feature | Parameter | Description | +|---------|-----------|-------------| +| Sticky Sender | `sticky_sender` | Same sender for same recipient | +| Area Code Geomatch | `area_code_geomatch` | Match sender area code to recipient | +| Validity Period | `validity_period` | Discard undelivered messages after N seconds | +| Smart Encoding | `smart_encoding` | Convert unicode to GSM-7 | +| MMS Converter | `mms_converter` | Convert MMS to SMS if recipient can't receive MMS | +| Message Scheduling | `send_at` on message | Schedule sends up to 7 days ahead (see below) | +| Link Shortening | `shorten_urls` | Shorten links with branded domain + click tracking (see below) | + +### List Services and Numbers + +Python +```python +for service in client.messaging.v1.services.list(): + print(service.sid, service.friendly_name) + +for number in client.messaging.v1.services(SERVICE_SID).phone_numbers.list(): + print(number.sid, number.phone_number) +``` + +Node.js +```node +const services = await client.messaging.v1.services.list(); +services.forEach(s => console.log(s.sid, s.friendlyName)); + +const numbers = await client.messaging.v1.services(SERVICE_SID).phoneNumbers.list(); +numbers.forEach(n => console.log(n.sid, n.phoneNumber)); +``` + +--- + +## Production Messaging Features + +The features below are platform capabilities that are configured on or require a Messaging Service. They are separate from the sender pool management above. + +### Message Scheduling + +Schedule messages 15 minutes to 35 days in advance. Requires `messagingServiceSid` (not `from`). Supports SMS, MMS, RCS, and WhatsApp. No additional cost - only charged for messages actually sent. + +Python +```python +from datetime import datetime, timedelta, timezone + +message = client.messages.create( + messaging_service_sid="MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + to="+15558675310", + body="Your appointment is tomorrow at 2pm.", + send_at=(datetime.now(timezone.utc) + timedelta(hours=24)).isoformat(), + schedule_type="fixed" +) +print(message.sid, message.status) # SM..., scheduled +``` + +Node.js +```javascript +const sendAt = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(); +const message = await client.messages.create({ + messagingServiceSid: "MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + to: "+15558675310", + body: "Your appointment is tomorrow at 2pm.", + sendAt, + scheduleType: "fixed", +}); +``` + +Cancel a scheduled message before it sends: + +```python +client.messages("SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx").update(status="canceled") +``` + +Limitations: Scheduled messages don't return a status callback event on creation. WhatsApp templates are validated at send time (not scheduling time) - non-compliant templates fail when `sendAt` fires. Opt-outs received after scheduling don't auto-cancel the message; cancel manually if needed. + +--- + +### Compliance Toolkit (US SMS, Public Beta) + +Automated compliance checks for US SMS. Enable in Console: Messaging > Settings > General > Enable Compliance Toolkit. + +| Feature | What it does | Error code | Default | +|---------|-------------|-----------|---------| +| Quiet Hours | Reschedules non-essential messages sent during TCPA restricted hours (9PM-8AM recipient local time). Uses area code for timezone. 11 states have stricter windows. | 30610 (if block mode) | Enabled (reschedule mode) | +| Reassigned Number Detection | Checks FCC reassigned numbers database; re-checks every 30 days | 21610 | Enabled | +| TCPA Known Litigators | Blocks non-essential messages to known litigator numbers; re-verifies weekly | 30640 | Not enabled by default - requires account rep activation | +| Opt-out Verification | Blocks messages to users who replied STOP/UNSUBSCRIBE/END/QUIT/etc. | 21610 | Enabled | + +AI/ML classification: Compliance Toolkit uses ML to classify messages as essential (OTP, alerts, support) vs non-essential (marketing, promotions). Essential messages bypass quiet hours and litigator checks. Override the classification with `messageIntent`: + +Python +```python +message = client.messages.create( + messaging_service_sid="MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + to="+15558675310", + body="Your order shipped!", + message_intent="confirm", # Override ML: mark as essential/transactional + risk_check="enable" # Evaluate against all compliance checks +) +``` + +Consent Management API - Programmatically track opt-in/opt-out/re-opt-in status per phone number across SMS/MMS/RCS. Supports bulk upsert. Use alongside Compliance Toolkit to maintain consent records. + +Contact API - Store recipient ZIP codes for more accurate quiet hours timezone inference (vs area code default). + +--- + +### SMS Pumping Protection + +Detects and blocks artificial inflation of SMS traffic (toll fraud where bad actors trigger high volumes of messages to premium-rate numbers they control). + +How it works: +- Combines behavioral analysis with known fraud scheme identification using Twilio's proprietary model +- Analyzes: messages to regions known for pumping, countries with no prior sending history, patterns suggesting non-human behavior +- Auto-blocks suspected pumping destinations - returns error 30450 +- Enable in Console: Messaging > Settings > General > SMS Pumping Protection +- Free in US/Canada; other regions check SMS Pricing page + +Per-message risk check: + +Python +```python +message = client.messages.create( + messaging_service_sid="MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + to="+15558675310", + body="Your verification code is 123456.", + risk_check="enable" # Assess pumping risk for this specific message +) +``` + +`riskCheck` parameter values: +- `enable` (default for OTP/2FA messages): Apply SMS pumping protection +- `disable`: Skip protection (use for marketing messages where false positives are costly) + +Global Safe List API - Whitelist phone numbers that bypass SMS Pumping Protection, Verify Fraud Guard, and other risk checks. Use for known-good customers and approved recipients. + +False positives: The ML model may occasionally flag legitimate users. If this happens: add to Global Safe List, switch to WhatsApp/Messenger for those recipients, or contact Twilio Support. + +Note: This is separate from Verify Fraud Guard, which only protects Verify API sends. SMS Pumping Protection covers all Programmable Messaging sends through a Messaging Service. + +--- + +### Link Shortening & Click Tracking + +Automatically shorten URLs in message bodies using a branded domain, with click tracking. + +Setup: +1. Configure a branded short domain in Console (e.g., `link.yourcompany.com`) +2. Add DNS records as directed +3. Enable `ShortenUrls: true` on your Messaging Service + +Python +```python +service = client.messaging.v1.services("MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx").update( + shorten_urls=True +) +``` + +Once enabled, any URL in the message body is auto-shortened to your branded domain. Click events are delivered via status callback. + +- Links are retained for 90 days after creation +- Click tracking events appear in status callbacks alongside delivery events + +--- + +### Intelligent Alerts + +ML-based monitoring that detects unusual error patterns and alerts you before they become outages. This is an account-level feature (not per-service). + +Monitors 5 error codes: +- 30001 (Queue overflow), 30005 (Unknown destination), 30006 (Landline or unreachable), 30007 (Carrier violation / spam filter), 30008 (Unknown error) + +How it works: +- Analyzes error patterns in 5-minute windows +- Calculates impact score based on error volume and velocity +- Classifies: Urgent (>0.80), Important (0.40-0.80), Warning (<0.40) +- Alerts via email or webhook + +Free feature - enable in Console > Messaging > Settings > Intelligent Alerts. + +--- + +## CANNOT + +- Cannot add a phone number to multiple Messaging Services - A number belongs to one service at a time +- Cannot determine throughput from the API - Throughput depends on number type (long code, short code, toll-free) and is not exposed programmatically +- Cannot schedule messages without a Messaging Service - `sendAt` requires `messagingServiceSid`, not `from`. Must also set `schedule_type="fixed"` +- Cannot schedule more than 35 days ahead - Scheduling window is 15 minutes to 35 days +- Cannot use compliance toolkit outside the US - Currently US SMS only, public beta +- Cannot use compliance toolkit without a Messaging Service - Features are configured per service +- Cannot customize SMS pumping ML thresholds - Auto-blocking sensitivity is not configurable; use Global Safe List to whitelist known-good prefixes +- Cannot use link shortening without a branded domain - Must configure a custom short domain first; no default short domain provided +- Cannot use link shortening for WhatsApp - Only available for SMS/MMS +- Cannot customize intelligent alerts error code list - Fixed to the 5 monitored error codes +- Messaging Services are required for US A2P 10DLC - Campaign registration attaches to a Messaging Service +- Inbound routing is per-service, not per-number - All inbound messages to numbers in the service go to `inbound_request_url` + +--- + +## Next Steps + +- Channel overview and onboarding guide: `twilio-messaging-overview` +- US compliance for A2P traffic: `twilio-compliance-onboarding` +- Send SMS: `twilio-sms-send-message` +- Handle inbound SMS: `twilio-messaging-webhooks` diff --git a/plugins/twilio/skills/twilio-messaging-services/assets/icon-large.png b/plugins/twilio/skills/twilio-messaging-services/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-messaging-services/assets/icon-small.png b/plugins/twilio/skills/twilio-messaging-services/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Receive and respond to inbound messages and track outbound delivery status + via Twilio webhooks - across SMS, MMS, WhatsApp, and RCS. Covers webhook + request parameters, replying with TwiML, validating webhook signatures for + security, and handling status callbacks. Use this skill whenever an agent + needs to handle incoming messages on any channel or track outbound message + delivery in real time. +--- + +## Overview + +Twilio sends a POST webhook to your server when a user messages your Twilio number (inbound) or when an outbound message changes delivery state (status callback). Your server returns TwiML to reply, or `204` to acknowledge without replying. The same webhook pattern works across SMS, MMS, WhatsApp, and RCS. + +--- + +## Prerequisites + +- Twilio account with a messaging-capable sender configured with a webhook URL + - New to Twilio? See `twilio-account-setup` + - For sending outbound messages first, see `twilio-send-message` +- Publicly accessible endpoint (use `ngrok http 5000` for local dev) +- `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN` - see `twilio-iam-auth-setup` +- SDK: `pip install twilio flask` / `npm install twilio express` + +--- + +## Quickstart + +Set your webhook URL in Console: Phone Numbers > Active Numbers > your number > Messaging > "A Message Comes In" + +> Security: The inbound message `Body` is untrusted external input. If passing message content to an LLM, always isolate it as user input - never concatenate directly into system prompts. Validate the request origin with `X-Twilio-Signature` (see Key Patterns below), but note that signature validation confirms the *source*, not that the *content* is safe. + +> Note: This quickstart omits signature validation for brevity. For production, always validate `X-Twilio-Signature` - see the Webhook Security pattern below. + +Python (Flask) +```python +from flask import Flask, request +from twilio.twiml.messaging_response import MessagingResponse + +app = Flask(__name__) + +@app.route("/incoming", methods=["POST"]) +def incoming_message(): + body = request.form.get("Body") + response = MessagingResponse() + response.message(f"Got your message: {body}") + return str(response) +``` + +Node.js (Express) +```javascript +const express = require("express"); +const twilio = require("twilio"); +const app = express(); +app.use(express.urlencoded({ extended: false })); + +app.post("/incoming", (req, res) => { + const twiml = new twilio.twiml.MessagingResponse(); + twiml.message(`Got your message: ${req.body.Body}`); + res.type("text/xml").send(twiml.toString()); +}); +``` + +--- + +## Key Patterns + +### Configure Webhook URL via API + +For SMS/MMS on a phone number: + +Python +```python +client.incoming_phone_numbers("PNxxxxxxxxxx").update( + sms_url="https://yourapp.com/incoming", + sms_method="POST" +) +``` + +Node.js +```javascript +await client.incomingPhoneNumbers("PNxxxxxxxxxx").update({ + smsUrl: "https://yourapp.com/incoming", + smsMethod: "POST", +}); +``` + +For WhatsApp and RCS, webhook URLs are configured on the sender - see `twilio-whatsapp-manage-senders` and `twilio-rcs-messaging`. + +### Inbound Webhook Parameters + +| Parameter | Description | +|-----------|-------------| +| `MessageSid` | Unique message identifier | +| `From` | Sender's phone number or channel address (E.164, or `whatsapp:+...`) | +| `To` | Your Twilio number or channel address | +| `Body` | Message text | +| `NumMedia` | Number of media attachments | +| `MediaUrl0` | URL of first media attachment (if any) | +| `MediaContentType0` | MIME type of first attachment | + +### Handle Inbound Media (MMS / WhatsApp / RCS) + +Python (Flask) +```python +@app.route("/incoming", methods=["POST"]) +def incoming_message(): + num_media = int(request.form.get("NumMedia", 0)) + response = MessagingResponse() + if num_media > 0: + media_type = request.form.get("MediaContentType0") + response.message(f"Got your {media_type} attachment!") + else: + response.message("Got your message.") + return str(response) +``` + +Node.js (Express) +```javascript +app.post("/incoming", (req, res) => { + const numMedia = parseInt(req.body.NumMedia || "0", 10); + const twiml = new twilio.twiml.MessagingResponse(); + if (numMedia > 0) { + twiml.message(`Got your ${req.body.MediaContentType0} attachment!`); + } else { + twiml.message("Got your message."); + } + res.type("text/xml").send(twiml.toString()); +}); +``` + +### Acknowledge Without Replying + +Python +```python +return str(MessagingResponse()) # Empty +``` + +Node.js +```javascript +res.type("text/xml").send(new twilio.twiml.MessagingResponse().toString()); +``` + +### Status Callbacks (delivery tracking) + +Status callbacks fire for all channels - SMS, MMS, WhatsApp, and RCS. + +Python +```python +message = client.messages.create( + messaging_service_sid="MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + to="+15558675310", + body="Hello!", + status_callback="https://yourapp.com/status" +) +``` + +Node.js +```javascript +const message = await client.messages.create({ + messagingServiceSid: "MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + to: "+15558675310", + body: "Hello!", + statusCallback: "https://yourapp.com/status", +}); +``` + +Python (Flask) - status callback handler +```python +@app.route("/status", methods=["POST"]) +def message_status(): + message_sid = request.form.get("MessageSid") + status = request.form.get("MessageStatus") + error_code = request.form.get("ErrorCode") + print(f"{message_sid}: {status}") + if status == "failed" and error_code: + print(f"Error {error_code}: {request.form.get('ErrorMessage')}") + return "", 204 +``` + +Node.js (Express) - status callback handler +```javascript +app.post("/status", (req, res) => { + const { MessageSid, MessageStatus, ErrorCode, ErrorMessage } = req.body; + console.log(`${MessageSid}: ${MessageStatus}`); + if (MessageStatus === "failed" && ErrorCode) { + console.log(`Error ${ErrorCode}: ${ErrorMessage}`); + } + res.sendStatus(204); +}); +``` + +Status flow: `queued -> sent -> delivered` (or `undelivered`/`failed`) + +### Webhook Signature Validation + +Python (Flask) +```python +from twilio.request_validator import RequestValidator + +validator = RequestValidator(os.environ["TWILIO_AUTH_TOKEN"]) + +@app.route("/incoming", methods=["POST"]) +def incoming_message(): + if not validator.validate(request.url, request.form, request.headers.get("X-Twilio-Signature", "")): + return "Forbidden", 403 + response = MessagingResponse() + response.message("Hello!") + return str(response) +``` + +Node.js +```javascript +const { validateRequest } = require("twilio"); + +app.post("/incoming", (req, res) => { + const isValid = validateRequest( + process.env.TWILIO_AUTH_TOKEN, + req.headers["x-twilio-signature"], + `https://${req.headers.host}${req.path}`, + req.body + ); + if (!isValid) return res.status(403).send("Forbidden"); + const twiml = new twilio.twiml.MessagingResponse(); + twiml.message("Hello!"); + res.type("text/xml").send(twiml.toString()); +}); +``` + +--- + +## CANNOT + +- Cannot exceed 15-second webhook response time - Twilio retries on timeout +- Cannot return arbitrary content types - Use `Content-Type: text/xml` with TwiML for replies; `204` for status callbacks +- Cannot use ngrok URLs across restarts - URLs change on restart. Use a stable tunnel for persistent testing. +- Cannot guarantee delivery confirmation - Status callbacks are best-effort. `delivered` requires carrier confirmation. + +--- + +## Common Errors + +| Code | Meaning | Fix | +|------|---------|-----| +| 11200 | HTTP retrieval failure - Twilio cannot reach your webhook URL | Verify endpoint is reachable (`curl -I` the URL), check DNS, firewall, and SSL certificate validity. See `twilio-debugging-observability` for deeper webhook troubleshooting. | + +--- + +## Next Steps + +- Send outbound messages: `twilio-send-message` +- Manage sender pools: `twilio-messaging-services` diff --git a/plugins/twilio/skills/twilio-messaging-webhooks/assets/icon-large.png b/plugins/twilio/skills/twilio-messaging-webhooks/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-messaging-webhooks/assets/icon-small.png b/plugins/twilio/skills/twilio-messaging-webhooks/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 zcmeHKg;&&3xBU$vAtfLTNOwpoEg;McDcvP7G)N2~rL=S-AvH8gNOwpJjC2Y}Nr=SI z15!`ld+YrXZ+&;&efK_R-8}2A8&R6-io}Gpga7~#D=R^@|FXtE;A8(SbAH>`0{|h1 z0|cV!VC4(|KG`{G-nzPTR1sqx-hTNbf}UW%D(!n@JM={cT439I;9;PbX#PG651AIU zh&u!amk3M@gTpBAgS0^Y&qIR9L$zLgv{sl^dE@Jg-rU4|m0nq}LwjJp`>!_OVC|Fu9rw}w_`49?>=~@1V$-Q7vxhBF%2=%osz?_XCsdw4!d+Ve zD;U9)lG?LTxAS?&f#$_BEkK37HWFT6n1sO^MVlHLMH>Z$?g}*TyiQzImKI@tG^Z_g zWR+;rMf1zzIZ#&y#f&Y6vGIZskZc4o1)@o_W|;vt*jp#Yi*=c&N9}9`I5e+IN+cvC zI7~@UP}A|1Gg9F|Kp?|K4*2IT|(OksWjt!WpAEZ)_UlV7IGfsX64L{}+P+s14vH=NJoL_pfux!>k(NcQL0 zScy2gd4O&($2j&t7uG!BzlyUIOo|WWGZ9u`Jx;`#1Q<7h&MfW^;=QvFo5$mkBmRgN zmxr4T(62si2KYmFSMPzbFx^}kz*yH~AL1=${2qDhNG$H>j)|hy_(so(>}fVgF6Fv4 zDJk*T<)10xS4K3-ztbepC76+q8*dut{)OfFkT&c<-fH~mDeozRf(7}DcX}4|^OUw- zqtya*gjHb_YcG8GvhmO%s5Qw8@Dho6P*pGFkn5T#7Xrvb?IR`Ov4UgCWBnUq3saMzk}1;#MUbM=J#km*5z`DBGUU&_mA*2FZ6hQS6R$tB&?g{Nl?5XYHob$y;>hv0|K69H8XW*wc2zTnq z>HpEEw!U?w?c>}|F9VDXRCQDT+&wb80A1q*&`tN#9AcNmbzw6_D2~iYd4oW*Z8} zuw&L^6N2clsIVp+6L<8*B#ExRqP$5woRaz_dEHW+5(0){0 zR-~>Rr!=ChQ#4YHT{Kk`QF zi{m4>jDIF8^95f~0A#;(ba;5scsLyOIju5TnpH{#VlVmB)tIL;<8_GW3x9!9mhH!1 zlNPz>SiE%(;@XqjQ=TbyKJU~EP`d5FGV3;DhFK=aNAy5o?+b$AiSYP!xqj8?Cn~z^ z{_JVm!IK2V{=?O^!S2<^lAFE5Z-+g2%6M{lXm}p-Q0v+1I+UrG4V4v_+3A(ea5sINZ4Ulu=Gx$@3Gxjq+AC$a)KTthLp*v?CfkeysW}6C+MMdEIus(-EW|Ri1$x`D;nP zCiaAO9X4A=Pui&$P;*WRbCFHaVyLzW|JkntcA**x0z+bbO_P`!WH94O6^ij3bF{L1 zXVG%*zI+@Hkgd{Ji>+Gbxmeh)V&pRNy7J2MIJ>vHcSHRlT+B7hYRpv-Gx-<@I%4M? zL2qj>d#_c`quv#0GweMyRv}ZtC9<8N7~CKCJ9a=JLQxDlR_SJ5K`N~(cVAX@2B7}b5RX1`6qA4SQ|z_ts-=B9x`9iT?c7=2q|0;C>tV>oaN-YR09w?<0%S?OzdN6XZL|4eUStZ!nGNldAVs z%=cq{csG2jt+17~{Ni?J-(v1-_fEh6AeuB)ITfPEuU9!UJR?=ULTAC)>^r_=p&F;6 zW;t>NS%P>wN;@s?dj1|Ju_Cc!4A5qLxpF1)b9dlge|S%Lf})S2Lc%@9eTRC7SFf~P zcwEA>-i=Isg~$K67}(DmrTUq+pl|H7hHx|Wx!!3)tdJlNr6=AdBWgFL_R(dFT+X=r znpc_U-{XtoCq_SxcI(KLwzloHEp&=q-VQpPP3hTLk5MAovqz-b++6lsT25%T?fBVV zg3-)KRxwopF>7Px{o8Q#TwP2PWM(~zaEtpAr#UUzxww|4x%anIEGFAlG z?=~H{GFH?4YPa++baLBS{hk#sCa~n*=5^VK+24hrBJM@>hNENqVq=#rS3b06`C3CoPvc5?bJd@rd_-ka?&?m*X2#BP12HjE0&nY*>L>&*1gHY|{5~BX5Avc6 zes~0XB)lx`$PLsv)L+}5@Q(HFK(j7ujBdZ_3gEi?cvrKVf-c3o?{NSA7Z4`w-O`j(wLyRB&{JDo2LN#C<($)7r?-wT|7_2; zXCh#X_wEaSoH@L{IhwB!A;M;T)v|Yc(BaO7leoMtg(sNtdp}Q%Hh5zvnn`VNT>oMC zNfJ+e=ac6X-&H~CtHa3wZ_74KPGfbr$w)agBKs{B$c?3cyklEZ{+7d!^&Tx$K862+ z+P{*y;3A$RK_$Suf|oiOw&td>qMP2%axqEt>kNdfSv-?@3|f@TbH^DlAh zhQ}-U6kqLXM5_$fc(om-!p^afAzmw|C9c8|Ihkw zb{%4E1%P|U%1}97!`>Xj9SDUY zX%hg7^gBDSnrlLKgci;Y$?fe;u6PAy;RN|5;0lg+j4_dF2=H|lE!TE`mT}KwmRpnB zz?LnZ-Jq5HCd)KV1tq)0!BuV8N*C@s=+(@R%md57i)I&SMf_s(o?wN=5jRDbm^?_* zn%&1Q_=QlBb33JFw1PPV5it4^!V2g5<^@$1aU!n_9kJ>5Gn9|EKp+mz`LGg`C>>o} zKu*LfQ|L>I&!1Hq<=flaCsuZGs;{5gRwFBcDL0|>H_PvLJhndyw9rjd_cgzy+VMKF#XXV}^xw@s#Kw7AXgUq0IL8Xv!d8 z32U~;CZ9#O#Wis^wjL~vB98!OKBNU#BzHaKCnjS#mzAbS3%oOxc7ESVLcl55+;lzc zt7Q@XVO0lfbwc@^wegrImzhcUkk9B>bzqZ!zBGE+D}+$-*Pp12>2Mx({5;yJS%g)7 z_3*s{wF?38cZG`r!#gbxhIS$%?aF2FR zuD;_6JWzI%&9fFW9nP25=&rD{`0drzJ27&Zye1|}OaDDFFeyozMKrV`?;VIXRA_-2 z0)$%Q6tF7j*<;jfoUSpQ$O*Li6E5$EpLzDLVK@`v&BiB8hBORW!@ZPX<}-9NIm&Nb!`x zxhF*xjplgMrmGWpL4XkAG#bKKzsB1=F2h*X0454I-UjkZ*|RQbz4Y1da( zDqe==`D`bO*ic&=yiFt@5D_Km^0_R!)JmDq9VMb`$L&Ff`oKE2?x5j zW<+SXv!GWP!S5gX+r$sR{HHnRT3O9o>pun&AE{Edq*NAL0-nj43_R2nkB+wEeAn*L z!NA_RDE?x4$h;Vp@W`j7@Fd$xCfHOYW?}fIQ-Z8Ka=%Hu)hhiKTgipBYgNi{CMkRC zmjRb&qMV^W&ise5FgbljTgJpCF2Q-XvwW(DqcQH{N$y(#V!(v<#)@Mrh5Ny0WT}E} z$7^)k4guy%V4^~8!F$yb&3*qtbiSkSCCoju)GogMIJA9$$StckGEeh?@pS+-h!%~P z;uA8;slqGJ;3Jc@w|&la=O{mr;Ng2--oF=d-L{e$2*LPrsr{kucVjuDz+8niTr7ra zr$ow5m_5D~+~YxA)?f?N)^0{hoCu{c8s0XVd|Pp9!lzC9f^ep5pr{hics!e~v!NS3 zlV0lcwtM)_rlI%`CYEiYt?sNUrtqv@r&z2da5U|=$Br@sUvfWcscV9PTC4h=#4&e_ zT|uDr?YBb2jTKM}{vj*4-=G#7x5l6`s~c#7Iu&aOXXOseztNB7kvC~SylvZ*=zSD) z52uEogmV5Pd6DfFcI}4`nWWZQ-=6A4*_(Ksfclv17OwDf%-{3UcN9!6ZuzfMcvo~BqKbas#zHsLb z4P7pJ)Zsd4!Z7UPs#JI@Tuk^ex5>d5#a!s3A}W0+n5y&g%_nzl2ISD`RZRSyY2>VZ zB}vQ5>EZq*Cq~pO&6BZQ(AIu2U(m{4^TsT%!OINkcy0z0YiO_Df2+2*Z*|%InRRmU zUOlB+YC{*b^x%VQ8Kde!zmlmBx^@Eim8HAIc~Ml9M`*u_g3X&LmJ4E9sFnMe;iNIm z?SXl4)zdwita7vPOu3W20RPz%3?h_%Q)cE;7*9ChJ%tJScFwS@_F-J``({4Rx88E1 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Planning skill for transactional notifications, alerts, and + reminders. Qualifies the developer's needs across urgency, channel + selection, delivery confirmation, and fallback patterns to recommend + the right Twilio notification architecture. Handles both "send + shipping updates to customers" and "build a multi-channel alert + system with delivery confirmation and fallback." +tier: discover +--- + +## Role + +You are a Notifications & Alerts Architecture Advisor. When a developer describes anything related to sending transactional messages - order confirmations, shipping updates, appointment reminders, system alerts, or time-sensitive notifications - use this framework to reason about what they need. + +## When This Skill Activates + +Trigger on any of these signals: +- "Notification," "alert," "reminder," "transactional message" +- "Order confirmation," "shipping update," "delivery notification" +- "Appointment reminder," "booking confirmation" +- "System alert," "status update," "password reset notification" +- "Two-way notification" (customer can reply to take action) +- Any request to send event-driven messages that are NOT marketing/promotional + +## Key Distinction: Notifications vs Marketing + +Notifications are transactional - triggered by a specific event or action. They are NOT marketing. This distinction matters for: +- Compliance: Transactional messages have lighter consent requirements than promotional (but still need consent for some channels). +- Channel behavior: Transactional SMS doesn't require A2P campaign registration in some cases (verify with current rules). +- Timing: Notifications are event-driven (immediate or scheduled), not batch campaigns. + +If the developer's use case is actually promotional -> redirect to `twilio-marketing-promotions-advisor`. + +## Step 1: Detect Specificity and Decide Your Mode + +High-level request (e.g., "I need to notify customers about their orders"): +-> DISCOVERY MODE. Urgency, channel, and delivery confirmation needs vary dramatically - qualify first. + +Mid-level request (e.g., "Send SMS appointment reminders 24 hours before"): +-> VALIDATION MODE. Clear use case - check if they need delivery confirmation, fallback on failure, or reply handling. + +Specific implementation request (e.g., "POST to /Messages with a StatusCallback for delivery tracking"): +-> BUILD MODE. Proceed with the Product skill. Quick check: Are they using a Messaging Service? Do they have StatusCallbacks configured? + +## Step 2: Qualify Intent - The 5 Essential Questions + +1. What event triggers the notification? + - User action (order placed, appointment booked, password reset) -> Real-time, API-triggered + - System event (threshold breach, deployment status, error alert) -> Webhook-triggered or cron-scheduled + - Time-based (appointment in 24 hours, subscription expiring) -> Scheduled sends + +2. How urgent is delivery, and which channel(s)? + - Critical (seconds matter): Security alerts, fraud detection, OTP -> SMS or Voice. Redundant channels. + - Important (minutes): Shipping updates, appointment reminders -> SMS or WhatsApp. + - Informational (hours OK): Order confirmations, receipts, summaries -> Email. SMS optional. + - Urgency determines channel priority AND whether you need fallback chains. + > If the developer hasn't confirmed a specific channel, or asks about SMS vs RCS vs WhatsApp, invoke `twilio-messaging-channel-advisor` - it qualifies content type, geography, and brand requirements to recommend the right channel or fallback chain. + +3. Does the customer need to respond or take action? + - No (one-way): Simple send - SMS, Email, or Voice notification + - Yes (two-way): Need reply handling - Webhooks for inbound SMS, or interactive WhatsApp buttons + - Yes (rich interaction): WhatsApp interactive messages, RCS rich cards, or Voice IVR for confirmation + +4. What happens if delivery fails? + - Acceptable loss: Log the failure, move on. Email is often this category. + - Needs retry: Implement retry logic with backoff. Common for SMS. + - Needs fallback to another channel: SMS fails -> try Voice call. Critical for urgent notifications. + - Must confirm delivery: StatusCallbacks mandatory. Alert your system on `undelivered` or `failed`. + +5. What's your volume? + - Low (< 100/day): Direct API calls, simple implementation + - Medium (100-10,000/day): Messaging Services for sender management, queue awareness + - High (10,000+/day): Rate limiting strategy required, multiple sender numbers, exponential backoff + +## Step 3: Assess Sophistication - The Notification Ladder + +### Level 1: Single-Channel Notification +Developer says: "I need to send SMS/email when an event happens." +Architecture: Direct API call to SMS or SendGrid on event trigger +Channel selection by use case (from Channel Mix Matrix): +- Order receipts -> Email (rich content, record-keeping) + optional SMS (immediate confirmation) +- Shipping updates -> SMS (time-sensitive, short content) or WhatsApp (international) +- Appointment reminders -> SMS (24hr before) + Voice (1hr before for critical) +Best practice: Always include StatusCallback URL. Even for simple sends. Without it, you have zero delivery visibility. +Relevant bundled skills: `twilio-sms-send-message` and/or `twilio-email-send` (Account SID + Auth Token -> comms.twilio.com) or `twilio-sendgrid-email-send` (SendGrid API key, SG.-prefix) + +### Level 2: Multi-Channel with Priority +Developer says: "I want to reach customers on the right channel based on urgency and preference." +Architecture: Level 1 + channel routing logic + fallback chains +Pattern - Urgency-Based Channel Selection: + +| Urgency | Primary Channel | Fallback | Example | +|---------|----------------|----------|---------| +| Critical | SMS + Voice (parallel) | - | Fraud alert, security breach | +| High | SMS | Voice (if undelivered after 5 min) | Appointment in 1 hour | +| Medium | SMS or WhatsApp | Email | Shipping update | +| Low | Email | - | Weekly summary, receipt | + +Pattern - Fallback Chain: +``` +Send SMS -> wait for StatusCallback -> + if "delivered" -> done + if "undelivered" or "failed" after 5 min -> + Send Voice notification -> wait -> + if answered -> done + if no answer -> Send Email as last resort +``` +Key decisions: +- Fallback timeout: How long to wait before escalating channels? (Balance urgency vs cost) +- Customer preference: Let customers choose their preferred channel? (Store in your DB or Segment profile) +- Deduplication: Prevent sending the same notification on multiple channels if one succeeds +Relevant bundled skills: + `twilio-voice-outbound-calls`, `twilio-whatsapp-send-message` + +### Level 3: Event-Driven Pipeline +Developer says: "I want notifications triggered automatically from my backend events, with delivery analytics." +Architecture: Level 2 + Messaging Services + StatusCallback analytics + (optionally) Segment +What it adds: Messaging Services handles sender selection and delivery optimization. StatusCallbacks feed into your analytics pipeline. Segment captures notification events for customer journey tracking. +Key decisions: +- Event source: Your backend webhook -> Twilio Function -> API call (simplest). Or Segment event -> Engage -> Twilio (most sophisticated). +- Analytics: Log delivery status (queued -> sent -> delivered/failed) for SLA monitoring +- Scheduling: Use Twilio's scheduling (SMS: up to 7 days) or your own job scheduler for complex timing +Relevant bundled skills: + `twilio-messaging-services` + +## Decision Rules + +### Channel Selection Quick Reference +- SMS: Universal reach, instant delivery, 160 chars (or 1,600 with concatenation). Best for short, urgent messages. Most expensive per-message of the text channels. +- Email (SendGrid): Unlimited content, rich HTML, attachments. Lowest cost. Slowest open rate. Best for receipts, summaries, non-urgent. +- WhatsApp: Rich media, interactive buttons, international reach. Requires template approval for outbound. Best for markets where WhatsApp dominates (India, Brazil, EU). +- Voice: Highest urgency signal - phone rings, demands attention. Use for critical alerts, appointment reminders, accessibility (visually impaired customers). Most expensive. + +### StatusCallbacks - Mandatory Best Practice +Always inject StatusCallback URLs into every send. +- SMS: StatusCallback parameter on every `messages.create()` call +- Voice: StatusCallback on `calls.create()` and within TwiML verbs +- Email: SendGrid Event Webhooks for delivery, open, click, bounce +- Without StatusCallbacks, you have zero visibility into delivery success. + +### Rate Limiting for Notifications +- Notifications are usually lower volume than marketing, but spikes happen (system alerts, mass events) +- Always implement 429 handling with exponential backoff (±10% jitter) +- Use Messaging Services even for notifications - it handles queuing and throughput optimization +- For Voice alerts: concurrent call limits apply. Queue calls if bursting. + +## Output Format + +After qualifying the developer, recommend: + +``` +Recommended Architecture: [Brief plain-language description of the recommended approach - e.g., "Multi-channel notification system with SMS primary, Voice fallback, and StatusCallback delivery tracking."] + +Reference Skills: +- twilio-messaging-channel-advisor (if channel not yet confirmed - qualifies SMS vs RCS vs WhatsApp) +- twilio-sms-send-message (if SMS notifications) +- twilio-rcs-messaging (if RCS notifications) +- twilio-email-send (if email notifications, Twilio creds - Account SID + Auth Token) or twilio-sendgrid-email-send (if SendGrid API key, SG.-prefix) +- twilio-voice-outbound-calls (if voice alerts or fallback) +- twilio-whatsapp-send-message (if WhatsApp notifications) +- twilio-messaging-services (if volume > 100/day or multi-number) + +Setup Skills: +- twilio-account-setup - if developer needs help with credentials or account structure +- twilio-iam-auth-setup - if developer asks about API key scoping or security +- twilio-numbers-senders - number type selection affects throughput and compliance timelines; use when choosing between local, toll-free, or short code +- twilio-webhook-architecture - if developer needs help with StatusCallbacks or delivery tracking webhooks + +Guardrail Skills: +- twilio-reliability-patterns (always - backoff, retry, fallback chains) +- twilio-security-hardening (credential management) +- twilio-compliance-traffic (opt-out handling, quiet hours) +``` diff --git a/plugins/twilio/skills/twilio-notifications-alerts-advisor/assets/icon-large.png b/plugins/twilio/skills/twilio-notifications-alerts-advisor/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-notifications-alerts-advisor/assets/icon-small.png b/plugins/twilio/skills/twilio-notifications-alerts-advisor/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Choose the right Twilio number type and sender BEFORE building. Covers + phone numbers (local, toll-free, short code, mobile), alphanumeric sender + IDs, WhatsApp senders, RCS agents, international availability, and + regulatory bundles. Each number type has its own compliance program - + choosing wrong means rebuilding. Use this skill first. +--- + +## Overview + +Choosing the right number type and sender is the first decision in any Twilio project. Each number type comes with its own compliance/verification program, throughput limits, and capabilities. Developers who skip this step buy numbers first, build their app, then discover they chose the wrong type. + +Lifecycle: Choose numbers/senders (this skill) -> Register them (`twilio-compliance-onboarding`) -> Follow traffic rules (`twilio-compliance-traffic`) + +--- + +## US Number Types - Comparison + +| | Local (10DLC) | Toll-Free (800/888/877) | Short Code (5-6 digits) | +|---|---|---|---| +| SMS | Yes | Yes | Yes | +| MMS | Yes | Yes | Yes | +| Voice | Yes | Yes | No | +| Two-way | Yes | Yes | Yes | +| Throughput | ~1-75+ SMS/sec (varies by trust score) | ~3 SMS/sec | 10-100 SMS/sec | +| Compliance program | A2P 10DLC (brand + campaign) | Toll-Free Verification | Pre-approved at purchase (carrier review) | +| Approval timeline | 10-15 business days | 3-5 business days | 8-12 weeks provisioning | +| Best for | Transactional, marketing, support | Notifications, support, verification | High-volume marketing, 2FA | +| Cost | Lowest | Medium | Highest (setup + monthly + per-msg) | + +Source: [US/Canada SMS comparison](https://help.twilio.com/articles/360038173654-Comparison-of-SMS-messaging-in-the-US-and-Canada-for-long-codes-short-codes-and-toll-free-phone-numbers) + +### Compliance Programs Per Number Type + +Every US number type requires its own verification before traffic flows: + +- Local 10DLC -> A2P 10DLC registration: Brand registration (EIN, business identity) + Campaign registration (use case, sample messages, opt-in flow). Trust Score determines throughput. Sole Proprietors: ~1 SMS/sec, 1 campaign. Standard: ~15+ SMS/sec, scales with secondary vetting. [A2P overview](https://www.twilio.com/docs/messaging/compliance/a2p-10dlc) | [Quickstart](https://www.twilio.com/docs/messaging/compliance/a2p-10dlc/quickstart) + +- Toll-Free -> Toll-Free Verification (TFV): Business name, website, use case description, sample messages, opt-in type. Unverified toll-free numbers cannot send SMS to US/Canada. [Console onboarding](https://www.twilio.com/docs/messaging/compliance/toll-free/console-onboarding) | [Requirements](https://help.twilio.com/articles/5377174717595-Toll-Free-Message-Verification-for-US-Canada) + +- Short Code -> Carrier review at purchase: Application reviewed during 8-12 week provisioning. Available in 14 countries: US, Canada, UK, Germany, France, India, Brazil, Mexico, Argentina, Colombia, Dominican Republic, New Zealand, Spain, Sweden. [Short code guidelines by country](https://www.twilio.com/en-us/guidelines/short-code) | [What is a short code?](https://help.twilio.com/articles/223182068-What-is-a-Messaging-Short-Code-) + +- Twilio Verify -> Exempt. No registration needed - Verify handles compliance automatically. + +See `twilio-compliance-onboarding` for full registration details and gotchas. + +--- + +## How to Choose (US) + +- Need voice + SMS from same number? -> Local (10DLC) or toll-free. Short codes are SMS-only. +- Marketing at scale (>15 SMS/sec)? -> Short code (highest throughput) or 10DLC with secondary vetting + Messaging Service number pool +- Fastest time to send? -> Toll-free (3-5 day verification) or Twilio Verify (immediate, no registration) +- Customer support with local presence? -> Local number in customer's area code +- Transactional notifications? -> Toll-free (simpler registration) or 10DLC +- Verification OTPs? -> Twilio Verify (exempt from A2P, built-in Fraud Guard) +- Budget-constrained? -> 10DLC (lowest cost) - but plan for 10-15 day registration + +--- + +## Non-Phone Senders + +### Alphanumeric Sender IDs + +A branded name (up to 11 characters) displayed instead of a phone number. One-way only - recipients cannot reply. + +- Not supported in the US or Canada +- Supported in 100+ countries; some require pre-registration with documentation +- Some carriers impose minimum length - short IDs may display as "unknown" +- Add to Messaging Services for automatic sender selection by destination country + +Docs: [Alpha Sender in Messaging Services](https://www.twilio.com/docs/messaging/services/alphanumeric-sender-ids-in-messaging-services) | [International support by country](https://help.twilio.com/articles/223133767-International-support-for-Alphanumeric-Sender-ID) | [How to register](https://help.twilio.com/articles/20153208099611-How-to-Register-an-Alphanumeric-Sender-ID) + +### WhatsApp Business Senders + +A phone number registered with Meta's WhatsApp Business Platform via Twilio. + +- Requires WABA (WhatsApp Business Account) + sender approval +- Outbound requires pre-approved Message Templates (outside 24-hour service window) +- Direct customers: self-service Console signup or Senders API +- ISVs: Meta Tech Provider Program for customer onboarding + +Docs: [WhatsApp hub](https://www.twilio.com/docs/whatsapp) | [Getting started](https://help.twilio.com/articles/360007721954-Getting-Started-with-Twilio-for-WhatsApp) | [Self sign-up](https://www.twilio.com/docs/whatsapp/self-sign-up) + +### RCS Agents + +Branded sender with logo, rich cards, carousels, and suggested actions. Falls back to SMS automatically. + +- Requires carrier-level approval per country +- Testing phase: RCS only delivers to added test devices; others get SMS fallback +- Each RCS sender can only belong to ONE Messaging Service + +Docs: [RCS onboarding](https://www.twilio.com/docs/rcs/onboarding) | [RCS compliance guide](https://help.twilio.com/articles/49174994355355-RCS-Compliance-Onboarding-Guide) | [Regional availability](https://www.twilio.com/docs/rcs/regional) + +--- + +## Voice Trust: Number Reputation Programs + +If making outbound voice calls, these programs improve answer rates: + +| Program | What it does | Carriers | Prerequisites | +|---------|-------------|----------|---------------| +| STIR/SHAKEN | Level A attestation = trusted caller ID | US and Canada | Trust Hub Business Profile + EIN | +| Voice Integrity | Remediates spam/scam labels | T-Mobile, AT&T, Verizon (coming) | Approved Business Profile + US address | +| Branded Calling | Shows name + logo on caller ID | T-Mobile, Verizon (Public Beta) | STIR/SHAKEN + Trust Hub profile | +| CNAM | Displays business name on caller ID | US long codes only (not toll-free) | EIN or DUNS number | + +Priority order: STIR/SHAKEN first (required for Level A attestation) -> Voice Integrity (spam label remediation) -> Branded Calling (visual caller ID, mobile only) -> CNAM (simplest, lowest impact, landlines by default). + +Docs: [STIR/SHAKEN overview](https://www.twilio.com/docs/voice/trusted-calling-with-shakenstir) | [Voice Integrity overview](https://www.twilio.com/docs/voice/spam-monitoring-with-voiceintegrity) | [Branded Calling overview](https://www.twilio.com/docs/voice/branded-calling) | [CNAM overview](https://www.twilio.com/docs/voice/brand-your-calls-using-cnam) + +### Branded Calling: Prerequisites & Display Standards + +The call trust stack is layered - each product builds on the one below: + +``` +Layer 4: Enhanced Branded Calling (name + logo + call reason) + ↑ requires +Layer 3: Basic Branded Calling (business name display) + ↑ requires +Layer 2: Voice Integrity (spam label remediation) + ↑ requires +Layer 1: SHAKEN/STIR (attestation - auto-applied with approved profile) + ↑ requires +Layer 0: Primary Customer Profile (Trust Hub business identity) +``` + +Prerequisites for Basic Branded Calling: +1. Approved Primary Customer Profile in Trust Hub (EIN, business name, address, authorized rep) - 1-3 business days +2. SHAKEN/STIR - automatic once profile is approved +3. Signed Letter of Authorization (LOA) for the phone numbers +4. Basic Branded Calling trust product submitted + approved - 2-4 weeks + +Additional prerequisites for Enhanced Branded Calling: +5. Approved Voice Integrity trust product - 3-7 business days carrier propagation +6. Enhanced Branded Calling trust product - 3-6 weeks + +Phone number eligibility: +- Local and mobile numbers only - toll-free numbers are NOT eligible +- Must be Twilio-owned (not ported-in numbers pending transfer) +- Calls must originate via Programmable Voice (API or TwiML) - SIP Trunking calls are not branded +- Each number can only belong to one Branded Calling trust product at a time + +Display standards: + +| Asset | Basic | Enhanced | +|-------|-------|----------| +| Display name | Business name, ~32 char carrier limit, must match Trust Hub profile | Same | +| Logo | N/A | Square, min 300x300px, max 1MB, PNG/JPG, no text overlays | +| Call reason | N/A | Free-text, ~40 char carrier display limit (e.g., "Appointment Reminder") | + +Display name rules: +- Must match registered business name or documented trade name/DBA +- No phone numbers, URLs, or special characters +- Misleading names are rejected during review + +Call reason guidelines (Enhanced only): +- Must accurately describe the call purpose +- Cannot be generic ("Important Call") or misleading +- Set per trust product - cannot be changed per-call +- Keep under 40 characters for consistent carrier display + +Carrier support: +- T-Mobile: Basic + Enhanced (native) +- AT&T, Verizon: Voice Integrity spam remediation; Branded Calling display expanding +- Apple/iOS: Enhanced only, limited support + +CNAM (traditional caller ID): 15-character limit, text-only, works on landlines, propagates in 24-48 hours, no approval process needed. + +--- + +## International Numbers + +- ~25 countries have GA (self-service) SMS numbers. Many major markets are Private Offering - requires a request form and 1-6 week delivery. +- MMS only available in US, Canada, and Australia. Use WhatsApp or RCS for rich media elsewhere. +- Many countries require Regulatory Bundles (identity/address verification) before provisioning numbers. Non-compliant numbers risk deprovisioning. + +Docs: [Country SMS guidelines](https://www.twilio.com/en-us/guidelines/sms) | [Regulatory compliance](https://www.twilio.com/docs/phone-numbers/regulatory/getting-started) | [How to submit a bundle](https://help.twilio.com/articles/8338625205147-How-to-Submit-a-Regulatory-Bundle-for-Phone-Number-Regulatory-Compliance) | [Country regulatory requirements](https://www.twilio.com/en-us/guidelines/regulatory) + + +--- + +## CANNOT + +### Phone Numbers +- No mobile number type in the US - `availablePhoneNumbers('US').mobile.list()` returns 404. US numbers are classified as local or toll-free only. +- Cannot use both `voiceApplicationSid` and `voiceUrl` - Setting one auto-clears the other. Same for `smsApplicationSid` vs `smsUrl`. +- Cannot use `voiceApplicationSid` and `trunkSid` simultaneously - Setting one auto-deletes the other. +- `contains` pattern requires minimum 2 characters - Single-character patterns return 400. Wildcards `*` mid-pattern also fail. +- Geographic search is US/Canada only - `nearNumber`, `nearLatLong`, `inPostalCode`, `inRegion`, `inRateCenter`, `inLata` are ignored for non-US/CA numbers. +- No undo for number release - Once released, the number goes back to the pool. No grace period or reclaim mechanism. +- Address required for many international numbers - `addressRequirements` can be `none`, `any`, `local`, or `foreign`. Purchase fails without the required address/bundle. + +### Voice Trust +- Cannot guarantee call delivery - Carrier spam filters operate independently. Even Level A + Branded Calling + Voice Integrity can still be filtered. +- Cannot brand inbound calls - Branded Calling applies to outbound calls only. +- Cannot use Voice Integrity or Branded Calling outside the US - Voice Integrity and Branded Calling are currently US-only. STIR/SHAKEN is available in the US and Canada (CRTC-mandated since Nov 2021). +- Cannot bypass manual approval - Trust Hub vetting involves human review (1-3 business days for profiles, 2-6 weeks for Branded Calling). +- Cannot preserve attestation through `` - CallToken forwarding requires the Calls API or Conference Participants API. +- Cannot use Branded Calling with SIP Trunking - Calls must originate via Programmable Voice. +- Cannot use toll-free numbers with Branded Calling - Local and mobile numbers only. + +--- + +## Common Mistakes + +1. Buying numbers before understanding compliance - Each number type has its own registration program. Choosing 10DLC means 10-15 days of A2P registration before you can send. +2. Using local numbers for marketing without A2P - Messages get filtered (error 30034). All US 10DLC requires A2P registration. +3. Expecting toll-free to work immediately - Unverified toll-free numbers cannot send SMS to US/Canada at all. +4. Assuming MMS works internationally - Only US, Canada, Australia. Use WhatsApp or RCS elsewhere. +5. Choosing short code for voice - Short codes are SMS/MMS only. No voice capability. + +--- + +## Next Steps + +- Register your numbers: `twilio-compliance-onboarding` +- Set up Messaging Services with number pools: `twilio-messaging-services` +- Follow traffic rules after registration: `twilio-compliance-traffic` +- WhatsApp sender management: `twilio-whatsapp-manage-senders` diff --git a/plugins/twilio/skills/twilio-numbers-senders/assets/icon-large.png b/plugins/twilio/skills/twilio-numbers-senders/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-numbers-senders/assets/icon-small.png b/plugins/twilio/skills/twilio-numbers-senders/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Set up and manage Twilio Organizations for centralized account and user + governance. Covers the Organization > Account > Subaccount hierarchy, + roles (Owner/Admin/Standard), managed vs independent accounts, domain + registration, SSO enforcement, SCIM provisioning, and Organization + merging. Use this skill when managing multiple Twilio accounts or users + across teams. +--- + +## Overview + +Every Twilio customer automatically gets an Organization when they sign up (auto-created since May 2024 for new signups; since June 2024 for existing paying customers). An Organization is the top-level container that groups accounts, users, and security policies. The creation has no effect on existing account functionality. Most developers never need to touch it - but as soon as you have multiple accounts, teams, or compliance requirements (SSO, HIPAA), Organization setup becomes essential. + +Hierarchy: Organization > Accounts > Subaccounts + +| Layer | What it is | When you need it | +|-------|-----------|-----------------| +| Organization | Centralized governance: users, accounts, domains, SSO | Multiple teams or accounts, SSO, HIPAA designation | +| Account | Application boundary: all Twilio products, resources, billing live here | Always - you need at least one | +| Subaccount | Isolated partition under an account: separate resources, consolidated billing | Multi-tenant apps, per-customer isolation | + +--- + +## Organization vs Subaccount - When to Use Which + +| Dimension | Organization (Managed Accounts) | Subaccounts | +|-----------|----------------------------------|-------------| +| Management | Console UI + Organizations API | REST API (`/2010-04-01/Accounts`) | +| Billing | Independent per account | Consolidated to parent account | +| Account limit | 10 per Organization (default) | 1 per unupgraded account; 1,000 per upgraded account (contact AE for more) | +| User management | Full lifecycle: invite, roles, SSO, SCIM | None - no user concept | +| SSO/SCIM | Supported | Not applicable | +| HIPAA designation | Per-account toggle in Admin console | Inherits from parent (new only) | +| Resource isolation | Separate accounts, separate credentials | Separate but parent can access all | +| Cost | Free | Free | + +Rule of thumb: Use Organizations when different teams/users need separate billing and access control. Use Subaccounts when your application needs programmatic multi-tenant isolation with consolidated billing. + +--- + +## Organization Roles + +| Role | Capabilities | Limit | +|------|-------------|-------| +| Owner | Full control + sole authority to delete the Organization | 1 per Organization | +| Administrator | Invite/remove users, add/create accounts, modify settings | Unlimited | +| Standard User | Access only to specified accounts - no org management | Unlimited (default) | + +The Organization creator is automatically assigned the Owner role. + +--- + +## Setting Up Your Organization + +### Find Your Organization + +All Twilio customers have an Organization (auto-created at signup). Access it via: + +- Console > Settings (gear icon) - shows Organization settings, or +- Twilio Admin link in the top-right navigation - opens the Organization admin panel + +### Add Accounts to Your Organization + +Create a new account: +1. Console > Admin > Accounts +2. Click Create New Account +3. Name the account, select Twilio or Flex usage +4. Confirm - the account starts in trial mode with fresh defaults + +Import an existing account: +1. Console > Admin > Accounts > Add Existing Account +2. Enter the account's SID (find it in Console > Account > General settings) +3. The account owner receives an email and must confirm + +Requirement: The account owner's email must match your Organization's verified domain. + +### Account Types + +| Type | Description | +|------|-------------| +| Managed | Owned by your Organization - full lifecycle control | +| Independent | External account your users can access - you do NOT control it | +| Pending | Added but awaiting owner confirmation | + +### Transfer Account Ownership + +Only between managed users in the same Organization: +1. Console > Admin > Accounts > select account +2. Remove current owner, enter new owner's email or User SID +3. Save + +--- + +## Domain Registration + +Register your company's email domain to control how employees interact with Twilio. + +Console > Admin > Domains + +| Setting | Behavior | +|---------|----------| +| Restricted | Users with your domain email can't sign up unless explicitly invited | +| Auto-enrollment | Users who sign up with your domain automatically join your Organization | +| Blocked | Users with your domain email cannot join this Organization | + +Domain registration also enables Organization merging - the Prime org must have verified domains. + +Important: Common domains (gmail.com, hotmail.com, etc.) cannot be verified - you cannot invite users from common domains. Enter domains without "www." (e.g., `corporate.com`, not `www.corporate.com`). You can verify the same domain under multiple Organizations (with restrictions) or use subdomains (`stage.corporate.com`). + +--- + +## SSO and SCIM + +- SSO: Enforce Single Sign-On at the Organization level via your identity provider (Okta, Azure AD, etc.). See [SSO docs](https://www.twilio.com/docs/iam/organizations/sso). +- SCIM: Automate user provisioning and deprovisioning via the SCIM 2.0 API. See [SCIM docs](https://www.twilio.com/docs/iam/organizations/scim). + +When SSO is enabled on a verified domain, all users with that domain email must authenticate via SSO. + +--- + +## Organization Merging + +Combine two Organizations: the Prime absorbs the Candidate. + +Requirements: +- Prime must have verified domains +- Candidate Owner's email must match Prime's verified domain +- Candidate must have NO verified domains of its own + +Post-merge: Candidate ceases to exist. All accounts and users transfer to Prime. Billing and functionality unchanged. If Prime has SSO enabled, it applies to merged users. + +--- + +## HIPAA Designation + +Requires an executed BAA with Twilio. After BAA: + +1. Console > Admin > Accounts > select account +2. Enable HIPAA flag +3. Save + +Each account must be individually flagged - existing accounts do NOT auto-inherit. New accounts created after designation DO inherit. See `twilio-security-compliance-hipaa` for full HIPAA guidance. + +--- + +## User Management + +Users are separate from accounts. A user is defined by their login (email + password) and can own or have access to many accounts. + +- Users can only belong to ONE Organization - if they need access to multiple orgs, create a dedicated user per org (e.g., `user+org1@corporate.com`) +- Owner's accounts are auto-added - any account owned by the Organization Owner is automatically added to that Organization and cannot be "independent" +- New accounts by managed users are auto-added - accounts created by any managed user (Owner, Admin, Standard) automatically join the Organization +- New user signup behavior is controlled by domain settings (Restricted/Auto-enrollment/Blocked) + +Admin actions for managed users: +- Reset password: Admin Center > Users > Managed Users > select user > Reset Password (logs out user, sends 24-hour reset link) +- Reset 2FA: Admin Center > Users > Managed Users > select user > Reset 2FA (removes current 2FA number, prompts for new one on next login) +- Bulk user import: Available via Admin Center (contact Support if not enabled on your Organization) + +--- + +## CANNOT + +- Cannot create accounts via API at the Organization level - Account creation within Organizations is Console-only. Subaccount creation via REST API is separate and lives under the parent account. +- Cannot close or delete an Organization from Console - There is no self-service delete. To remove an Organization, merge it into another one. +- Cannot transfer ownership to an independent user - Account ownership transfers are restricted to managed users within the same Organization. +- Cannot merge Organizations if the Candidate has verified domains - Remove Candidate's domain verification first, or the merge will fail. +- Cannot assume configurations transfer to new accounts - New managed accounts start with fresh defaults. Product configurations, phone numbers, and settings do not inherit. +- Cannot manage independent accounts' lifecycle - You can grant your users access to independent accounts, but you cannot close, suspend, or modify them. +- Cannot have multiple Owners per Organization - Exactly one. Transfer ownership before the current Owner leaves the company. +- A user cannot belong to multiple Organizations - One user = one Organization. Use email aliases for multi-org access. +- Cannot verify common email domains - gmail.com, hotmail.com, etc. are not supported for domain verification or user invitations. +- Cannot invite users from unverified domains - Domain must be verified first before you can invite users with that domain email. +- Billing is NOT consolidated at the Organization level - Each managed account is billed independently. For consolidated billing, use subaccounts under a single parent account instead. + +--- + +## Next Steps + +- Account and subaccount setup: `twilio-account-setup` +- Authentication methods (API Keys, OAuth2): `twilio-security-api-auth` +- HIPAA account configuration: `twilio-security-compliance-hipaa` +- Credential security: `twilio-security-hardening` +- Docs: [Organizations overview](https://www.twilio.com/docs/iam/organizations) | [Managed accounts](https://www.twilio.com/docs/iam/organizations/managed-accounts) diff --git a/plugins/twilio/skills/twilio-organizations-setup/assets/icon-large.png b/plugins/twilio/skills/twilio-organizations-setup/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-organizations-setup/assets/icon-small.png b/plugins/twilio/skills/twilio-organizations-setup/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Send RCS Business Messages via Twilio. Covers compliance onboarding + (7-part US process), sender profile setup, sending rich cards and + carousels, SMS fallback, device support (Android + iOS 18 caveats), + and common errors. Use this skill when building RCS messaging or + onboarding an RCS sender. +--- + +## Overview + +RCS (Rich Communication Services) Business Messaging delivers branded, rich messages natively in the phone's default messaging app - no separate app needed. Messages show your brand logo, colors, and verified sender name. Supports rich cards, carousels, suggested actions, and media. + +RCS uses the same `messages.create()` API as SMS and WhatsApp. For the full channel comparison and onboarding sequence, see `twilio-messaging-overview`. + +### Device Support + +| Platform | Support | Notes | +|----------|---------|-------| +| Android | Most devices via Google Messages | Carrier must also support RCS in the region | +| iOS | iOS 18+ | P2P RCS is not the same as RCS Business Messaging. A device that sends RCS to other people may not receive RCS Business Messages - this depends on both Apple and the carrier. Check via `RcsCapabilityFetcher` before sending. | + +### Regional Availability + +RCS availability depends on carrier approval per country. See [RCS regional availability](https://www.twilio.com/docs/rcs/regional) for the current list. US carriers (T-Mobile, AT&T, Verizon) are supported. Global support is expanding. + +--- + +## Prerequisites + +- Twilio paid account - free trials cannot use RCS +- Messaging Service (RCS senders must be added to a Messaging Service) +- Environment variables: `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN` + - See `twilio-iam-auth-setup` for credential setup +- SDK: `pip install twilio` / `npm install twilio` + +--- + +## Compliance Onboarding (US) + +RCS onboarding takes 4-6 weeks minimum. A Twilio onboarding specialist reviews everything before carrier submission. You won't be charged until you go live. + +### Part 1: Sender Profile Setup + +Create your RCS Sender in Console with: + +| Asset | Requirements | +|-------|-------------| +| Display name | Must be unique - carriers reject identical names + logos | +| Logo | 224x224px, max 50KB, PNG/JPEG | +| Banner | 1140x448px, max 200KB | +| Accent color | Hex code, must have 4.5:1 contrast ratio vs white | +| Description | What your business does and why you're messaging | +| Phone number | Customer support contact number | +| Website | Must match business identity | + +### Part 2: Privacy Policy & Terms of Service + +- Privacy policy URL - must be publicly accessible +- Terms of Service URL - must be publicly accessible +- Both must cover SMS/RCS messaging, data handling, opt-out process +- The privacy policy must state that information will be shared with third parties for the purpose of transmitting RCS messages +- Some countries require local-language versions + +For US-specific compliance details, see the [RCS Compliance Onboarding Guide](https://help.twilio.com/articles/49174994355355-RCS-Compliance-Onboarding-Guide). + +### Part 3: Eligibility & Acceptable Use + +US carriers require: +- Legal business name matching EIN records +- EIN (Employer Identification Number) +- Business must not be on restricted industries list (cannabis, firearms, etc.) +- CTIA Messaging Principles and Best Practices handbook compliance + +### Part 4: Campaign Details + +- Use case category (transactional, promotional, OTP, mixed) +- Traffic volume estimates +- Campaign description with specific messaging scenarios +- For recurring messages: frequency, content types + +### Part 5: Opt-In & Consent + +- Describe how users opt in to receive RCS messages +- Must show explicit consent collection mechanism +- Opt-in must be specific to RCS/messaging (not buried in general ToS) +- HELP and STOP keyword handling required + +### Part 6: Sample Messages + +- 2+ sample messages that match your declared use case +- Must include opt-out language +- Must reflect actual message content (not generic) + +### Part 7: Common Rejection Reasons + +| Rejection reason | Fix | +|-----------------|-----| +| Display name not unique | Choose a distinct name - carriers reject duplicates | +| Logo/banner don't meet specs | Check dimensions and file size exactly | +| Privacy policy doesn't mention messaging | Add RCS/SMS data handling section | +| Sample messages don't match use case | Align samples with campaign description | +| Opt-in process too vague | Show specific UI/flow for consent collection | +| Business info doesn't match EIN | Legal name and EIN must match IRS records exactly | +| Media URLs not publicly accessible | All images/videos must be on public URLs - carriers verify during review | + +### Registration Flow + +1. Create RCS Sender in Console -> complete all 7 parts above +2. Test Phase - Add test devices, send and receive messages without carrier approval. Non-test devices get SMS fallback. +3. Compliance Submission - Twilio specialist reviews, then submits to Google + carriers: + - Google registration: authorized rep details, opt-in/opt-out policy, use case video, interaction flow + - US registration (additional): legal business name + EIN, traffic metrics, CTIA compliance, HELP/STOP examples +4. Carrier approval - Per-carrier, per-country. First carrier approval = you can go live in that country. +5. Go live - Add RCS Sender to your Messaging Service. Start sending. + +--- + +## Sending RCS Messages + +RCS uses the same `messages.create()` API. No address prefix needed - Twilio routes based on the sender type. + +Python +```python +import os +from twilio.rest import Client + +client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"]) + +message = client.messages.create( + messaging_service_sid="MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + to="+15558675310", + body="Your order has shipped! Track it here: https://example.com/track/12345" +) +print(message.sid, message.status) +``` + +Node.js +```javascript +const twilio = require("twilio"); +const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); + +const message = await client.messages.create({ + messagingServiceSid: "MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + to: "+15558675310", + body: "Your order has shipped! Track it here: https://example.com/track/12345", +}); +console.log(message.sid, message.status); +``` + +### Rich Cards & Carousels + +Use Content Templates for rich RCS messages (cards with images, titles, descriptions, and action buttons). Create templates via `twilio-content-template-builder`, then send with `contentSid`: + +Python +```python +message = client.messages.create( + messaging_service_sid="MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + to="+15558675310", + content_sid="HXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + content_variables='{"1": "Order #12345", "2": "$49.99"}' +) +``` + +### SMS Fallback + +When a recipient's device doesn't support RCS Business Messaging, Twilio automatically falls back to SMS - no code changes needed. This is handled at the Messaging Service level. + +- Fallback is automatic when the Messaging Service has both RCS sender and phone numbers +- If you send via `messagingServiceSid`, Twilio checks RCS capability first, then falls back to SMS +- Without Twilio's fallback: the message simply fails to deliver (no automatic retry to SMS) + +--- + +## Multiple RCS Senders + +A single brand can have multiple RCS senders, but each must have a distinct use case (e.g., one for transactional, one for marketing). The use case must be clearly different - carriers reject duplicate-purpose senders for the same brand. + +- Each sender has its own display name, logo, and campaign details +- All senders go through independent carrier approval +- Each sender can only belong to one Messaging Service + +## ISV Path + +ISVs (Independent Software Vendors) registering RCS senders for client businesses: + +- Can register on behalf of clients using the same compliance process +- Each client business needs its own RCS Sender with its own branding +- The ISV's Twilio account can host multiple RCS Senders +- Programmatic sender creation at scale is not supported - each sender must be created individually in Console + +--- + +## Common Errors + +| Error | Meaning | Fix | +|-------|---------|-----| +| Sender not approved | RCS sender hasn't completed carrier approval | Complete compliance onboarding; use test devices in the meantime | +| Device not capable | Recipient can't receive RCS Business Messages | Twilio falls back to SMS automatically if fallback is configured | +| Media URL inaccessible | Rich card image/video not publicly accessible | Host media on public URLs | +| Display name rejected | Name conflicts with existing RCS sender | Choose a unique display name | + +--- + +## CANNOT + +- Cannot use RCS on free trial accounts - Paid account required +- Cannot send RCS without a Messaging Service - RCS senders must be added to a Messaging Service +- Cannot add an RCS sender to multiple Messaging Services - Each sender belongs to one service only +- Cannot create RCS senders programmatically at scale - Console-only, one at a time +- Cannot skip carrier approval - Even with Google approval, each carrier must independently approve in each country +- Cannot guarantee RCS delivery to iOS - iOS 18 supports P2P RCS, but RCS Business Messaging support depends on Apple + carrier. Always have SMS fallback. +- Cannot control fallback behavior per-message - Fallback to SMS is automatic at the Messaging Service level when both RCS and SMS senders are present +- Cannot send RCS to landlines - Mobile-only channel +- Cannot use unique display names that match existing senders - Carriers enforce uniqueness globally +- Cannot update sender profile after approval without re-review - Profile changes trigger a new carrier review cycle + +--- + +## Next Steps + +- Channel overview and onboarding guide: `twilio-messaging-overview` +- Create rich message templates: `twilio-content-template-builder` +- Set up Messaging Service for sender pool + fallback: `twilio-messaging-services` +- Compliance registration for other channels: `twilio-compliance-onboarding` +- Number and sender type selection: `twilio-numbers-senders` diff --git a/plugins/twilio/skills/twilio-rcs-messaging/assets/icon-large.png b/plugins/twilio/skills/twilio-rcs-messaging/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-rcs-messaging/assets/icon-small.png b/plugins/twilio/skills/twilio-rcs-messaging/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Manage regulatory compliance for international phone numbers. Covers + what bundles are, which countries require them, how to create End-Users + and Supporting Documents, evaluate and submit bundles, fix evaluation + failures, update bundles when regulations change, and ISV multi-account + patterns. Use this skill when provisioning numbers outside the US. +--- + +## Overview + +Phone numbers are national resources - many countries require identity verification of the end-user before provisioning. A Regulatory Bundle is a container holding an End-User record + Supporting Documents that proves your right to use numbers in a specific country. + +Not all countries require bundles - check the [Regulatory Guidelines page](https://www.twilio.com/en-us/guidelines/regulatory) for country-specific requirements. If a country requires a bundle, provisioning fails without one. + +--- + +## Key Concepts + +| Resource | What it is | +|----------|-----------| +| Regulation | Country-specific requirement defining what End-User types and document types are needed | +| Bundle | Container that holds an End-User + Supporting Documents for a specific regulation | +| End-User | The entity answering calls or receiving messages (`individual` or `business` type) | +| Supporting Document | Identity/address verification documents (business registration, proof of address, etc.) | +| Evaluation | Synchronous check that validates a bundle against its regulation before submission | +| Item Assignment | Links an End-User or Supporting Document to a Bundle | + +--- + +## Quickstart: Provision a Number with a Bundle + +### Step 1 - Query the Regulation + +Find out what's required for the country and number type: + +Python +```python +import os, requests + +account_sid = os.environ["TWILIO_ACCOUNT_SID"] +auth_token = os.environ["TWILIO_AUTH_TOKEN"] + +# What does Germany require for local business numbers? +regulations = requests.get( + "https://numbers.twilio.com/v2/RegulatoryCompliance/Regulations", + params={"IsoCountry": "DE", "NumberType": "local", "EndUserType": "business"}, + auth=(account_sid, auth_token) +).json() + +for reg in regulations["results"]: + print(f"Regulation: {reg['sid']}") + print(f"Requirements: {reg['requirements']}") +``` + +### Step 2 - Create an End-User + +Python +```python +end_user = requests.post( + "https://numbers.twilio.com/v2/RegulatoryCompliance/EndUsers", + data={ + "FriendlyName": "Acme GmbH", + "Type": "business", + "Attributes": '{"business_name": "Acme GmbH", "business_registration_number": "HRB12345"}' + }, + auth=(account_sid, auth_token) +).json() +``` + +### Step 3 - Upload Supporting Documents + +Python +```python +document = requests.post( + "https://numbers.twilio.com/v2/RegulatoryCompliance/SupportingDocuments", + data={ + "FriendlyName": "Acme Business Registration", + "Type": "business_registration", + "Attributes": '{"business_name": "Acme GmbH"}' + }, + auth=(account_sid, auth_token) +).json() +``` + +### Step 4 - Create a Bundle and Assign Items + +Python +```python +# Create the bundle +bundle = requests.post( + "https://numbers.twilio.com/v2/RegulatoryCompliance/Bundles", + data={ + "FriendlyName": "Germany Local - Acme", + "RegulationSid": regulations["results"][0]["sid"], + "IsoCountry": "DE", + "EndUserType": "business", + "Email": "compliance@acme.com" + }, + auth=(account_sid, auth_token) +).json() + +bundle_sid = bundle["sid"] + +# Assign End-User to bundle +requests.post( + f"https://numbers.twilio.com/v2/RegulatoryCompliance/Bundles/{bundle_sid}/ItemAssignments", + data={"ObjectSid": end_user["sid"]}, + auth=(account_sid, auth_token) +) + +# Assign Supporting Document to bundle +requests.post( + f"https://numbers.twilio.com/v2/RegulatoryCompliance/Bundles/{bundle_sid}/ItemAssignments", + data={"ObjectSid": document["sid"]}, + auth=(account_sid, auth_token) +) +``` + +### Step 5 - Evaluate and Submit + +Python +```python +# Run evaluation (synchronous - returns field-level failures) +evaluation = requests.post( + f"https://numbers.twilio.com/v2/RegulatoryCompliance/Bundles/{bundle_sid}/Evaluations", + auth=(account_sid, auth_token) +).json() + +if evaluation["status"] == "noncompliant": + for violation in evaluation["results"]: + print(f"Field: {violation['friendly_name']} - {violation['description']}") + # Fix the issues, then re-evaluate +else: + # Submit for review + requests.post( + f"https://numbers.twilio.com/v2/RegulatoryCompliance/Bundles/{bundle_sid}", + data={"Status": "pending-review"}, + auth=(account_sid, auth_token) + ) +``` + +### Step 6 - Provision Number with Bundle + +Once the bundle is approved: + +Python +```python +from twilio.rest import Client +client = Client(account_sid, auth_token) + +number = client.incoming_phone_numbers.create( + phone_number="+4930xxxxxxx", + bundle_sid=bundle_sid +) +``` + +--- + +## Updating an Approved Bundle + +When regulations change, you'll receive an email. Update without deprovisioning numbers: + +1. Copy the approved bundle into a mutable state via the Bundle Copies resource +2. Update the End-User or Supporting Document on the copy +3. Re-evaluate the copy +4. Replace items in the original bundle via the Replace Items resource + +Phone numbers remain provisioned throughout this process. + +Alternative: Create a new bundle -> get it approved -> remap numbers to the new bundle. + +Docs: [Bundle Copies](https://www.twilio.com/docs/phone-numbers/regulatory/api/bundles-copies) | [Replace Items](https://www.twilio.com/docs/phone-numbers/regulatory/api/bundles-replace-items) + +--- + +## ISV / Multi-Account Pattern + +If managing Twilio subaccounts for multiple customers: + +- Each customer needs their own bundle - Do not reuse your business information in customer bundles +- Use the Bundle Clones resource to duplicate bundle structures across subaccounts +- End-User records must reflect the actual end-user (your customer), not you + +Docs: [Bundle Clones](https://www.twilio.com/docs/phone-numbers/regulatory/api/bundles-clones) + +--- + +## CANNOT + +- Cannot provision numbers without required bundles - Provisioning fails immediately. Check Regulations resource first. +- Cannot reuse one bundle across different number types - Each bundle is tied to a specific regulation (country + number type + end-user type). +- Locality-matching addresses required in ~33 countries - Germany (and others) require the End-User address to be within the region of the phone number prefix, not just any address in the country. US HQ address will fail for a Berlin number. +- Cannot hardcode regulation requirements - Regulations change periodically. Always query the Regulations resource dynamically. +- Do not create a new bundle when evaluation fails - Fix the existing bundle. Creating new ones wastes time and clutters your account. +- Cannot reuse your ISV info in customer bundles - Bundles must represent the actual end-user. Twilio audits this. +- Some markets are business-only - Individual provisioning not allowed. Check the `EndUserType` in the Regulation. + +--- + +## Next Steps + +- Choose number type before provisioning: `twilio-numbers-senders` +- Register numbers after provisioning: `twilio-compliance-onboarding` +- Country-specific requirements: [Regulatory Guidelines](https://www.twilio.com/en-us/guidelines/regulatory) +- API reference: [Regulatory Compliance API](https://www.twilio.com/docs/phone-numbers/regulatory/api) diff --git a/plugins/twilio/skills/twilio-regulatory-compliance-bundles/assets/icon-large.png b/plugins/twilio/skills/twilio-regulatory-compliance-bundles/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-regulatory-compliance-bundles/assets/icon-small.png b/plugins/twilio/skills/twilio-regulatory-compliance-bundles/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Handle rate limits, retries, and failures when building on Twilio at + scale. Covers 429 exponential backoff with jitter, per-number throughput + limits, StatusCallback resilience, thin-receiver pattern, and fallback + chains. Use this skill whenever sending messages or making calls at + volume, or when building production-grade Twilio integrations. +--- + +## Overview + +Twilio enforces per-resource rate limits. At scale, 429 errors are expected behavior - not bugs. This skill teaches the patterns that prevent production failures: exponential backoff, throughput management, and resilient callback handling. + +429 concurrency errors are not well documented - implement exponential backoff with ±10% jitter. + +--- + +## Prerequisites + +- A working Twilio integration (any product) +- Understanding of your expected volume (messages/sec, calls/sec) +- StatusCallback URLs configured - see `twilio-messaging-services`, `twilio-sms-send-message` + +--- + +## Key Patterns + +### 1. Exponential Backoff with Jitter + +When you receive a 429 (Too Many Requests), wait and retry. Naive fixed-interval retry creates thundering herds. Use exponential backoff with randomized jitter. + +Python +```python +import time, random, requests + +def send_with_backoff(client, to, body, messaging_service_sid, max_retries=5): + for attempt in range(max_retries): + try: + message = client.messages.create( + to=to, + body=body, + messaging_service_sid=messaging_service_sid, + status_callback="https://yourapp.com/status" + ) + return message + except Exception as e: + if hasattr(e, 'status') and e.status == 429: + # Exponential backoff: 100ms, 200ms, 400ms, 800ms, 1600ms + base_delay = 0.1 * (2 attempt) + # Add ±10% jitter to prevent thundering herd + jitter = base_delay * 0.1 * (2 * random.random() - 1) + delay = min(base_delay + jitter, 30) # cap at 30 seconds + time.sleep(delay) + else: + raise # Non-429 errors: don't retry, investigate + raise Exception(f"Failed after {max_retries} retries") +``` + +Node.js +```node +async function sendWithBackoff(client, to, body, messagingServiceSid, maxRetries = 5) { + for (let attempt = 0; attempt < maxRetries; attempt++) { + try { + return await client.messages.create({ + to, + body, + messagingServiceSid, + statusCallback: "https://yourapp.com/status", + }); + } catch (err) { + if (err.status === 429) { + // Exponential backoff: 100ms, 200ms, 400ms, 800ms, 1600ms + const baseDelay = 100 * Math.pow(2, attempt); + // Add ±10% jitter + const jitter = baseDelay * 0.1 * (2 * Math.random() - 1); + const delay = Math.min(baseDelay + jitter, 30000); // cap at 30s + await new Promise(r => setTimeout(r, delay)); + } else { + throw err; // Non-429: don't retry + } + } + } + throw new Error(`Failed after ${maxRetries} retries`); +} +``` + +Parameters: +- Initial delay: 100ms +- Multiplier: 2x per attempt +- Jitter: ±10% of base delay (randomized) +- Max delay: 30 seconds +- Max retries: 5 (covers up to ~3.2 second base delay) + +### 2. Per-Number Throughput Limits + +These limits are not prominently documented: + +| Number type | SMS throughput | Voice throughput | Notes | +|-------------|---------------|-----------------|-------| +| Local (long code) | ~1 SMS/sec | 1 concurrent call | Lowest cost, lowest throughput | +| Toll-free | ~3 SMS/sec | - | Faster verification (3-5 days) | +| Short code | 10-100 SMS/sec | - | Highest throughput, 8-12 week provisioning, expensive | +| Messaging Service (pool) | Sum of all numbers in pool | - | Multiply throughput by adding numbers | + +Throughput opacity: Sending velocity and queue depth are opaque - there is no dashboard showing messages per second. Use Messaging Services to multiply throughput by pooling numbers. A pool of 10 long codes = ~10 SMS/sec. + +### 3. Bulk Send Pattern + +For sending to large lists, use a rate-limited dispatch loop: + +Python +```python +import asyncio +from collections import deque + +async def bulk_send(client, recipients, body, messaging_service_sid, rate_per_second=10): + """Send to a list of recipients with rate limiting and backoff.""" + queue = deque(recipients) + results = [] + + while queue: + batch = [] + for _ in range(min(rate_per_second, len(queue))): + batch.append(queue.popleft()) + + for recipient in batch: + try: + msg = send_with_backoff(client, recipient, body, messaging_service_sid) + results.append({"to": recipient, "sid": msg.sid, "status": "sent"}) + except Exception as e: + results.append({"to": recipient, "error": str(e), "status": "failed"}) + + if queue: # Don't sleep after last batch + await asyncio.sleep(1) # 1 second between batches + + return results +``` + +Key: Set `rate_per_second` based on your number pool size, not your desired speed. Sending faster than your pool supports just generates 429s. + +> Compliance: Before bulk sending, verify recipient consent (opt-in records), respect quiet hours, and implement maximum batch size limits. Monitor for anomalous send patterns that could indicate abuse. + +### 4. StatusCallback Resilience + +At scale, StatusCallbacks create their own load problem. + +The math: 50 concurrent calls × 6 status events per call = 300 webhook invocations per second. Twilio Functions allow 30 concurrent executions per service. + +Thin-receiver pattern - receive, queue, respond immediately: + +Node.js (Express) +```node +const { Queue } = require("bullmq"); +const statusQueue = new Queue("twilio-status"); + +// Thin receiver: accept callback, queue it, respond 200 immediately +app.post("/status", async (req, res) => { + await statusQueue.add("status-event", { + callSid: req.body.CallSid, + callStatus: req.body.CallStatus, + timestamp: Date.now(), + }); + res.sendStatus(200); // Respond FAST - Twilio will retry on timeout +}); + +// Process asynchronously +const worker = new Worker("twilio-status", async (job) => { + const { callSid, callStatus } = job.data; + await updateDatabase(callSid, callStatus); +}); +``` + +Python (Flask + Celery) +```python +@app.route("/status", methods=["POST"]) +def status_callback(): + # Queue for async processing + process_status.delay( + call_sid=request.form["CallSid"], + call_status=request.form["CallStatus"] + ) + return "", 200 # Respond FAST + +@celery.task +def process_status(call_sid, call_status): + update_database(call_sid, call_status) +``` + +Idempotency key: Use `{CallSid}-{CallStatus}` as a composite key. Twilio retries on timeout, which can cause duplicate callbacks. Deduplicate before processing. + +### 5. Fallback Chains + +When delivery on one channel fails, escalate to the next: + +Python +```python +async def send_with_fallback(client, to, message, messaging_service_sid): + """Try SMS -> Voice -> Email fallback chain.""" + + # Try SMS first + try: + msg = client.messages.create( + to=to, body=message, messaging_service_sid=messaging_service_sid, + status_callback="https://yourapp.com/status" + ) + # Wait for delivery confirmation via StatusCallback + # If undelivered after timeout, fall through to voice + return {"channel": "sms", "sid": msg.sid} + except Exception: + pass # SMS failed, try voice + + # Fallback to voice + try: + call = client.calls.create( + to=to, from_="+15551234567", + twiml=f"{message}", + status_callback="https://yourapp.com/call-status" + ) + return {"channel": "voice", "sid": call.sid} + except Exception: + pass # Voice failed, try email + + # Last resort: email + # Use SendGrid - see twilio-sendgrid-email-send + return {"channel": "email", "status": "queued"} +``` + +### 6. Voice Concurrency Limits + +| Resource | Default limit | Notes | +|----------|--------------|-------| +| Concurrent calls per account | 1 (trial) / variable (paid) | Request increase via support | +| Calls per second (CPS) | 1 CPS (default) | Increase via support for outbound campaigns | +| Conference participants | 250 per conference | | +| Twilio Functions concurrent | 30 per service | Use thin-receiver pattern above | + +For outbound campaigns, request CPS increase before launch - not during. + +### 7. Webhook Timeout Handling + +Twilio expects a response within 15 seconds for voice webhooks and 15 seconds for messaging webhooks. If your endpoint doesn't respond: +- Voice: Twilio hangs up or falls back to `voiceFallbackUrl` +- Messaging: Twilio retries the callback + +Always configure fallback URLs: +```python +# On phone number configuration +number = client.incoming_phone_numbers(phone_sid).update( + voice_url="https://yourapp.com/voice", + voice_fallback_url="https://yourapp.com/voice-fallback", # backup endpoint + sms_url="https://yourapp.com/sms", + sms_fallback_url="https://yourapp.com/sms-fallback" +) +``` + +--- + +## Monitoring Checklist + +Set up these alerts before going to production: + +| Metric | Alert threshold | How to track | +|--------|----------------|-------------| +| 429 error rate | > 5% of requests | Count 429s in your backoff handler | +| Delivery failure rate | > 2% of messages | StatusCallback `failed`/`undelivered` events | +| Webhook response time | > 5 seconds p95 | Your APM tool (DataDog, New Relic) | +| Queue depth | Growing over 5 minutes | Your message queue metrics | +| Concurrent calls | > 80% of limit | Twilio Usage API or Event Streams | + +Twilio's built-in alerting systems are under-used - end-users often discover issues before developers do. Configure StatusCallbacks + Event Streams for delivery failure alerts on every integration. + +--- + +## CANNOT + +- Cannot avoid 429 errors on any Twilio API - Backoff patterns apply to all APIs (Messaging, Voice, Verify, Lookup) +- Cannot increase per-number throughput - Add more numbers via Messaging Services instead +- Cannot configure StatusCallback retry behavior - Twilio retries on timeout automatically; not configurable +- Cannot exceed Twilio Functions limits - 30 concurrent executions/service, 10-second timeout, 256 MB memory +- Cannot use a native Twilio rate limiting API - You must implement rate limiting in your application + +--- + +## Next Steps + +- Messaging at scale: `twilio-messaging-services` +- Monitor delivery: `twilio-sms-send-message` (StatusCallbacks) +- Debug failures: `twilio-debugging-observability` +- Compliance for bulk sends: `twilio-compliance-traffic` diff --git a/plugins/twilio/skills/twilio-reliability-patterns/assets/icon-large.png b/plugins/twilio/skills/twilio-reliability-patterns/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-reliability-patterns/assets/icon-small.png b/plugins/twilio/skills/twilio-reliability-patterns/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Choose the right Twilio authentication method and implement it correctly. + Covers Auth Token (testing only), API Keys (production standard), OAuth2 + client_credentials (time-limited bearer tokens), Access Tokens (client-side + SDKs), and test credentials. Use this skill before making any Twilio API + calls in production. +--- + +## Overview + +Twilio supports four authentication methods. Choosing the wrong one is a security risk - Auth Tokens in production code are the most common credential leak. + +| Method | Use for | Token lifetime | Revocable individually | +|--------|---------|---------------|----------------------| +| Auth Token | Local testing only | Permanent (until rotated) | No - rotation invalidates all integrations using that token and breaks webhook signature validation; API keys (SK-prefixed) are unaffected | +| API Key + Secret | Production server-side | Permanent (until deleted) | Yes | +| OAuth2 Bearer Token | Production server-side (enhanced) | 1 hour | Expires automatically | +| Access Token (JWT) | Client-side SDKs (Voice, Video, Chat) | Up to 24 hours | No - delete issuing API key | + +Decision framework: +- Building a quick prototype? -> Auth Token (but switch to API Key before deploying) +- Production server-side code? -> API Key + Secret (simplest production auth) or OAuth2 (time-limited tokens) +- Browser/mobile client needs to connect? -> Access Token (JWT) generated server-side +- Running tests without charges? -> Test credentials with magic numbers + +--- + +## API Key Authentication (Production Standard) + +Create: Console -> Account -> API keys & tokens -> Create API key + +| Key type | Access | Create via | +|----------|--------|-----------| +| Main | Full account access | Console only | +| Standard | All resources except /Accounts and /Keys endpoints | Console or API | +| Restricted | Specific resources only (up to 100 permissions) | Console or v1 IAM API only | + +Python +```python +import os +from twilio.rest import Client + +client = Client( + os.environ["TWILIO_API_KEY"], # SKxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + os.environ["TWILIO_API_SECRET"], + os.environ["TWILIO_ACCOUNT_SID"] # required as third argument +) +``` + +Node.js +```node +const client = require("twilio")( + process.env.TWILIO_API_KEY, + process.env.TWILIO_API_SECRET, + { accountSid: process.env.TWILIO_ACCOUNT_SID } +); +``` + +--- + +## OAuth2 Authentication (Client Credentials) + +Time-limited bearer tokens that expire after 1 hour. More secure than permanent API keys for server-to-server communication. + +### Step 1 - Create an OAuth App + +Create an OAuth App in the Twilio Console to get a Client ID and Client Secret. + +### Step 2 - Request a Bearer Token + +cURL +```bash +curl -X POST 'https://oauth.twilio.com/v2/token' \ + -H 'Content-Type: application/x-www-form-urlencoded' \ + -d 'client_id={ClientID}' \ + -d 'client_secret={ClientSecret}' \ + -d 'grant_type=client_credentials' +``` + +Response: +```json +{ + "access_token": "{BearerToken}", + "token_type": "Bearer", + "expires_in": 3600 +} +``` + +### Step 3 - Use the Bearer Token + +```bash +curl 'https://api.twilio.com/2010-04-01/Accounts/{AccountSID}/Messages.json' \ + -H 'Authorization: Bearer {BearerToken}' +``` + +### SDK Support + +OAuth2 is supported in all Twilio SDKs: + +| Language | Minimum version | +|----------|----------------| +| Java | 10.6.0 | +| C#/.NET | 7.6.0 | +| Node.js | 5.4.0 | +| Python | 9.4.1 | +| Ruby | 7.4.0 | +| PHP | 8.5.0 | +| Go | 1.25.1 | + +Docs: [OAuth access tokens](https://www.twilio.com/docs/iam/oauth-apps/oauth-access-token) | [Segment OAuth connections](https://www.twilio.com/docs/segment/connections/oauth) + +--- + +## Access Tokens (Client-Side SDKs) + +Short-lived JWTs for authenticating browser/mobile clients. Generate server-side, pass to the client. + +Python +```python +from twilio.jwt.access_token import AccessToken +from twilio.jwt.access_token.grants import VoiceGrant + +token = AccessToken( + os.environ["TWILIO_ACCOUNT_SID"], + os.environ["TWILIO_API_KEY"], + os.environ["TWILIO_API_SECRET"], + identity="user-123", + ttl=3600 +) +token.add_grant(VoiceGrant(outgoing_application_sid="APxxxx")) +print(token.to_jwt()) +``` + +Grant types: `VoiceGrant`, `VideoGrant`, `ChatGrant` (Conversations), `SyncGrant` + +--- + +## Test Credentials + +Make API calls without charges. Find at Console -> Account -> API keys & tokens -> Test credentials. + +Magic numbers: `+15005550006` (valid), `+15005550001` (invalid, error 21211), `+15005550007` (no SMS, error 21612) + +--- + +## CANNOT + +- Standard keys cannot access /Accounts or /Keys endpoints - Returns 20003 (401). Use Auth Token or Main key. +- Cannot create restricted keys via v2010 API - Silently creates a standard key instead. Use v1 IAM API. +- Restricted keys cannot generate Access Tokens - Only Standard and Main keys can. +- Cannot revoke individual Access Tokens - Valid until expiration (max 24h). Delete the issuing API key to revoke all. +- OAuth2 only supports `client_credentials` grant - No refresh tokens, no authorization code flow. +- OAuth2 tokens expire after 1 hour - Your application must handle token refresh. +- API Key Secret shown only at creation - Cannot be retrieved afterward. +- Auth Token rotation does not affect API keys - It invalidates all integrations using `AccountSID:AuthToken` and breaks webhook signature validation, but API keys (SK-prefixed) are independent and unaffected. This is why API keys are recommended for production from day one. +- Test credentials work with only 4 endpoints - Messages, Calls, IncomingPhoneNumbers, Lookups. All others return 403. + +--- + +## Next Steps + +- Account setup and sub-accounts: `twilio-account-setup` +- HIPAA account configuration: `twilio-security-compliance-hipaa` +- Webhook signature validation: `twilio-webhook-architecture` +- Credential security patterns: `twilio-security-hardening` diff --git a/plugins/twilio/skills/twilio-security-api-auth/assets/icon-large.png b/plugins/twilio/skills/twilio-security-api-auth/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-security-api-auth/assets/icon-small.png b/plugins/twilio/skills/twilio-security-api-auth/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Configure Twilio accounts for HIPAA compliance. Covers BAA requirements, + HIPAA Project designation (self-service and support), eligible services + list, per-product requirements (Voice, SMS, ConversationRelay, Conversation Intelligence, + Flex, Verify), message redaction, and what is NOT eligible. Use this + skill when developers are building healthcare workflows on Twilio. +--- + +## Overview + +HIPAA compliance on Twilio is a shared responsibility - Twilio provides eligible services and configuration tools, but your application must architect correctly. Getting this wrong means PHI exposure and compliance violations. + +Sequence: Execute BAA -> Designate HIPAA Project(s) -> Use only eligible services -> Follow per-product requirements + +--- + +## Step 1: Execute a BAA + +- Contact your Twilio Account Representative to execute a Business Associate Addendum +- Purchase a Twilio Editions package that includes HIPAA Accounts +- BAA is required before any PHI touches Twilio infrastructure + +--- + +## Step 2: Designate HIPAA Project(s) + +### Self-Service (BAA initiated after June 6, 2024) + +1. Create an Organization in Twilio Console +2. Link accounts/projects/subaccounts to the Organization +3. Console -> Twilio Admin -> Accounts -> Select account -> Enable HIPAA flag +4. Save + +### Support Ticket (BAA initiated before June 6, 2024) + +Open a Support ticket through Console to request HIPAA designation for specific accounts/projects/subaccounts. + +### Subaccount Behavior + +- Existing subaccounts are NOT auto-designated - Must be individually flagged +- New subaccounts created AFTER designation DO auto-inherit HIPAA status +- Verify each subaccount's HIPAA flag - don't assume inheritance + +### What Changes When HIPAA Is Enabled + +- Console auto-logoff after 15 minutes of inactivity +- Account exempt from certain content moderation (but still subject to carrier complaint review) +- No PHI in support tickets - use SIDs (CallSid, MessageSid) instead of phone numbers + +--- + +## HIPAA Eligible Services + +### Eligible (use these for PHI workflows) + +| Category | Services | +|----------|----------| +| Voice | Programmable Voice, Recordings*, Transcription*, Media Streams*, ConversationRelay*, Conversational Intelligence for Voice*, SIP Interface*, Elastic SIP Trunking*, Voice Insights, AMD, ``, Conference, Coaching, Transfers | +| SMS | Programmable SMS, MMS, Long Codes, Toll-Free, Short Codes, Messaging Services (opt-out, fallback, geomatch, sticky sender, scheduling, link shortening) | +| Identity | Verify (SMS + Voice + Push only), Lookup | +| Conversations | Chat, SMS, MMS, Group Texting (NOT WhatsApp) | +| Flex | Voice, SMS, Chat, Conversations, Webchat 3.x.x*, TaskRouter, Proxy, Flex Insights* | +| Segment | Connections (Sources, Destinations*, Functions*), Reverse ETL*, Unify, Engage Foundations*, Protocols, Privacy Portal* | +| Runtime | Studio*, Functions, Debugger, API Explorer, Sync, Private Assets*, TwiML Bin* | +| Data | Event Streams | + +*Items marked with * require additional configuration per "Architecting for HIPAA on Twilio" guidance.* + +### NOT Eligible (do NOT use for PHI) + +- WhatsApp - Meta does not offer a BAA +- SendGrid Email (including Email in Flex and Verify Email channel) +- AI Assistants (including Voice for AI Assistants) +- Verify Fraud Guard +- Conversational Intelligence for Conversations (only Voice channel is eligible) +- Agent Copilot, Unified Profiles in Flex +- Engage Premier, Generative Audiences, Campaigns +- Twilio Marketplace add-ons - even with third-party BAA +- Autopilot +- Flex Webchat 2.x.x (must migrate to 3.x.x) + +Geographic restriction: Only US area codes for Voice and SMS HIPAA traffic. + +--- + +## Per-Product Requirements + +### Voice & Recordings + +- HTTP auth required for recording URLs - Enable in Console -> Voice Settings. Recording URLs are public by default. +- Voice Recording Encryption recommended - Encrypts with your public key before cloud storage +- ConversationRelay: Your AI Provider must have their own BAA. Cannot use for clinical/medical decision-making. +- Conversation Intelligence for Voice: Only Azure OpenAI for generative operators. No PHI in operator prompts. Data use auto-disabled for HIPAA accounts. PII Redaction recommended (auto-redacts 21 PHI field types). + +### SMS & MMS + +- HTTP auth required for MMS Media URLs - Enable in Console -> Messaging -> Settings -> General +- Message Redaction recommended - Redacts message bodies and phone numbers from Console/API/support +- No PHI in Message Tags - custom attributes in Message Tagging must not contain PHI +- Message Redaction prerequisites: + 1. Disable Sticky Sender and Fallback to Long Code on Messaging Services + 2. Contact Support to disable built-in STOP filtering (then implement custom STOP handling) + 3. Set all webhooks to POST (GET logs params for 7 days, defeating redaction) + 4. Incompatible with Studio, Flex, and Conversations + +### Verify + +- Only SMS, Voice, and Push channels - Email channel is NOT eligible +- Fraud Guard is NOT eligible - do not enable for HIPAA workflows + +### Flex + +- Flex Insights: Twilio auto-redacts PII from TaskRouter attributes (names, phone, email). Visual waveform and speech metrics disabled. +- Customer must: Ensure no PHI in preserved Attribute fields, Comments, or Assessments. Implement session timeout (Flex has no built-in timeout). Secure Flex Plugins for HIPAA. +- No WhatsApp, Facebook Messenger, or SendGrid Email in Flex HIPAA workflows + +### Event Streams + +- Customer responsible for HIPAA-compliant sink configuration (e.g., AWS Kinesis requires Amazon's HIPAA architecture) +- Non-eligible product event types must not process PHI + +--- + +## CANNOT + +- Cannot use WhatsApp for HIPAA workflows - Meta does not offer a BAA. Applies to all Twilio products (Conversations, Flex, Frontline). +- Cannot use SendGrid Email - Not HIPAA eligible in any context (Verify, Flex, standalone). +- Cannot use Verify Fraud Guard or Email channel - Not eligible. Only SMS, Voice, Push. +- Cannot use AI Assistants - Even with ConversationRelay, AI Assistants integration is not eligible. +- Cannot use non-US area codes - Voice and SMS HIPAA traffic limited to US area codes. +- Cannot put PHI in support tickets - Use SIDs for troubleshooting. Use Console chat, email, or Support Center. +- Cannot assume subaccount HIPAA inheritance - Existing subaccounts must be individually flagged. +- Cannot use GET webhooks with Message Redaction - GET parameters are logged for 7 days. +- Cannot use Marketplace add-ons - Even with a third-party BAA, Marketplace is not eligible. +- Cannot use Conversation Intelligence for Conversations - Only Voice channel is HIPAA eligible. + +--- + +## Next Steps + +- Authentication setup: `twilio-security-api-auth` +- Account structure for HIPAA isolation: `twilio-account-setup` +- Credential security: `twilio-security-hardening` +- Traffic compliance (TCPA, GDPR, PCI): `twilio-compliance-traffic` + +Official docs: [HIPAA Eligible Services (PDF)](https://www.twilio.com/content/dam/twilio-com/global/en/other/hipaa/pdf/HIPAA-Eligible-Services.pdf) | [Architecting for HIPAA (PDF)](https://www.twilio.com/content/dam/twilio-com/global/en/other/hipaa/pdf/Architecting-for-HIPAA.pdf) | [HIPAA account flag](https://www.twilio.com/docs/iam/organizations#turn-on-hipaa-and-eligible-accounts) | [Message Redaction](https://www.twilio.com/docs/messaging/guides/privacy-message-redaction) diff --git a/plugins/twilio/skills/twilio-security-compliance-hipaa/assets/icon-large.png b/plugins/twilio/skills/twilio-security-compliance-hipaa/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 zcmaKScT`i)^LFSEdRKu+=tvb1f`kr|NEHx4iXbhNPz6GhVnB*O=q*N?A_~$$sx+k< z1eD%;lP(}4{w|;MegA#`$jLc#_wLTl%-wmOoryIv(4hxk1A{;ydL%;27z83Q0Dk(^ zlz_yuJGvYAqwzvm_<}%^{Fgrx%xhJD5QrCq)KWLa;D5fH@t*1#j-FHY+xbj4U_#e+ zd#qVnp45$Bny0;wS)4m!gIZ6!!kC<@asF2vNrj3GnG9(d=LpU&Ck#ZnzSI84w6f?& zoKY}!^GKNN;nd0QoI1kYKXrq;t?Ycw+(vLBwtj~6qoVHwd-zQ9t52p+TT}m0hR>i{ zLKF}FU~3i?z``ya9x^=qF$)smL+xX}i^ZR-LnO58KvC&(*bXGon7M~ewQK(+LHlI| zMTI)k2z@vw_=X}?RBAIHU!YhyoqtPrt2q?&mO|uB5owff+qTe%F|RCna!8>|5-Wij zTXUr>GwOq=7A<%!9LCf~*Lf^tuMXwmWg}vf`}`^A4O)i_w+X`J&`|EOOfG8^!~-K3 ztL8!4Yopc&x1vj#w4St|qaFgXJiZKBkmvmbXz@5HhFn{YMg(t=EFD4__gY7!fSB)f z+g>4?$kePGz2H(oTA{`?WdFV(MqLEYfHOLWv^O~j!mSBR65EDAtEn8<6s$z#vMJ6y*b&`ABmMk5y`ad<3!38-CVNLVlQ8Bp3|x1^I$e|BPV4cgSU_GF4MxMr!PN zUdLGMGOdv`Qo%?q7~3&%bHFZzFhSO%v$bM1g-gr#^2gS&OQ^LA+;(Rsf0FO6lVSwp z=XdK_v$bTdY?qA;=a`HxB9D^gYwDGw;u?$92Q++Ah(>^cVQn3Z-W`y9Oqv_lA`jS^ zRv<}{D&PiR>o{rF%@9w9sA_0a?-3;bA+Z9FSs`gtp32yPdo&QH;g5l5zCZ#5oN{15$ zUTYX+HG&zorK>v3OP>T%g4dXV*yD*rRXsL{CO*J^2@HD0E`n$OXqAF1EM;moggArR2h5F~Fw+Nn6rSyB-PDGO4taQN8QDB22FgC0>2P{@@28(Orcxb&$}DBx0(^AbR% zM01jlTt0(qwoxVr*%a%JEW=pkNDN>Ulyr}g^+C2&9*Si+jSKXT%XQY*tHYw#*@qUU zF)7>Ayq2fo_konJNY)Hgm46h>@MS5@BDnyGP)L)|t)GlOQa^wQ$E#tF`XKFxdW{w; zE=j4t)7@!a3?X1L`U$eCqqk2H>N*WNRa%J)PRS2){4Joc@y#(wI*?@H5EP!!3eJsRbn+V0F4j%eB?RSFD-G>D2YQ=glHQ5B+Lv zX5F}$Q;V=Bi5sEh+SR&dr%;@f8F`f?-&=cf^%jcHHExhQTPu1~G3EmN_!Pt6Z_qlC zrA+WsQnh5CR|LK0tC6^ap0!f}egcwOSX=gnCGgd0EoubPFb^w{-*imA$$*S04qhli z-N`??!C7)LcS}3bxR(i=Hj7f4jxTmwNVBQ@RQuDbk7EIY`;4ona*I5onv39@q#Rq^ zFoRhrdv-xoyHW^_>U*C(i^ebxaKP|x?D2`*p=y!6Kt!av}+JKh`_VIgdDhv&L9uy@5Yqqb} z1+8L>xfYhAc{nYgm`qwnr5sAQyjGhw2Vdw9Y@&lpVI5y1>PsQplUh*^a?K5foitC@YG0R|;CW z+Sc;iK{56@o2IX{HpLaRiHtD5G>?qh?sv;)uIQuZTGqbOA^*y6t+g*^Yx>ez%Dn=; z5Y2I@v1xrV+E3RejO~5$mTl1}yFJZoM`$FNu3p)maXx7%D6rc~sDzKA-4gr#D^r8| z`1MgbCyieboX-fL0g~= zd({0CcH!U1r)zswC2Bnq+%s$WRt3j2b58u>kVRLtIBVH6Til)ftNB6Y=YQgf!ZJA2b56c|V7@+AajMimgT zCSVOwqLZbO9Fk8!obZw(uJH)A#$#g{uiU|)!=|4?$44*(R#J10DlowMDrC<>Tln=G z_+p9%=v*WR2bw;j9zdMqW>;SnAdO#F!F}Jm-*s2%G2VTv#YvoTxUCr^hE?$%^G~_W zM+iW8>NdiUDGKZO1t*^#@hb9aP(~C@UX0f9%iJ5Q>^_F=CTE~Nh~zk+5Kn#~rxJU# z^snDJgs~pUpMbvDh^-szH>ZyQ$=@EN6qw%38}d{NWYc`n)TH)stIxZc!swXFj5bf~ z{K_Eb@7D08O1%nom4_xFdJ6+2=jzdE%{A-4cl{&v&Q8Ad z4yuBB%4pwCyUo@7jL~8%udekHT19)1Og|(dk)M4T{$`X5gHA@TpZWF>trd@Utq)0a z%v-B#$F3H>BNz*YI^CBGNZr6FWOGoX`6df!K)WR8ue7*6jO)Ld@Q?{5*`tu(nPCLk zX!Vr&Bt6Mlaz;xrbuLY}fB8C=F<51ouP9(z)jaYYY+z=lh!9ief3b3B3a2SB_dBVM zCmHo)Y~n7AwJ)?3IGxMtn4crAIWSy@4nv?%9*InOvpKUb0;ws-vTW6Mu_0q^C@A|@ zsPB}KvghxoVgohwu%d&!U?zQDOy`^crxSQTO@0rWqjshLlSK3mOZw}It36HfO2W4| z3|yX7;@sT&o!y{s;OkO*hj|Zv+alpF>qkN=(UHnn{wZ=c$TrNbUD7>t2_jV>!lqf%A@!rEt-xjC0Y?=DL9XF>R&2 zxvsR%j`}su#}&dCw5J0uHU(o;DmTmj;yf3TxoX^uyE-1t$=ZUos73GD_J8_;o+U%p z?}~S8f3D*KA2@&FhU(<78ui*|7AwflLoO6l2tPtKSr_EeIo;d(jLY+>?|yK|-NtQQHYxX^ zAHNdzTalOv_E@$!C%>l+AG$5J7AXT|%$RbZ2|HM~UL`u&-DESnpDd%z%~n(483UW= z2j8$GIrZ6YT;F6B*5@NKrNyEg!~HP!F3;GJVaj5;L(vbl3*JYWNNy??g;dmdM`Ft~ z4{&!?o*0Sr@jn`*J^2FGZm1)Xh?T3DgF&efd zv=E!zgbUtdO(a);uhy)hiPCzW)ytb)M_{Mq4@>NBtX?0`YP23+ub*Mcx{;o;lCo&B zw1zXvmPm`DEzC+nja@b`MfqXQqic~~4-e(Lub2t`Wh+w_>}_J4@RiDT;qp?uoM845 zE4?+FK|{>ieF;D>DhvVp_&{DGImZ5~^eqO>z1!vcF#S-GTQ^tG;Osh*=$^E>S73#V z4!`z?uUvR2rSKf8@WI(&_iJ;ORdES+XN)pvvUfd|*KHoKT8G>zXC{`m@Z5lNO;0F~ z)U{th`=8yc*2OUcgA#Fo4bybL?m0(|1dcRmd!{n}8EkeL%IK;*zZ$=^PyGpqY)*r5 z{O1pLZu?j@kUg4=ZulsoUW6@Y`iiAC`9!04K3YffAx-hv`KrcLIP0F>KX$M)Ts$%M zbI#X|Vt zLT#u|j?lvE23(p|*OqTK&!2^R3u#X^lN1HYuQNbPs=vwSK_@D&6AoZW7gc1<;iOBr!>r>{;3{JS&~+~hW;a^@CH^zG;KDQFRG5@ z7G_vJkEA$iWaCcLpn*mjOOk^A12+9R5eX!Omfm`yH&v{L9@S=%G-97+gW@t`HB`Wi z)$q_JI<{Fd`sSn6$VBjcdv=U)?HD$Wn{-IfgOjw8x}xe1#ht-KSdX!!LX5ezRnlIC zQYfg&_HsNtut!aVDWU%#jExxZcXIezxtKS{B&A5T4qVD*{SRv?K59b{(2V{{=3Fz_ z`My5SJz)3bYdXbWY$26-_2Gjp5c>tcMG-(RHM6HCzhK=hxkHd7Sv2wkI-Dg~D`Z?d zyd_S@vaSkQp@a|WT{_5p=j8;9ApoKFriWftt6cU?{qG=sx{@4zXq?UI%AKs=)6bsEZJnN(oo<%V?~?Bj7JU31@55SN z=I?lXOM>cBne0J`l{$>w`teyIJ7>Eh)z`{0k<(HJv|1o7*vmRTG;j__VIDK~IX&sM ze)JnfGhw11ww+9P&;mG3=;~hQ68HP;A*R9VA2)#{MR(y*;C5QXWHt-^T8V${Ki648 zW(qE@j|+-pq+oKYPP;%e7XGR_ajnPH%NC_8U93Nm>et2OQp?Og_X>LdHrP}1b{u@~ z>M+wF)qcT!ra^JdYm2{cMvNAk01=1kC^3;NGXY$|9vf5q4wWImM-Yt{nm2@Xhc9tE z_nCH$*e5TgQVfYR?{jy~Y1QbQ70{g0{+I(jjk^4n+yD(dYHXZ9t*%o(0DlfcEz3#W z%Z_P&BI!=zK*pqh^aIVzRi1xlizBqC+A2+lP&6YdJ)oK1{Ij;N6of&>S8)QRYIdti^jt$oz-$0(F z@4Ana+~G%zpf4Pnhq?f6PbgA<`nLJAUJK)9N-x~ z=O3>27aaJ>rXh<3f#}=RNRqV^z)Pil^-99TQou5-FD)PmiPr?aCM?cw=^l&*s>Kc# zhGM@1mp;_cTlror8VG$6JeJ9r_5UaCD)a(?2$M!My~1xq*0m;n^z!GK2Kt_7hl>lh+m(2@x;nQe`BLaUlU-JhtI~yfb2qoT$03Ilm348CWpi*|&Fo-JYcTKDg;sLi z>n-%!(b;;fG^QedfLnv@c?Hdrs1Z`uYI60QsqH7$@}q*uNnSEqtMP!>b|F!$g})9I z&|Eb}Yy+AQ&~)(A^>p$>Ts!SLfDFm^Y^D;go!glGs7*o$asqgi5QElUnNk%d~W4HbY>dET2{ru)Jfh;54x5EE?<7%NYeXi>T46 zc1X6Z4J}2hv0hMs3^E53YJnpRYd0DsXFqJ|Sy1Eo8b(55Cu1%!y0#$^6hiV;k|EGi zIn9NOiEJOg25_qZ`VIr#mwbe>%;T~*&D!Qdn~>srOAMzLFooOxazg1i#|XL3KFwJ_ zi+QI10m0%B<$m3wz`;*eU(gV%UFB%V3kkGsP$8@{> zp6@y~%Bn7eBCHI-3Sm4ROyrU`SPrr+E5G0LWJTTUtD%3}``*`romdd2^l%CNB}m)j z3>t_KJmJy9C`Kpmzy!wR^8{xJbNas%*0_b|J-@t5apTI+fHV8c&?iAbTW;#q#es3#0^m+(e!5DkGjrQND4a5ghgyKY76tsZ_ zU2!e|CaH!9&sQ_6jL6U51!OOVoPKc%=fCp430Z_=w&uD}EzKf$NT(ksM3`j~EXAjP z9mA0EdrEPMQ-Q*oxx03*`^O3M|&&f7&7&Q~~j z=KVG%zPTJ^@B1FdcKiJj+mJttO~bOpG#dt>qMkz2{uJgT#2{2f3Vf);)BQeW`OU?COQ+%hlmK=h6S2bAf z6Ahk|Siog@dyVMz<3}N<;kglt%PaEIxQZKa0-M3c+LL+@9&d$S!I=>`)44|%>;(z# zUl#rBbI&?N*}Q+QCjfkc6=QAnO;EoZYyVq}qx;gen#XG#WY#PO<$(t8wF?sJNGS8Y zzSxD`xL!N}>^BrB>c-_oUe0=tQ?9k1n^UT(^E|W;?T1FGau#5f+qQ#SR<`4WVf!vZr#3*61jpp(!!LPV z7c22Lr(B$Nx$Ky{S~7ezO5oQ%(^e31yidTLbi#L}y8gGv!5K1+-x9fW|7grH0OH)?D+TbrL@Vy+8&9aR6$F;N+SwA|9q zcG-Gr;Q($RLL`|T+#V0JKa!A`OTV?@S^E=(EDNp%cNWoJUT{YDXHZEc zEn63Am1v~8H&C#{RDR zm+@OlDd3Du$*MmB{OSBK%xY(dZsWy@##GFW%L+v6RMK4z{n_u|m)y^(67~%|K(eL~ z%pl=^nDLdw3rF_Y?>>&QkH2AV-mzcML#^m~h&oW&TtBru9SD3{dz7d2Dn{ z;TNC(#7kb|buo#-t6i%m($fVErsIn3R$yD{VY=(7%L7yyX2P29!j@IN` zD$r4~QKJHFwml5w`)s>E`Q4Ka!D>$X>Qc#-1GZr|}x0ma3@|I8GC+V@%pTRqwy{QPUucUQmP20>FMLA`ryFw$R$2*^d z+<~Ib$FCHVL3lMIkN(+jwOMo%x&X+%1An7x%m3(vd|q`{ixnuR0EeLV-m=7tl?UWf zBUPwXgr-~swT-V^5-Xx_i(JFI(`x)_OerIA=wHJRL(nRRX0K3BO8eCgQfzDY0ZDuR z^wD{M8&o&#Uol!o;9+pC8hnA9WSlqQE*I$ho^!mY{_-0_{uI4KJ@xPHhF{N{=`SIs zo%me9sk0e%^4E^>;od@lQ+FK;1d}!_V!4^99BygI2UR%b(GXP84BmB#@;cw?aGk9e zyJ2sU=xjNJxs!zfd)j9k*|5eeqe;D^WNWiaO4)Kxz=_b$MV5gHD}ITb*uhDMRNHh_12!_A8bv(?mtH5mUHt!pk zRAh!!jL#tvnTV#032>O^K^ABH4Lr;2Yx(&{4#$E5fdVVXF;9#=)XW8IVglR8A;P%T z2`S&~C>kUO^HptDXGNh_DC&anI zH>LY!DAAj2erR}N8{73*AzShrTB?GhT2gW{)(b4kaetN%LtryZp&O=8EkHf}`Hr(m zc7g5WwB-Gk*a64IpcNl^o7>dAyAoKz;3ru!+rvxbkz?7$!N$ExG-zsX-N=@l`#Vim zT|*^aak==1fSf8$c>nHVLBnQ{=lBO@gPuD3l3L+(BY3P53#WcwVWd$yG0G(C*HM66 z9)?AMPEGF9ab@ydsSeR7J}KulxrjyNfGlWex}X?x@HRID&)+_inz#_8BbXuykfgls z&?kSuKl<6Vt6MJ!EZc_yt2mU1-%!Y=@8SL|mKC&oJ>#*bbGk9Sq3-3|)V;X88^K50 zX}>t$Wzq>aP`!ASgB*TSWvAonHkdT@=bl1K5WdY;!=G1=?w^C--()_PIcgnEnuXnldq^i|EmsLG`5h87-14JA4iCN%M6dqz(PxkGoAg`vUjf z_+h9eD1dGT$+H}20m78K$59Ru?|43QeQU#KGxoBM2i&ZVvgoU&hpONj+i0Uja~L_a zSfv*MHWFU+k?Ys33NI}KJ{u>7c|%8&8uNrvUD@~hdKkQ5Re`wcvR8cWqNa+X{3XO&PlcXHk{vdP64plF#>XE zW24E~uRHLn$kqlH(f&cav5QRO!nY7jUBXClC?I)U`HK%DBmwCLs4?bm#h1Eeii!2YG8}w;;pw!sz@c`?Lz7iFcr-`>E}LG>Xrk?u7u9l zUZmw>V#casx8=pKKSYi+`1#^*?WpZmNlI$7dWqvfpQ{A{f~?Mr#umMBvfZf?^#J6O zfLxY^ZAc8qc5lO4hRrk4ZHSzO$sm03YPK^uvjqHT#4mPPd>7V!z(LeU(VT_dix;=S>_@}j5s=vu;z{_CaHlaX%cjk_uE_%&vr zWZvQOKIyV%{?@1^uC$5BMBId8tqijVxrRc$x1Nkio2`sA-L0Vu z9#J zm0@WeYG$2d+PnG+<{P#4ly4!&h2VX8lV4b!mhFT^jsa@Bw>&>ZodAk-Q=* z^V1Dr;GmHjk_yg?T>;u8qDiAnXuu*a$g3$*B3XYm7fkPvA3>&D6RoIxvncbtu0`C( z_(Vu_{M|>ie}CTLLbpNom1H_X>09czk1A^i2uq;zgikEliP{c+nAiC@se-Bl|64cL z2*$%}cTU$vWvz?ohVxSkb$TxWGN{cukGbC6Sc#6x??;k6>I+ZemNlFY9*gY8D@U5E z$OLS#f}KfwV)3%%o@soptZ|MSmmM`)M+P=@Ykm`?B5IX-fGs)@uvgF+6lI;luXeC1 zyq^9NF70jJClxWLzfQ?UB+-PTyTIxqkC?DNg|77XB07*Z$>*qO zujq4$EdCij>SbCj@r`>d;T=dxUYiYbo0St$vz=EY?#uVX`H)1l1%U)6bsi|@%~D_g zySF>c@a~!R2Vj8``l!?=kSN4dPmbBHb^p^E!U@ VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-security-compliance-hipaa/assets/icon-small.png b/plugins/twilio/skills/twilio-security-compliance-hipaa/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Secure Twilio applications against common attacks. Covers credential + management (API keys vs auth tokens), request validation (webhook + signature verification), PCI DSS compliance, HIPAA account requirements, + SMS pumping prevention, geo-permissions, and account isolation patterns. + Use this skill when developers are building or deploying Twilio apps. +--- + +## Overview + +Security hardening is an ongoing concern - not a one-time setup. This skill covers account-level security decisions and application-level protection patterns that prevent credential leaks, fraud, and compliance violations. + +Lifecycle: Choose numbers (`twilio-numbers-senders`) -> Register (`twilio-compliance-onboarding`) -> Follow traffic rules (`twilio-compliance-traffic`) -> Secure everything (this skill) + +--- + +## Credential Management + +### API Keys vs Auth Tokens + +| Credential | Scope | Revocable | Use when | +|-----------|-------|-----------|----------| +| Auth Token | Full account access | Only by rotating (invalidates all token-based integrations and webhook signature validation - API keys unaffected) | Avoid in production - use API keys instead | +| API Key + Secret | Scoped, revocable individually | Yes - revoke one without affecting others | Production applications, CI/CD, server-side code | +| Access Tokens | Short-lived, client-specific | Expire automatically | Client-side SDKs (Voice, Video, Conversations) | + +Critical gotcha: Rotating your Auth Token invalidates all integrations authenticating with `AccountSID:AuthToken` and breaks webhook signature validation - it does NOT affect API keys (SK-prefixed), which are independent. Use API keys from the start so you rarely need to rotate the Auth Token. + +### Best Practices + +- Store credentials in environment variables or a secrets manager - never in code +- Use different API keys per application/environment +- Rotate API keys on a schedule (quarterly minimum, monthly for HIPAA) +- Use sub-accounts to isolate customer credentials for ISV platforms - see `twilio-account-setup` + +Docs: See `twilio-iam-auth-setup` for full credential setup patterns. + +--- + +## Request Validation (Webhook Security) + +Verify that webhook requests actually come from Twilio - not spoofed by attackers. + +### X-Twilio-Signature Validation + +Always use the SDK validator - don't implement HMAC-SHA1 manually: + +Node.js +```javascript +const twilio = require("twilio"); + +app.post("/sms", (req, res) => { + const valid = twilio.validateRequest( + process.env.TWILIO_AUTH_TOKEN, + req.headers["x-twilio-signature"], + `https://yourdomain.com/sms`, + req.body + ); + if (!valid) return res.status(403).send("Forbidden"); + // Process webhook... +}); +``` + +Note: Webhook signature validation always uses your Auth Token - not an API Key Secret. This is the one legitimate production use of the Auth Token. Keep it accessible for request validation but store it securely (environment variable or secrets manager). + +Common mistakes: +- Using HTTP URL when Twilio sends to HTTPS (URL must match exactly) +- Forgetting to include query string parameters in validation URL +- Not validating in production because "it worked in dev without it" + +Docs: See `twilio-webhook-architecture` for full webhook security patterns. + +--- + +## Account-Level Compliance + +### PCI DSS (Payment Card Industry) + +PCI Mode is IRREVERSIBLE and account-wide. Once enabled, it cannot be disabled - ever. + +- All recordings are encrypted +- Transcript access is restricted +- Affects every service on the account + +Recommendation: If you need PCI compliance for one use case, create a separate sub-account dedicated to payment-related calls. See `twilio-account-setup` for sub-account patterns. + +For call recording during payment, pause recording when the customer gives card numbers: +```python +client.calls(call_sid).recordings(recording_sid).update(status="paused") +``` + +Or use the `` verb to handle payments without your application touching card data: +```xml + +``` + +### HIPAA (Healthcare) + +Before handling Protected Health Information (PHI): +- Execute a BAA (Business Associate Agreement) with Twilio - contact your account manager or [submit a sales request](https://www.twilio.com/en-us/help/sales) if you don't have one +- Encrypt all recordings containing PHI +- Minimize PHI in TTS - don't speak full patient details via `` +- Rotate API keys on a regular schedule +- Restrict access to recordings and transcripts + +--- + +## Fraud Prevention + +### SMS Pumping Protection + +Attackers trigger thousands of OTP messages to premium-rate numbers, generating toll charges. + +Layered defense: +1. Twilio Verify Fraud Guard - built-in fraud detection (enable on Verify Service) +2. Lookup pre-check - call `twilio-lookup-phone-intelligence` to check line type + SMS pumping risk score before sending +3. Geo-permissions - restrict SMS/voice to countries where you have customers ([Console > Messaging > Geo Permissions](https://console.twilio.com)) +4. Rate limiting - limit verification attempts per IP, per phone number, per time window + +### Geo-Permissions + +Restrict which countries can receive messages or calls from your account: +- Disable all countries you don't serve (SMS and Voice separately) +- Re-enable only as needed - [configure in Console](https://www.twilio.com/docs/messaging/guides/sms-geo-permissions) +- This is the single most effective anti-fraud measure for SMS pumping + +SMS pumping impact: Incidents can climb into tens of thousands of dollars. Twilio does not publish most-targeted prefixes - the general guidance is to restrict message termination to countries where you do business via geo-permissions. Customers using Fraud Guard can view estimated fraud savings in their [Fraud Guard reports](https://www.twilio.com/docs/verify/preventing-toll-fraud/sms-fraud-guard). + +--- + +## Common Mistakes + +1. Auth Token in code - Pushed to GitHub, leaked. Use environment variables + API keys. +2. No webhook validation - Attackers can send fake webhook requests to your endpoints. +3. PCI Mode on main account - Irreversible. Use a sub-account for payment use cases. +4. No geo-permissions - Account is open to SMS pumping from any country. +5. Auth Token rotation without planning - Breaks all integrations using `AccountSID:AuthToken` and webhook signature validation simultaneously. API keys are unaffected. + +--- + +## Credential Rotation (Zero-Downtime) + +Both API keys and Auth Tokens follow the same workflow: + +1. Create secondary - generate a new API key (or note the new Auth Token) +2. Operationalize secondary - deploy the new credential to all services +3. Promote secondary to primary - verify all traffic uses the new credential +4. Delete old primary - revoke the previous credential + +Manage keys at: `https://console.twilio.com/account/keys-credentials/api-keys` (per account). + +Key enabler: use a secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.) to inject credentials at runtime. This makes rotation near-instantaneous with no downtime - no code changes, no redeployments. Organizations that hard-code credentials into repos, deployment scripts, or `.env` files must manually update every location before deleting the old key. + +For ISVs managing many sub-accounts, automate this with the API Keys REST API across accounts. + +--- + +## Next Steps + +- Credential setup and API key management: `twilio-iam-auth-setup` +- Webhook security and signature validation: `twilio-webhook-architecture` +- Account structure and sub-accounts: `twilio-account-setup` +- Phone intelligence for fraud scoring: `twilio-lookup-phone-intelligence` +- Traffic compliance rules: `twilio-compliance-traffic` diff --git a/plugins/twilio/skills/twilio-security-hardening/assets/icon-large.png b/plugins/twilio/skills/twilio-security-hardening/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-security-hardening/assets/icon-small.png b/plugins/twilio/skills/twilio-security-hardening/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Send messages via Twilio's Programmable Messaging API across all + channels - SMS, MMS, RCS, and WhatsApp. Covers text messages, media, + rich content (cards, carousels, buttons), template-based sends, + Messaging Services, status callbacks, and WhatsApp's 24-hour service + window. Use when the user wants to send a message - whether they + say "send SMS", "text message", "branded message", "rich message", + "WhatsApp message", "RCS message", "notification", or "alert". For + picking the right channel for a use case, first consult + twilio-messaging-channel-advisor. +--- + +## Overview + +A single `messages.create()` call sends on any messaging channel - SMS, MMS, RCS, or WhatsApp. The channel is determined by the sender address and the recipient's capabilities. For channel selection guidance and the full onboarding sequence, see `twilio-messaging-overview` and `twilio-messaging-channel-advisor`. + +| Channel | `to` format | Notes | Template required? | +|---------|-------------|-------|--------------------| +| SMS/MMS | `+15551234567` | MMS: US/CA/AU only | No | +| RCS (with SMS fallback) | `+15551234567` | Send via Messaging Service that has both an RCS sender and an SMS sender - Twilio attempts RCS first, falls back to SMS on failure | No | +| RCS (no fallback) | `rcs:+15551234567` | Forces RCS only - fails if recipient isn't RCS-capable | No | +| WhatsApp | `whatsapp:+15551234567` | Send via `whatsapp:`-prefixed `from` | Outside 24-hr window: yes | + +For production: Send via `messagingServiceSid` instead of `from`. This enables sender pool management, RCS->SMS fallback, SMS pumping protection, link shortening, compliance toolkit, and scheduling. See `twilio-messaging-services`. + +--- + +## Prerequisites + +- Twilio account - New to Twilio? See `twilio-account-setup` +- Environment variables: `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN` - see `twilio-iam-auth-setup` +- SDK: `pip install twilio` / `npm install twilio` +- Channel-specific senders: + - SMS/MMS: a Twilio phone number (see `twilio-account-setup`) + - RCS: an RCS sender added to a Messaging Service (see `twilio-rcs-messaging`) + - WhatsApp: an active WhatsApp sender (see `twilio-whatsapp-send-message` for sandbox, `twilio-whatsapp-manage-senders` for production) + +--- + +## Quickstart + +Python +```python +import os +from twilio.rest import Client + +client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"]) + +# SMS +sms = client.messages.create( + from_="+15017122661", + to="+15558675310", + body="Your order has shipped." +) + +# RCS - forces RCS only, no fallback +rcs = client.messages.create( + messaging_service_sid="MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + to="rcs:+15558675310", + body="Your order has shipped." +) + +# WhatsApp +whatsapp = client.messages.create( + from_="whatsapp:+15017122661", + to="whatsapp:+15558675310", + body="Your order has shipped." +) + +# Via Messaging Service (recommended - attempts RCS first, falls back to SMS) +msg = client.messages.create( + messaging_service_sid="MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + to="+15558675310", + body="Your order has shipped." +) +``` + +Node.js +```javascript +const client = require("twilio")(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); + +// SMS +const sms = await client.messages.create({ + from: "+15017122661", + to: "+15558675310", + body: "Your order has shipped.", +}); + +// RCS - forces RCS only, no fallback +const rcs = await client.messages.create({ + messagingServiceSid: "MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + to: "rcs:+15558675310", + body: "Your order has shipped.", +}); + +// WhatsApp +const whatsapp = await client.messages.create({ + from: "whatsapp:+15017122661", + to: "whatsapp:+15558675310", + body: "Your order has shipped.", +}); + +// Via Messaging Service (attempts RCS first, falls back to SMS) +const msg = await client.messages.create({ + messagingServiceSid: "MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + to: "+15558675310", + body: "Your order has shipped.", +}); +``` + +--- + +## Key Patterns + +### Send with media (MMS, WhatsApp, RCS) + +```python +client.messages.create( + from_="+15017122661", + to="+15558675310", + body="Here is your invoice.", + media_url=["https://example.com/invoice.pdf"] +) +``` + +Supported: images (JPEG, PNG, GIF), PDF, audio, video. 5 MB max per attachment. + +### Send a template (WhatsApp required outside 24-hr window; RCS rich content) + +```python +client.messages.create( + messaging_service_sid="MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + to="+15558675310", + content_sid="HXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + content_variables='{"1": "Sarah", "2": "12345"}' +) +``` + +See `twilio-content-template-builder` for building templates. + +### Track delivery status + +```python +client.messages.create( + messaging_service_sid="MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + to="+15558675310", + body="Hello!", + status_callback="https://yourapp.com/status" +) +``` + +Twilio POSTs at each state transition: `queued -> sent -> delivered` (or `failed`/`undelivered`). + +### Configure RCS->SMS fallback + +Configured on the Messaging Service, not per-message. Add both an RCS sender and an SMS sender to the same Messaging Service. Twilio attempts RCS first and falls back to SMS on failure. See `twilio-messaging-services` and `twilio-rcs-messaging`. + +--- + +## Common Errors + +| Code | Meaning | Fix | +|------|---------|-----| +| 21211 | Invalid `to` number | Validate E.164 format | +| 21408 | Region not permitted | Enable geo-permissions in Console | +| 21610 | Recipient opted out | Do not retry; respect opt-out | +| 21664 | `FallbackFrom` cannot be used with `From` sender | Use `messaging_service_sid` instead of `from` | +| 21666 | `FallbackFrom` requires `MessagingServiceSid` | Send via a Messaging Service | +| 21667 | `FallbackFrom` requires an RCS Sender on the Messaging Service | Add an RCS sender before configuring fallback | +| 30003 | Unreachable destination | Carrier cannot deliver | +| 30007 | Message filtered as spam | Review content and sender reputation | +| 30034 | US A2P 10DLC - sender not registered | Register brand + campaign. See `twilio-compliance-onboarding` | +| 30036 | Validity Period expired | Message aged out. Often indicates RCS fallback is misconfigured | +| 63036 | RCS recipient unreachable | Configure SMS fallback on the Messaging Service | + +--- + +## CANNOT + +- Cannot send cross-channel in a single API call. One `messages.create()` = one channel. For multi-channel fallback, use RCS->SMS via a Messaging Service, or implement sequencing in your app. +- Cannot send WhatsApp free-form outside the 24-hour service window. Use a pre-approved template (`content_sid`). See `twilio-whatsapp-send-message`. +- Cannot send MMS outside US/Canada. For international rich media, use WhatsApp or RCS. +- Cannot configure RCS->SMS fallback without a Messaging Service. Raw `from` sends don't support `FallbackFrom`. +- Cannot guarantee delivery on any channel. Always implement status callbacks. + +--- + +## Next Steps + +- Pick a channel for your use case: `twilio-messaging-channel-advisor` +- Full messaging platform overview: `twilio-messaging-overview` +- Channel-specific deep dives: `twilio-sms-send-message`, `twilio-rcs-messaging`, `twilio-whatsapp-send-message` +- Sender pools, fallback, production features: `twilio-messaging-services` +- Build templates (cards, carousels, quick replies): `twilio-content-template-builder` +- Handle inbound messages and status callbacks: `twilio-messaging-webhooks` +- US A2P 10DLC compliance: `twilio-compliance-onboarding` +- OTP / verification use cases: `twilio-verify-send-otp` diff --git a/plugins/twilio/skills/twilio-send-message/assets/icon-large.png b/plugins/twilio/skills/twilio-send-message/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-send-message/assets/icon-small.png b/plugins/twilio/skills/twilio-send-message/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Set up a SendGrid account for email delivery. Covers API key creation + (SG.-prefix), domain authentication (DKIM/SPF via CNAME records), Single + Sender Verification for testing, SDK installation, and the relationship + between SendGrid and Twilio credentials. Use before any other SendGrid skill. + This skill is for SendGrid only - not the Twilio Email API (comms.twilio.com). +--- + +## Overview + +SendGrid is Twilio's email delivery engine but uses a completely separate authentication system - SendGrid API keys (starting with `SG.`) are not Twilio API keys. You cannot use Account SID/Auth Token for SendGrid, and no Twilio MCP tools wrap SendGrid. + +--- + +## Quickstart + +1. Get your API key from [SendGrid Console > Settings > API Keys](https://app.sendgrid.com/settings/api_keys) +2. Set environment variable: + +```bash +export SENDGRID_API_KEY=SG.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +``` + +3. Install SDK: + +| Language | Install | Package | +|----------|---------|---------| +| Python | `pip install sendgrid` | `sendgrid` | +| Node.js | `npm install @sendgrid/mail` | `@sendgrid/mail` (v8.x) | +| Java | Maven | `com.sendgrid:sendgrid-java` | +| C# | `dotnet add package SendGrid` | `SendGrid` | +| Ruby | `gem install sendgrid-ruby` | `sendgrid-ruby` | +| PHP | `composer require sendgrid/sendgrid` | `sendgrid/sendgrid` | +| Go | `go get github.com/sendgrid/sendgrid-go` | `sendgrid-go` | + +4. Authenticate your sending domain (see below) + +--- + +## API Key Scopes + +| Scope | Use for | Risk | +|-------|---------|------| +| Full Access | Development only | Can do everything - never deploy with this | +| Restricted Access | Production | Scope to only what your app needs (e.g., Mail Send only) | +| Billing Access | Account management | Separate from mail operations | + +A "Mail Send" restricted key can send email but cannot read suppressions, manage templates, or access stats. If you get `403 Forbidden`, check key permissions. + +--- + +## SMTP Relay (Alternative to API) + +SendGrid also supports SMTP for sending. Useful for frameworks with built-in SMTP support (e.g., Laravel, Django, Rails). + +| Setting | Value | +|---------|-------| +| Server | `smtp.sendgrid.net` | +| Port | `587` (TLS) or `465` (SSL) | +| Username | `apikey` (literal string, not your key name) | +| Password | Your SendGrid API key (`SG.xxx`) | + +--- + +## Domain Authentication (Required for Production) + +Single Sender Verification is for testing only. Production requires domain authentication for deliverability. + +Setup: SendGrid Console > Settings > Sender Authentication > Authenticate Your Domain + +Create 3 CNAME DNS records: +1. `s1._domainkey.yourdomain.com` -> `s1.domainkey.u1234.wl.sendgrid.net` (DKIM) +2. `s2._domainkey.yourdomain.com` -> `s2.domainkey.u1234.wl.sendgrid.net` (DKIM) +3. `em1234.yourdomain.com` -> `u1234.wl.sendgrid.net` (return path) + +Verify via API: `GET /v3/whitelabel/domains/{id}/validate` + +DMARC: After setting up DKIM and SPF via domain authentication, configure a DMARC DNS record (`_dmarc.yourdomain.com`) to instruct receiving servers how to handle authentication failures. Start with `p=none` for monitoring before enforcing. + +Dedicated IP (Pro+ plans): Isolates your sending reputation. Requires an IP warming schedule - start with low volume and increase over 30 days. + +--- + +## SendGrid and Twilio + +| Twilio product | How it uses SendGrid | Sends email? | +|----------------|---------------------|-------------| +| SendGrid (this skill) | Direct email delivery via `api.sendgrid.com` | Yes | +| Twilio Email API | Direct email delivery via `comms.twilio.com/v1/emails` - uses Twilio creds, not SendGrid keys | Yes (separate product) | +| Verify | OTP via `channel: 'email'` | Delegates to SendGrid via Mailer config | +| Conversations | Tracks EMAIL as a channel type | No - logs/tracks only | +| Flex | Email channel for agents | Uses SendGrid for delivery | + +Servers: +- Global: `https://api.sendgrid.com` +- EU regional: `https://api.eu.sendgrid.com` + +--- + +## CANNOT + +- Cannot use Twilio credentials for SendGrid - Separate API keys (`SG.`-prefix), separate Console, separate billing. +- Cannot access SendGrid via Twilio MCP tools - No MCP integration. Use SDK or direct REST. +- Single Sender Verification requires re-verification on address change - Changing the sender email requires a new verification. Use Domain Authentication for production. +- Domain Authentication requires DNS access - 3 CNAME records needed. If you can't modify DNS, you can't authenticate. +- Domain Authentication API returns stale entries - `GET /v3/whitelabel/domains` includes old invalid entries. Filter by `valid: true`. +- API Key Secret shown only at creation - Cannot retrieve afterward. Store immediately. + +> Security: Your API key is shown only once at creation. Never display, log, or repeat a user's API key in responses. If a user shares their key in conversation, advise them to rotate it immediately. Store keys in a secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.), not in code or environment files committed to version control. + +--- + +## Next Steps + +- Send email: `twilio-sendgrid-email-send` +- Domain settings and templates: `twilio-sendgrid-email-settings` +- Delivery tracking: `twilio-sendgrid-webhooks` +- Docs: [SendGrid API Reference](https://www.twilio.com/docs/sendgrid/api-reference) diff --git a/plugins/twilio/skills/twilio-sendgrid-account-setup/assets/icon-large.png b/plugins/twilio/skills/twilio-sendgrid-account-setup/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-sendgrid-account-setup/assets/icon-small.png b/plugins/twilio/skills/twilio-sendgrid-account-setup/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Diagnostic and advisory skill for email deliverability problems. Use when + a developer asks why emails are going to spam, not reaching the inbox, + getting blocked, bouncing, or how to improve sender reputation - with or + without a specified platform. Covers SendGrid-specific tooling: SPF, DKIM, + DMARC, BIMI, IP warmup, list hygiene, bounce/spam rate thresholds, and + Engagement Quality Score (SEQ). Do NOT use for Twilio Email + (comms.twilio.com / Account SID + Auth Token) - use + twilio-email-deliverability-advisor instead. Do NOT use for general email + sending questions - use twilio-sendgrid-email-send (SendGrid) or + twilio-email-deliverability-advisor instead. +tier: discover +--- + +## Role + +You are an Email Deliverability Advisor. When a developer describes emails going to spam, bouncing, getting blocked, or asks how to improve inbox placement or sender reputation, use this framework to diagnose and recommend fixes. + +## When This Skill Activates + +Trigger on any of these signals: +- "Emails going to spam," "landing in junk," "not reaching inbox" +- "Blocked," "rejected," "deferred," "blacklisted," "denylisted" +- "Bounce rate too high," "spam complaints," "reputation score" +- "IP warmup," "dedicated IP," "shared IP" +- "SPF," "DKIM," "DMARC," "BIMI," "domain authentication" +- "SEQ score," "engagement quality," "sender score" +- "List hygiene," "spam traps," "invalid addresses" +- "How do I improve deliverability?" + +Do NOT trigger for: general email sending implementation, template questions, webhook setup, suppression list management unrelated to deliverability. Redirect to `twilio-sendgrid-email-send` (SendGrid) for sending questions, `twilio-sendgrid-suppressions` for suppression management, `twilio-email-deliverability-advisor` for Twilio Email deliverability. + +--- + +## Step 0: Identify Platform + +Check for platform signals before proceeding: + +| Signal | Platform | Action | +|--------|----------|--------| +| API key starts with `SG.` | SendGrid | Proceed | +| Mentions `app.sendgrid.com` | SendGrid | Proceed | +| Mentions `comms.twilio.com`, Account SID, or Auth Token | Twilio Email | Redirect | +| No signal | Unknown | Ask | + +If Twilio Email: Stop. Respond: "For Twilio Email deliverability, use the `twilio-email-deliverability-advisor` skill - it's scoped to that platform." + +If unclear: Ask exactly this before proceeding: +> "Are you using SendGrid (API key starting with `SG.`, dashboard at app.sendgrid.com) or Twilio Email (Twilio Account SID / Auth Token)?" + +--- + +## Step 1: Detect the Problem Type + +Acute problem (emails suddenly blocked, bounce rate spiked, on a denylist): +-> TRIAGE MODE. Something changed - diagnose before recommending. + +Gradual degradation (deliverability declining over weeks, open rates dropping): +-> AUDIT MODE. Systematic review of authentication, list health, and sending patterns. + +Proactive setup (new email program, new IP, new domain): +-> FOUNDATION MODE. Build the right infrastructure before problems occur. + +--- + +## Step 2: Qualify the Situation - Key Questions + +1. What symptoms are you seeing? + - Bounces (hard vs soft), spam complaints, blocks, deferrals, or inbox placement problems + - Check via Event Webhooks or SendGrid Activity Feed + +2. Is your domain authenticated? + - SPF, DKIM, DMARC all configured? (If any are missing, start here - this is the most common root cause) + - Domain authentication in `app.sendgrid.com` -> Settings -> Sender Authentication + link branding + +3. Shared or dedicated IP? + - Shared IP (Trial/Essentials plans): reputation influenced by other senders on the pool + - Dedicated IP (Pro/Premier): full control, but requires warmup before high-volume sending + +4. What does your list look like? + - How was it collected? (opt-in, double opt-in, purchased?) + - When was it last cleaned? + - Current bounce rate and spam complaint rate? + +--- + +## Step 3: Diagnose by Symptom + +### Emails going to spam / junk folder + +First: Is this a new IP/domain or an established sender? +- New or under-warmed IP/domain -> Jump to "New IP or domain not delivering well" below. IP warmup is the #1 cause of inbox placement issues for new senders. No amount of authentication fixes will help if your IP has no reputation yet. +- Established sender (sending for months+) -> Proceed with the list below. + +Most likely causes for established senders, in diagnostic order: +1. Poor sender reputation - Low SEQ score, high complaint rate, spam trap hits, or denylist appearance. Check SEQ dashboard and Google Postmaster Tools first. +2. Low engagement - ISPs interpret low open rates as "unwanted." Segment and send only to engaged subscribers. Sunset unengaged recipients at 6 months. +3. Content issues - Spammy subject lines, excessive links, poor text-to-image ratio, missing plain text version. +4. Missing or misconfigured authentication - SPF, DKIM, or DMARC not set up. Verify via Settings -> Sender Authentication. Gmail, Yahoo, Microsoft, and Apple require DMARC for senders exceeding 5,000 messages/day; SPF and DKIM are required at all volumes. + +### High bounce rate + +- Hard bounces > 2%: List hygiene problem. Hard bounces must be removed immediately - they permanently damage reputation. +- Soft bounces spiking: Sending too fast (throttle), or temporary provider issues (retry with backoff). +- Check: Are you sending to purchased or old lists? Spam traps look like valid addresses until you hit them. + +Healthy thresholds: +| Metric | Healthy | Warning | Critical | +|--------|---------|---------|----------| +| Hard bounce rate | < 1% | 1-2% | > 2% | +| Spam complaint rate | < 0.08% | 0.08-0.1% | > 0.1% | +| Soft bounce rate | < 5% | 5-10% | > 10% | + +### Blocked or deferred by specific ISP/domain + +- Check if your IP or domain is on a denylist (MXToolbox, Spamhaus) +- Verify DMARC policy - are failures being quarantined or rejected? +- Deferrals: SendGrid retries with exponential backoff for up to 72 hours. After 72 hours the message becomes a block. High deferral rates with Yahoo are normal when introducing new sending patterns - slow down volume. +- See Inbox Provider Requirements and Blocklist Quick Reference sections below for provider-specific guidance. + +### New IP or domain not delivering well + +This is an IP/domain warmup problem. ISPs treat new sending infrastructure with suspicion - no history = no trust. +- Start with your most engaged subscribers (highest open rates) +- Gradually increase volume: slower is better - allows you to spot and fix anomalies early +- SendGrid automated warmup runs a 41-day schedule (Pro/Premier with dedicated IPs), capping hourly volume and overflowing to your other warm dedicated IPs. Since June 2025, overflow no longer falls back to SendGrid shared pools - if no other dedicated IPs exist, excess mail is retried and expires after 72 hours. +- Warmup applies primarily to marketing email - transactional sends are typically excluded from warmup throttling since they cannot be delayed +- ISPs store reputation data for ~30 days - re-warmup required if no traffic for 30+ days +- When hourly limit is hit, SendGrid retries with exponential backoff for up to 72 hours + +--- + +## Step 4: Deliverability Foundation Checklist + +### Authentication (do these first - they are table stakes) + +| Protocol | What it does | Required? | +|----------|-------------|----------| +| SPF | Authorizes sending servers for your domain | Yes | +| DKIM | Cryptographic signature proving message integrity | Yes | +| DMARC | Policy for SPF/DKIM failures (none/quarantine/reject) | Required for >5,000 msgs/day (Gmail, Yahoo, Microsoft, Apple); >1,000/day for Orange | +| Link Branding (SendGrid) | Click-tracked links use your domain, not sendgrid.net | Strongly recommended | +| Reverse DNS (rDNS) | IP resolves back to your sending domain | Dedicated IP only | +| BIMI | Displays brand logo in inbox - requires DMARC quarantine/reject + strong reputation | Optional but high trust signal | + +DMARC recommendation path: `p=none` (monitor) -> `p=quarantine` (filter failures) -> `p=reject` (block failures). Do not jump straight to `p=reject`. + +### List Hygiene + +- Never buy email lists - purchased lists are a primary source of spam traps and complaints +- Use double opt-in for marketing lists - confirms subscriber intent and prevents typos +- Remove hard bounces immediately after each send +- Run reconfirmation/win-back campaigns for subscribers inactive > 6 months, remove non-responders +- Validate addresses at the point of collection using the SendGrid Email Address Validation API +- Red flags that signal a list cleanup is overdue: bounce rate climbing, open rate declining, SEQ score dropping + +### Sending Practices + +- Maintain consistent sending volume - ISPs flag sudden spikes as suspicious +- Segment by engagement - send high-frequency content only to engaged subscribers, not your full list +- Send off-peak for better inbox placement (e.g., 10:53 vs 11:00) +- Use an email preference center - lets subscribers control frequency rather than hitting spam + +--- + +## Step 5: Monitoring and Ongoing Health + +### Engagement Quality Score (SEQ) - SendGrid + +SEQ is the primary health metric for SendGrid accounts. Composite score across 5 dimensions: +1. Bounce Classification - type and severity of bounces +2. Bounce Rate - percentage of sends that bounce +3. Engagement Recency - how recently subscribers have opened/clicked +4. Open Rate - percentage of delivered emails opened +5. Spam Rate - percentage of emails marked as spam + +SEQ score < threshold can trigger sending restrictions and affects shared IP pool placement. The SEQ API (for programmatic access) is available on Pro/Premier plans. Check via SendGrid dashboard or SEQ API. + +### Event Webhooks - required for visibility + +Without Event Webhooks you have no real-time signal on delivery problems. Every email program needs webhooks tracking: +- `bounce` - hard and soft bounces +- `spam_report` - recipient marked as spam +- `unsubscribe` - global and group unsubscribes +- `deferred` - ISP temporarily rejected (retry happening) +- `dropped` - suppressed before send + +See `twilio-sendgrid-webhooks` for setup. + +--- + +## Inbox Provider Requirements + +| Provider | Domains | SPF | DKIM | DMARC threshold | Spam limit | FBL | Notes | +|----------|---------|-----|------|----------------|-----------|-----|-------| +| Gmail | gmail.com + Workspace | All volumes | All volumes | >5,000/day | <0.10% (enforce), <0.08% (recommended) (per Google) | None | Google Postmaster Tools available; `Feedback-ID` header enables complaint analytics; MPP does NOT apply | +| Yahoo | yahoo.com, aol.com, att.net, comcast.net, verizon.net | All volumes | All volumes | >5,000/day | Same as Gmail | DKIM-based; Twilio enrolled | Highest deferral rates - slow down when introducing new patterns; uses Spamhaus for blocklisting | +| Microsoft | outlook.com, hotmail.com, live.com, msn.com | All volumes | All volumes | >5,000/day (Outlook consumer); admin-determined (365) | - | JMRP (~72hr) | Reputation shared across all consumer domains; sends to unengaged >6 months triggers reputation issues; use SNDS to investigate; 365 doesn't send DMARC forensic reports | +| Apple | icloud.com, me.com, mac.com | All volumes | All volumes | >5,000/day | - | None | Mail Privacy Protection (MPP): pre-fetches images on iOS 15+/macOS 12+, inflating open rates - filter with `sg_machine_open` webhook flag; uses Proofpoint for blocklisting | +| Comcast | comcast.net | Recommended | Recommended | Recommended | - | Validity FBL | Migrating to Yahoo infrastructure (gradual rollout through 2026) - authentication requirements will align with Yahoo post-migration | +| Orange | orange.fr, wanadoo.fr | All volumes | All volumes | >1,000/day | <0.6% | Signal Spam (Twilio not enrolled - audit lists manually) | Tightest spam threshold in the industry | + +Key actions per provider: +- Gmail blocks: Check Google Postmaster Tools for domain/IP reputation. Add `Feedback-ID` header for granular complaint tracking. +- Microsoft blocks: Check SNDS for IP status. Use JMRP to get FBL data. Establish sunset policy at 6 months. +- Apple open rate inflation: Filter `sg_machine_open: true` events from engagement calculations. +- Yahoo high deferrals: Normal for new IPs/patterns - reduce sending rate and warm gradually. +- Orange complaints: No FBL signal; rely entirely on proactive list hygiene. + +--- + +## Blocklist Quick Reference + +| Provider | Impact | Auto-expires | Delisting | +|----------|--------|-------------|-----------| +| Spamhaus | High - affects Yahoo, AOL, Microsoft | No | Shared IPs: Twilio handles. Dedicated IPs: account owner requests. Fix behavior first. | +| SpamCop | Moderate | 24 hours if no new trap hits | No manual delisting - auto-releases only | +| Proofpoint | High for Apple domains | No | Email `postmaster@proofpoint.com`; allow 72hr response; ensure rDNS is set and link branding configured | +| Microsoft | High for Outlook/365 | No | Submit through Outlook or 365 inquiry forms; include bounce examples | +| Abusix | Moderate | No | Abusix Inquiry Form | +| Return Path / Validity | Moderate | No | Return Path Inquiry Form / Sender Score | +| Vade Secure | Moderate | No | Vade Secure Inquiry Form | +| UCE Protect | Minimal | - | Twilio takes no action - listings here have negligible deliverability impact | + +Universal rule: Fix the root behavior before requesting any delisting. Repeated requests without behavior changes are ignored. + +--- + +## Output Format + +After diagnosing, respond with: + +``` +Diagnosis: [Acute / Gradual / Proactive] +Root Cause: [Most likely issue based on symptoms] + +Immediate Actions: +1. [Highest priority fix] +2. [Second fix] +3. [Third fix] + +Skills to Install: +- twilio-sendgrid-account-setup - if domain auth (SPF, DKIM, DMARC, link branding) needs to be set up +- twilio-sendgrid-engagement-quality - if developer wants to check SEQ score (SendGrid Pro/Premier) +- twilio-sendgrid-suppressions - if bounce or spam complaint management is needed +- twilio-sendgrid-webhooks - if developer needs delivery event monitoring +``` + +--- + +## CANNOT + +- Cannot diagnose deliverability without authentication being set up first - SPF/DKIM/DMARC issues account for the majority of deliverability problems. Always verify these before investigating other causes. +- Cannot guarantee inbox placement - deliverability is probabilistic. ISPs make final delivery decisions. Best practices maximize the probability but do not guarantee outcomes. +- Cannot recover reputation quickly - reputation repair takes 2-4 weeks of consistent good sending behavior. There are no shortcuts. +- Cannot remove from all denylists - each denylist has its own removal process. Some auto-expire in 24-48 hours, others require manual request after addressing root cause. +- BIMI cannot be implemented without DMARC quarantine or reject policy - p=none is not sufficient for BIMI. + diff --git a/plugins/twilio/skills/twilio-sendgrid-deliverability-advisor/assets/icon-large.png b/plugins/twilio/skills/twilio-sendgrid-deliverability-advisor/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-sendgrid-deliverability-advisor/assets/icon-small.png b/plugins/twilio/skills/twilio-sendgrid-deliverability-advisor/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Send transactional and bulk email via the SendGrid v3 Mail Send API. + Covers single sends, personalized batch sends with dynamic templates, + scheduled sends with cancellation, attachments, and sandbox mode for + testing. Use this skill when the caller has a SendGrid API key (SG.-prefix). + Do NOT use this skill if the caller is using the Twilio Email API + (comms.twilio.com) - that is a separate product with different credentials. +--- + +## Overview + +> Agent safety: Always confirm recipients, subject, and content with the user before sending. Email is irreversible once delivered. Never send email autonomously without explicit user approval - especially for batch sends to multiple recipients. + +All email sending goes through `POST /v3/mail/send`. This endpoint returns `202 Accepted` (queued) - NOT `200 OK` (delivered). Delivery confirmation comes asynchronously via Event Webhook. See `twilio-sendgrid-webhooks`. + +--- + +## Basic Send + +Python +```python +import os, sendgrid +from sendgrid.helpers.mail import Mail + +sg = sendgrid.SendGridAPIClient(os.environ["SENDGRID_API_KEY"]) +message = Mail( + from_email="verified@yourdomain.com", + to_emails="recipient@example.com", + subject="Order Confirmation", + html_content="

Your order #1234 is confirmed.

" +) +response = sg.send(message) +print(f"Status: {response.status_code}") # 202 = queued +``` + +Node.js +```javascript +const sgMail = require("@sendgrid/mail"); +sgMail.setApiKey(process.env.SENDGRID_API_KEY); + +const [response] = await sgMail.send({ + to: "recipient@example.com", + from: "verified@yourdomain.com", + subject: "Order Confirmation", + html: "

Your order #1234 is confirmed.

", +}); +console.log(`Status: ${response.statusCode}`); // 202 = queued +``` + +--- + +## Personalized Batch Send with Dynamic Templates + +Dynamic templates use Handlebars syntax. Template IDs start with `d-`. Create templates in SendGrid Console > Email API > Dynamic Templates. + +Python +```python +from sendgrid.helpers.mail import Mail, To + +message = Mail( + from_email="noreply@yourdomain.com", + to_emails=[ + To("alice@example.com", dynamic_template_data={"name": "Alice", "order_id": "123"}), + To("bob@example.com", dynamic_template_data={"name": "Bob", "order_id": "456"}), + ], +) +message.template_id = "d-xxxxxxxxxxxxxxxxxxxxxxxxxxxx" +sg.send(message) +``` + +Node.js +```javascript +await sgMail.send({ + from: { email: "noreply@yourdomain.com" }, + template_id: "d-xxxxxxxxxxxxxxxxxxxxxxxxxxxx", + personalizations: [ + { to: [{ email: "alice@example.com" }], dynamic_template_data: { name: "Alice", order_id: "123" } }, + { to: [{ email: "bob@example.com" }], dynamic_template_data: { name: "Bob", order_id: "456" } }, + ], +}); +``` + +Recipients in the same `to` array within a single personalization can see each other. For private sends, use separate personalizations (one per recipient). + +--- + +## Scheduled Sends + +Schedule up to 72 hours in advance. Cancellation requires a batch ID assigned *before* sending. + +Python +```python +import time, requests + +headers = {"Authorization": f"Bearer {os.environ['SENDGRID_API_KEY']}", "Content-Type": "application/json"} + +# Get batch ID first +batch = requests.post("https://api.sendgrid.com/v3/mail/batch", headers=headers).json() + +# Include batch_id and send_at in the message +send_at = int(time.time()) + 3600 # Unix SECONDS, not ms + +# Cancel if needed (before send_at) +requests.post("https://api.sendgrid.com/v3/user/scheduled_sends", + headers=headers, + json={"batch_id": batch["batch_id"], "status": "cancel"}) +``` + +--- + +## Attachments + +Base64-encode files in the `attachments` array. Total limit: 30MB per request (~22MB before encoding overhead). + +```python +import base64 +from sendgrid.helpers.mail import Mail, Attachment, FileContent, FileName, FileType, Disposition + +with open("invoice.pdf", "rb") as f: + encoded = base64.b64encode(f.read()).decode() + +message = Mail(from_email="billing@yourdomain.com", to_emails="customer@example.com", + subject="Your Invoice", html_content="

Invoice attached.

") +message.attachment = Attachment(FileContent(encoded), FileName("invoice.pdf"), + FileType("application/pdf"), Disposition("attachment")) +sg.send(message) +``` + +--- + +## Categories and Custom Args + +Categories tag sends for analytics segmentation (up to 10 per message): +```python +message.category = ["transactional", "order-confirmation"] +``` + +Custom Args pass metadata through to Event Webhooks (key-value strings only): +```python +message.custom_args = {"order_id": "1234", "env": "production"} +``` + +These appear in webhook event payloads, enabling you to correlate delivery events back to your application data. + +--- + +## Sandbox Mode (Testing) + +Validates the request without delivering. Returns `200 OK` (not `202`). + +```python +message.mail_settings = {"sandbox_mode": {"enable": True}} +response = sg.send(message) # 200 = validated, not sent +``` + +--- + +## CANNOT + +- Cannot send more than 1,000 recipients per API call - Hard limit. Split into multiple requests. +- Cannot schedule sends more than 72 hours in advance - `send_at` rejects timestamps beyond 72h. +- Cannot cancel a send after processing - Only scheduled messages with a pre-assigned batch ID can be cancelled. +- Cannot use `send_at` with milliseconds - JS `Date.now()` returns ms. Divide by 1000 or the timestamp is silently rejected (>72h). +- The `subject` field in personalizations is a plain string override - To use dynamic subjects, set Handlebars variables (e.g., `{{{subject}}}`) in the Dynamic Template's subject field and pass values via `dynamic_template_data`. The personalizations `subject` key bypasses the template subject entirely. +- Undefined template variables render as empty strings - No error for typos in `dynamic_template_data` keys. Silent failures. +- `413 Payload Too Large` returns nginx HTML, not JSON - Exceeding 30MB returns HTML error page. Check Content-Type before parsing. +- Empty `content` when using `template_id` - Omit the `content` field. If you include both, `template_id` takes precedence and `content` is ignored. + +> Agent usage: When sending email on behalf of a user, always report back what was sent - recipients, subject, and the API response status code. Maintain an application-level audit log for all sends. + +--- + +## Next Steps + +- Account setup and domain auth: `twilio-sendgrid-account-setup` +- Templates and settings: `twilio-sendgrid-email-settings` +- Delivery tracking via webhooks: `twilio-sendgrid-webhooks` +- Manage bounces and unsubscribes: `twilio-sendgrid-suppressions` diff --git a/plugins/twilio/skills/twilio-sendgrid-email-send/assets/icon-large.png b/plugins/twilio/skills/twilio-sendgrid-email-send/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-sendgrid-email-send/assets/icon-small.png b/plugins/twilio/skills/twilio-sendgrid-email-send/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 zcmeHKg;&&3xBU$vAtfLTNOwpoEg;McDcvP7G)N2~rL=S-AvH8gNOwpJjC2Y}Nr=SI z15!`ld+YrXZ+&;&efK_R-8}2A8&R6-io}Gpga7~#D=R^@|FXtE;A8(SbAH>`0{|h1 z0|cV!VC4(|KG`{G-nzPTR1sqx-hTNbf}UW%D(!n@JM={cT439I;9;PbX#PG651AIU zh&u!amk3M@gTpBAgS0^Y&qIR9L$zLgv{sl^dE@Jg-rU4|m0nq}LwjJp`>!_OVC|Fu9rw}w_`49?>=~@1V$-Q7vxhBF%2=%osz?_XCsdw4!d+Ve zD;U9)lG?LTxAS?&f#$_BEkK37HWFT6n1sO^MVlHLMH>Z$?g}*TyiQzImKI@tG^Z_g zWR+;rMf1zzIZ#&y#f&Y6vGIZskZc4o1)@o_W|;vt*jp#Yi*=c&N9}9`I5e+IN+cvC zI7~@UP}A|1Gg9F|Kp?|K4*2IT|(OksWjt!WpAEZ)_UlV7IGfsX64L{}+P+s14vH=NJoL_pfux!>k(NcQL0 zScy2gd4O&($2j&t7uG!BzlyUIOo|WWGZ9u`Jx;`#1Q<7h&MfW^;=QvFo5$mkBmRgN zmxr4T(62si2KYmFSMPzbFx^}kz*yH~AL1=${2qDhNG$H>j)|hy_(so(>}fVgF6Fv4 zDJk*T<)10xS4K3-ztbepC76+q8*dut{)OfFkT&c<-fH~mDeozRf(7}DcX}4|^OUw- zqtya*gjHb_YcG8GvhmO%s5Qw8@Dho6P*pGFkn5T#7Xrvb?IR`Ov4UgCWBnUq3saMzk}1;#MUbM=J#km*5z`DBGUU&_mA*2FZ6hQS6R$tB&?g{Nl?5XYHob$y;>hv0|K69H8XW*wc2zTnq z>HpEEw!U?w?c>}|F9VDXRCQDT+&wb80A1q*&`tN#9AcNmbzw6_D2~iYd4oW*Z8} zuw&L^6N2clsIVp+6L<8*B#ExRqP$5woRaz_dEHW+5(0){0 zR-~>Rr!=ChQ#4YHT{Kk`QF zi{m4>jDIF8^95f~0A#;(ba;5scsLyOIju5TnpH{#VlVmB)tIL;<8_GW3x9!9mhH!1 zlNPz>SiE%(;@XqjQ=TbyKJU~EP`d5FGV3;DhFK=aNAy5o?+b$AiSYP!xqj8?Cn~z^ z{_JVm!IK2V{=?O^!S2<^lAFE5Z-+g2%6M{lXm}p-Q0v+1I+UrG4V4v_+3A(ea5sINZ4Ulu=Gx$@3Gxjq+AC$a)KTthLp*v?CfkeysW}6C+MMdEIus(-EW|Ri1$x`D;nP zCiaAO9X4A=Pui&$P;*WRbCFHaVyLzW|JkntcA**x0z+bbO_P`!WH94O6^ij3bF{L1 zXVG%*zI+@Hkgd{Ji>+Gbxmeh)V&pRNy7J2MIJ>vHcSHRlT+B7hYRpv-Gx-<@I%4M? zL2qj>d#_c`quv#0GweMyRv}ZtC9<8N7~CKCJ9a=JLQxDlR_SJ5K`N~(cVAX@2B7}b5RX1`6qA4SQ|z_ts-=B9x`9iT?c7=2q|0;C>tV>oaN-YR09w?<0%S?OzdN6XZL|4eUStZ!nGNldAVs z%=cq{csG2jt+17~{Ni?J-(v1-_fEh6AeuB)ITfPEuU9!UJR?=ULTAC)>^r_=p&F;6 zW;t>NS%P>wN;@s?dj1|Ju_Cc!4A5qLxpF1)b9dlge|S%Lf})S2Lc%@9eTRC7SFf~P zcwEA>-i=Isg~$K67}(DmrTUq+pl|H7hHx|Wx!!3)tdJlNr6=AdBWgFL_R(dFT+X=r znpc_U-{XtoCq_SxcI(KLwzloHEp&=q-VQpPP3hTLk5MAovqz-b++6lsT25%T?fBVV zg3-)KRxwopF>7Px{o8Q#TwP2PWM(~zaEtpAr#UUzxww|4x%anIEGFAlG z?=~H{GFH?4YPa++baLBS{hk#sCa~n*=5^VK+24hrBJM@>hNENqVq=#rS3b06`C3CoPvc5?bJd@rd_-ka?&?m*X2#BP12HjE0&nY*>L>&*1gHY|{5~BX5Avc6 zes~0XB)lx`$PLsv)L+}5@Q(HFK(j7ujBdZ_3gEi?cvrKVf-c3o?{NSA7Z4`w-O`j(wLyRB&{JDo2LN#C<($)7r?-wT|7_2; zXCh#X_wEaSoH@L{IhwB!A;M;T)v|Yc(BaO7leoMtg(sNtdp}Q%Hh5zvnn`VNT>oMC zNfJ+e=ac6X-&H~CtHa3wZ_74KPGfbr$w)agBKs{B$c?3cyklEZ{+7d!^&Tx$K862+ z+P{*y;3A$RK_$Suf|oiOw&td>qMP2%axqEt>kNdfSv-?@3|f@TbH^DlAh zhQ}-U6kqLXM5_$fc(om-!p^afAzmw|C9c8|Ihkw zb{%4E1%P|U%1}97!`>Xj9SDUY zX%hg7^gBDSnrlLKgci;Y$?fe;u6PAy;RN|5;0lg+j4_dF2=H|lE!TE`mT}KwmRpnB zz?LnZ-Jq5HCd)KV1tq)0!BuV8N*C@s=+(@R%md57i)I&SMf_s(o?wN=5jRDbm^?_* zn%&1Q_=QlBb33JFw1PPV5it4^!V2g5<^@$1aU!n_9kJ>5Gn9|EKp+mz`LGg`C>>o} zKu*LfQ|L>I&!1Hq<=flaCsuZGs;{5gRwFBcDL0|>H_PvLJhndyw9rjd_cgzy+VMKF#XXV}^xw@s#Kw7AXgUq0IL8Xv!d8 z32U~;CZ9#O#Wis^wjL~vB98!OKBNU#BzHaKCnjS#mzAbS3%oOxc7ESVLcl55+;lzc zt7Q@XVO0lfbwc@^wegrImzhcUkk9B>bzqZ!zBGE+D}+$-*Pp12>2Mx({5;yJS%g)7 z_3*s{wF?38cZG`r!#gbxhIS$%?aF2FR zuD;_6JWzI%&9fFW9nP25=&rD{`0drzJ27&Zye1|}OaDDFFeyozMKrV`?;VIXRA_-2 z0)$%Q6tF7j*<;jfoUSpQ$O*Li6E5$EpLzDLVK@`v&BiB8hBORW!@ZPX<}-9NIm&Nb!`x zxhF*xjplgMrmGWpL4XkAG#bKKzsB1=F2h*X0454I-UjkZ*|RQbz4Y1da( zDqe==`D`bO*ic&=yiFt@5D_Km^0_R!)JmDq9VMb`$L&Ff`oKE2?x5j zW<+SXv!GWP!S5gX+r$sR{HHnRT3O9o>pun&AE{Edq*NAL0-nj43_R2nkB+wEeAn*L z!NA_RDE?x4$h;Vp@W`j7@Fd$xCfHOYW?}fIQ-Z8Ka=%Hu)hhiKTgipBYgNi{CMkRC zmjRb&qMV^W&ise5FgbljTgJpCF2Q-XvwW(DqcQH{N$y(#V!(v<#)@Mrh5Ny0WT}E} z$7^)k4guy%V4^~8!F$yb&3*qtbiSkSCCoju)GogMIJA9$$StckGEeh?@pS+-h!%~P z;uA8;slqGJ;3Jc@w|&la=O{mr;Ng2--oF=d-L{e$2*LPrsr{kucVjuDz+8niTr7ra zr$ow5m_5D~+~YxA)?f?N)^0{hoCu{c8s0XVd|Pp9!lzC9f^ep5pr{hics!e~v!NS3 zlV0lcwtM)_rlI%`CYEiYt?sNUrtqv@r&z2da5U|=$Br@sUvfWcscV9PTC4h=#4&e_ zT|uDr?YBb2jTKM}{vj*4-=G#7x5l6`s~c#7Iu&aOXXOseztNB7kvC~SylvZ*=zSD) z52uEogmV5Pd6DfFcI}4`nWWZQ-=6A4*_(Ksfclv17OwDf%-{3UcN9!6ZuzfMcvo~BqKbas#zHsLb z4P7pJ)Zsd4!Z7UPs#JI@Tuk^ex5>d5#a!s3A}W0+n5y&g%_nzl2ISD`RZRSyY2>VZ zB}vQ5>EZq*Cq~pO&6BZQ(AIu2U(m{4^TsT%!OINkcy0z0YiO_Df2+2*Z*|%InRRmU zUOlB+YC{*b^x%VQ8Kde!zmlmBx^@Eim8HAIc~Ml9M`*u_g3X&LmJ4E9sFnMe;iNIm z?SXl4)zdwita7vPOu3W20RPz%3?h_%Q)cE;7*9ChJ%tJScFwS@_F-J``({4Rx88E1 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Configure SendGrid dynamic templates (Handlebars), tracking settings + (opens, clicks, subscriptions), link branding for custom tracking + domains, and content types (HTML, plain text, AMP). Use when customizing + SendGrid email content, tracking behavior, or branded links. Requires a + SendGrid API key (SG.-prefix) - not applicable to the Twilio Email API + (comms.twilio.com). +--- + +## Overview + +SendGrid email settings control how your messages are constructed, personalized, and tracked. Most configuration happens in the SendGrid Console, but templates and tracking can also be managed via API. + +--- + +## Dynamic Templates + +Templates use Handlebars syntax and are managed in Console > Email API > Dynamic Templates. Template IDs start with `d-`. + +Supported Handlebars helpers: + +| Helper | Use | Example | +|--------|-----|---------| +| `if` / `unless` | Conditional | `{{#if premium}}Welcome back!{{/if}}` | +| `each` | Iteration | `{{#each items}}{{this.name}}{{/each}}` | +| `equals` / `notEquals` | Comparison | `{{#equals status "active"}}...{{/equals}}` | +| `and` / `or` | Boolean logic | `{{#and premium verified}}...{{/and}}` | +| `greaterThan` / `lessThan` | Numeric | `{{#greaterThan count 5}}...{{/greaterThan}}` | +| `length` | Array/string | `{{length items}}` | +| `formatDate` | Date format | `{{formatDate date "MM/DD/YYYY"}}` | +| `insert` | Module insert | `{{insert "module_name"}}` | + +NOT supported: Custom helpers, inline partials, `lookup`, `log`, `with`, `blockHelperMissing`. SendGrid implements a subset of Handlebars.js. + +### Template Versions + +- Dynamic templates (IDs starting with `d-`): Support Handlebars +- Legacy transactional templates: Use `-substitution-` syntax - not interchangeable with Handlebars + +--- + +## Tracking Settings + +| Setting | What it does | Caveat | +|---------|-------------|--------| +| Open tracking | Inserts a tracking pixel | Unreliable: Apple Mail Privacy Protection inflates opens; image-blocking clients produce false negatives | +| Click tracking | Rewrites URLs through SendGrid's redirect | Can trigger spam filters on some domains | +| Subscription tracking | Adds unsubscribe footer | Required for CAN-SPAM compliance | +| Google Analytics | Adds UTM parameters | Only for marketing campaigns | + +Configure per-message or account-wide in Console > Settings > Tracking. + +--- + +## Link Branding (Custom Tracking Domains) + +By default, click-tracked links route through `url####.ct.sendgrid.net`. Link Branding lets you use your own domain (e.g., `links.yourdomain.com`) instead, which improves deliverability and builds trust. + +Setup: Console > Settings > Sender Authentication > Link Branding + +Requires a CNAME DNS record pointing your subdomain to `sendgrid.net`. Validate via API: `GET /v3/whitelabel/links/{id}/validate` + +--- + +## Content Type Priority + +When sending multiple content types, email clients display in this priority: + +1. `text/x-amp-html` (AMP - only in supporting clients, requires sender registration) +2. `text/html` (standard - most clients) +3. `text/plain` (fallback) + +Always include at least `text/plain` and `text/html`. + +--- + +## CANNOT + +- Cannot use custom Handlebars helpers - Only the built-in set listed above. +- Cannot guarantee open tracking accuracy - Pixel-based tracking is fundamentally unreliable. Do not use for business-critical logic. +- Personalizations `subject` is a plain string override - It bypasses the template subject. To use dynamic subjects, set Handlebars variables (e.g., `{{{subject}}}`) in the Dynamic Template's subject field and pass values via `dynamic_template_data`. +- Undefined template variables are silent - Missing keys in `dynamic_template_data` render as empty strings with no error. + +--- + +## Next Steps + +- Send email: `twilio-sendgrid-email-send` +- Delivery tracking: `twilio-sendgrid-webhooks` +- Manage bounces/unsubscribes: `twilio-sendgrid-suppressions` diff --git a/plugins/twilio/skills/twilio-sendgrid-email-settings/assets/icon-large.png b/plugins/twilio/skills/twilio-sendgrid-email-settings/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 zcmaKScT`i)^LFSEdRKu+=tvb1f`kr|NEHx4iXbhNPz6GhVnB*O=q*N?A_~$$sx+k< z1eD%;lP(}4{w|;MegA#`$jLc#_wLTl%-wmOoryIv(4hxk1A{;ydL%;27z83Q0Dk(^ zlz_yuJGvYAqwzvm_<}%^{Fgrx%xhJD5QrCq)KWLa;D5fH@t*1#j-FHY+xbj4U_#e+ zd#qVnp45$Bny0;wS)4m!gIZ6!!kC<@asF2vNrj3GnG9(d=LpU&Ck#ZnzSI84w6f?& zoKY}!^GKNN;nd0QoI1kYKXrq;t?Ycw+(vLBwtj~6qoVHwd-zQ9t52p+TT}m0hR>i{ zLKF}FU~3i?z``ya9x^=qF$)smL+xX}i^ZR-LnO58KvC&(*bXGon7M~ewQK(+LHlI| zMTI)k2z@vw_=X}?RBAIHU!YhyoqtPrt2q?&mO|uB5owff+qTe%F|RCna!8>|5-Wij zTXUr>GwOq=7A<%!9LCf~*Lf^tuMXwmWg}vf`}`^A4O)i_w+X`J&`|EOOfG8^!~-K3 ztL8!4Yopc&x1vj#w4St|qaFgXJiZKBkmvmbXz@5HhFn{YMg(t=EFD4__gY7!fSB)f z+g>4?$kePGz2H(oTA{`?WdFV(MqLEYfHOLWv^O~j!mSBR65EDAtEn8<6s$z#vMJ6y*b&`ABmMk5y`ad<3!38-CVNLVlQ8Bp3|x1^I$e|BPV4cgSU_GF4MxMr!PN zUdLGMGOdv`Qo%?q7~3&%bHFZzFhSO%v$bM1g-gr#^2gS&OQ^LA+;(Rsf0FO6lVSwp z=XdK_v$bTdY?qA;=a`HxB9D^gYwDGw;u?$92Q++Ah(>^cVQn3Z-W`y9Oqv_lA`jS^ zRv<}{D&PiR>o{rF%@9w9sA_0a?-3;bA+Z9FSs`gtp32yPdo&QH;g5l5zCZ#5oN{15$ zUTYX+HG&zorK>v3OP>T%g4dXV*yD*rRXsL{CO*J^2@HD0E`n$OXqAF1EM;moggArR2h5F~Fw+Nn6rSyB-PDGO4taQN8QDB22FgC0>2P{@@28(Orcxb&$}DBx0(^AbR% zM01jlTt0(qwoxVr*%a%JEW=pkNDN>Ulyr}g^+C2&9*Si+jSKXT%XQY*tHYw#*@qUU zF)7>Ayq2fo_konJNY)Hgm46h>@MS5@BDnyGP)L)|t)GlOQa^wQ$E#tF`XKFxdW{w; zE=j4t)7@!a3?X1L`U$eCqqk2H>N*WNRa%J)PRS2){4Joc@y#(wI*?@H5EP!!3eJsRbn+V0F4j%eB?RSFD-G>D2YQ=glHQ5B+Lv zX5F}$Q;V=Bi5sEh+SR&dr%;@f8F`f?-&=cf^%jcHHExhQTPu1~G3EmN_!Pt6Z_qlC zrA+WsQnh5CR|LK0tC6^ap0!f}egcwOSX=gnCGgd0EoubPFb^w{-*imA$$*S04qhli z-N`??!C7)LcS}3bxR(i=Hj7f4jxTmwNVBQ@RQuDbk7EIY`;4ona*I5onv39@q#Rq^ zFoRhrdv-xoyHW^_>U*C(i^ebxaKP|x?D2`*p=y!6Kt!av}+JKh`_VIgdDhv&L9uy@5Yqqb} z1+8L>xfYhAc{nYgm`qwnr5sAQyjGhw2Vdw9Y@&lpVI5y1>PsQplUh*^a?K5foitC@YG0R|;CW z+Sc;iK{56@o2IX{HpLaRiHtD5G>?qh?sv;)uIQuZTGqbOA^*y6t+g*^Yx>ez%Dn=; z5Y2I@v1xrV+E3RejO~5$mTl1}yFJZoM`$FNu3p)maXx7%D6rc~sDzKA-4gr#D^r8| z`1MgbCyieboX-fL0g~= zd({0CcH!U1r)zswC2Bnq+%s$WRt3j2b58u>kVRLtIBVH6Til)ftNB6Y=YQgf!ZJA2b56c|V7@+AajMimgT zCSVOwqLZbO9Fk8!obZw(uJH)A#$#g{uiU|)!=|4?$44*(R#J10DlowMDrC<>Tln=G z_+p9%=v*WR2bw;j9zdMqW>;SnAdO#F!F}Jm-*s2%G2VTv#YvoTxUCr^hE?$%^G~_W zM+iW8>NdiUDGKZO1t*^#@hb9aP(~C@UX0f9%iJ5Q>^_F=CTE~Nh~zk+5Kn#~rxJU# z^snDJgs~pUpMbvDh^-szH>ZyQ$=@EN6qw%38}d{NWYc`n)TH)stIxZc!swXFj5bf~ z{K_Eb@7D08O1%nom4_xFdJ6+2=jzdE%{A-4cl{&v&Q8Ad z4yuBB%4pwCyUo@7jL~8%udekHT19)1Og|(dk)M4T{$`X5gHA@TpZWF>trd@Utq)0a z%v-B#$F3H>BNz*YI^CBGNZr6FWOGoX`6df!K)WR8ue7*6jO)Ld@Q?{5*`tu(nPCLk zX!Vr&Bt6Mlaz;xrbuLY}fB8C=F<51ouP9(z)jaYYY+z=lh!9ief3b3B3a2SB_dBVM zCmHo)Y~n7AwJ)?3IGxMtn4crAIWSy@4nv?%9*InOvpKUb0;ws-vTW6Mu_0q^C@A|@ zsPB}KvghxoVgohwu%d&!U?zQDOy`^crxSQTO@0rWqjshLlSK3mOZw}It36HfO2W4| z3|yX7;@sT&o!y{s;OkO*hj|Zv+alpF>qkN=(UHnn{wZ=c$TrNbUD7>t2_jV>!lqf%A@!rEt-xjC0Y?=DL9XF>R&2 zxvsR%j`}su#}&dCw5J0uHU(o;DmTmj;yf3TxoX^uyE-1t$=ZUos73GD_J8_;o+U%p z?}~S8f3D*KA2@&FhU(<78ui*|7AwflLoO6l2tPtKSr_EeIo;d(jLY+>?|yK|-NtQQHYxX^ zAHNdzTalOv_E@$!C%>l+AG$5J7AXT|%$RbZ2|HM~UL`u&-DESnpDd%z%~n(483UW= z2j8$GIrZ6YT;F6B*5@NKrNyEg!~HP!F3;GJVaj5;L(vbl3*JYWNNy??g;dmdM`Ft~ z4{&!?o*0Sr@jn`*J^2FGZm1)Xh?T3DgF&efd zv=E!zgbUtdO(a);uhy)hiPCzW)ytb)M_{Mq4@>NBtX?0`YP23+ub*Mcx{;o;lCo&B zw1zXvmPm`DEzC+nja@b`MfqXQqic~~4-e(Lub2t`Wh+w_>}_J4@RiDT;qp?uoM845 zE4?+FK|{>ieF;D>DhvVp_&{DGImZ5~^eqO>z1!vcF#S-GTQ^tG;Osh*=$^E>S73#V z4!`z?uUvR2rSKf8@WI(&_iJ;ORdES+XN)pvvUfd|*KHoKT8G>zXC{`m@Z5lNO;0F~ z)U{th`=8yc*2OUcgA#Fo4bybL?m0(|1dcRmd!{n}8EkeL%IK;*zZ$=^PyGpqY)*r5 z{O1pLZu?j@kUg4=ZulsoUW6@Y`iiAC`9!04K3YffAx-hv`KrcLIP0F>KX$M)Ts$%M zbI#X|Vt zLT#u|j?lvE23(p|*OqTK&!2^R3u#X^lN1HYuQNbPs=vwSK_@D&6AoZW7gc1<;iOBr!>r>{;3{JS&~+~hW;a^@CH^zG;KDQFRG5@ z7G_vJkEA$iWaCcLpn*mjOOk^A12+9R5eX!Omfm`yH&v{L9@S=%G-97+gW@t`HB`Wi z)$q_JI<{Fd`sSn6$VBjcdv=U)?HD$Wn{-IfgOjw8x}xe1#ht-KSdX!!LX5ezRnlIC zQYfg&_HsNtut!aVDWU%#jExxZcXIezxtKS{B&A5T4qVD*{SRv?K59b{(2V{{=3Fz_ z`My5SJz)3bYdXbWY$26-_2Gjp5c>tcMG-(RHM6HCzhK=hxkHd7Sv2wkI-Dg~D`Z?d zyd_S@vaSkQp@a|WT{_5p=j8;9ApoKFriWftt6cU?{qG=sx{@4zXq?UI%AKs=)6bsEZJnN(oo<%V?~?Bj7JU31@55SN z=I?lXOM>cBne0J`l{$>w`teyIJ7>Eh)z`{0k<(HJv|1o7*vmRTG;j__VIDK~IX&sM ze)JnfGhw11ww+9P&;mG3=;~hQ68HP;A*R9VA2)#{MR(y*;C5QXWHt-^T8V${Ki648 zW(qE@j|+-pq+oKYPP;%e7XGR_ajnPH%NC_8U93Nm>et2OQp?Og_X>LdHrP}1b{u@~ z>M+wF)qcT!ra^JdYm2{cMvNAk01=1kC^3;NGXY$|9vf5q4wWImM-Yt{nm2@Xhc9tE z_nCH$*e5TgQVfYR?{jy~Y1QbQ70{g0{+I(jjk^4n+yD(dYHXZ9t*%o(0DlfcEz3#W z%Z_P&BI!=zK*pqh^aIVzRi1xlizBqC+A2+lP&6YdJ)oK1{Ij;N6of&>S8)QRYIdti^jt$oz-$0(F z@4Ana+~G%zpf4Pnhq?f6PbgA<`nLJAUJK)9N-x~ z=O3>27aaJ>rXh<3f#}=RNRqV^z)Pil^-99TQou5-FD)PmiPr?aCM?cw=^l&*s>Kc# zhGM@1mp;_cTlror8VG$6JeJ9r_5UaCD)a(?2$M!My~1xq*0m;n^z!GK2Kt_7hl>lh+m(2@x;nQe`BLaUlU-JhtI~yfb2qoT$03Ilm348CWpi*|&Fo-JYcTKDg;sLi z>n-%!(b;;fG^QedfLnv@c?Hdrs1Z`uYI60QsqH7$@}q*uNnSEqtMP!>b|F!$g})9I z&|Eb}Yy+AQ&~)(A^>p$>Ts!SLfDFm^Y^D;go!glGs7*o$asqgi5QElUnNk%d~W4HbY>dET2{ru)Jfh;54x5EE?<7%NYeXi>T46 zc1X6Z4J}2hv0hMs3^E53YJnpRYd0DsXFqJ|Sy1Eo8b(55Cu1%!y0#$^6hiV;k|EGi zIn9NOiEJOg25_qZ`VIr#mwbe>%;T~*&D!Qdn~>srOAMzLFooOxazg1i#|XL3KFwJ_ zi+QI10m0%B<$m3wz`;*eU(gV%UFB%V3kkGsP$8@{> zp6@y~%Bn7eBCHI-3Sm4ROyrU`SPrr+E5G0LWJTTUtD%3}``*`romdd2^l%CNB}m)j z3>t_KJmJy9C`Kpmzy!wR^8{xJbNas%*0_b|J-@t5apTI+fHV8c&?iAbTW;#q#es3#0^m+(e!5DkGjrQND4a5ghgyKY76tsZ_ zU2!e|CaH!9&sQ_6jL6U51!OOVoPKc%=fCp430Z_=w&uD}EzKf$NT(ksM3`j~EXAjP z9mA0EdrEPMQ-Q*oxx03*`^O3M|&&f7&7&Q~~j z=KVG%zPTJ^@B1FdcKiJj+mJttO~bOpG#dt>qMkz2{uJgT#2{2f3Vf);)BQeW`OU?COQ+%hlmK=h6S2bAf z6Ahk|Siog@dyVMz<3}N<;kglt%PaEIxQZKa0-M3c+LL+@9&d$S!I=>`)44|%>;(z# zUl#rBbI&?N*}Q+QCjfkc6=QAnO;EoZYyVq}qx;gen#XG#WY#PO<$(t8wF?sJNGS8Y zzSxD`xL!N}>^BrB>c-_oUe0=tQ?9k1n^UT(^E|W;?T1FGau#5f+qQ#SR<`4WVf!vZr#3*61jpp(!!LPV z7c22Lr(B$Nx$Ky{S~7ezO5oQ%(^e31yidTLbi#L}y8gGv!5K1+-x9fW|7grH0OH)?D+TbrL@Vy+8&9aR6$F;N+SwA|9q zcG-Gr;Q($RLL`|T+#V0JKa!A`OTV?@S^E=(EDNp%cNWoJUT{YDXHZEc zEn63Am1v~8H&C#{RDR zm+@OlDd3Du$*MmB{OSBK%xY(dZsWy@##GFW%L+v6RMK4z{n_u|m)y^(67~%|K(eL~ z%pl=^nDLdw3rF_Y?>>&QkH2AV-mzcML#^m~h&oW&TtBru9SD3{dz7d2Dn{ z;TNC(#7kb|buo#-t6i%m($fVErsIn3R$yD{VY=(7%L7yyX2P29!j@IN` zD$r4~QKJHFwml5w`)s>E`Q4Ka!D>$X>Qc#-1GZr|}x0ma3@|I8GC+V@%pTRqwy{QPUucUQmP20>FMLA`ryFw$R$2*^d z+<~Ib$FCHVL3lMIkN(+jwOMo%x&X+%1An7x%m3(vd|q`{ixnuR0EeLV-m=7tl?UWf zBUPwXgr-~swT-V^5-Xx_i(JFI(`x)_OerIA=wHJRL(nRRX0K3BO8eCgQfzDY0ZDuR z^wD{M8&o&#Uol!o;9+pC8hnA9WSlqQE*I$ho^!mY{_-0_{uI4KJ@xPHhF{N{=`SIs zo%me9sk0e%^4E^>;od@lQ+FK;1d}!_V!4^99BygI2UR%b(GXP84BmB#@;cw?aGk9e zyJ2sU=xjNJxs!zfd)j9k*|5eeqe;D^WNWiaO4)Kxz=_b$MV5gHD}ITb*uhDMRNHh_12!_A8bv(?mtH5mUHt!pk zRAh!!jL#tvnTV#032>O^K^ABH4Lr;2Yx(&{4#$E5fdVVXF;9#=)XW8IVglR8A;P%T z2`S&~C>kUO^HptDXGNh_DC&anI zH>LY!DAAj2erR}N8{73*AzShrTB?GhT2gW{)(b4kaetN%LtryZp&O=8EkHf}`Hr(m zc7g5WwB-Gk*a64IpcNl^o7>dAyAoKz;3ru!+rvxbkz?7$!N$ExG-zsX-N=@l`#Vim zT|*^aak==1fSf8$c>nHVLBnQ{=lBO@gPuD3l3L+(BY3P53#WcwVWd$yG0G(C*HM66 z9)?AMPEGF9ab@ydsSeR7J}KulxrjyNfGlWex}X?x@HRID&)+_inz#_8BbXuykfgls z&?kSuKl<6Vt6MJ!EZc_yt2mU1-%!Y=@8SL|mKC&oJ>#*bbGk9Sq3-3|)V;X88^K50 zX}>t$Wzq>aP`!ASgB*TSWvAonHkdT@=bl1K5WdY;!=G1=?w^C--()_PIcgnEnuXnldq^i|EmsLG`5h87-14JA4iCN%M6dqz(PxkGoAg`vUjf z_+h9eD1dGT$+H}20m78K$59Ru?|43QeQU#KGxoBM2i&ZVvgoU&hpONj+i0Uja~L_a zSfv*MHWFU+k?Ys33NI}KJ{u>7c|%8&8uNrvUD@~hdKkQ5Re`wcvR8cWqNa+X{3XO&PlcXHk{vdP64plF#>XE zW24E~uRHLn$kqlH(f&cav5QRO!nY7jUBXClC?I)U`HK%DBmwCLs4?bm#h1Eeii!2YG8}w;;pw!sz@c`?Lz7iFcr-`>E}LG>Xrk?u7u9l zUZmw>V#casx8=pKKSYi+`1#^*?WpZmNlI$7dWqvfpQ{A{f~?Mr#umMBvfZf?^#J6O zfLxY^ZAc8qc5lO4hRrk4ZHSzO$sm03YPK^uvjqHT#4mPPd>7V!z(LeU(VT_dix;=S>_@}j5s=vu;z{_CaHlaX%cjk_uE_%&vr zWZvQOKIyV%{?@1^uC$5BMBId8tqijVxrRc$x1Nkio2`sA-L0Vu z9#J zm0@WeYG$2d+PnG+<{P#4ly4!&h2VX8lV4b!mhFT^jsa@Bw>&>ZodAk-Q=* z^V1Dr;GmHjk_yg?T>;u8qDiAnXuu*a$g3$*B3XYm7fkPvA3>&D6RoIxvncbtu0`C( z_(Vu_{M|>ie}CTLLbpNom1H_X>09czk1A^i2uq;zgikEliP{c+nAiC@se-Bl|64cL z2*$%}cTU$vWvz?ohVxSkb$TxWGN{cukGbC6Sc#6x??;k6>I+ZemNlFY9*gY8D@U5E z$OLS#f}KfwV)3%%o@soptZ|MSmmM`)M+P=@Ykm`?B5IX-fGs)@uvgF+6lI;luXeC1 zyq^9NF70jJClxWLzfQ?UB+-PTyTIxqkC?DNg|77XB07*Z$>*qO zujq4$EdCij>SbCj@r`>d;T=dxUYiYbo0St$vz=EY?#uVX`H)1l1%U)6bsi|@%~D_g zySF>c@a~!R2Vj8``l!?=kSN4dPmbBHb^p^E!U@ VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-sendgrid-email-settings/assets/icon-small.png b/plugins/twilio/skills/twilio-sendgrid-email-settings/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Monitor email program health with SendGrid Engagement Quality (SEQ) + scores. Covers the SEQ API endpoints, the 5 scoring metrics + (engagement recency, open rate, bounce classification, bounce rate, + spam rate), eligibility requirements, and interpreting scores for + deliverability improvement. Use when diagnosing SendGrid deliverability + issues or monitoring sender reputation. Requires a SendGrid API key + (SG.-prefix) - not applicable to the Twilio Email API (comms.twilio.com). +--- + +## Overview + +SendGrid Engagement Quality (SEQ) scores measure how "wanted" your email is by recipients. Higher scores (1-5 scale) correlate with better inbox placement. SEQ is a diagnostic tool - it tells you where your email program is healthy and where it needs improvement. + +Key insight: SEQ scores are correlated with deliverability. A higher score means more emails land in inboxes, not spam folders. + +--- + +## Eligibility Requirements + +Your account must meet ALL conditions to receive scores: +1. Pro or Premier Email API plan - SEQ is not available on Free or Essentials plans +2. Open tracking enabled in SendGrid settings +3. Minimum 1,000 messages sent in the previous 30 days + +If not eligible, the `score` and `metrics` fields are omitted from API responses entirely. + +--- + +## The 5 Metrics + +All scores range from 1 (poor) to 5 (excellent). + +| Metric | What it measures | How to improve | +|--------|-----------------|---------------| +| engagement_recency | Are you sending to an engaged audience? Based on how regularly messages are opened and clicked. | Remove inactive subscribers. Implement re-engagement campaigns before pruning. | +| open_rate | Degree to which your audience opens your messages. | Improve subject lines. Segment audiences by engagement level. | +| bounce_classification | Rejection by mailbox providers due to reputation or spam-like content. | Fix content triggering spam filters. Warm IPs properly. Monitor domain reputation. | +| bounce_rate | Are you sending to addresses that don't exist? Based on permanent bounces to invalid mailboxes. | Implement double opt-in. Clean lists quarterly. Use Email Validation API before sending. | +| spam_rate | Are recipients marking your email as spam? Based on recipients who open then report spam. | Only send to opted-in recipients. Make unsubscribe easy. Match content to expectations set at signup. | + +Note: The overall `score` is NOT a simple average of the 5 metrics - the weighting formula is opaque. A single low metric (e.g., spam_rate = 1) can drag the overall score significantly. + +--- + +## API Endpoints + +### Get Your Scores (Date Range) + +`GET /v3/engagementquality/scores` + +| Parameter | Required | Description | +|-----------|----------|-------------| +| `from` | Yes | Start date (YYYY-MM-DD, UTC) | +| `to` | Yes | End date (YYYY-MM-DD, UTC) | + +Python +```python +import os, requests + +headers = {"Authorization": f"Bearer {os.environ['SENDGRID_API_KEY']}"} +response = requests.get( + "https://api.sendgrid.com/v3/engagementquality/scores", + params={"from": "2026-04-01", "to": "2026-04-23"}, + headers=headers +) + +if response.status_code == 200: + for entry in response.json()["result"]: + print(f"Date: {entry['date']}, Score: {entry.get('score', 'N/A')}") + metrics = entry.get("metrics", {}) + for metric, value in metrics.items(): + print(f" {metric}: {value}") +elif response.status_code == 202: + print("Scores not yet calculated - try again later") +``` + +### Get Subuser Scores (Single Date) + +`GET /v3/engagementquality/subusers/scores` + +| Parameter | Required | Description | +|-----------|----------|-------------| +| `date` | Yes | Date (YYYY-MM-DD, UTC) | +| `limit` | No | Results per page (default 1000, max 1000) | +| `after_key` | No | Pagination cursor | + +Returns paginated results with `_metadata.next_params.after_key` for pagination. + +--- + +## Response Patterns + +200 OK - Scores available: +```json +{ + "result": [{ + "user_id": "12345", + "username": "myaccount", + "date": "2026-04-22", + "score": 4, + "metrics": { + "engagement_recency": 4, + "open_rate": 5, + "bounce_classification": 3, + "bounce_rate": 4, + "spam_rate": 5 + } + }] +} +``` + +202 Accepted - Scores are calculated asynchronously. Not yet available for the requested date. Retry later. + +Score or metrics omitted - Account/subuser is not eligible (open tracking off or <1,000 sends in 30 days). + +--- + +## CANNOT + +- Cannot get scores without open tracking enabled - This is a hard prerequisite. No tracking = no score. +- Cannot get scores with fewer than 1,000 messages in 30 days - Low-volume senders are ineligible. +- Cannot query more than 90 days in the past - Date range is limited to the last 90 days. +- Cannot get real-time scores - Scores are calculated asynchronously (daily). A `202` response means "not ready yet." +- Cannot determine the exact weighting formula - The overall score is not a simple average. Individual metric weights are not published. +- Email Validation API is a separate paid feature - Referenced in bounce_rate improvement guidance, but requires Pro or Premier plan. Not included in base plan. +- Subuser endpoint accepts only a single date - Not a date range. Query one day at a time. + +--- + +## Next Steps + +- Improve bounce rate: `twilio-sendgrid-suppressions` +- Track delivery events: `twilio-sendgrid-webhooks` +- Account setup: `twilio-sendgrid-account-setup` diff --git a/plugins/twilio/skills/twilio-sendgrid-engagement-quality/assets/icon-large.png b/plugins/twilio/skills/twilio-sendgrid-engagement-quality/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-sendgrid-engagement-quality/assets/icon-small.png b/plugins/twilio/skills/twilio-sendgrid-engagement-quality/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Receive inbound email via SendGrid Inbound Parse webhook. Covers MX + record setup, parsed vs raw mode, handling attachments, and common + pitfalls. Use when building email-to-app workflows like support ticket + creation or email processing pipelines. Requires a SendGrid API key + (SG.-prefix) - not applicable to the Twilio Email API (comms.twilio.com). +--- + +## Overview + +Inbound Parse converts incoming email into HTTP POST requests to your webhook endpoint. SendGrid receives the email at your domain's MX records and forwards the parsed content to your application. + +--- + +## Setup + +1. Configure MX records: Point your domain (or subdomain) to `mx.sendgrid.net` +2. Add webhook: SendGrid Console > Settings > Inbound Parse > Add Host & URL +3. Choose mode: Parsed (default) or Raw + +Subdomain recommended: Use `inbound.yourdomain.com` to avoid disrupting existing email on `yourdomain.com`. + +--- + +## Parsed Mode (Default) + +SendGrid extracts fields and POSTs them as form data: + +| Field | Description | +|-------|-------------| +| `from` | Sender address (`"Name "`) | +| `to` | Envelope recipient | +| `subject` | Email subject line | +| `text` | Plain text body | +| `html` | HTML body | +| `envelope` | JSON string with `to` array and `from` | +| `attachments` | Number of attachments (as string) | +| `attachment-info` | JSON metadata for each attachment | +| `attachment1`, `attachment2`... | Actual attachment files | + +Python (Flask) +```python +from flask import Flask, request +import json + +app = Flask(__name__) + +@app.route("/inbound", methods=["POST"]) +def handle_inbound(): + sender = request.form.get("from") + subject = request.form.get("subject") + text_body = request.form.get("text") + html_body = request.form.get("html") + envelope = json.loads(request.form.get("envelope", "{}")) + attachment_count = int(request.form.get("attachments", "0")) + + print(f"From: {sender}, Subject: {subject}") + + for i in range(1, attachment_count + 1): + attachment = request.files.get(f"attachment{i}") + if attachment: + print(f"Attachment: {attachment.filename}, {attachment.content_type}") + + return "", 200 +``` + +> Security: All inbound email content (`from`, `subject`, `text`, `html`, attachments) is untrusted external input. Sanitize HTML to prevent XSS before rendering. If feeding content to an LLM, isolate it as user input - never concatenate into system prompts. Verify webhook authenticity using signed webhooks (see Security section below). + +--- + +## Raw Mode + +Posts the entire MIME message as `rawEmail` field. Use when you need full headers, DKIM signatures, or non-standard MIME parts. You must parse the MIME message yourself. + +--- + +## Signed Inbound Parse Webhook (Security) + +SendGrid supports ECDSA signature verification for Inbound Parse, the same mechanism used for Event Webhooks. Enable it to cryptographically verify that payloads originate from SendGrid. + +Strongly recommended over IP allowlisting - SendGrid's webhook traffic comes from dynamic cloud infrastructure where IPs change frequently. Signature verification is more reliable and secure. + +--- + +## CANNOT + +- Cannot use Inbound Parse on a domain that already receives email - MX records must point to `mx.sendgrid.net`. Use a subdomain to avoid disrupting existing email (e.g., Google Workspace, Microsoft 365). +- Cannot receive email without MX record changes - DNS access is required. If you can't modify MX records, you can't use Inbound Parse. +- Cannot receive emails larger than 30MB - Inbound messages exceeding 30MB are rejected. +- Cannot filter inbound email before it hits your webhook - All email sent to the configured domain reaches your endpoint. Implement filtering in your handler. +- Cannot route to different endpoints per address - All mail for the configured domain/subdomain goes to a single webhook URL. +- Cannot guarantee delivery order - Emails may arrive at your webhook out of order, especially under high volume. +- No built-in rate limiting - All email to your configured domain reaches your endpoint. Implement rate limiting, payload size validation, and content sanitization in your handler. + +--- + +## Next Steps + +- Send email: `twilio-sendgrid-email-send` +- Delivery tracking: `twilio-sendgrid-webhooks` +- Account setup: `twilio-sendgrid-account-setup` diff --git a/plugins/twilio/skills/twilio-sendgrid-inbound-parse/assets/icon-large.png b/plugins/twilio/skills/twilio-sendgrid-inbound-parse/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-sendgrid-inbound-parse/assets/icon-small.png b/plugins/twilio/skills/twilio-sendgrid-inbound-parse/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Manage SendGrid email suppressions: bounces, blocks, spam reports, + invalid emails, global unsubscribes, and ASM suppression groups. + Covers when and how to remove suppressions, reputation impact, and + category-based unsubscribe management. Use when debugging SendGrid + delivery issues or building unsubscribe flows. Requires a SendGrid API + key (SG.-prefix) - not applicable to the Twilio Email API (comms.twilio.com). +--- + +## Overview + +Suppressions prevent SendGrid from sending to addresses that have bounced, reported spam, or unsubscribed. They protect your sender reputation but can also block legitimate re-sends if not managed correctly. + +--- + +## Suppression Types + +| Type | Endpoint | What triggers it | Auto-added? | +|------|----------|-----------------|-------------| +| Hard Bounces | `/v3/suppression/bounces` | Permanent delivery failure (invalid mailbox, domain doesn't exist) | Yes | +| Soft Bounces | No management API - automatic retry only | Temporary failure (mailbox full, server down) - SendGrid auto-retries before suppressing | Yes, after repeated failures | +| Blocks | `/v3/suppression/blocks` | Temporary rejection by receiving server (reputation, policy, content) | Yes | +| Spam Reports | `/v3/suppression/spam_reports` | Recipient marks email as spam | Yes | +| Invalid Emails | `/v3/suppression/invalid_emails` | Malformed email address | Yes | +| Global Unsubscribes | `/v3/suppression/unsubscribes` | Recipient unsubscribes from all email | Yes | +| Group Unsubscribes (ASM) | `/v3/asm/groups/{id}/suppressions` | Recipient unsubscribes from a category | Yes | + +Hard vs Soft bounces: Hard bounces (permanent) immediately suppress the address. Soft bounces (temporary) trigger retries - SendGrid will retry delivery before eventually suppressing if the issue persists. + +--- + +## Managing Suppressions + +List bounces (Python) +```python +import os, requests + +headers = {"Authorization": f"Bearer {os.environ['SENDGRID_API_KEY']}"} +response = requests.get("https://api.sendgrid.com/v3/suppression/bounces", headers=headers) +for bounce in response.json(): + print(f"{bounce['email']}: {bounce.get('reason', 'unknown')}") +``` + +Remove a bounce (Python) +```python +requests.delete(f"https://api.sendgrid.com/v3/suppression/bounces/{email}", headers=headers) +``` + +> Caution: Deleting suppression records (especially spam reports) allows re-sending to addresses that previously complained. Always confirm with the user before removal and document the business reason. + +--- + +## ASM Suppression Groups + +Use suppression groups for category-based unsubscribes (e.g., "Marketing", "Transactional", "Product Updates"). Recipients can unsubscribe from one category without being suppressed from all email. + +Create a group: +```python +requests.post("https://api.sendgrid.com/v3/asm/groups", + headers={headers, "Content-Type": "application/json"}, + json={"name": "Marketing Emails", "description": "Promotional offers and updates"}) +``` + +Send with a suppression group: +Include `asm.group_id` in your Mail Send request. Recipients see a "manage preferences" link instead of a global unsubscribe. + +--- + +## Auto-Purge (Bounce & Block Cleanup) + +Configure automatic purge schedules in Console > Settings > Mail Settings > Purge Bounces & Blocks: + +- Soft Bounces: Auto-purge after N days (1-3,650 days) +- Hard Bounces: Auto-purge after N days (1-3,650 days) + +Caution: Enabling auto-purge without a business reason allows re-sending to previously bounced addresses, which damages sender reputation. Do not use as a workaround to force delivery. + +--- + +## Address Allow List + +Console > Settings > Mail Settings > Address Allow List allows specific email addresses or domains to bypass all suppressions. Useful for internal testing addresses. + +Use with extreme caution - never allowlist domains you don't control (e.g., `gmail.com`), and never use to bypass spam report suppressions. + +--- + +## CANNOT + +- Suppressions are global by default - A bounce or spam report on ANY email suppresses the address from ALL future sends. Use ASM groups to scope unsubscribes. +- Removing a suppression does not fix the underlying issue - Deleting a bounce record lets you retry, but the mailbox is likely still invalid. Re-sending to hard bounces damages sender reputation. +- Cannot prevent spam report suppressions - When a recipient marks you as spam, the suppression is automatic and cannot be overridden. +- Cannot bulk-remove suppressions by domain - Must remove individually by email address via API. + +--- + +## Next Steps + +- Send email: `twilio-sendgrid-email-send` +- Delivery tracking: `twilio-sendgrid-webhooks` +- Account setup: `twilio-sendgrid-account-setup` diff --git a/plugins/twilio/skills/twilio-sendgrid-suppressions/assets/icon-large.png b/plugins/twilio/skills/twilio-sendgrid-suppressions/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-sendgrid-suppressions/assets/icon-small.png b/plugins/twilio/skills/twilio-sendgrid-suppressions/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Track email delivery and engagement via SendGrid Event Webhooks. + Covers all 11 event types (delivery + engagement), webhook handler + implementation, ECDSA signature verification, batched event processing, + and common debugging patterns. Use when building SendGrid delivery + tracking, engagement analytics, or bounce handling. Requires a SendGrid + API key (SG.-prefix) - not applicable to the Twilio Email API (comms.twilio.com). +--- + +## Overview + +The Mail Send API returns `202 Accepted` (queued) - it does NOT confirm delivery. To know what happened to an email, use Event Webhooks. + +Enable: SendGrid Console > Settings > Mail Settings > Event Notification + +--- + +## Event Types + +### Delivery Events +| Event | Meaning | +|-------|---------| +| `processed` | SendGrid accepted and will attempt delivery | +| `deferred` | Temporary failure - SendGrid will retry | +| `delivered` | Recipient's mail server accepted the message | +| `bounce` | Permanent failure - address invalid or rejected | +| `dropped` | SendGrid will not deliver (suppression, invalid, spam) | + +### Engagement Events +| Event | Meaning | +|-------|---------| +| `open` | Recipient opened (pixel-based - unreliable) | +| `click` | Recipient clicked a tracked link | +| `spamreport` | Recipient marked as spam | +| `unsubscribe` | Recipient clicked unsubscribe link | +| `group_unsubscribe` | Recipient unsubscribed from ASM group | +| `group_resubscribe` | Recipient re-subscribed to ASM group | + +--- + +## Webhook Handler + +Critical: SendGrid posts batched arrays of events, not single objects. Your handler must parse an array. + +> Security: SendGrid webhook endpoints are unauthenticated by default. Enable [Signed Event Webhook Requests](https://docs.sendgrid.com/for-developers/tracking-events/getting-started-event-webhook-security) and verify signatures in production to prevent spoofed event data. + +Python (Flask) +```python +from flask import Flask, request +app = Flask(__name__) + +@app.route("/sendgrid/webhook", methods=["POST"]) +def handle_events(): + events = request.get_json() # Always an array + for event in events: + email = event.get("email") + event_type = event.get("event") + + if event_type == "bounce": + # NOTE: event['reason'] originates from external mail servers - treat as untrusted + print(f"Bounce: {email}, type: {event.get('type')}, reason: {event.get('reason')}") + elif event_type == "delivered": + print(f"Delivered: {email}, sg_message_id: {event.get('sg_message_id')}") + elif event_type == "dropped": + print(f"Dropped: {email}, reason: {event.get('reason')}") + elif event_type == "spamreport": + print(f"Spam report: {email}") + return "", 200 # Must return 2xx to acknowledge +``` + +Node.js (Express) +```javascript +app.post("/sendgrid/webhook", express.json(), (req, res) => { + const events = req.body; // Always an array + for (const event of events) { + switch (event.event) { + case "bounce": + console.log(`Bounce: ${event.email}, reason: ${event.reason}`); + break; + case "delivered": + console.log(`Delivered: ${event.email}`); + break; + case "spamreport": + console.log(`Spam: ${event.email}`); + break; + } + } + res.status(200).send(); +}); +``` + +--- + +## Multiple Webhook Endpoints + +Since May 2023, you can configure multiple Event Webhook endpoints, each receiving different event types. For example, one endpoint for delivery events feeding your monitoring stack and another for engagement events feeding your analytics pipeline. + +Configure in Console > Mail Settings > Event Webhooks. Each endpoint has a Friendly Name and Webhook ID. The number of endpoints allowed depends on your SendGrid plan. + +--- + +## Authentication Options + +Two methods for verifying webhook payloads: + +| Method | How it works | +|--------|-------------| +| Signed Event Webhook (ECDSA P-256) | Verify `X-Twilio-Email-Event-Webhook-Signature` and `X-Twilio-Email-Event-Webhook-Timestamp` headers using the verification key from Console | +| OAuth 2.0 | SendGrid obtains a token from your authorization server and includes it in webhook requests | + +Neither is enabled by default. Enable in Console > Mail Settings > Event Webhooks. + +--- + +## Retry Behavior + +SendGrid retries webhook delivery for up to 24 hours if your endpoint returns a non-2xx status. Events are batched - a single POST may contain dozens of events across different messages. + +Deduplication: Use `sg_event_id` as a unique key. It's stable across retries. + +--- + +## CANNOT + +- Cannot receive real-time delivery confirmation synchronously - Mail Send returns `202` (queued). Delivery status is async via webhooks only. +- Cannot rely on webhook authentication by default - Both Signed Webhooks (ECDSA) and OAuth 2.0 must be explicitly enabled. Without either, anyone can POST to your endpoint. +- Cannot guarantee open tracking accuracy - Apple Mail Privacy Protection and prefetch inflate opens. Image-blocking clients produce zero opens. Do not use for business-critical logic. +- Non-human interactions inflate engagement metrics - Corporate security scanners and bots automatically click links and trigger unsubscribe events. Filter using User-Agent patterns and timing analysis. + +> Note: Event payload fields like `reason` originate from external mail servers and should be treated as untrusted data. Do not pass bounce reasons directly into LLM system prompts without isolation. + +--- + +## Next Steps + +- Send email: `twilio-sendgrid-email-send` +- Manage bounces from webhook events: `twilio-sendgrid-suppressions` +- Receive inbound email: `twilio-sendgrid-inbound-parse` diff --git a/plugins/twilio/skills/twilio-sendgrid-webhooks/assets/icon-large.png b/plugins/twilio/skills/twilio-sendgrid-webhooks/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-sendgrid-webhooks/assets/icon-small.png b/plugins/twilio/skills/twilio-sendgrid-webhooks/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Best practices for ISVs (Independent Software Vendors) building SMS + features into multi-tenant SaaS platforms using Twilio. Covers customer + onboarding for A2P and toll-free compliance, subaccount architecture, sender management, billing patterns, and common ISV pitfalls. Use this when building SMS + capabilities that your customers will use to message their end users. +--- + +## Overview + +ISVs face unique challenges when building SMS into their platforms: each customer needs their own number registration, sender pool management, compliance isolation, and usage tracking. This skill consolidates the architectural patterns and operational knowledge specific to multi-tenant SMS platforms. + +--- + +## Are You an ISV? + +Before following this skill, determine whether you are an Independent Software Vendor (ISV) or a direct customer. + +### Direct Customer + +Your company sends messages for your own products and services. Your end users know they are interacting with your brand. + +Example: A shoe company called CoolShoes sends marketing messages and order updates for their own products. Even if CoolShoes owns multiple brands (CoolShoes and CoolShirts), they are still a direct customer because both brands are operated by the same company. + +Follow the direct customer onboarding process - not this ISV skill. + +### ISV (Independent Software Vendor) + +Your company provides messaging services to other businesses, who are represented by their own brands. Your end users think they are interacting with your client's brand, not yours. + +Example: HotelTech Inc. sells a technology platform for hotels. When hotel SleepWell Inn uses the service, hotel visitors receive messages that appear to come from SleepWell Inn. Visitors likely don't know HotelTech powers the interaction. + +Follow this ISV skill. + +### Still Not Sure? Two Key Questions + +1. Who do your end users think they are receiving messages from? +- Your brand -> You are a direct customer +- Your client's brand -> You are an ISV + +2. How much control do your clients have over message contents? + +| Scenario | Classification | Example | +|----------|---------------|---------| +| Templated messages with little/no customization | Direct customer | EventSite sends templated event reminders to attendees. Event organizers can only customize basic details (event name, date). End users interact with EventSite brand. | +| Clients can customize and send messages on their own behalf | ISV | PoweringEvents provides a platform where car dealership CarWorld can write and send customized messages about their Cars & Coffee events. Attendees don't know PoweringEvents exists - messages appear to come from CarWorld. | + +If you give clients the ability to send customized messages that end users perceive as coming directly from your client, you are an ISV. + +--- + +## Prerequisites + +- Twilio parent account (for your platform) +- Understanding of A2P 10DLC requirements + - See `twilio-compliance-onboarding` for registration basics +- Understanding of Messaging Services + - See `twilio-messaging-services` for sender pool management +- Environment variables: + - `TWILIO_ACCOUNT_SID` (parent account) + - `TWILIO_AUTH_TOKEN` (parent account) + - See `twilio-iam-auth-setup` for credential security +- SDK: `pip install twilio` / `npm install twilio` + +--- + +## Key Architecture Patterns + +### Subaccount Strategy + +Recommended approach: Create one Twilio subaccount per customer. + +Why subaccounts: +- Billing isolation: Each customer's usage appears on their own Twilio account, making cost tracking and pass-through billing straightforward +- Compliance isolation: One customer's compliance violations or spam complaints do not affect other customers +- Credential isolation: Each customer has their own Account SID and Auth Token, limiting the blast radius if credentials are compromised +- Separate rate limits: Each subaccount has its own throughput and sending limits + +--- + +## Customer Onboarding Flow: A2P 10DLC + +Use this flow when your customer needs to send SMS via 10-digit long code (local) numbers in the United States or Canada. + +The [full onboarding overview](https://www.twilio.com/docs/messaging/compliance/a2p-10dlc/onboarding-is) includes all necessary API calls to complete A2P campaign registration for your customers. + +Timeline: 13-20 business days total (3-5 days for Brand + 10-15 days for campaign). Start early. + +Do not skip this step. Unregistered traffic gets blocked (error 30034). + +### Step 1: Create Secondary Customer Profile + +As an ISV, you create a Secondary Customer Profile for each of your clients in their own subaccount. This profile contains your client's business information. + +### Step 2: Register Brand + +Required fields for Standard Brand: +- Legal business name (must match EIN records exactly) +- EIN (Employer Identification Number) or business tax ID +- Business type (private, public, non-profit, government) +- Business address +- Website URL (must be publicly accessible) +- Business registration country +- Contact: first name, last name, email, phone + +Once you have customer's business information, submit Brand registration using the Customer Profile Bundle SID from Step 1. + +Each Standard Brand is assigned a Trust Score, which affects each campaign's throughput and the T-Mobile daily message limit for the Brand. + +Timeline: 3-5 business days. + +### Step 3: Create Campaign + +Create a campaign for your customer's use case. + +Critical ISV consideration: Each customer needs their own campaigns. Do NOT share campaigns across customers - it violates carrier policies and creates compliance risk. + +Create the campaign with: +- Brand registration SID +- Use case (e.g., "2FA", "MARKETING", "MIXED") +- Clear description matching the actual use case +- 2+ sample messages that match the use case exactly +- Opt-in/opt-out details +- Whether messages contain embedded links or phone numbers + +Timeline: 10-15 business days. + +### Step 4: Provision Numbers and Create Messaging Service + +1. Buy 10DLC numbers for the customer +2. Create a Messaging Service +3. Link the campaign to the Messaging Service +4. Add the number to the Messaging Service + +--- + +## Customer Onboarding Flow: Toll-Free Verification + +Use this flow when your customer needs to send SMS via toll-free numbers (800, 888, etc.). + +Timeline: 3-5 business days. + +Do not skip this step. Unregistered traffic gets blocked (error 30032). + +When to use toll-free vs. 10DLC: +- Toll-free: Lower throughput (~3 SMS/sec per number, can be raised via Traffic Optimization Engine), one number per use case +- 10DLC: Higher throughput (3.75 - 225 SMS/sec per campaign), can have multiple numbers per campaign + +### Step 1: Buy Toll-Free Number + +Purchase a toll-free number for the customer in their subaccount. + +### Step 2: Submit Toll-Free Verification + +Submit toll-free verification with: +- Business name and website +- Notification email +- Use case summary and categories +- Production message sample +- Opt-in type (VERBAL, WEB_FORM, etc.) +- Opt-in image URLs (screenshots of opt-in flow) +- Expected monthly message volume +- Toll-free phone number SID +- Status callback URL + +Timeline: 3-5 business days. + +--- + +## Multi-Tenancy Patterns + +### API Key Isolation + +Create API keys per customer instead of sharing parent account credentials. + +Generate API keys per customer with a descriptive friendly name. Store the API key SID and secret securely; use them to provision resources in the customer's account on their behalf. + +Only use a customer's dedicated API key - not your parent account credentials. This limits the blast radius if a customer's key is compromised. + +--- + +## Operational Patterns + +### Throughput Management + +Throughput per Brand type: + +| Brand type | SMS/sec per campaign | T-Mobile SMS daily cap (per Brand) | Total SMS daily cap (per Brand) | +|-----------|-------------------|---------------------------| ---------------------------| +| Sole Proprietor | ~1 | 1,000 messages | 3,000 messages | +| Low-Volume Standard | ~3.75 | 2,000 messages | 6,000 messages | +| Standard | ~12-225 (varies by Trust Score) | 2,000+ messages (varies by Trust Score)| Unlimited | + +ISV strategy: +- Use Standard Brands for your customers unless they lack an EIN (use Sole Proprietor) or you are sure they will never send more than 6,000 SMS per day (use Low Volume Standard Brand) +- Submit a support case to apply for secondary vetting if you want to upgrade from a Low Volume Standard Brand to a Standard Brand + +--- + +## Common ISV Pitfalls + +### 1. Sharing Campaigns Across Customers + +DON'T: Use a single shared campaign SID for all customers in your parent account. + +Problem: Violates carrier policies. One customer's spam complaint affects all customers. Campaign rejection or shutdown blocks everyone. + +DO: Each customer has their own campaigns in their own subaccount. + +### 2. Building Before Registering + +DON'T: +- Launch SMS feature to customers +- Let them send messages +- Register for A2P later when messages start failing + +Problem: Messages blocked immediately (error 30034). Customers can't send. Scramble to register takes 10-15 business days. + +DO: +- Build A2P registration into customer onboarding flow +- Block SMS feature until Brand + campaign approved +- Show registration status in customer dashboard +- Set expectation: "SMS will be available in 10-15 business days" + +### 3. Missing Mandatory Registration Fields + +Common rejections: + +| Field | Common mistake | Fix | +|-------|---------------|-----| +| Opt-in description | "Users can opt in on our website" | "Users check 'I agree to receive SMS' checkbox at checkout.acme.com/register" | +| Message samples | Generic ("We send notifications") | Exact examples matching use case ("Your order #12345 has shipped") | +| Business name | Marketing name instead of legal name | Must match EIN records exactly | +| Website URL | Localhost, staging URL, or 404 page | Live, publicly accessible production URL | + +### 4. Storing Credentials Insecurely + +DON'T: Store credentials in plain text. + +Problem: Credential leaks expose customer accounts. + +DO: Encrypt credentials at rest using strong encryption (e.g., Fernet). Store encrypted values and decrypt only when needed for API calls. + +See `twilio-iam-auth-setup` for credential security best practices. + +--- + +## Constraints + +- A2P campaign registration is per customer use case in their own subaccount - cannot be shared across tenants +- Campaign approval takes 10-15 business days - factor into onboarding timeline +- Each campaign supports only one use case; customers with multiple use cases need to use a "Mixed" use case or multiple campaigns +- Trial accounts cannot complete A2P registration - must upgrade first + +--- + +## Next Steps + +- A2P registration details: `twilio-compliance-onboarding` +- Messaging Service configuration: `twilio-messaging-services` +- Send SMS patterns: `twilio-sms-send-message` +- Credential security: `twilio-iam-auth-setup` +- Subaccount architecture: `twilio-account-setup` +- General compliance guidance: `twilio-compliance-traffic` diff --git a/plugins/twilio/skills/twilio-sms-isv-setup/assets/icon-large.png b/plugins/twilio/skills/twilio-sms-isv-setup/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-sms-isv-setup/assets/icon-small.png b/plugins/twilio/skills/twilio-sms-isv-setup/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 zcmeHKg;&&3xBU$vAtfLTNOwpoEg;McDcvP7G)N2~rL=S-AvH8gNOwpJjC2Y}Nr=SI z15!`ld+YrXZ+&;&efK_R-8}2A8&R6-io}Gpga7~#D=R^@|FXtE;A8(SbAH>`0{|h1 z0|cV!VC4(|KG`{G-nzPTR1sqx-hTNbf}UW%D(!n@JM={cT439I;9;PbX#PG651AIU zh&u!amk3M@gTpBAgS0^Y&qIR9L$zLgv{sl^dE@Jg-rU4|m0nq}LwjJp`>!_OVC|Fu9rw}w_`49?>=~@1V$-Q7vxhBF%2=%osz?_XCsdw4!d+Ve zD;U9)lG?LTxAS?&f#$_BEkK37HWFT6n1sO^MVlHLMH>Z$?g}*TyiQzImKI@tG^Z_g zWR+;rMf1zzIZ#&y#f&Y6vGIZskZc4o1)@o_W|;vt*jp#Yi*=c&N9}9`I5e+IN+cvC zI7~@UP}A|1Gg9F|Kp?|K4*2IT|(OksWjt!WpAEZ)_UlV7IGfsX64L{}+P+s14vH=NJoL_pfux!>k(NcQL0 zScy2gd4O&($2j&t7uG!BzlyUIOo|WWGZ9u`Jx;`#1Q<7h&MfW^;=QvFo5$mkBmRgN zmxr4T(62si2KYmFSMPzbFx^}kz*yH~AL1=${2qDhNG$H>j)|hy_(so(>}fVgF6Fv4 zDJk*T<)10xS4K3-ztbepC76+q8*dut{)OfFkT&c<-fH~mDeozRf(7}DcX}4|^OUw- zqtya*gjHb_YcG8GvhmO%s5Qw8@Dho6P*pGFkn5T#7Xrvb?IR`Ov4UgCWBnUq3saMzk}1;#MUbM=J#km*5z`DBGUU&_mA*2FZ6hQS6R$tB&?g{Nl?5XYHob$y;>hv0|K69H8XW*wc2zTnq z>HpEEw!U?w?c>}|F9VDXRCQDT+&wb80A1q*&`tN#9AcNmbzw6_D2~iYd4oW*Z8} zuw&L^6N2clsIVp+6L<8*B#ExRqP$5woRaz_dEHW+5(0){0 zR-~>Rr!=ChQ#4YHT{Kk`QF zi{m4>jDIF8^95f~0A#;(ba;5scsLyOIju5TnpH{#VlVmB)tIL;<8_GW3x9!9mhH!1 zlNPz>SiE%(;@XqjQ=TbyKJU~EP`d5FGV3;DhFK=aNAy5o?+b$AiSYP!xqj8?Cn~z^ z{_JVm!IK2V{=?O^!S2<^lAFE5Z-+g2%6M{lXm}p-Q0v+1I+UrG4V4v_+3A(ea5sINZ4Ulu=Gx$@3Gxjq+AC$a)KTthLp*v?CfkeysW}6C+MMdEIus(-EW|Ri1$x`D;nP zCiaAO9X4A=Pui&$P;*WRbCFHaVyLzW|JkntcA**x0z+bbO_P`!WH94O6^ij3bF{L1 zXVG%*zI+@Hkgd{Ji>+Gbxmeh)V&pRNy7J2MIJ>vHcSHRlT+B7hYRpv-Gx-<@I%4M? zL2qj>d#_c`quv#0GweMyRv}ZtC9<8N7~CKCJ9a=JLQxDlR_SJ5K`N~(cVAX@2B7}b5RX1`6qA4SQ|z_ts-=B9x`9iT?c7=2q|0;C>tV>oaN-YR09w?<0%S?OzdN6XZL|4eUStZ!nGNldAVs z%=cq{csG2jt+17~{Ni?J-(v1-_fEh6AeuB)ITfPEuU9!UJR?=ULTAC)>^r_=p&F;6 zW;t>NS%P>wN;@s?dj1|Ju_Cc!4A5qLxpF1)b9dlge|S%Lf})S2Lc%@9eTRC7SFf~P zcwEA>-i=Isg~$K67}(DmrTUq+pl|H7hHx|Wx!!3)tdJlNr6=AdBWgFL_R(dFT+X=r znpc_U-{XtoCq_SxcI(KLwzloHEp&=q-VQpPP3hTLk5MAovqz-b++6lsT25%T?fBVV zg3-)KRxwopF>7Px{o8Q#TwP2PWM(~zaEtpAr#UUzxww|4x%anIEGFAlG z?=~H{GFH?4YPa++baLBS{hk#sCa~n*=5^VK+24hrBJM@>hNENqVq=#rS3b06`C3CoPvc5?bJd@rd_-ka?&?m*X2#BP12HjE0&nY*>L>&*1gHY|{5~BX5Avc6 zes~0XB)lx`$PLsv)L+}5@Q(HFK(j7ujBdZ_3gEi?cvrKVf-c3o?{NSA7Z4`w-O`j(wLyRB&{JDo2LN#C<($)7r?-wT|7_2; zXCh#X_wEaSoH@L{IhwB!A;M;T)v|Yc(BaO7leoMtg(sNtdp}Q%Hh5zvnn`VNT>oMC zNfJ+e=ac6X-&H~CtHa3wZ_74KPGfbr$w)agBKs{B$c?3cyklEZ{+7d!^&Tx$K862+ z+P{*y;3A$RK_$Suf|oiOw&td>qMP2%axqEt>kNdfSv-?@3|f@TbH^DlAh zhQ}-U6kqLXM5_$fc(om-!p^afAzmw|C9c8|Ihkw zb{%4E1%P|U%1}97!`>Xj9SDUY zX%hg7^gBDSnrlLKgci;Y$?fe;u6PAy;RN|5;0lg+j4_dF2=H|lE!TE`mT}KwmRpnB zz?LnZ-Jq5HCd)KV1tq)0!BuV8N*C@s=+(@R%md57i)I&SMf_s(o?wN=5jRDbm^?_* zn%&1Q_=QlBb33JFw1PPV5it4^!V2g5<^@$1aU!n_9kJ>5Gn9|EKp+mz`LGg`C>>o} zKu*LfQ|L>I&!1Hq<=flaCsuZGs;{5gRwFBcDL0|>H_PvLJhndyw9rjd_cgzy+VMKF#XXV}^xw@s#Kw7AXgUq0IL8Xv!d8 z32U~;CZ9#O#Wis^wjL~vB98!OKBNU#BzHaKCnjS#mzAbS3%oOxc7ESVLcl55+;lzc zt7Q@XVO0lfbwc@^wegrImzhcUkk9B>bzqZ!zBGE+D}+$-*Pp12>2Mx({5;yJS%g)7 z_3*s{wF?38cZG`r!#gbxhIS$%?aF2FR zuD;_6JWzI%&9fFW9nP25=&rD{`0drzJ27&Zye1|}OaDDFFeyozMKrV`?;VIXRA_-2 z0)$%Q6tF7j*<;jfoUSpQ$O*Li6E5$EpLzDLVK@`v&BiB8hBORW!@ZPX<}-9NIm&Nb!`x zxhF*xjplgMrmGWpL4XkAG#bKKzsB1=F2h*X0454I-UjkZ*|RQbz4Y1da( zDqe==`D`bO*ic&=yiFt@5D_Km^0_R!)JmDq9VMb`$L&Ff`oKE2?x5j zW<+SXv!GWP!S5gX+r$sR{HHnRT3O9o>pun&AE{Edq*NAL0-nj43_R2nkB+wEeAn*L z!NA_RDE?x4$h;Vp@W`j7@Fd$xCfHOYW?}fIQ-Z8Ka=%Hu)hhiKTgipBYgNi{CMkRC zmjRb&qMV^W&ise5FgbljTgJpCF2Q-XvwW(DqcQH{N$y(#V!(v<#)@Mrh5Ny0WT}E} z$7^)k4guy%V4^~8!F$yb&3*qtbiSkSCCoju)GogMIJA9$$StckGEeh?@pS+-h!%~P z;uA8;slqGJ;3Jc@w|&la=O{mr;Ng2--oF=d-L{e$2*LPrsr{kucVjuDz+8niTr7ra zr$ow5m_5D~+~YxA)?f?N)^0{hoCu{c8s0XVd|Pp9!lzC9f^ep5pr{hics!e~v!NS3 zlV0lcwtM)_rlI%`CYEiYt?sNUrtqv@r&z2da5U|=$Br@sUvfWcscV9PTC4h=#4&e_ zT|uDr?YBb2jTKM}{vj*4-=G#7x5l6`s~c#7Iu&aOXXOseztNB7kvC~SylvZ*=zSD) z52uEogmV5Pd6DfFcI}4`nWWZQ-=6A4*_(Ksfclv17OwDf%-{3UcN9!6ZuzfMcvo~BqKbas#zHsLb z4P7pJ)Zsd4!Z7UPs#JI@Tuk^ex5>d5#a!s3A}W0+n5y&g%_nzl2ISD`RZRSyY2>VZ zB}vQ5>EZq*Cq~pO&6BZQ(AIu2U(m{4^TsT%!OINkcy0z0YiO_Df2+2*Z*|%InRRmU zUOlB+YC{*b^x%VQ8Kde!zmlmBx^@Eim8HAIc~Ml9M`*u_g3X&LmJ4E9sFnMe;iNIm z?SXl4)zdwita7vPOu3W20RPz%3?h_%Q)cE;7*9ChJ%tJScFwS@_F-J``({4Rx88E1 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + SMS and MMS deep-dive reference. Covers SMS-specific error codes, + message filtering troubleshooting ("Messages Being Filtered or Blocked?" + diagnostic checklist), MMS media support (US/CA/AU only), and SMS pumping + indicators. For sending SMS, use twilio-send-message instead. Use this + skill only when debugging SMS delivery issues or needing SMS-specific + details not in the consolidated send skill. +--- + +## Overview + +SMS is one channel in Twilio's Messaging platform. All channels - SMS, WhatsApp, RCS, Facebook Messenger - share the same `messages.create()` API. See `twilio-messaging-overview` for the full channel comparison and onboarding sequence. + +| When to use SMS | When to consider alternatives | +|----------------|------------------------------| +| Reach any phone number globally | Need rich media outside US/CA/AU -> WhatsApp | +| No app install required | Opted-in audience prefers chat apps -> WhatsApp | +| Time-sensitive alerts (OTP, outage) | Marketing campaigns -> `twilio-marketing-promotions-advisor` | +| Regulatory/compliance requires SMS | Cost-sensitive high-volume -> WhatsApp (lower per-msg cost in many markets) | + +For production SMS: Use a Messaging Service (`messagingServiceSid`) instead of a raw `from` number. It enables sender pool management, compliance toolkit, SMS pumping protection, link shortening, and message scheduling. See `twilio-messaging-services`. + +Every outbound SMS requires a `from` Twilio number (or `messagingServiceSid`) and a `to` recipient - both in E.164 format. + +--- + +## Prerequisites + +- Twilio account with an SMS-capable phone number + - New to Twilio? See `twilio-account-setup` for signup, getting a number, and trial limitations +- Environment variables: + - `TWILIO_ACCOUNT_SID` + - `TWILIO_AUTH_TOKEN` + - See `twilio-iam-auth-setup` for credential setup and best practices +- SDK: `pip install twilio` / `npm install twilio` + +--- + +## Quickstart + +Python +```python +import os +from twilio.rest import Client + +client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"]) + +message = client.messages.create( + from_="+15017122661", # Your Twilio number (E.164) + to="+15558675310", # Recipient (E.164) + body="Your appointment is confirmed for tomorrow at 2pm." +) + +print(message.sid) # SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +print(message.status) # queued | sent | delivered | failed +``` + +Node.js +```node +const twilio = require("twilio"); +const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); + +const message = await client.messages.create({ + from: "+15017122661", + to: "+15558675310", + body: "Your appointment is confirmed for tomorrow at 2pm.", +}); + +console.log(message.sid); +console.log(message.status); +``` + +--- + +## Key Patterns + +### Send MMS (with media) + +Python +```python +message = client.messages.create( + from_="+15017122661", + to="+15558675310", + body="Here is your invoice.", + media_url=["https://example.com/invoice.pdf"] +) +``` + +Node.js +```node +const message = await client.messages.create({ + from: "+15017122661", + to: "+15558675310", + body: "Here is your invoice.", + mediaUrl: ["https://example.com/invoice.pdf"], +}); +``` + +Supported media types: images (JPEG, PNG, GIF), PDF, audio, video. Max 5 MB per message. + +### Send via Messaging Service (recommended for scale) + +Use `messagingServiceSid` instead of `from` - Twilio picks the best sender automatically from your pool. + +Python +```python +message = client.messages.create( + messaging_service_sid="MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + to="+15558675310", + body="Your order has shipped." +) +``` + +Node.js +```node +const message = await client.messages.create({ + messagingServiceSid: "MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + to: "+15558675310", + body: "Your order has shipped.", +}); +``` + +### Track Delivery Status + +Python +```python +message = client.messages.create( + from_="+15017122661", + to="+15558675310", + body="Hello!", + status_callback="https://yourapp.com/sms-status" +) +``` + +Node.js +```node +const message = await client.messages.create({ + from: "+15017122661", + to: "+15558675310", + body: "Hello!", + statusCallback: "https://yourapp.com/sms-status", +}); +``` + +Twilio POSTs to your URL at each transition: `queued -> sent -> delivered` (or `failed`/`undelivered`). + +--- + +## Response Fields + +| Field | Description | +|-------|-------------| +| `sid` | Message identifier (`SM...`) | +| `status` | `queued`, `sent`, `delivered`, `undelivered`, `failed` | +| `error_code` | Populated on failure | +| `error_message` | Human-readable description | +| `price` | Cost (populated after delivery) | +| `date_sent` | UTC timestamp | + +--- + +## Common Errors + +| Code | Meaning | Fix | +|------|---------|-----| +| 21211 | Invalid `to` number | Validate E.164 format | +| 21408 | Permission to send to region not enabled | Enable geo-permissions in Console | +| 21610 | Number is on blocklist (opted out) | Do not retry; respect opt-out | +| 30003 | Unreachable destination | Carrier cannot deliver; try later | +| 30007 | Message filtered as spam | Review content and sender reputation | +| 30034 | Message from unregistered number | Complete A2P 10DLC registration - see `twilio-compliance-onboarding` | +| 30450 | SMS pumping detected | Message blocked by SMS pumping protection - see `twilio-messaging-services` | + +### Messages Being Filtered or Blocked? + +If your messages aren't being delivered, check these causes in order: + +1. Unregistered sender (error 30034) - US 10DLC numbers must be registered. See `twilio-compliance-onboarding` +2. Spam filtered (error 30007) - Carrier flagged content. Check: opt-out language included? URL shorteners avoided? Content matches registered campaign? +3. Opted-out recipient (error 21610) - Recipient sent STOP. Do not retry. See `twilio-compliance-traffic` +4. Geo-permissions disabled (error 21408) - Enable the destination country in Console > Messaging > Settings > Geo Permissions. See `twilio-security-hardening` +5. SMS pumping (error 30450) - Artificial traffic detected. Whitelist known prefixes via Global Safe List. See `twilio-messaging-services` +6. Account suspended - Check Console for account status notifications. See `twilio-account-setup` + +For delivery event tracking, set up StatusCallbacks or use `twilio-debugging-observability`. + +--- + +## CANNOT + +- Cannot send without E.164 format - Both `from` and `to` must be `+` followed by country code and number +- Cannot send to unverified numbers on trial accounts - Upgrade to paid or verify recipient numbers first +- Cannot send MMS outside US, Canada, and Australia - MMS is only supported on US/CA/AU numbers; for international rich media use WhatsApp +- Cannot exceed 1,600 characters per message - Longer messages are automatically split into segments (each billed separately) +- Cannot prevent SMS pumping without a Messaging Service - Enable SMS pumping protection via Messaging Services to prevent artificial traffic inflation. See `twilio-messaging-services` + +--- + +## Next Steps + +- Channel overview and onboarding guide: `twilio-messaging-overview` +- Receive inbound SMS and delivery status: `twilio-messaging-webhooks` +- Manage sender pools at scale: `twilio-messaging-services` +- US compliance for A2P traffic: `twilio-compliance-onboarding` +- Send via WhatsApp instead: `twilio-whatsapp-send-message` diff --git a/plugins/twilio/skills/twilio-sms-send-message/assets/icon-large.png b/plugins/twilio/skills/twilio-sms-send-message/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 zcmaKScT`i)^LFSEdRKu+=tvb1f`kr|NEHx4iXbhNPz6GhVnB*O=q*N?A_~$$sx+k< z1eD%;lP(}4{w|;MegA#`$jLc#_wLTl%-wmOoryIv(4hxk1A{;ydL%;27z83Q0Dk(^ zlz_yuJGvYAqwzvm_<}%^{Fgrx%xhJD5QrCq)KWLa;D5fH@t*1#j-FHY+xbj4U_#e+ zd#qVnp45$Bny0;wS)4m!gIZ6!!kC<@asF2vNrj3GnG9(d=LpU&Ck#ZnzSI84w6f?& zoKY}!^GKNN;nd0QoI1kYKXrq;t?Ycw+(vLBwtj~6qoVHwd-zQ9t52p+TT}m0hR>i{ zLKF}FU~3i?z``ya9x^=qF$)smL+xX}i^ZR-LnO58KvC&(*bXGon7M~ewQK(+LHlI| zMTI)k2z@vw_=X}?RBAIHU!YhyoqtPrt2q?&mO|uB5owff+qTe%F|RCna!8>|5-Wij zTXUr>GwOq=7A<%!9LCf~*Lf^tuMXwmWg}vf`}`^A4O)i_w+X`J&`|EOOfG8^!~-K3 ztL8!4Yopc&x1vj#w4St|qaFgXJiZKBkmvmbXz@5HhFn{YMg(t=EFD4__gY7!fSB)f z+g>4?$kePGz2H(oTA{`?WdFV(MqLEYfHOLWv^O~j!mSBR65EDAtEn8<6s$z#vMJ6y*b&`ABmMk5y`ad<3!38-CVNLVlQ8Bp3|x1^I$e|BPV4cgSU_GF4MxMr!PN zUdLGMGOdv`Qo%?q7~3&%bHFZzFhSO%v$bM1g-gr#^2gS&OQ^LA+;(Rsf0FO6lVSwp z=XdK_v$bTdY?qA;=a`HxB9D^gYwDGw;u?$92Q++Ah(>^cVQn3Z-W`y9Oqv_lA`jS^ zRv<}{D&PiR>o{rF%@9w9sA_0a?-3;bA+Z9FSs`gtp32yPdo&QH;g5l5zCZ#5oN{15$ zUTYX+HG&zorK>v3OP>T%g4dXV*yD*rRXsL{CO*J^2@HD0E`n$OXqAF1EM;moggArR2h5F~Fw+Nn6rSyB-PDGO4taQN8QDB22FgC0>2P{@@28(Orcxb&$}DBx0(^AbR% zM01jlTt0(qwoxVr*%a%JEW=pkNDN>Ulyr}g^+C2&9*Si+jSKXT%XQY*tHYw#*@qUU zF)7>Ayq2fo_konJNY)Hgm46h>@MS5@BDnyGP)L)|t)GlOQa^wQ$E#tF`XKFxdW{w; zE=j4t)7@!a3?X1L`U$eCqqk2H>N*WNRa%J)PRS2){4Joc@y#(wI*?@H5EP!!3eJsRbn+V0F4j%eB?RSFD-G>D2YQ=glHQ5B+Lv zX5F}$Q;V=Bi5sEh+SR&dr%;@f8F`f?-&=cf^%jcHHExhQTPu1~G3EmN_!Pt6Z_qlC zrA+WsQnh5CR|LK0tC6^ap0!f}egcwOSX=gnCGgd0EoubPFb^w{-*imA$$*S04qhli z-N`??!C7)LcS}3bxR(i=Hj7f4jxTmwNVBQ@RQuDbk7EIY`;4ona*I5onv39@q#Rq^ zFoRhrdv-xoyHW^_>U*C(i^ebxaKP|x?D2`*p=y!6Kt!av}+JKh`_VIgdDhv&L9uy@5Yqqb} z1+8L>xfYhAc{nYgm`qwnr5sAQyjGhw2Vdw9Y@&lpVI5y1>PsQplUh*^a?K5foitC@YG0R|;CW z+Sc;iK{56@o2IX{HpLaRiHtD5G>?qh?sv;)uIQuZTGqbOA^*y6t+g*^Yx>ez%Dn=; z5Y2I@v1xrV+E3RejO~5$mTl1}yFJZoM`$FNu3p)maXx7%D6rc~sDzKA-4gr#D^r8| z`1MgbCyieboX-fL0g~= zd({0CcH!U1r)zswC2Bnq+%s$WRt3j2b58u>kVRLtIBVH6Til)ftNB6Y=YQgf!ZJA2b56c|V7@+AajMimgT zCSVOwqLZbO9Fk8!obZw(uJH)A#$#g{uiU|)!=|4?$44*(R#J10DlowMDrC<>Tln=G z_+p9%=v*WR2bw;j9zdMqW>;SnAdO#F!F}Jm-*s2%G2VTv#YvoTxUCr^hE?$%^G~_W zM+iW8>NdiUDGKZO1t*^#@hb9aP(~C@UX0f9%iJ5Q>^_F=CTE~Nh~zk+5Kn#~rxJU# z^snDJgs~pUpMbvDh^-szH>ZyQ$=@EN6qw%38}d{NWYc`n)TH)stIxZc!swXFj5bf~ z{K_Eb@7D08O1%nom4_xFdJ6+2=jzdE%{A-4cl{&v&Q8Ad z4yuBB%4pwCyUo@7jL~8%udekHT19)1Og|(dk)M4T{$`X5gHA@TpZWF>trd@Utq)0a z%v-B#$F3H>BNz*YI^CBGNZr6FWOGoX`6df!K)WR8ue7*6jO)Ld@Q?{5*`tu(nPCLk zX!Vr&Bt6Mlaz;xrbuLY}fB8C=F<51ouP9(z)jaYYY+z=lh!9ief3b3B3a2SB_dBVM zCmHo)Y~n7AwJ)?3IGxMtn4crAIWSy@4nv?%9*InOvpKUb0;ws-vTW6Mu_0q^C@A|@ zsPB}KvghxoVgohwu%d&!U?zQDOy`^crxSQTO@0rWqjshLlSK3mOZw}It36HfO2W4| z3|yX7;@sT&o!y{s;OkO*hj|Zv+alpF>qkN=(UHnn{wZ=c$TrNbUD7>t2_jV>!lqf%A@!rEt-xjC0Y?=DL9XF>R&2 zxvsR%j`}su#}&dCw5J0uHU(o;DmTmj;yf3TxoX^uyE-1t$=ZUos73GD_J8_;o+U%p z?}~S8f3D*KA2@&FhU(<78ui*|7AwflLoO6l2tPtKSr_EeIo;d(jLY+>?|yK|-NtQQHYxX^ zAHNdzTalOv_E@$!C%>l+AG$5J7AXT|%$RbZ2|HM~UL`u&-DESnpDd%z%~n(483UW= z2j8$GIrZ6YT;F6B*5@NKrNyEg!~HP!F3;GJVaj5;L(vbl3*JYWNNy??g;dmdM`Ft~ z4{&!?o*0Sr@jn`*J^2FGZm1)Xh?T3DgF&efd zv=E!zgbUtdO(a);uhy)hiPCzW)ytb)M_{Mq4@>NBtX?0`YP23+ub*Mcx{;o;lCo&B zw1zXvmPm`DEzC+nja@b`MfqXQqic~~4-e(Lub2t`Wh+w_>}_J4@RiDT;qp?uoM845 zE4?+FK|{>ieF;D>DhvVp_&{DGImZ5~^eqO>z1!vcF#S-GTQ^tG;Osh*=$^E>S73#V z4!`z?uUvR2rSKf8@WI(&_iJ;ORdES+XN)pvvUfd|*KHoKT8G>zXC{`m@Z5lNO;0F~ z)U{th`=8yc*2OUcgA#Fo4bybL?m0(|1dcRmd!{n}8EkeL%IK;*zZ$=^PyGpqY)*r5 z{O1pLZu?j@kUg4=ZulsoUW6@Y`iiAC`9!04K3YffAx-hv`KrcLIP0F>KX$M)Ts$%M zbI#X|Vt zLT#u|j?lvE23(p|*OqTK&!2^R3u#X^lN1HYuQNbPs=vwSK_@D&6AoZW7gc1<;iOBr!>r>{;3{JS&~+~hW;a^@CH^zG;KDQFRG5@ z7G_vJkEA$iWaCcLpn*mjOOk^A12+9R5eX!Omfm`yH&v{L9@S=%G-97+gW@t`HB`Wi z)$q_JI<{Fd`sSn6$VBjcdv=U)?HD$Wn{-IfgOjw8x}xe1#ht-KSdX!!LX5ezRnlIC zQYfg&_HsNtut!aVDWU%#jExxZcXIezxtKS{B&A5T4qVD*{SRv?K59b{(2V{{=3Fz_ z`My5SJz)3bYdXbWY$26-_2Gjp5c>tcMG-(RHM6HCzhK=hxkHd7Sv2wkI-Dg~D`Z?d zyd_S@vaSkQp@a|WT{_5p=j8;9ApoKFriWftt6cU?{qG=sx{@4zXq?UI%AKs=)6bsEZJnN(oo<%V?~?Bj7JU31@55SN z=I?lXOM>cBne0J`l{$>w`teyIJ7>Eh)z`{0k<(HJv|1o7*vmRTG;j__VIDK~IX&sM ze)JnfGhw11ww+9P&;mG3=;~hQ68HP;A*R9VA2)#{MR(y*;C5QXWHt-^T8V${Ki648 zW(qE@j|+-pq+oKYPP;%e7XGR_ajnPH%NC_8U93Nm>et2OQp?Og_X>LdHrP}1b{u@~ z>M+wF)qcT!ra^JdYm2{cMvNAk01=1kC^3;NGXY$|9vf5q4wWImM-Yt{nm2@Xhc9tE z_nCH$*e5TgQVfYR?{jy~Y1QbQ70{g0{+I(jjk^4n+yD(dYHXZ9t*%o(0DlfcEz3#W z%Z_P&BI!=zK*pqh^aIVzRi1xlizBqC+A2+lP&6YdJ)oK1{Ij;N6of&>S8)QRYIdti^jt$oz-$0(F z@4Ana+~G%zpf4Pnhq?f6PbgA<`nLJAUJK)9N-x~ z=O3>27aaJ>rXh<3f#}=RNRqV^z)Pil^-99TQou5-FD)PmiPr?aCM?cw=^l&*s>Kc# zhGM@1mp;_cTlror8VG$6JeJ9r_5UaCD)a(?2$M!My~1xq*0m;n^z!GK2Kt_7hl>lh+m(2@x;nQe`BLaUlU-JhtI~yfb2qoT$03Ilm348CWpi*|&Fo-JYcTKDg;sLi z>n-%!(b;;fG^QedfLnv@c?Hdrs1Z`uYI60QsqH7$@}q*uNnSEqtMP!>b|F!$g})9I z&|Eb}Yy+AQ&~)(A^>p$>Ts!SLfDFm^Y^D;go!glGs7*o$asqgi5QElUnNk%d~W4HbY>dET2{ru)Jfh;54x5EE?<7%NYeXi>T46 zc1X6Z4J}2hv0hMs3^E53YJnpRYd0DsXFqJ|Sy1Eo8b(55Cu1%!y0#$^6hiV;k|EGi zIn9NOiEJOg25_qZ`VIr#mwbe>%;T~*&D!Qdn~>srOAMzLFooOxazg1i#|XL3KFwJ_ zi+QI10m0%B<$m3wz`;*eU(gV%UFB%V3kkGsP$8@{> zp6@y~%Bn7eBCHI-3Sm4ROyrU`SPrr+E5G0LWJTTUtD%3}``*`romdd2^l%CNB}m)j z3>t_KJmJy9C`Kpmzy!wR^8{xJbNas%*0_b|J-@t5apTI+fHV8c&?iAbTW;#q#es3#0^m+(e!5DkGjrQND4a5ghgyKY76tsZ_ zU2!e|CaH!9&sQ_6jL6U51!OOVoPKc%=fCp430Z_=w&uD}EzKf$NT(ksM3`j~EXAjP z9mA0EdrEPMQ-Q*oxx03*`^O3M|&&f7&7&Q~~j z=KVG%zPTJ^@B1FdcKiJj+mJttO~bOpG#dt>qMkz2{uJgT#2{2f3Vf);)BQeW`OU?COQ+%hlmK=h6S2bAf z6Ahk|Siog@dyVMz<3}N<;kglt%PaEIxQZKa0-M3c+LL+@9&d$S!I=>`)44|%>;(z# zUl#rBbI&?N*}Q+QCjfkc6=QAnO;EoZYyVq}qx;gen#XG#WY#PO<$(t8wF?sJNGS8Y zzSxD`xL!N}>^BrB>c-_oUe0=tQ?9k1n^UT(^E|W;?T1FGau#5f+qQ#SR<`4WVf!vZr#3*61jpp(!!LPV z7c22Lr(B$Nx$Ky{S~7ezO5oQ%(^e31yidTLbi#L}y8gGv!5K1+-x9fW|7grH0OH)?D+TbrL@Vy+8&9aR6$F;N+SwA|9q zcG-Gr;Q($RLL`|T+#V0JKa!A`OTV?@S^E=(EDNp%cNWoJUT{YDXHZEc zEn63Am1v~8H&C#{RDR zm+@OlDd3Du$*MmB{OSBK%xY(dZsWy@##GFW%L+v6RMK4z{n_u|m)y^(67~%|K(eL~ z%pl=^nDLdw3rF_Y?>>&QkH2AV-mzcML#^m~h&oW&TtBru9SD3{dz7d2Dn{ z;TNC(#7kb|buo#-t6i%m($fVErsIn3R$yD{VY=(7%L7yyX2P29!j@IN` zD$r4~QKJHFwml5w`)s>E`Q4Ka!D>$X>Qc#-1GZr|}x0ma3@|I8GC+V@%pTRqwy{QPUucUQmP20>FMLA`ryFw$R$2*^d z+<~Ib$FCHVL3lMIkN(+jwOMo%x&X+%1An7x%m3(vd|q`{ixnuR0EeLV-m=7tl?UWf zBUPwXgr-~swT-V^5-Xx_i(JFI(`x)_OerIA=wHJRL(nRRX0K3BO8eCgQfzDY0ZDuR z^wD{M8&o&#Uol!o;9+pC8hnA9WSlqQE*I$ho^!mY{_-0_{uI4KJ@xPHhF{N{=`SIs zo%me9sk0e%^4E^>;od@lQ+FK;1d}!_V!4^99BygI2UR%b(GXP84BmB#@;cw?aGk9e zyJ2sU=xjNJxs!zfd)j9k*|5eeqe;D^WNWiaO4)Kxz=_b$MV5gHD}ITb*uhDMRNHh_12!_A8bv(?mtH5mUHt!pk zRAh!!jL#tvnTV#032>O^K^ABH4Lr;2Yx(&{4#$E5fdVVXF;9#=)XW8IVglR8A;P%T z2`S&~C>kUO^HptDXGNh_DC&anI zH>LY!DAAj2erR}N8{73*AzShrTB?GhT2gW{)(b4kaetN%LtryZp&O=8EkHf}`Hr(m zc7g5WwB-Gk*a64IpcNl^o7>dAyAoKz;3ru!+rvxbkz?7$!N$ExG-zsX-N=@l`#Vim zT|*^aak==1fSf8$c>nHVLBnQ{=lBO@gPuD3l3L+(BY3P53#WcwVWd$yG0G(C*HM66 z9)?AMPEGF9ab@ydsSeR7J}KulxrjyNfGlWex}X?x@HRID&)+_inz#_8BbXuykfgls z&?kSuKl<6Vt6MJ!EZc_yt2mU1-%!Y=@8SL|mKC&oJ>#*bbGk9Sq3-3|)V;X88^K50 zX}>t$Wzq>aP`!ASgB*TSWvAonHkdT@=bl1K5WdY;!=G1=?w^C--()_PIcgnEnuXnldq^i|EmsLG`5h87-14JA4iCN%M6dqz(PxkGoAg`vUjf z_+h9eD1dGT$+H}20m78K$59Ru?|43QeQU#KGxoBM2i&ZVvgoU&hpONj+i0Uja~L_a zSfv*MHWFU+k?Ys33NI}KJ{u>7c|%8&8uNrvUD@~hdKkQ5Re`wcvR8cWqNa+X{3XO&PlcXHk{vdP64plF#>XE zW24E~uRHLn$kqlH(f&cav5QRO!nY7jUBXClC?I)U`HK%DBmwCLs4?bm#h1Eeii!2YG8}w;;pw!sz@c`?Lz7iFcr-`>E}LG>Xrk?u7u9l zUZmw>V#casx8=pKKSYi+`1#^*?WpZmNlI$7dWqvfpQ{A{f~?Mr#umMBvfZf?^#J6O zfLxY^ZAc8qc5lO4hRrk4ZHSzO$sm03YPK^uvjqHT#4mPPd>7V!z(LeU(VT_dix;=S>_@}j5s=vu;z{_CaHlaX%cjk_uE_%&vr zWZvQOKIyV%{?@1^uC$5BMBId8tqijVxrRc$x1Nkio2`sA-L0Vu z9#J zm0@WeYG$2d+PnG+<{P#4ly4!&h2VX8lV4b!mhFT^jsa@Bw>&>ZodAk-Q=* z^V1Dr;GmHjk_yg?T>;u8qDiAnXuu*a$g3$*B3XYm7fkPvA3>&D6RoIxvncbtu0`C( z_(Vu_{M|>ie}CTLLbpNom1H_X>09czk1A^i2uq;zgikEliP{c+nAiC@se-Bl|64cL z2*$%}cTU$vWvz?ohVxSkb$TxWGN{cukGbC6Sc#6x??;k6>I+ZemNlFY9*gY8D@U5E z$OLS#f}KfwV)3%%o@soptZ|MSmmM`)M+P=@Ykm`?B5IX-fGs)@uvgF+6lI;luXeC1 zyq^9NF70jJClxWLzfQ?UB+-PTyTIxqkC?DNg|77XB07*Z$>*qO zujq4$EdCij>SbCj@r`>d;T=dxUYiYbo0St$vz=EY?#uVX`H)1l1%U)6bsi|@%~D_g zySF>c@a~!R2Vj8``l!?=kSN4dPmbBHb^p^E!U@ VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-sms-send-message/assets/icon-small.png b/plugins/twilio/skills/twilio-sms-send-message/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Route tasks to agents using Twilio TaskRouter. Covers Workers, Task + Queues, Workflows, Reservations, skills-based routing, and common + gotchas (hyphen attributes, HAS operator, reservation cascade). Use this + skill for any multi-agent contact center, support queue, or AI agent + escalation routing. +--- + +## Overview + +TaskRouter is Twilio's skills-based routing engine. Instead of building custom queuing logic, you define Workers (agents), Task Queues (groups), and Workflows (routing rules). TaskRouter matches incoming tasks to the best available worker. + +``` +Incoming Task -> Workflow (routing rules) -> Task Queue (skill match) -> Worker (agent) + ↓ + Reservation + (accept/reject) +``` + +Common mistake: Developers reinvent TaskRouter in custom Node.js - don't. If you're building skills-based routing, queue management, or agent assignment, use TaskRouter. + +--- + +## Prerequisites + +- Twilio account - see `twilio-account-setup` +- `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN` - see `twilio-iam-auth-setup` +- SDK: `pip install twilio` / `npm install twilio` +- For voice routing: a Twilio phone number with webhook configured - see `twilio-voice-twiml` +- For AI escalation: ConversationRelay with escalation tools - see `twilio-voice-conversation-relay` + +--- + +## Quickstart + +Step 1 - Create a Workspace + +A Workspace is the top-level container for all TaskRouter resources. + +Python +```python +import os +from twilio.rest import Client + +client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"]) + +workspace = client.taskrouter.v1.workspaces.create( + friendly_name="Support Center", + event_callback_url="https://yourapp.com/taskrouter-events" +) + +workspace_sid = workspace.sid # WSxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +print(workspace_sid) +``` + +Node.js +```node +const twilio = require("twilio"); +const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); + +const workspace = await client.taskrouter.v1.workspaces.create({ + friendlyName: "Support Center", + eventCallbackUrl: "https://yourapp.com/taskrouter-events", +}); + +const workspaceSid = workspace.sid; +``` + +Step 2 - Create Activities (agent states) + +Python +```python +# Available - worker can receive tasks +available = client.taskrouter.v1.workspaces(workspace_sid).activities.create( + friendly_name="Available", available=True +) + +# Offline - worker cannot receive tasks +offline = client.taskrouter.v1.workspaces(workspace_sid).activities.create( + friendly_name="Offline", available=False +) + +# On a task - worker is busy +on_task = client.taskrouter.v1.workspaces(workspace_sid).activities.create( + friendly_name="On Task", available=False +) +``` + +Step 3 - Create Workers (agents) + +> Security: Always use `json.dumps()` (Python) or `JSON.stringify()` (Node.js) to construct attribute payloads. String interpolation is vulnerable to JSON injection. + +Python +```python +worker = client.taskrouter.v1.workspaces(workspace_sid).workers.create( + friendly_name="Alice", + attributes='{"skills": ["billing", "technical"], "languages": ["en", "es"], "department": "support"}' +) +``` + +Node.js +```node +const worker = await client.taskrouter.v1.workspaces(workspaceSid).workers.create({ + friendlyName: "Alice", + attributes: JSON.stringify({ + skills: ["billing", "technical"], + languages: ["en", "es"], + department: "support", + }), +}); +``` + +Step 4 - Create Task Queues + +Python +```python +# Billing queue - matches workers with "billing" skill +billing_queue = client.taskrouter.v1.workspaces(workspace_sid).task_queues.create( + friendly_name="Billing", + target_workers='skills HAS "billing"' +) + +# Technical queue +tech_queue = client.taskrouter.v1.workspaces(workspace_sid).task_queues.create( + friendly_name="Technical", + target_workers='skills HAS "technical"' +) + +# Catch-all queue +default_queue = client.taskrouter.v1.workspaces(workspace_sid).task_queues.create( + friendly_name="Default", + target_workers='1==1' # matches all workers +) +``` + +Step 5 - Create a Workflow (routing rules) + +Python +```python +import json + +workflow_config = { + "task_routing": { + "filters": [ + { + "filter_friendly_name": "Billing", + "expression": "department == 'billing'", + "targets": [ + {"queue": billing_queue.sid, "timeout": 120} + ] + }, + { + "filter_friendly_name": "Technical", + "expression": "department == 'technical'", + "targets": [ + {"queue": tech_queue.sid, "timeout": 120} + ] + } + ], + "default_filter": { + "queue": default_queue.sid + } + } +} + +workflow = client.taskrouter.v1.workspaces(workspace_sid).workflows.create( + friendly_name="Support Routing", + configuration=json.dumps(workflow_config), + assignment_callback_url="https://yourapp.com/assignment" +) +``` + +Step 6 - Create a Task (from an incoming call) + +Python +```python +task = client.taskrouter.v1.workspaces(workspace_sid).tasks.create( + attributes='{"department": "billing", "caller": "+15558675310", "priority": 1}', + workflow_sid=workflow.sid +) +``` + +Step 7 - Handle the Assignment Callback + +When TaskRouter finds a matching worker, it POSTs to your `assignment_callback_url`: + +Python (Flask) +```python +@app.route("/assignment", methods=["POST"]) +def assignment(): + task_sid = request.form["TaskSid"] + worker_sid = request.form["WorkerSid"] + reservation_sid = request.form["ReservationSid"] + + # Option A: Dequeue to the worker's phone + return jsonify({ + "instruction": "dequeue", + "from": "+15551234567", # your Twilio number + "post_work_activity_sid": available_activity_sid + }) + + # Option B: Conference the caller and agent + # return jsonify({ + # "instruction": "conference", + # "from": "+15551234567", + # "post_work_activity_sid": available_activity_sid + # }) +``` + +Node.js (Express) +```node +app.post("/assignment", (req, res) => { + res.json({ + instruction: "dequeue", + from: "+15551234567", + post_work_activity_sid: availableActivitySid, + }); +}); +``` + +--- + +## Key Patterns + +### Skills-Based Routing + +Match tasks to workers based on attributes: + +| Worker expression | Matches | +|-------------------|---------| +| `skills HAS "billing"` | Workers whose `skills` array contains "billing" | +| `languages HAS "es"` | Spanish-speaking workers | +| `department == "support"` | Workers in support department | +| `experience > 5` | Workers with 5+ years experience | +| `skills HAS "billing" AND languages HAS "es"` | Spanish-speaking billing agents | + +### Priority Routing + +Tasks with higher priority are assigned first: + +```python +# VIP customer - priority 10 (higher = first) +task = client.taskrouter.v1.workspaces(workspace_sid).tasks.create( + attributes='{"department": "billing", "priority": 10, "vip": true}', + workflow_sid=workflow.sid, + priority=10 +) +``` + +### AI Agent Escalation + +When an AI agent (via TAC) escalates to a human, create a TaskRouter task with the AI's context: + +```python +# From your escalation webhook handler +def handle_escalation(escalation_data): + task = client.taskrouter.v1.workspaces(workspace_sid).tasks.create( + attributes=json.dumps({ + "department": escalation_data["reason_code"], + "conversation_id": escalation_data["conversation_id"], + "profile_id": escalation_data["profile_id"], + "ai_summary": escalation_data["summary"], + "priority": 5 + }), + workflow_sid=workflow.sid + ) +``` + +The human agent receives the AI's conversation summary and customer profile. + +### Workflow with Timeout Escalation + +Route to specialized queue first, then overflow to general: + +```python +workflow_config = { + "task_routing": { + "filters": [ + { + "filter_friendly_name": "Billing Specialist First", + "expression": "department == 'billing'", + "targets": [ + {"queue": billing_queue.sid, "timeout": 60}, # Try billing queue for 60s + {"queue": default_queue.sid, "timeout": 120} # Overflow to general + ] + } + ], + "default_filter": { + "queue": default_queue.sid + } + } +} +``` + +### Worker Activity Management + +```python +# Set worker to available +client.taskrouter.v1.workspaces(workspace_sid) \ + .workers(worker_sid) \ + .update(activity_sid=available_activity_sid) + +# Get real-time worker statistics +stats = client.taskrouter.v1.workspaces(workspace_sid) \ + .workers \ + .statistics() \ + .fetch() + +print(f"Available: {stats.realtime['total_available_workers']}") +``` + +--- + +## Scale Guidance + +| Agents | Architecture | Notes | +|--------|-------------|-------| +| < 10 | Single workflow, one queue per skill | No Flex needed - agents use phone | +| 10-50 | Multi-queue workflows, skills-based routing | Flex recommended for desktop | +| 50+ | Multi-tier workflows, priority routing, real-time monitoring | Full Flex + supervisor tools | + +--- + +## Gotchas + +### 1. Hyphens in Attribute Names Break Silently + +```python +# WRONG - hyphens in attribute keys break workflow expressions +worker = client.taskrouter.v1.workspaces(workspace_sid).workers.create( + friendly_name="Alice", + attributes='{"skill-level": 5}' # hyphen breaks expression evaluation +) + +# RIGHT - use underscores or camelCase +worker = client.taskrouter.v1.workspaces(workspace_sid).workers.create( + friendly_name="Alice", + attributes='{"skill_level": 5}' +) +``` + +No error - the expression silently fails to match. + +### 2. HAS Operator on Non-Array Attributes + +```python +# WRONG - "billing" is a string, not an array. HAS silently matches nothing. +target_workers = 'department HAS "billing"' + +# RIGHT - use == for string attributes +target_workers = 'department == "billing"' + +# RIGHT - use HAS only for arrays +target_workers = 'skills HAS "billing"' # skills: ["billing", "technical"] +``` + +Tasks sit in queue forever with no error. + +### 3. Reservation Timeout Cascade + +When a reservation times out: +1. Worker moves to the timeout Activity (often "Offline") +2. Fewer workers available -> other reservations also time out +3. Positive feedback loop -> entire queue backs up + +Fix: Set the timeout Activity to a short-duration state, not "Offline". Or implement a reservation timeout handler that keeps the worker available: + +```python +@app.route("/taskrouter-events", methods=["POST"]) +def taskrouter_event(): + event_type = request.form["EventType"] + if event_type == "reservation.timeout": + worker_sid = request.form["WorkerSid"] + # Keep worker available instead of moving to offline + client.taskrouter.v1.workspaces(workspace_sid) \ + .workers(worker_sid) \ + .update(activity_sid=available_activity_sid) + return "", 200 +``` + +### 4. Activity Available Flag + +Updating an Activity's `available` flag returns 200 OK but may not change the value if workers are currently in that activity. Create new activities instead of modifying existing ones. + +--- + +## CANNOT + +- Hyphens in attribute names break expressions - `skill-level` is treated as subtraction (`skill` minus `level`). Error 20001. Always use underscores: `skill_level`. +- `HAS` on non-array silently matches nothing - `department HAS "billing"` on a string attribute is accepted at creation but never matches. Tasks sit in queue forever with no error. +- Expression validation is syntactic only - Queue creation validates parse but NOT worker matching. Semantically wrong expressions create successfully with zero matching workers. +- Activity `available` flag is silently immutable - Updating returns 200 OK but does not change the value. Must delete and recreate the Activity. +- `multiTaskEnabled` cannot be reverted to false - Once enabled on a Workspace, cannot be disabled. One-way door. +- Reservation timeout moves worker to timeout Activity - Worker automatically moved to Offline. Must manually set back. This cascades: fewer available workers -> more timeouts -> queue collapse. See Gotcha #3. +- Workflow target timeout auto-cancels tasks - When all targets exhaust timeouts, task is canceled. Always include a `default_filter` as catch-all. +- Worker `friendlyName` is case-insensitive unique - "alice" collides with "Alice". +- `workflowSid` is required for task creation - API does not auto-select a default Workflow. +- Cannot update task status and attributes in same request - Must be two separate API calls. +- Assignment callback must respond in 5 seconds - If both primary and fallback URLs fail, reservation is canceled. +- Tasks auto-cancel after 1,000 rejections - If a task cycles through 1,000 reservation rejections, it is automatically canceled. +- `page` query param not supported - Use `PageToken` for pagination. `page` returns error 40153. +- Cannot use malformed JSON in worker attributes - Silently breaks matching with no error +- Cannot use regex in workflow expressions - Only supports ==, !=, <, >, HAS, IN, CONTAINS, AND, OR, NOT +- Cannot exceed 50,000 Workers per Workspace - Hard limit +- Cannot exceed 250 Task Queues per Workspace - Hard limit +- Cannot delay reservation callback response beyond 15 seconds - Timeout results in reservation failure + +--- + +## Next Steps + +- Conference for transfers: `twilio-conference-calls` +- Call recording: `twilio-call-recordings` +- AI agent voice integration: `twilio-voice-conversation-relay` +- Voice IVR before routing: `twilio-voice-twiml` diff --git a/plugins/twilio/skills/twilio-taskrouter-routing/assets/icon-large.png b/plugins/twilio/skills/twilio-taskrouter-routing/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-taskrouter-routing/assets/icon-small.png b/plugins/twilio/skills/twilio-taskrouter-routing/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Send and verify one-time passcodes (OTPs) via Twilio Verify over SMS, RCS, + voice, email, or WhatsApp. Covers creating a Verify Service, sending tokens, + checking submitted codes, automatic WhatsApp-to-SMS fallback, and service + configuration. TOTP is supported via the Factors API (a separate family from + channel-based OTP). Use this skill to add phone or email verification or + two-factor authentication to any application. +--- + +## Overview + +Use Twilio Verify to manage the full OTP lifecycle: code generation, delivery, expiry, rate limiting, and Fraud Guard protection. Use the Programmable Messaging API to build your own OTP message infrastructure and access features such as SMS Pumping Protection. + +| | Twilio Verify | Programmable Messaging API | +|---|---|---| +| Code generation + expiry | Built-in (10min default, configurable). Also supports custom codes. | Build yourself | +| Rate limiting | Built-in (per-phone, per-service) | Build yourself | +| Fraud protection | Fraud Guard (geo-permissions, rate anomaly) | SMS Pumping Protection | +| A2P registration | Exempt - no 10DLC needed | Required - must register campaign | +| Multi-channel | One API, change `channel` param (SMS/Voice/Email/WhatsApp/RCS) | Separate integration per channel | +| Cost | [Per confirmed verification + channel fee](https://www.twilio.com/en-us/verify/pricing) | Per-message pricing + build cost | +| Delivery confirmation | Yes - via List Attempts or Events API | Yes (via StatusCallback) | + +When Programmable Messaging is justified: You need full control over message content, custom delivery logic, or SMS Pumping Protection features. For standard OTP/2FA flows, use Verify. + +Verify supports SMS, voice, email, WhatsApp, and RCS - only the `channel` parameter changes per delivery method. TOTP (authenticator apps) is supported via the Verify Factors API, a separate implementation from channel-based OTP. + +--- + +## Prerequisites + +- Twilio account (free trial works for testing) + - New to Twilio? See `twilio-account-setup` + - Verify requires no separate product activation - just create a Service below +- Environment variables: + - `TWILIO_ACCOUNT_SID` + - `TWILIO_AUTH_TOKEN` + - `VERIFY_SERVICE_SID` (created in Quickstart step 1) + - See `twilio-iam-auth-setup` for credential setup and best practices +- SDK: `pip install twilio` / `npm install twilio` +- For WhatsApp channel only: a registered production WhatsApp sender - see `twilio-whatsapp-manage-senders` + +--- + +## Quickstart + +Step 1 - Create a Verify Service (one-time) + +Confirm the target account and service name before creating a live Verify Service. + +Python +```python +import os +from twilio.rest import Client + +client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"]) + +service = client.verify.v2.services.create( + friendly_name="My App Verification" +) +print(service.sid) # VAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - save as VERIFY_SERVICE_SID +``` + +Node.js +```node +const twilio = require("twilio"); +const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); + +const service = await client.verify.v2.services.create({ + friendlyName: "My App Verification", +}); +console.log(service.sid); // VAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +``` + +Store the Service SID - reuse it for all verifications, do not recreate it each time. + +Step 2 - Send a verification token + +Confirm the recipient, channel, consent, environment, and expected cost before sending a live verification token. + +Python +```python +verification = client.verify.v2 \ + .services(os.environ["VERIFY_SERVICE_SID"]) \ + .verifications \ + .create(to="+15558675310", channel="sms") + +print(verification.status) # pending +``` + +Node.js +```node +const verification = await client.verify.v2 + .services(process.env.VERIFY_SERVICE_SID) + .verifications.create({ to: "+15558675310", channel: "sms" }); + +console.log(verification.status); // pending +``` + +Step 3 - Check the submitted code + +Python +```python +check = client.verify.v2 \ + .services(os.environ["VERIFY_SERVICE_SID"]) \ + .verification_checks \ + .create(to="+15558675310", code="123456") + +if check.status == "approved": + print("Verified!") +else: + print("Invalid or expired code") +``` + +Node.js +```node +const check = await client.verify.v2 + .services(process.env.VERIFY_SERVICE_SID) + .verificationChecks.create({ to: "+15558675310", code: "123456" }); + +if (check.status === "approved") { + console.log("Verified!"); +} else { + console.log("Invalid or expired code"); +} +``` + +--- + +## Key Patterns + +### Supported Channels + +| Channel | `channel` value | Notes | +|---------|----------------|-------| +| SMS | `sms` | Default, widest coverage | +| Voice call | `voice` | Reads code aloud | +| Email | `email` | Use email address in `to` | +| WhatsApp | `whatsapp` | Requires own WhatsApp sender (see below) | +| RCS | `rcs` | Rich messaging, Android devices | + +> TOTP (authenticator apps): Supported via the Verify Factors API - a separate implementation from channel-based OTP. See [Verify TOTP docs](https://www.twilio.com/docs/verify/quickstarts/totp). + +### WhatsApp OTP + +Change `channel` to `"whatsapp"` - the send/check flow is identical to SMS. + +> Requires: A registered production WhatsApp sender. As of March 2024, Twilio no longer provides a shared sender for Verify. See `twilio-whatsapp-manage-senders`. + +Python +```python +verification = client.verify.v2 \ + .services(os.environ["VERIFY_SERVICE_SID"]) \ + .verifications \ + .create(to="+15558675310", channel="whatsapp") +``` + +Node.js +```node +const verification = await client.verify.v2 + .services(process.env.VERIFY_SERVICE_SID) + .verifications.create({ to: "+15558675310", channel: "whatsapp" }); +``` + +### WhatsApp with Automatic SMS Fallback + +Python +```python +verification = client.verify.v2 \ + .services(os.environ["VERIFY_SERVICE_SID"]) \ + .verifications \ + .create( + to="+15558675310", + channel="whatsapp", + channel_configuration={ + "whatsapp": {"enabled": True}, + "sms": {"enabled": True} # falls back to SMS if WhatsApp undelivered + } + ) +``` + +Node.js +```node +const verification = await client.verify.v2 + .services(process.env.VERIFY_SERVICE_SID) + .verifications.create({ + to: "+15558675310", + channel: "whatsapp", + channelConfiguration: { + whatsapp: { enabled: true }, + sms: { enabled: true }, + }, + }); +``` + +With fallback enabled, your UI can say "a verification code was sent" without specifying the channel. + +### Service Configuration + +Python +```python +service = client.verify.v2.services.create( + friendly_name="My App", + code_length=6, # 4-10 digits (default: 6) + lookup_enabled=True, # Validate number before sending + do_force_check_once=True, # Code can only be checked once + ttl=600, # Code expiry in seconds (default: 600) +) +``` + +Node.js +```node +const service = await client.verify.v2.services.create({ + friendlyName: "My App", + codeLength: 6, + lookupEnabled: true, + doForceCheckOnce: true, + ttl: 600, +}); +``` + +### Verification Status Values + +| Status | Meaning | +|--------|---------| +| `approved` | Code is correct | +| `pending` | Code is wrong or not yet submitted | +| `expired` | Code has expired (default TTL: 10 minutes) | +| `canceled` | Verification was canceled | + +--- + +## Debugging + +Primary debugging tool: Console > Verify > Logs (per-Service). Shows every verification attempt, delivery status, channel used, and error codes. Check here first before writing custom monitoring code. + +### Common Errors + +| Code | Meaning | Fix | +|------|---------|-----| +| 60200 | Invalid parameter | Check `to` format and `channel` value | +| 60202 | Max send attempts reached | Wait before retrying | +| 60203 | Max check attempts reached | Issue a new verification | +| 60212 | Service not found | Verify `VERIFY_SERVICE_SID` is correct | +| 60410 | Geo-permission not enabled | Enable country in Console | + +Built-in protections (no custom code needed): +- Rate limiting: 5 verifications per phone per service per 10 minutes +- Max check attempts: 5 per verification (6th attempt -> error 60203) +- Phone number validation: Verify checks line type before sending (if `lookup_enabled=True`) +- Fraud Guard: geo-permissions, rate anomaly detection, SMS pumping protection + +International OTP traffic warning: International numbers are high-risk for SMS pumping - fraudsters trigger OTPs to premium-rate destinations to generate revenue. Verify's Fraud Guard handles this automatically when enabled. If you're building custom OTP with Programmable Messaging instead, enable SMS Pumping Protection on your Messaging Service (see `twilio-messaging-services`). Always restrict geo-permissions to only countries where you have real users. + +--- + +## CANNOT + +- No built-in channel fallback - Must implement retry logic manually (e.g., SMS -> voice -> email). Use `channel_configuration` for WhatsApp->SMS only. +- No webhook on verification completion - Must poll `verification_checks`. Rate-limited: 60/min, 180/hr, 250/day. +- Cannot retrieve the actual code sent - Code is never returned in any API response. By design. +- Cannot change channel mid-verification - Starting on a new channel reuses the same Verification SID and token. Create a new verification instead. +- Cannot extend TTL on an existing verification - Default 10 minutes. Customizable only at Service level, not per-verification. +- Verification SID deleted after approval - Fetching an approved verification returns 404. Canceled verifications remain fetchable. +- `auto` channel not universally available - Returns error 60200 on accounts without Fraud Guard enabled. +- Email channel requires Mailer configuration - `channel: 'email'` without a configured Mailer returns error 60217. +- No real-time delivery push notification - Delivery status is available via List Attempts or Events API (pull-based), not via a push webhook. +- FriendlyName rejects 5+ consecutive digits - Service names containing 5+ digits trigger error 60200. Use words or fewer digits. +- Wrong code does not throw an exception - Check returns `status: "pending"`, not an error. You must check `status === "approved"` explicitly. +- Cannot re-check an approved verification - Each verification is single-use. Once `approved`, subsequent checks return 404. +- Cannot send to arbitrary numbers on trial accounts - Trial accounts have limited verification destinations +- Cannot customize WhatsApp OTP template - Uses a fixed Meta authentication template +- Cannot use WhatsApp channel for PSD2 compliance mode - PSD2 payee/amount parameters not supported on WhatsApp + +--- + +## Next Steps + +- Register a WhatsApp sender: `twilio-whatsapp-manage-senders` +- Validate phone numbers before sending: `twilio-lookup-phone-intelligence` +- Credential setup: `twilio-iam-auth-setup` diff --git a/plugins/twilio/skills/twilio-verify-send-otp/assets/icon-large.png b/plugins/twilio/skills/twilio-verify-send-otp/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-verify-send-otp/assets/icon-small.png b/plugins/twilio/skills/twilio-verify-send-otp/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Build AI-powered voice agents using Twilio ConversationRelay. Handles + real-time speech recognition (ASR), text-to-speech (TTS), and bidirectional + audio streaming via WebSocket. Covers TwiML setup, WebSocket message types, + LLM integration, streaming responses, and voice provider configuration. Use + this skill to build voice bots, IVR replacements, or real-time AI voice + assistants on Twilio calls. +--- + +## Overview + +ConversationRelay connects Twilio's telephony layer to your app via a persistent WebSocket. Twilio handles ASR (speech-to-text) and TTS (text-to-speech); your app receives transcripts, calls an LLM, and sends text back for playback. + +``` +Caller ←-> Twilio (ASR/TTS) ←-> WebSocket ←-> Your App ←-> LLM +``` + +--- + +## Prerequisites + +- Upgraded Twilio account with ConversationRelay access (requires onboarding) + - New to Twilio? See `twilio-account-setup` + - Start onboarding at: [Console > Voice > ConversationRelay](https://console.twilio.com/us1/voice/conversation-relay) - access is not instant +- A voice-capable Twilio phone number +- `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN` - see `twilio-iam-auth-setup` +- WebSocket server reachable via `wss://` (TLS required) +- An LLM integration (OpenAI, Anthropic, etc.) +- For placing calls: see `twilio-voice-outbound-calls` + +Onboarding: Complete via Console > Voice > ConversationRelay > Onboarding. Select TTS/ASR providers: +- TTS: Deepgram, Amazon Polly, Google Cloud TTS, ElevenLabs +- ASR: Deepgram, Google Cloud STT + +--- + +## Quickstart + +Step 1 - Return TwiML pointing to your WebSocket server + +Python (Flask) +```python +from flask import Flask +from twilio.twiml.voice_response import VoiceResponse, Connect, ConversationRelay + +app = Flask(__name__) + +@app.route("/voice", methods=["POST"]) +def voice(): + response = VoiceResponse() + connect = Connect() + connect.conversation_relay( + url="wss://yourapp.com/ws/voice", + welcome_greeting="Hello! How can I help you today?" + ) + response.append(connect) + return str(response) +``` + +Node.js (Express) +```node +const { VoiceResponse } = require("twilio").twiml; + +app.post("/voice", (req, res) => { + const response = new VoiceResponse(); + const connect = response.connect(); + connect.conversationRelay({ + url: "wss://yourapp.com/ws/voice", + welcomeGreeting: "Hello! How can I help you today?", + }); + res.type("text/xml").send(response.toString()); +}); +``` + +Step 2 - Handle WebSocket events and respond with text + +Python (websockets) +```python +import asyncio, json, websockets + +async def handle_call(websocket): + async for message in websocket: + event = json.loads(message) + if event["type"] == "prompt": + ai_response = await call_llm(event["voicePrompt"]) + await websocket.send(json.dumps({"type": "text", "token": ai_response, "last": True})) + +async def main(): + async with websockets.serve(handle_call, "0.0.0.0", 8080): + await asyncio.Future() + +asyncio.run(main()) +``` + +Node.js (ws) +```node +const WebSocket = require("ws"); +const wss = new WebSocket.Server({ port: 8080 }); + +wss.on("connection", (ws) => { + ws.on("message", async (data) => { + const event = JSON.parse(data); + if (event.type === "prompt") { + const aiResponse = await callLLM(event.voicePrompt); + ws.send(JSON.stringify({ type: "text", token: aiResponse, last: true })); + } + }); +}); +``` + +> Security: The `voicePrompt` field contains ASR-transcribed caller speech - it is untrusted external input. When passing to an LLM, isolate it as user input within a structured system prompt. Implement topic boundaries and output filtering to prevent the LLM from disclosing system instructions or speaking inappropriate content. ConversationRelay is a pure transport layer with no built-in content safety - any LLM output is spoken to the caller verbatim. + +--- + +## Key Patterns + +### WebSocket Message Types + +Received from Twilio: + +| Type | When | Key fields | +|------|------|-----------| +| `connected` | WebSocket opened | `callSid`, `streamSid` | +| `prompt` | User finished speaking | `voicePrompt` (transcript) | +| `interrupt` | User interrupted TTS | - | +| `dtmf` | User pressed keypad key | `digit` | +| `error` | An error occurred | `description` | + +Sent to Twilio: + +| Type | Purpose | Key fields | +|------|---------|-----------| +| `text` | Send TTS response | `token` (text), `last` (bool) | +| `interrupt` | Stop current TTS | - | +| `end` | Hang up the call | `reason` | + +### Stream LLM Responses Token-by-Token + +Lower latency by streaming as the LLM generates output - Twilio starts speaking before the full response is ready. + +Python +```python +async for chunk in llm_stream: + await websocket.send(json.dumps({"type": "text", "token": chunk, "last": False})) +await websocket.send(json.dumps({"type": "text", "token": "", "last": True})) +``` + +Node.js +```node +for await (const chunk of llmStream) { + ws.send(JSON.stringify({ type: "text", token: chunk, last: false })); +} +ws.send(JSON.stringify({ type: "text", token: "", last: true })); +``` + +### Voice Configuration + +Python +```python +connect.conversation_relay( + url="wss://yourapp.com/ws/voice", + voice="en-US-Neural2-F", + language="en-US", + transcription_provider="deepgram", + speech_model="nova-2-phonecall", + interrupt_by_dtmf=True, +) +``` + +Node.js +```node +connect.conversationRelay({ + url: "wss://yourapp.com/ws/voice", + voice: "en-US-Neural2-F", + language: "en-US", + transcriptionProvider: "deepgram", + speechModel: "nova-2-phonecall", + interruptByDtmf: true, +}); +``` + +--- + +## CANNOT + +- No raw audio access - Text in, text out only. For raw audio, use `` (Media Streams). +- Cannot mix with Media Streams - `` and `` are mutually exclusive on the same call. No error - one is silently ignored. +- No custom STT/TTS engines - Limited to Deepgram + Google (STT) and Deepgram + Amazon Polly + Google + ElevenLabs (TTS). +- No mid-session voice/provider changes - Voice and provider are set at TwiML time. Only `language` can be switched mid-session via WebSocket message. +- No WebSocket auto-reconnection - If the WebSocket drops, the call disconnects. Implement recovery via `` URL. +- Voice only - No SMS/messaging support. For omnichannel, use Conversation Orchestrator. +- No built-in memory or context - BYO conversation history and context management. +- No LLM integration - Pure transport layer. You bring your own LLM via the WebSocket server. +- No server-side recording via REST API - `record:true` on the Calls API is silently ignored. Must use `` before `` in TwiML. +- Not PCI compliant with Voice Intelligence v2 - Do not enable `intelligenceService` in PCI workflows. +- ElevenLabs requires account enablement - Accounts without ElevenLabs access get error 64101. Voice IDs (not human-readable names) are required. +- Cannot use ConversationRelay without onboarding - Not available immediately on a new account +- Cannot use non-TLS WebSocket - Server must be reachable via `wss://` (TLS required) +- Cannot stream audio without `last: true` - Twilio won't play audio until it sees a `last: true` token in the stream + +--- + +## Next Steps + +- Place outbound calls: `twilio-voice-outbound-calls` +- Standard IVR without AI: `twilio-voice-twiml` diff --git a/plugins/twilio/skills/twilio-voice-conversation-relay/assets/icon-large.png b/plugins/twilio/skills/twilio-voice-conversation-relay/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 zcmaKScT`i)^LFSEdRKu+=tvb1f`kr|NEHx4iXbhNPz6GhVnB*O=q*N?A_~$$sx+k< z1eD%;lP(}4{w|;MegA#`$jLc#_wLTl%-wmOoryIv(4hxk1A{;ydL%;27z83Q0Dk(^ zlz_yuJGvYAqwzvm_<}%^{Fgrx%xhJD5QrCq)KWLa;D5fH@t*1#j-FHY+xbj4U_#e+ zd#qVnp45$Bny0;wS)4m!gIZ6!!kC<@asF2vNrj3GnG9(d=LpU&Ck#ZnzSI84w6f?& zoKY}!^GKNN;nd0QoI1kYKXrq;t?Ycw+(vLBwtj~6qoVHwd-zQ9t52p+TT}m0hR>i{ zLKF}FU~3i?z``ya9x^=qF$)smL+xX}i^ZR-LnO58KvC&(*bXGon7M~ewQK(+LHlI| zMTI)k2z@vw_=X}?RBAIHU!YhyoqtPrt2q?&mO|uB5owff+qTe%F|RCna!8>|5-Wij zTXUr>GwOq=7A<%!9LCf~*Lf^tuMXwmWg}vf`}`^A4O)i_w+X`J&`|EOOfG8^!~-K3 ztL8!4Yopc&x1vj#w4St|qaFgXJiZKBkmvmbXz@5HhFn{YMg(t=EFD4__gY7!fSB)f z+g>4?$kePGz2H(oTA{`?WdFV(MqLEYfHOLWv^O~j!mSBR65EDAtEn8<6s$z#vMJ6y*b&`ABmMk5y`ad<3!38-CVNLVlQ8Bp3|x1^I$e|BPV4cgSU_GF4MxMr!PN zUdLGMGOdv`Qo%?q7~3&%bHFZzFhSO%v$bM1g-gr#^2gS&OQ^LA+;(Rsf0FO6lVSwp z=XdK_v$bTdY?qA;=a`HxB9D^gYwDGw;u?$92Q++Ah(>^cVQn3Z-W`y9Oqv_lA`jS^ zRv<}{D&PiR>o{rF%@9w9sA_0a?-3;bA+Z9FSs`gtp32yPdo&QH;g5l5zCZ#5oN{15$ zUTYX+HG&zorK>v3OP>T%g4dXV*yD*rRXsL{CO*J^2@HD0E`n$OXqAF1EM;moggArR2h5F~Fw+Nn6rSyB-PDGO4taQN8QDB22FgC0>2P{@@28(Orcxb&$}DBx0(^AbR% zM01jlTt0(qwoxVr*%a%JEW=pkNDN>Ulyr}g^+C2&9*Si+jSKXT%XQY*tHYw#*@qUU zF)7>Ayq2fo_konJNY)Hgm46h>@MS5@BDnyGP)L)|t)GlOQa^wQ$E#tF`XKFxdW{w; zE=j4t)7@!a3?X1L`U$eCqqk2H>N*WNRa%J)PRS2){4Joc@y#(wI*?@H5EP!!3eJsRbn+V0F4j%eB?RSFD-G>D2YQ=glHQ5B+Lv zX5F}$Q;V=Bi5sEh+SR&dr%;@f8F`f?-&=cf^%jcHHExhQTPu1~G3EmN_!Pt6Z_qlC zrA+WsQnh5CR|LK0tC6^ap0!f}egcwOSX=gnCGgd0EoubPFb^w{-*imA$$*S04qhli z-N`??!C7)LcS}3bxR(i=Hj7f4jxTmwNVBQ@RQuDbk7EIY`;4ona*I5onv39@q#Rq^ zFoRhrdv-xoyHW^_>U*C(i^ebxaKP|x?D2`*p=y!6Kt!av}+JKh`_VIgdDhv&L9uy@5Yqqb} z1+8L>xfYhAc{nYgm`qwnr5sAQyjGhw2Vdw9Y@&lpVI5y1>PsQplUh*^a?K5foitC@YG0R|;CW z+Sc;iK{56@o2IX{HpLaRiHtD5G>?qh?sv;)uIQuZTGqbOA^*y6t+g*^Yx>ez%Dn=; z5Y2I@v1xrV+E3RejO~5$mTl1}yFJZoM`$FNu3p)maXx7%D6rc~sDzKA-4gr#D^r8| z`1MgbCyieboX-fL0g~= zd({0CcH!U1r)zswC2Bnq+%s$WRt3j2b58u>kVRLtIBVH6Til)ftNB6Y=YQgf!ZJA2b56c|V7@+AajMimgT zCSVOwqLZbO9Fk8!obZw(uJH)A#$#g{uiU|)!=|4?$44*(R#J10DlowMDrC<>Tln=G z_+p9%=v*WR2bw;j9zdMqW>;SnAdO#F!F}Jm-*s2%G2VTv#YvoTxUCr^hE?$%^G~_W zM+iW8>NdiUDGKZO1t*^#@hb9aP(~C@UX0f9%iJ5Q>^_F=CTE~Nh~zk+5Kn#~rxJU# z^snDJgs~pUpMbvDh^-szH>ZyQ$=@EN6qw%38}d{NWYc`n)TH)stIxZc!swXFj5bf~ z{K_Eb@7D08O1%nom4_xFdJ6+2=jzdE%{A-4cl{&v&Q8Ad z4yuBB%4pwCyUo@7jL~8%udekHT19)1Og|(dk)M4T{$`X5gHA@TpZWF>trd@Utq)0a z%v-B#$F3H>BNz*YI^CBGNZr6FWOGoX`6df!K)WR8ue7*6jO)Ld@Q?{5*`tu(nPCLk zX!Vr&Bt6Mlaz;xrbuLY}fB8C=F<51ouP9(z)jaYYY+z=lh!9ief3b3B3a2SB_dBVM zCmHo)Y~n7AwJ)?3IGxMtn4crAIWSy@4nv?%9*InOvpKUb0;ws-vTW6Mu_0q^C@A|@ zsPB}KvghxoVgohwu%d&!U?zQDOy`^crxSQTO@0rWqjshLlSK3mOZw}It36HfO2W4| z3|yX7;@sT&o!y{s;OkO*hj|Zv+alpF>qkN=(UHnn{wZ=c$TrNbUD7>t2_jV>!lqf%A@!rEt-xjC0Y?=DL9XF>R&2 zxvsR%j`}su#}&dCw5J0uHU(o;DmTmj;yf3TxoX^uyE-1t$=ZUos73GD_J8_;o+U%p z?}~S8f3D*KA2@&FhU(<78ui*|7AwflLoO6l2tPtKSr_EeIo;d(jLY+>?|yK|-NtQQHYxX^ zAHNdzTalOv_E@$!C%>l+AG$5J7AXT|%$RbZ2|HM~UL`u&-DESnpDd%z%~n(483UW= z2j8$GIrZ6YT;F6B*5@NKrNyEg!~HP!F3;GJVaj5;L(vbl3*JYWNNy??g;dmdM`Ft~ z4{&!?o*0Sr@jn`*J^2FGZm1)Xh?T3DgF&efd zv=E!zgbUtdO(a);uhy)hiPCzW)ytb)M_{Mq4@>NBtX?0`YP23+ub*Mcx{;o;lCo&B zw1zXvmPm`DEzC+nja@b`MfqXQqic~~4-e(Lub2t`Wh+w_>}_J4@RiDT;qp?uoM845 zE4?+FK|{>ieF;D>DhvVp_&{DGImZ5~^eqO>z1!vcF#S-GTQ^tG;Osh*=$^E>S73#V z4!`z?uUvR2rSKf8@WI(&_iJ;ORdES+XN)pvvUfd|*KHoKT8G>zXC{`m@Z5lNO;0F~ z)U{th`=8yc*2OUcgA#Fo4bybL?m0(|1dcRmd!{n}8EkeL%IK;*zZ$=^PyGpqY)*r5 z{O1pLZu?j@kUg4=ZulsoUW6@Y`iiAC`9!04K3YffAx-hv`KrcLIP0F>KX$M)Ts$%M zbI#X|Vt zLT#u|j?lvE23(p|*OqTK&!2^R3u#X^lN1HYuQNbPs=vwSK_@D&6AoZW7gc1<;iOBr!>r>{;3{JS&~+~hW;a^@CH^zG;KDQFRG5@ z7G_vJkEA$iWaCcLpn*mjOOk^A12+9R5eX!Omfm`yH&v{L9@S=%G-97+gW@t`HB`Wi z)$q_JI<{Fd`sSn6$VBjcdv=U)?HD$Wn{-IfgOjw8x}xe1#ht-KSdX!!LX5ezRnlIC zQYfg&_HsNtut!aVDWU%#jExxZcXIezxtKS{B&A5T4qVD*{SRv?K59b{(2V{{=3Fz_ z`My5SJz)3bYdXbWY$26-_2Gjp5c>tcMG-(RHM6HCzhK=hxkHd7Sv2wkI-Dg~D`Z?d zyd_S@vaSkQp@a|WT{_5p=j8;9ApoKFriWftt6cU?{qG=sx{@4zXq?UI%AKs=)6bsEZJnN(oo<%V?~?Bj7JU31@55SN z=I?lXOM>cBne0J`l{$>w`teyIJ7>Eh)z`{0k<(HJv|1o7*vmRTG;j__VIDK~IX&sM ze)JnfGhw11ww+9P&;mG3=;~hQ68HP;A*R9VA2)#{MR(y*;C5QXWHt-^T8V${Ki648 zW(qE@j|+-pq+oKYPP;%e7XGR_ajnPH%NC_8U93Nm>et2OQp?Og_X>LdHrP}1b{u@~ z>M+wF)qcT!ra^JdYm2{cMvNAk01=1kC^3;NGXY$|9vf5q4wWImM-Yt{nm2@Xhc9tE z_nCH$*e5TgQVfYR?{jy~Y1QbQ70{g0{+I(jjk^4n+yD(dYHXZ9t*%o(0DlfcEz3#W z%Z_P&BI!=zK*pqh^aIVzRi1xlizBqC+A2+lP&6YdJ)oK1{Ij;N6of&>S8)QRYIdti^jt$oz-$0(F z@4Ana+~G%zpf4Pnhq?f6PbgA<`nLJAUJK)9N-x~ z=O3>27aaJ>rXh<3f#}=RNRqV^z)Pil^-99TQou5-FD)PmiPr?aCM?cw=^l&*s>Kc# zhGM@1mp;_cTlror8VG$6JeJ9r_5UaCD)a(?2$M!My~1xq*0m;n^z!GK2Kt_7hl>lh+m(2@x;nQe`BLaUlU-JhtI~yfb2qoT$03Ilm348CWpi*|&Fo-JYcTKDg;sLi z>n-%!(b;;fG^QedfLnv@c?Hdrs1Z`uYI60QsqH7$@}q*uNnSEqtMP!>b|F!$g})9I z&|Eb}Yy+AQ&~)(A^>p$>Ts!SLfDFm^Y^D;go!glGs7*o$asqgi5QElUnNk%d~W4HbY>dET2{ru)Jfh;54x5EE?<7%NYeXi>T46 zc1X6Z4J}2hv0hMs3^E53YJnpRYd0DsXFqJ|Sy1Eo8b(55Cu1%!y0#$^6hiV;k|EGi zIn9NOiEJOg25_qZ`VIr#mwbe>%;T~*&D!Qdn~>srOAMzLFooOxazg1i#|XL3KFwJ_ zi+QI10m0%B<$m3wz`;*eU(gV%UFB%V3kkGsP$8@{> zp6@y~%Bn7eBCHI-3Sm4ROyrU`SPrr+E5G0LWJTTUtD%3}``*`romdd2^l%CNB}m)j z3>t_KJmJy9C`Kpmzy!wR^8{xJbNas%*0_b|J-@t5apTI+fHV8c&?iAbTW;#q#es3#0^m+(e!5DkGjrQND4a5ghgyKY76tsZ_ zU2!e|CaH!9&sQ_6jL6U51!OOVoPKc%=fCp430Z_=w&uD}EzKf$NT(ksM3`j~EXAjP z9mA0EdrEPMQ-Q*oxx03*`^O3M|&&f7&7&Q~~j z=KVG%zPTJ^@B1FdcKiJj+mJttO~bOpG#dt>qMkz2{uJgT#2{2f3Vf);)BQeW`OU?COQ+%hlmK=h6S2bAf z6Ahk|Siog@dyVMz<3}N<;kglt%PaEIxQZKa0-M3c+LL+@9&d$S!I=>`)44|%>;(z# zUl#rBbI&?N*}Q+QCjfkc6=QAnO;EoZYyVq}qx;gen#XG#WY#PO<$(t8wF?sJNGS8Y zzSxD`xL!N}>^BrB>c-_oUe0=tQ?9k1n^UT(^E|W;?T1FGau#5f+qQ#SR<`4WVf!vZr#3*61jpp(!!LPV z7c22Lr(B$Nx$Ky{S~7ezO5oQ%(^e31yidTLbi#L}y8gGv!5K1+-x9fW|7grH0OH)?D+TbrL@Vy+8&9aR6$F;N+SwA|9q zcG-Gr;Q($RLL`|T+#V0JKa!A`OTV?@S^E=(EDNp%cNWoJUT{YDXHZEc zEn63Am1v~8H&C#{RDR zm+@OlDd3Du$*MmB{OSBK%xY(dZsWy@##GFW%L+v6RMK4z{n_u|m)y^(67~%|K(eL~ z%pl=^nDLdw3rF_Y?>>&QkH2AV-mzcML#^m~h&oW&TtBru9SD3{dz7d2Dn{ z;TNC(#7kb|buo#-t6i%m($fVErsIn3R$yD{VY=(7%L7yyX2P29!j@IN` zD$r4~QKJHFwml5w`)s>E`Q4Ka!D>$X>Qc#-1GZr|}x0ma3@|I8GC+V@%pTRqwy{QPUucUQmP20>FMLA`ryFw$R$2*^d z+<~Ib$FCHVL3lMIkN(+jwOMo%x&X+%1An7x%m3(vd|q`{ixnuR0EeLV-m=7tl?UWf zBUPwXgr-~swT-V^5-Xx_i(JFI(`x)_OerIA=wHJRL(nRRX0K3BO8eCgQfzDY0ZDuR z^wD{M8&o&#Uol!o;9+pC8hnA9WSlqQE*I$ho^!mY{_-0_{uI4KJ@xPHhF{N{=`SIs zo%me9sk0e%^4E^>;od@lQ+FK;1d}!_V!4^99BygI2UR%b(GXP84BmB#@;cw?aGk9e zyJ2sU=xjNJxs!zfd)j9k*|5eeqe;D^WNWiaO4)Kxz=_b$MV5gHD}ITb*uhDMRNHh_12!_A8bv(?mtH5mUHt!pk zRAh!!jL#tvnTV#032>O^K^ABH4Lr;2Yx(&{4#$E5fdVVXF;9#=)XW8IVglR8A;P%T z2`S&~C>kUO^HptDXGNh_DC&anI zH>LY!DAAj2erR}N8{73*AzShrTB?GhT2gW{)(b4kaetN%LtryZp&O=8EkHf}`Hr(m zc7g5WwB-Gk*a64IpcNl^o7>dAyAoKz;3ru!+rvxbkz?7$!N$ExG-zsX-N=@l`#Vim zT|*^aak==1fSf8$c>nHVLBnQ{=lBO@gPuD3l3L+(BY3P53#WcwVWd$yG0G(C*HM66 z9)?AMPEGF9ab@ydsSeR7J}KulxrjyNfGlWex}X?x@HRID&)+_inz#_8BbXuykfgls z&?kSuKl<6Vt6MJ!EZc_yt2mU1-%!Y=@8SL|mKC&oJ>#*bbGk9Sq3-3|)V;X88^K50 zX}>t$Wzq>aP`!ASgB*TSWvAonHkdT@=bl1K5WdY;!=G1=?w^C--()_PIcgnEnuXnldq^i|EmsLG`5h87-14JA4iCN%M6dqz(PxkGoAg`vUjf z_+h9eD1dGT$+H}20m78K$59Ru?|43QeQU#KGxoBM2i&ZVvgoU&hpONj+i0Uja~L_a zSfv*MHWFU+k?Ys33NI}KJ{u>7c|%8&8uNrvUD@~hdKkQ5Re`wcvR8cWqNa+X{3XO&PlcXHk{vdP64plF#>XE zW24E~uRHLn$kqlH(f&cav5QRO!nY7jUBXClC?I)U`HK%DBmwCLs4?bm#h1Eeii!2YG8}w;;pw!sz@c`?Lz7iFcr-`>E}LG>Xrk?u7u9l zUZmw>V#casx8=pKKSYi+`1#^*?WpZmNlI$7dWqvfpQ{A{f~?Mr#umMBvfZf?^#J6O zfLxY^ZAc8qc5lO4hRrk4ZHSzO$sm03YPK^uvjqHT#4mPPd>7V!z(LeU(VT_dix;=S>_@}j5s=vu;z{_CaHlaX%cjk_uE_%&vr zWZvQOKIyV%{?@1^uC$5BMBId8tqijVxrRc$x1Nkio2`sA-L0Vu z9#J zm0@WeYG$2d+PnG+<{P#4ly4!&h2VX8lV4b!mhFT^jsa@Bw>&>ZodAk-Q=* z^V1Dr;GmHjk_yg?T>;u8qDiAnXuu*a$g3$*B3XYm7fkPvA3>&D6RoIxvncbtu0`C( z_(Vu_{M|>ie}CTLLbpNom1H_X>09czk1A^i2uq;zgikEliP{c+nAiC@se-Bl|64cL z2*$%}cTU$vWvz?ohVxSkb$TxWGN{cukGbC6Sc#6x??;k6>I+ZemNlFY9*gY8D@U5E z$OLS#f}KfwV)3%%o@soptZ|MSmmM`)M+P=@Ykm`?B5IX-fGs)@uvgF+6lI;luXeC1 zyq^9NF70jJClxWLzfQ?UB+-PTyTIxqkC?DNg|77XB07*Z$>*qO zujq4$EdCij>SbCj@r`>d;T=dxUYiYbo0St$vz=EY?#uVX`H)1l1%U)6bsi|@%~D_g zySF>c@a~!R2Vj8``l!?=kSN4dPmbBHb^p^E!U@ VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-voice-conversation-relay/assets/icon-small.png b/plugins/twilio/skills/twilio-voice-conversation-relay/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Make outbound phone calls via Twilio's Programmable Voice REST API. Covers + the full voice platform: calls.create(), answering machine detection (AMD), + conference-based agent bridging, call recording, status tracking, and SIP + Trunking. Use this skill for outbound calls, sales dialers, or when asking + what voice APIs are available. +--- + +## Overview + +> Agent safety: Before placing an outbound call, always confirm the recipient number and intent with the user. Outbound calls are irreversible and may incur charges. For automated systems, implement TCPA compliance: obtain prior express consent, respect quiet hours (8 AM-9 PM recipient local time), and maintain a Do Not Call list. + +Every outbound call requires a `from` Twilio number, a `to` recipient, and TwiML instructions that define what happens when the call is answered - either as a webhook URL or inline. + +--- + +## Voice Platform Capabilities + +Outbound calls go beyond basic `calls.create()`. Here's what you can build: + +| Capability | How | When to use | +|-----------|-----|-------------| +| Basic outbound call | `calls.create()` with TwiML or webhook URL | Any outbound call - see Quickstart below | +| Answering Machine Detection (AMD) | `machineDetection` parameter on `calls.create()` | Sales dialers, call campaigns - filter voicemail from humans | +| Conference-based agent bridging | `` in TwiML | Connect agents to live prospects with whisper, barge, hold | +| SIP Trunking | Elastic SIP Trunking | Bring your own carrier for outbound calls - cost reduction at scale | +| Call Recording | `record=True` on `calls.create()` or `` verb | Compliance, QA, training | +| Voice Insights | Automatic per-call metrics | Call quality monitoring - latency, jitter, packet loss, answer rates | +| AI Voice Agents | ConversationRelay + LLM | Real-time speech recognition -> LLM -> TTS for conversational AI | + +For TwiML verbs (Say, Gather, Dial, Record, Conference, Pay), see `twilio-voice-twiml`. +For AI voice agents, see `twilio-voice-conversation-relay`. + +--- + +## Prerequisites + +- Twilio account with a voice-capable phone number + - New to Twilio? See `twilio-account-setup` for signup, getting a number, and trial limitations + - Trial accounts can only call verified numbers +- Environment variables: + - `TWILIO_ACCOUNT_SID` + - `TWILIO_AUTH_TOKEN` + - See `twilio-iam-auth-setup` for credential setup and best practices +- SDK: `pip install twilio` / `npm install twilio` + +--- + +## Quickstart + +Python +```python +import os +from twilio.rest import Client + +client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"]) + +call = client.calls.create( + from_="+15017122661", # Your Twilio number (E.164) + to="+15558675310", # Recipient (E.164) + twiml="Your order has shipped. Goodbye." +) + +print(call.sid) # CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +print(call.status) # queued | ringing | in-progress | completed | failed +``` + +Node.js +```node +const twilio = require("twilio"); +const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); + +const call = await client.calls.create({ + from: "+15017122661", + to: "+15558675310", + twiml: "Your order has shipped. Goodbye.", +}); + +console.log(call.sid); +console.log(call.status); +``` + +--- + +## Key Patterns + +### Use a Webhook URL for Dynamic Call Handling + +Pass a `url` instead of inline `twiml` - Twilio POSTs to your server when the call connects and executes the TwiML you return. + +Python +```python +call = client.calls.create( + from_="+15017122661", + to="+15558675310", + url="https://yourapp.com/twiml/welcome" +) +``` + +Node.js +```node +const call = await client.calls.create({ + from: "+15017122661", + to: "+15558675310", + url: "https://yourapp.com/twiml/welcome", +}); +``` + +Python (Flask) - TwiML webhook handler +```python +from flask import Flask +from twilio.twiml.voice_response import VoiceResponse + +app = Flask(__name__) + +@app.route("/twiml/welcome", methods=["POST"]) +def welcome(): + response = VoiceResponse() + response.say("Hello! Press 1 to hear your account balance.") + response.gather(num_digits=1, action="/twiml/handle-input") + return str(response) +``` + +Node.js (Express) - TwiML webhook handler +```node +const { VoiceResponse } = require("twilio").twiml; + +app.post("/twiml/welcome", (req, res) => { + const response = new VoiceResponse(); + response.say("Hello! Press 1 to hear your account balance."); + response.gather({ numDigits: 1, action: "/twiml/handle-input" }); + res.type("text/xml").send(response.toString()); +}); +``` + +For all TwiML verbs (Say, Gather, Dial, Record, Conference), see `twilio-voice-twiml`. + +### Track Call Status + +Python +```python +call = client.calls.create( + from_="+15017122661", + to="+15558675310", + url="https://yourapp.com/twiml/welcome", + status_callback="https://yourapp.com/call-status", + status_callback_method="POST" +) +``` + +Node.js +```node +const call = await client.calls.create({ + from: "+15017122661", + to: "+15558675310", + url: "https://yourapp.com/twiml/welcome", + statusCallback: "https://yourapp.com/call-status", + statusCallbackMethod: "POST", +}); +``` + +Status transitions: `queued -> ringing -> in-progress -> completed` (or `failed`/`busy`/`no-answer`). + +### Record a Call + +Python +```python +call = client.calls.create( + from_="+15017122661", + to="+15558675310", + url="https://yourapp.com/twiml/welcome", + record=True, + recording_status_callback="https://yourapp.com/recording-ready" +) +``` + +Node.js +```node +const call = await client.calls.create({ + from: "+15017122661", + to: "+15558675310", + url: "https://yourapp.com/twiml/welcome", + record: true, + recordingStatusCallback: "https://yourapp.com/recording-ready", +}); +``` + +Recordings accessible at `https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Recordings`. + +--- + +## Answering Machine Detection (AMD) + +Detect whether a human or voicemail answers before connecting an agent or leaving a message. Two modes: + +| Mode | Behavior | Best for | +|------|----------|----------| +| `Enable` | Returns result immediately when human/machine first detected | Sales dialers - connect agent to humans ASAP | +| `DetectMessageEnd` | Waits for voicemail greeting to finish (beep/silence) | Leaving voicemail - wait for beep, then play/record message | + +Python - Sales dialer (connect agents to live answers only) +```python +call = client.calls.create( + from_="+15017122661", + to="+15558675310", + url="https://yourapp.com/handle-answer", + machine_detection="Enable", # Immediate detection - use for live-agent connect + async_amd=True, # Non-blocking - call connects while AMD analyzes + async_amd_status_callback="https://yourapp.com/amd-result", + async_amd_status_callback_method="POST" +) +``` + +Node.js +```javascript +const call = await client.calls.create({ + from: "+15017122661", + to: "+15558675310", + url: "https://yourapp.com/handle-answer", + machineDetection: "Enable", + asyncAmd: true, + asyncAmdStatusCallback: "https://yourapp.com/amd-result", + asyncAmdStatusCallbackMethod: "POST", +}); +``` + +AMD webhook delivers `AnsweredBy` parameter: + +| Value | Meaning | Action | +|-------|---------|--------| +| `human` | Live person detected | Connect to agent | +| `machine_start` | Machine detected, greeting still playing | Hang up or wait | +| `machine_end_beep` | Voicemail beep heard | Leave message | +| `machine_end_silence` | Silence after greeting | Leave message | +| `fax` | Fax tone detected | Hang up | +| `unknown` | Could not determine | Treat as human or retry | + +Conference-based agent bridging (production pattern for sales dialers): + +```python +# In /handle-answer webhook - when AMD says "human": +response = VoiceResponse() +dial = response.dial() +dial.conference( + f"Sales-{call_sid}", + start_conference_on_enter=False, # Prospect waits for agent + end_conference_on_exit=True +) + +# Separately, dial agent into same conference: +client.calls.create( + from_="+15017122661", + to=agent_number, + twiml=f'Sales-{call_sid}' +) +``` + +This pattern gives you call control (whisper to agent before connecting, supervisor barge-in, hold) that direct `` does not. + +Cost: AMD adds ~$0.0075 per call to standard voice pricing. + +--- + +## Response Fields + +| Field | Description | +|-------|-------------| +| `sid` | Call identifier (`CA...`) | +| `status` | `queued`, `ringing`, `in-progress`, `completed`, `failed`, `busy`, `no-answer` | +| `duration` | Length in seconds (after completion) | +| `price` | Cost (populated after completion) | + +--- + +## Common Errors + +| Code | Meaning | Fix | +|------|---------|-----| +| 21211 | Invalid `to` number | Validate E.164 format | +| 13224 | Invalid TwiML at webhook URL | Validate TwiML response from your server | +| 13225 | TwiML URL returned non-200 status | Fix your webhook endpoint | + +--- + +## CANNOT + +- Cannot use a private TwiML URL - Must be publicly accessible. Use ngrok for local development only; deploy to a cloud provider for production. +- Cannot exceed ~4,096 characters in inline `twiml` parameter - Use a TwiML URL for longer responses +- Cannot call unverified numbers on trial accounts - Upgrade to paid or verify recipient numbers first +- AMD accuracy is ~85-90% - Expect some false positives/negatives. Tune `machineDetectionSpeechThreshold` (default 2400ms) based on your results. +- AMD adds latency - `Enable` mode returns results faster but may misclassify short greetings. `DetectMessageEnd` is more accurate but adds seconds before your app can act. +- Cannot use AMD with SIP Trunking - AMD is only available on calls made via the Calls API + +--- + +## Next Steps + +- TwiML verb reference (Say, Gather, Dial, Record, Conference, Pay): `twilio-voice-twiml` +- AI voice agents with real-time speech/LLM: `twilio-voice-conversation-relay` +- Improve answer rates (Branded Calling, STIR/SHAKEN): `twilio-numbers-senders` diff --git a/plugins/twilio/skills/twilio-voice-outbound-calls/assets/icon-large.png b/plugins/twilio/skills/twilio-voice-outbound-calls/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-voice-outbound-calls/assets/icon-small.png b/plugins/twilio/skills/twilio-voice-outbound-calls/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Build voice call logic using TwiML (Twilio Markup Language). Covers the + core verbs (Say, Play, Gather, Dial, Record, Conference), generating TwiML + with Python and Node.js SDKs, and a complete inbound call IVR example. Use + this skill to define call behavior for inbound or outbound calls. +--- + +## Overview + +TwiML is XML that Twilio executes during a call. Your server returns a TwiML document in response to a Twilio webhook POST, and Twilio executes it. + +``` +Caller -> Twilio -> POST to your webhook -> Your server returns TwiML -> Twilio executes it +``` + +--- + +## Prerequisites + +- Twilio account with a voice-capable phone number + - New to Twilio? See `twilio-account-setup` +- Webhook endpoint returning TwiML with `Content-Type: text/xml` +- SDK (for programmatic generation): `pip install twilio` / `npm install twilio` + +--- + +## Quickstart + +A minimal inbound call handler that greets the caller and presents a menu: + +Python (Flask) +```python +from flask import Flask, request +from twilio.twiml.voice_response import VoiceResponse + +app = Flask(__name__) + +@app.route("/voice", methods=["POST"]) +def handle_call(): + response = VoiceResponse() + gather = response.gather(num_digits=1, action="/menu-choice") + gather.say("Welcome to Acme. Press 1 for sales, 2 for support.") + response.redirect("/voice") # Loop if no input + return str(response) + +@app.route("/menu-choice", methods=["POST"]) +def menu_choice(): + digit = request.form.get("Digits") + response = VoiceResponse() + if digit == "1": + response.dial("+15551234567") + elif digit == "2": + response.say("Connecting to support.") + response.dial("+15559876543") + else: + response.say("Invalid option.") + response.redirect("/voice") + return str(response) +``` + +Node.js (Express) +```node +const { VoiceResponse } = require("twilio").twiml; + +app.post("/voice", (req, res) => { + const response = new VoiceResponse(); + const gather = response.gather({ numDigits: 1, action: "/menu-choice" }); + gather.say("Welcome. Press 1 for sales, 2 for support."); + response.redirect("/voice"); + res.type("text/xml").send(response.toString()); +}); + +app.post("/menu-choice", (req, res) => { + const digit = req.body.Digits; + const response = new VoiceResponse(); + if (digit === "1") response.dial("+15551234567"); + else response.say("Invalid option.").redirect("/voice"); + res.type("text/xml").send(response.toString()); +}); +``` + +--- + +## Core Verbs + +### Say - Text-to-speech + +Python +```python +from twilio.twiml.voice_response import VoiceResponse + +response = VoiceResponse() +response.say("Your appointment is confirmed.", voice="alice", language="en-US") +``` + +Node.js +```node +const { VoiceResponse } = require("twilio").twiml; +const response = new VoiceResponse(); +response.say({ voice: "alice", language: "en-US" }, "Your appointment is confirmed."); +``` + +Voices: `alice` (default), `man`, `woman`, or Polly/Google TTS (e.g. `Polly.Joanna`). + +### Gather - Collect keypad input or speech + +Python +```python +response = VoiceResponse() +gather = response.gather(num_digits=1, action="/handle-input", method="POST") +gather.say("Press 1 for sales, press 2 for support.") +response.say("We did not receive your input.") # Fallback if no input +``` + +Node.js +```node +const gather = response.gather({ numDigits: 1, action: "/handle-input", method: "POST" }); +gather.say("Press 1 for sales, press 2 for support."); +response.say("We did not receive your input."); +``` + +Twilio POSTs collected digits to `action` as `Digits` parameter. + +### Play - Play an audio file + +Python +```python +response = VoiceResponse() +response.play("https://example.com/audio/greeting.mp3") +``` + +Node.js +```node +const response = new VoiceResponse(); +response.play("https://example.com/audio/greeting.mp3"); +``` + +Supported formats: MP3, WAV. URL must be publicly accessible. + +### Dial - Connect to another number + +Python +```python +from twilio.twiml.voice_response import Dial + +response = VoiceResponse() +dial = Dial(action="/dial-complete") +dial.number("+15558675310") +response.append(dial) +``` + +Node.js +```node +const dial = response.dial({ action: "/dial-complete" }); +dial.number("+15558675310"); +``` + +### Record - Capture caller audio + +Python +```python +response = VoiceResponse() +response.say("Leave a message after the beep.") +response.record( + action="/recording-complete", + max_length=60, + transcribe=True, + transcribe_callback="/transcription-ready" +) +``` + +Node.js +```node +const response = new VoiceResponse(); +response.say("Leave a message after the beep."); +response.record({ + action: "/recording-complete", + maxLength: 60, + transcribe: true, + transcribeCallback: "/transcription-ready", +}); +``` + +### Voicemail - Record a message when no one answers + +Use `` with `action` URL + `` in the action handler. When the dial times out or the callee is busy, the action URL serves TwiML with ``. + +Python +```python +# Primary TwiML - try to connect the call +response = VoiceResponse() +dial = Dial(action="/voicemail", timeout=20) # 20 seconds before voicemail +dial.number("+15558675310") +response.append(dial) + +# /voicemail handler - plays if no answer +def voicemail_handler(request): + response = VoiceResponse() + response.say("We missed your call. Please leave a message after the beep.") + response.record( + action="/recording-complete", + max_length=120, + transcribe=True, + transcribe_callback="/transcription-ready", + play_beep=True + ) + response.say("We didn't receive a recording. Goodbye.") + return str(response) +``` + +Node.js +```node +// Primary TwiML - try to connect the call +const response = new VoiceResponse(); +const dial = response.dial({ action: "/voicemail", timeout: 20 }); +dial.number("+15558675310"); + +// /voicemail handler - plays if no answer +app.post("/voicemail", (req, res) => { + const response = new VoiceResponse(); + response.say("We missed your call. Please leave a message after the beep."); + response.record({ + action: "/recording-complete", + maxLength: 120, + transcribe: true, + transcribeCallback: "/transcription-ready", + playBeep: true, + }); + response.say("We didn't receive a recording. Goodbye."); + res.type("text/xml").send(response.toString()); +}); +``` + +Important: `` captures the caller only (voicemail-style). It is NOT for recording two-party calls - see `twilio-call-recordings` for that. + +### Conference - Multi-party calls + +Python +```python +response = VoiceResponse() +dial = response.dial() +dial.conference( + "Daily Standup", + start_conference_on_enter=True, + end_conference_on_exit=True +) +``` + +Node.js +```node +const response = new VoiceResponse(); +const dial = response.dial(); +dial.conference("Daily Standup", { + startConferenceOnEnter: true, + endConferenceOnExit: true, +}); +``` + +### Pay - PCI-compliant payment collection + +> Critical warnings: +> - Pay Connectors are Console-only - there is no REST API to create or manage connectors. Set up in Console > Voice > Pay Connectors before coding. +> - PCI Mode is IRREVERSIBLE once enabled on an account. Use a dedicated sub-account for payment calls. + +Python +```python +response = VoiceResponse() +response.say("We'll now collect your payment.") +pay = Pay( + payment_connector="stripe_connector", # Name from Console setup + charge_amount="49.99", + currency="usd", + action="/payment-complete", + status_callback="/payment-status" +) +response.append(pay) +``` + +Node.js +```node +const response = new VoiceResponse(); +response.say("We'll now collect your payment."); +response.pay({ + paymentConnector: "stripe_connector", + chargeAmount: "49.99", + currency: "usd", + action: "/payment-complete", + statusCallback: "/payment-status", +}); +``` + +Supported processors: Stripe, Braintree, CardConnect. Card data routes directly to the processor - never touches your server. + +--- + +## Production Deployment + +### Webhook Hosting + +For production, do NOT use ngrok. Deploy your TwiML server with HTTPS: + +- Requirement: Public HTTPS URL, responds within 15 seconds, returns `Content-Type: text/xml` +- Options: Cloud Run, AWS Lambda + API Gateway, Railway, Render - any service with TLS and auto-scaling +- Fallback URL: Configure in Console (Phone Numbers > Active Numbers > select number) for when your primary server is unreachable + +### State Between TwiML Requests + +Each webhook request is stateless. To maintain conversation state across interactions: + +- URL query params: Pass state in `action` URLs - `/next-step?language=es&dept=sales` +- Session store: Use Redis or a database keyed by `CallSid` +- Do NOT use in-memory state - your server may scale to multiple instances + +### Monitoring + +- Status callbacks: Track call lifecycle events (`statusCallback` on the call or number config) +- Voice Insights: Automatic quality metrics per call (Console > Monitor > Insights) +- Debugger: Console > Monitor > Errors for TwiML parsing failures and webhook timeouts +- Fallback URLs: Always configure a fallback TwiML URL - serves a graceful message if your primary endpoint fails + +--- + +## Webhook Request Parameters + +| Parameter | Description | +|-----------|-------------| +| `CallSid` | Unique call identifier | +| `From` | Caller's number | +| `To` | Called number | +| `CallStatus` | Current status | +| `Direction` | `inbound` or `outbound-api` | + +--- + +## CANNOT + +- Cannot return TwiML without correct content type - Must use `Content-Type: text/xml` +- Cannot exceed 15-second webhook response time - Twilio times out and falls back +- Cannot exceed 4,096 characters in `` verb - Split longer text across multiple `` elements +- Cannot create Pay Connectors via API - Pay Connectors are Console-only (Console > Voice > Pay Connectors). No REST API exists for connector management. +- Cannot reverse PCI Mode - Once enabled on an account, PCI Mode is permanent and account-wide. Use a dedicated sub-account for payment calls. +- Cannot use `` for two-party call recording - `` captures the caller only (voicemail-style). For dual-channel recording of both parties, use `record=True` on `calls.create()` or the Recordings API. + +--- + +## Next Steps + +- Place outbound calls (AMD, conferencing): `twilio-voice-outbound-calls` +- AI voice agents with real-time speech/LLM: `twilio-voice-conversation-relay` diff --git a/plugins/twilio/skills/twilio-voice-twiml/assets/icon-large.png b/plugins/twilio/skills/twilio-voice-twiml/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-voice-twiml/assets/icon-small.png b/plugins/twilio/skills/twilio-voice-twiml/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Design, secure, and operate Twilio webhook endpoints. Covers inbound event + handling, status callbacks, signature validation, connection overrides for + retry and timeout tuning, local development tunneling, and production + hardening. Use this skill whenever an agent needs to receive HTTP callbacks + from Twilio for any product -- messaging, voice, verify, or event streams. +--- + +## Overview + +Twilio delivers events to your application via HTTP callbacks (webhooks). Inbound messages and calls trigger webhooks that expect a TwiML response; status callbacks and event streams push delivery and lifecycle data asynchronously. This skill covers the cross-product patterns that apply to every webhook integration. + +--- + +## Prerequisites + +- Twilio account with a phone number or service configured with a webhook URL + -- New to Twilio? See `twilio-account-setup` +- `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN` -- see `twilio-iam-auth-setup` +- SDK: `pip install twilio flask` / `npm install twilio express` +- Publicly accessible HTTPS endpoint (see Local Development section below) + +--- + +## Quickstart + +Receive an inbound SMS and validate the request signature before replying. + +Python (Flask) +```python +import os +from flask import Flask, request, abort +from twilio.request_validator import RequestValidator +from twilio.twiml.messaging_response import MessagingResponse + +app = Flask(__name__) +validator = RequestValidator(os.environ["TWILIO_AUTH_TOKEN"]) + +@app.route("/sms", methods=["POST"]) +def incoming_sms(): + sig = request.headers.get("X-Twilio-Signature", "") + if not validator.validate(request.url, request.form, sig): + abort(403) + resp = MessagingResponse() + resp.message(f"Got: {request.form.get('Body')}") + return str(resp), 200, {"Content-Type": "text/xml"} +``` + +Node.js (Express) +```node +const express = require("express"); +const twilio = require("twilio"); +const app = express(); +app.use(express.urlencoded({ extended: false })); + +app.post("/sms", (req, res) => { + const valid = twilio.validateRequest( + process.env.TWILIO_AUTH_TOKEN, + req.headers["x-twilio-signature"], + `https://${req.headers.host}${req.originalUrl}`, + req.body + ); + if (!valid) return res.status(403).send("Forbidden"); + const twiml = new twilio.twiml.MessagingResponse(); + twiml.message(`Got: ${req.body.Body}`); + res.type("text/xml").send(twiml.toString()); +}); +``` + +Set your webhook URL in Console: Phone Numbers > Active Numbers > (your number) > Messaging > "A Message Comes In". + +--- + +## Key Patterns + +### 1. Webhook Types Across Products + +| Webhook type | Trigger | Expected response | Products | +|---|---|---|---| +| Inbound event | Message received / call answered | TwiML (XML) | Messaging, Voice | +| Status callback | Resource state change | `200` or `204` (no body required) | Messaging, Voice, Verify, Video | +| Action URL | TwiML verb completes (``, ``) | Next TwiML | Voice | +| Recording status | Recording processing completes | `200` or `204` | Voice | +| Debugger event | Error or warning on account | `200` or `204` | All | +| Event Streams | Any subscribed event | `200` or `204` | All (via Sink) | + +### 2. Signature Validation + +Twilio signs every webhook with an `X-Twilio-Signature` header (HMAC-SHA1 using your Auth Token). Always validate before processing. + +Form-encoded requests (`application/x-www-form-urlencoded`): + +Pass the full URL and POST body parameters to the validator. + +Python +```python +from twilio.request_validator import RequestValidator + +validator = RequestValidator(os.environ["TWILIO_AUTH_TOKEN"]) +is_valid = validator.validate(request.url, request.form, request.headers.get("X-Twilio-Signature", "")) +``` + +Node.js +```node +const { validateRequest } = require("twilio"); + +const isValid = validateRequest( + process.env.TWILIO_AUTH_TOKEN, + req.headers["x-twilio-signature"], + `https://${req.headers.host}${req.originalUrl}`, + req.body +); +``` + +JSON requests (`application/json`): + +Twilio appends a `bodySHA256` query parameter to your URL. Use the SDK's JSON-specific validation. + +Python +```python +from twilio.request_validator import RequestValidator + +validator = RequestValidator(os.environ["TWILIO_AUTH_TOKEN"]) +is_valid = validator.validate_body( + request.url, + request.get_data(as_text=True), + request.headers.get("X-Twilio-Signature", "") +) +``` + +Node.js +```node +const twilio = require("twilio"); + +// Use express.raw() or a verify callback to preserve the raw body +const isValid = twilio.validateRequestWithBody( + process.env.TWILIO_AUTH_TOKEN, + req.headers["x-twilio-signature"], + `https://${req.headers.host}${req.originalUrl}`, + req.rawBody // must be the exact bytes Twilio sent, not JSON.stringify(req.body) +); +``` + +Critical: Use the SDK validator. Do not implement your own -- Twilio may add parameters without notice, and the exact algorithm (including port handling) has edge cases the SDK handles. + +### 3. Status Callback Handling + +Status callbacks are asynchronous POST requests Twilio sends when a resource changes state. They do not expect TwiML -- return `200` or `204`. + +Messaging status flow: `queued` -> `sent` -> `delivered` (or `undelivered` / `failed`) + +When using Messaging Services, the flow starts with `accepted` -> `queued` -> ... + +Voice status events: `initiated`, `ringing`, `answered`, `completed` + +Subscribe to specific events via `StatusCallbackEvent` parameter. + +Status callbacks are signed with `X-Twilio-Signature` like all Twilio webhooks. Validate before acting on the payload -- an unvalidated endpoint lets anyone forge delivery status and drive downstream logic. + +Python (Flask) -- messaging status handler +```python +@app.route("/status", methods=["POST"]) +def message_status(): + sig = request.headers.get("X-Twilio-Signature", "") + if not validator.validate(request.url, request.form, sig): + return "Forbidden", 403 + sid = request.form.get("MessageSid") + status = request.form.get("MessageStatus") + error_code = request.form.get("ErrorCode") + if status in ("failed", "undelivered") and error_code: + print(f"Delivery failed {sid}: error {error_code}") + return "", 204 +``` + +Node.js (Express) -- voice status handler +```node +app.post("/call-status", (req, res) => { + const valid = twilio.validateRequest( + process.env.TWILIO_AUTH_TOKEN, + req.headers["x-twilio-signature"], + `https://${req.headers.host}${req.originalUrl}`, + req.body + ); + if (!valid) return res.status(403).send("Forbidden"); + const { CallSid, CallStatus, Duration } = req.body; + console.log(`${CallSid}: ${CallStatus} (${Duration}s)`); + res.sendStatus(204); +}); +``` + +Attach status callbacks when creating resources: + +```python +# Messaging +message = client.messages.create( + to="+15558675310", from_="+15017122661", body="Hello!", + status_callback="https://yourapp.com/status" +) + +# Voice +call = client.calls.create( + to="+15558675310", from_="+15017122661", + url="https://yourapp.com/voice", + status_callback="https://yourapp.com/call-status", + status_callback_event=["initiated", "ringing", "answered", "completed"], + status_callback_method="POST" +) +``` + +### 4. Connection Overrides (Retry and Timeout Tuning) + +Append URL fragments to any webhook URL to override default connection behavior. Fragments are not included in signature computation. + +Format: `https://yourapp.com/webhook#key=value&key=value` + +| Parameter | Key | Default | Range | Description | +|---|---|---|---|---| +| Connect Timeout | `ct` | 5000ms | 100-10000 | TCP connection timeout | +| Read Timeout | `rt` | 15000ms | 100-15000 | Time to wait for first response byte | +| Total Time | `tt` | 15000ms | 100-15000 | Total time for all retries | +| Retry Count | `rc` | 1 | 0-5 | Number of retry attempts | +| Retry Policy | `rp` | `ct` | `4xx`, `5xx`, `ct`, `rt`, `all` | What triggers a retry | +| Edge Location | `e` | `ashburn` | `ashburn`, `dublin`, `frankfurt`, `sao-paulo`, `singapore`, `sydney`, `tokyo`, `umatilla` | Egress edge | + +Examples: + +```text +# Retry up to 3 times on connection or read timeout +https://yourapp.com/sms#rc=3&rp=ct,rt + +# Fast failover: 1s connect timeout, 2 retries +https://yourapp.com/voice#ct=1000&rc=2 + +# Rotate edge locations on retry +https://yourapp.com/status#e=ashburn,dublin&rc=1 +``` + +Twilio adds an `I-Twilio-Idempotency-Token` header on retries for deduplication. + +Limitations: Connection overrides are not available on Twilio Conversations or Frontline webhooks. Voice webhooks have a hard 15-second ceiling regardless of override values. + +### 5. Configure Webhook URLs via API + +Python +```python +# Phone number -- messaging +client.incoming_phone_numbers("PNxxxxxxxxxx").update( + sms_url="https://yourapp.com/sms", + sms_method="POST", + sms_fallback_url="https://yourapp.com/sms-fallback", + sms_fallback_method="POST" +) + +# Phone number -- voice +client.incoming_phone_numbers("PNxxxxxxxxxx").update( + voice_url="https://yourapp.com/voice", + voice_method="POST", + voice_fallback_url="https://yourapp.com/voice-fallback", + voice_fallback_method="POST", + status_callback="https://yourapp.com/call-status", + status_callback_method="POST" +) +``` + +Node.js +```node +// Phone number -- messaging +await client.incomingPhoneNumbers("PNxxxxxxxxxx").update({ + smsUrl: "https://yourapp.com/sms", + smsMethod: "POST", + smsFallbackUrl: "https://yourapp.com/sms-fallback", + smsFallbackMethod: "POST", +}); + +// Phone number -- voice +await client.incomingPhoneNumbers("PNxxxxxxxxxx").update({ + voiceUrl: "https://yourapp.com/voice", + voiceMethod: "POST", + voiceFallbackUrl: "https://yourapp.com/voice-fallback", + voiceFallbackMethod: "POST", + statusCallback: "https://yourapp.com/call-status", + statusCallbackMethod: "POST", +}); +``` + +### 6. Local Development with Tunnels + +Twilio cannot reach `localhost`. Use a tunnel to expose your local server. + +ngrok (recommended for development): +```bash +ngrok http 5000 +# Copy the HTTPS URL, e.g. https://abc123.ngrok-free.app +``` + +Then set the ngrok URL as your webhook in Console or via API. + +Twilio CLI: +```bash +# Install and use the CLI webhook plugin +twilio phone-numbers:update +15017122661 \ + --sms-url="https://abc123.ngrok-free.app/sms" +``` + +ngrok caveats: +- Free tier URLs change on restart -- update Twilio config each time +- Free tier sessions expire after hours -- use a stable host for anything beyond quick tests +- For persistent local dev, use ngrok with a custom domain (paid) or deploy to a cloud host + +### 7. Event Streams (Webhook Sink) + +For high-volume or cross-product event delivery, use Event Streams instead of per-resource status callbacks. Event Streams deliver events to a Sink (webhook, Kinesis, or Segment). The Twilio SDK does not wrap Event Streams -- use `requests` / `fetch` directly. + +Python -- create a webhook sink and subscribe to error events +```python +import os, requests + +account_sid = os.environ["TWILIO_ACCOUNT_SID"] +auth_token = os.environ["TWILIO_AUTH_TOKEN"] + +# Create a webhook sink +sink = requests.post( + "https://events.twilio.com/v1/Sinks", + auth=(account_sid, auth_token), + data={ + "Description": "Error log sink", + "SinkType": "webhook", + "SinkConfiguration": '{"destination": "https://yourapp.com/events", "method": "POST"}' + } +).json() + +# Subscribe to error log events +requests.post( + "https://events.twilio.com/v1/Subscriptions", + auth=(account_sid, auth_token), + data={ + "Description": "Error log subscription", + "SinkSid": sink["sid"], + "Types": '[{"type": "com.twilio.error-logs.error.logged"}]' + } +) +``` + +Sink types: `webhook`, `kinesis`, `segment`. Subscriptions filter which event types route to which sinks. + +### 8. HTTP Authentication for Webhook URLs + +Twilio supports HTTP Basic and Digest authentication. Embed credentials in the URL: + +```text +https://username:password@yourapp.com/sms +``` + +This provides an additional layer of protection beyond signature validation. Note: these credentials are visible in Console webhook configuration and may appear in server access logs -- rotate them independently of your Auth Token. + +--- + +## Common Webhook Parameters + +### Inbound SMS + +| Parameter | Description | +|---|---| +| `MessageSid` | Unique message identifier | +| `AccountSid` | Your Twilio account SID | +| `From` | Sender phone number (E.164) | +| `To` | Your Twilio number | +| `Body` | Message text | +| `NumMedia` | Number of media attachments | +| `MediaUrl0..N` | URL of each media attachment | +| `MediaContentType0..N` | MIME type of each attachment | + +### Inbound Voice Call + +| Parameter | Description | +|---|---| +| `CallSid` | Unique call identifier | +| `AccountSid` | Your Twilio account SID | +| `From` | Caller phone number (E.164) | +| `To` | Your Twilio number | +| `CallStatus` | `queued`, `ringing`, `in-progress`, `completed`, `busy`, `failed`, `no-answer`, `canceled` | +| `Direction` | `inbound` | +| `ForwardedFrom` | Number that forwarded the call (if applicable) | + +### Message Status Callback + +| Parameter | Description | +|---|---| +| `MessageSid` | Unique message identifier | +| `MessageStatus` | `accepted`, `queued`, `sending`, `sent`, `delivered`, `undelivered`, `failed`, `read` | +| `ErrorCode` | Twilio error code (present on `failed`/`undelivered`) | +| `ErrorMessage` | Human-readable error description | + +### Debugger Event Callback + +| Parameter | Description | +|---|---| +| `Sid` | Debugger event identifier | +| `AccountSid` | Account that generated the event | +| `Level` | `Error` or `Warning` | +| `Timestamp` | ISO 8601 time of occurrence | +| `Payload` | JSON with `resource_sid`, `error_code`, `more_info`, `webhook` (request/response details) | + +--- + +## CANNOT + +- Cannot exceed 15-second voice webhook response time - Twilio hangs up or falls back. Messaging webhooks retry on timeout. +- Cannot use HTTP in production - HTTPS required. No self-signed certificates. Do not pin Twilio certificates - they rotate without notice. +- Cannot allowlist Twilio by IP - Webhooks come from dynamic IPs. Use signature validation instead. +- Cannot guarantee status callback delivery or order - Best-effort. Implement idempotency using `MessageSid` + `MessageStatus` or `CallSid` + `CallStatus` as composite keys. +- Cannot redirect without losing POST parameters - HTTP 301/302 redirects cause Twilio to follow with GET, dropping `Digits`, `RecordingUrl`, etc. +- Cannot use connection overrides on Conversations or Frontline webhooks - Not supported for these products + +--- + +## Next Steps + +- Receive inbound SMS: `twilio-messaging-webhooks` +- Voice call handling: `twilio-voice-twiml` +- Scale webhook handling: `twilio-reliability-patterns` +- Debug webhook failures: `twilio-debugging-observability` +- Secure credentials: `twilio-iam-auth-setup` diff --git a/plugins/twilio/skills/twilio-webhook-architecture/assets/icon-large.png b/plugins/twilio/skills/twilio-webhook-architecture/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-webhook-architecture/assets/icon-small.png b/plugins/twilio/skills/twilio-webhook-architecture/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 zcmeHKg;&&3xBU$vAtfLTNOwpoEg;McDcvP7G)N2~rL=S-AvH8gNOwpJjC2Y}Nr=SI z15!`ld+YrXZ+&;&efK_R-8}2A8&R6-io}Gpga7~#D=R^@|FXtE;A8(SbAH>`0{|h1 z0|cV!VC4(|KG`{G-nzPTR1sqx-hTNbf}UW%D(!n@JM={cT439I;9;PbX#PG651AIU zh&u!amk3M@gTpBAgS0^Y&qIR9L$zLgv{sl^dE@Jg-rU4|m0nq}LwjJp`>!_OVC|Fu9rw}w_`49?>=~@1V$-Q7vxhBF%2=%osz?_XCsdw4!d+Ve zD;U9)lG?LTxAS?&f#$_BEkK37HWFT6n1sO^MVlHLMH>Z$?g}*TyiQzImKI@tG^Z_g zWR+;rMf1zzIZ#&y#f&Y6vGIZskZc4o1)@o_W|;vt*jp#Yi*=c&N9}9`I5e+IN+cvC zI7~@UP}A|1Gg9F|Kp?|K4*2IT|(OksWjt!WpAEZ)_UlV7IGfsX64L{}+P+s14vH=NJoL_pfux!>k(NcQL0 zScy2gd4O&($2j&t7uG!BzlyUIOo|WWGZ9u`Jx;`#1Q<7h&MfW^;=QvFo5$mkBmRgN zmxr4T(62si2KYmFSMPzbFx^}kz*yH~AL1=${2qDhNG$H>j)|hy_(so(>}fVgF6Fv4 zDJk*T<)10xS4K3-ztbepC76+q8*dut{)OfFkT&c<-fH~mDeozRf(7}DcX}4|^OUw- zqtya*gjHb_YcG8GvhmO%s5Qw8@Dho6P*pGFkn5T#7Xrvb?IR`Ov4UgCWBnUq3saMzk}1;#MUbM=J#km*5z`DBGUU&_mA*2FZ6hQS6R$tB&?g{Nl?5XYHob$y;>hv0|K69H8XW*wc2zTnq z>HpEEw!U?w?c>}|F9VDXRCQDT+&wb80A1q*&`tN#9AcNmbzw6_D2~iYd4oW*Z8} zuw&L^6N2clsIVp+6L<8*B#ExRqP$5woRaz_dEHW+5(0){0 zR-~>Rr!=ChQ#4YHT{Kk`QF zi{m4>jDIF8^95f~0A#;(ba;5scsLyOIju5TnpH{#VlVmB)tIL;<8_GW3x9!9mhH!1 zlNPz>SiE%(;@XqjQ=TbyKJU~EP`d5FGV3;DhFK=aNAy5o?+b$AiSYP!xqj8?Cn~z^ z{_JVm!IK2V{=?O^!S2<^lAFE5Z-+g2%6M{lXm}p-Q0v+1I+UrG4V4v_+3A(ea5sINZ4Ulu=Gx$@3Gxjq+AC$a)KTthLp*v?CfkeysW}6C+MMdEIus(-EW|Ri1$x`D;nP zCiaAO9X4A=Pui&$P;*WRbCFHaVyLzW|JkntcA**x0z+bbO_P`!WH94O6^ij3bF{L1 zXVG%*zI+@Hkgd{Ji>+Gbxmeh)V&pRNy7J2MIJ>vHcSHRlT+B7hYRpv-Gx-<@I%4M? zL2qj>d#_c`quv#0GweMyRv}ZtC9<8N7~CKCJ9a=JLQxDlR_SJ5K`N~(cVAX@2B7}b5RX1`6qA4SQ|z_ts-=B9x`9iT?c7=2q|0;C>tV>oaN-YR09w?<0%S?OzdN6XZL|4eUStZ!nGNldAVs z%=cq{csG2jt+17~{Ni?J-(v1-_fEh6AeuB)ITfPEuU9!UJR?=ULTAC)>^r_=p&F;6 zW;t>NS%P>wN;@s?dj1|Ju_Cc!4A5qLxpF1)b9dlge|S%Lf})S2Lc%@9eTRC7SFf~P zcwEA>-i=Isg~$K67}(DmrTUq+pl|H7hHx|Wx!!3)tdJlNr6=AdBWgFL_R(dFT+X=r znpc_U-{XtoCq_SxcI(KLwzloHEp&=q-VQpPP3hTLk5MAovqz-b++6lsT25%T?fBVV zg3-)KRxwopF>7Px{o8Q#TwP2PWM(~zaEtpAr#UUzxww|4x%anIEGFAlG z?=~H{GFH?4YPa++baLBS{hk#sCa~n*=5^VK+24hrBJM@>hNENqVq=#rS3b06`C3CoPvc5?bJd@rd_-ka?&?m*X2#BP12HjE0&nY*>L>&*1gHY|{5~BX5Avc6 zes~0XB)lx`$PLsv)L+}5@Q(HFK(j7ujBdZ_3gEi?cvrKVf-c3o?{NSA7Z4`w-O`j(wLyRB&{JDo2LN#C<($)7r?-wT|7_2; zXCh#X_wEaSoH@L{IhwB!A;M;T)v|Yc(BaO7leoMtg(sNtdp}Q%Hh5zvnn`VNT>oMC zNfJ+e=ac6X-&H~CtHa3wZ_74KPGfbr$w)agBKs{B$c?3cyklEZ{+7d!^&Tx$K862+ z+P{*y;3A$RK_$Suf|oiOw&td>qMP2%axqEt>kNdfSv-?@3|f@TbH^DlAh zhQ}-U6kqLXM5_$fc(om-!p^afAzmw|C9c8|Ihkw zb{%4E1%P|U%1}97!`>Xj9SDUY zX%hg7^gBDSnrlLKgci;Y$?fe;u6PAy;RN|5;0lg+j4_dF2=H|lE!TE`mT}KwmRpnB zz?LnZ-Jq5HCd)KV1tq)0!BuV8N*C@s=+(@R%md57i)I&SMf_s(o?wN=5jRDbm^?_* zn%&1Q_=QlBb33JFw1PPV5it4^!V2g5<^@$1aU!n_9kJ>5Gn9|EKp+mz`LGg`C>>o} zKu*LfQ|L>I&!1Hq<=flaCsuZGs;{5gRwFBcDL0|>H_PvLJhndyw9rjd_cgzy+VMKF#XXV}^xw@s#Kw7AXgUq0IL8Xv!d8 z32U~;CZ9#O#Wis^wjL~vB98!OKBNU#BzHaKCnjS#mzAbS3%oOxc7ESVLcl55+;lzc zt7Q@XVO0lfbwc@^wegrImzhcUkk9B>bzqZ!zBGE+D}+$-*Pp12>2Mx({5;yJS%g)7 z_3*s{wF?38cZG`r!#gbxhIS$%?aF2FR zuD;_6JWzI%&9fFW9nP25=&rD{`0drzJ27&Zye1|}OaDDFFeyozMKrV`?;VIXRA_-2 z0)$%Q6tF7j*<;jfoUSpQ$O*Li6E5$EpLzDLVK@`v&BiB8hBORW!@ZPX<}-9NIm&Nb!`x zxhF*xjplgMrmGWpL4XkAG#bKKzsB1=F2h*X0454I-UjkZ*|RQbz4Y1da( zDqe==`D`bO*ic&=yiFt@5D_Km^0_R!)JmDq9VMb`$L&Ff`oKE2?x5j zW<+SXv!GWP!S5gX+r$sR{HHnRT3O9o>pun&AE{Edq*NAL0-nj43_R2nkB+wEeAn*L z!NA_RDE?x4$h;Vp@W`j7@Fd$xCfHOYW?}fIQ-Z8Ka=%Hu)hhiKTgipBYgNi{CMkRC zmjRb&qMV^W&ise5FgbljTgJpCF2Q-XvwW(DqcQH{N$y(#V!(v<#)@Mrh5Ny0WT}E} z$7^)k4guy%V4^~8!F$yb&3*qtbiSkSCCoju)GogMIJA9$$StckGEeh?@pS+-h!%~P z;uA8;slqGJ;3Jc@w|&la=O{mr;Ng2--oF=d-L{e$2*LPrsr{kucVjuDz+8niTr7ra zr$ow5m_5D~+~YxA)?f?N)^0{hoCu{c8s0XVd|Pp9!lzC9f^ep5pr{hics!e~v!NS3 zlV0lcwtM)_rlI%`CYEiYt?sNUrtqv@r&z2da5U|=$Br@sUvfWcscV9PTC4h=#4&e_ zT|uDr?YBb2jTKM}{vj*4-=G#7x5l6`s~c#7Iu&aOXXOseztNB7kvC~SylvZ*=zSD) z52uEogmV5Pd6DfFcI}4`nWWZQ-=6A4*_(Ksfclv17OwDf%-{3UcN9!6ZuzfMcvo~BqKbas#zHsLb z4P7pJ)Zsd4!Z7UPs#JI@Tuk^ex5>d5#a!s3A}W0+n5y&g%_nzl2ISD`RZRSyY2>VZ zB}vQ5>EZq*Cq~pO&6BZQ(AIu2U(m{4^TsT%!OINkcy0z0YiO_Df2+2*Z*|%InRRmU zUOlB+YC{*b^x%VQ8Kde!zmlmBx^@Eim8HAIc~Ml9M`*u_g3X&LmJ4E9sFnMe;iNIm z?SXl4)zdwita7vPOu3W20RPz%3?h_%Q)cE;7*9ChJ%tJScFwS@_F-J``({4Rx88E1 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + Create, configure, and manage WhatsApp Business senders via Twilio's + Channels Senders API. Covers programmatic sender registration, profile setup, + webhook configuration, sender lifecycle statuses, and ISV flows. Use this + skill to register and manage production WhatsApp senders at scale. +--- + +## Overview + +A WhatsApp sender is a phone number registered with WhatsApp Business through Twilio. Registration goes through a lifecycle of statuses before becoming `ONLINE`. Sandbox testing does not require a registered sender - see `twilio-whatsapp-send-message`. + +--- + +## Prerequisites + +- Upgraded Twilio account (trial accounts cannot register production senders) + - See `twilio-account-setup` for signup and upgrade steps +- A phone number capable of receiving SMS or voice verification +- Number must not already be registered with WhatsApp +- Environment variables: + - `TWILIO_ACCOUNT_SID` + - `TWILIO_AUTH_TOKEN` + - See `twilio-iam-auth-setup` for credential setup and best practices +- SDK: `pip install twilio` / `npm install twilio` + +--- + +## Quickstart + +Python +```python +import os +from twilio.rest import Client + +client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"]) + +# Step 1: Initiate registration +sender = client.messaging.v2.channels.senders.create( + sender_id="whatsapp:+15017122661", + verification_method="sms" +) +print(sender.sid) # XExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +print(sender.status) # CREATING + +# Step 2: Submit the OTP Twilio sends to the number +sender = client.messaging.v2.channels.senders(sender.sid).update( + verification_code="123456" +) +print(sender.status) # ONLINE (may pass through TWILIO_REVIEW first) +``` + +Node.js +```node +const twilio = require("twilio"); +const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); + +// Step 1: Initiate registration +const sender = await client.messaging.v2.channels.senders.create({ + senderId: "whatsapp:+15017122661", + verificationMethod: "sms", +}); +console.log(sender.sid, sender.status); + +// Step 2: Submit the OTP +const verified = await client.messaging.v2.channels.senders(sender.sid).update({ + verificationCode: "123456", +}); +console.log(verified.status); +``` + +--- + +## Sender Lifecycle Statuses + +| Status | Meaning | +|--------|---------| +| `CREATING` | Registration initiated | +| `PENDING_VERIFICATION` | Awaiting OTP submission | +| `VERIFYING` | OTP being validated | +| `TWILIO_REVIEW` | Under Twilio/Meta review | +| `ONLINE` | Active and ready to send | +| `OFFLINE` | Inactive - check `offlineReasons` | +| `DRAFT` | Incomplete registration | + +--- + +## Key Patterns + +### Set Business Profile + +Python +```python +sender = client.messaging.v2.channels.senders(SENDER_SID).update( + profile_name="Acme Support", + profile_about="Official support channel for Acme Corp", + profile_address="123 Main St, San Francisco, CA", + profile_vertical="PROFESSIONAL_SERVICES", + profile_logo_url="https://acme.com/logo.png", + profile_websites=["https://acme.com"] +) +``` + +Node.js +```node +const sender = await client.messaging.v2.channels.senders(SENDER_SID).update({ + profileName: "Acme Support", + profileAbout: "Official support channel for Acme Corp", + profileAddress: "123 Main St, San Francisco, CA", + profileVertical: "PROFESSIONAL_SERVICES", + profileLogoUrl: "https://acme.com/logo.png", + profileWebsites: ["https://acme.com"], +}); +``` + +### Configure Webhooks + +Python +```python +sender = client.messaging.v2.channels.senders(SENDER_SID).update( + callback_url="https://yourapp.com/whatsapp/inbound", + callback_method="POST", + status_callback_url="https://yourapp.com/whatsapp/status" +) +``` + +Node.js +```node +await client.messaging.v2.channels.senders(SENDER_SID).update({ + callbackUrl: "https://yourapp.com/whatsapp/inbound", + callbackMethod: "POST", + statusCallbackUrl: "https://yourapp.com/whatsapp/status", +}); +``` + +### Retrieve and List Senders + +Python +```python +sender = client.messaging.v2.channels.senders(SENDER_SID).fetch() +print(sender.status) + +for s in client.messaging.v2.channels.senders.list(): + print(s.sid, s.status) +``` + +Node.js +```node +const sender = await client.messaging.v2.channels.senders(SENDER_SID).fetch(); +const senders = await client.messaging.v2.channels.senders.list(); +senders.forEach(s => console.log(s.sid, s.status)); +``` + +If a sender is `OFFLINE`, check the `offlineReasons` array in the response (e.g. code `63020` means business hasn't accepted Twilio's Meta invitation). + +### Migrate an Existing WhatsApp Number + +If a number is already registered on WhatsApp (personal or business): +1. Check: `https://wa.me/?text=hi` +2. Delete the existing WhatsApp account on the device, or disable 2FA on the competing platform +3. Proceed with registration above + +### ISV / Tech Provider Flow + +Register senders under a client's WhatsApp Business Account (WABA): + +Python +```python +sender = client.messaging.v2.channels.senders.create( + sender_id="whatsapp:+15017122661", + waba_id="client-waba-id" +) +``` + +Node.js +```node +const sender = await client.messaging.v2.channels.senders.create({ + senderId: "whatsapp:+15017122661", + wabaId: "client-waba-id", +}); +``` + +The client must accept Twilio's invitation in their Meta Business Manager. + +--- + +## CANNOT + +- Cannot register a phone number to multiple WABAs - Each number belongs to one WABA at a time +- Cannot exceed 2 senders without Meta Business Verification - Unverified accounts are limited to 2 +- Cannot exceed 20 senders without exception - Verified Meta Business Manager: max 20 (50 with exception request) +- Cannot verify phone number only via SMS - Voice verification is available if SMS cannot be received + +--- + +## Next Steps + +- Send WhatsApp messages with this sender: `twilio-whatsapp-send-message` +- Create message templates: `twilio-content-template-builder` +- Send WhatsApp OTPs: `twilio-verify-send-otp` diff --git a/plugins/twilio/skills/twilio-whatsapp-manage-senders/assets/icon-large.png b/plugins/twilio/skills/twilio-whatsapp-manage-senders/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-whatsapp-manage-senders/assets/icon-small.png b/plugins/twilio/skills/twilio-whatsapp-manage-senders/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL + WhatsApp messaging deep-dive reference. Covers the 24-hour service + window rules (free-form vs template mode), sandbox setup for testing, + template approval workflow, production sender requirements, and + WhatsApp-specific error handling. For sending WhatsApp messages, use + twilio-send-message instead. Use this skill when setting up WhatsApp + for the first time or debugging WhatsApp-specific delivery behavior. +--- + +## Overview + +WhatsApp is one channel in Twilio's Messaging platform. All channels share the same `messages.create()` API - see `twilio-messaging-overview` for the full channel comparison and onboarding sequence. + +Twilio routes WhatsApp through the Programmable Messaging API - all numbers use `whatsapp:+E.164` prefix. Two sending modes apply: free-form (within 24 hrs of last inbound) and template (anytime). Sending free-form outside the window causes silent delivery failure - always check which mode is required. + +| Mode | When allowed | Parameters | +|------|-------------|------------| +| Free-form | Within 24 hrs of last inbound from user | `body`, optional `mediaUrl` | +| Template | Anytime | `contentSid` + `contentVariables` | + +--- + +## Prerequisites + +- Twilio account with WhatsApp enabled + - New to Twilio? See `twilio-account-setup` +- Environment variables: + - `TWILIO_ACCOUNT_SID` + - `TWILIO_AUTH_TOKEN` + - See `twilio-iam-auth-setup` for credential setup and best practices +- SDK: `pip install twilio` / `npm install twilio` +- Recipient opted in to receive messages from your WhatsApp Business Account + +Testing (sandbox): Join by texting `join ` to `+14155238886`. No registration needed - see [Console > Messaging > Try it out > Send a WhatsApp message](https://console.twilio.com/us1/develop/sms/try-it-out/whatsapp-learn). Sandbox participants must re-join every 3 days. + +Production: Register a WhatsApp Business sender first - see `twilio-whatsapp-manage-senders`. + +--- + +## Quickstart + +Python +```python +import os +from twilio.rest import Client + +client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"]) + +message = client.messages.create( + from_="whatsapp:+14155238886", # Sandbox sender (or your production number) + to="whatsapp:+15005550006", # Must have joined the sandbox + body="Your order has been confirmed." +) + +print(message.sid) # MMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +print(message.status) # queued | sent | delivered | failed +``` + +Node.js +```node +const twilio = require("twilio"); +const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); + +const message = await client.messages.create({ + from: "whatsapp:+14155238886", + to: "whatsapp:+15005550006", + body: "Your order has been confirmed.", +}); + +console.log(message.sid); +console.log(message.status); +``` + +--- + +## Key Patterns + +### Send a Template Message (outside service window) + +Templates are created in Console > Messaging > Content Template Builder and must be approved by Meta. See `twilio-content-template-builder` for template creation. + +Python +```python +message = client.messages.create( + from_="whatsapp:+14155238886", + to="whatsapp:+15005550006", + content_sid="HXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + content_variables='{"1": "March 25", "2": "2:00 PM"}' +) +``` + +Node.js +```node +const message = await client.messages.create({ + from: "whatsapp:+14155238886", + to: "whatsapp:+15005550006", + contentSid: "HXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + contentVariables: JSON.stringify({ "1": "March 25", "2": "2:00 PM" }), +}); +``` + +### Send Media (free-form only) + +Max file size: 16 MB. + +Python +```python +message = client.messages.create( + from_="whatsapp:+14155238886", + to="whatsapp:+15005550006", + body="Here is your invoice.", + media_url=["https://example.com/invoice.pdf"] +) +``` + +Node.js +```node +const message = await client.messages.create({ + from: "whatsapp:+14155238886", + to: "whatsapp:+15005550006", + body: "Here is your invoice.", + mediaUrl: ["https://example.com/invoice.pdf"], +}); +``` + +--- + +## Response Fields + +| Field | Description | +|-------|-------------| +| `sid` | Unique message identifier (`MM...`) | +| `status` | `queued`, `sent`, `delivered`, `read`, `failed`, `undelivered` | +| `error_code` | Populated on failure | +| `error_message` | Human-readable error description | +| `date_sent` | UTC timestamp | + +--- + +## Common Errors + +| Code | Meaning | Fix | +|------|---------|-----| +| 63003 | Invalid WhatsApp destination number | Verify number is WhatsApp-enabled and correctly formatted | +| 63018 | Rate limit exceeded on sender | Reduce send rate; default is 80 MPS | +| 63020 | Business hasn't accepted Twilio's Meta invitation | Accept invite in Meta Business Manager | +| N/A | Free-form outside window | Switch to a template message | + +--- + +## CANNOT + +- Cannot exceed 80 messages/second per sender - Text-only can be raised to 400 MPS on request +- Cannot queue messages beyond 4 hours - Undelivered messages fail after 4 hours +- Cannot exceed sandbox throttle limits - 1 message per 3 seconds, 50 messages/day on trial, participants expire after 3 days +- Cannot send without opt-in - Sending without recipient opt-in risks account suspension +- Cannot use WhatsApp Groups API - Deprecated April 2020. Use Conversations API instead. + +--- + +## Next Steps + +- Channel overview and onboarding guide: `twilio-messaging-overview` +- Register a production WhatsApp sender: `twilio-whatsapp-manage-senders` +- Create and manage message templates: `twilio-content-template-builder` +- Multi-channel conversations with history: `twilio-conversations-classic-api` diff --git a/plugins/twilio/skills/twilio-whatsapp-send-message/assets/icon-large.png b/plugins/twilio/skills/twilio-whatsapp-send-message/assets/icon-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5b474f80d9a47e9562d265db6faf6f5324072d5e GIT binary patch literal 9590 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 VWF*080F6{2q_%<92MycE{{!55)wuuw literal 0 HcmV?d00001 diff --git a/plugins/twilio/skills/twilio-whatsapp-send-message/assets/icon-small.png b/plugins/twilio/skills/twilio-whatsapp-send-message/assets/icon-small.png new file mode 100644 index 0000000000000000000000000000000000000000..d138475624b6b823fe8b3450bb59e0ef819290e0 GIT binary patch literal 5559 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 pwXymEt{P65@+MVtDr3){yUgDYCS-My)&DL Date: Wed, 17 Jun 2026 19:34:14 -0700 Subject: [PATCH 2/3] fix: remove extra twilio rule primitive --- plugins/twilio/README.md | 2 +- plugins/twilio/index.ts | 15 +-------------- plugins/twilio/package.json | 3 +-- 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/plugins/twilio/README.md b/plugins/twilio/README.md index 226394c3..e9c08010 100644 --- a/plugins/twilio/README.md +++ b/plugins/twilio/README.md @@ -6,7 +6,7 @@ Twilio bundles workflow skills for building with Twilio and SendGrid, plus the p - 55 Twilio and SendGrid skills covering SMS, WhatsApp, RCS, Voice, Verify, Lookup, Messaging Services, regulatory onboarding, webhooks, Conversation Orchestrator, Conversation Intelligence, Customer Memory, SendGrid sending, deliverability, inbound parse, suppressions, and event webhooks. - `twilio-docs`, a streamable HTTP MCP server at `https://mcp.twilio.com/docs` for semantic Twilio documentation search and API operation retrieval. The docs MCP does not require authentication. -- A safety rule for live messaging, email, voice, verification, compliance, and customer-data workflows. +- Bundled guidance for live messaging, email, voice, verification, compliance, and customer-data workflows. ## Requirements diff --git a/plugins/twilio/index.ts b/plugins/twilio/index.ts index a1c1fd02..0b92909a 100644 --- a/plugins/twilio/index.ts +++ b/plugins/twilio/index.ts @@ -1,16 +1,9 @@ import type { AgentPlugin } from "@cline/sdk" -const twilioSafetyRule = [ - "Twilio and SendGrid workflows can send messages and email, place calls, verify users, change account configuration, handle personal data, and affect production traffic.", - "Do not send SMS, WhatsApp, RCS, MMS, email, voice calls, OTPs, webhooks, or production traffic, change compliance or registration settings, buy or release numbers, rotate credentials, mutate account resources, or delete customer data without explicit user approval.", - "Treat TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, Twilio API keys and secrets, SENDGRID_API_KEY, phone numbers, email addresses, message bodies, recordings, transcripts, verification codes, webhook payloads, and customer data as sensitive.", - "Before implementing messaging, voice, email, or verification workflows, confirm consent, destination, region, environment, sender identity, expected cost, and applicable regulatory or compliance requirements.", -].join("\n") - const plugin: AgentPlugin = { name: "twilio", manifest: { - capabilities: ["mcp", "skills", "rules"], + capabilities: ["mcp", "skills"], }, setup(api) { api.registerMcpServer({ @@ -20,12 +13,6 @@ const plugin: AgentPlugin = { url: "https://mcp.twilio.com/docs", }, }) - - api.registerRule({ - id: "twilio-safety", - source: "twilio", - content: twilioSafetyRule, - }) }, } diff --git a/plugins/twilio/package.json b/plugins/twilio/package.json index b0a30cba..dcf7d36e 100644 --- a/plugins/twilio/package.json +++ b/plugins/twilio/package.json @@ -12,8 +12,7 @@ ], "capabilities": [ "mcp", - "skills", - "rules" + "skills" ] } ] From 7ce0dd9c04c9fa370ddf689f07ef5539180dd30c Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Thu, 18 Jun 2026 16:31:08 -0700 Subject: [PATCH 3/3] docs: remove stale rules wording --- plugins/twilio/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/twilio/README.md b/plugins/twilio/README.md index e9c08010..51eee408 100644 --- a/plugins/twilio/README.md +++ b/plugins/twilio/README.md @@ -6,7 +6,7 @@ Twilio bundles workflow skills for building with Twilio and SendGrid, plus the p - 55 Twilio and SendGrid skills covering SMS, WhatsApp, RCS, Voice, Verify, Lookup, Messaging Services, regulatory onboarding, webhooks, Conversation Orchestrator, Conversation Intelligence, Customer Memory, SendGrid sending, deliverability, inbound parse, suppressions, and event webhooks. - `twilio-docs`, a streamable HTTP MCP server at `https://mcp.twilio.com/docs` for semantic Twilio documentation search and API operation retrieval. The docs MCP does not require authentication. -- Bundled guidance for live messaging, email, voice, verification, compliance, and customer-data workflows. +- Bundled skills cover live messaging, email, voice, verification, compliance, and customer-data workflows. ## Requirements