MoSMS Developer API

Send SMS programmatically, track delivery, and manage contacts — the same REST API that powers the MoSMS dashboard.

MoSMS Developer API

Send SMS (and soon WhatsApp) programmatically through MoSMS — a multi-tenant SMS/WhatsApp messaging platform for Tanzania.

Base URL

  • https://mosms.co.tz/api

Set the collection variable baseUrl if you ever need to point requests elsewhere.

Authentication

MoSMS uses Bearer token authentication (Laravel Sanctum personal access tokens) — there is no separate api_key parameter.

  1. Call Auth → Login (or Auth → Register for a brand-new account) with your email/password.
  2. Copy the token from the response.
  3. Send it on every subsequent request as a header:

`` Authorization: Bearer <token> ``

This collection is pre-wired to do this automatically: Login and Register each run a test script that saves the returned token into the collection variable token, and every other request inherits Bearer auth from the collection root using {{token}}. Just run Login once, then everything else works.

Permissions (RBAC)

Each tenant user has a granular permission set (returned as permissions by Login/Me). Calling an endpoint your token lacks permission for returns 403. Common permissions referenced below: messages.send, bulk_sms.send, group_sms.send, messages, messages.view, messages.check_status, messages.cancel_scheduled, messages.delete, dashboard, contacts*, groups*, templates*, sms_logs, delivery_reports, purchase_sms*, events, events.add, events.view, events.edit, events.delete, events.manage_invitees, events.manage_payments, events.send_sms.

Rate limits

The three Send SMS endpoints (/sms/send, /sms/send-bulk, /sms/send-group/{group}) are throttled to 60 requests/minute per tenant. Exceeding this returns 429 Too Many Requests. No other endpoint is currently rate-limited.

Phone numbers

Send numbers in any reasonable local or international format — 0712345678, +255712345678, or 255712345678 all work; MoSMS normalizes to the 255XXXXXXXXX MSISDN format server-side.

Credit / segment cost

SMS is billed per segment: 1 credit per 160 characters for plain GSM-7 text, or 1 credit per 70 characters once the message contains any Unicode (emoji, non-Latin script). Sends that would exceed your remaining balance return 422 with the shape:

{ "message": "Insufficient SMS balance...", "balance": 0, "required": 2 }

Common error format

Validation errors follow Laravel's default shape:

{
  "message": "The given data was invalid.",
  "errors": { "to": ["The to field is required."] }
}

Other errors are { "message": "..." } with an appropriate HTTP status (401, 403, 404, 422, 429, 500, 502).

Folders in this collection

  1. Auth — register, login, password reset, profile
  2. Dashboard & Balance — account overview, SMS credit balance
  3. Send SMS — single, bulk, and group sends
  4. Messages — history, status polling, cancel, delete
  5. Delivery Reports & Logs — raw gateway-side records
  6. Contacts — CRUD + CSV/vCard import
  7. Groups — CRUD + membership management
  8. Templates — reusable message bodies with placeholders
  9. SMS Packages & Purchases — buying more credits
  10. Events — invitees, pledge/payment tracking, and multi-stage SMS/WhatsApp campaigns
  11. Webhooks (reference only) — inbound callbacks MoSMS itself receives; not callable by clients
  12. Coming Soon — WhatsApp Send API — planned endpoints, not yet live

Auth

Account creation, login, and password management. All endpoints here except Get Current User / Profile / Logout are public (no token required).

POST /register No auth required

Register (Create Account)

Self-service sign-up. Creates a new tenant organization plus its first admin user, and returns a Bearer token you can use immediately — no separate login call needed.

The org_name is slugified to derive a unique organization slug; if the slug or email is already taken you get a 422.

Headers

Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "org_name": "Acme Traders",
  "phone": "255712345678",
  "name": "Jane Doe",
  "email": "jane@acme.co.tz",
  "password": "SecurePass123",
  "password_confirmation": "SecurePass123"
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/register' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "org_name": "Acme Traders",
  "phone": "255712345678",
  "name": "Jane Doe",
  "email": "jane@acme.co.tz",
  "password": "SecurePass123",
  "password_confirmation": "SecurePass123"
}'

Example responses

201 Created
{
  "message": "Registration successful.",
  "token": "1|abcdEFGH1234567890tokenExample",
  "user_type": "user",
  "user": {
    "id": 12,
    "name": "Jane Doe",
    "email": "jane@acme.co.tz",
    "role": "admin",
    "tenant": {
      "id": 7,
      "name": "Acme Traders",
      "slug": "acme-traders"
    }
  }
}
422 Organization exists
{
  "message": "An organization with this name already exists."
}
POST /login No auth required

Login

Unified login for both tenant (developer) accounts and platform Super Admins — it checks the admin table first, then falls back to tenant users. Returns a Sanctum Bearer token to use on every subsequent request as Authorization: Bearer <token>, plus the caller's permissions list (granular RBAC — check this before assuming an endpoint is reachable).

Headers

Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "email": "jane@acme.co.tz",
  "password": "SecurePass123"
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/login' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "email": "jane@acme.co.tz",
  "password": "SecurePass123"
}'

Example responses

