RebelText API Documentation

RebelText API

Send SMS, check delivery status, and get status updates pushed to your server over webhooks.

Base URL

https://api.rebeltexting.com

All requests and responses use JSON. Send Content-Type: application/json on every request that has a body.


Contents


Quick start

  1. Send a message.

    curl -X POST https://api.rebeltexting.com/v1/messages \
      -H "Authorization: Bearer $REBELTEXT_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"to_number": "5125550199", "message": "Your order has shipped."}'
    

    The response contains a reference_id such as msg_01HQ3F5X8K2M9N4P6R7S8T9V0W.

  2. Check its status.

    curl https://api.rebeltexting.com/v1/messages/msg_01HQ3F5X8K2M9N4P6R7S8T9V0W \
      -H "Authorization: Bearer $REBELTEXT_API_KEY"
    
  3. Or have status updates pushed to you by registering a webhook, so you don't have to poll.


Changes: credits are now a dollar balance

Your account used to hold credits. It now holds a balance in US dollars, and every message has a price in dollars. Amounts are sent as decimal strings, such as "0.0095", never as JSON numbers. See Segments and pricing.

These fields and codes were renamed. The old names are no longer returned, so update any code that reads them:

Where Before Now
Message object: send responses, message reads, sms.status and sms.inbound webhooks, batch entries credits (integer) cost (string, such as "0.0095") and currency ("usd")
POST /v1/messages response credits_remaining (integer) balance (string, such as "48.21")
POST /v1/messages/batch response credits, credits_remaining cost, balance, currency
GET /v1/batches/{id} credits cost, currency
GET /v1/account/usage and GET /v1/account credits_remaining balance, currency
GET /v1/account/grants each grant's credits amount, currency
GET /v1/plans included_credits included_balance (string)
Error code, 402 insufficient_credits insufficient_balance

Authentication

Authenticate every request with your API key as a bearer token:

Authorization: Bearer rt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

There are two kinds of key:

Prefix Use
rt_live_ Sends real messages to real phones.
rt_test_ For testing your integration. Messages go through validation, pricing, rate limits and webhooks exactly like live ones, but never reach a phone and always end up delivered. Test keys are not accepted in production.

Keep your key secret. Don't put it in client-side code or commit it to source control.

Problem Status code
Key missing or not recognised 401 unauthenticated
Key is valid, but the account is suspended or closed 403 account_inactive
Account has no active subscription 402 subscription_required
Test key used against production 403 test_key_in_production

You need a subscription to send

Your key authenticates straight away, but sending needs an active plan behind it. Without one, every endpoint that sends a message — or that registers a brand or campaign, or buys a number — answers:

{
  "status": false,
  "message": "This account has no active subscription. Choose a plan to start sending.",
  "code": "subscription_required"
}

Reading is unaffected: you can still call GET /v1/account, GET /v1/plans, GET /v1/messages and your invoices, so you can see where you stand. Sign in to the portal and pick a plan, and your key starts working on the next request — there is no waiting period and nothing to restart.

subscription_required and insufficient_balance are deliberately different codes. The first means there is no plan; the second means the plan is there and your balance has run out.


Responses and errors

Every response uses the same shape.

Success

{
  "status": true,
  "message": "Success",
  "data": { }
}

Error

{
  "status": false,
  "message": "The to_number is not a valid North American number.",
  "code": "invalid_request",
  "errors": {
    "to_number": ["The to_number is not a valid North American number."]
  }
}

Unknown fields in a request body are rejected rather than silently ignored, so a typo such as toNumber gives you a 422 telling you which field is wrong.

Request IDs

Every response has an X-Request-Id header. You can also send your own X-Request-Id and we'll use it. Include it when you contact support so we can find the exact request.


Send an SMS

POST /v1/messages

Queues one SMS for delivery. On success it returns 202 Accepted: the message is queued, not yet delivered. Delivery status comes later. You can check the status or get it from a webhook.

Headers

Header Required Description
Authorization yes Bearer <your API key>
Content-Type yes application/json
Idempotency-Key no A unique string for this send, such as order-4471-shipped. If you retry a request with the same key within 24 hours, no second message is sent. See Safe retries.

Body

