Mailchk
API DocsSMS Testing

SMS Testing API

Provision real test phone numbers, capture inbound SMS, and extract OTP codes automatically. Perfect for CI pipelines and automated SMS verification testing.

Overview

The SMS Testing API lets you provision real US phone numbers that capture all incoming SMS. Send any text message to the provisioned number — from your app, your SMS provider, or your CI pipeline — and retrieve the full message content with auto-extracted OTP codes via API.

1-48h

Configurable message expiry

3

Active inboxes per user

50

Messages per inbox

1

API call per inbox created

Paid plans only

SMS Testing requires a Business or Enterprise plan, or purchased credits. Each inbox creation costs 1 API call. Listing, reading, and deleting are free.

Quick Start

# 1. Provision a test phone number
curl -X POST "https://api.mailchk.io/v1/test-sms-inboxes" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"label": "OTP flow", "expires_in": "24h"}'

# Response:
# {
#   "id": "a1b2c3d4-...",
#   "phone_number": "+12025557842",
#   "label": "OTP flow",
#   "expires_in": "24h",
#   "message_count": 0,
#   "created_at": "2026-03-14T10:00:00.000Z"
# }

# 2. Send an SMS to the test number from your app

# 3. Poll for messages
curl "https://api.mailchk.io/v1/test-sms-inboxes/a1b2c3d4-.../messages" \
  -H "X-API-Key: YOUR_API_KEY"

# 4. Inspect a captured message (with auto-extracted OTP codes)
curl "https://api.mailchk.io/v1/test-sms-inboxes/a1b2c3d4-.../messages/MSG_ID" \
  -H "X-API-Key: YOUR_API_KEY"

Base URL

https://api.mailchk.io

Authenticate with your API key in the X-API-Key header. See the authentication guide for details.

Endpoints

POST /v1/test-sms-inboxesCosts 1 API call

Provision a test phone number and create a new SMS inbox. Returns a real US phone number that captures all incoming SMS. Requires a label and optional expiry duration.

Auth: API Key or JWT

Request Body

{
  "label": "OTP flow",         // required, max 100 chars
  "expires_in": "24h"          // optional: "1h", "6h", "12h", "24h", "48h"
}

Response

{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "phone_number": "+12025557842",
  "label": "OTP flow",
  "expires_in": "24h",
  "message_count": 0,
  "created_at": "2026-03-14T10:00:00.000Z"
}
GET /v1/test-sms-inboxes

List all SMS test inboxes for the authenticated user.

Auth: API Key or JWT

Response

{
  "inboxes": [
    {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "phone_number": "+12025557842",
      "label": "OTP flow",
      "expires_in": "24h",
      "message_count": 3,
      "created_at": "2026-03-14T10:00:00.000Z"
    }
  ]
}
DELETE /v1/test-sms-inboxes/:id

Delete an SMS test inbox, release the phone number back to the carrier, and remove all stored messages.

Auth: API Key or JWT

Response

{
  "success": true
}
GET /v1/test-sms-inboxes/:id/messages

List messages received by an SMS test inbox. Returns metadata and body preview (no full content or extracted codes).

Auth: API Key or JWT

Response

{
  "messages": [
    {
      "id": "msg-1234-5678-abcd",
      "from_number": "+14155551234",
      "to_number": "+12025557842",
      "body_preview": "Your verification code is 384729...",
      "segments": 1,
      "media_urls": [],
      "received_at": "2026-03-14T10:05:30.000Z"
    }
  ]
}
GET /v1/test-sms-inboxes/:id/messages/:msgId

Get the full content of a captured SMS — body, auto-extracted OTP codes, and link validation results.

Auth: API Key or JWT

Response

{
  "id": "msg-1234-5678-abcd",
  "inbox_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "from_number": "+14155551234",
  "to_number": "+12025557842",
  "body": "Your verification code is 384729. It expires in 10 minutes.",
  "segments": 1,
  "media_urls": [],
  "codes": [
    {
      "value": "384729",
      "type": "otp",
      "context": "Your verification code is 384729"
    }
  ],
  "links": [
    {
      "url": "https://example.com/verify?token=abc",
      "text": "https://example.com/verify?token=abc",
      "status": 200,
      "ok": true,
      "error": null
    }
  ],
  "received_at": "2026-03-14T10:05:30.000Z"
}