200 OK
{
  "token": "1|abcdEFGH1234567890tokenExample",
  "user_type": "user",
  "user": {
    "id": 12,
    "name": "Jane Doe",
    "email": "jane@acme.co.tz",
    "role": "admin",
    "tenant": {
      "id": 7,
      "name": "Acme Traders",
      "slug": "acme-traders"
    }
  },
  "permissions": [
    "dashboard",
    "messages",
    "messages.send",
    "bulk_sms.send",
    "group_sms.send",
    "contacts",
    "groups",
    "templates"
  ]
}
401 Invalid credentials
{
  "message": "Invalid credentials."
}
403 Organization inactive
{
  "message": "Your organization is inactive. Contact support."
}
POST /forgot-password No auth required

Forgot Password

Requests a password-reset email. Always returns 200 regardless of whether the email exists, to avoid leaking which emails are registered.

Headers

Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "email": "jane@acme.co.tz"
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/forgot-password' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "email": "jane@acme.co.tz"
}'

Example responses

200 OK
{
  "message": "If that email exists, a reset link has been sent."
}
POST /reset-password No auth required

Reset Password

Completes a password reset using the token emailed by Forgot Password. Tokens expire after 60 minutes.

Headers

Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "token": "the-token-from-the-email",
  "email": "jane@acme.co.tz",
  "password": "NewSecurePass456",
  "password_confirmation": "NewSecurePass456"
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/reset-password' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "token": "the-token-from-the-email",
  "email": "jane@acme.co.tz",
  "password": "NewSecurePass456",
  "password_confirmation": "NewSecurePass456"
}'

Example responses

200 OK
{
  "message": "Your password has been reset. You can now log in."
}
422 Invalid/expired token
{
  "message": "This reset link is invalid or has already been used."
}
GET /me

Get Current User

Returns the authenticated user's profile, tenant, and permission list. Useful right after login to confirm the token works and to know what the caller is allowed to do.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/me' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'
GET /profile

Get Profile

Returns the authenticated user's profile (id, name, email, role, tenant).

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/profile' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'
PATCH /profile

Update Profile

Updates the caller's name/email, and optionally changes password (requires current_password).

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "name": "Jane Doe",
  "email": "jane@acme.co.tz",
  "current_password": "SecurePass123",
  "new_password": "EvenMoreSecure789",
  "new_password_confirmation": "EvenMoreSecure789"
}

Example request (curl)

curl --location --request PATCH 'https://mosms.co.tz/api/profile' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "name": "Jane Doe",
  "email": "jane@acme.co.tz",
  "current_password": "SecurePass123",
  "new_password": "EvenMoreSecure789",
  "new_password_confirmation": "EvenMoreSecure789"
}'
POST /logout

Logout

Revokes the Bearer token used on this request. Call this when a client (script, app) is done using its token.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/logout' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'

Dashboard & Balance

Account-level summary data and SMS credit balance.

GET /balance

Get SMS Balance

Returns the tenant's remaining SMS credit balance. Check this before sending to avoid a 422 insufficient-balance error — 1 credit = 1 SMS segment (see Send Single SMS for segment math).

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/balance' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'

Example responses

200 OK
{
  "sms_balance": 1520
}
GET /dashboard

Get Dashboard Summary

Aggregate counters for the tenant: users, contacts, groups, messages (total + today), plus upcoming holiday/birthday auto-wish previews.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/dashboard' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'

Example responses

200 OK
{
  "users_count": 3,
  "contacts_count": 240,
  "groups_count": 5,
  "messages_count": 18432,
  "messages_today": 56,
  "upcoming_holiday": null,
  "upcoming_birthdays": [
    {
      "id": 4,
      "name": "Amina Suleiman",
      "phone": "255712345678",
      "birth_date": "1990-07-20",
      "days_until": 3,
      "upcoming_age": 36
    }
  ],
  "birthday_wishes_enabled": true
}

Send SMS

The core sending endpoints — single message, bulk batch, or a saved group.

POST /sms/send

Send Single SMS

Sends one SMS immediately (or schedules it for later with scheduled_at). Requires the messages.send permission.

Body parameters

FieldTypeRequiredNotes
tostringyesAny reasonable format (0712345678, +255712345678, 255712345678) — normalized server-side.
textstringrequired unless template_id givenMax 4000 chars.
contact_idintegernoLinks the message to an existing contact.
template_idintegernoRenders a saved template ({name}, {phone}, {email} placeholders) instead of text.
scheduled_atdatetimenoISO date-time in the future — queues the send instead of sending now.

Credit cost: GSM-7 messages cost 1 credit per 160 characters; messages containing Unicode (emoji, non-Latin scripts) cost 1 credit per 70 characters. The gateway rejects the request with 422 if your balance can't cover the segment count.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "to": "0712345678",
  "text": "Hello from MoSMS!",
  "scheduled_at": null
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/sms/send' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "to": "0712345678",
  "text": "Hello from MoSMS!",
  "scheduled_at": null
}'

Example responses

