API key

SMS Messages

Send SMS, list message history, and inspect delivery state for a single message.

POST
/v1/message-templates
API key scope templates:write or user session - Submit a multilingual message template for review.
PUT
/v1/message-templates/:id
API key scope templates:write or user session - Edit and resubmit a multilingual message template.
GET
/v1/message-templates
API key scope templates:read or user session - List template submissions and review status.
POST
/v1/sender-ids
Production API key scope sender-ids:write or user session - Submit a SenderID application for review.
GET
/v1/sender-ids
Production API key scope sender-ids:read or user session - List SenderID applications and review status.
POST
/v1/messages
API key scope sms:send - Queue an SMS message and debit credits.
POST
/v1/messages/bulk
API key scope sms:send:bulk - Queue multiple SMS messages in a single API call.
GET
/v1/messages
API key scope sms:read - List messages with pagination and filters.
GET
/v1/messages/:message_id
API key scope sms:read - Fetch one message with body and delivery fields.
GET
/v1/messages/events
User session - List message lifecycle events for the dashboard.

Overview

The Messages API queues outbound SMS and returns 202 Accepted. Custom message bodies must come from a template accepted by Bar9. Submit a named template with all required language variants in Dashboard → Templates, then send its ID, language, and placeholder values. Callers cannot provide arbitrary SMS copy.

It requires a production API key from an account whose production verification has been approved. Test keys use the isolated /v1/test/messages API with the same accepted templates. Credits are debited when a live message is queued. If an automatic prohibited-content rule matches the rendered body, the message is created as pending_review without a debit or provider job; an administrator must approve it before Bar9 atomically charges and queues it. A rejection changes the message to failed and includes the moderation reason.

Timestamps are Unix seconds. Backend reporting windows use GMT+1; browser dashboards render timestamps in the user's local timezone.

POST /v1/message-templates

Submit a template with an authenticated dashboard session or an API key carrying templates:write. A name is required and may contain at most 120 characters. Supply 1 to 10 variants; every language must be unique, every body must contain at most 2,000 characters, and all variants must use the same placeholder names.

{
	"name": "Shipping update",
	"variants": [
		{ "language": "fr", "body": "Bonjour {{name}}, commande {{order_id}} expédiée." },
		{ "language": "ar", "body": "مرحبًا {{name}}، تم شحن الطلب {{order_id}}." }
	]
}

The response is 201 Created. A new submission has status pending until an administrator accepts or rejects the complete set.

{
	"ok": true,
	"data": {
		"id": "tpl_...",
		"name": "Shipping update",
		"status": "pending",
		"rejection_reason": null,
		"submitted_at": 1778407200,
		"reviewed_at": null,
		"variants": [
			{
				"language": "fr",
				"body": "Bonjour {{name}}, commande {{order_id}} expédiée.",
				"variables": ["name", "order_id"]
			}
		]
	}
}

PUT /v1/message-templates/:id

Edit the name and complete set of language variants with the same payload as creation. The template keeps its ID, but every edit clears the previous decision and returns it to pending; it cannot be used again until an administrator accepts the updated copy.

GET /v1/message-templates

List the current user's submissions with a dashboard session or templates:read API key:

curl "https://api.bar9.dev/v1/message-templates?page=1&per_page=25" \
  -H "Authorization: Bearer $BAR9_API_KEY"

Items include all variants and the current pending, accepted, or rejected status. Rejected submissions include rejection_reason. Template, campaign, and automatically intercepted-message decisions are handled from one moderation desk. Rejections carry a stable category (prohibited_content, misleading_content, missing_consent, policy_violation, or other) plus administrator detail. Bar9 emails the decision to the submitting account and records delivery success or failure without rolling back the moderation decision. The list uses the standard pagination object described below.

POST /v1/sender-ids

Submit a branded SenderID for review using a production API key with sender-ids:write, or a dashboard session. The multipart request requires the sender name, company, use case, and an INAPI certificate (PDF or supported image, at most 10 MB).

curl https://api.bar9.dev/v1/sender-ids \
  -H "Authorization: Bearer $BAR9_API_KEY" \
  -F "sender_id=BAR9" \
  -F "company=Bar9" \
  -F "use_case=Transactional notifications" \
  -F "[email protected]"

The response is 201 Created with a pending application. An administrator later accepts or rejects it; rejected applications include the review notes.

GET /v1/sender-ids

List the account's SenderID applications with a production API key carrying sender-ids:read:

curl "https://api.bar9.dev/v1/sender-ids?page=1&per_page=25" \
  -H "Authorization: Bearer $BAR9_API_KEY"

