Tunzaa

Introduction

#

Welcome to the Malipo API documentation. We provide a simple yet powerful REST API to accept payments, manage subscriptions, and handle payouts across Africa.

Our API is organized around REST. Our API has predictable resource-oriented URLs, accepts form-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes, authentication, and verbs.

Base URL

https://pay.tunzaa.co.tz

Authentication

#

The Malipo API uses API keys to authenticate requests. You can view and manage your API keys in the Dashboard.

Exchange your api_key and secret_key for a time-limited Bearer token via POST /accounts/request/token. Keep your credentials secure and never expose them in client-side code or public repositories.

Authenticate subsequent requests with HTTP Bearer Auth and include the X-Environment header (sandbox or live).
cURL Example
curl -X POST "https://pay.tunzaa.co.tz/accounts/request/token" \
  -H "Content-Type: application/json" \
  -H "X-Environment: sandbox" \
  -d '{
  "api_key": "YOUR_API_KEY",
  "secret_key": "YOUR_SECRET_KEY"
}'

AI Integration (MCP)

#

Malipo by Tunzaa supports the Model Context Protocol (MCP). Add this single config to any MCP-compatible editor (Claude Desktop, Cursor, Windsurf, etc.) and your AI assistant gets full context about the Malipo API.

Why use MCP?

By configuring the MCP server, you give your AI context about the Malipo API, enabling it to write integration code, debug errors, and answer questions specifically for your codebase.

  • Claude Desktop: Settings > Developer > Edit Config, then paste the JSON into the mcpServers section.
  • Cursor: Settings > Features > MCP, then add a new server and paste the JSON config (or open mcp.json).
  • Windsurf: Open the Cascade panel, click the MCPs icon, choose View Raw Config, and paste the JSON into the mcpServers section.
MCP Server Config
{
  "mcpServers": {
    "tunzaa": {
      "command": "npx",
      "args": ["-y", "github:Tunzaa/tunzaa_mcp"]
    }
  }
}

Authentication

#

Authenticate your requests using your API Key and Secret.

Get Access Token

#

Method: POST

POST
/accounts/request/token

Exchange your API credentials for a time-limited Bearer token.

Request
curl -X POST "https://pay.tunzaa.co.tz/accounts/request/token" \
  -H "Content-Type: application/json" \
  -H "X-Environment: sandbox" \
  -d '{
  "api_key": "YOUR_API_KEY",
  "secret_key": "YOUR_SECRET_KEY"
}'
Response
{
  "access_token": "eyJhbGciOiJIUzI1Ni...",
  "expires_in": 3600,
  "token_type": "Bearer"
}

Payments

#

Initiate and track mobile money transactions.

Initiate Payment

#

Method: POST

POST
/payments/initiate-payment

Trigger a USSD push to the customer's mobile device. In sandbox mode you can simulate outcomes via the X-Sandbox-Scenario header (success, failure).

Request
curl -X POST "https://pay.tunzaa.co.tz/payments/initiate-payment" \
  -H "Content-Type: application/json" \
  -H "X-Environment: sandbox" \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -d '{
  "customer_msisdn": "255700000000",
  "amount": "1000",
  "reference": "ORDER-123"
}'
Sandbox scenario: add the optional X-Sandbox-Scenario header (e.g., success or failure) to simulate transaction outcomes without real money moving.
Response
{
  "statusCode": 202,
  "success": true,
  "message": "Payment request accepted and being processed",
  "transactionID": "TXN_12345"
}

Check Payment Status

#

Method: GET

GET
/payments/check-status/:transactionID

Verify the final status of a transaction.

ParameterTypeDescription
transactionIDRequiredstringThe ID returned from initiation
Request
curl -X GET "https://pay.tunzaa.co.tz/payments/check-status/:transactionID" \
  -H "Content-Type: application/json" \
  -H "X-Environment: sandbox" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"
Response
{
  "statusCode": 200,
  "success": true,
  "message": "Transaction status retrieved successfully",
  "data": {
    "transactionID": "TXN_12345",
    "status": "COMPLETED",
    "amount": "1000.00",
    "customerMsisdn": "255700000000",
    "paymentDate": "2024-11-25T14:30:45",
    "utilityref": "REF12345",
    "remark": "Payment processed successfully"
  }
}

