API reference · v1

Reach12 API reference

Send SMS and email through Reach12 with a key of your own. Base URL:

https://api.reach12.com/v1

Version 1 covers SMS (Israeli mobiles, through InforU, under your own sender name) and email (one recipient per request, from an address on a domain we verified for you). There is no voice or call endpoint in v1, and no WhatsApp; GET /policy/contact-window answers the hours question for a call you place yourself.

Nothing is open by default. We create your account, set what it may send, and mint your keys. There is no self-service signup yet: ask for early access.

Keys

KeyDoes
sd_live_...Sends for real.
sd_test_...Runs the whole path (validation, caps, opt-outs, hours) and records the message, but never reaches a carrier. Test messages, opt-outs and caps live in a sandbox of their own, apart from live.

A key is shown once, when it is minted. We keep only its hash, so a lost key cannot be recovered: tell us, and we revoke it and mint a new one.

Send it as a header, either spelling:

X-Api-Key: sd_live_...
    Authorization: Bearer sd_live_...

Every answer carries X-SendOS-Mode: live|test, so a test key pasted into production is noticed at the first response.

Conventions

Send an SMS

POST /sms

Field
toAn Israeli mobile: +9725XXXXXXXX or 05XXXXXXXX.
textUp to 670 characters (ten Hebrew segments).
categorytransactional, service (default) or marketing.
kindOptional label for your own log, e.g. appointment.reminder.
idempotencyKeyOptional (or the header).

A marketing SMS must open with the word פרסומת (Israeli Communications Law, section 30A), and goes out only 09:00 to 20:00 Israel time, Friday and a holiday eve until 14:00, never on Shabbat or a holiday. Outside those hours it is refused with the next allowed moment.

curl https://api.reach12.com/v1/sms \
      -H "X-Api-Key: $REACH12_KEY" \
      -H "Idempotency-Key: reminder-4812" \
      -H "Content-Type: application/json" \
      -d '{"to":"0501234567","text":"תזכורת: התור שלך מחר ב-10:00","kind":"appointment.reminder"}'
{ "id": "cm1...", "channel": "sms", "status": "sent", "to": "+972501234567", "mode": "live" }

Send an email

POST /email

Field
toOne address.
subjectOne line, up to 200 characters.
html, textAt least one.
replyToOptional.
category, kind, idempotencyKeyAs for SMS.

The sender is always the address we set up for your account; attachments are not supported in v1.

curl https://api.reach12.com/v1/email \
      -H "X-Api-Key: $REACH12_KEY" \
      -H "Content-Type: application/json" \
      -d '{"to":"dana@example.co.il","subject":"הקבלה שלך","text":"תודה!","category":"transactional"}'

Your message log

GET /messages?channel=sms|email&before=<ISO date>&limit=1..100

Newest first, only your own (a test key sees only test messages). Page with nextBefore.

curl "https://api.reach12.com/v1/messages?channel=sms&limit=20" -H "X-Api-Key: $REACH12_KEY"
{ "data": [ { "id": "cm1...", "channel": "sms", "to": "+972501234567", "text": "...", "status": "sent", "error": null, "createdAt": "..." } ], "hasMore": false, "nextBefore": null }

status is sent, failed (then error is vendor_failed) or blocked.

Your account

GET /account

Who the key belongs to, what the account may send, its daily caps, and how much of today's cap is used. today counts exactly what the caps count: an SMS the carrier accepted or refused (a blocked one never reached a carrier) and every email attempt, in Israel's day. A test key sees its own sandbox's day.

curl https://api.reach12.com/v1/account -H "X-Api-Key: $REACH12_KEY"
{
      "id": "acme-clinic",
      "name": "Acme Clinic",
      "status": "active",
      "mode": "live",
      "key": { "name": "Production server", "prefix": "sd_live_7Hq2" },
      "channels": {
        "sms": { "enabled": true, "sender": "AcmeClinic" },
        "email": { "enabled": true, "from": "Acme Clinic <hello@acmeclinic.com>" }
      },
      "caps": { "sms": 500, "email": 1000 },
      "today": {
        "date": "2026-11-10",
        "resetsAt": "2026-11-10T22:00:00.000Z",
        "sms": { "used": 42, "cap": 500, "remaining": 458 },
        "email": { "used": 7, "cap": 1000, "remaining": 993 }
      },
      "pricing": { "currency": "ILS", "monthlyFee": 25000, "perSms": 8, "perEmail": 1, "perCallMinute": 30 }
    }