Message Detail Fields

Fields returned by GET /v1/test-sms-inboxes/:id/messages/:msgId:

FieldTypeDescription
idstringUnique message identifier
inbox_idstringThe SMS test inbox that received this message
from_numberstringSender phone number in E.164 format
to_numberstringRecipient phone number (the test inbox number)
bodystringFull SMS message body text
segmentsnumberNumber of SMS segments (long messages are split into multiple segments)
media_urlsarrayMMS media attachment URLs (empty for SMS-only messages)
codesarrayAuto-extracted OTP/verification codes with value, type, and context
linksarrayAll URLs in the message with HTTP status check results
received_atstringWhen the SMS was received (ISO 8601)

Error Codes

400

Bad Request

Maximum active inboxes reached (3 per user), missing label, or invalid expires_in value

401

Authentication Required

Missing or invalid API key / JWT token. Code: AUTH_REQUIRED

403

Forbidden

SMS Testing requires a Business or Enterprise plan, or purchased credits

404

Not Found

Inbox or message not found, or inbox belongs to a different user

429

Usage Limit Exceeded

Subscription limit reached and no credits remaining. Code: USAGE_LIMIT_EXCEEDED

502

Bad Gateway

Failed to provision a phone number from the carrier. Try again later.

CI/CD Integration

Test your SMS flows in CI. Provision a test number before your test suite runs, trigger SMS through your app, then assert on the captured content and extracted OTP codes.

GitHub Actions Example
# GitHub Actions example — test your OTP SMS flow
name: SMS Integration Tests
on: [push]

jobs:
  test-sms:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Provision test phone number
        id: inbox
        run: |
          RESPONSE=$(curl -s -X POST "https://api.mailchk.io/v1/test-sms-inboxes" \
            -H "X-API-Key: ${{ secrets.MAILCHK_API_KEY }}" \
            -H "Content-Type: application/json" \
            -d '{"label": "CI run #${{ github.run_number }}", "expires_in": "1h"}')
          echo "INBOX_ID=$(echo $RESPONSE | jq -r '.id')" >> $GITHUB_OUTPUT
          echo "PHONE=$(echo $RESPONSE | jq -r '.phone_number')" >> $GITHUB_OUTPUT

      - name: Trigger OTP send
        run: |
          curl -X POST "http://localhost:3000/api/send-otp" \
            -H "Content-Type: application/json" \
            -d '{"phone": "${{ steps.inbox.outputs.PHONE }}"}'

      - name: Wait for SMS & extract OTP
        run: |
          sleep 10
          MESSAGES=$(curl -s "https://api.mailchk.io/v1/test-sms-inboxes/${{ steps.inbox.outputs.INBOX_ID }}/messages" \
            -H "X-API-Key: ${{ secrets.MAILCHK_API_KEY }}")

          COUNT=$(echo $MESSAGES | jq '.messages | length')
          if [ "$COUNT" -eq "0" ]; then
            echo "No SMS received!"
            exit 1
          fi

          MSG_ID=$(echo $MESSAGES | jq -r '.messages[0].id')
          DETAIL=$(curl -s "https://api.mailchk.io/v1/test-sms-inboxes/${{ steps.inbox.outputs.INBOX_ID }}/messages/$MSG_ID" \
            -H "X-API-Key: ${{ secrets.MAILCHK_API_KEY }}")

          OTP=$(echo $DETAIL | jq -r '.codes[0].value')
          echo "OTP extracted: $OTP"

      - name: Cleanup
        if: always()
        run: |
          curl -s -X DELETE "https://api.mailchk.io/v1/test-sms-inboxes/${{ steps.inbox.outputs.INBOX_ID }}" \
            -H "X-API-Key: ${{ secrets.MAILCHK_API_KEY }}"

OTP & 2FA Verification

Provision a number, trigger OTP send, extract the code automatically, and verify the login flow end-to-end.

SMS Notifications

Assert that order confirmations, appointment reminders, and shipping alerts arrive with correct content.

Marketing SMS

Test promotional SMS campaigns. Verify links work, content is correct, and opt-out instructions are present.

Ready to Start Testing?

Provision your first test phone number in seconds. Use the dashboard or integrate via API.