201 Created — sent
{
  "data": {
    "id": 1042,
    "to_number": "255712345678",
    "from_sender": "MOSMS",
    "body": "Hello from MoSMS!",
    "status": "PENDING_ACCEPTED",
    "delivery_status": null,
    "delivered_at": null,
    "delivery_error": null,
    "gateway_message_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "gateway_response": {
      "messageId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
    },
    "sent_at": "2026-07-17T10:00:00.000000Z",
    "scheduled_at": null,
    "contact": null,
    "group": null,
    "user": null,
    "created_at": "2026-07-17T10:00:00.000000Z"
  }
}
422 Insufficient balance
{
  "message": "Insufficient SMS balance. Message requires 2 credit(s) but you have 0 remaining.",
  "balance": 0,
  "required": 2
}
POST /sms/send-bulk

Send Bulk SMS

Queues up to any number of distinct messages for background delivery (does not send synchronously — you'll get a 202 immediately, then track each message via List Messages). Requires bulk_sms.send.

Body parameters

FieldTypeRequiredNotes
messagesarrayyesAt least 1 item: { "to": "...", "text": "...", "contact_id": null }.
messages[].tostringyesRecipient number.
messages[].textstringrequired unless template_id givenPer-message text (max 4000 chars).
template_idintegernoApplies one template to every message in the array, with per-contact placeholder rendering.
scheduled_atdatetimenoDelays the whole batch.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "messages": [
    {
      "to": "0712345678",
      "text": "Hi Juma, your order has shipped!"
    },
    {
      "to": "0765432109",
      "text": "Hi Amina, your order has shipped!"
    }
  ]
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/sms/send-bulk' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "messages": [
    {
      "to": "0712345678",
      "text": "Hi Juma, your order has shipped!"
    },
    {
      "to": "0765432109",
      "text": "Hi Amina, your order has shipped!"
    }
  ]
}'

Example responses

202 Accepted
{
  "message": "Bulk SMS queued for delivery.",
  "count": 2
}
422 Insufficient balance
{
  "message": "Insufficient SMS balance. Messages require 2 credit(s) but you have 0 remaining.",
  "balance": 0,
  "required": 2
}
POST /sms/send-group/:group_id

Send SMS to Group

Sends (queues) the same message to every contact in a saved Group. Requires group_sms.send.

Path parameter: group_id — the group's numeric ID (see Groups folder).

Body: same text / template_id / scheduled_at fields as Send Single SMS. Returns 422 if the group has no contacts or the balance can't cover contacts_count × segments_per_message.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "text": "Reminder: our office is closed tomorrow for the public holiday.",
  "template_id": null,
  "scheduled_at": null
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/sms/send-group/:group_id' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "text": "Reminder: our office is closed tomorrow for the public holiday.",
  "template_id": null,
  "scheduled_at": null
}'

Example responses

202 Accepted
{
  "message": "Group SMS to 'VIP Customers' queued for delivery.",
  "contacts": 84
}
422 Empty group
{
  "message": "Group has no contacts."
}

Messages

Message history, delivery-status polling, cancellation, and deletion.

GET /messages

List Messages

Paginated history of every message sent (or queued/scheduled) by the tenant, newest first.

Query parameters (all optional): status (e.g. PENDING_ACCEPTED, DELIVERED, scheduled, cancelled), search (matches to_number or body), date_from, date_to (both YYYY-MM-DD, filtered on sent_at), page.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Query parameters

statusFilter by delivery/send status
searchSearch phone number or message body
date_fromYYYY-MM-DD
date_toYYYY-MM-DD
pagePagination page number

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/messages' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'

Example responses

200 OK
{
  "data": [
    {
      "id": 1042,
      "to_number": "255712345678",
      "from_sender": "MOSMS",
      "body": "Hello from MoSMS!",
      "status": "DELIVERED",
      "delivery_status": "DELIVERED_TO_HANDSET",
      "delivered_at": "2026-07-17T10:00:05.000000Z",
      "delivery_error": null,
      "gateway_message_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "sent_at": "2026-07-17T10:00:00.000000Z",
      "scheduled_at": null,
      "created_at": "2026-07-17T10:00:00.000000Z"
    }
  ],
  "links": {
    "first": "https://mosms.co.tz/api/messages?page=1",
    "last": "https://mosms.co.tz/api/messages?page=12",
    "prev": null,
    "next": "https://mosms.co.tz/api/messages?page=2"
  },
  "meta": {
    "current_page": 1,
    "last_page": 12,
    "per_page": 20,
    "total": 231
  }
}
GET /messages/:message_id

Get Message

Fetches a single message by ID, with its linked contact/group/user (when present).

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/messages/:message_id' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'
POST /messages/:message_id/check-status

Check Delivery Status

Actively polls the upstream SMS gateway for the latest delivery report on this message and updates status / delivery_status / delivered_at / delivery_error accordingly, then returns the refreshed message. Requires messages.check_status.

Errors: 422 if the message has no gateway_message_id (e.g. it's still scheduled), 404 if the gateway has no report yet, 429 if the gateway rate-limited us, 502 if the gateway is unreachable.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/messages/:message_id/check-status' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'

Example responses

200 OK
{
  "data": {
    "id": 1042,
    "to_number": "255712345678",
    "status": "DELIVERED",
    "delivery_status": "DELIVERED_TO_HANDSET",
    "delivered_at": "2026-07-17T10:00:05.000000Z",
    "delivery_error": null
  }
}
422 No gateway ID
{
  "message": "This message has no gateway ID \u2014 status cannot be checked."
}
POST /messages/:message_id/cancel

Cancel Scheduled Message

Cancels a message that hasn't been sent yet (status === 'scheduled'). Requires messages.cancel_scheduled. Returns 422 if the message already sent.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/messages/:message_id/cancel' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'

Example responses

422 Not scheduled
{
  "message": "Only scheduled messages can be cancelled."
}
DELETE /messages/bulk

Bulk Delete Messages

Permanently deletes multiple message records by ID. Requires messages.delete.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "ids": [
    1042,
    1043,
    1044
  ]
}

