Quickstart
This walkthrough takes you from zero to a working integration in about 30 minutes. By the end you'll have:
- An approved merchant account
- A funded wallet
- An API key
- A successful test shipment in sandbox
1. Create an account
Go to merchant.dodo.co.tz/register and fill in:
- Business name — appears on receipts and rider apps
- Business email — receives approval, billing, and webhook notifications
- Phone — E.164 format:
+255712345678 - Password — minimum 8 characters, mixed case + a digit
You'll receive a verification email. Click the link to confirm your email address.
2. Wait for approval
Every merchant is reviewed manually before going live. Approval usually takes less than one business day.
During this window:
- You can log in to the dashboard
- You cannot generate API keys or fund a wallet yet
- We may call the phone number you registered with to verify
You'll get a Your Dodo account is approved email when you can proceed.
3. Generate your sandbox API key
Always integrate against sandbox first. In the dashboard:
- Toggle the environment switch (top-right) to Sandbox.
- Go to Settings → API Keys.
- Click Generate API Key.
- Copy the key (
dk_test_…) — it's shown only once. Store it somewhere safe.
Store it as an environment variable on your server:
export DODO_API_KEY="dk_test_a1B2c3D4e5F6g7H8i9J0kLmNoPqRsTuVw"
Get a production key the same way after switching the dashboard environment toggle to Live. Production keys start with dk_live_.
4. Fund your wallet
Every shipment is paid for by debiting your Dodo wallet — no per-order checkout, no payment URLs, no customer-facing redirects.
In the dashboard:
- Go to Wallet → Top Up.
- Enter an amount in TZS. Minimum top-up in sandbox: 5,000 TZS.
- Choose a method (M-Pesa, Tigo Pesa, Airtel Money, or card).
- Complete the Selcom checkout. In sandbox, use the test credentials.
Your wallet balance updates within seconds. Keep an eye on Wallet → Low-balance alerts to avoid declined shipments — when the balance falls below your threshold, we email you.
5. Get a price quote
Now your backend can talk to Dodo. Start with a rate quote — this is a read-only check, no charges or DB writes.
curl -X POST https://sandbox.dodo.co.tz/api/v1/merchant/shipments/rates \
-H "X-API-Key: $DODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"pickup_latitude": -6.7924,
"pickup_longitude": 39.2083,
"dropoff_latitude": -6.7600,
"dropoff_longitude": 39.2400,
"items": [
{ "description": "Food order", "weight_kg": 2.5, "quantity": 1 }
]
}'
You'll get back the detected shipment type, the vehicle Dodo will use, and itemized pricing:
{
"shipment_type": "same_city",
"vehicle_id": 1,
"vehicle_name": "Motorcycle",
"first_mile_fee": 3727,
"transit_fee": 0,
"last_mile_fee": 0,
"handling_fee": 0,
"service_fee": 373,
"fragile_surcharge": 0,
"total_amount": 4100,
"distance_km": 5.2,
"estimated_delivery_hours": 1
}
total_amount (TZS 4,100 here) is what will be debited from your wallet when you create the shipment.
See Rate Quotes for the full reference.
6. Create your first shipment
When you're ready to dispatch:
curl -X POST https://sandbox.dodo.co.tz/api/v1/merchant/shipments \
-H "X-API-Key: $DODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sender_name": "My Restaurant",
"sender_phone": "+255712345678",
"pickup_address": "Malaika Restaurant, Samora Ave, Dar es Salaam",
"pickup_latitude": -6.7924,
"pickup_longitude": 39.2083,
"receiver_name": "Jane Doe",
"receiver_phone": "+255765432109",
"dropoff_address": "Apartment 4B, Msasani, Dar es Salaam",
"dropoff_latitude": -6.7600,
"dropoff_longitude": 39.2400,
"items": [
{ "description": "Food order — 2 containers", "weight_kg": 2.5, "quantity": 1 }
],
"payment_source": "wallet",
"merchant_reference": "your-internal-order-id-123"
}'
payment_source: "wallet" is required. Without it the API will try to issue a Selcom checkout URL — not what you want for S2S.
The response confirms creation and payment in one shot:
{
"id": 12345,
"shipment_number": "SHP-20260515-A1B2C3",
"tracking_code": "DODO7K9M2P4QR8",
"status": "confirmed",
"total_amount": 4100,
"payment_status": "paid",
"merchant_reference": "your-internal-order-id-123",
"created_at": "2026-05-15T10:30:00Z",
...
}
Store id (integer) so you can match webhook events to your records. Share tracking_code with your customer.
See Create a Shipment for the full reference.
7. Subscribe to webhooks
While shipments can be polled with GET /merchant/shipments/{id}, webhooks are how you get real-time updates.
In the dashboard:
- Go to Settings → Webhooks → Add endpoint.
- Enter your URL:
https://yourapp.com/webhooks/dodo. - Select events you want — at minimum:
shipment.created,shipment.paid,delivery.picked_up,delivery.delivered,delivery.failed,shipment.cancelled. - Click Save. Copy the signing secret — you'll use it to verify incoming requests.
Your endpoint must:
- Accept
POSTwith JSON body - Verify
X-Dodo-Signature(HMAC-SHA256 of"{timestamp}.{raw_body}") - Return a
2xxwithin 30 seconds
See Webhooks for the full payload reference and verification code in Node.js, Python, and PHP.
8. Testing in sandbox
Sandbox behaves exactly like production except:
- No real money moves — wallet top-ups use Selcom sandbox credentials
- A simulated rider auto-progresses your shipment through every status within ~2 minutes
- You can fast-forward the lifecycle from the dashboard (Shipments → Detail → Simulate)
Selcom sandbox test credentials:
| Method | Phone / Card | Result |
|---|---|---|
| M-Pesa | +255652000001 | Success |
| M-Pesa | +255652000099 | Failure |
| Card | 5555 5555 5555 4444, any future expiry, CVV 123 | Success |
| Card | 4000 0000 0000 0002 | Failure |
When you're ready to go live, regenerate keys in the Live environment and switch your base URL to https://api.dodo.co.tz/api/v1. Everything else is identical.
Next steps
- Rate Quotes — pricing details and item dimensions
- Create a Shipment — every field, every option
- Webhooks — events, signature verification, retries
- Shipment Lifecycle — what each status means
- Code Examples — full Node, Python, and PHP integrations