v3 Messaging API · v3.1.0

Nyaraka za API ya Mawasiliano

Unganisha SMS, WhatsApp, anwani na salio kwa kutumia API ya Momo Business yenye mifano kamili.

OpenAPI JSON
01Thibitisha
02Tuma ombi
03Fuatilia matokeo

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."
}

SMS

Send an SMS

POST/api/v3/sms/send

Queues one or many SMS messages using legacy-compatible fields.

Mwili wa ombi

SehemuAinaLazimaMaelezo
recipientstringNdiyoOne or more recipients, comma-separated (e.g. 255700111222 or 255700111222,255700111223).
recipientsarrayHapanaAlternative to recipient: array of phone numbers.
sender_idstringHapanaOptional approved sender ID, tenant-owned SMS-capable number, or active short code. Unknown or ambiguous identities are rejected.
typestringHapanaMessage type (e.g. plain).
messagestringNdiyoSMS body text.
tenant_channel_idintegerHapanaDeprecated compatibility input. Ignored; routing is resolved automatically from sender_id and account/system defaults.
schedule_timestringHapanaOptional ISO datetime for scheduled send.
CURL
curl -X POST '/api/v3/sms/send' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
    "recipient": "255700111222",
    "sender_id": "MyBrand",
    "message": "Hello from Momo Business \u2014 your verification code is 4821."
}'
NODE
const axios = require('axios')

const response = await axios({
    method: 'post',
    url: '/api/v3/sms/send',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json',
      'Content-Type': 'application/json'
    },
    data: {
    "recipient": "255700111222",
    "sender_id": "MyBrand",
    "message": "Hello from Momo Business \u2014 your verification code is 4821."
}
})

console.log(response.data)
PYTHON
import requests

response = requests.post(
    "/api/v3/sms/send",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json",
        "Content-Type": "application/json"
    },
    json={
    "recipient": "255700111222",
    "sender_id": "MyBrand",
    "message": "Hello from Momo Business \u2014 your verification code is 4821."
}
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('POST', '/api/v3/sms/send', [
    'headers' => [
        'Authorization' => 'Bearer YOUR_API_TOKEN',
        'Accept'        => 'application/json',
        'Content-Type'  => 'application/json',
    ],
    'json' => {
    "recipient": "255700111222",
    "sender_id": "MyBrand",
    "message": "Hello from Momo Business \u2014 your verification code is 4821."
},
]);

$data = json_decode($response->getBody(), true);
RUBY
require 'net/http'
require 'json'
require 'uri'

uri = URI('/api/v3/sms/send')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'

request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Accept'] = 'application/json'
request['Content-Type'] = 'application/json'
request.body = '{
    "recipient": "255700111222",
    "sender_id": "MyBrand",
    "message": "Hello from Momo Business \u2014 your verification code is 4821."
}'

response = http.request(request)
puts JSON.parse(response.body)
GO
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("POST", "/api/v3/sms/send", bytes.NewBuffer([]byte(`{
    "recipient": "255700111222",
    "sender_id": "MyBrand",
    "message": "Hello from Momo Business \u2014 your verification code is 4821."
}`)))
	req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, _ := client.Do(req)
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))
}

Majibu

201 Messages queued. Each recipient yields one Message row; check `data.messages[].status` for delivery progression.
{
    "status": "success",
    "data": {
        "messages": [
            {
                "id": 101,
                "uid": "msg_01JXYZSMS01",
                "direction": "outbound",
                "channel_type": "sms",
                "sender": "MyBrand",
                "recipient": "255700111222",
                "body": "Hello from API v3",
                "status": "queued"
            }
        ]
    }
}
422 Validation error.
{
    "status": "error",
    "message": "At least one recipient is required.",
    "errors": {
        "recipient": [
            "Provide recipient or recipients."
        ]
    }
}
401 Unauthorized.
{
    "status": "error",
    "message": "Invalid API token."
}

SMS

Create an SMS campaign

POST/api/v3/sms/campaign

Creates one campaign per resolved contact list identifier and queues campaign processing.

Mwili wa ombi

SehemuAinaLazimaMaelezo
contact_list_idstringNdiyo
messagestringNdiyo
sender_idstringHapanaOptional approved sender ID, tenant-owned SMS-capable number, or active short code.
tenant_channel_idintegerHapanaDeprecated compatibility input. Ignored; routing is resolved automatically from sender_id and account/system defaults.
schedule_timestringHapana
namestringHapana
CURL
curl -X POST '/api/v3/sms/campaign' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
    "contact_list_id": "grp_01JXYZABC",
    "message": "Campaign message",
    "sender_id": "Brand"
}'
NODE
const axios = require('axios')

const response = await axios({
    method: 'post',
    url: '/api/v3/sms/campaign',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json',
      'Content-Type': 'application/json'
    },
    data: {
    "contact_list_id": "grp_01JXYZABC",
    "message": "Campaign message",
    "sender_id": "Brand"
}
})

console.log(response.data)
PYTHON
import requests

response = requests.post(
    "/api/v3/sms/campaign",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json",
        "Content-Type": "application/json"
    },
    json={
    "contact_list_id": "grp_01JXYZABC",
    "message": "Campaign message",
    "sender_id": "Brand"
}
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('POST', '/api/v3/sms/campaign', [
    'headers' => [
        'Authorization' => 'Bearer YOUR_API_TOKEN',
        'Accept'        => 'application/json',
        'Content-Type'  => 'application/json',
    ],
    'json' => {
    "contact_list_id": "grp_01JXYZABC",
    "message": "Campaign message",
    "sender_id": "Brand"
},
]);

$data = json_decode($response->getBody(), true);
RUBY
require 'net/http'
require 'json'
require 'uri'

uri = URI('/api/v3/sms/campaign')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'

