Dodo Delivery API
Integrate delivery services into your app in minutes.
https://api.dodo.co.tz/api/v1
https://sandbox.dodo.co.tz/api/v1
Getting Started
Create a Merchant Account
Sign up at dashboard.dodo.co.tz or contact sales@dodo.co.tz
Get Your API Key
After approval, find your API key in Dashboard → Settings → API Keys
Make Your First Request
curl -X POST https://api.dodo.co.tz/api/v1/orders \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customer_name": "John Doe",
"customer_phone": "+255712345678",
"pickup_address": "Your Store, Samora Ave, Dar es Salaam",
"dropoff_address": "Customer Address, Mikocheni",
"package_description": "1x Food order"
}'
Authentication
Include your API key in the Authorization header:
Authorization: Bearer dodo_live_abc123...
| Environment | Key Prefix |
|---|---|
| Production | dodo_live_ |
| Sandbox | dodo_test_ |
⚠️ Security: Keep your API key secret. Never expose it in client-side code.
Pricing
/orders/pricing
Get delivery estimate before creating an order.
Request
{
"pickup_latitude": -6.7924,
"pickup_longitude": 39.2083,
"dropoff_latitude": -6.7800,
"dropoff_longitude": 39.2200
}
Response
{
"distance_km": 5.2,
"delivery_fee": 2600,
"currency": "TZS",
"estimated_time_minutes": 25
}
Pricing Structure
| Per kilometer | 500 TZS |
| Minimum charge | 500 TZS |
| Maximum distance | 50 km |
Orders
/orders
Create a new delivery order.
Request
{
"customer_name": "Jane Doe",
"customer_phone": "+255765432109",
"customer_email": "jane@email.com",
"pickup_address": "Malaika Restaurant, Samora Ave",
"pickup_latitude": -6.7924,
"pickup_longitude": 39.2083,
"pickup_instructions": "Ask for manager",
"dropoff_address": "Apartment 4B, Ocean View, Msasani",
"dropoff_latitude": -6.7600,
"dropoff_longitude": 39.2400,
"dropoff_instructions": "Call on arrival",
"package_description": "Food order - 2 containers",
"package_size": "medium"
}
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
customer_name |
string | Yes | Recipient's name |
customer_phone |
string | Yes | Recipient's phone (+255...) |
pickup_address |
string | Yes | Pickup location |
dropoff_address |
string | Yes | Delivery location |
package_description |
string | Yes | What's being delivered |
pickup_latitude |
number | No | GPS coordinate |
pickup_longitude |
number | No | GPS coordinate |
package_size |
string | No | small, medium, large |
metadata |
object | No | Your custom data |
Response
{
"id": "ord_abc123",
"order_number": "ORD-XK7M2P",
"status": "pending",
"customer_name": "Jane Doe",
"distance_km": 8.5,
"delivery_fee": 4250,
"currency": "TZS",
"estimated_time_minutes": 35,
"created_at": "2024-01-15T12:00:00Z",
"tracking_url": "https://track.dodo.co.tz/ORD-XK7M2P"
}
💡 Tip: Providing GPS coordinates improves accuracy and speeds up delivery.
/orders
List all your orders with pagination and filters.
Query Parameters
page |
Page number (default: 1) |
size |
Results per page (default: 20, max: 100) |
status |
Filter by status |
date_from |
From date (YYYY-MM-DD) |
date_to |
To date (YYYY-MM-DD) |
Example
GET /orders?status=delivered&date_from=2024-01-01&size=50
/orders/{order_id}
Get detailed information about an order including rider location.
Response
{
"id": "ord_abc123",
"order_number": "ORD-XK7M2P",
"status": "in_transit",
"rider": {
"name": "James M.",
"phone": "+255787654321",
"location": {
"latitude": -6.7700,
"longitude": 39.2300,
"updated_at": "2024-01-15T12:35:00Z"
}
},
"estimated_delivery": "2024-01-15T12:45:00Z",
"tracking_url": "https://track.dodo.co.tz/ORD-XK7M2P"
}
/orders/{order_id}/cancel
Cancel an order before it's picked up.
Request
{
"reason": "Customer requested cancellation"
}
Response
{
"id": "ord_abc123",
"status": "cancelled",
"refund_status": "processing"
}
⚠️ Orders can only be cancelled before pickup. Once picked up, contact support.
Order Status
| Status | Description |
|---|---|
pending |
Order created, awaiting payment |
confirmed |
Payment confirmed, finding rider |
assigned |
Rider assigned, heading to pickup |
picked_up |
Package collected |
in_transit |
On the way to customer |
delivered |
Successfully delivered ✓ |
cancelled |
Order cancelled |
failed |
Delivery failed |
Status Flow
Webhooks
Get real-time updates when order status changes.
Setup
- Go to Dashboard → Settings → Webhooks
- Add your endpoint URL (must be HTTPS)
- Select events to receive
- Save your webhook secret
Events
order.confirmed |
Payment confirmed |
order.assigned |
Rider assigned |
order.picked_up |
Package picked up |
order.in_transit |
On the way |
order.delivered |
Delivered |
order.cancelled |
Cancelled |
order.failed |
Failed |
Webhook Payload
{
"event": "order.delivered",
"timestamp": "2024-01-15T12:45:00Z",
"data": {
"id": "ord_abc123",
"order_number": "ORD-XK7M2P",
"status": "delivered",
"customer_name": "Jane Doe",
"delivery_fee": 4250,
"rider": {
"name": "James M.",
"phone": "+255787654321"
},
"delivered_at": "2024-01-15T12:45:00Z",
"metadata": {
"your_order_id": "12345"
}
}
}
Verify Signature
Always verify webhook signatures to ensure authenticity.
Signature header:
X-Dodo-Signature: sha256=a1b2c3d4...
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return `sha256=${expected}` === signature;
}
app.post('/webhooks/dodo', (req, res) => {
const signature = req.headers['x-dodo-signature'];
if (!verifyWebhook(JSON.stringify(req.body), signature, SECRET)) {
return res.status(401).send('Invalid');
}
// Process webhook...
res.status(200).send('OK');
});
Errors
Error Response Format
{
"error": {
"code": "invalid_request",
"message": "Customer phone number is required"
}
}
| Code | HTTP | Description |
|---|---|---|
invalid_request |
400 | Missing or invalid parameters |
unauthorized |
401 | Invalid or missing API key |
forbidden |
403 | Account not approved |
not_found |
404 | Order not found |
rate_limit_exceeded |
429 | Too many requests |
internal_error |
500 | Server error |
Code Examples
const axios = require('axios');
const dodo = axios.create({
baseURL: 'https://api.dodo.co.tz/api/v1',
headers: {
'Authorization': `Bearer ${process.env.DODO_API_KEY}`,
'Content-Type': 'application/json'
}
});
async function createDelivery(orderData) {
const response = await dodo.post('/orders', {
customer_name: orderData.customerName,
customer_phone: orderData.customerPhone,
pickup_address: 'Your Store Address',
dropoff_address: orderData.deliveryAddress,
package_description: `Order #${orderData.orderId}`
});
return response.data;
}
// Usage
const delivery = await createDelivery({
customerName: 'John Doe',
customerPhone: '+255712345678',
deliveryAddress: '123 Street, Dar es Salaam',
orderId: '12345'
});
console.log(`Tracking: ${delivery.tracking_url}`);
Testing
Sandbox Environment
Use sandbox for testing without real payments:
Base URL: https://sandbox.dodo.co.tz/api/v1
API Key: dodo_test_...
Test Phone Numbers
+255700000001 |
Payment succeeds |
+255700000002 |
Payment fails |
+255700000003 |
Payment pending (timeout) |
💡 Auto-progression: In sandbox, orders automatically progress through statuses for testing.