Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Send and receive iMessage, SMS, and RCS from application code via the Sendblue HTTP API — text, media, group messages, send styles, reactions, typing indicators, status callbacks, and inbound webhooks.
.claude/skills/lingxling-sendblue-api/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 39% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 56% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 182% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 144% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 112% | 0% |
Sendblue is a REST API that sends iMessage (blue bubbles), SMS, and RCS from a provisioned phone number. Everything is plain JSON over HTTPS — no SDK is required. The API covers outbound 1:1 and group sends, iMessage effects, reactions, typing indicators, status callbacks, and inbound webhooks.
https://api.sendblue.comEvery request needs two headers:
sb-api-key-id: <YOUR_API_KEY_ID>
sb-api-secret-key: <YOUR_API_SECRET>
Content-Type: application/jsonKeep both values server-side — never ship them to a browser or mobile client.
bashcurl -X POST https://api.sendblue.com/api/send-message \ -H "sb-api-key-id: $KEY_ID" \ -H "sb-api-secret-key: $SECRET" \ -H 'Content-Type: application/json' \ -d '{ "number": "+15551234567", "from_number": "+1YOUR_SENDBLUE_NUMBER", "content": "Hello from the API!" }'
Phone numbers must be E.164. from_number must be a line you own — list yours with GET /api/lines.
The synchronous response includes a message_handle (Apple GUID — persist this; you need it for reactions and replies) and a status from REGISTERED, PENDING, QUEUED, ACCEPTED, SENT, DELIVERED, DECLINED, ERROR. Only DELIVERED means it landed. Use status_callback instead of polling /api/status.
Configure webhook URLs in the dashboard or via POST /api/account/webhooks. Sendblue POSTs JSON to your endpoint. Respond with 2xx promptly — non-2xx triggers retries and duplicate deliveries. Event types: receive, outbound, typing_indicator, call_log, line_blocked, line_assigned, contact_created.
| Method | Path | Purpose | |--------|------|---------| | POST | /api/send-message | Send a 1:1 message (text and/or media) | | POST | /api/send-group-message | Send to multiple recipients | | POST | /api/create-group | Create a named group thread | | POST | /api/send-reaction | Send a tapback (love/like/dislike/laugh/emphasize/question) | | POST | /api/send-typing-indicator | Show "typing…" in the recipient's thread | | POST | /api/mark-read | Send a read receipt | | POST | /api/upload-file / /api/upload-media-object | Upload media (direct or from URL) | | GET | /api/status | Poll a message's delivery status | | GET | /api/evaluate-service | Check whether a number is on iMessage | | GET | /api/v2/messages / /api/v2/messages/:id | Read message history | | GET / POST / PUT / DELETE | /api/v2/contacts[...] | Manage contacts | | GET | /api/lines | List your Sendblue phone numbers | | POST | /api/account/webhooks | CRUD webhook subscriptions |
jsonPOST /api/send-message { "number": "+15551234567", "from_number": "+1YOUR_SENDBLUE_NUMBER", "content": "Optional text", "media_url": "https://example.com/img.jpg", "send_style": "celebration", "status_callback": "https://yourapp.com/sendblue/status" }
content and/or media_url is required. send_style is iMessage-only — valid values: celebration, shooting_star, fireworks, lasers, love, confetti, balloons, spotlight, echo, invisible, gentle, loud, slam. Ignored on SMS. Text up to 18,996 chars; media up to 100 MB on iMessage, 5 MB on SMS.
jsonPOST /api/send-group-message { "numbers": ["+15551234567", "+15557654321"], "from_number": "+1YOUR_SENDBLUE_NUMBER", "content": "Hey team" }
The response returns a group_id — persist it to send follow-ups into the same thread instead of creating a new one each time.
jsonPOST /api/send-reaction { "from_number": "+1YOUR_SENDBLUE_NUMBER", "message_handle": "<message_handle from prior send>", "reaction": "love" }
Reactions only work on iMessage and need the original message's message_handle. Valid values: love, like, dislike, laugh, emphasize, question.
receive)json{ "accountEmail": "you@example.com", "content": "Reply text", "media_url": "https://...", "is_outbound": false, "number": "+15551234567", "from_number": "+1YOUR_SENDBLUE_NUMBER", "service": "iMessage", "group_id": "...", "date_sent": "2024-01-01T12:00:00Z" }
Status callback payloads (outbound) mirror the send-message response and update as the message moves through SENT → DELIVERED (or ERROR).
message_handle on every send. You need it for reactions, replies, and correlating status callbacks.status_callback over polling. It's lower-cost and more accurate than GET /api/status./api/evaluate-service before relying on iMessage-only features for a recipient.sb-api-key-id / sb-api-secret-key to a client. They are server-side credentials./api/send-message as delivery. It only means "accepted".status_callback or GET /api/status.send_style silently no-ops on SMS (green-bubble recipients).sb-api-key-id and sb-api-secret-key server-side. They are not safe in browser, mobile, or CI logs.message_handle may arrive more than once.5551234567 or (555) 123-4567 will fail — always send +15551234567.from_number must be one of your lines. A spoofed or unprovisioned number returns an error.send_style silently no-ops on SMS. If the recipient is green-bubble, effects don't render — check service first with /api/evaluate-service if it matters.message_handle. You need it for reactions, replies, and correlating status callbacks back to your records./api/send-message means accepted, not delivered. Use status_callback rather than blocking on the synchronous response.@sendblue-cli — Shell wrapper for shell-context outbound (scripts, cron, agent hooks). Use it when you don't need a full HTTP integration.@sendblue-notify — Patterns and copy rules for outbound "text me when X is done" notifications layered on top of the API or CLI./api/send-carousel), FaceTime/contact-card sharing, advanced webhook filtering, contacts API beyond basic CRUD — see the docs site.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 23,637 | 18,779 | -21% | 1 | 1 | 0% | 3,524 | 4,904 | +39% | 0 | 0 | — |
case-02 | fail→pass | 20,587 | 16,827 | -18% | 1 | 1 | 0% | 4,014 | 6,267 | +56% | 0 | 0 | — |
case-03 | pass→pass | 12,038 | 7,503 | -38% | 1 | 1 | 0% | 1,852 | 3,813 | +106% | 0 | 0 | — |
case-04 | pass→pass | 18,516 | 17,010 | -8% | 1 | 1 | 0% | 2,752 | 4,870 | +77% | 0 | 0 | — |
case-05 | pass→pass | 12,106 | 12,586 | +4% | 1 | 1 | 0% | 2,421 | 4,602 | +90% | 0 | 0 | — |
case-06 | pass→pass | 9,177 | 4,902 | -47% | 1 | 1 | 0% | 1,752 | 3,469 | +98% | 0 | 0 | — |
case-07 | pass→pass | 10,018 | 6,722 | -33% | 1 | 1 | 0% | 1,547 | 3,556 | +130% | 0 | 0 | — |
case-08 | fail→pass | 6,185 | 3,652 | -41% | 1 | 1 | 0% | 1,095 | 3,092 | +182% | 0 | 0 | — |
case-09 | pass→pass | 5,726 | 2,728 | -52% | 1 | 1 | 0% | 773 | 2,968 | +284% | 0 | 0 | — |
case-10 | fail→pass | 9,390 | 3,152 | -66% | 1 | 1 | 0% | 1,288 | 3,139 | +144% | 0 | 0 | — |
case-11 | pass→pass | 8,492 | 2,888 | -66% | 1 | 1 | 0% | 1,171 | 3,107 | +165% | 0 | 0 | — |
case-12 | fail→pass | 7,933 | 3,654 | -54% | 1 | 1 | 0% | 1,489 | 3,153 | +112% | 0 | 0 | — |
case-13 | fail→pass | 11,482 | 4,747 | -59% | 1 | 1 | 0% | 1,688 | 3,336 | +98% | 0 | 0 | — |
case-14 | pass→pass | 12,939 | 7,357 | -43% | 1 | 1 | 0% | 2,029 | 3,548 | +75% | 0 | 0 | — |
case-15 | pass→pass | 12,661 | 14,217 | +12% | 1 | 1 | 0% | 2,075 | 4,341 | +109% | 0 | 0 | — |
case-16 | pass→pass | 10,935 | 4,539 | -58% | 1 | 1 | 0% | 1,409 | 3,196 | +127% | 0 | 0 | — |
case-17 | fail→pass | 9,512 | 4,088 | -57% | 1 | 1 | 0% | 1,363 | 3,261 | +139% | 0 | 0 | — |
case-18 | pass→pass | 12,891 | 9,558 | -26% | 1 | 1 | 0% | 1,884 | 4,002 | +112% | 0 | 0 | — |
case-19 | fail→pass | 8,161 | 5,736 | -30% | 1 | 1 | 0% | 1,552 | 3,433 | +121% | 0 | 0 | — |
case-20 | fail→pass | 7,570 | 2,583 | -66% | 1 | 1 | 0% | 1,350 | 2,928 | +117% | 0 | 0 | — |
case-21 | fail→pass | 12,346 | 4,048 | -67% | 1 | 1 | 0% | 1,740 | 3,104 | +78% | 0 | 0 | — |
case-22 | fail→pass | 10,890 | 3,410 | -69% | 1 | 1 | 0% | 1,392 | 2,978 | +114% | 0 | 0 | — |
case-23 | fail→pass | 13,346 | 3,710 | -72% | 1 | 1 | 0% | 2,141 | 3,026 | +41% | 0 | 0 | — |
case-24 | pass→pass | 9,948 | 4,194 | -58% | 1 | 1 | 0% | 1,354 | 3,141 | +132% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 24 cases were attempted. The headline lift of +50 percentage points is the difference between those two pass rates over the 24 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.