migrate-to-sent
sentdm/sent-plugin
Plan and execute safe migration from Twilio, Sinch, Infobip, Vonage, or MessageBird to Sent v3.
What is migrate-to-sent?
Guides migration from incumbent CPaaS providers to Sent by mapping send calls, status vocabularies, webhook signatures, opt-out stores, templates, and tenancy models. Use when replacing a provider, translating provider code to Sent, or planning a phased cutover with verification gates and rollback.
- Maps send call patterns and ordered fallback arrays to Sent's automatic routing model
- Translates provider status vocabularies to Sent's nine-state model with compliance-safe retry logic
- Rewrites webhook signature verification from provider-specific schemes to Sent's HMAC-SHA256 format
- Reconciles incumbent suppression lists into Sent's channel-agnostic opt-out model
- Re-registers WhatsApp templates with named parameters and handles asynchronous approval
- Plans phased cutover sequence with dual-run traffic split, delivery verification, and rollback triggers
How to install migrate-to-sent
npx skills add https://github.com/sentdm/sent-plugin --skill migrate-to-sentHow to use migrate-to-sent
- 1.Run `scripts/inventory_scan.py` to mechanically find all send call sites, webhook handlers, status branches, templates, and credentials
- 2.Use `references/provider-mapping.md` to map each incumbent construct to Sent equivalents, flagging ordered-fallback arrays and numeric error codes as required rewrites
- 3.Stand up Sent in parallel: provision credentials, create one webhook per environment, build and verify the receiver, re-register templates and await approval
- 4.Prove equivalence in sandbox with `"sandbox": true`, then with a small live cohort confirmed to `DELIVERED` status
- 5.Execute dual-run with traffic split, comparing delivery rates, latency, and cost per message on the same message classes
- 6.Cut over by message class (transactional first, marketing last) while keeping the incumbent receiver live for rollback
- 7.Decommission only after a full billing cycle of clean data, then revoke incumbent credentials
Use cases
- Replacing Twilio with Sent: port send calls, rewrite webhook handlers, migrate opt-out lists, and cut over by message class
- Migrating from Sinch or Infobip: map status codes, rebuild signature verification, re-register templates, and validate delivery rates in sandbox
- Planning a phased cutover: inventory all send sites and handlers, stand up Sent in parallel, dual-run with traffic split, then decommission incumbent by billing cycle
- Translating provider-specific error handling: convert numeric error codes to Sent's string error families and fix compliance issues like retrying FILTERED (opt-out) messages
- Consolidating multi-provider tenancy: map subaccounts, messaging services, or applications to Sent Sender Profiles with scoped credentials
- Backend engineers executing provider migrations
- Platform teams managing multi-tenant CPaaS infrastructure
- Compliance and operations leads verifying cutover safety
- Integration architects designing phased rollouts
migrate-to-sent FAQ
Sent's `channel` array is a broadcast list that sends one message per recipient-channel pair. Ordered fallback arrays must be translated to automatic routing (omit `channel` or send `["sent"]`) to let Sent select and reroute across up to three channel-and-provider pairs on the same `message_id`.
`ROUTED` (route chosen; fires again on reroute) and `SCHEDULED` (quiet-hours parking; resumes automatically). Handlers that treat every non-delivered state as retryable will retry `FILTERED` (consent block) and `BLOCKED` (account precondition), causing compliance failures.
Sent uses `x-webhook-signature: v1,{base64}` with HMAC-SHA256 over `{x-webhook-id}.{x-webhook-timestamp}.{raw_body}`, constant-time comparison, and 300-second timestamp validation. No two incumbent providers sign the same way; rewrite the receiver rather than adapting the old verifier.
Export the incumbent's suppression list before cutover, treat any opt-out on any incumbent channel as a global Sent opt-out, and never clear `opt_out`. Sent applies opt-out channel-agnostically: a `STOP` on SMS suppresses WhatsApp and RCS too. Custom keywords need custom keyword entries; do not rewrite consent to Sent.
Positional placeholders (`{{1}}`, `{{2}}`) become named parameters in Sent, so every call site must pass a named map instead of an ordered array. Approval is asynchronous via `templates` webhook event; build the template inventory before cutover.
Full instructions (SKILL.md)
Source of truth, from sentdm/sent-plugin.
name: migrate-to-sent description: Plans and executes a migration from Twilio, Sinch, Infobip, Vonage, or MessageBird/Bird to Sent v3 — mapping send calls, status vocabularies, webhook signature schemes, opt-out stores, templates, and tenancy models, then cutting over safely with dual-run and rollback. Use when replacing an incumbent CPaaS provider, translating provider code or webhook handlers to Sent, or planning a phased cutover and its verification gates.
Migrate to Sent
Every migration from a major CPaaS provider hits the same five translation problems. Work them in this order, because the first one silently doubles cost and is invisible in tests.
1. Ordered fallback becomes automatic routing
Incumbent platforms express cross-channel delivery through different caller-side arrays, failover objects, messaging-service features, or application-level priority configuration. Do not assume those shapes have a direct Sent request-field equivalent.
Sent's channel array is a broadcast list. Porting an ordered array produces one message and one charge per recipient-channel pair, which passes tests and multiplies production spend. The correct translation is automatic routing — omit channel or send ["sent"] — which lets the platform select a route and reroute across up to three channel-and-provider pairs on the same message_id. Details belong to sent-routing-strategist; the migration rule is simply: never port an ordered channel list.
2. Status vocabularies do not line up
Incumbent statuses map onto Sent's, but Sent adds two states that have no equivalent and that break naive retry logic.
| Sent status | Closest incumbent analogue | Migration note |
|---|---|---|
QUEUED | Twilio queued, Sinch QUEUED_ON_CHANNEL | Accepted, not sent |
ROUTED | no analogue | Route chosen; fires again on reroute |
SENT | Twilio sent, Sinch MESSAGE_SUBMIT | Provider handoff only |
DELIVERED | delivered everywhere | The first proof of handset receipt |
READ | Twilio read, Sinch READ | WhatsApp and RCS only |
FAILED | failed, undelivered | May still reroute; not necessarily final |
FILTERED | Twilio error 21610 (opt-out) | Policy gate. Never retry |
BLOCKED | account-level errors | Account precondition. Fix the account, then resend |
SCHEDULED | no analogue | Quiet-hours parking; resumes automatically |
Two consequences for ported code. Handlers that treat every non-delivered terminal state as retryable will retry consent blocks, which is a compliance failure rather than a bug. And handlers keyed on numeric provider error codes — Twilio's 21610 is the classic — must be rewritten against Sent's string error.code families.
3. Webhook verification is a rewrite, not a port
No two providers sign the same way, and no Sent SDK ships a verifier.
| Provider | Scheme |
|---|---|
| Twilio | X-Twilio-Signature, base64 HMAC-SHA1 over the full URL plus sorted POST parameters |
| Sinch | HMAC-SHA256 over body.nonce.timestamp, four x-sinch-webhook-signature* headers, or OAuth 2.0 |
| Infobip | Basic, HMAC-SHA256 over the raw body, or OAuth on a notification profile; the header name is account-configured |
| Vonage | JWT in Authorization: Bearer, or a legacy sig parameter |
| MessageBird/Bird | messagebird-signature, base64 HMAC-SHA256 over timestamp, URL, and a SHA-256 body hash |
| Sent | x-webhook-signature: v1,{base64}, HMAC-SHA256 over {x-webhook-id}.{x-webhook-timestamp}.{raw_body} |
Sent's key is the signing secret with whsec_ stripped and the remainder base64-decoded, compared in constant time, with timestamps outside 300 seconds rejected. Because Sent provides no per-event id, dedupe keys must be derived from payload semantics. Build the receiver with sent-webhook-engineer rather than adapting the incumbent's verifier.
4. Opt-out stores must be reconciled, not migrated by copy
Every provider keeps its own suppression list — Twilio Advanced Opt-Out, Infobip Blocklist, Sinch OPT_IN/OPT_OUT events. Sent enforces consent at the platform level before events reach the application, stores it as opt_out on the contact, and applies it channel-agnostically: a STOP on SMS suppresses WhatsApp and RCS too.
Reconciliation rules: export the incumbent's suppression list before cutover, treat any opt-out on any incumbent channel as a global Sent opt-out, and never clear opt_out to "clean up" migrated data. Sent's ten default keywords are STOP, CANCEL, UNSUBSCRIBE, QUIT, END, START, UNSTOP, SUBSCRIBE, HELP, INFO, matched only when the entire trimmed body equals the keyword — so incumbent-specific keywords need custom keyword entries. Rewrite any incumbent keyword matcher as an exact local consent mirror and audit mechanism; the matcher must not write consent to Sent again. Consent semantics belong to sent-two-way-messaging.
5. Templates and tenancy are re-registered, not transferred
WhatsApp templates live with the WABA, so the migration question is whether the WABA moves. Positional placeholders ({{1}}, {{2}}) become named parameters in Sent, which means every call site that passed an ordered array must pass a named map. Approval is asynchronous and arrives as a templates webhook event, so build the template inventory before cutover rather than during it.
Tenancy maps as follows, with the boundary decision owned by sender-profile-architect and the API work by sent-profile-provisioning:
| Incumbent construct | Sent equivalent |
|---|---|
| Twilio subaccount | Sender Profile |
| Twilio Messaging Service | routing plus profile configuration, not a caller-side pool |
| Infobip Application or Entity | Sender Profile |
| Sinch Conversation API app | Sender Profile |
| Provider API credential per tenant | Profile-scoped API key, or organization key with x-profile-id |
Migration sequence
- Inventory every send call site, webhook handler, status branch, template, suppression list, and credential. Use
scripts/inventory_scan.pyto find them mechanically. - Map each item using references/provider-mapping.md, flagging ordered-fallback arrays and numeric error codes as required rewrites.
- Stand up Sent in parallel: credentials, one webhook per environment, verified receiver, templates re-registered and approved.
- Prove equivalence in sandbox with
"sandbox": true, then with a small live cohort confirmed toDELIVERED. - Dual-run with a traffic split, comparing delivery rates, latency, and cost per message on the same message classes.
- Cut over by message class — lowest-risk transactional first, marketing last — keeping the incumbent receiver live.
- Decommission only after a full billing cycle of clean data, then revoke incumbent credentials.
Sequencing detail, verification gates, and rollback triggers are in references/cutover-playbook.md.
Mistakes that survive testing
- Porting an ordered channel array. Doubles cost, never errors.
- Treating
FILTEREDas retryable. Compliance exposure. - Reusing the incumbent's signature verifier. Every delivery returns 401.
- Assuming
202means delivered. Sent acknowledges acceptance only. - Keeping positional template placeholders. Parameters silently mismatch.
- Retrying on
401. Ten consecutive auth failures lock the credential with escalating lockout. - Omitting
Idempotency-Keyduring dual-run. A timeout retry sends twice. - Sending
x-profile-idwith a profile-scoped key. Returns403. - Copying an incumbent's
Authorization: Bearerpattern. Sent authenticates withx-api-key.
Boundaries
This skill owns provider mapping and line-by-line migration planning. Hand the resulting Sent client and resilience work to sent-integration-starter, channel semantics to sent-routing-strategist, receiver construction to sent-webhook-engineer, WhatsApp onboarding to waba-embedded-signup, and US campaign registration to sms-10dlc-registration.
Related skills
More from sentdm/sent-plugin and the wider catalog.

rcs-agent-onboarding
Guide RCS and RBM onboarding, carrier approval, templates, and safe routing for Sent messaging.

sender-profile-architect
Design multi-tenant Sender Profile architecture for Sent messaging systems with isolation, billing, and credential scoping.

sent
Routes ambiguous Sent messaging requests to the correct specialist skill or MCP operation.

sent-account-readiness
Verify Sent account authorization, balance, and onboarding readiness before sending.

sent-analytics
Query Sent messaging analytics: volume, deliverability, contacts, and number capabilities.

sent-contacts
Manage Sent contacts: search, create, delete, and review messaging summaries with confirmation gates.