REST API · v1

Send Message API

Submit outbound SMS messages using your project API key. The endpoint accepts a message submission, persists the logical message record, and queues delivery asynchronously.

Response202 Accepted (async)
AuthX-API-Key header
Content-Typeapplication/json

Note: 202 Accepted means the platform queued the message — not that the carrier has delivered it. Current v1 limitation: API key auth is supported for message submission only. Read endpoints require Sanctum auth.

Endpoint

Send a POST request to a single path. The project context is supplied via the id field in the request body, not in the URL.

POST/api/v1/send-message

# Project context is passed via the request body id field

Full URL example

https://api.lomisend.com/api/v1/send-message

Authentication

Send the project API key in the X-API-Key header. Keys follow the format {prefix}_{type}_{64_hex_characters}.

sms_live_<64 hex>

Live key — real delivery

sms_test_<64 hex>

Test key — sandboxed

HTTP headers
Content-Type: application/json
Accept: application/json
X-API-Key: sms_live_your_64_hex_character_key_here
X-Request-ID: your-request-id   # optional but echoed in error responses

X-Request-ID is optional but useful — RFC 7807 error responses echo it back as request_id, making it easy to correlate failures in your logs.

Request body

JSON object with the following fields:

FieldTypeRequired
idstringYes
tostringYes
sender_idstringConditional
bodystringYes
1

id is a client assertion only. The backend compares it against the authenticated API key project and derives execution context from the key snapshot.

2

sender_id is matched by sender name/value. Legacy UUID-style sender payloads are rejected.

3

If a live-key request omits sender_id, the project must have an active default sender ID or the request fails with 422.

4

encoding and metadata must not be provided. Both fields are rejected if the client sends them.

Live key example

LIVE

Use a live key after sender IDs, subscription access, and billing are ready.

request.sh
bash
curl -X POST "https://api.lomisend.com/api/v1/send-message" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: sms_live_your_64_hex_character_key_here" \
  -H "X-Request-ID: msg-001" \
  -d '{
    "id": "01926d3e-8f1c-7a2b-b8e5-3f4a5c6d7e8f",
    "to": "+251911234567",
    "sender_id": "MyBrand",
    "body": "Hello from our platform!"
  }'

202 Accepted response

response.json
json
{
  "data": {
    "id": "01JRXXXXXXXXXXXXXXXXXXXXX",
    "to_number": "+251911234567",
    "sender_address": "MyBrand",
    "body": "Hello from our platform!",
    "segment_count": 1,
    "attempt_count": 1,
    "status": "pending",
    "created_at": "2026-03-18T12:00:00+00:00",
    "mode": "live",
    "charged_from": "quota",
    "cost_cents": 0
  }
}

Test key example

TEST

Test keys use the same endpoint but are fully sandboxed. Use them while building your integration.

sender_id must equal the configured platform test sender name

to must already be approved for that project

Accepted responses return mode: "test"

No subscription or billing cycle required

test-request.sh
bash
curl -X POST "https://api.lomisend.com/api/v1/send-message" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: sms_test_your_64_hex_character_key_here" \
  -d '{
    "id": "01926d3e-8f1c-7a2b-b8e5-3f4a5c6d7e8f",
    "to": "+251911000001",
    "sender_id": "PlatformTest",
    "body": "Test mode send"
  }'

202 Accepted response

response.json
json
{
  "data": {
    "id": "01JRXXXXXXXXXXXXXXXXXXXXX",
    "to_number": "+251911000001",
    "sender_address": "PlatformTest",
    "body": "Test mode send",
    "segment_count": 1,
    "attempt_count": 1,
    "status": "pending",
    "created_at": "2026-03-18T12:00:00+00:00",
    "mode": "test",
    "charged_from": "wallet",
    "cost_cents": 7
  }
}

Response fields

Successful responses wrap the message object under data.

FieldType
idstring
to_numberstring
sender_addressstring
bodystring
segment_countinteger
attempt_countinteger
statusstring
created_atstring
modestring
charged_fromstring | null
cost_centsinteger

Error handling

The API uses RFC 7807 problem responses. Handle errors by slug, not only by status code, as multiple slugs can share the same HTTP status.

error-response.json
json
{
  "type": "https://api.lomisend.com/errors/validation-failed",
  "title": "Validation Failed",
  "status": 422,
  "detail": "The given data was invalid.",
  "errors": {
    "to": ["A recipient phone number is required."]
  },
  "instance": "/api/v1/workspaces/.../projects/.../messages",
  "request_id": "msg-001"
}
StatusSlug
401unauthenticated
401invalid-api-key-format
401invalid-api-key
401api-key-expired
401api-key-rotation-expired
402insufficient-balance
402no-billing-cycle
403subscription-required
403workspace-suspended
403api-key-insufficient-scope
422validation-failed
422test-sender-id-required
422recipient-not-approved-for-test
500invalid-overage-rate
500platform-not-configured

Validation rules

  • ·id is required and must be the target project UUID
  • ·to must be E.164 format — e.g. +251911234567
  • ·body is required
  • ·sender_id fails when the value is unknown, inactive, belongs to another project, or no default sender is available
  • ·encoding is rejected if the client provides it — encoding is resolved internally
  • ·metadata is rejected if the client provides it — metadata is generated internally when needed

Live vs test keys

The same endpoint handles both key types. Behavior differs in several ways:

BehaviorLive keyTest key
Requires active subscription
Requires open billing cycle

unless wallet can't cover test rate

Sender resolutionProject sender or active defaultPlatform test sender only
Recipient restrictionNormal send rulesMust be approved for project
mode in responselivetest
1

Use a test key while building your integration.

2

Move to a live key after sender IDs, subscription, and billing are ready.

3

Do not hardcode sender UUIDs — the contract expects sender names.

Integration notes

Checklist for a reliable integration:

1

Treat 202 as asynchronous acceptance only

The carrier has not delivered the message yet. Poll delivery status or use webhooks to track the outcome.

2

Persist the message id

Keep the returned message id on your side as your platform reference for support and correlation.

3

Log X-Request-ID

If you send X-Request-ID, keep it in your logs. It is echoed in RFC 7807 error responses as request_id.

4

Handle errors by slug

Match RFC 7807 errors on the slug field, not only HTTP status code. Multiple slugs share the same status.

5

Default sender for live traffic

If you omit sender_id for live requests, ensure the project has one active default sender configured or the request will fail with 422.

Ready to send your first message?

Create a free account, pick up your API key from the dashboard, and you can send your first SMS in minutes.

© 2026 Lomisend · API reference v1