Example request (curl)

curl --location --request DELETE 'https://mosms.co.tz/api/messages/bulk' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "ids": [
    1042,
    1043,
    1044
  ]
}'

Example responses

200 OK
{
  "message": "Messages deleted.",
  "count": 3
}

Delivery Reports & Logs

Raw records proxied from the upstream SMS gateway, for reconciliation.

GET /delivery-reports

List Delivery Reports

Proxies straight through to the upstream carrier's native delivery-report endpoint and returns its raw JSON — useful for reconciling against gateway-side records directly. Requires delivery_reports.

Query: messageId (optional, filters to one message), limit (default 25).

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Query parameters

messageIdFilter to a single gateway message ID
limitMax results to return

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/delivery-reports' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'

Example responses

502 Gateway unreachable
{
  "message": "Unable to reach the SMS gateway. Please try again later."
}
GET /sms-logs

List SMS Logs

Tenant-scoped raw send logs proxied from the upstream gateway (distinct from List Messages, which is MoSMS's own local record). Requires sms_logs.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/sms-logs' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'

Contacts

Manage the tenant's contact book, including CSV/vCard bulk import.

GET /contacts

List Contacts

Paginated contact list. Query: search (matches name/phone), page.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Query parameters

searchSearch by name or phone
page

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/contacts' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'

Example responses

200 OK
{
  "data": [
    {
      "id": 501,
      "name": "Juma Hassan",
      "phone": "255712345678",
      "email": null,
      "birth_date": null,
      "groups": [],
      "created_at": "2026-01-10T08:00:00.000000Z",
      "updated_at": "2026-01-10T08:00:00.000000Z"
    }
  ],
  "links": {},
  "meta": {
    "current_page": 1,
    "last_page": 3,
    "per_page": 20,
    "total": 58
  }
}
POST /contacts

Create Contact

name and phone are required; phone must be unique per tenant (normalized before the uniqueness check). birth_date (if set) powers the birthday auto-SMS feature.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "name": "Juma Hassan",
  "phone": "0712345678",
  "email": "juma@example.com",
  "birth_date": "1988-03-14"
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/contacts' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "name": "Juma Hassan",
  "phone": "0712345678",
  "email": "juma@example.com",
  "birth_date": "1988-03-14"
}'

Example responses

201 Created
{
  "data": {
    "id": 501,
    "name": "Juma Hassan",
    "phone": "255712345678",
    "email": "juma@example.com",
    "birth_date": "1988-03-14",
    "groups": []
  }
}
GET /contacts/:contact_id

Get Contact

Fetch one contact with its groups loaded.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/contacts/:contact_id' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'
PUT /contacts/:contact_id

Update Contact

All fields optional (sometimes validation) — send only what changes.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "name": "Juma Hassan Mnyonge",
  "phone": "0712345678",
  "email": "juma@example.com"
}

Example request (curl)

curl --location --request PUT 'https://mosms.co.tz/api/contacts/:contact_id' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "name": "Juma Hassan Mnyonge",
  "phone": "0712345678",
  "email": "juma@example.com"
}'
DELETE /contacts/:contact_id

Delete Contact

Permanently deletes one contact.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request DELETE 'https://mosms.co.tz/api/contacts/:contact_id' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'
DELETE /contacts/bulk

Bulk Delete Contacts

Deletes multiple contacts by ID.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "ids": [
    501,
    502,
    503
  ]
}

Example request (curl)

curl --location --request DELETE 'https://mosms.co.tz/api/contacts/bulk' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "ids": [
    501,
    502,
    503
  ]
}'

Example responses

200 OK
{
  "message": "Contacts deleted.",
  "count": 3
}
POST /contacts/import/csv

Import Contacts (CSV)

Multipart upload. CSV columns: name, phone, email, birth_date (header row required). Max 5MB.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Form data (multipart)

fileCSV file — columns: name, phone, email, birth_date

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/contacts/import/csv' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json' \
  --form 'file=@/path/to/your/file'
POST /contacts/import/vcf

Import Contacts (vCard)

Multipart upload of a .vcf vCard export (e.g. exported from a phone's contacts app). Max 5MB.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Form data (multipart)

filevCard (.vcf) file

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/contacts/import/vcf' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json' \
  --form 'file=@/path/to/your/file'

Groups

Organize contacts into groups for targeted group sends.

GET /groups

List Groups

Paginated list of contact groups, with contacts_count on each.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/groups' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'

Example responses

200 OK
{
  "data": [
    {
      "id": 9,
      "name": "VIP Customers",
      "description": "Top-tier clients",
      "contacts_count": 84,
      "created_at": "2026-02-01T00:00:00.000000Z",
      "updated_at": "2026-02-01T00:00:00.000000Z"
    }
  ]
}
POST /groups

Create Group

Creates an empty group — add contacts afterwards via Add Contacts to Group.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "name": "VIP Customers",
  "description": "Top-tier clients"
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/groups' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "name": "VIP Customers",
  "description": "Top-tier clients"
}'

