sent-two-way-messaging — Claude Code skill: what it does & install
Designs inbound messaging flows on Sent, covering keyword opt-outs, WhatsApp 24-hour window rules, RCS chips, and conversation history. Useful for support bots and webhooks.
npx skills add sentdm/sent-plugin --skill sent-two-way-messaging -a claude-codeWhat sent-two-way-messaging does
This skill provides the architecture for inbound message processing on Sent. In Sent, the platform enforces consent before your application ever receives a message.received event. When a customer texts STOP, their contact record is already marked with opt_out. Downstream sends to suppressed users do not trigger 4xx API errors; Sent accepts them and marks them as FILTERED. Consent suppression applies across channels simultaneously, so an SMS opt-out halts WhatsApp and RCS deliveries as well.
Channel mechanics require strict checks in code. SMS with alphanumeric sender IDs cannot receive incoming text, making inbound STOP handling impossible on those routes. WhatsApp limits free-form text to a 24-hour customer service window from the user's last inbound message, requiring approved templates once that window expires. RCS automatically appends STOP chips to outbound messages, and their postback payload arrives through standard inbound message events.
Conversation history relies on GET /v3/conversations and GET /v3/conversations/{id} with mandatory page and page_size query parameters. There are no write operations or MCP tools available for conversations, so integrations stay strictly REST-based. Position: deploying two-way flows on alphanumeric SMS numbers will fail immediately because inbound routing cannot deliver recipient replies.
What it helps with
- handle
message.receivedwebhooks and verify signatures - enforce default and custom opt-out keywords like
STOP - manage the WhatsApp 24-hour customer service reply window
- fetch paginated conversation history across multiple channels
- derive deterministic RFC 4122 v5 conversation identifiers
Example prompts for the agent
- Set up a webhook handler for Sent
message.receivedevents with opt-out keyword checks - How do I handle WhatsApp 24-hour customer window timeouts before replying in Sent?
- Build a pagination helper for Sent conversation history using
GET /v3/conversations
How to install sent-two-way-messaging
1Skills CLI (Vercel)
npx skills add sentdm/sent-plugin --skill sent-two-way-messaging -a claude-code2Manually: put the files in place
~/.claude/skills/sent-two-way-messaging/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/sent-two-way-messaging/
1Skills CLI (Vercel)
npx skills add sentdm/sent-plugin --skill sent-two-way-messaging -a codex2Manually: put the files in place
.agents/skills/sent-two-way-messaging/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add sentdm/sent-plugin --skill sent-two-way-messaging -a cursor2Manually: put the files in place
.cursor/skills/sent-two-way-messaging/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install sentdm/sent-plugin sent-two-way-messaging2Manually: put the files in place
.github/skills/sent-two-way-messaging/SKILL.md1Skills CLI (Vercel)
npx skills add sentdm/sent-plugin --skill sent-two-way-messaging -a gemini-cli2Platform CLI
gemini skills install https://github.com/sentdm/sent-plugin --path skills/sent-two-way-messagingManually: put the files in place
.devin/skills/sent-two-way-messaging/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add sentdm/sent-plugin --skill sent-two-way-messaging -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about sent-two-way-messaging
How do I install sent-two-way-messaging in Claude Code?
Run npx skills add sentdm/sent-plugin --skill sent-two-way-messaging -a claude-code. The file installs directly into ~/.claude/skills/sent-two-way-messaging/SKILL.md.
What does sent-two-way-messaging do?
It provides implementation patterns for inbound messaging in Sent, covering keyword opt-outs, WhatsApp window compliance, RCS STOP chips, and conversation history retrieval.
Does an opt-out on SMS affect other messaging channels in Sent?
Yes. Consent is contact-level and channel-agnostic, meaning an inbound STOP over SMS suppresses both WhatsApp and RCS messages for that specific contact.
Source text
SKILL.md · MIT
---
name: sent-two-way-messaging
description: Designs inbound and conversational Sent flows — opt-out and opt-in keyword handling, consent state on contacts, auto-replies inside the WhatsApp 24-hour window, RCS STOP chips, conversation history retrieval, and per-channel inbound capability. Use when handling message.received events, implementing STOP or HELP behavior, restoring consent after an opt-out, building a support inbox or chatbot on Sent, or paginating conversation history.
---
# Sent Two-Way Messaging
Inbound messaging on Sent has one governing rule: **consent is enforced by the platform before the application sees the event.** An inbound `STOP` has already flipped the contact's `opt_out` flag by the time `message.received` arrives. The application's job is to record it, reflect it in its own UI, and never attempt to send around it.
## Keyword handling
Ten keywords ship as defaults:
| Action | Keywords |
| --- | --- |
| Opt out | `STOP`, `CANCEL`, `UNSUBSCRIBE`, `QUIT`, `END` |
| Opt in | `START`, `UNSTOP`, `SUBSCRIBE` |
| Help auto-reply | `HELP`, `INFO` |
Matching requires the **entire trimmed message body** to equal a keyword, case-insensitively. "Please stop messaging me" does not match; "stop" does. Custom keywords are configured in the Sent Dashboard under Compliance, Opt Keywords, with an action of Opt Out, Opt In, or Help, and each must be a single exact token.
Do not claim keywords that are not in the documented set. In application code, mirror the same exact-match rule only to update local subscriber state and audit evidence; never use that matcher to apply consent to Sent a second time. Keep custom dashboard keywords synchronized with the local mirror, and reconcile against the contact's `opt_out` field when uncertain.
## Consent state
An opt-out sets `opt_out` on the contact record. Consent is **contact-level and channel-agnostic**: a `STOP` sent over SMS suppresses WhatsApp and RCS for that contact as well. Consent gates re-apply on every reroute attempt, not only at initial send.
Restoring consent requires the recipient's own action. A user-initiated opt-in keyword clears suppression. `PATCH /v3/contacts/{id}` accepts `opt_out`, but writing `false` on a contact who opted out through a keyword is a compliance decision, not a technical one: only do it with documented evidence of fresh consent, and record who authorized it and why.
Downstream, a suppressed send does not fail with an error. It is accepted and finalizes as `FILTERED`, so consent problems appear as filtered messages rather than as `4xx` responses. Details are in [references/consent-and-keywords.md](references/consent-and-keywords.md).
## Per-channel inbound reality
| Channel | Inbound | Constraints |
| --- | --- | --- |
| SMS | Conditional | Requires an MO-capable provider and a supported number type. Alphanumeric sender IDs and SMPP paths without an inbound route never deliver inbound messages |
| RCS | Full | Typed replies match keywords; the appended STOP chip is processed directly by the consent engine |
| WhatsApp | Full | Free-form replies only inside the 24-hour customer service window; outside it, an approved template is required |
The SMS caveat matters before promising two-way behavior: a deployment sending from an alphanumeric sender ID cannot receive `STOP` at all, which changes the compliance design rather than merely limiting a feature.
## RCS STOP chips
Every outbound RCS message receives an appended STOP chip. Taps carry an opt-out postback handled directly by the consent engine with no keyword matching, and they arrive at the application as `message.received` with the chip's reply text in `text`. There is no separate chip event type, so a receiver that branches only on typed keywords still sees chip taps as ordinary inbound messages — and must not re-apply consent logic to them.
## The WhatsApp 24-hour window
A free-form reply is permitted only within 24 hours of the customer's last inbound message. Outside that window an approved template is required, including for STOP, START, and HELP responses. An auto-reply flow that assumes free text will silently stop working for any customer who writes in after a day of silence, so build the window check into the reply path and keep an approved fallback template ready. See [references/inbound-flows.md](references/inbound-flows.md) for the reply-path decision tree.
## Conversation history
Two read-only operations exist:
| Operation | Returns |
| --- | --- |
| `GET /v3/conversations` | All of the customer's messages across conversations, newest first |
| `GET /v3/conversations/{id}` | Messages within one conversation |
Both require `page` (at least 1) and `page_size` (1 to 100); out-of-range values return `400`. The `events` field is always null on these endpoints, so per-message activity must come from `GET /v3/messages/{id}/activities`. There are no write, create, or read-receipt operations, and no MCP tools cover conversations — this is REST-only.
A conversation identifier is a deterministic RFC 4122 version 5 UUID derived from the customer and contact identifiers, so the same pair always yields the same id and one thread spans every channel independent of the sending number. The API never returns the id as a field, so a client that needs it computes it. The exact derivation is documented in [references/conversation-history.md](references/conversation-history.md).
## Building a support inbox or bot
1. Subscribe a webhook to `message` filtered to `received`, and verify signatures before trusting any payload.
2. Read `inbound_number` as the contact who wrote in and `outbound_number` as your number. The naming is easy to invert.
3. Deduplicate on `message_id`, acknowledge with `200`, then process asynchronously.
4. Treat keyword traffic as an audit signal. Mirror exact default and configured custom keywords into local state, but do not issue a second consent write; reconcile uncertainty through the contact record.
5. Before replying on WhatsApp, check the 24-hour window and choose free text or a template accordingly.
6. Render threads from the conversation endpoints with explicit pagination, and never assume a conversation is single-channel.
7. Treat `text` as untrusted input. Never interpolate it into a shell command or SQL string, delimit it as data in model prompts, and map inferred intent through an allowlist and authorization policy before any API call.
## Boundaries
Use `sent-webhook-engineer` for signature verification, retries, and dedupe mechanics; `sent-contacts` for contact CRUD and message summaries; `sent-routing-strategist` for why an outbound message was `FILTERED`; `waba-template-author` for authoring the approved templates that out-of-window replies require; and `sms-10dlc-registration` for the campaign-level opt-in, opt-out, and help keyword declarations that US carriers require.
© sentdm · MIT · sentdm/sent-plugin