request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Accept'] = 'application/json'
request['Content-Type'] = 'application/json'
request.body = '{
    "contact_list_id": "grp_01JXYZABC",
    "message": "Campaign message",
    "sender_id": "Brand"
}'

response = http.request(request)
puts JSON.parse(response.body)
GO
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("POST", "/api/v3/sms/campaign", bytes.NewBuffer([]byte(`{
    "contact_list_id": "grp_01JXYZABC",
    "message": "Campaign message",
    "sender_id": "Brand"
}`)))
	req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, _ := client.Do(req)
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))
}

Majibu

201 Campaigns created.
{
    "status": "success",
    "data": {
        "campaigns": [
            {
                "id": 15,
                "uid": "cmp_01JXYZ001",
                "name": "API Campaign - VIP List",
                "status": "draft",
                "channel_type": "sms",
                "message": "Campaign message"
            }
        ]
    }
}
422 Validation error.
{
    "status": "error",
    "message": "contact_list_id must contain at least one group id."
}
401 Unauthorized.
{
    "status": "error",
    "message": "Invalid API token."
}

SMS

List SMS messages

GET/api/v3/sms

Returns tenant-scoped SMS message logs with pagination.

Vigezo

JinaMahaliAinaLazimaMaelezo
statusquerystringHapana
directionquerystringHapana
limitqueryintegerHapana
per_pagequeryintegerHapana
CURL
curl -X GET '/api/v3/sms' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'get',
    url: '/api/v3/sms',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.get(
    "/api/v3/sms",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('GET', '/api/v3/sms', [
    '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/sms')
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/sms", 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 SMS collection.
{
    "status": "success",
    "data": {
        "items": [
            {
                "id": 101,
                "uid": "msg_01JXYZSMS01",
                "direction": "outbound",
                "channel_type": "sms",
                "recipient": "255700111222",
                "body": "Hello from API v3",
                "status": "queued"
            }
        ],
        "pagination": {
            "current_page": 1,
            "per_page": 20,
            "last_page": 1,
            "total": 1,
            "has_more_pages": false
        }
    }
}
401 Unauthorized.
{
    "status": "error",
    "message": "Missing bearer token."
}

SMS

Get an SMS message

GET/api/v3/sms/{uid}

Fetches one SMS message by public uid with numeric id fallback.

Vigezo

JinaMahaliAinaLazimaMaelezo
uidpathstringNdiyo
CURL
curl -X GET '/api/v3/sms/{uid}' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'get',
    url: '/api/v3/sms/{uid}',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.get(
    "/api/v3/sms/{uid}",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('GET', '/api/v3/sms/{uid}', [
    '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/sms/{uid}')
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/sms/{uid}", 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 Single SMS message.
{
    "status": "success",
    "data": {
        "id": 101,
        "uid": "msg_01JXYZSMS01",
        "direction": "outbound",
        "channel_type": "sms",
        "recipient": "255700111222",
        "body": "Hello from API v3",
        "status": "delivered"
    }
}
404 Message not found.
{
    "status": "error",
    "message": "Message not found."
}

SMS

View one campaign

GET/api/v3/campaign/{uid}/view

Retrieves one SMS campaign by uid.

Vigezo

JinaMahaliAinaLazimaMaelezo
uidpathstringNdiyo
CURL
curl -X GET '/api/v3/campaign/{uid}/view' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'get',
    url: '/api/v3/campaign/{uid}/view',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.get(
    "/api/v3/campaign/{uid}/view",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('GET', '/api/v3/campaign/{uid}/view', [
    '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/campaign/{uid}/view')
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/campaign/{uid}/view", 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 Campaign details.
{
    "status": "success",
    "data": {
        "id": 15,
        "uid": "cmp_01JXYZ001",
        "name": "API Campaign - VIP List",
        "status": "running",
        "channel_type": "sms",
        "message": "Campaign message"
    }
}
404 Campaign not found.
{
    "status": "error",
    "message": "Campaign not found."
}

WhatsApp

Send a WhatsApp message

POST/api/v3/whatsapp/send

Unified send supporting text, media, template, interactive, and reaction payloads.

Mwili wa ombi

SehemuAinaLazimaMaelezo
recipientstringNdiyo
recipientsarrayHapana
messagestringHapana
bodystringHapana
message_typestringHapana
typestringHapana
media_urlstringHapana
media_typestringHapana
templateobjectHapana
interactiveobjectHapana
reactionobjectHapana
in_reply_to_gateway_idstringHapana
tenant_channel_idintegerHapanaDeprecated compatibility input. Ignored; routing is resolved automatically from the endpoint type, sending identity, and account/system defaults.
CURL
curl -X POST '/api/v3/whatsapp/send' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
    "recipient": "255700111222",
    "message": "Hello from the API"
}'
NODE
const axios = require('axios')

const response = await axios({
    method: 'post',
    url: '/api/v3/whatsapp/send',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json',
      'Content-Type': 'application/json'
    },
    data: {
    "recipient": "255700111222",
    "message": "Hello from the API"
}
})

console.log(response.data)
PYTHON
import requests

response = requests.post(
    "/api/v3/whatsapp/send",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json",
        "Content-Type": "application/json"
    },
    json={
    "recipient": "255700111222",
    "message": "Hello from the API"
}
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('POST', '/api/v3/whatsapp/send', [
    'headers' => [
        'Authorization' => 'Bearer YOUR_API_TOKEN',
        'Accept'        => 'application/json',
        'Content-Type'  => 'application/json',
    ],
    'json' => {
    "recipient": "255700111222",
    "message": "Hello from the API"
},
]);

$data = json_decode($response->getBody(), true);
RUBY
require 'net/http'
require 'json'
require 'uri'

uri = URI('/api/v3/whatsapp/send')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'

request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Accept'] = 'application/json'
request['Content-Type'] = 'application/json'
request.body = '{
    "recipient": "255700111222",
    "message": "Hello from the API"
}'

response = http.request(request)
puts JSON.parse(response.body)
GO
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("POST", "/api/v3/whatsapp/send", bytes.NewBuffer([]byte(`{
    "recipient": "255700111222",
    "message": "Hello from the API"
}`)))
	req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, _ := client.Do(req)
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))
}