Field Type Required Description
to_number string yes The recipient's phone number. Any common format works: 5125550199, (512) 555-0199, +15125550199.
message string yes The text to send. Up to 1600 characters and 10 segments.
from_number string no One of your numbers to send from. Leave this out to have us pick the best available number on your account.
country_code string no Country code for to_number, such as +1. Defaults to +1. Ignored if to_number starts with +.
metadata object no Your own string key/value pairs, such as {"order_id": "4471"}. Returned when you read the message and in its webhooks.
scheduled_at string no Send later instead of now. An RFC 3339 timestamp, such as 2026-09-20T15:00:00Z, up to 90 days ahead. It can't be in the past.

Example request

curl -X POST https://api.rebeltexting.com/v1/messages \
  -H "Authorization: Bearer $REBELTEXT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-4471-shipped" \
  -d '{
    "to_number": "5125550199",
    "from_number": "5125550100",
    "message": "Your order #4471 has shipped.",
    "metadata": { "order_id": "4471" }
  }'

Example response — 202 Accepted

{
  "status": true,
  "message": "Message accepted",
  "data": {
    "reference_id": "msg_01HQ3F5X8K2M9N4P6R7S8T9V0W",
    "sender": "+15125550100",
    "receiver": "+15125550199",
    "sms_type": "sms",
    "message": "Your order #4471 has shipped.",
    "media_url": null,
    "status": "accepted",
    "remarks": "The message was accepted and is waiting to be sent.",
    "error_code": null,
    "carrier_error_code": null,
    "carrier_error_message": null,
    "segments": 1,
    "cost": "0.0095",
    "currency": "usd",
    "attempts": 0,
    "metadata": { "order_id": "4471" },
    "api_key_id": "key_01HQ2Z8Y7X6W5V4T3S2R1Q0P9N",
    "scheduled_at": null,
    "sent_at": null,
    "received_at": null,
    "delivered_at": null,
    "created_at": "2026-09-17T10:04:11.204000Z",
    "updated_at": "2026-09-17T10:04:11.204000Z",
    "balance": "48.21"
  }
}

data is a message object, plus balance, what your account has left in US dollars after this message, as a decimal string.

Phone numbers

Segments and pricing

Your account holds a balance in US dollars. You add to it by topping up, and your plan may add to it each billing period. Each message is paid for from it:

cost in the response is what this message costs, and currency is always "usd". For example, a 200-character GSM message is 2 segments; at $0.0095 a segment it has "segments": 2 and "cost": "0.019", and your balance goes down by that much.

Amounts — cost, balance and the others — are decimal strings of US dollars, with up to 6 decimal places and never fewer than 2: "0.01", "0.019", "0.0095", "48.21". They're strings so they never pass through floating point; parse them with a decimal type, not a float, if you do arithmetic on them.

How many segments a message takes:

Safe retries

A network error can leave you unsure whether a send went through. Retrying without protection can send the message twice.

Send an Idempotency-Key header. If you retry with the same key, from the same API key, within 24 hours:

{
  "status": true,
  "message": "Message accepted",
  "data": {
    "reference_id": "msg_01HQ3F5X8K2M9N4P6R7S8T9V0W",
    "status": "accepted",
    "created_at": null,
    "updated_at": null,
    "balance": "48.21",
    "duplicate": true
  }
}

(Shortened. The full message object is returned.)

Use a key that identifies the send itself, such as your order ID plus the event, not a random value generated for each attempt.

Errors

Problem Status code
Invalid phone number or country_code, missing or too-long message, invalid scheduled_at 422 invalid_request
Body isn't valid JSON, or has an unknown field 400 / 422 invalid_request
Recipient has opted out of messages from you 422 recipient_suppressed
Balance too low for this message 402 insufficient_balance
No active subscription 402 subscription_required
Too many requests per second 429 rate_limited
Request body too large 413 payload_too_large

Rate limits

Your plan sets how many messages per second you can send. Past that limit you get 429 rate_limited and a Retry-After header saying how many seconds to wait. A rate-limited request isn't charged. Wait at least that long before you retry.


Check message status

GET /v1/messages/{reference_id}

Returns the current state of a message you sent.

Example request

curl https://api.rebeltexting.com/v1/messages/msg_01HQ3F5X8K2M9N4P6R7S8T9V0W \
  -H "Authorization: Bearer $REBELTEXT_API_KEY"

Example response — 200 OK

