SMS Best Practices With Rewrite
Use this skill when the user wants guidance, standards, or a checklist for building SMS flows with Rewrite.
If the task is implementation-heavy, pair this skill with:
send-sms/ for outbound send code, contacts, segments, and delivery lifecycle work
rewrite-inbound/ for Rewrite webhook intake and event processing
agent-sms-inbox/ for application-owned SMS agents
Rewrite Reality Check
- Public Rewrite API v1 base URL:
https://api.rewritetoday.com/v1
- Public audience resources live at
/contacts and /segments
- Public sends currently target Brazilian
+55 numbers unless the project has international sending enabled
- Message sends accept
to or contact
contact resolves by contact ID or exact contact name
- Sending to
to also ensures a Rewrite contact exists for that phone number
- Templates use
templateId plus variables
- Write operations should carry
Idempotency-Key
- Delivery is asynchronous and webhook-driven
- Segments are audience containers, not a direct broadcast endpoint
message.delivered exists in the public contract but is currently WIP
Default Guidance
- Start with the simplest reliable flow: one send path, one webhook endpoint, one persistence model.
- Treat the initial send response as acceptance, not final delivery.
- Add idempotency before adding retries or concurrency.
- Persist raw webhook payloads and deduplicate by webhook event ID.
- Move heavy work off the webhook request path.
- Use templates for repeated or localized content.
- Add batch sends only after single-send observability is solid.
Content And Deliverability
- Use valid
+55 E.164 destination numbers.
- Keep OTP and transactional content short.
- Put brand context near the start of the SMS body.
- Prefer explicit, low-ambiguity wording over marketing-heavy copy.
- Watch segmentation counts and encoding because they affect cost and delivery behavior.
- Use
tags to encode stable business context like flow, campaign, or use_case.
Audience Strategy
- Model reusable recipients as Rewrite contacts instead of duplicating recipient metadata across message payloads.
- Keep contact names unique per project if you plan to address contacts by name in
/messages.
- Use segments for audience reuse and campaign assembly, then fan out the segment contacts through
/messages or /messages/batch.
- Treat Rewrite contact tags and your app's business state separately unless you intentionally mirror them.
Templates And Localization
- Prefer templates when the same flow is reused.
- Keep variable names stable across all template variants.
- Keep
content as the default fallback on templates.
- Put locale variants in template
i18n, not in the message-create body.
- Treat localization as a template concern, not an ad hoc string-building concern.
Reliability And Retries
- Retry only
429 and transient 5xx.
- Do not blindly retry
400 INVALID_JSON_BODY.
- Treat
401 INVALID_OR_MISSING_API_KEY as a configuration issue.
- Treat
403 PROJECT_SUBSCRIPTION_REQUIRED and PROJECT_BILLING_PAYMENT_REQUIRED as operational or billing blocks.
- Respect
X-RateLimit-Retry-After; the current platform returns it in milliseconds.
Retry Workflow
- Check status — only retry
429 or 5xx responses.
- Read header — parse
X-RateLimit-Retry-After (ms) from the response.
- Wait — use the header value, or exponential backoff (1 s, 2 s, 4 s) if absent.
- Retry — resend with the same
Idempotency-Key.
- Validate — confirm
2xx; stop after 3 failed attempts.
- Escalate — log message ID + error code, alert ops or dead-letter the job.
Webhooks
- Verify signatures with the raw body and
svix-id, svix-timestamp, svix-signature.
- Return a fast
2xx once the event is durably accepted by your system.
- Expect at-least-once delivery.
- Deduplicate by webhook event ID.
- Keep downstream writes idempotent.
- Use delivery logs when debugging retries, malformed responses, or missing events.
Signature Verification Example
import { Webhook } from "svix";
export function verifyWebhook(rawBody: string, headers: Record<string, string>) {
const wh = new Webhook(process.env.REWRITE_WEBHOOK_SECRET!);
return wh.verify(rawBody, {
"svix-id": headers["svix-id"],
"svix-timestamp": headers["svix-timestamp"],
"svix-signature": headers["svix-signature"],
});
}
Observability
Persist or emit at minimum:
- Rewrite message ID
- idempotency key
- destination number
- tags or business correlation IDs
- accepted timestamp
- message analysis: encoding, characters, segments
- webhook event history
- last known status and last error code
Architecture Patterns
- Small apps: official SDK plus one database table plus one webhook worker
- Service-oriented stacks:
@rewritetoday/rest plus @rewritetoday/types or @rewritetoday/zod
- High volume flows: queue-backed fan-out, controlled concurrency, dead-letter handling
- Audience-heavy flows: Rewrite contacts plus segments for recipient modeling, then queue-backed batch orchestration
- Critical OTP flows: isolate traffic, rate limit aggressively, and keep content minimal
Batch Strategy
- Use
POST /messages/batch only for bounded chunks
- Current public contract accepts 1 to 100 messages per batch request
- Resolve segment members first; there is no direct
/segments/:id/send public route
- Treat
message.batch as a summary event, not a replacement for per-message tracking
- If every batch item fails, no
message.batch event is emitted
Review Checklist
When using this skill, produce recommendations grouped by: content and deliverability risks, reliability and retry policy, webhook and event-processing safety, observability gaps, and rollout or scaling risks. Prefer concrete guardrails and tradeoffs over generic SMS advice.
1---2name: sms-best-practices3description: General SMS best-practices guide for Rewrite-based systems. Use when the agent needs to define SMS architecture, contacts and segments strategy, deliverability, templates, retries, observability, webhook handling, or rollout guardrails before or alongside implementation.4---56# SMS Best Practices With Rewrite78Use this skill when the user wants guidance, standards, or a checklist for building SMS flows with Rewrite.910If the task is implementation-heavy, pair this skill with:1112- `send-sms/` for outbound send code, contacts, segments, and delivery lifecycle work13- `rewrite-inbound/` for Rewrite webhook intake and event processing14- `agent-sms-inbox/` for application-owned SMS agents1516## Rewrite Reality Check1718- Public Rewrite API v1 base URL: `https://api.rewritetoday.com/v1`19- Public audience resources live at `/contacts` and `/segments`20- Public sends currently target Brazilian `+55` numbers unless the project has international sending enabled21- Message sends accept `to` or `contact`22- `contact` resolves by contact ID or exact contact name23- Sending to `to` also ensures a Rewrite contact exists for that phone number24- Templates use `templateId` plus `variables`25- Write operations should carry `Idempotency-Key`26- Delivery is asynchronous and webhook-driven27- Segments are audience containers, not a direct broadcast endpoint28- `message.delivered` exists in the public contract but is currently WIP2930## Default Guidance31321. Start with the simplest reliable flow: one send path, one webhook endpoint, one persistence model.332. Treat the initial send response as acceptance, not final delivery.343. Add idempotency before adding retries or concurrency.354. Persist raw webhook payloads and deduplicate by webhook event ID.365. Move heavy work off the webhook request path.376. Use templates for repeated or localized content.387. Add batch sends only after single-send observability is solid.3940## Content And Deliverability4142- Use valid `+55` E.164 destination numbers.43- Keep OTP and transactional content short.44- Put brand context near the start of the SMS body.45- Prefer explicit, low-ambiguity wording over marketing-heavy copy.46- Watch segmentation counts and encoding because they affect cost and delivery behavior.47- Use `tags` to encode stable business context like `flow`, `campaign`, or `use_case`.4849## Audience Strategy5051- Model reusable recipients as Rewrite contacts instead of duplicating recipient metadata across message payloads.52- Keep contact names unique per project if you plan to address contacts by name in `/messages`.53- Use segments for audience reuse and campaign assembly, then fan out the segment contacts through `/messages` or `/messages/batch`.54- Treat Rewrite contact tags and your app's business state separately unless you intentionally mirror them.5556## Templates And Localization5758- Prefer templates when the same flow is reused.59- Keep variable names stable across all template variants.60- Keep `content` as the default fallback on templates.61- Put locale variants in template `i18n`, not in the message-create body.62- Treat localization as a template concern, not an ad hoc string-building concern.6364## Reliability And Retries6566- Retry only `429` and transient `5xx`.67- Do not blindly retry `400 INVALID_JSON_BODY`.68- Treat `401 INVALID_OR_MISSING_API_KEY` as a configuration issue.69- Treat `403 PROJECT_SUBSCRIPTION_REQUIRED` and `PROJECT_BILLING_PAYMENT_REQUIRED` as operational or billing blocks.70- Respect `X-RateLimit-Retry-After`; the current platform returns it in milliseconds.7172### Retry Workflow73741. **Check status** — only retry `429` or `5xx` responses.752. **Read header** — parse `X-RateLimit-Retry-After` (ms) from the response.763. **Wait** — use the header value, or exponential backoff (1 s, 2 s, 4 s) if absent.774. **Retry** — resend with the same `Idempotency-Key`.785. **Validate** — confirm `2xx`; stop after 3 failed attempts.796. **Escalate** — log message ID + error code, alert ops or dead-letter the job.8081## Webhooks8283- Verify signatures with the raw body and `svix-id`, `svix-timestamp`, `svix-signature`.84- Return a fast `2xx` once the event is durably accepted by your system.85- Expect at-least-once delivery.86- Deduplicate by webhook event ID.87- Keep downstream writes idempotent.88- Use delivery logs when debugging retries, malformed responses, or missing events.8990### Signature Verification Example9192```ts93import { Webhook } from "svix";9495export function verifyWebhook(rawBody: string, headers: Record<string, string>) {96 const wh = new Webhook(process.env.REWRITE_WEBHOOK_SECRET!);97 return wh.verify(rawBody, {98 "svix-id": headers["svix-id"],99 "svix-timestamp": headers["svix-timestamp"],100 "svix-signature": headers["svix-signature"],101 });102}103```104105## Observability106107Persist or emit at minimum:108109- Rewrite message ID110- idempotency key111- destination number112- tags or business correlation IDs113- accepted timestamp114- message analysis: encoding, characters, segments115- webhook event history116- last known status and last error code117118## Architecture Patterns119120- Small apps: official SDK plus one database table plus one webhook worker121- Service-oriented stacks: `@rewritetoday/rest` plus `@rewritetoday/types` or `@rewritetoday/zod`122- High volume flows: queue-backed fan-out, controlled concurrency, dead-letter handling123- Audience-heavy flows: Rewrite contacts plus segments for recipient modeling, then queue-backed batch orchestration124- Critical OTP flows: isolate traffic, rate limit aggressively, and keep content minimal125126## Batch Strategy127128- Use `POST /messages/batch` only for bounded chunks129- Current public contract accepts 1 to 100 messages per batch request130- Resolve segment members first; there is no direct `/segments/:id/send` public route131- Treat `message.batch` as a summary event, not a replacement for per-message tracking132- If every batch item fails, no `message.batch` event is emitted133134## Review Checklist135136When using this skill, produce recommendations grouped by: content and deliverability risks, reliability and retry policy, webhook and event-processing safety, observability gaps, and rollout or scaling risks. Prefer concrete guardrails and tradeoffs over generic SMS advice.