Majibu

201 Messages queued.
{
    "status": "success",
    "data": {
        "messages": [
            {
                "id": 300,
                "uid": "msg_01JXYZWA01",
                "direction": "outbound",
                "channel_type": "whatsapp",
                "recipient": "255700111222",
                "body": "Interactive message",
                "status": "queued",
                "metadata": {
                    "interactive": {
                        "type": "button"
                    }
                }
            }
        ]
    }
}
422 Validation or payload combination error.
{
    "status": "error",
    "message": "Reaction cannot be combined with text, media, template, or interactive payload."
}
401 Unauthorized.
{
    "status": "error",
    "message": "Invalid API token."
}

WhatsApp

List WhatsApp messages

GET/api/v3/whatsapp

Returns tenant-scoped WhatsApp message logs with pagination.

Vigezo

JinaMahaliAinaLazimaMaelezo
statusquerystringHapana
directionquerystringHapana
limitqueryintegerHapana
per_pagequeryintegerHapana
CURL
curl -X GET '/api/v3/whatsapp' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'get',
    url: '/api/v3/whatsapp',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.get(
    "/api/v3/whatsapp",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('GET', '/api/v3/whatsapp', [
    '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/whatsapp')
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/whatsapp", 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 WhatsApp collection.
{
    "status": "success",
    "data": {
        "items": [
            {
                "id": 300,
                "uid": "msg_01JXYZWA01",
                "direction": "outbound",
                "channel_type": "whatsapp",
                "recipient": "255700111222",
                "body": "Interactive message",
                "status": "queued"
            }
        ],
        "pagination": {
            "current_page": 1,
            "per_page": 20,
            "last_page": 1,
            "total": 1,
            "has_more_pages": false
        }
    }
}
401 Unauthorized.
{
    "status": "error",
    "message": "Missing bearer token."
}

WhatsApp

Get a WhatsApp message

GET/api/v3/whatsapp/{uid}

Fetches one WhatsApp message by public uid.

Vigezo

JinaMahaliAinaLazimaMaelezo
uidpathstringNdiyo
CURL
curl -X GET '/api/v3/whatsapp/{uid}' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'get',
    url: '/api/v3/whatsapp/{uid}',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.get(
    "/api/v3/whatsapp/{uid}",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('GET', '/api/v3/whatsapp/{uid}', [
    '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/whatsapp/{uid}')
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/whatsapp/{uid}", 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 Single WhatsApp message.
{
    "status": "success",
    "data": {
        "id": 300,
        "uid": "msg_01JXYZWA01",
        "direction": "outbound",
        "channel_type": "whatsapp",
        "recipient": "255700111222",
        "body": "Interactive message",
        "status": "delivered"
    }
}
404 Message not found.
{
    "status": "error",
    "message": "Message not found."
}

Contacts

Create a contact

POST/api/v3/contacts/{group_id}/store

Stores one contact in a group using legacy fields and custom dynamic attributes.

Vigezo

JinaMahaliAinaLazimaMaelezo
group_idpathstringNdiyo

Mwili wa ombi

SehemuAinaLazimaMaelezo
PHONEstringNdiyo
country_codestringHapana
namestringHapana
FIRST_NAMEstringHapana
LAST_NAMEstringHapana
is_subscribedbooleanHapana
CURL
curl -X POST '/api/v3/contacts/{group_id}/store' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
    "PHONE": "255700333444"
}'
NODE
const axios = require('axios')

const response = await axios({
    method: 'post',
    url: '/api/v3/contacts/{group_id}/store',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json',
      'Content-Type': 'application/json'
    },
    data: {
    "PHONE": "255700333444"
}
})

console.log(response.data)
PYTHON
import requests

response = requests.post(
    "/api/v3/contacts/{group_id}/store",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json",
        "Content-Type": "application/json"
    },
    json={
    "PHONE": "255700333444"
}
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('POST', '/api/v3/contacts/{group_id}/store', [
    'headers' => [
        'Authorization' => 'Bearer YOUR_API_TOKEN',
        'Accept'        => 'application/json',
        'Content-Type'  => 'application/json',
    ],
    'json' => {
    "PHONE": "255700333444"
},
]);

$data = json_decode($response->getBody(), true);
RUBY
require 'net/http'
require 'json'
require 'uri'

uri = URI('/api/v3/contacts/{group_id}/store')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'

request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Accept'] = 'application/json'
request['Content-Type'] = 'application/json'
request.body = '{
    "PHONE": "255700333444"
}'

response = http.request(request)
puts JSON.parse(response.body)
GO
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("POST", "/api/v3/contacts/{group_id}/store", bytes.NewBuffer([]byte(`{
    "PHONE": "255700333444"
}`)))
	req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, _ := client.Do(req)
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))
}

Majibu

201 Contact created. Phone numbers are normalised to E.164 (international) format and de-duplicated within the group — re-posting the same PHONE returns the existing row.
{
    "status": "success",
    "data": {
        "id": 66,
        "uid": "ctc_01JXYZ001",
        "group_id": 8,
        "group_uid": "grp_01JXYZABC",
        "name": "John Doe",
        "country_code": "255",
        "phone_number": "700333444",
        "full_phone_number": "255700333444",
        "is_subscribed": true,
        "custom_field_values": {
            "CITY": "Dar es Salaam"
        }
    }
}
404 Group not found.
{
    "status": "error",
    "message": "Contact group not found."
}
422 Validation error.
{
    "status": "error",
    "message": "Validation failed.",
    "errors": {
        "PHONE": [
            "The PHONE field is required."
        ]
    }
}