{
  "status": true,
  "message": "Success",
  "data": {
    "reference_id": "msg_01HQ3F5X8K2M9N4P6R7S8T9V0W",
    "sender": "+15125550100",
    "receiver": "+15125550199",
    "sms_type": "sms",
    "message": "Your order #4471 has shipped.",
    "media_url": null,
    "status": "delivered",
    "remarks": "The message was delivered to the recipient.",
    "error_code": null,
    "carrier_error_code": null,
    "carrier_error_message": null,
    "segments": 1,
    "cost": "0.0095",
    "currency": "usd",
    "attempts": 1,
    "metadata": { "order_id": "4471" },
    "api_key_id": "key_01HQ2Z8Y7X6W5V4T3S2R1Q0P9N",
    "scheduled_at": null,
    "sent_at": "2026-09-17T10:04:11.881000Z",
    "received_at": null,
    "delivered_at": "2026-09-17T10:04:14.019000Z",
    "created_at": "2026-09-17T10:04:11.204000Z",
    "updated_at": "2026-09-17T10:04:14.019000Z"
  }
}

data is a message object. Check status to see where the message is. Message statuses lists every value.

If a message fails, status is failed or undelivered, error_code is set, and remarks includes the reason. When the carrier gave its own code and text, they're in carrier_error_code and carrier_error_message, in the carrier's own words:

{
  "status": "undelivered",
  "remarks": "The carrier accepted the message but could not deliver it. Reason: Blocked by carrier spam filter.",
  "error_code": "40002",
  "carrier_error_code": "40002",
  "carrier_error_message": "Message blocked by carrier spam filter"
}

Errors

Problem Status code
No message with this ID on your account 404 not_found

Tip: Don't poll this endpoint in a tight loop. Webhooks tell you the moment a status changes.


Webhooks

Webhooks send an HTTP POST to your server when something happens, such as a message being delivered or failing.

Event When it's sent
sms.status A message you sent changes status: scheduled, sending, sent, delivered, undelivered or failed.
sms.inbound Someone sends an SMS to one of your numbers.

Each webhook endpoint subscribes to one or more event types. New endpoints subscribe to sms.status by default.

Create a webhook

POST /v1/webhooks
Field Type Required Description
url string yes The URL we send events to. Must be http or https; use https in production.
event_types string[] no The events to receive: sms.status, sms.inbound, or both. Defaults to ["sms.status"].
description string no A note for yourself, up to 500 characters.

Example request

curl -X POST https://api.rebeltexting.com/v1/webhooks \
  -H "Authorization: Bearer $REBELTEXT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/hooks/rebeltext",
    "event_types": ["sms.status", "sms.inbound"],
    "description": "Production order notifications"
  }'

Example response — 201 Created

{
  "status": true,
  "message": "Webhook endpoint created",
  "data": {
    "id": "wh_01HQ3F5X8K2M9N4P6R7S8T9V0W",
    "url": "https://example.com/hooks/rebeltext",
    "event_types": ["sms.status", "sms.inbound"],
    "status": "active",
    "description": "Production order notifications",
    "secret": "whsec_3vK9dQ2mR7pL5xW8nT4bY6cF1hJ0sA...",
    "consecutive_failures": 0,
    "created_at": "2026-09-17T10:04:11Z"
  }
}

⚠️ Save secret now. It is shown only once. You need it to verify signatures. If you lose it, delete the webhook and create a new one.

Errors

Problem Status code
Missing or invalid url, unknown event type, description too long 422 invalid_request

List webhooks

GET /v1/webhooks

Returns all your webhook endpoints and their health. secret is never included.

curl https://api.rebeltexting.com/v1/webhooks \
  -H "Authorization: Bearer $REBELTEXT_API_KEY"
{
  "status": true,
  "message": "Success",
  "data": {
    "webhooks": [
      {
        "id": "wh_01HQ3F5X8K2M9N4P6R7S8T9V0W",
        "url": "https://example.com/hooks/rebeltext",
        "event_types": ["sms.status", "sms.inbound"],
        "status": "active",
        "description": "Production order notifications",
        "consecutive_failures": 0,
        "last_success_at": "2026-09-17T11:20:03Z",
        "created_at": "2026-09-17T10:04:11Z"
      }
    ]
  }
}
Field Description
consecutive_failures Failed deliveries in a row. Goes back to 0 after a successful delivery.
disabled_until Set when too many deliveries have failed and we've paused sending to this endpoint. See Retries. Left out when not paused.
last_success_at When a delivery last succeeded. Left out if none has.
last_failure_at When a delivery last failed. Left out if none has.

Delete a webhook

DELETE /v1/webhooks/{id}

Stops all events to this endpoint.