The certificate itself is never returned by this account endpoint.

POST /v1/messages

curl https://api.bar9.dev/v1/messages \
  -H "Authorization: Bearer $BAR9_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+213555000000",
    "template_id": "tpl_welcome",
    "language": "fr",
    "variables": { "name": "Amine" }
  }'

The response is 202 Accepted and contains the queued message shown in the response example below.

POST /v1/messages/bulk

curl https://api.bar9.dev/v1/messages/bulk \
  -H "Authorization: Bearer $BAR9_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      { "to": "+213555000000", "template_id": "tpl_campaign", "language": "fr", "variables": { "first_name": "Amine" } },
      { "to": "+213555000001", "template_id": "tpl_campaign", "language": "ar", "variables": { "first_name": "أمين" } }
    ]
  }'

Bulk requests accept from 1 to 100 message objects. The request is rejected if any item is invalid or the account cannot cover the messages queued immediately. Matched items remain uncharged in pending_review; each is charged separately if approved later. A successful 202 Accepted response contains count and messages:

{
	"ok": true,
	"data": {
		"count": 2,
		"messages": [{ "id": "msg_...", "status": "queued", "cost_credits": 5 }]
	}
}

Request fields

Field Required Notes
to Yes Algerian mobile number. E.164 (+213...) and local forms work.
template_id Yes An accepted template owned by the API-key account.
language Yes One of the accepted template's language tags.
variables Yes Object whose keys exactly match the variant's placeholders.
sender_id No Approved sender ID to use for this message.

Message response

{
	"ok": true,
	"data": {
		"id": "msg_...",
		"to": "+213555000000",
		"sender": "BAR9",
		"body": "Bonjour Amine",
		"type": "message",
		"status": "queued",
		"provider_message_id": null,
		"segments": 1,
		"cost_credits": 5,
		"created_at": 1778407200,
		"queued_at": 1778407200,
		"sent_at": null,
		"delivered_at": null,
		"failed_at": null,
		"failure_reason": null
	}
}

List responses omit body to keep history payloads smaller. Fetch an individual message to retrieve the body.

queued_at is null for pending_review messages. The cost_credits value is the amount that will be charged if an administrator approves the message; it is not a reservation, so approval can return an insufficient-credit error if the balance changes first.

GET /v1/messages

List production messages owned by the API-key account:

curl "https://api.bar9.dev/v1/messages?page=1&per_page=25&status=delivered" \
  -H "Authorization: Bearer $BAR9_API_KEY"

The endpoint supports these filters:

Query Notes
page Page number.
per_page Items per page.
status Filter by message status.
type Filter by message or otp.
to Filter by recipient number.

List responses use this envelope:

{
	"ok": true,
	"data": [{ "id": "msg_...", "status": "delivered" }],
	"pagination": {
		"limit": 25,
		"offset": 0,
		"total": 1,
		"has_more": false
	}
}

page defaults to 1, per_page defaults to 25, and per_page is capped at 100.

GET /v1/messages/:message_id

Fetch one production message owned by the API-key account:

curl https://api.bar9.dev/v1/messages/msg_123 \
  -H "Authorization: Bearer $BAR9_API_KEY"

The response uses the full message representation, including body. An unknown ID or a message owned by another account returns 404.

GET /v1/messages/events

This dashboard-session endpoint lists lifecycle events across the current user's messages:

curl "https://api.bar9.dev/v1/messages/events?page=1&per_page=25&event_type=delivered" \
  --cookie "$BAR9_SESSION_COOKIE"
Query Notes
page Page number; defaults to 1.
per_page Items per page; defaults to 25 and is capped at 100.
search Searches message ID, recipient, sender, and message content.
event_type Filters the lifecycle event type.
status Filters the associated message status.

Each event includes id, message_id, event_type, status, timestamps, recipient, sender, type, segment count, and response time when available. The response uses the standard list envelope.

Message status values

Status Meaning
pending_review An automatic rule matched; no credits or provider job exist yet.
queued The message was accepted and is waiting to send.
sent The provider accepted or submitted the SMS.
delivered The provider reported successful delivery.
failed Sending, delivery, or content moderation rejected the message.

Common errors

Status Typical cause
400 Invalid number, template, language, variables, filters, or bulk size.
401 Missing or invalid API key/session.
403 Wrong environment, missing scope, unapproved production access, or key.
404 Message or template does not exist for the authenticated account.
409 Template state conflicts with the requested operation.
402 Insufficient credits for the message or complete bulk request.