Contacts

Find a contact

POST/api/v3/contacts/{group_id}/search/{uid}

Finds a single contact in a group by public uid.

Vigezo

JinaMahaliAinaLazimaMaelezo
group_idpathstringNdiyo
uidpathstringNdiyo
CURL
curl -X POST '/api/v3/contacts/{group_id}/search/{uid}' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'post',
    url: '/api/v3/contacts/{group_id}/search/{uid}',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.post(
    "/api/v3/contacts/{group_id}/search/{uid}",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('POST', '/api/v3/contacts/{group_id}/search/{uid}', [
    '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/contacts/{group_id}/search/{uid}')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'

request = Net::HTTP::Post.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("POST", "/api/v3/contacts/{group_id}/search/{uid}", 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 Single contact.
{
    "status": "success",
    "data": {
        "id": 66,
        "uid": "ctc_01JXYZ001",
        "group_id": 8,
        "group_uid": "grp_01JXYZABC",
        "name": "John Doe",
        "country_code": "255",
        "phone_number": "700333444",
        "full_phone_number": "255700333444",
        "is_subscribed": true,
        "custom_field_values": {
            "CITY": "Dar es Salaam"
        }
    }
}
404 Contact not found.
{
    "status": "error",
    "message": "Contact not found."
}

Contacts

Update a contact

PATCH/api/v3/contacts/{group_id}/update/{uid}

Updates a contact record in a group with the same payload conventions as create.

Vigezo

JinaMahaliAinaLazimaMaelezo
group_idpathstringNdiyo
uidpathstringNdiyo

Mwili wa ombi

SehemuAinaLazimaMaelezo
PHONEstringNdiyo
country_codestringHapana
namestringHapana
is_subscribedbooleanHapana
CURL
curl -X PATCH '/api/v3/contacts/{group_id}/update/{uid}' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
    "PHONE": "255700333444",
    "name": "John Updated"
}'
NODE
const axios = require('axios')

const response = await axios({
    method: 'patch',
    url: '/api/v3/contacts/{group_id}/update/{uid}',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json',
      'Content-Type': 'application/json'
    },
    data: {
    "PHONE": "255700333444",
    "name": "John Updated"
}
})

console.log(response.data)
PYTHON
import requests

response = requests.patch(
    "/api/v3/contacts/{group_id}/update/{uid}",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json",
        "Content-Type": "application/json"
    },
    json={
    "PHONE": "255700333444",
    "name": "John Updated"
}
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('PATCH', '/api/v3/contacts/{group_id}/update/{uid}', [
    'headers' => [
        'Authorization' => 'Bearer YOUR_API_TOKEN',
        'Accept'        => 'application/json',
        'Content-Type'  => 'application/json',
    ],
    'json' => {
    "PHONE": "255700333444",
    "name": "John Updated"
},
]);

$data = json_decode($response->getBody(), true);
RUBY
require 'net/http'
require 'json'
require 'uri'

uri = URI('/api/v3/contacts/{group_id}/update/{uid}')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'

request = Net::HTTP::Patch.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Accept'] = 'application/json'
request['Content-Type'] = 'application/json'
request.body = '{
    "PHONE": "255700333444",
    "name": "John Updated"
}'

response = http.request(request)
puts JSON.parse(response.body)
GO
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("PATCH", "/api/v3/contacts/{group_id}/update/{uid}", bytes.NewBuffer([]byte(`{
    "PHONE": "255700333444",
    "name": "John Updated"
}`)))
	req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, _ := client.Do(req)
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))
}

Majibu

200 Contact updated.
{
    "status": "success",
    "data": {
        "id": 66,
        "uid": "ctc_01JXYZ001",
        "group_id": 8,
        "group_uid": "grp_01JXYZABC",
        "name": "John Updated",
        "country_code": "255",
        "phone_number": "700333444",
        "full_phone_number": "255700333444",
        "is_subscribed": true,
        "custom_field_values": []
    }
}
422 Validation error.
{
    "status": "error",
    "message": "Validation failed.",
    "errors": {
        "PHONE": [
            "The PHONE field is required."
        ]
    }
}
404 Contact or group not found.
{
    "status": "error",
    "message": "Contact not found."
}

Contacts

Delete a contact

DELETE/api/v3/contacts/{group_id}/delete/{uid}

Deletes one contact by uid within a contact group.

Vigezo