curl -X DELETE https://api.rebeltexting.com/v1/webhooks/wh_01HQ3F5X8K2M9N4P6R7S8T9V0W \
  -H "Authorization: Bearer $REBELTEXT_API_KEY"
{ "status": true, "message": "Webhook endpoint deleted" }

Returns 404 not_found if there's no webhook with this ID on your account.

Send a test event

POST /v1/webhooks/{id}/test

Sends a sample sms.status event with status delivered to your endpoint. Use it to check that your server receives events and verifies signatures, without sending a real message.

curl -X POST https://api.rebeltexting.com/v1/webhooks/wh_01HQ3F5X8K2M9N4P6R7S8T9V0W/test \
  -H "Authorization: Bearer $REBELTEXT_API_KEY"
{
  "status": true,
  "message": "Test webhook queued",
  "data": { "delivery_id": "whd_01HQ4A2B3C4D5E6F7G8H9J0K1M" }
}

The test event has reference_id msg_test_00000000000000000000000000 and metadata {"test": "true"}, so you can recognise and ignore it.


Receiving events

Each event is sent as a POST with a JSON body.

Headers

Header Description
X-RebelText-Event The event type: sms.status or sms.inbound.
X-RebelText-Delivery-Id A unique ID for this event (whd_...). The same event keeps its ID when retried, so use it to ignore duplicates.
X-RebelText-Attempt Which attempt this is, starting at 1.
X-RebelText-Timestamp When it was sent, in Unix seconds.
X-RebelText-Signature The signature for checking that the event came from us. See Verifying signatures.

Body

The body is a message object, the same one GET /v1/messages/{reference_id} returns, so one parser handles both.

sms.status

{
  "reference_id": "msg_01HQ3F5X8K2M9N4P6R7S8T9V0W",
  "sender": "+15125550100",
  "receiver": "+15125550199",
  "sms_type": "sms",
  "message": "Your order #4471 has shipped.",
  "media_url": null,
  "status": "delivered",
  "remarks": "The message was delivered to the recipient.",
  "error_code": null,
  "carrier_error_code": null,
  "carrier_error_message": null,
  "segments": 1,
  "cost": "0.0095",
  "currency": "usd",
  "attempts": 1,
  "metadata": { "order_id": "4471" },
  "api_key_id": "key_01HQ2Z8Y7X6W5V4T3S2R1Q0P9N",
  "scheduled_at": null,
  "sent_at": "2026-09-17T10:04:11.881000Z",
  "received_at": null,
  "delivered_at": "2026-09-17T10:04:14.019000Z",
  "created_at": "2026-09-17T10:04:11.204000Z",
  "updated_at": "2026-09-17T10:04:14.019000Z"
}

sms.inbound

For incoming messages, sender is the person who texted you, receiver is your number, and reference_id starts with inb_.

{
  "reference_id": "inb_01HQ5B7C8D9E0F1G2H3J4K5M6N",
  "sender": "+15125550199",
  "receiver": "+15125550100",
  "sms_type": "sms",
  "message": "Thanks! When will it arrive?",
  "media_url": null,
  "status": "received",
  "remarks": "The message was received from the sender.",
  "error_code": null,
  "carrier_error_code": null,
  "carrier_error_message": null,
  "segments": 1,
  "cost": "0.00",
  "currency": "usd",
  "attempts": 0,
  "metadata": {},
  "api_key_id": null,
  "scheduled_at": null,
  "sent_at": null,
  "received_at": "2026-09-17T10:06:40.512000Z",
  "delivered_at": null,
  "created_at": "2026-09-17T10:06:40.730000Z",
  "updated_at": "2026-09-17T10:06:40.730000Z"
}

Responding


Verifying signatures

Always check that an event came from RebelText before you trust it.

X-RebelText-Signature is built like this:

base64( hex( HMAC-SHA256(raw_request_body, webhook_secret) ) )
  1. Compute HMAC-SHA256 of the raw request body, using your webhook's full secret (including whsec_) as the key.
  2. Write the result as a lowercase hex string.
  3. Base64-encode that hex string.
  4. Compare it with X-RebelText-Signature using a constant-time comparison.
  5. Reject the event if X-RebelText-Timestamp is more than 5 minutes from your current time, in either direction.

Use the raw body exactly as received. If you parse the JSON and re-encode it, the bytes change and the signature won't match.

Node.js (Express)

const crypto = require('crypto');
const express = require('express');

const app = express();
const SECRET = process.env.REBELTEXT_WEBHOOK_SECRET;

