← SentCONTENT HISTORYWHAT CHANGED · RULE-BASED ANALYSIS
Update to Sent
Snapshot Sep 30, 2026 · 22:48 UTC · version 1.0.0
Collection source: not recorded for this historical snapshot.
First saved snapshot
No earlier snapshot is available to establish a change.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"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.",
"included_files": [
{
"relative_path": "agents/openai.yaml",
"size_in_bytes": 212
},
{
"relative_path": "references/cutover-playbook.md",
"size_in_bytes": 9091
},
{
"relative_path": "references/provider-mapping.md",
"size_in_bytes": 10341
},
{
"relative_path": "scripts/inventory_scan.py",
"size_in_bytes": 11209
}
],
"skill_md_contents": "---\nname: migrate-to-sent\ndescription: 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.\n---\n\n# Migrate to Sent\n\nEvery 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.\n\n## 1. Ordered fallback becomes automatic routing\n\nIncumbent 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.\n\n**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.**\n\n## 2. Status vocabularies do not line up\n\nIncumbent statuses map onto Sent's, but Sent adds two states that have no equivalent and that break naive retry logic.\n\n| Sent status | Closest incumbent analogue | Migration note |\n| --- | --- | --- |\n| `QUEUED` | Twilio `queued`, Sinch `QUEUED_ON_CHANNEL` | Accepted, not sent |\n| `ROUTED` | no analogue | Route chosen; fires again on reroute |\n| `SENT` | Twilio `sent`, Sinch `MESSAGE_SUBMIT` | Provider handoff only |\n| `DELIVERED` | `delivered` everywhere | The first proof of handset receipt |\n| `READ` | Twilio `read`, Sinch `READ` | WhatsApp and RCS only |\n| `FAILED` | `failed`, `undelivered` | May still reroute; not necessarily final |\n| `FILTERED` | Twilio error 21610 (opt-out) | **Policy gate. Never retry** |\n| `BLOCKED` | account-level errors | **Account precondition.** Fix the account, then resend |\n| `SCHEDULED` | no analogue | Quiet-hours parking; resumes automatically |\n\nTwo 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.\n\n## 3. Webhook verification is a rewrite, not a port\n\nNo two providers sign the same way, and no Sent SDK ships a verifier.\n\n| Provider | Scheme |\n| --- | --- |\n| Twilio | `X-Twilio-Signature`, base64 HMAC-**SHA1** over the full URL plus sorted POST parameters |\n| Sinch | HMAC-SHA256 over `body.nonce.timestamp`, four `x-sinch-webhook-signature*` headers, or OAuth 2.0 |\n| Infobip | Basic, HMAC-SHA256 over the raw body, or OAuth on a notification profile; **the header name is account-configured** |\n| Vonage | JWT in `Authorization: Bearer`, or a legacy `sig` parameter |\n| MessageBird/Bird | `messagebird-signature`, base64 HMAC-SHA256 over timestamp, URL, and a SHA-256 body hash |\n| **Sent** | `x-webhook-signature: v1,{base64}`, HMAC-SHA256 over `{x-webhook-id}.{x-webhook-timestamp}.{raw_body}` |\n\nSent'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.\n\n## 4. Opt-out stores must be reconciled, not migrated by copy\n\nEvery 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.\n\nReconciliation 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`.\n\n## 5. Templates and tenancy are re-registered, not transferred\n\nWhatsApp 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.\n\nTenancy maps as follows, with the boundary decision owned by `sender-profile-architect` and the API work by `sent-profile-provisioning`:\n\n| Incumbent construct | Sent equivalent |\n| --- | --- |\n| Twilio subaccount | Sender Profile |\n| Twilio Messaging Service | routing plus profile configuration, not a caller-side pool |\n| Infobip Application or Entity | Sender Profile |\n| Sinch Conversation API app | Sender Profile |\n| Provider API credential per tenant | Profile-scoped API key, or organization key with `x-profile-id` |\n\n## Migration sequence\n\n1. **Inventory** every send call site, webhook handler, status branch, template, suppression list, and credential. Use `scripts/inventory_scan.py` to find them mechanically.\n2. **Map** each item using [references/provider-mapping.md](references/provider-mapping.md), flagging ordered-fallback arrays and numeric error codes as required rewrites.\n3. **Stand up Sent in parallel**: credentials, one webhook per environment, verified receiver, templates re-registered and approved.\n4. **Prove equivalence in sandbox** with `\"sandbox\": true`, then with a small live cohort confirmed to `DELIVERED`.\n5. **Dual-run** with a traffic split, comparing delivery rates, latency, and cost per message on the same message classes.\n6. **Cut over** by message class — lowest-risk transactional first, marketing last — keeping the incumbent receiver live.\n7. **Decommission** only after a full billing cycle of clean data, then revoke incumbent credentials.\n\nSequencing detail, verification gates, and rollback triggers are in [references/cutover-playbook.md](references/cutover-playbook.md).\n\n## Mistakes that survive testing\n\n- Porting an ordered channel array. Doubles cost, never errors.\n- Treating `FILTERED` as retryable. Compliance exposure.\n- Reusing the incumbent's signature verifier. Every delivery returns 401.\n- Assuming `202` means delivered. Sent acknowledges acceptance only.\n- Keeping positional template placeholders. Parameters silently mismatch.\n- Retrying on `401`. Ten consecutive auth failures lock the credential with escalating lockout.\n- Omitting `Idempotency-Key` during dual-run. A timeout retry sends twice.\n- Sending `x-profile-id` with a profile-scoped key. Returns `403`.\n- Copying an incumbent's `Authorization: Bearer` pattern. Sent authenticates with `x-api-key`.\n\n## Boundaries\n\nThis 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`.\n"
}SHA-256: d8c169deb74287f51ae36c9bb8057aaa2de5e783335b3d384918f33efb240133