An hour is realistic for a first campaign, but only if you do the steps in the right order. Almost every team that takes a week does the same thing: they build the whole list pipeline first, then discover on the day of launch that the sender is not registered in one of their markets. Get one message to one handset first. Everything else is easier afterwards.
Take a sandbox key
Keys are scoped two ways: to an environment, and to a set of route classes. A sandbox key can call every endpoint but never reaches a real network. Scope it narrowly now and widen it later, because a key that can only send transactional traffic cannot be turned into a marketing blast by accident.
what a key looks like
mr_sbx_7c41ad9e... sandbox, all routes
mr_live_08b2f3c1... production, transactional + marketingPut it in an environment variable. There is no session and no refresh token, so the key is the only credential in play and it must never reach a client bundle or a repository.
Reserve the sender
Every message needs a sender the destination network recognises. In sandbox you can reserve any alphanumeric string instantly. In production the same call submits a registration to each network in the market, and that is the step that actually gates a launch. Start it on day one, not on the day you are ready.
POST /v1/senders
curl https://api.messagereach.net/v1/senders \
-H "Authorization: Bearer $MR_KEY" \
-d sender="LUXPLAY" \
-d market="CY" \
-d type="alphanumeric"Write the template, not the message
Templates render server side with locale fallback and give you a stable identifier to report against later. A raw body works, but you lose per-market character-set handling and you lose the ability to see which version of the copy performed.
template body
Hi {{first_name}}, {{spins}} free spins are on your
account until {{expires}}. Play at {{link}}
18+. Play responsibly. Reply STOP to opt out.Put the opt-out in the template, not in your sending code. Networks in most regulated markets check for it, and a template is the only place it cannot be forgotten.
Send one message
One destination, one template reference, one idempotency key. Read the response before you go anywhere near a list.
POST /v1/messages
curl https://api.messagereach.net/v1/messages \
-H "Authorization: Bearer $MR_KEY" \
-H "Idempotency-Key: first-campaign-001" \
-d sender="LUXPLAY" \
-d to="+35799xxxxxx" \
-d template="free_spins_v4" \
-d channel="auto"
{
"id": "msg_01K4TQ8V",
"state": "queued",
"channel": "sms",
"route": "CY-direct-02",
"segments": 1,
"eta_ms": 180
}Always send an idempotency key. It is honoured for twenty four hours, which means a retried request after a timeout returns the original message instead of sending a second one. During an incident this is the difference between a delay and messaging thirty thousand people twice.
Read the receipt
The response above says the gateway accepted the message. It is not proof of delivery. State moves through queued, submitted, then either delivered or failed with a carrier reason attached. Poll while you are learning, then move to events before you scale.
GET /v1/messages/msg_01K4TQ8V
{
"id": "msg_01K4TQ8V",
"state": "delivered",
"channel": "sms",
"segments": 1,
"timeline": [
{ "state": "queued", "at": "2026-08-21T19:35:02Z" },
{ "state": "submitted", "at": "2026-08-21T19:35:02Z" },
{ "state": "delivered", "at": "2026-08-21T19:35:07Z" }
]
}Only now, the list
With one delivered message behind you, a campaign is the same call in a loop with a consent check in front of it. The gateway checks consent anyway and will refuse the send, but checking first turns a rejected batch into a clean segment.
- Filter your audience against the consent endpoint before you build the batch.
- Derive the idempotency key from something stable in your own system, such as a campaign id plus a player id.
- Send in batches you can reason about. A thousand at a time is plenty to spot a problem early.
- Watch the first hundred receipts before releasing the rest. Reason codes tell you fast if a template is being filtered.
Moving to production
Swap the key and keep the code. Sandbox and production share one schema, one set of error codes and one state machine, so the only differences you meet are real ones: registration status, market licensing, and the fact that carriers occasionally take their time. Before you switch, check that your webhook consumer returns a 2xx in under two seconds and is safe to call twice, and that the markets on your licence are attached to the key. Sends outside them are refused with a 409.


