Skip to main content

Worked request and response samples

Copy a request as written, substitute your own ids, and compare the response envelope. The chain a pool-integration runs: create → list → send through the pool → (optional) preview → swap strategy. For narrative walkthroughs see the Sender pools guide.

1. Create a pool

POST /api/v1/messaging/sender-pools
Request
sender_dids accepts a mix of E.164 long codes, numeric short codes, and alphanumeric sender IDs (up to 50 per pool, no duplicates). Success returns 201 with the new pool id:

2. List pools

GET /api/v1/messaging/sender-pools
Request
The list is keyset-paginated: limit is clamped to 1–100 (default 50) and meta.pagination carries has_more plus the opaque next_cursor to pass back as ?cursor=.... Each row includes the full sender_dids membership:

3. Send through the pool

POST /api/v1/messages/sms
Request
Omit from and pass sender_pool_id; the service resolves the sender via the pool’s strategy. When both are set, the pool wins — same precedence Twilio uses for Messaging Services vs From. The echoed envelope names the resolved sender:
The full send-and-track chain (status polls, DLR webhooks) is on the Messaging samples page.

4. Preview the pick

GET /api/v1/messaging/sender-pools/{id}/preview?recipient=<E.164>
Request
Preview is strictly read-only: it never persists stickiness state, advances the round-robin counter, or mutates production routing. Use it to verify exactly which DID a pool hands a recipient before you wire the pool into a campaign:

5. Swap the strategy

PATCH /api/v1/messaging/sender-pools/{id}
Request
PATCH must carry at least one of label, sender_dids, or strategy; the response echoes the full pool so the diff (strategy moved sticky → round_robin, updated_at bumped) is visible:
The pool cache refreshes within seconds, so the next send uses the new strategy immediately.

Errors worth branching on

These failures cover the pool endpoints shown above. Each block is a full { error, meta } envelope as the API returns it, and the matrix at the bottom answers retry vs surface. For the platform-wide decision table these branches plug into, see the error handling guide.

401 — Unauthorized

A 401 on this page is the bearer key failing before the route ran — it never means the resource is wrong. Rotate the key or re-mint the scoped token; retrying the same request changes nothing.

403 — Forbidden

A 403 means the key authenticated but the operation is gated by scope — check the key’s scopes on the developer page; a 403 is never a data-not-found shape.

422 — Schema

A 422 means the payload did not match the request schema — branch on error.details.field and resend with valid sender_dids entries instead of blind-retrying the same body. A pool whose members were all pruned after creation surfaces the same status with code SENDER_POOL_EMPTY on the next send or preview — add at least one DID before sending through it.

429 — Rate

The Retry-After header and error.details.retry_after are both set on every 429 — resend the SAME request after the lower of the two.

409 — Conflict

Sender membership is single-owner: a sender one pool already holds is excluded from every other pool’s member selector. Move the sender out of its current pool (or drop it from this request) and resend — repeating the same body re-conflicts deterministically, so this is a fix-then-resend class, not a retry class.

60-second retry matrix