Installment Plans

#

Manage Buy Now Pay Later (BNPL) plans.

Create Plan

#

Method: POST

POST
/installments/create

Create a new installment plan for a customer.

Request
curl -X POST "https://pay.tunzaa.co.tz/installments/create" \
  -H "Content-Type: application/json" \
  -H "X-Environment: sandbox" \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -d '{
  "customer": {
    "first_name": "John",
    "last_name": "Doe",
    "phone": "255700000000",
    "address": "Dar es Salaam"
  },
  "name": "Samsung S20",
  "description": "Smartphone purchase",
  "total_amount": 500000,
  "payment_frequency": "monthly",
  "start_date": "2024-01-01",
  "end_date": "2024-06-01"
}'
Response
{
  "customer": {
    "customer_id": 1234,
    "first_name": "John",
    "last_name": "Doe",
    "phone": "255700000000",
    "address": "Dar es Salaam",
    "created_at": "2024-01-01T00:00:00",
    "updated_at": "2024-01-01T00:00:00"
  },
  "plan": {
    "plan_id": 1001,
    "customer": 1234,
    "name": "Samsung S20",
    "description": "Smartphone purchase",
    "total_amount": "500000.00",
    "paid_amount": "0.00",
    "remaining_balance": "500000.00",
    "payment_frequency": "monthly",
    "start_date": "2024-01-01",
    "end_date": "2024-06-01",
    "custom_interval": null,
    "status": "active"
  },
  "installments": [
    {
      "installment_id": 10000,
      "installment_number": 1,
      "amount": "83334.00",
      "due_date": "2024-01-01",
      "status": "PENDING",
      "created_at": "2024-01-01T00:00:00",
      "updated_at": "2024-01-01T00:00:00"
    }
  ]
}

List Plans

#

Method: GET

GET
/installments/

Retrieve a paginated list of installment plans.

ParameterTypeDescription
pagenumberPage number (default: 1)
page_sizenumberItems per page (default: 20, max: 1000)
Request
curl -X GET "https://pay.tunzaa.co.tz/installments/" \
  -H "Content-Type: application/json" \
  -H "X-Environment: sandbox" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"
Response
{
  "count": 1,
  "next_page": null,
  "previous_page": null,
  "results": [
    {
      "plan_id": 1001,
      "name": "Samsung S20",
      "description": "Smartphone purchase",
      "total_amount": "500000.00",
      "paid_amount": "0.00",
      "remaining_balance": "500000.00",
      "payment_frequency": "monthly",
      "start_date": "2024-01-01",
      "end_date": "2024-06-01",
      "status": "active"
    }
  ]
}

Get Plan Details

#

Method: GET

GET
/installments/:plan_id

Retrieve full details for a specific installment plan including the customer, plan summary, and generated installments. Append ?include_payments=true to also receive completed payment history and progress totals.

ParameterTypeDescription
plan_idRequirednumberThe unique 6-digit plan identifier
include_paymentsbooleanWhen true, adds payment_history and progress (paid/remaining/percentage) to the response
Request
curl -X GET "https://pay.tunzaa.co.tz/installments/:plan_id" \
  -H "Content-Type: application/json" \
  -H "X-Environment: sandbox" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"
Response
{
  "customer": {
    "customer_id": 1234,
    "first_name": "John",
    "last_name": "Doe",
    "phone": "255700000000",
    "address": "Dar es Salaam",
    "created_at": "2024-01-01T00:00:00",
    "updated_at": "2024-01-01T00:00:00"
  },
  "plan": {
    "plan_id": 1001,
    "customer": 1234,
    "name": "Samsung S20",
    "description": "Smartphone purchase",
    "total_amount": "500000.00",
    "paid_amount": "0.00",
    "remaining_balance": "500000.00",
    "payment_frequency": "monthly",
    "start_date": "2024-01-01",
    "end_date": "2024-06-01",
    "custom_interval": null,
    "status": "active"
  },
  "installments": [
    {
      "installment_id": 10000,
      "installment_number": 1,
      "amount": "83334.00",
      "due_date": "2024-01-01",
      "status": "PENDING",
      "created_at": "2024-01-01T00:00:00",
      "updated_at": "2024-01-01T00:00:00"
    }
  ]
}