JinaMahaliAinaLazimaMaelezo
group_idpathstringNdiyo
uidpathstringNdiyo
CURL
curl -X DELETE '/api/v3/contacts/{group_id}/delete/{uid}' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'delete',
    url: '/api/v3/contacts/{group_id}/delete/{uid}',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.delete(
    "/api/v3/contacts/{group_id}/delete/{uid}",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('DELETE', '/api/v3/contacts/{group_id}/delete/{uid}', [
    '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/contacts/{group_id}/delete/{uid}')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'

request = Net::HTTP::Delete.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("DELETE", "/api/v3/contacts/{group_id}/delete/{uid}", 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 Contact deleted.
{
    "status": "success",
    "data": {
        "deleted": true,
        "uid": "ctc_01JXYZ001"
    }
}
404 Contact or group not found.
{
    "status": "error",
    "message": "Contact not found."
}

Contacts

List contacts in a group

POST/api/v3/contacts/{group_id}/all

Lists contacts by group with optional search and pagination controls.

Vigezo

JinaMahaliAinaLazimaMaelezo
group_idpathstringNdiyo

Mwili wa ombi

SehemuAinaLazimaMaelezo
searchstringHapana
limitintegerHapana
per_pageintegerHapana
CURL
curl -X POST '/api/v3/contacts/{group_id}/all' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
    "limit": 25
}'
NODE
const axios = require('axios')

const response = await axios({
    method: 'post',
    url: '/api/v3/contacts/{group_id}/all',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json',
      'Content-Type': 'application/json'
    },
    data: {
    "limit": 25
}
})

console.log(response.data)
PYTHON
import requests

response = requests.post(
    "/api/v3/contacts/{group_id}/all",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json",
        "Content-Type": "application/json"
    },
    json={
    "limit": 25
}
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('POST', '/api/v3/contacts/{group_id}/all', [
    'headers' => [
        'Authorization' => 'Bearer YOUR_API_TOKEN',
        'Accept'        => 'application/json',
        'Content-Type'  => 'application/json',
    ],
    'json' => {
    "limit": 25
},
]);

$data = json_decode($response->getBody(), true);
RUBY
require 'net/http'
require 'json'
require 'uri'

uri = URI('/api/v3/contacts/{group_id}/all')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'

request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Accept'] = 'application/json'
request['Content-Type'] = 'application/json'
request.body = '{
    "limit": 25
}'

response = http.request(request)
puts JSON.parse(response.body)
GO
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("POST", "/api/v3/contacts/{group_id}/all", bytes.NewBuffer([]byte(`{
    "limit": 25
}`)))
	req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, _ := client.Do(req)
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))
}

Majibu

200 Contact collection. Phone numbers are returned in two parts: `country_code` + `phone_number` (local), and a pre-joined `full_phone_number`.
{
    "status": "success",
    "data": {
        "items": [
            {
                "id": 66,
                "uid": "ctc_01JXYZ001",
                "group_id": 8,
                "group_uid": "grp_01JXYZABC",
                "name": "John Doe",
                "country_code": "255",
                "phone_number": "700333444",
                "full_phone_number": "255700333444",
                "is_subscribed": true,
                "custom_field_values": {
                    "CITY": "Dar es Salaam"
                }
            }
        ],
        "pagination": {
            "current_page": 1,
            "per_page": 25,
            "last_page": 1,
            "total": 1,
            "has_more_pages": false
        }
    }
}
404 Group not found.
{
    "status": "error",
    "message": "Contact group not found."
}

Profile & Balance

Get current account

GET/api/v3/me

Returns the tenant profile represented by the bearer token.

CURL
curl -X GET '/api/v3/me' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'get',
    url: '/api/v3/me',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.get(
    "/api/v3/me",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('GET', '/api/v3/me', [
    '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/me')
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/me", 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 Tenant profile.
{
    "status": "success",
    "data": {
        "id": 12,
        "name": "Workspace Alpha",
        "slug": "workspace-alpha",
        "external_client_id": null
    }
}
401 Unauthorized.
{
    "status": "error",
    "message": "Invalid API token."
}

Webhooks

How webhooks work — payload reference (not an endpoint)

POST/api/v3/webhooks

**This is a documentation entry, not a callable endpoint.** It describes the payload your server will receive when subscribed events fire. When a subscribed event occurs, we send a `POST` to your configured webhook URL with a JSON body and these headers: | Header | Purpose | |---|---| | `Content-Type: application/json` | Always JSON. | | `User-Agent: MomoBusiness-Webhook/1.0` | Identifies our delivery agent. | | `X-Webhook-Signature: sha256=<hex>` | HMAC-SHA256 of the raw request body using your webhook secret — verify before trusting the payload. | | `X-Event: <event_name>` | Repeats the `event` field from the body for fast routing. | ## Verifying the signature ```php $signature = $request->header('X-Webhook-Signature'); $expected = 'sha256='.hash_hmac('sha256', $request->getContent(), $yourWebhookSecret); if (! hash_equals($expected, $signature)) abort(401); ``` ```node const crypto = require('crypto'); const expected = crypto.createHmac('sha256', YOUR_WEBHOOK_SECRET).update(rawBody).digest('hex'); if ('sha256=' + expected !== req.headers['x-webhook-signature']) return res.status(401).end(); ``` ## Subscribed event names | Event | When it fires | |---|---| | `message.received` | Customer sent you a message (any channel). | | `message.sent` | Your outbound message was accepted by the gateway. | | `message.delivered` | Gateway confirmed delivery to the recipient device. | | `message.read` | Recipient opened the message (WhatsApp only, requires read receipts). | | `message.failed` | Send failed; `metadata.error` carries the gateway message. | | `message.echoed` | An agent replied via the WhatsApp Business app on their phone (out-of-band reply mirrored back to you). | | `order.received` | Customer submitted a cart through your WhatsApp catalogue. | ## Retry policy If your endpoint returns a non-2xx status or doesn't respond within 10s, we retry with exponential backoff (15s, 60s, 300s, 1800s, 3600s) for up to 24h. Idempotency key: `message_id` + `event`.

Mwili wa ombi

SehemuAinaLazimaMaelezo
eventstringNdiyoWhich event triggered this delivery.
message_idintegerNdiyoOur internal message identifier.
directionstringNdiyo
senderstringHapanaE.164 phone (or sender ID for SMS) of the message originator.
recipientstringHapanaE.164 phone of the message recipient.
statusstringNdiyo
bodystringHapanaMessage text or caption (null for media-only / interactive replies).
media_urlstringHapanaDirect URL to the media file when the message contains media.
channel_typestringNdiyo
timestampstringNdiyoISO 8601 timestamp of when the event was emitted.
CURL
curl -X POST '/api/v3/webhooks' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
    "event": "message.received",
    "message_id": 8421,
    "direction": "inbound",
    "sender": "255700111222",
    "recipient": "255700000111",
    "status": "received",
    "body": "Hi, is the laptop still in stock?",
    "media_url": null,
    "channel_type": "whatsapp",
    "timestamp": "2026-04-18T10:21:33Z"
}'
NODE
const axios = require('axios')

const response = await axios({
    method: 'post',
    url: '/api/v3/webhooks',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json',
      'Content-Type': 'application/json'
    },
    data: {
    "event": "message.received",
    "message_id": 8421,
    "direction": "inbound",
    "sender": "255700111222",
    "recipient": "255700000111",
    "status": "received",
    "body": "Hi, is the laptop still in stock?",
    "media_url": null,
    "channel_type": "whatsapp",
    "timestamp": "2026-04-18T10:21:33Z"
}
})

