migrate-to-sent

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

By sentdm · 2,583 installs

npx skills add sentdm/sent-plugin --skill migrate-to-sent

Source repository · Upstream listing

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 1. Inventory every send call site, webhook handler, status branch, template, suppression list, and credential. Use scripts/inventory scan.py to find them mechanically. 2. Map each item using [references/provider mapping.md](references/provider mapping.md), flagging ordered fallback arrays and numeric error codes as required rewrites. 3. Stand up Sent in parallel : credentials, one webhook per environment, verified receiver, templates re registered and approved. 4. Prove equivalence in sandbox with "sandbox": true , then with a small live cohort confirmed to DELIVERED . 5. Dual run with a traffic split, comparing delivery rates, latency, and cost per message on the same message classes. 6. Cut over by message class — lowest risk transactional first, marketing last — keeping the incumbent receiver live. 7. 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](references/cutover playbook.md). Mistakes that survive testing Porting an ordered channel array. Doubles cost, never errors. Treating FILTERED as retryable. Compliance exposure. Reusing the incumbent's signature verifier. Every delivery returns 401. Assuming 202 means 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 Key during dual run. A timeout retry sends twice. Sending x profile id with a profile scoped key. Returns 403 . Copying an incumbent's Authorization: Bearer pattern. Sent authenticates with x 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 .