Skip to main content
skill CX Codex

sent-integration-starter — Claude Code skill: setup and overview

Sets up a production-ready Sent v3 integration: SDK setup, idempotent requests, webhook verification, and retry policies. Useful when adding Sent to an app or handling 429 and 409 errors.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add sentdm/sent-plugin --skill sent-integration-starter -a claude-code
Open on GitHub

What sent-integration-starter does

The skill organizes Sent v3 integration into four stages: client setup, idempotent sending, webhook verification, and hardening. It covers SDK configuration for TypeScript and Python. REST calls to api.sent.dm require an x-api-key header. Passing x-profile-id with a profile-scoped key triggers a 403 error. Configuration must fail fast at boot if credentials are missing.

Outbound mutations require an Idempotency-Key header between 1 and 255 characters. Sent caches this key for 24 hours. A concurrent duplicate in flight pauses up to five seconds before returning 409 CONFLICT_001. The API returns 202 on send, which confirms acceptance rather than delivery. This is a reason to persist message_id right away in application storage.

No Sent SDK includes a built-in webhook verifier. The skill guides HMAC-SHA256 verification using the stripped secret and rejects timestamps older than 300 seconds. Ten delivery failures disable the webhook URL. Standard rate limits allow 200 requests per minute on a sliding window. Because rate-limit headers appear only on 429 responses, callers must pace traffic proactively instead of reacting to headers.

What it helps with

  • authenticate Sent v3 REST requests with x-api-key
  • prevent duplicate message dispatches using Idempotency-Key
  • verify incoming webhook signatures with HMAC-SHA256
  • configure retry policies for 429 and 409 CONFLICT_001
  • map application logic to the 46-code error catalog

Example prompts for the agent

  • Help me set up the @sentdm/sentdm client and configure Idempotency-Key headers
  • Build a webhook receiver for Sent with HMAC-SHA256 verification and deduplication
  • Configure retry logic for Sent 429 and 409 responses according to the specification

How to install sent-integration-starter

1Skills CLI (Vercel)

npx skills add sentdm/sent-plugin --skill sent-integration-starter -a claude-code

2Manually: put the files in place

~/.claude/skills/sent-integration-starter/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/sent-integration-starter/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about sent-integration-starter

How to install sent-integration-starter in Claude Code?

Run npx skills add sentdm/sent-plugin --skill sent-integration-starter -a claude-code in your terminal.

What does the sent-integration-starter skill do?

It guides the Sent v3 integration through four stages: client credentials, idempotent message sends, HMAC-SHA256 webhook verification, and hardening with retry policies.

How should request timeouts be retried?

Retries after a timeout are only safe when reusing the original Idempotency-Key. Without that key, Sent provides no reliable lookup by recipient, so automated resends must be avoided.

Source text

SKILL.md · MIT
---
name: sent-integration-starter
description: Stands up a production-ready Sent v3 integration in an existing codebase — SDK selection and client construction, x-api-key configuration, idempotent sends, retry and rate-limit handling, the 46-code error catalog, sandbox verification, and a verified webhook receiver. Use when adding Sent to an app for the first time, choosing an SDK or framework wiring, handling 429 or 409 responses, deciding what to log, or hardening an integration before launch.
---

# Sent Integration Starter

Bring up a Sent integration in four stages: authenticate, send idempotently, receive verified events, then harden. Do not conflate them — most broken integrations pass stage one and skip stage three.

## Stage 1: client and credentials

Direct Sent v3 REST requests authenticate with the `x-api-key` header. An application proxy may accept `Authorization: Bearer` from its own callers, and the Sent MCP server uses client-managed OAuth, but neither changes the REST header sent to `api.sent.dm`. Organization keys may add `x-profile-id` to act for a child profile; a profile-scoped key that sends that header receives `403`.

| Language | Package | Client |
| --- | --- | --- |
| TypeScript | `@sentdm/sentdm` | `new SentDm({ apiKey })` |
| Python | `sentdm` (imports `sent_dm`) | `Sent(api_key=api_key)` or `AsyncSent(api_key=api_key)` |
| Go | `github.com/sentdm/sent-dm-go` | `sentdm.NewClient(option.WithAPIKey(apiKey))` |
| Java | `dm.sent:sent-java` | `SentOkHttpClient.builder().apiKey(apiKey).build()` |
| C# | `Sentdm` | `new SentClient { ApiKey = apiKey }` |
| PHP | `sentdm/sent-dm-php` | `new SentDm\Client($apiKey)` |
| Ruby | `sentdm` | `Sentdm::Client.new(api_key: api_key)` |