console.log(response.data)
PYTHON
import requests

response = requests.post(
    "/api/v3/webhooks",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json",
        "Content-Type": "application/json"
    },
    json={
    "event": "message.received",
    "message_id": 8421,
    "direction": "inbound",
    "sender": "255700111222",
    "recipient": "255700000111",
    "status": "received",
    "body": "Hi, is the laptop still in stock?",
    "media_url": null,
    "channel_type": "whatsapp",
    "timestamp": "2026-04-18T10:21:33Z"
}
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('POST', '/api/v3/webhooks', [
    'headers' => [
        'Authorization' => 'Bearer YOUR_API_TOKEN',
        'Accept'        => 'application/json',
        'Content-Type'  => 'application/json',
    ],
    'json' => {
    "event": "message.received",
    "message_id": 8421,
    "direction": "inbound",
    "sender": "255700111222",
    "recipient": "255700000111",
    "status": "received",
    "body": "Hi, is the laptop still in stock?",
    "media_url": null,
    "channel_type": "whatsapp",
    "timestamp": "2026-04-18T10:21:33Z"
},
]);

$data = json_decode($response->getBody(), true);
RUBY
require 'net/http'
require 'json'
require 'uri'

uri = URI('/api/v3/webhooks')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'

request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Accept'] = 'application/json'
request['Content-Type'] = 'application/json'
request.body = '{
    "event": "message.received",
    "message_id": 8421,
    "direction": "inbound",
    "sender": "255700111222",
    "recipient": "255700000111",
    "status": "received",
    "body": "Hi, is the laptop still in stock?",
    "media_url": null,
    "channel_type": "whatsapp",
    "timestamp": "2026-04-18T10:21:33Z"
}'

response = http.request(request)
puts JSON.parse(response.body)
GO
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("POST", "/api/v3/webhooks", bytes.NewBuffer([]byte(`{
    "event": "message.received",
    "message_id": 8421,
    "direction": "inbound",
    "sender": "255700111222",
    "recipient": "255700000111",
    "status": "received",
    "body": "Hi, is the laptop still in stock?",
    "media_url": null,
    "channel_type": "whatsapp",
    "timestamp": "2026-04-18T10:21:33Z"
}`)))
	req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, _ := client.Do(req)
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))
}

Majibu

200 Your endpoint should respond with a 2xx within 10 seconds. Body is ignored.
{
    "ok": true
}
401 Return 401 when the signature does not match — we will retry, but persistent 401s eventually disable the webhook.
{
    "error": "Invalid signature"
}

Profile & Balance

Get balance

GET/api/v3/balance

Returns wallet balance, currency, billing mode, and spend metadata.