app.post('/hooks/rebeltext', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.get('X-RebelText-Signature') || '';
  const timestamp = Number(req.get('X-RebelText-Timestamp'));

  if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > 300) {
    return res.sendStatus(400);
  }

  const hex = crypto.createHmac('sha256', SECRET).update(req.body).digest('hex');
  const expected = Buffer.from(Buffer.from(hex).toString('base64'));
  const received = Buffer.from(signature);

  if (expected.length !== received.length || !crypto.timingSafeEqual(expected, received)) {
    return res.sendStatus(403);
  }

  const event = JSON.parse(req.body);
  res.sendStatus(200);

  // Process `event` after responding.
});

PHP

<?php
$rawBody   = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_REBELTEXT_SIGNATURE'] ?? '';
$timestamp = (int) ($_SERVER['HTTP_X_REBELTEXT_TIMESTAMP'] ?? 0);
$secret    = getenv('REBELTEXT_WEBHOOK_SECRET');

if (abs(time() - $timestamp) > 300) {
    http_response_code(400);
    exit;
}

$expected = base64_encode(hash_hmac('sha256', $rawBody, $secret));

if (!hash_equals($expected, $signature)) {
    http_response_code(403);
    exit;
}

$event = json_decode($rawBody, true);
http_response_code(200);

Python

import base64, hashlib, hmac, time

def verify(secret: str, signature: str, timestamp: str, body: bytes) -> bool:
    try:
        if abs(time.time() - int(timestamp)) > 300:
            return False
    except (TypeError, ValueError):
        return False

    digest = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    expected = base64.b64encode(digest.encode()).decode()
    return hmac.compare_digest(expected, signature)

Retries

If a delivery fails, we retry it on this schedule. Each delay varies by up to ±20%.

Retry Delay after previous attempt
1 30 seconds
2 2 minutes
3 10 minutes
4 1 hour
5 6 hours

If every retry fails, the delivery is marked dead. You can replay it once your endpoint is working again.

Paused endpoints. After 10 failed deliveries in a row, we pause sending to that endpoint. The first pause lasts 30 seconds. If it keeps failing, later pauses last 1, 2, 5 and then 15 minutes. When a pause ends we send one delivery first, and send the rest once it succeeds. While it's paused, disabled_until in List webhooks shows when sending resumes. A successful delivery resets the count and the pause length.

Pacing. We send at most 8 deliveries to one endpoint at a time. If events queue up, they arrive as a steady stream rather than all at once.

Delivery history and replay

List deliveries

GET /v1/webhook-deliveries

Shows recent webhook deliveries, newest first, including the payload we sent and the response your server gave. Use it to debug a webhook that isn't working.

Query parameter Default Description
message_id — Only deliveries for this message (msg_...).
limit 50 Number of deliveries to return, up to 200.
curl "https://api.rebeltexting.com/v1/webhook-deliveries?message_id=msg_01HQ3F5X8K2M9N4P6R7S8T9V0W" \
  -H "Authorization: Bearer $REBELTEXT_API_KEY"
{
  "status": true,
  "message": "Success",
  "data": {
    "deliveries": [
      {
        "id": "whd_01HQ4A2B3C4D5E6F7G8H9J0K1M",
        "endpoint_id": "wh_01HQ3F5X8K2M9N4P6R7S8T9V0W",
        "url": "https://example.com/hooks/rebeltext",
        "message_id": "msg_01HQ3F5X8K2M9N4P6R7S8T9V0W",
        "event_type": "sms.status",
        "status": "dead",
        "attempt": 6,
        "response_code": 502,
        "last_error": "unexpected status 502",
        "payload": { "reference_id": "msg_01HQ3F5X8K2M9N4P6R7S8T9V0W", "status": "delivered" },
        "created_at": "2026-09-17T10:04:14Z"
      }
    ]
  }
}

(payload is shortened here. The full message object is returned.)

Delivery status Meaning
pending Waiting to be sent. After a failed attempt, last_error and response_code explain the failure and next_retry_at shows when it will be retried.
sending Being sent now.
delivered Your server returned a 2xx (delivered_at).
dead All retries failed, or your server returned 410. Nothing more happens unless you replay it.

Replay a delivery

POST /v1/webhook-deliveries/{id}/replay

Sends a dead or delivered delivery again, starting a new set of retries. It keeps the same payload and delivery ID.