Update Plan

#

Method: PUT

PUT
/installments/:plan_id/update

Partially update a plan and/or its customer. Changing any critical plan field (start_date, end_date, total_amount, payment_frequency, or custom_interval) will regenerate the installment schedule. Other fields update in place without affecting installments.

ParameterTypeDescription
plan_idRequirednumberThe unique 6-digit plan identifier
Request
curl -X PUT "https://pay.tunzaa.co.tz/installments/:plan_id/update" \
  -H "Content-Type: application/json" \
  -H "X-Environment: sandbox" \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -d '{
  "customer": {
    "phone": "255711111111"
  },
  "total_amount": 450000
}'
Response
{
  "customer": {
    "customer_id": 1234,
    "first_name": "John",
    "last_name": "Doe",
    "phone": "255711111111",
    "address": "Dar es Salaam",
    "created_at": "2024-01-01T00:00:00",
    "updated_at": "2024-01-01T00:00:00"
  },
  "plan": {
    "plan_id": 1001,
    "customer": 1234,
    "name": "Samsung S20",
    "description": "Smartphone purchase",
    "total_amount": "450000.00",
    "paid_amount": "0.00",
    "remaining_balance": "450000.00",
    "payment_frequency": "monthly",
    "start_date": "2024-01-01",
    "end_date": "2024-06-01",
    "custom_interval": null,
    "status": "active"
  },
  "installments": [
    {
      "installment_id": 10000,
      "installment_number": 1,
      "amount": "75000.00",
      "due_date": "2024-01-01",
      "status": "PENDING",
      "created_at": "2024-01-01T00:00:00",
      "updated_at": "2024-01-01T00:00:00"
    }
  ]
}

Cancel Plan

#

Method: DELETE

DELETE
/installments/:plan_id/cancel

Terminate an installment plan.

ParameterTypeDescription
plan_idRequirednumber-
Request
curl -X DELETE "https://pay.tunzaa.co.tz/installments/:plan_id/cancel" \
  -H "Content-Type: application/json" \
  -H "X-Environment: sandbox" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"
Response
{
  "message": "Plan and its installments have been cancelled successfully.",
  "plan_id": 353095,
  "plan_status": "cancelled",
  "installments_updated": 4
}

Invoices

#

Create, send, and manage customer-facing invoices. Invoices support a public payment page and mobile money C2B push.

Create Invoice

#

Method: POST

POST
/invoices/create/

Create a new invoice for the authenticated merchant. Set `save_as_draft` to true to keep it as a draft; otherwise it is marked as sent immediately.

Request
curl -X POST "https://pay.tunzaa.co.tz/invoices/create/" \
  -H "Content-Type: application/json" \
  -H "X-Environment: sandbox" \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -d '{
  "recipient_name": "John Doe",
  "recipient_email": "john@example.com",
  "recipient_phone": "255700000000",
  "amount": 150000,
  "currency": "TZS",
  "description": "Consulting services",
  "due_date": "2025-01-31",
  "metadata": null,
  "save_as_draft": false
}'
Response
{
  "statusCode": 201,
  "success": true,
  "message": "Invoice created successfully.",
  "data": {
    "invoice_id": "INV-XXXXXXX",
    "merchant_name": "Tunzaa Merchant",
    "recipient_name": "John Doe",
    "recipient_email": "john@example.com",
    "recipient_phone": "255700000000",
    "amount": "150000.00",
    "currency": "TZS",
    "description": "Consulting services",
    "due_date": "2025-01-31",
    "status": "sent",
    "paid_at": null,
    "metadata": null,
    "created_at": "2025-01-01T10:00:00"
  }
}

List Invoices

#

Method: GET

GET
/invoices/

List invoices for the authenticated merchant. Admins can filter by merchant and status.

ParameterTypeDescription
statusstringFilter by status: draft, sent, paid, cancelled, expired
merchantstringAdmin only: filter by merchant_id
Request
curl -X GET "https://pay.tunzaa.co.tz/invoices/" \
  -H "Content-Type: application/json" \
  -H "X-Environment: sandbox" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"