For generated application code, require an explicit, non-empty API key supplied by the application owner and pass it through the SDK's credential option. Inject the signing secret resolved for each webhook id into receiver helpers. Never inspect the plugin installer's environment, credential store, or MCP tokens; the plugin itself authenticates through client-managed OAuth. Multi-tenant production receivers need a secret registry keyed by webhook id.

Choose the client lifecycle from the credential model. A single-account service with one server-managed key should reuse a long-lived client and its connection pool. A multi-tenant proxy that resolves a caller or profile credential per request should construct the client for that request and discard it, so tenant credentials cannot leak through shared state. Framework-specific wiring, the Ruby `messages.send_` naming quirk, and per-ecosystem background-work choices are in [references/sdk-and-frameworks.md](references/sdk-and-frameworks.md).

Validate configuration at boot and fail fast when the key is missing, rather than surfacing an auth error on the first customer send.

## Stage 2: idempotent sends

```json
{
  "to": ["+14155551234"],
  "template": {
    "name": "order_confirmation",
    "parameters": { "order_id": "12345" }
  },
  "sandbox": true
}
```

`to` is the only required field. Supply `template` or `text`, and omit `channel` to let automatic routing choose. Never write a `channel` array with several values expecting fallback — that broadcasts and multiplies charges. Channel decisions belong to `sent-routing-strategist`.

Send `Idempotency-Key` on every POST, PUT, and PATCH, derived deterministically from your own domain object (for example the order id plus the notification type) so a retry after a timeout cannot double-send. Keys are 1–255 characters of `[A-Za-z0-9_-]`, cached 24 hours per key per customer. A replay returns the cached body with `Idempotent-Replayed: true` and `X-Original-Request-Id`. A duplicate arriving while the original is still in flight waits up to five seconds and then fails `409 CONFLICT_001`; a `503 SERVICE_001` means the idempotency store was unavailable and the request was deliberately not executed.

`202` means accepted, not delivered. Persist the returned `message_id` values immediately with your own tenant, profile, and logical send identifiers. Webhook events carry the Sent message id and account data, but never your application's tenant identifier.

## Stage 3: verified webhook receiver

An integration without a receiver has no delivery truth. Register an endpoint, then verify every delivery: HMAC-SHA256 over `{x-webhook-id}.{x-webhook-timestamp}.{raw_body}`, keyed on the base64-decoded secret after stripping `whsec_`, compared in constant time, rejecting timestamps outside 300 seconds. No SDK ships a verifier in any language.

Acknowledge with `200` before doing work, and deduplicate on `{message_id}:{message_status}` for outbound events and `message_id` for inbound. Ten consecutive failed deliveries disable the endpoint. Full mechanics belong to `sent-webhook-engineer`; treat a verified, fast-acknowledging, deduplicating receiver as a launch requirement here.

## Stage 4: harden

### Retry policy by response class

| Response | Retry | How |
| --- | --- | --- |
| `2xx` | No | Success |
| `400`, `422` `VALIDATION_*` | No | Fix the request |
| `401`, `403` `AUTH_*` | No | Stop immediately; ten consecutive auth failures lock the credential with escalating lockouts |
| `404` `RESOURCE_*` | No | The referenced object does not exist |
| `409 CONFLICT_001` | Yes, once, after a pause | A concurrent duplicate is in flight |
| `429` | Yes | Honor `Retry-After`; jittered backoff |
| `5xx`, `503 SERVICE_001` | Yes | Exponential backoff with jitter and a ceiling |
| Timeout with no response | Retry safely only with evidence | Reuse the same `Idempotency-Key`; without one, there is no reliable API lookup by key or recipient, so do not automate a resend |

The standard limit is 200 requests per minute on a sliding window. `POST /v3/webhooks/{id}/rotate-secret` and `POST /v3/webhooks/{id}/test` are limited to 10 per minute. Rate-limit headers appear **only** on `429` responses, so pacing must be designed rather than measured — batch up to 1,000 recipients per request and pace at roughly one request per second for bulk work.