curl -X POST https://api.rebeltexting.com/v1/webhook-deliveries/whd_01HQ4A2B3C4D5E6F7G8H9J0K1M/replay \
  -H "Authorization: Bearer $REBELTEXT_API_KEY"
{ "status": true, "message": "Delivery queued for replay" }

Returns 404 not_found if the delivery doesn't exist, if it's still pending or sending, or if its webhook has been deleted.


Reference

The message object

Sending a message, reading a message, and every webhook event all return this object. Every field is always present. A field that doesn't apply is null, or {} for metadata.

Field Type Description
reference_id string The message ID: msg_... for messages you send, inb_... for messages you receive.
sender string The sending number in E.164 format. "" until a number is chosen, if you didn't set from_number.
receiver string The receiving number in E.164 format.
sms_type string sms.
message string The message text.
media_url string | null null for SMS.
status string See Message statuses.
remarks string A plain-English description of status. On failure, it ends with Reason: and the reason.
error_code string | null Set when status is failed, undelivered or rejected; otherwise null.
carrier_error_code string | null The carrier's own error code, as it sent it, when a failure came from the carrier. null when the carrier gave none, including for failures RebelText sets itself.
carrier_error_message string | null The carrier's own error text, as it sent it, up to 512 characters. null when the carrier gave none.
segments integer How many segments the message was split into.
cost string What this message costs, in US dollars, as a decimal string such as "0.0095". See Segments and pricing.
currency string The currency of cost. Always usd.
attempts integer Send attempts so far.
metadata object The metadata you sent, or {}.
api_key_id string | null The ID of the API key the message was sent with, such as key_01HQ.... null for messages you receive.
scheduled_at string | null The scheduled send time, if any.
sent_at string | null When the carrier accepted the message.
received_at string | null When an incoming message was received. null for messages you send.
delivered_at string | null When delivery to the phone was confirmed.
created_at string | null When the message was created.
updated_at string | null When the message last changed status.

All timestamps are UTC, such as 2026-09-17T10:04:14.019000Z.

Message statuses

A sent message usually moves through these statuses:

accepted → queued → sending → sent → delivered
                                   ↘ undelivered
Status Meaning Final? Charged?
accepted We received it and queued it for sending.
scheduled Waiting for its scheduled_at time.
queued Waiting to be sent.
sending Being handed to the carrier.
sent The carrier accepted it, but hasn't confirmed delivery yet. Yes
delivered The carrier confirmed it reached the phone. ✅ Yes
undelivered The carrier accepted it but couldn't deliver it. ✅ Yes
failed It couldn't be sent. ✅ Only if the carrier accepted it first
rejected Refused before sending. ✅ No
cancelled Cancelled before it was sent. ✅ No
received An incoming message (sms.inbound). ✅ Yes

A message never moves back to an earlier status. sent usually becomes delivered or undelivered within seconds, but some carriers never confirm delivery. In that case sent may be the last status you see.

Webhooks are sent for scheduled, sending, sent, delivered, undelivered and failed. They aren't sent for accepted, because you already have that status from the send response.

Failure codes

When status is failed, error_code tells you why. These codes come from RebelText. Any other value comes from the carrier, and carrier_error_code and carrier_error_message then carry the carrier's own code and wording, so you can tell a carrier's spam block from a problem on the route.

error_code Meaning How to fix it
no_sender_number Your account has no number available to send from. Add a number to your account.
sender_not_sms_capable The sending number can't send SMS. Send from a different number, or leave out from_number.
sender_not_found from_number isn't one of your numbers. Use one of your account's numbers, or leave out from_number.

Error codes

code Status Meaning
invalid_request 400, 422 The request is malformed or failed validation. For 422, errors lists the problems by field.
unauthenticated 401 The API key is missing or invalid.
account_inactive 403 Your account is suspended or closed.
test_key_in_production 403 You used a rt_test_ key against production.
insufficient_balance 402 Your balance is too low to send this message. Top up, or wait for your plan's next renewal.
subscription_required 402 Your account has no active subscription. Choose a plan in the portal; sending resumes on the next request.
not_found 404 The resource doesn't exist or doesn't belong to your account.
payload_too_large 413 The request body is too large.
recipient_suppressed 422 The recipient has opted out of messages from you.
rate_limited 429 Too many requests. Wait for the number of seconds in Retry-After, then retry.
internal_error 500, 503 Something went wrong on our side. Retry with the same Idempotency-Key. If the problem continues, contact support with the X-Request-Id.