pricing is null until we set your prices with you. Every amount is in minor units: agorot for ILS, cents for USD.

Usage and what you owe

GET /usage?month=YYYY-MM

One month, by channel and status, with a series by day. month defaults to the current month; months are Israel time, so a message sent at 00:30 on the 1st in Israel belongs to that month. A month that has not begun is refused with invalid_month. For the current month, complete is false and daily stops at today.

When the account is priced, amountDue is the monthly fee plus every message the carrier accepted (sent). A failed or blocked message is never billed, and neither is anything sent with a test key: for a test key billable is false and amountDue is null.

curl "https://api.reach12.com/v1/usage?month=2026-11" -H "X-Api-Key: $REACH12_KEY"
{
      "month": "2026-11",
      "mode": "live",
      "timezone": "Asia/Jerusalem",
      "from": "2026-10-31T22:00:00.000Z",
      "to": "2026-11-30T22:00:00.000Z",
      "complete": false,
      "sms": { "sent": 1200, "failed": 30, "blocked": 9 },
      "email": { "sent": 450, "failed": 4 },
      "daily": [ { "date": "2026-11-01", "sms": { "sent": 40, "failed": 1, "blocked": 0 }, "email": { "sent": 12, "failed": 0 } } ],
      "billable": true,
      "amountDue": {
        "currency": "ILS",
        "lines": [
          { "item": "monthly_fee", "quantity": 1, "unitAmount": 25000, "amount": 25000 },
          { "item": "sms", "quantity": 1200, "unitAmount": 8, "amount": 9600 },
          { "item": "email", "quantity": 450, "unitAmount": 1, "amount": 450 },
          { "item": "call_minutes", "quantity": 0, "unitAmount": 30, "amount": 0 }
        ],
        "total": 35050
      }
    }

The lines always add up to total. The amount for the current month is what it comes to so far.

Your dashboard

Everything above is also at reach12.com/dashboard: paste a key to see the account, the month's usage and amount, the message log and the opt-out list. The key stays in that browser tab only and is sent to nothing but this API.

Opt-outs

One list per account, per channel (all, sms, email, call, whatsapp; all holds every channel). An address is a phone number or an email.

# who asked not to be contacted
    curl "https://api.reach12.com/v1/opt-outs?channel=sms" -H "X-Api-Key: $REACH12_KEY"
    
    # someone replied STOP
    curl https://api.reach12.com/v1/opt-outs -H "X-Api-Key: $REACH12_KEY" -H "Content-Type: application/json" \
      -d '{"address":"0501234567","channel":"sms","reason":"replied STOP"}'
    
    # they asked to be let back in ("all" lifts every channel)
    curl -X DELETE https://api.reach12.com/v1/opt-outs -H "X-Api-Key: $REACH12_KEY" -H "Content-Type: application/json" \
      -d '{"address":"0501234567","channel":"sms"}'

May I contact someone now?

GET /policy/contact-window?kind=call|message

{ "kind": "call", "allowed": false, "nextWindow": "2026-11-15T08:00:00.000Z" }

Calls: 10:00 to 19:30 (Friday and a holiday eve until 13:00). Messages: 09:00 to 20:00 (until 14:00). Never on Shabbat or a holiday. Israel time.

Refusals

HTTPcodeMeans
400invalid_request, invalid_phone, missing_text, text_too_long, invalid_category, invalid_kind, marketing_label_required, invalid_email, missing_subject, invalid_subject, missing_body, body_too_large, invalid_reply_to, attachments_not_supported, invalid_address, invalid_idempotency_key, invalid_monthThe request itself.
401unauthorizedNo valid key. The same answer for every reason, on purpose.
403channel_not_enabledThis account may not send on that channel yet.
404not_foundNo such endpoint.
409opted_outThe recipient asked not to be contacted on this channel.
409idempotency_in_progressA request with this key is still being processed.
422outside_hoursA marketing message outside the hours; nextWindow says when.
422idempotency_mismatchThis key was used with a different request.
429rate_limitedToo many requests (per key), or too many failed keys from your address.
429daily_cap_reachedThe account's daily cap for the channel; resetsAt is midnight in Israel.
502vendor_failedThe carrier refused the message; it is in your log with status failed.
503unavailableThe channel or the API is not available right now.

Ready for a key?

We onboard early-access accounts one by one and set up your sender name and domain with you.

Get early access