Example responses

201 Created
{
  "data": {
    "id": 9,
    "name": "VIP Customers",
    "description": "Top-tier clients",
    "contacts_count": 0
  }
}
GET /groups/:group_id

Get Group

Fetch one group with its contacts_count.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/groups/:group_id' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'
PUT /groups/:group_id

Update Group

Rename a group or change its description.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "name": "VIP Customers (Tier 1)",
  "description": "Top-tier clients"
}

Example request (curl)

curl --location --request PUT 'https://mosms.co.tz/api/groups/:group_id' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "name": "VIP Customers (Tier 1)",
  "description": "Top-tier clients"
}'
DELETE /groups/:group_id

Delete Group

Deletes the group (contacts themselves are not deleted, only the grouping).

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request DELETE 'https://mosms.co.tz/api/groups/:group_id' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'
GET /groups/:group_id/contacts

List Group Contacts

Paginated list of contacts that belong to this group.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/groups/:group_id/contacts' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'
POST /groups/:group_id/contacts

Add Contacts to Group

Attaches existing contacts (by ID) to the group — safe to call repeatedly, duplicates are ignored.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "contact_ids": [
    501,
    502,
    503
  ]
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/groups/:group_id/contacts' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "contact_ids": [
    501,
    502,
    503
  ]
}'

Example responses

200 OK
{
  "message": "Contacts added to group."
}
DELETE /groups/:group_id/contacts

Remove Contacts from Group

Detaches contacts from the group without deleting the contacts themselves.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "contact_ids": [
    501
  ]
}

Example request (curl)

curl --location --request DELETE 'https://mosms.co.tz/api/groups/:group_id/contacts' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "contact_ids": [
    501
  ]
}'

Example responses

200 OK
{
  "message": "Contacts removed from group."
}

Templates

Reusable message bodies with {name}/{phone}/{email} placeholders.

GET /templates

List Templates

Paginated list of saved message templates.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/templates' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'
POST /templates

Create Template

body supports {name}, {phone}, and {email} placeholders, filled in per-contact when the template is used in a send.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "name": "Order Shipped",
  "body": "Hi {name}, your order has shipped! Track it at example.com/track."
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/templates' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "name": "Order Shipped",
  "body": "Hi {name}, your order has shipped! Track it at example.com/track."
}'

Example responses

201 Created
{
  "data": {
    "id": 3,
    "name": "Order Shipped",
    "body": "Hi {name}, your order has shipped! Track it at example.com/track.",
    "created_at": "2026-01-05T00:00:00.000000Z"
  }
}
GET /templates/:template_id

Get Template

Fetch one template.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/templates/:template_id' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'
PUT /templates/:template_id

Update Template

Updates a template's name and/or body.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "name": "Order Shipped",
  "body": "Hi {name}, your order #{phone} has shipped!"
}

Example request (curl)

curl --location --request PUT 'https://mosms.co.tz/api/templates/:template_id' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "name": "Order Shipped",
  "body": "Hi {name}, your order #{phone} has shipped!"
}'
DELETE /templates/:template_id

Delete Template

Deletes a template.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request DELETE 'https://mosms.co.tz/api/templates/:template_id' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'

SMS Packages & Purchases

Buying more SMS/WhatsApp credits — manual (admin-approved) or online via Pesapal.

GET /sms-packages

List SMS Packages

Public price tiers available for purchase (no purchase_sms permission required to view).

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/sms-packages' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'

Example responses

200 OK
{
  "data": [
    {
      "id": 1,
      "name": "Starter",
      "min_quantity": 100,
      "max_quantity": 999,
      "price_per_sms": 20
    },
    {
      "id": 2,
      "name": "Growth",
      "min_quantity": 1000,
      "max_quantity": 9999,
      "price_per_sms": 18
    }
  ]
}
POST /sms-purchases

Purchase SMS Credits (Manual)

Submits a manual top-up request (e.g. after a bank/mobile-money transfer) for admin approval — credits are not added instantly. sms_quantity must be ≥100 for SMS or ≥10 for channel: whatsapp. Only one pending manual request per channel is allowed at a time.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "sms_quantity": 1000,
  "transaction_reference": "MPESA-XJ29KD8",
  "payer_phone": "0712345678"
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/sms-purchases' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "sms_quantity": 1000,
  "transaction_reference": "MPESA-XJ29KD8",
  "payer_phone": "0712345678"
}'

Example responses

201 Created
{
  "message": "Purchase request submitted successfully.",
  "data": {
    "id": 88,
    "sms_quantity": 1000,
    "total_amount": 20000,
    "status": "pending"
  }
}
422 Pending request exists
{
  "message": "You already have a pending SMS purchase of 500 credits (Ref: MPESA-AB12CD) awaiting approval. Please wait for it to be approved before creating a new one.",
  "pending_id": 87
}
POST /sms-purchases/pesapal

Purchase via Pesapal

Initiates an online card/mobile-money checkout through Pesapal. Returns a redirect_url — send the user there to complete payment, then poll Check Pesapal Purchase Status.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "sms_quantity": 1000
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/sms-purchases/pesapal' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "sms_quantity": 1000
}'