CURL
curl -X GET '/api/v3/balance' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'get',
    url: '/api/v3/balance',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.get(
    "/api/v3/balance",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('GET', '/api/v3/balance', [
    '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/balance')
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/balance", 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 Tenant wallet balance.
{
    "status": "success",
    "data": {
        "wallet_balance": 12000.5,
        "wallet_currency": "TZS",
        "billing_mode": "prepaid",
        "cumulative_spend_cents": 0,
        "tier_override": false
    }
}
401 Unauthorized.
{
    "status": "error",
    "message": "Missing bearer token."
}

Catalogue

List shops

GET/api/v3/catalogues

List the shops (product catalogues) belonging to your tenant, with their name, currency and the channels each is published to.

CURL
curl -X GET '/api/v3/catalogues' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'get',
    url: '/api/v3/catalogues',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.get(
    "/api/v3/catalogues",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('GET', '/api/v3/catalogues', [
    '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/catalogues')
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/catalogues", 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 Successful response.
{
    "status": "success",
    "data": []
}
422 Validation error.
{
    "status": "error",
    "message": "Validation failed.",
    "errors": {
        "field": [
            "The field is required."
        ]
    }
}

Catalogue

Get one shop with its products

GET/api/v3/catalogues/{catalogue}

Return a single shop and the products it holds, including SKU, price, availability, brand and category.

Vigezo

JinaMahaliAinaLazimaMaelezo
cataloguepathintegerNdiyoThe shop id, as returned by the shop list.
CURL
curl -X GET '/api/v3/catalogues/{catalogue}' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'get',
    url: '/api/v3/catalogues/{catalogue}',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.get(
    "/api/v3/catalogues/{catalogue}",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('GET', '/api/v3/catalogues/{catalogue}', [
    '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/catalogues/{catalogue}')
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/catalogues/{catalogue}", 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 Successful response.
{
    "status": "success",
    "data": []
}
422 Validation error.
{
    "status": "error",
    "message": "Validation failed.",
    "errors": {
        "field": [
            "The field is required."
        ]
    }
}

Catalogue

Add a product to a shop

POST/api/v3/catalogues/{catalogue}/products

Create one product in a shop. The SKU is optional — leave it out and the shop issues its own code, which is also what gets sent to any connected platform as its required retailer id.

Vigezo

JinaMahaliAinaLazimaMaelezo
cataloguepathintegerNdiyoThe shop the product belongs to.
CURL
curl -X POST '/api/v3/catalogues/{catalogue}/products' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'post',
    url: '/api/v3/catalogues/{catalogue}/products',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.post(
    "/api/v3/catalogues/{catalogue}/products",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('POST', '/api/v3/catalogues/{catalogue}/products', [
    '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/catalogues/{catalogue}/products')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'

request = Net::HTTP::Post.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("POST", "/api/v3/catalogues/{catalogue}/products", 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 Successful response.
{
    "status": "success",
    "data": []
}
422 Validation error.
{
    "status": "error",
    "message": "Validation failed.",
    "errors": {
        "field": [
            "The field is required."
        ]
    }
}

Catalogue

List a shop's products

GET/api/v3/catalogues/{catalogue}/products

List the products in one shop, with SKU, price, availability, brand and category.

Vigezo

JinaMahaliAinaLazimaMaelezo
cataloguepathintegerNdiyoThe shop whose products to list.
CURL
curl -X GET '/api/v3/catalogues/{catalogue}/products' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'get',
    url: '/api/v3/catalogues/{catalogue}/products',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.get(
    "/api/v3/catalogues/{catalogue}/products",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('GET', '/api/v3/catalogues/{catalogue}/products', [
    '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/catalogues/{catalogue}/products')
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/catalogues/{catalogue}/products", 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 Successful response.
{
    "status": "success",
    "data": []
}
422 Validation error.
{
    "status": "error",
    "message": "Validation failed.",
    "errors": {
        "field": [
            "The field is required."
        ]
    }
}

Catalogue

Add or update many products at once

POST/api/v3/catalogues/{catalogue}/products/batch

Create or update products in bulk. Rows are matched on SKU where you supply one, so re-sending the same batch updates rather than duplicating.

Vigezo

JinaMahaliAinaLazimaMaelezo
cataloguepathintegerNdiyoThe shop the products belong to.
CURL
curl -X POST '/api/v3/catalogues/{catalogue}/products/batch' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'post',
    url: '/api/v3/catalogues/{catalogue}/products/batch',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.post(
    "/api/v3/catalogues/{catalogue}/products/batch",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('POST', '/api/v3/catalogues/{catalogue}/products/batch', [
    '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/catalogues/{catalogue}/products/batch')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'

request = Net::HTTP::Post.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("POST", "/api/v3/catalogues/{catalogue}/products/batch", 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 Successful response.
{
    "status": "success",
    "data": []
}
422 Validation error.
{
    "status": "error",
    "message": "Validation failed.",
    "errors": {
        "field": [
            "The field is required."
        ]
    }
}

Catalogue

Remove a product from a shop

DELETE/api/v3/catalogues/{catalogue}/products/{product}

Delete one product. It is also retired from any platform it had been published to.

Vigezo

JinaMahaliAinaLazimaMaelezo
cataloguepathintegerNdiyoThe shop the product belongs to.
productpathintegerNdiyoThe product to remove.
CURL
curl -X DELETE '/api/v3/catalogues/{catalogue}/products/{product}' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'delete',
    url: '/api/v3/catalogues/{catalogue}/products/{product}',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.delete(
    "/api/v3/catalogues/{catalogue}/products/{product}",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('DELETE', '/api/v3/catalogues/{catalogue}/products/{product}', [
    '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/catalogues/{catalogue}/products/{product}')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'

request = Net::HTTP::Delete.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("DELETE", "/api/v3/catalogues/{catalogue}/products/{product}", 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 Successful response.
{
    "status": "success",
    "data": []
}
422 Validation error.
{
    "status": "error",
    "message": "Validation failed.",
    "errors": {
        "field": [
            "The field is required."
        ]
    }
}

Catalogue

Get one product

GET/api/v3/catalogues/{catalogue}/products/{product}

Return a single product in full, including its description, images and the platforms it is currently published to.

Vigezo

JinaMahaliAinaLazimaMaelezo
cataloguepathintegerNdiyoThe shop the product belongs to.
productpathintegerNdiyoThe product to return.
CURL
curl -X GET '/api/v3/catalogues/{catalogue}/products/{product}' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'get',
    url: '/api/v3/catalogues/{catalogue}/products/{product}',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.get(
    "/api/v3/catalogues/{catalogue}/products/{product}",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('GET', '/api/v3/catalogues/{catalogue}/products/{product}', [
    '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/catalogues/{catalogue}/products/{product}')
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/catalogues/{catalogue}/products/{product}", 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 Successful response.
{
    "status": "success",
    "data": []
}
422 Validation error.
{
    "status": "error",
    "message": "Validation failed.",
    "errors": {
        "field": [
            "The field is required."
        ]
    }
}

Catalogue

Update a product

PUT/api/v3/catalogues/{catalogue}/products/{product}

Update a product's details. The SKU is fixed once the product has been published to a platform, because that platform treats it as the item's identity.

Vigezo

JinaMahaliAinaLazimaMaelezo
cataloguepathintegerNdiyoThe shop the product belongs to.
productpathintegerNdiyoThe product to update.
CURL
curl -X PUT '/api/v3/catalogues/{catalogue}/products/{product}' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'put',
    url: '/api/v3/catalogues/{catalogue}/products/{product}',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.put(
    "/api/v3/catalogues/{catalogue}/products/{product}",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('PUT', '/api/v3/catalogues/{catalogue}/products/{product}', [
    '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/catalogues/{catalogue}/products/{product}')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'

request = Net::HTTP::Put.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("PUT", "/api/v3/catalogues/{catalogue}/products/{product}", 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 Successful response.
{
    "status": "success",
    "data": []
}
422 Validation error.
{
    "status": "error",
    "message": "Validation failed.",
    "errors": {
        "field": [
            "The field is required."
        ]
    }
}

Catalogue

List shop orders

GET/api/v3/catalogues/orders

List orders placed against your shops, newest first, with their status, total and whether payment has settled.

CURL
curl -X GET '/api/v3/catalogues/orders' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'get',
    url: '/api/v3/catalogues/orders',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.get(
    "/api/v3/catalogues/orders",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('GET', '/api/v3/catalogues/orders', [
    '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/catalogues/orders')
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/catalogues/orders", 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 Successful response.
{
    "status": "success",
    "data": []
}
422 Validation error.
{
    "status": "error",
    "message": "Validation failed.",
    "errors": {
        "field": [
            "The field is required."
        ]
    }
}

Catalogue

Get one order

GET/api/v3/catalogues/orders/{order}

Return a single order in full: the customer, the items ordered, the total, the status history and every payment attempt against it.

Vigezo

JinaMahaliAinaLazimaMaelezo
orderpathintegerNdiyoThe order id, as returned by the order list.
CURL
curl -X GET '/api/v3/catalogues/orders/{order}' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'get',
    url: '/api/v3/catalogues/orders/{order}',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.get(
    "/api/v3/catalogues/orders/{order}",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('GET', '/api/v3/catalogues/orders/{order}', [
    '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/catalogues/orders/{order}')
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/catalogues/orders/{order}", 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 Successful response.
{
    "status": "success",
    "data": []
}
422 Validation error.
{
    "status": "error",
    "message": "Validation failed.",
    "errors": {
        "field": [
            "The field is required."
        ]
    }
}

Catalogue

Move an order to a new status

PUT/api/v3/catalogues/orders/{order}/status

Advance an order to confirmed, processing, shipped, delivered, cancelled or refunded, and optionally notify the customer on the channel they ordered from.

Vigezo

JinaMahaliAinaLazimaMaelezo
orderpathintegerNdiyoThe order to move.
CURL
curl -X PUT '/api/v3/catalogues/orders/{order}/status' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'put',
    url: '/api/v3/catalogues/orders/{order}/status',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.put(
    "/api/v3/catalogues/orders/{order}/status",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('PUT', '/api/v3/catalogues/orders/{order}/status', [
    '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/catalogues/orders/{order}/status')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'

request = Net::HTTP::Put.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("PUT", "/api/v3/catalogues/orders/{order}/status", 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 Successful response.
{
    "status": "success",
    "data": []
}
422 Validation error.
{
    "status": "error",
    "message": "Validation failed.",
    "errors": {
        "field": [
            "The field is required."
        ]
    }
}

Catalogue

Send one product to a customer

POST/api/v3/catalogues/send-product

Send a single product card to a customer on WhatsApp, so they can view it and add it to a cart without leaving the chat.

CURL
curl -X POST '/api/v3/catalogues/send-product' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'post',
    url: '/api/v3/catalogues/send-product',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.post(
    "/api/v3/catalogues/send-product",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('POST', '/api/v3/catalogues/send-product', [
    '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/catalogues/send-product')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'

request = Net::HTTP::Post.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("POST", "/api/v3/catalogues/send-product", 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 Successful response.
{
    "status": "success",
    "data": []
}
422 Validation error.
{
    "status": "error",
    "message": "Validation failed.",
    "errors": {
        "field": [
            "The field is required."
        ]
    }
}

Catalogue

Send a list of products to a customer

POST/api/v3/catalogues/send-product-list

Send several products grouped into sections as one interactive message — the usual way to answer "what do you have?" in chat.

CURL
curl -X POST '/api/v3/catalogues/send-product-list' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'post',
    url: '/api/v3/catalogues/send-product-list',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.post(
    "/api/v3/catalogues/send-product-list",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('POST', '/api/v3/catalogues/send-product-list', [
    '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/catalogues/send-product-list')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'

request = Net::HTTP::Post.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("POST", "/api/v3/catalogues/send-product-list", 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 Successful response.
{
    "status": "success",
    "data": []
}
422 Validation error.
{
    "status": "error",
    "message": "Validation failed.",
    "errors": {
        "field": [
            "The field is required."
        ]
    }
}

Catalogue

Send the whole shop to a customer

POST/api/v3/catalogues/send-catalogue

Send the full catalogue as one message, letting the customer browse everything the shop has published to that channel.

CURL
curl -X POST '/api/v3/catalogues/send-catalogue' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Accept: application/json'
NODE
const axios = require('axios')

const response = await axios({
    method: 'post',
    url: '/api/v3/catalogues/send-catalogue',
    headers: {
      Authorization: 'Bearer YOUR_API_TOKEN',
      Accept: 'application/json'
    }
})

console.log(response.data)
PYTHON
import requests

response = requests.post(
    "/api/v3/catalogues/send-catalogue",
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Accept": "application/json"
    }
)

print(response.json())
PHP
$client = new \GuzzleHttp\Client();

$response = $client->request('POST', '/api/v3/catalogues/send-catalogue', [
    '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/catalogues/send-catalogue')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'

request = Net::HTTP::Post.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("POST", "/api/v3/catalogues/send-catalogue", 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 Successful response.
{
    "status": "success",
    "data": []
}
422 Validation error.
{
    "status": "error",
    "message": "Validation failed.",
    "errors": {
        "field": [
            "The field is required."
        ]
    }
}

Ushirikiano wa uhandisi

Kutoka mfano wa kwanza hadi uendeshaji wa uzalishaji.

OpenAPIMkataba unaosomeka na mashine.

MifanoMaombi yanayoweza kunakiliwa.

MsaadaTuma ombi · support@business.momo.tz

Anza leo

Unganisha mawasiliano yako.

Fungua akaunti na ujaribu API katika workspace yako.