Response
{
  "statusCode": 200,
  "success": true,
  "data": [
    {
      "invoice_id": "INV-XXXXXXX",
      "merchant_name": "Tunzaa Merchant",
      "recipient_name": "John Doe",
      "amount": "150000.00",
      "currency": "TZS",
      "status": "sent",
      "due_date": "2025-01-31",
      "created_at": "2025-01-01T10:00:00"
    }
  ]
}

Get Invoice

#

Method: GET

GET
/invoices/:invoice_id/

Retrieve full details of a specific invoice.

ParameterTypeDescription
invoice_idRequiredstringThe invoice ID (e.g. INV-XXXXXXX)
Request
curl -X GET "https://pay.tunzaa.co.tz/invoices/:invoice_id/" \
  -H "Content-Type: application/json" \
  -H "X-Environment: sandbox" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Update Invoice

#

Method: PATCH

PATCH
/invoices/:invoice_id/update/

Partially update an invoice. Only draft invoices can be updated.

ParameterTypeDescription
invoice_idRequiredstringThe invoice ID
Request
curl -X PATCH "https://pay.tunzaa.co.tz/invoices/:invoice_id/update/" \
  -H "Content-Type: application/json" \
  -H "X-Environment: sandbox" \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -d '{
  "recipient_name": "Jane Doe",
  "amount": 160000
}'

Cancel Invoice

#

Method: DELETE

DELETE
/invoices/:invoice_id/cancel/

Cancel/void an invoice. Paid invoices cannot be cancelled.

ParameterTypeDescription
invoice_idRequiredstringThe invoice ID
Request
curl -X DELETE "https://pay.tunzaa.co.tz/invoices/:invoice_id/cancel/" \
  -H "Content-Type: application/json" \
  -H "X-Environment: sandbox" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Send Invoice Email

#

Method: POST

POST
/invoices/:invoice_id/send/

Send (or re-send) the invoice payment link to the recipient's email.

ParameterTypeDescription
invoice_idRequiredstringThe invoice ID
Request
curl -X POST "https://pay.tunzaa.co.tz/invoices/:invoice_id/send/" \
  -H "Content-Type: application/json" \
  -H "X-Environment: sandbox" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

How Callbacks Work

#

Configure your callback URL in merchant settings to receive automatic transaction status updates.

Callback Process

  1. Transaction Status Changes: When a payment completes or fails
  2. Malipo Sends POST: To your configured callback URL
  3. Your System Processes: Handle the update and respond with 200 OK
  4. Retry on Failure: Up to 5 attempts if response is not 200 OK

Callbacks

#

Transaction status notifications sent from Malipo to your system.

Transaction Status Callback

#

Method: POST

POST
https://your-domain.com/callback-url

Malipo sends POST requests to your configured sandbox or live callback URL when a transaction completes, fails, or is linked to an invoice. The X-Signature header is an HMAC-SHA256 hex digest of the JSON payload (with alphabetically sorted keys) computed using the API secret key for the transaction's environment.

ParameterTypeDescription
transaction_idstringUnique identifier for the transaction on Malipo
reference_idstringReference for the transaction on the merchant's system
invoice_idstringInvoice ID if the transaction was linked to an invoice, otherwise null
statusstringCurrent status of the transaction (COMPLETED, FAILED)
amountstringTransaction amount
payment_datestringPayment date (Format: YYYY-MM-DD HH:mm:ss)
timestampstringWhen the callback was sent (Format: YYYY-MM-DD HH:mm:ss)
remarkstringHuman-readable note about the transaction status
Callback Headers

This is what you'll receive from Malipo

{
  "Content-Type": "application/json",
  "X-Signature": "<HMAC_SHA256_SIGNATURE>"
}
Callback Payload
{
  "transaction_id": "TXNVV3BHMU",
  "reference_id": "REF12345",
  "invoice_id": "INV-XXXXXXX",
  "status": "COMPLETED",
  "amount": "1500.00",
  "payment_date": "2024-11-25 14:30:45",
  "timestamp": "2024-11-25 16:45:10",
  "remark": "Payment successful"
}