Sending Your First SMS

Once your account is set up and a number is provisioned, sending a message takes one API call. This article walks through the pre-flight, the call itself, delivery tracking, and what to do if something goes wrong.

Just want to try the API? You don't need a brand, campaign, or number to send your first request. Use the shared test phone described in Testing SMS With the Shared Test Phone — same endpoint, same SDK, just prefix the body with [Otter]. Switch the from to your real number when you're ready for production traffic.

Pre-flight checklist

Step Where
Brand registered and Verified Brand Registry
10DLC campaign in Approved status Campaigns → 10DLC
At least one number attached to the campaign Numbers → Manage
Number belongs to a messaging profile Messaging Profiles
API key with messages.send permission API Keys
Balance sufficient for at least one segment Billing → Overview

If any box is unchecked, the API returns a clear error pointing to the step you missed.

The API call

curl -X POST https://api.otterblocx.com/messaging/send \
  -H "x-access-key-id: $BLOCX_ACCESS_KEY_ID" \
  -H "x-secret-access-key: $BLOCX_SECRET_ACCESS_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "+15551234567",
    "to":   "+15557654321",
    "body": "Hello from Blocx!",
    "type": "SMS"
  }'

Response (202 Accepted):

{
  "messageId": "msg_...",
  "type": "SMS",
  "status": "QUEUED",
  "to":   "+15557654321",
  "from": "+15551234567"
}

Full reference: api.otterblocx.com/docs.

Using the SDK

import { createBlocxClient, Messaging } from '@otterlabs/blocx'

const { client } = createBlocxClient({
  accessKeyId:     process.env.BLOCX_ACCESS_KEY_ID!,
  secretAccessKey: process.env.BLOCX_SECRET_ACCESS_KEY!,
})

const res = await Messaging.sendMessage({
  client,
  body: {
    from: '+15551234567',
    to:   '+15557654321',
    body: 'Hello from Blocx!',
    type: 'SMS',
  },
})

console.log(res.data?.messageId)

Install: npm install @otterlabs/blocx. Source on npm: @otterlabs/blocx.

MMS

Set type: 'MMS' and pass mediaUrls:

await Messaging.sendMessage({
  client,
  body: {
    from: '+15551234567',
    to:   '+15557654321',
    body: 'Check this out',
    type: 'MMS',
    mediaUrls: ['https://cdn.example.com/image.png'],
  },
})

The sending number must have MMS capability. Check at Numbers → Manage.

Tracking delivery

Configure a webhook endpoint at Developer → Webhooks and subscribe to message.sent, message.delivered, and message.failed to receive each transition. Status is also visible per-message at Messaging → Debug and aggregated at Messaging → Reports.

Compliance basics

Debugging a failed send

If the API returns an error or the message later fails:

  1. Note the message ID from the response (or webhook event).
  2. Open Messaging → Debug and paste the ID.
  3. The timeline shows the full path: request received → routing decision → carrier handoff → carrier response → delivery receipt.
  4. Each step displays its exact payload and any error codes.

Common errors:

Code Meaning Fix
30003 Unreachable handset Retry later
30005 Unknown destination Number doesn't exist
30006 Landline / can't receive SMS Validate numbers before sending
30007 Filtered by carrier Review content and opt-in; see compliance inbox
30008 Carrier-side unknown Retry with backoff
21610 Recipient opted out Stop sending to this number

See Messaging Reports and Debugging Tools for deeper troubleshooting.

Next steps

Related articles