Example responses

201 Created
{
  "message": "Pesapal checkout initiated.",
  "data": {
    "payment_id": 89,
    "redirect_url": "https://pay.pesapal.com/...",
    "order_tracking_id": "..."
  }
}
GET /sms-purchases/:purchase_id/status

Check Pesapal Purchase Status

Polls Pesapal for the latest status of a pending online payment and updates the local record.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/sms-purchases/:purchase_id/status' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'

Example responses

200 OK
{
  "data": {
    "id": 89,
    "status": "approved",
    "payment_status_description": "COMPLETED",
    "confirmation_code": "ABC123",
    "payment_method": "pesapal",
    "sms_quantity": 1000,
    "total_amount": 18000
  }
}
GET /sms-purchases

List Purchase History

Paginated history of the tenant's SMS/WhatsApp credit purchases.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/sms-purchases' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'
GET /sms-purchases/:purchase_id/receipt

Download Purchase Receipt

Streams a PDF receipt for an approved purchase. Only available once status === 'approved'.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/sms-purchases/:purchase_id/receipt' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'

Events

Weddings, harambees, and fundraisers — invitees, pledge/payment tracking, and multi-stage SMS/WhatsApp campaigns (invitations, reminders, thank-yous).

GET /events

List Events

Paginated list of events (weddings, harambees/fundraisers, etc.) with computed SMS-cost stats. Requires events. Query: search (matches name), status (draft/active/completed/cancelled), page.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Query parameters

searchSearch by event name
statusdraft|active|completed|cancelled
page

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/events' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'

Example responses

200 OK
{
  "data": [
    {
      "id": 12,
      "name": "Amina & Juma's Wedding",
      "type": "wedding",
      "description": null,
      "event_date": "2026-09-12",
      "venue": "Kigamboni Beach Hall",
      "target_amount": 5000000,
      "status": "active",
      "invitees_count": 84,
      "payments_count": 12,
      "sms_stages_count": 3,
      "total_pledged": 3200000,
      "total_collected": 1450000,
      "total_sms": 252,
      "sent_sms": 84,
      "sms_cost": 5040,
      "created_at": "2026-06-01T09:00:00.000000Z"
    }
  ]
}
POST /events

Create Event

Body parameters

FieldTypeRequiredNotes
namestringyes
typestringyeswedding, harambee, fundraiser, or other.
event_datedateyes
descriptionstringnoMax 2000 chars.
venuestringno
target_amountnumbernoFundraising target, if any.
statusstringnodraft, active, completed, or cancelled (default draft).
public_messagestringnoShown on the public RSVP/pledge page.
bank_name / bank_account_number / bank_account_namestringnoFor displaying payment instructions publicly.
pledge_deadlinedateno

Requires events.add.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "name": "Amina & Juma's Wedding",
  "type": "wedding",
  "event_date": "2026-09-12",
  "venue": "Kigamboni Beach Hall",
  "target_amount": 5000000
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/events' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "name": "Amina & Juma'\''s Wedding",
  "type": "wedding",
  "event_date": "2026-09-12",
  "venue": "Kigamboni Beach Hall",
  "target_amount": 5000000
}'

Example responses

201 Created
{
  "data": {
    "id": 12,
    "name": "Amina & Juma's Wedding",
    "type": "wedding",
    "event_date": "2026-09-12",
    "status": "draft",
    "invitees_count": 0
  }
}
GET /events/:event_id

Get Event

Fetch one event, including total_pledged/total_collected totals. Requires events.view.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/events/:event_id' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'
PUT /events/:event_id

Update Event

Same fields as Create Event, all optional (sometimes validation). Requires events.edit.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "status": "active"
}

Example request (curl)

curl --location --request PUT 'https://mosms.co.tz/api/events/:event_id' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "status": "active"
}'
DELETE /events/:event_id

Delete Event

Requires events.delete. This also removes the event's invitees, payments, and SMS stages.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request DELETE 'https://mosms.co.tz/api/events/:event_id' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'
GET /events/:event_id/invitees

List Event Invitees

Paginated. Query: search (matches name/phone), page. Requires events.view.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Query parameters

searchSearch by name or phone
page

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/events/:event_id/invitees' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'
POST /events/:event_id/invitees

Add Invitee

name and phone required; pledge_amount and contact_id (link to an existing Contact) optional. Requires events.manage_invitees.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "name": "Fatuma Rajabu",
  "phone": "0712345678",
  "pledge_amount": 50000
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/events/:event_id/invitees' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "name": "Fatuma Rajabu",
  "phone": "0712345678",
  "pledge_amount": 50000
}'

Example responses

201 Created
{
  "data": {
    "id": 501,
    "event_id": 12,
    "name": "Fatuma Rajabu",
    "phone": "255712345678",
    "pledge_amount": 50000,
    "paid_amount": 0,
    "status": "pending"
  }
}
POST /events/:event_id/invitees/import

Import Invitees (from Contacts/Group)

Bulk-adds invitees from existing Contacts and/or a Group — pass contact_ids (array) and/or group_id (at least one required). Duplicates (already-invited contacts) are skipped. Requires events.manage_invitees.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "group_id": 9,
  "contact_ids": [
    501,
    502
  ]
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/events/:event_id/invitees/import' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "group_id": 9,
  "contact_ids": [
    501,
    502
  ]
}'

