Before you start
Submit a message template in Dashboard → Templates first. Include every language you will send and use {{placeholder}} names for runtime values. An administrator must accept the complete multilingual submission before either the test or production Messages API can use it.
Then start in the dashboard's Test environment and create a test API key with the sms:send and sms:read scopes. Test keys work only with /v1/test/* endpoints and never send or charge.
Keep the key in a server-side secret store. Never embed it in browser code, a mobile app, a public repository, or a client-visible environment variable.
Choose API-key scopes
Bar9 accepts only the scopes below when a key is created. Any other value is rejected with 400 validation_error and no key is saved.
| Scope | Allows |
|---|---|
sms:send |
Send one SMS. |
sms:send:bulk |
Send SMS batches. |
sms:read |
List and retrieve messages. |
otp:create |
Create OTP sessions. |
otp:read |
Retrieve OTP session state. |
stats:read |
Read account statistics. |
marketing:audiences:read |
List audiences and members. |
marketing:audiences:write |
Manage audiences and members. |
marketing:campaigns:read |
List campaigns and delivery results. |
marketing:campaigns:write |
Submit campaigns for review. |
templates:read |
List template submissions and decisions. |
templates:write |
Submit and update templates for review. |
sender-ids:read |
List SenderID applications and decisions. |
sender-ids:write |
Submit SenderID applications for review. |
If scopes are omitted, a key receives sms:send, sms:read, otp:create, and otp:read. Management permissions are never granted by default; select them explicitly using least privilege. Campaign and SenderID permissions require a production key, while audience and template permissions work with either key environment. Code verification remains a public end-user operation and has no API-key scope.
Send your first test SMS
Use the test key for an executable first request. Sandbox messages apply production validation and require an accepted template, but they never contact a provider or debit credit.
export BAR9_API_KEY="bar9_your_test_key"
curl https://api.bar9.dev/v1/test/messages \
-H "Authorization: Bearer $BAR9_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+213555000000",
"template_id": "tpl_your_accepted_template",
"language": "fr",
"variables": { "name": "Amine" }
}'
The API returns 202 Accepted with an ID beginning with test_msg_, status: "delivered", and cost_credits: 0. Retrieve that exact record to verify your read permission and response handling:
curl https://api.bar9.dev/v1/test/messages/test_msg_from_the_response \
-H "Authorization: Bearer $BAR9_API_KEY"
Test and production histories are isolated. Continue with the Test API Sandbox reference for bulk sends and test OTP sessions.
Promote the request to production
After production verification is approved, create a production key with sms:send and sms:read, replace the test key, and use the live path shown below.
Bar9 accepts Algerian mobile numbers in local or E.164 form. E.164 is recommended at integration boundaries.
export BAR9_API_KEY="bar9_your_key"
curl https://api.bar9.dev/v1/messages \
-H "Authorization: Bearer $BAR9_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+213555000000",
"template_id": "tpl_your_accepted_template",
"language": "fr",
"variables": { "name": "Amine" }
}'
The API returns 202 Accepted after the message is validated, charged, and queued. Accepted does not mean delivered; retain the message id and follow its status.
{
"ok": true,
"data": {
"id": "msg_...",
"to": "+213555000000",
"status": "queued",
"segments": 1,
"cost_credits": 5
}
}
Read the latest delivery state with the same production key:
curl https://api.bar9.dev/v1/messages/msg_from_the_response \
-H "Authorization: Bearer $BAR9_API_KEY"
The status progresses from queued to sent, then reaches delivered or failed. For production systems, configure a DLR webhook instead of frequent polling.
Use Bar9 from a server
const response = await fetch('https://api.bar9.dev/v1/messages', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.BAR9_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
to: '+213555000000',
template_id: 'tpl_your_accepted_template',
language: 'fr',
variables: { name: 'Amine' }
})
});
const result = await response.json();
if (!response.ok) {
throw new Error(result.error?.message ?? 'Bar9 rejected the message');
}
console.log(result.data.id, result.data.status);
API errors use { "ok": false, "error": { "code": "...", "message": "..." } }. A request sent to an existing path with an unsupported HTTP method returns HTTP 405 with error.code: "method_not_allowed".
Next steps
- Read the SMS Messages reference for delivery states, filters, and bulk sends.
- Use DLR Webhooks instead of polling when delivery state matters.
- Use OTP Verification when Bar9 should create and verify the code.
- Follow an authentication tutorial when Firebase, Supabase, or Better Auth owns the user session.
Production checklist
- Request production verification in the dashboard and wait for approval before creating live credentials.
- Submit all message language variants and wait for template approval before integrating their IDs.
- Restrict each API key to the scopes its service needs and rotate exposed keys immediately.
- Validate and normalize phone numbers before submitting them.
- Add application-level rate limits and abuse controls before any endpoint that sends SMS.
- Treat
202 Acceptedas queued, not delivered, and handle terminaldeliveredandfailedstates. - Log Bar9 message or OTP session IDs, but do not log API keys or OTP codes.