### Error handling

Errors arrive as `{success, data, error: {code, message, details, doc_url}, meta: {request_id, timestamp, version}}`. Branch on the `error.code` prefix family (`AUTH_`, `VALIDATION_`, `RESOURCE_`, `BUSINESS_`, `CONFLICT_`, `SERVICE_`, `INTERNAL_`) rather than on message text or on individual codes. The full 46-code catalog with retry classification is in [references/errors-and-limits.md](references/errors-and-limits.md).

Two codes are counterintuitive: `BUSINESS_003` and `BUSINESS_004` are documented as request-level errors, but on `POST /v3/messages` the request is accepted with `202` and the affected messages finalize as `BLOCKED` and `FILTERED`. Insufficient balance therefore does not fail the send call.

### Observability

Log `meta.request_id` on every response, success or failure — it is the correlation handle for support. Record the mapping from your logical send to the returned `message_id` values, and keep an append-only event history so a reroute's sequence remains auditable. Never log the API key, the webhook signing secret, `payment_details`, or raw recipient message content beyond your retention policy.

### Launch checklist

- [ ] Credentials are explicitly supplied from application-owned configuration; nothing is committed, and separate keys exist per environment.
- [ ] Client lifecycle matches credential scope: shared for one server-managed key, per request for tenant-supplied credentials.
- [ ] `Idempotency-Key` on every mutating call, derived deterministically.
- [ ] Retry policy distinguishes retryable from terminal by error family.
- [ ] Bulk paths pace against 200 requests per minute and batch to at most 1,000 recipients.
- [ ] Webhook receiver verifies signature and timestamp, returns `200` fast, and dedupes.
- [ ] Receiver returns non-2xx on genuine failure so Sent retries.
- [ ] `message_id` to tenant mapping is persisted before sending.
- [ ] `request_id` is logged; secrets and card data are not.
- [ ] Sandbox smoke test passes, then a real send reaches `DELIVERED`.
- [ ] Alerting covers webhook `consecutive_failures`, `429` volume, and filtered or blocked rates.

## Verification

Run the local preflight, which needs no credentials and no network:

```bash
python3 scripts/preflight.py --self-test
```

Then verify a real path with `"sandbox": true`, which authenticates and validates without executing, and finally with one live send confirmed to `DELIVERED` through the receiver.

## Boundaries

Use `sent-webhook-engineer` for receiver depth, `sent-routing-strategist` for channel choice, `sent-messaging` for a confirmed one-off send, `sent-two-way-messaging` for inbound and consent, `sent-profile-provisioning` for multi-tenant provisioning, and `migrate-to-sent` when replacing another CPaaS provider.

© sentdm · MIT · sentdm/sent-plugin

Similar skills

All skills for Backend and APIs →

From the same set15

template-builder-ui Helps design and audit tenant-facing Sent v3 template builders, preview interfaces, channel validation, and lifecycle states before submitting to providers. CX Codex +1 2,900installs waba-embedded-signup Walks you through WhatsApp Business Account onboarding via Sent and keeps three integration paths apart. Useful when wiring profiles, tokens and completion callbacks. CX Codex +1 2,900installs waba-template-author Converts messaging copy into valid payloads for the Sent v3 API and reviews WhatsApp policy constraints. Useful for building utility, marketing, and OTP templates. CX Codex +1 2,900installs sent Turns vague Sent requests into a specific skill or MCP-backed operation instead of guessing. CX Codex +1 2,900installs sent-profile-provisioning This skill drives the Sender Profile lifecycle in Sent: list, inspect, create, update and delete over MCP tools, plus the wider REST flow. Useful when onboarding a tenant and getting a profile ready to send. CX Codex +1 2,700installs sent-routing-strategist Works out how a Sent message reaches the recipient: automatic routing versus a pinned channel, what the channel array really does, and why a message landed as FAILED, FILTERED or BLOCKED. CX Codex +1 2,700installs sent-webhook-engineer A skill for building and debugging Sent v3 webhook receivers: signature checks, replay rejection, event dedupe and delivery-log triage. CX Codex +1 2,700installs sent-two-way-messaging 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. CX Codex +1 2,700installs

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor