Authentication
How authentication works (token-based)
GET/api/v3/_auth
Every v3 API request must include an `Authorization: Bearer <token>` header. Tokens are tenant-scoped — a token can only act on resources belonging to the tenant that created it. ## 1. Create a token Log in to your dashboard, open **Settings → API Keys**, click **Create token**, and copy the token value (shown only once). Tokens have an optional expiry; if omitted, they don't expire. ## 2. Make your first request Use the token in the `Authorization` header. Verify it works by hitting `GET /api/v3/me`: ```bash curl https://business.momo.tz/api/v3/me \ -H 'Authorization: Bearer YOUR_TOKEN' ``` ## 3. Token scopes All v3 tokens have full read/write access on the tenant's resources today. Per-endpoint scoping is on the roadmap; in the meantime, treat tokens as full credentials and rotate them if leaked. ## 4. Rate limiting - SMS / WhatsApp send: **60 req/min** per tenant - Read endpoints: **120 req/min** - Campaign creation: **10 req/min** Responses include `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` headers. A `429` means you've hit the cap — back off and retry after the reset. ## 5. Error handling All errors follow the envelope `{ "status": "error", "message": "...", "errors": { ... } }`. Validation errors come back as `422` with `errors` keyed by field name.
CURL
curl -X GET '/api/v3/_auth' \
-H 'Authorization: Bearer YOUR_API_TOKEN' \
-H 'Accept: application/json'NODE
const axios = require('axios')
const response = await axios({
method: 'get',
url: '/api/v3/_auth',
headers: {
Authorization: 'Bearer YOUR_API_TOKEN',
Accept: 'application/json'
}
})
console.log(response.data)PYTHON
import requests
response = requests.get(
"/api/v3/_auth",
headers={
"Authorization": "Bearer YOUR_API_TOKEN",
"Accept": "application/json"
}
)
print(response.json())PHP
$client = new \GuzzleHttp\Client();
$response = $client->request('GET', '/api/v3/_auth', [
'headers' => [
'Authorization' => 'Bearer YOUR_API_TOKEN',
'Accept' => 'application/json',
],
]);
$data = json_decode($response->getBody(), true);RUBY
require 'net/http'
require 'json'
require 'uri'
uri = URI('/api/v3/_auth')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Accept'] = 'application/json'
response = http.request(request)
puts JSON.parse(response.body)GO
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "/api/v3/_auth", nil)
req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")
req.Header.Set("Accept", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}Majibu
200 Probe endpoint — responds with the calling tenant's details when the token is valid.
{
"status": "success",
"data": {
"id": 12,
"uid": "tnt_01JXYZTENANT",
"name": "Acme Communications",
"email": "ops@acme.example.com",
"default_currency": "TZS"
}
} 401 Token missing, invalid, or expired.
{
"status": "error",
"message": "Invalid API token."
}