Example responses

200 OK
{
  "message": "42 invitees imported.",
  "imported": 42
}
POST /events/:event_id/invitees/import/file

Import Invitees (CSV/vCard file)

Multipart upload — CSV (with name/phone/email columns) or .vcf vCard export. Max 5MB. Requires events.manage_invitees.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Form data (multipart)

fileCSV or .vcf file

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/events/:event_id/invitees/import/file' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json' \
  --form 'file=@/path/to/your/file'

Example responses

200 OK
{
  "message": "38 invitees imported, 4 skipped.",
  "imported": 38,
  "skipped": 4
}
PUT /events/:event_id/invitees/:invitee_id

Update Invitee

All fields optional. status is one of pending, invited, confirmed, declined. Requires events.manage_invitees.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "status": "confirmed",
  "pledge_amount": 60000
}

Example request (curl)

curl --location --request PUT 'https://mosms.co.tz/api/events/:event_id/invitees/:invitee_id' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "status": "confirmed",
  "pledge_amount": 60000
}'
DELETE /events/:event_id/invitees/:invitee_id

Remove Invitee

Requires events.manage_invitees.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request DELETE 'https://mosms.co.tz/api/events/:event_id/invitees/:invitee_id' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'
GET /events/:event_id/payments

List Event Payments

Paginated, newest first, with invitee + recorder loaded. Requires events.view.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/events/:event_id/payments' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'
POST /events/:event_id/payments

Record a Payment

Records a pledge payment against an invitee. If the event has auto_payment_sms enabled, a thank-you SMS is sent automatically. Requires events.manage_payments.

Body parameters

FieldTypeRequiredNotes
event_invitee_idintegeryesMust belong to this event.
amountnumberyes> 0.
payment_datedateyes
payment_methodstringnocash, mobile_money, bank, or other.
referencestringnoe.g. mobile money transaction ID.
notesstringno

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "event_invitee_id": 501,
  "amount": 50000,
  "payment_method": "mobile_money",
  "reference": "MPESA-XJ29KD8",
  "payment_date": "2026-07-18"
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/events/:event_id/payments' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "event_invitee_id": 501,
  "amount": 50000,
  "payment_method": "mobile_money",
  "reference": "MPESA-XJ29KD8",
  "payment_date": "2026-07-18"
}'

Example responses

201 Created
{
  "data": {
    "id": 88,
    "event_id": 12,
    "event_invitee_id": 501,
    "amount": 50000,
    "payment_method": "mobile_money",
    "reference": "MPESA-XJ29KD8",
    "payment_date": "2026-07-18"
  }
}
PUT /events/:event_id/payments/:payment_id

Update Payment

All fields optional. Requires events.manage_payments.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "amount": 55000
}

Example request (curl)

curl --location --request PUT 'https://mosms.co.tz/api/events/:event_id/payments/:payment_id' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "amount": 55000
}'
DELETE /events/:event_id/payments/:payment_id

Delete Payment

Requires events.manage_payments.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request DELETE 'https://mosms.co.tz/api/events/:event_id/payments/:payment_id' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'
GET /events/:event_id/sms-stages

List SMS Stages

Each stage includes computed stage_total_sms/stage_remaining/stage_cost based on the current invitee count. Requires events.view.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/events/:event_id/sms-stages' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'
POST /events/:event_id/sms-stages

Create SMS Stage

Defines a message (SMS or WhatsApp) to send to all invitees, once or on a recurring schedule. SMS and WhatsApp are always separate stages — there's no combined "both" channel; create one stage per channel if you want to reach invitees on both. Requires events.send_sms.

Body parameters

FieldTypeRequiredNotes
stage_typestringyesinvitation, reminder, pledge_reminder, non_pledge_reminder, thank_you, rsvp, or custom.
namestringyesInternal label.
bodystringyesMax 4000 chars. Supports placeholders like {invitee_name}, {event_name}, {event_date}, {venue}, {pledge_amount}, {event_link}.
channelstringnosms (default) or whatsapp.
whatsapp_template_idintegerrequired if channel=whatsappMust be an approved WhatsApp template.
send_atdatetimenoFirst/only send time.
frequencystringnoonce, daily, weekly, or monthly.
end_atdatenoRequired alongside a recurring frequency; must be ≥ send_at.
statusstringnodraft or scheduled.

Creating a stage does not send it — call Send SMS Stage (or let it fire on send_at).

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "stage_type": "invitation",
  "name": "Save the Date",
  "channel": "sms",
  "body": "You're invited to {invitee_name}'s wedding on {event_date} at {venue}!",
  "send_at": "2026-08-01T09:00:00",
  "frequency": "once"
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/events/:event_id/sms-stages' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "stage_type": "invitation",
  "name": "Save the Date",
  "channel": "sms",
  "body": "You'\''re invited to {invitee_name}'\''s wedding on {event_date} at {venue}!",
  "send_at": "2026-08-01T09:00:00",
  "frequency": "once"
}'

Example responses

201 Created
{
  "data": {
    "id": 7,
    "event_id": 12,
    "stage_type": "invitation",
    "name": "Save the Date",
    "status": "draft",
    "sent_count": 0
  }
}
PUT /events/:event_id/sms-stages/:stage_id

Update SMS Stage

Same fields as Create SMS Stage. status may additionally be set to cancelled. Requires events.send_sms.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "status": "scheduled"
}

Example request (curl)

curl --location --request PUT 'https://mosms.co.tz/api/events/:event_id/sms-stages/:stage_id' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "status": "scheduled"
}'
DELETE /events/:event_id/sms-stages/:stage_id

Delete SMS Stage

Requires events.send_sms.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request DELETE 'https://mosms.co.tz/api/events/:event_id/sms-stages/:stage_id' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'
POST /events/:event_id/sms-stages/:stage_id/send

Send SMS Stage

Queues the stage's message to every eligible invitee now. WhatsApp stages are metered against the tenant's WhatsApp wallet (not the SMS balance) and billed per conversation. SMS stages are checked against the SMS credit balance the same way as Send SMS to Group. Requires events.send_sms.

Headers

AuthorizationBearer YOUR_TOKEN
Acceptapplication/json

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/events/:event_id/sms-stages/:stage_id/send' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Accept: application/json'

Example responses

200 OK — queued
{
  "message": "Sending 84 message(s). You'll be notified when complete."
}
422 Already sent
{
  "message": "This stage has already been sent."
}
422 No eligible invitees
{
  "message": "No invitees are eligible for this stage."
}

Webhooks (reference only)

Inbound callbacks that MoSMS itself receives from the SMS carrier and Meta. Not callable by API clients — included so the origin of delivery-status updates is clear.

POST /webhooks/delivery No auth required

Delivery Callback (carrier → MoSMS)

Not called by developers. This is the endpoint the upstream SMS carrier calls to push delivery-status updates for messages MoSMS sent on your behalf. Documented here only so you understand where delivery_status on a message comes from — you don't need to (and can't meaningfully) call this yourself.

There is currently no outbound webhook that notifies *your* systems when a message's status changes — poll Check Delivery Status or List Messages instead.

Headers

Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "messageId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "DELIVERED_TO_HANDSET",
  "error": null
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/webhooks/delivery' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "messageId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "DELIVERED_TO_HANDSET",
  "error": null
}'

Example responses

200 OK
{
  "message": "OK"
}
GET /webhooks/whatsapp?hub.mode=subscribe&hub.verify_token=...&hub.challenge=... No auth required

WhatsApp Webhook — Verify (Meta → MoSMS)

Not called by developers. Meta's webhook-verification handshake for the tenant's WhatsApp Cloud API integration.

Headers

Acceptapplication/json

Query parameters

hub.mode
hub.verify_token
hub.challenge

Example request (curl)

curl --location --request GET 'https://mosms.co.tz/api/webhooks/whatsapp?hub.mode=subscribe&hub.verify_token=...&hub.challenge=...' \
  --header 'Accept: application/json'
POST /webhooks/whatsapp No auth required

WhatsApp Webhook — Receive (Meta → MoSMS)

Not called by developers. Meta pushes WhatsApp delivery statuses (sent/delivered/read/failed) and inbound message replies here. Verified via X-Hub-Signature-256 HMAC when WHATSAPP_APP_SECRET is configured.

Headers

Acceptapplication/json

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/webhooks/whatsapp' \
  --header 'Accept: application/json'

Coming Soon — WhatsApp Send API

WhatsApp sending currently only exists in MoSMS's internal dashboard (session auth), not this token-based developer API. These items sketch the likely shape of a future public endpoint for planning purposes — not live, subject to change.

POST /whatsapp/send

[Planned] Send Single WhatsApp Message

⚠️ Not yet available on the public API. WhatsApp sending today only exists in the internal MoSMS dashboard (session-authenticated web routes), metered against the tenant's whatsapp_balance wallet — mirroring Send Single SMS, but via App\Services\WhatsApp\WhatsAppCloudService instead of the SMS gateway.

This item documents the shape a future token-authenticated /api/whatsapp/send endpoint would likely take, based on the existing dashboard implementation, so client integrations can be planned ahead of time. Confirm with the MoSMS team before building against it.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "to": "0712345678",
  "whatsapp_template_id": 4,
  "text": null
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/whatsapp/send' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "to": "0712345678",
  "whatsapp_template_id": 4,
  "text": null
}'
POST /whatsapp/send-bulk

[Planned] Send Bulk WhatsApp Broadcast

⚠️ Not yet available on the public API. Modeled on the internal bulk/group broadcast flow (channel: whatsapp), which requires an approved, no-variable WhatsApp template and is metered 1 WhatsApp credit per recipient. Confirm with the MoSMS team before building against it.

Headers

AuthorizationBearer YOUR_TOKEN
Content-Typeapplication/json
Acceptapplication/json

Request body

{
  "numbers": [
    "0712345678",
    "0765432109"
  ],
  "whatsapp_template_id": 4
}

Example request (curl)

curl --location --request POST 'https://mosms.co.tz/api/whatsapp/send-bulk' \
  --header 'Authorization: Bearer YOUR_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{
  "numbers": [
    "0712345678",
    "0765432109"
  ],
  "whatsapp_template_id": 4
}'