Dropzon Public API
Integrate DZ Address lookup, Dropzon location search, package tracking, “Sign in with Dropzon” OAuth, and more into your applications. Built for e-commerce platforms, logistics companies, and developers.
Quick Start
Get your API key
Sign in to the Developer Console and create an API key. Any Dropzon account can access the console.
Authenticate your requests
Include your API key in every request header.
curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://api.dropzon.app/api/v1/addresses/lookup?code=DZ%23001-01"Start building
Explore the endpoints below. All responses are JSON. Rate limit: 100 requests/minute per key.
Authentication
All API requests require authentication using a Bearer token in the Authorization header.
Authorization: Bearer YOUR_API_KEYRate Limit
100 requests / minute
Per API key. Burst-friendly with sliding window.
Response Format
JSON (application/json)
All responses include a success boolean.
Error Handling
Errors follow a consistent format with HTTP status codes and descriptive messages.
{
"success": false,
"error": "Invalid or expired API key"
}| Status | Code | Description |
|---|---|---|
| 400 | BAD_REQUEST | Missing or invalid parameters |
| 401 | AUTH_INVALID | Invalid or missing API key |
| 403 | FORBIDDEN | Insufficient permissions for this resource |
| 404 | NOT_FOUND | Resource not found |
| 429 | RATE_LIMITED | Too many requests - slow down |
| 500 | INTERNAL_ERROR | Something went wrong on our end |
Addresses
Look up, search, and validate DZ Addresses. Search by DZ# code, full DZ code, or full address string. You can pass the query as a query parameter (?q=...) or directly in the URL path (/search/DZ%23001-01). Ideal for e-commerce checkout integrations.
Dropzons
Search for Dropzon locations, look up full address details for e-commerce checkout, and find the nearest pickup points.
Tracking
Track packages by tracking number and get real-time status updates for shipments.
Locations
Access the Dropzon location hierarchy - countries, regions, cities, and municipalities where Dropzon operates.
Shipping Rates
Get shipping rate estimates between DZ addresses. Calculate costs for in-city, domestic, cross-border, and international shipments.
Dispatch
Create and manage package dispatches via the API. Send single parcels or bulk dispatch up to 50 at once. Requires the "dispatch" scope on your API key.
Webhooks
Register webhook URLs to receive real-time notifications when package statuses change or deliveries are completed.
Sign in with Dropzon
OAuth 2.0 Authorization Code with PKCE
Overview
“Sign in with Dropzon” lets your users authenticate using their Dropzon account. After authorization, your app receives an access token that can be used to read the user's profile, email, phone, and DZ Address information - depending on the scopes you request.
The implementation follows the OAuth 2.0 Authorization Code flow with optional PKCE (Proof Key for Code Exchange) for public clients like mobile apps and SPAs.
Setup
Create an OAuth app
Go to the Developer Console → OAuth Apps and create a new OAuth application.
Save your credentials
Copy your client_id (starts with dzo_) and client_secret (starts with dzs_). The secret is only shown once at creation time.
Register redirect URIs
Add one or more callback URLs where users will be redirected after authorization. Must be HTTPS in production (http://localhost is allowed for development).
Available Scopes
Request only the scopes your application needs. Users will see a list of requested permissions on the consent screen.
| Scope | Description |
|---|---|
profile | Name and avatar |
email | Email address |
phone | Phone number |
address | Primary DZ Address |
addresses | All DZ Addresses |
identity | Verified identity info |
kyc_status | Verification status |
Authorization Flow
Redirect user to Dropzon
Redirect the user's browser to the Dropzon authorization page with your app's parameters.
GET https://dropzon.app/oauth/authorize?
client_id=dzo_your_client_id
&redirect_uri=https://yourapp.com/callback
&response_type=code
&scope=profile email
&state=random_csrf_token
&code_challenge=S256_hash_of_verifier # optional, for PKCE
&code_challenge_method=S256 # optional, for PKCE| Parameter | Description |
|---|---|
client_id | Your OAuth app client ID (dzo_ prefix) |
redirect_uri | Must match a registered redirect URI |
response_type | Must be "code" |
scope | Space-separated scopes (defaults to "profile") |
state | Random string to prevent CSRF attacks |
code_challenge | PKCE challenge (S256 hash of code_verifier) |
code_challenge_method | Must be "S256" when using PKCE |
User signs in and approves
The user sees a consent screen showing your app name, description, and the permissions you're requesting. If they're not logged in, they'll sign in first. After approving, Dropzon redirects them back to your redirect_uri with an authorization code.
Handle the callback
Dropzon redirects the user back to your app with a code and state parameter. Verify the state matches what you sent.
https://yourapp.com/callback?code=AUTH_CODE_HERE&state=random_csrf_tokenIf the user denies access, you'll receive ?error=access_denied&state=... instead. Always check for the error parameter first.
Exchange code for tokens
Make a server-side POST request to exchange the authorization code for an access token and refresh token. This request must include your client_secret - never expose it in frontend code.
curl -X POST https://api.dropzon.app/api/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "authorization_code",
"code": "AUTH_CODE_HERE",
"redirect_uri": "https://yourapp.com/callback",
"client_id": "dzo_your_client_id",
"client_secret": "dzs_your_client_secret",
"code_verifier": "original_pkce_verifier" // only if using PKCE
}'Response
{
"success": true,
"data": {
"access_token": "dza_...",
"refresh_token": "dzr_...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "profile email"
}
}Get user info
Use the access token to fetch the authenticated user's profile. The fields returned depend on the scopes granted by the user.
curl https://api.dropzon.app/api/oauth/userinfo \
-H "Authorization: Bearer dza_your_access_token"Fields returned per scope
alwayssub - the user's unique Dropzon IDprofilename, avatar_url, roleemailemail, email_verifiedphonephone, phone_verifiedaddressaddress - the user's primary DZ Address with full Dropzon location detailsaddressesaddresses - array of all the user's DZ Addressesidentityidentity - date of birth, gender, nationality, ID document type/countrykyc_statuskyc - verification level, status, verified_at, email/phone verifiedExample response (profile + email scopes only)
{
"success": true,
"data": {
"sub": "c25afbbd-73d8-465b-91e6-4567c0f2aed2",
"name": "Musa Lawi",
"avatar_url": null,
"role": "customer",
"email": "musa@example.com",
"email_verified": true
}
}Full response (all scopes granted)
{
"success": true,
"data": {
"sub": "c25afbbd-73d8-465b-91e6-4567c0f2aed2",
"name": "Musa Lawi",
"avatar_url": null,
"role": "customer",
"email": "musa@example.com",
"email_verified": true,
"phone": "+255621451238",
"phone_verified": false,
"address": {
"dz_code": "DZ#001-01",
"dz_full_code": "DZ-001-001-001-003-001",
"label": "Crib",
"status": "active",
"is_primary": true,
"dropzon": {
"id": "a1b2c3d4-...",
"name": "Sinza C Dropzon Hub",
"short_code": "DZ#001",
"full_code": "DZ-001-001-001-003-001",
"type": "branch",
"status": "active",
"house_no": "21",
"street": "Manet St",
"street_alias": "Gandabahari Rd",
"ward": "Sinza C",
"district": "Ubungo",
"state": "Dar es Salaam",
"zip_code": "16102",
"gps": {
"lat": -6.781463,
"lng": 39.221159
},
"plus_code": "669C+CF7 Dar es Salaam",
"municipality": "Ubungo",
"city": "Dar es Salaam",
"region": "Dar es Salaam",
"country": "Tanzania",
"country_code": "TZ",
"phone_prefix": "+255"
},
"formatted": {
"full": "21 Manet St, DZ#001-01, Dar es Salaam, 16102 TZ",
"line1": "21 Manet St",
"line2": "DZ#001-01",
"city": "Dar es Salaam",
"region": "Dar es Salaam",
"municipality": "Ubungo",
"country": "Tanzania",
"country_code": "TZ",
"zip_code": "16102",
"postal_code": "16102"
}
},
"addresses": [
"...array of address objects (same structure as above)"
],
"identity": {
"date_of_birth": "1995-08-22",
"gender": "male",
"nationality": "TZ",
"id_document_type": "national_id",
"id_document_country": "TZ"
},
"kyc": {
"level": "standard",
"status": "verified",
"verified_at": "2025-03-15T10:30:00.000Z",
"email_verified": true,
"phone_verified": true
}
}
}Token Management
Token Lifetimes
Access Token
1 hour
Prefix: dza_
Refresh Token
30 days
Prefix: dzr_
Refreshing Tokens
When the access token expires, use the refresh token to get a new pair. The old refresh token is rotated (invalidated) and a new one is returned.
curl -X POST https://api.dropzon.app/api/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "refresh_token",
"refresh_token": "dzr_your_refresh_token",
"client_id": "dzo_your_client_id",
"client_secret": "dzs_your_client_secret"
}'Revoking Tokens
Revoke an access or refresh token when the user logs out of your app. Per RFC 7009, this endpoint always returns 200.
curl -X POST https://api.dropzon.app/api/oauth/revoke \
-H "Content-Type: application/json" \
-d '{
"token": "dza_or_dzr_token_here",
"client_id": "dzo_your_client_id",
"client_secret": "dzs_your_client_secret"
}'Complete Example- Node.js / Express
const express = require('express');
const crypto = require('crypto');
const app = express();
const CLIENT_ID = 'dzo_your_client_id';
const CLIENT_SECRET = 'dzs_your_client_secret';
const REDIRECT_URI = 'https://yourapp.com/callback';
const OAUTH_AUTHORIZE = 'https://dropzon.app/oauth'; // consent screen (frontend)
const OAUTH_API = 'https://api.dropzon.app/api/oauth'; // token + userinfo (API)
// Step 1: Redirect to Dropzon
app.get('/login', (req, res) => {
const state = crypto.randomBytes(16).toString('hex');
req.session.oauthState = state;
const params = new URLSearchParams({
client_id: CLIENT_ID,
redirect_uri: REDIRECT_URI,
response_type: 'code',
scope: 'profile email',
state,
});
res.redirect(OAUTH_AUTHORIZE + '/authorize?' + params);
});
// Step 3-4: Handle callback and exchange code
app.get('/callback', async (req, res) => {
const { code, state, error } = req.query;
if (error) return res.send('Authorization denied');
if (state !== req.session.oauthState) return res.status(403).send('Invalid state');
// Exchange code for tokens
const tokenRes = await fetch(OAUTH_API + '/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
grant_type: 'authorization_code',
code,
redirect_uri: REDIRECT_URI,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
}),
});
const { data: tokens } = await tokenRes.json();
// Step 5: Fetch user info
const userRes = await fetch(OAUTH_API + '/userinfo', {
headers: { Authorization: 'Bearer ' + tokens.access_token },
});
const { data: user } = await userRes.json();
// user = { sub, name, email, ... }
req.session.user = user;
res.redirect('/dashboard');
});Endpoint Reference
| Method | Endpoint | Auth |
|---|---|---|
| GET | /oauth/authorize | None |
| POST | /oauth/token | None |
| GET | /oauth/userinfo | Bearer (access token) |
| POST | /oauth/revoke | None |
Security Best Practices
Always validate the state parameter
Compare the state in the callback with the one you sent to prevent CSRF attacks.
Keep client_secret server-side
Never expose your client secret in frontend JavaScript, mobile app binaries, or version control.
Use PKCE for public clients
Single-page apps and mobile apps should use PKCE (code_challenge + code_verifier) since they cannot securely store a client_secret.
Use HTTPS redirect URIs
Always use HTTPS for redirect URIs in production. HTTP is only allowed for localhost during development.
Request minimal scopes
Only request the scopes your application actually needs. Users are more likely to approve fewer permissions.
Handle token expiry gracefully
Use the refresh token flow to renew access tokens. If the refresh token is also expired, redirect the user to re-authorize.
KYC-Lite Identity Verification
Dropzon verifies user identities as part of its address infrastructure. With the KYC-lite scopes, your app can check a user's verification level or request verified identity data - without building your own KYC pipeline.
How it works
User signs up on Dropzon
When a user creates a Dropzon account and registers a DZ Address, they go through identity verification steps.
Verification levels increase
Email + phone verification gives "basic" level. Government ID submission upgrades to "standard". ID + selfie match reaches "enhanced".
Your app requests KYC scopes
Add identity and/or kyc_status to your OAuth scope parameter. The user sees what you are requesting on the consent screen.
Read verification data
After authorization, call GET /oauth/userinfo. The response includes the user's verification level and/or identity details based on granted scopes.
Verification Levels
| Level | Requirements |
|---|---|
none | Account created, no verification |
basic | Email and phone verified |
standard | Government-issued ID submitted and approved |
enhanced | Government ID + selfie verification match |
KYC-Lite Scopes
kyc_statusRecommendedReturns the user's verification level and status without revealing any personal data. Ideal for age gates, trust tiers, and risk decisions.
// GET /oauth/userinfo (with kyc_status scope)
{
"sub": "usr_abc123",
"kyc": {
"level": "standard", // none | basic | standard | enhanced
"status": "verified", // none | pending | verified | rejected | expired
"verified_at": "2025-03-15T10:30:00Z",
"email_verified": true,
"phone_verified": true
}
}identitySensitiveReturns verified personal identity information. Users will see a clear warning on the consent screen. Only request this scope if your application legally requires identity data (e.g. financial services).
// GET /oauth/userinfo (with identity scope)
{
"sub": "usr_abc123",
"identity": {
"date_of_birth": "1995-08-22",
"gender": "male",
"nationality": "TZ",
"id_document_type": "national_id", // national_id | passport | driving_license
"id_document_country": "TZ"
}
}Common Use Cases
Age verification
Check if a user is 18+ using kyc_status level without seeing their date of birth.
scope: kyc_statusTrust tiers
Offer higher transaction limits to users with "standard" or "enhanced" verification.
scope: kyc_statusFinancial compliance
Retrieve government ID details for users of financial or insurance products.
scope: identityDelivery confirmation
Combine address + kyc_status scopes to verify both the delivery address and recipient identity.
scope: identity + addressExample: Request KYC Status
GET https://api.dropzon.app/api/oauth/authorize?
client_id=dzo_your_client_id
&redirect_uri=https://yourapp.com/callback
&response_type=code
&scope=profile kyc_status
&state=random_csrf_tokenThe user will see a consent screen asking to share their name, avatar, and identity verification status. No personal identity documents or dates are shared with the kyc_status scope.
Sign in with Dropzon - Button
Add a branded “Sign in with Dropzon” button to your app. Pick a variant and size below, then copy the code.
Variant
Size
<a href="https://dropzon.app/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&response_type=code&scope=profile email&state=RANDOM_STATE"
style="display:inline-flex;align-items:center;gap:10px;height:44px;padding:0 20px;background:#0d84d4;color:#fff;border:none;border-radius:12px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;font-size:14px;font-weight:600;text-decoration:none;cursor:pointer;transition:opacity 0.15s;"
onmouseover="this.style.opacity='0.9'" onmouseout="this.style.opacity='1'">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7z" fill="#ffffff"/><circle cx="12" cy="9" r="2.5" fill="white"/></svg>
Sign in with Dropzon
</a>Branding Guidelines
Use the provided Dropzon pin icon next to the text
Use "Sign in with Dropzon" as the button label
Match one of the four official variants above
Do not stretch, skew, or modify the pin icon
Do not change the button text to other wording
Do not use a font size smaller than 14px
Checkout Widget
Embeddable DZ Address autofill for e-commerce checkout pages
Overview
The Checkout Widget is a drop-in JavaScript component that adds a DZ# code input to your checkout page. When a customer enters their DZ# code (e.g. DZ#001-01), the widget validates it in real time and auto-fills their full address into your existing form fields.
No framework dependencies required - works with any website, from plain HTML to React, Vue, or Shopify. The widget handles input formatting, debounced API calls, error states, and a polished verified-address display.
Quick Start
Add two lines to your checkout page and the widget handles the rest.
<!-- 1. Add a target div where the widget should appear -->
<div id="dz-address"></div>
<!-- 2. Include the widget script with your API key -->
<script
src="https://api.dropzon.app/widget/dz-address.js"
data-api-key="YOUR_API_KEY"
data-target="#dz-address"
></script>The widget automatically initializes when the page loads. It reads configuration fromdata-*attributes on the script tag.
Auto-Fill Checkout Fields
Point the widget at your existing form fields using data-autofill-* attributes. When a DZ# code resolves successfully, the widget writes the address components directly into your form inputs.
<div id="dz-address"></div>
<!-- Your existing checkout form -->
<input id="address-line1" name="line1" placeholder="Address line 1" />
<input id="address-line2" name="line2" placeholder="Address line 2" />
<input id="address-city" name="city" placeholder="City" />
<input id="address-zip" name="zip" placeholder="ZIP / Postal code" />
<input id="address-country" name="country" placeholder="Country" />
<script
src="https://api.dropzon.app/widget/dz-address.js"
data-api-key="YOUR_API_KEY"
data-target="#dz-address"
data-autofill-line1="#address-line1"
data-autofill-line2="#address-line2"
data-autofill-city="#address-city"
data-autofill-zip="#address-zip"
data-autofill-country="#address-country"
></script>Programmatic Usage
For full control, initialize the widget via JavaScript instead of data attributes. This gives you access to event callbacks and programmatic methods.
<script src="https://api.dropzon.app/widget/dz-address.js"></script>
<script>
const widget = Dropzon.init({
apiKey: 'YOUR_API_KEY',
target: '#dz-address',
label: 'Delivery Address',
placeholder: 'Enter your DZ# code',
// Auto-fill form fields (CSS selectors)
autofill: {
line1: '#checkout-line1',
line2: '#checkout-line2',
city: '#checkout-city',
zip: '#checkout-zip',
country: '#checkout-country',
},
// Callbacks
onResolve: function(address) {
console.log('Address resolved:', address);
// address.formatted.full - "21 Manet St, DZ#001-01, Dar es Salaam, 16102, TZ"
// address.formatted.line1 - "21 Manet St"
// address.formatted.line2 - "DZ#001-01"
// address.formatted.city - "Dar es Salaam"
// address.formatted.zip_code - "16102"
// address.formatted.country - "Tanzania"
// address.dropzon.name - "Goba Road Dropzon"
},
onError: function(err) {
console.log('Lookup failed:', err.error);
},
onClear: function() {
console.log('Address cleared');
},
});
// Programmatic methods
widget.getCode(); // Returns current input value
widget.getValue(); // Returns resolved address or null
widget.setCode('DZ#001-01'); // Set a code and trigger lookup
widget.clear(); // Clear everything
widget.destroy(); // Remove widget from DOM
</script>Headless API (No Widget UI)
If you only need the DZ# lookup logic without the widget UI, use the static helper methods directly. These return Promises and work well with async/await.
<script src="https://api.dropzon.app/widget/dz-address.js"></script>
<script>
// Look up a DZ# code - returns full address data
Dropzon.lookup('YOUR_API_KEY', 'DZ#001-01')
.then(function(address) {
console.log(address.formatted.full);
})
.catch(function(err) {
console.error(err);
});
// Validate a DZ# code - check if it exists and is active
Dropzon.validate('YOUR_API_KEY', 'DZ#001-01')
.then(function(result) {
console.log('Valid:', result.valid);
});
</script>Configuration Reference
Script Tag Attributes
| Attribute | Description |
|---|---|
data-api-key | Your Dropzon API key (dzk_...) |
data-target | CSS selector for the container element. Default: #dz-address |
data-label | Label text above the input. Default: "DZ Address" |
data-placeholder | Input placeholder text. Default: "Enter DZ# code (e.g. DZ#001-01)" |
data-show-result | Show resolved address card below input. Default: true |
data-show-powered | Show "Powered by Dropzon" badge. Default: true |
data-autofill-line1 | CSS selector for the address line 1 input |
data-autofill-line2 | CSS selector for the address line 2 input |
data-autofill-city | CSS selector for the city input |
data-autofill-state | CSS selector for the state/region input |
data-autofill-zip | CSS selector for the ZIP/postal code input |
data-autofill-country | CSS selector for the country input |
data-autofill-country-code | CSS selector for the country code input (ISO 2-letter) |
JavaScript Init Options
| Option | Description |
|---|---|
apiKey | Your Dropzon API key (required) |
target | CSS selector or DOM element for the container (required) |
label | Label text, or false to hide. Default: "DZ Address" |
placeholder | Input placeholder text |
showResult | Show verified address card. Default: true |
showPowered | Show "Powered by Dropzon". Default: true |
autofill | Map of field names to CSS selectors: { line1, line2, city, state, zip, country, country_code } |
onResolve | Called when a DZ# code resolves to an address. Receives address data object. |
onError | Called when a lookup fails. Receives { code, error }. |
onClear | Called when the input is cleared. |
onChange | Called on every input change. Receives the current value. |
Resolved Address Shape
The onResolve callback (and Dropzon.lookup()) return the same address object as the GET /api/v1/addresses/lookup endpoint. See the Addresses section above for the full response shape.
{
"dz_code": "DZ#001-01",
"dz_full_code": "DZ-001-001-001-001-001",
"label": "Home",
"status": "active",
"formatted": {
"full": "21 Manet St, DZ#001-01, Dar es Salaam, 16102, TZ",
"line1": "21 Manet St",
"line2": "DZ#001-01",
"city": "Dar es Salaam",
"region": "Dar es Salaam",
"zip_code": "16102",
"postal_code": "16102",
"country": "Tanzania",
"country_code": "TZ"
},
"dropzon": {
"name": "Goba Road Dropzon",
"dz_code": "DZ#001",
"city": "Dar es Salaam"
}
}How It Works
Customer enters DZ# code
The widget shows a styled input field. As the customer types their DZ# code (e.g. DZ#001-01), the widget validates the format in real time.
Widget calls Dropzon API
Once a valid DZ# code format is detected (including the slot number after the dash), the widget makes a debounced API call to /api/v1/addresses/lookup using your API key.
Address resolves
The API returns the full address - street, city, ZIP code, country, and the associated Dropzon location. The widget displays a verified address card with all details.
Form fields auto-fill
If you configured autofill selectors, the widget writes each address component into your existing form inputs and triggers input/change events so your framework picks up the values.
Verification Badge
Embeddable trust seal showing a DZ address is verified
Overview
The Verification Badge is a lightweight embeddable seal that confirms a DZ address is valid and active on the Dropzon network. Display it on order confirmations, receipts, invoices, or anywhere you want to show the customer that their delivery address has been verified.
The badge loads, calls the Dropzon validate API in the background, and renders a Verified, Not Verified, or Verifying... state with smooth animations. It supports 4 themes, 3 sizes, and hover tooltips.
Quick Start
Add two lines to your order confirmation or receipt page.
<!-- 1. Add a target div where the badge should appear -->
<div id="dz-badge"></div>
<!-- 2. Include the badge script with your API key and the DZ# code -->
<script
src="https://api.dropzon.app/widget/dz-badge.js"
data-api-key="YOUR_API_KEY"
data-code="DZ#001-01"
data-target="#dz-badge"
></script>The badge automatically verifies the code on load and displays the result. It transitions from a “Verifying...” spinner to either “DZ Verified” (green) or “Not Verified” (red).
Themes and Sizes
The badge ships with 4 built-in themes and 3 sizes. Set them viadata-theme anddata-size attributes.
<!-- Light theme (default), medium size -->
<script src="https://api.dropzon.app/widget/dz-badge.js"
data-api-key="YOUR_API_KEY" data-code="DZ#001-01"
data-target="#badge-1" data-theme="light" data-size="md">
</script>
<!-- Dark theme, large size -->
<script src="https://api.dropzon.app/widget/dz-badge.js"
data-api-key="YOUR_API_KEY" data-code="DZ#001-01"
data-target="#badge-2" data-theme="dark" data-size="lg">
</script>
<!-- Minimal theme (transparent bg), small size -->
<script src="https://api.dropzon.app/widget/dz-badge.js"
data-api-key="YOUR_API_KEY" data-code="DZ#001-01"
data-target="#badge-3" data-theme="minimal" data-size="sm">
</script>
<!-- Brand theme (Dropzon blue) -->
<script src="https://api.dropzon.app/widget/dz-badge.js"
data-api-key="YOUR_API_KEY" data-code="DZ#001-01"
data-target="#badge-4" data-theme="brand">
</script>Light
Green verified on white - works on light backgrounds
data-theme="light"Dark
Green verified on dark green - works on dark backgrounds
data-theme="dark"Minimal
Transparent background with subtle border
data-theme="minimal"Brand
Dropzon blue verified - matches the Dropzon brand
data-theme="brand"Programmatic Usage
For full control, initialize the badge via JavaScript. This gives you access to event callbacks and methods like reverify().
<script src="https://api.dropzon.app/widget/dz-badge.js"></script>
<script>
const badge = DropzonBadge.init({
apiKey: 'YOUR_API_KEY',
code: 'DZ#001-01',
target: '#dz-badge',
size: 'md', // 'sm' | 'md' | 'lg'
theme: 'light', // 'light' | 'dark' | 'minimal' | 'brand'
showCode: true, // Show the DZ# code next to "Verified"
showTooltip: true, // Hover tooltip with status message
animated: true, // Pop-in animation on verify
onVerified: function(data) {
console.log('Address verified:', data);
// data.valid = true
// data.active = true
// data.code = 'DZ#001-01'
},
onFailed: function(err) {
console.log('Verification failed:', err);
},
});
// Methods
badge.getStatus(); // 'pending' | 'verified' | 'unverified'
badge.reverify(); // Re-check (e.g. after code change)
badge.destroy(); // Remove badge from DOM
</script>Headless Verify (No Badge UI)
If you only need the verification check without any UI, use the staticDropzonBadge.verify()method. It returns a Promise.
<script src="https://api.dropzon.app/widget/dz-badge.js"></script>
<script>
DropzonBadge.verify('YOUR_API_KEY', 'DZ#001-01')
.then(function(result) {
if (result.valid && result.active) {
console.log('Address is verified and active');
} else {
console.log('Address not verified');
}
})
.catch(function(err) {
console.error('Verification error:', err);
});
</script>Configuration Reference
Script Tag Attributes
| Attribute | Description |
|---|---|
data-api-key | Your Dropzon API key (dzk_...) |
data-code | The DZ# code to verify (e.g. DZ#001-01) |
data-target | CSS selector for the container element. Default: #dz-badge |
data-size | Badge size: "sm", "md" (default), or "lg" |
data-theme | Badge theme: "light" (default), "dark", "minimal", or "brand" |
data-show-code | Show the DZ# code in the badge. Default: true |
data-show-tooltip | Show hover tooltip with status message. Default: true |
data-animated | Enable pop-in animation on verify. Default: true |
JavaScript Init Options
| Option | Description |
|---|---|
apiKey | Your Dropzon API key (required) |
code | DZ# code to verify (required) |
target | CSS selector or DOM element for the container (required) |
size | "sm", "md" (default), or "lg" |
theme | "light" (default), "dark", "minimal", or "brand" |
showCode | Show the DZ# code in the badge. Default: true |
showTooltip | Show hover tooltip. Default: true |
animated | Pop-in animation on verify. Default: true |
onVerified | Called when the address is verified. Receives { valid, active, code }. |
onFailed | Called when verification fails. Receives { code, error }. |
Common Use Cases
Order Confirmation Pages
After checkout, show the badge next to the shipping address to reassure the customer their DZ address is verified and the package will reach the right Dropzon.
Email Receipts
Include a "DZ Verified" status line in order confirmation emails. Use the headless DropzonBadge.verify() API to check before sending.
Shipping Labels
Validate the DZ# code before printing shipping labels to catch typos and inactive addresses early.
Admin Dashboards
Display verification badges in your order management system so support staff can quickly see which orders have verified DZ addresses.
Address Embeds
Copy-paste HTML snippets to showcase a DZ Address on any website
Overview
Private Dropzon holders can embed their verified DZ Address directly on their own website - product pages, checkout confirmations, contact pages, invoices, and more.
Every embed links to the public address page at https://dropzon.app/address/DZ%23XXX-XX where anyone can verify the address is real and active on the Dropzon network. No script tags, no API keys, no dependencies - pure HTML with inline styles that works on any platform.
Where to get your embed snippets
Each snippet is pre-generated with your exact address data. To get yours:
- 1Log in to your Dropzon account at dropzon.app/app
- 2Go to Private Dropzons and open your DZ
- 3Click the "Embed & Share" tab
- 4Pick a variant, preview it live, then click "Copy HTML" and paste it into your site
Embed Variants
Dark Badge
Compact inline badge. Great for footers and navigation bars.
<a href="https://dropzon.app/address/DZ%23007-01" target="_blank" rel="noopener noreferrer" style="display:inline-flex;align-items:center;gap:8px;background:#0f172a;color:#fff;padding:8px 14px;border-radius:8px;text-decoration:none;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;font-size:13px;line-height:1;"> <img src="https://dropzon.app/email-logo.png" width="18" height="18" style="border-radius:4px;flex-shrink:0;" alt="Dropzon"> <span style="font-weight:700;letter-spacing:-0.3px;">DZ#007-01</span> <span style="color:#94a3b8;font-size:11px;">Dar es Salaam · TZ</span> </a>
Blue Badge
Brand-colored badge. Stands out on light backgrounds.
<a href="https://dropzon.app/address/DZ%23007-01" target="_blank" rel="noopener noreferrer" style="display:inline-flex;align-items:center;gap:8px;background:#0d84d4;color:#fff;padding:8px 14px;border-radius:8px;text-decoration:none;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;font-size:13px;line-height:1;"> <img src="https://dropzon.app/email-logo.png" width="18" height="18" style="border-radius:4px;flex-shrink:0;" alt="Dropzon"> <span style="font-weight:700;letter-spacing:-0.3px;">DZ#007-01</span> <span style="color:rgba(255,255,255,0.75);font-size:11px;">Dar es Salaam · TZ</span> </a>
Light Badge
Subtle bordered badge. Works well inside cards and forms.
<a href="https://dropzon.app/address/DZ%23007-01" target="_blank" rel="noopener noreferrer" style="display:inline-flex;align-items:center;gap:8px;background:#fff;color:#0f172a;padding:8px 14px;border-radius:8px;border:1.5px solid #e2e8f0;text-decoration:none;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;font-size:13px;line-height:1;"> <img src="https://dropzon.app/email-logo.png" width="18" height="18" style="border-radius:4px;flex-shrink:0;" alt="Dropzon"> <span style="font-weight:700;letter-spacing:-0.3px;color:#0d84d4;">DZ#007-01</span> <span style="color:#64748b;font-size:11px;">Dar es Salaam · TZ</span> </a>
Address Card (Light)
Full branded card with business name and complete address. Ideal for Contact Us pages.
<a href="https://dropzon.app/address/DZ%23007-01" target="_blank" rel="noopener noreferrer" style="display:block;max-width:280px;background:#fff;border:1.5px solid #e2e8f0;border-radius:14px;padding:16px 18px;text-decoration:none;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;padding-bottom:12px;border-bottom:1px solid #f1f5f9;">
<img src="https://dropzon.app/email-logo.png" width="22" height="22" style="border-radius:6px;flex-shrink:0;" alt="Dropzon">
<span style="font-size:13px;font-weight:700;color:#0d84d4;">Drop<span style="font-weight:400;">zon</span></span>
<span style="margin-left:auto;font-size:10px;font-weight:600;color:#16a34a;background:#f0fdf4;border:1px solid #bbf7d0;padding:2px 7px;border-radius:20px;">VERIFIED</span>
</div>
<p style="margin:0 0 2px;font-size:14px;font-weight:700;color:#0f172a;">Dropzon HQ</p>
<p style="margin:0 0 1px;font-size:12px;color:#475569;">21 Manet St</p>
<p style="margin:0 0 1px;font-size:12px;color:#0d84d4;font-weight:600;">DZ#007-01</p>
<p style="margin:0 0 12px;font-size:12px;color:#475569;">Dar es Salaam, 16102 TZ</p>
<p style="margin:0;font-size:11px;color:#94a3b8;">Tap to verify this address →</p>
</a>Address Card (Dark)
Same card in dark theme. Matches dark-mode websites.
<a href="https://dropzon.app/address/DZ%23007-01" target="_blank" rel="noopener noreferrer" style="display:block;max-width:280px;background:#0f172a;border:1.5px solid #1e293b;border-radius:14px;padding:16px 18px;text-decoration:none;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;padding-bottom:12px;border-bottom:1px solid #1e293b;">
<img src="https://dropzon.app/email-logo.png" width="22" height="22" style="border-radius:6px;flex-shrink:0;" alt="Dropzon">
<span style="font-size:13px;font-weight:700;color:#38bdf8;">Drop<span style="font-weight:400;color:#94a3b8;">zon</span></span>
<span style="margin-left:auto;font-size:10px;font-weight:600;color:#4ade80;background:rgba(74,222,128,0.1);border:1px solid rgba(74,222,128,0.3);padding:2px 7px;border-radius:20px;">VERIFIED</span>
</div>
<p style="margin:0 0 2px;font-size:14px;font-weight:700;color:#f1f5f9;">Dropzon HQ</p>
<p style="margin:0 0 1px;font-size:12px;color:#94a3b8;">21 Manet St</p>
<p style="margin:0 0 1px;font-size:12px;color:#38bdf8;font-weight:600;">DZ#007-01</p>
<p style="margin:0 0 12px;font-size:12px;color:#94a3b8;">Dar es Salaam, 16102 TZ</p>
<p style="margin:0;font-size:11px;color:#475569;">Tap to verify this address →</p>
</a>Inline Text Link
Drop a clickable address line inside any paragraph or block of text.
<a href="https://dropzon.app/address/DZ%23007-01" target="_blank" rel="noopener noreferrer" style="display:inline-flex;align-items:center;gap:6px;color:#0d84d4;text-decoration:none;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;font-size:14px;"><img src="https://dropzon.app/email-logo.png" width="16" height="16" style="border-radius:3px;flex-shrink:0;" alt="Dropzon">21 Manet St, DZ#007-01, Dar es Salaam, 16102 TZ</a>
Checkout / Shipping Form Fields
When filling out a shipping or checkout form on any platform (Amazon, Shopify, Jumia, Craught, etc.), use this field mapping. The DZ# code goes into Address Line 2 - most international forms have this field and it routes the package to the correct Dropzon automatically.
| Form Field | Value to enter |
|---|---|
| Full Name / Business | Dropzon HQYour business or personal name |
| Address Line 1 | 21 Manet StStreet address from your DZ profile |
| Address Line 2 | DZ#007-01Your DZ# code - this is the key field |
| City | Dar es Salaam |
| ZIP / Postal Code | 16102 |
| Country | TZISO 2-letter country code |
Public Address Page
Every DZ# code has a public, SEO-indexed page that anyone can visit to verify the address is real. All embed snippets link to this page automatically.
https://dropzon.app/address/DZ%23007-01
# URL pattern
https://dropzon.app/address/{url-encoded-dz-code}
# Examples
https://dropzon.app/address/DZ%23001-01 → DZ#001-01
https://dropzon.app/address/DZ%23007-03 → DZ#007-03The page shows the Dropzon name, full address, GPS location, operating hours, and the active/inactive status. It is server-rendered and fully indexable by search engines - useful for SEO when you link your shipping address from your website.
Webhook Integration Guide
Real-time delivery notifications via HMAC-signed HTTP callbacks
Overview
Webhooks let your application receive real-time notifications when package events occur - such as a package being received, dispatched, delivered, or collected. Instead of polling the tracking API, you register an HTTPS endpoint and Dropzon pushes event payloads to it automatically.
Every webhook delivery is signed with HMAC-SHA256 using your webhook secret, so you can verify that requests genuinely come from Dropzon. Failed deliveries are retried with exponential backoff, and delivery history is logged for debugging.
How It Works
Register a webhook
POST to /api/v1/webhooks with your HTTPS endpoint URL and the event types you want to subscribe to. Your API key must have the "webhooks" scope.
Dropzon signs and sends
When a matching event occurs, Dropzon builds a JSON payload, signs it with HMAC-SHA256 using your webhook secret, and POSTs it to your URL.
You verify and respond
Your server verifies the signature, processes the event, and responds with a 2xx status code within 10 seconds.
Retries on failure
If your endpoint returns a non-2xx status or times out, Dropzon retries up to 3 times with exponential backoff (2s, 4s, 8s delays).
Payload Format
Every webhook delivery sends a JSON payload wrapped in a standard envelope. The top-level fields are always present. The data object contains the event-specific information (package details, tracking info, etc.).
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"event": "package.delivered",
"createdAt": "2025-01-15T14:30:00.000Z",
"data": {
"package_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"tracking_number": "DZP-KE-20250115-ABC123",
"status": "delivered",
"origin_code": "DZ#001-01",
"destination_code": "DZ#002-05",
"sender_name": "John Doe",
"recipient_name": "Jane Smith",
"delivered_at": "2025-01-15T14:28:00.000Z"
}
}| Field | Description |
|---|---|
id | Unique delivery ID - use this for idempotency checks |
event | The event type that triggered this delivery (e.g. "package.delivered") |
createdAt | Timestamp when the event was created |
data | Event-specific payload with package or tracking details |
Event Types
Subscribe to specific events when registering your webhook. You can subscribe to all 11 events or pick only the ones relevant to your integration.
| Event | Description |
|---|---|
package.received | Package has been received at the origin Dropzon location |
package.at_origin | Package is at the origin hub, awaiting dispatch |
package.in_transit | Package has been dispatched and is in transit to the destination |
package.at_destination | Package has arrived at the destination Dropzon location |
package.ready_for_pickup | Package is ready for the recipient to pick up |
package.out_for_delivery | Package is out for last-mile delivery to the recipient |
package.delivered | Package has been delivered to the recipient |
package.collected | Package has been collected by the recipient from the Dropzon |
package.returned | Package has been returned to the sender |
package.lost | Package has been marked as lost |
tracking.updated | General tracking update (weighed, payment confirmed, customs hold/cleared, arrived at hub) |
HTTP Headers
Each webhook delivery includes these headers. Use them to identify the event, verify the signature, and correlate deliveries.
| Header | Description |
|---|---|
Content-Type | Always application/json |
User-Agent | Always Dropzon-Webhooks/1.0 - use this to identify webhook requests |
X-Dropzon-Event | The event type (e.g. package.delivered) |
X-Dropzon-Delivery | Unique delivery ID (UUID) matching the payload id field |
X-Dropzon-Signature | HMAC-SHA256 signature in the format sha256=<hex_digest> |
Signature Verification
Always verify the X-Dropzon-Signature header to confirm the request comes from Dropzon and has not been tampered with. The signature is computed as sha256=HMAC-SHA256(raw_body, your_webhook_secret).
Node.js / Express
const crypto = require('crypto');
function verifyWebhookSignature(req, secret) {
const signature = req.headers['x-dropzon-signature'];
if (!signature) return false;
const body = JSON.stringify(req.body);
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
// Use timingSafeEqual to prevent timing attacks
try {
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
} catch {
return false;
}
}
// Express middleware example
app.post('/webhooks/dropzon', express.json(), (req, res) => {
const WEBHOOK_SECRET = process.env.DROPZON_WEBHOOK_SECRET;
if (!verifyWebhookSignature(req, WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const { id, event, data } = req.body;
switch (event) {
case 'package.delivered':
console.log('Package delivered:', data.tracking_number);
// Update your order status, send customer notification, etc.
break;
case 'package.in_transit':
console.log('Package in transit:', data.tracking_number);
break;
// Handle other events...
}
// Respond quickly with 200
res.status(200).json({ received: true });
});Python / Flask
import hmac
import hashlib
from flask import Flask, request, jsonify
app = Flask(__name__)
WEBHOOK_SECRET = 'your_webhook_secret'
def verify_signature(payload, signature, secret):
if not signature:
return False
expected = 'sha256=' + hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
@app.route('/webhooks/dropzon', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Dropzon-Signature')
if not verify_signature(request.data, signature, WEBHOOK_SECRET):
return jsonify({'error': 'Invalid signature'}), 401
event = request.json
event_type = event.get('event')
data = event.get('data', {})
if event_type == 'package.delivered':
print(f"Package delivered: {data.get('tracking_number')}")
# Update order status, notify customer, etc.
return jsonify({'received': True}), 200PHP
<?php
$webhookSecret = getenv('DROPZON_WEBHOOK_SECRET');
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_DROPZON_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $payload, $webhookSecret);
if (!hash_equals($expected, $signature)) {
http_response_code(401);
echo json_encode(['error' => 'Invalid signature']);
exit;
}
$event = json_decode($payload, true);
$eventType = $event['event'];
$data = $event['data'];
switch ($eventType) {
case 'package.delivered':
// Update order status, notify customer, etc.
break;
}
http_response_code(200);
echo json_encode(['received' => true]);Retry and Failure Policy
Dropzon automatically retries failed webhook deliveries with exponential backoff. After persistent failures, webhooks are automatically disabled to protect both systems.
Max Attempts
3
Each event triggers up to 3 delivery attempts
Retry Delays
2s, 4s, 8s
Exponential backoff starting at 2 seconds
Request Timeout
10 seconds
Your endpoint must respond within 10 seconds
Auto-disable
10 failures
After 10 consecutive failures the webhook is auto-disabled
Re-enabling disabled webhooks: If your webhook is auto-disabled after 10 consecutive failures, fix your endpoint and then PATCH the webhook with { "active": true }. This resets the failure counter and resumes delivery.
Success Criteria
A delivery is considered successful when your endpoint returns any 2xx HTTP status code (200-299). Any other status code or a timeout triggers a retry.
| Response | Result |
|---|---|
200-299 | Success - delivery marked as complete, failure counter reset to 0 |
3xx, 4xx, 5xx | Failure - delivery retried (up to 3 attempts), failure counter incremented |
Timeout (>10s) | Failure - request aborted, delivery retried with exponential backoff |
Connection error | Failure - DNS, TLS, or network errors trigger retry |
Delivery History
Every delivery attempt is logged and accessible via the API. When you fetch a specific webhook with GET /api/v1/webhooks/:id, the response includes the last 25 delivery attempts with status codes, response bodies, timing, and error details.
// Example delivery record in the webhook detail response
{
"deliveries": [
{
"id": "d1e2f3a4-b5c6-7890-abcd-ef1234567890",
"event_type": "package.delivered",
"status_code": 200,
"success": true,
"attempt": 1,
"duration_ms": 145,
"delivered_at": "2025-01-15T14:30:01.145Z",
"error_message": null
},
{
"id": "a9b8c7d6-e5f4-3210-abcd-ef0987654321",
"event_type": "package.in_transit",
"status_code": 500,
"success": false,
"attempt": 1,
"duration_ms": 2340,
"delivered_at": null,
"error_message": null
}
]
}Quick Start
Register a webhook endpoint using your API key with the webhooks scope. See the Webhooks section in the API Reference above for full CRUD endpoint details.
# Register a webhook
curl -X POST https://api.dropzon.app/api/v1/webhooks \
-H "Authorization: Bearer dzk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yoursite.com/webhooks/dropzon",
"events": ["package.delivered", "package.in_transit", "package.collected"],
"description": "Order status updates"
}'
# Response includes the webhook secret - save it!
# {
# "webhook": {
# "id": "abc123...",
# "url": "https://yoursite.com/webhooks/dropzon",
# "events": ["package.delivered", "package.in_transit", "package.collected"],
# "secret": "whsec_xxxxxxxxxxxxxxxx",
# "active": true,
# "description": "Order status updates"
# }
# }Save your webhook secret immediately. The secret is only returned in the registration response. You cannot retrieve it again later. If you lose it, delete and re-create the webhook.
Best Practices
Respond quickly
Return a 200 OK as soon as you receive and validate the webhook. Process the event asynchronously (e.g. add to a queue) to avoid hitting the 10-second timeout.
Always verify signatures
Never skip signature verification, even in development. Use constant-time comparison (timingSafeEqual, hmac.compare_digest, hash_equals) to prevent timing attacks.
Handle duplicates
Use the payload id field as an idempotency key. In rare cases (retries, network issues), the same event may be delivered more than once. Store processed IDs and skip duplicates.
Monitor delivery history
Periodically check GET /api/v1/webhooks/:id to review delivery success rates, error messages, and response times. Catch issues before you hit the auto-disable threshold.
Subscribe to specific events
Only subscribe to the events you need. This reduces unnecessary traffic and simplifies your handler logic. You can update subscribed events anytime via PATCH.
Use HTTPS endpoints only
Webhook URLs must use HTTPS. This is enforced by the API - HTTP URLs will be rejected during registration.
Limits
| Limit | Value |
|---|---|
| Webhooks per API key | 20 |
| Webhook URL length | 2,048 characters |
| Description length | 500 characters |
| Response body logged | 1 KB (truncated) |
| Required API key scope | "webhooks" |
Shipping Rate Calculator
Get instant shipping estimates between any two DZ addresses
Overview
The Shipping Rate Calculator lets you get instant price estimates for sending packages between any two points in the Dropzon network. Use it to display shipping costs at checkout, build pricing tables, or validate costs before creating shipments.
Rates are calculated using Dropzon's zone-based pricing engine, which considers the origin and destination locations, package weight, and optional pickup/delivery services. The API automatically determines the shipping scope (in-city, domestic, cross-border, or international) based on the locations provided.
Quick Start
Get a rate estimate with a single API call. Pass origin and destination DZ codes plus the package weight.
curl -X POST https://api.dropzon.app/api/v1/rates/estimate \
-H "Authorization: Bearer dzk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"origin": "DZ#001-01",
"destination": "DZ#002-05",
"weight_kg": 2.5
}'The response includes a full price breakdown, the shipping scope (automatically determined), and estimated transit time.
{
"success": true,
"data": {
"origin": {
"dropzon_id": "abc-123",
"name": "Kariakoo Hub",
"code": "DZ#001"
},
"destination": {
"dropzon_id": "def-456",
"name": "Mikocheni Point",
"code": "DZ#002"
},
"scope": "in_city",
"weight_kg": 2.5,
"pricing": {
"pickup_fee": 0,
"shipping_fee": 5750,
"delivery_fee": 0,
"total": 5750,
"currency": "TZS"
},
"estimated_transit": {
"min_days": 0,
"max_days": 1
},
"pickup_method": "self_drop",
"delivery_method": "pickup"
}
}Shipping Scopes
The API automatically determines the shipping scope based on the origin and destination locations. Each scope has different base rates, per-kg rates, and estimated transit times.
| Scope | Description |
|---|---|
in_city | Same city - origin and destination Dropzons are in the same city |
domestic | Different cities within the same country |
cross_border | Between neighboring countries (e.g. Tanzania to Kenya) |
international | Overseas corridors (US, China, UAE, UK) |
Price Breakdown
Every rate estimate returns a detailed breakdown of the total cost. The formula depends on the shipping scope and the zone-pair pricing configured for those locations.
total = pickup_fee + shipping_fee + delivery_fee
shipping_fee = max(min_fee, base_fee + rate_per_kg x weight_kg)
| Field | Description |
|---|---|
pickup_fee | Fee for door pickup from sender's location (0 if self_drop) |
shipping_fee | Zone-based Dropzon-to-Dropzon shipping cost based on weight and distance |
delivery_fee | Fee for last-mile door delivery to recipient (0 if pickup) |
total | Sum of all three fees - the full cost to ship this package |
currency | ISO currency code for all amounts (default: TZS) |
Pickup and Delivery Options
Customize the rate estimate by specifying how the sender gets the package to the origin Dropzon and how the recipient receives it at the destination.
Pickup Methods (sender side)
self_dropSender walks to their nearest Dropzon and drops off the parcel. No pickup fee.
door_pickupA Dropper rider picks up the parcel from the sender's door and brings it to the origin Dropzon. Adds a pickup fee.
Delivery Methods (recipient side)
pickupRecipient picks up the package from their Dropzon location using OTP verification. No delivery fee.
dropperA Dropper rider delivers the package from the Dropzon to the recipient's door. Adds a delivery fee.
Location Input Formats
The origin and destination fields accept multiple formats. Use whichever is most convenient for your integration.
| Format | Example |
|---|---|
| DZ# User Code | DZ#001-01 |
| DZ# Short Code | DZ#001 |
| Full DZ Code | DZ-001-001-001-001-001 |
| Dropzon UUID | a1b2c3d4-e5f6-... |
Code Examples
JavaScript / Fetch
const response = await fetch('https://api.dropzon.app/api/v1/rates/estimate', {
method: 'POST',
headers: {
'Authorization': 'Bearer dzk_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
origin: 'DZ#001-01',
destination: 'DZ#002-05',
weight_kg: 3.0,
delivery_method: 'dropper', // include door delivery fee
}),
});
const { data } = await response.json();
console.log(data.pricing.total, data.pricing.currency);
// 8750 TZS
console.log(data.estimated_transit);
// { min_days: 0, max_days: 1 }Python / Requests
import requests
response = requests.post(
'https://api.dropzon.app/api/v1/rates/estimate',
headers={'Authorization': 'Bearer dzk_your_api_key'},
json={
'origin': 'DZ#001-01',
'destination': 'DZ#005-12',
'weight_kg': 5.0,
'pickup_method': 'door_pickup',
'delivery_method': 'dropper',
},
)
data = response.json()['data']
print(f"Shipping: {data['pricing']['total']} {data['pricing']['currency']}")
print(f"Scope: {data['scope']}")
print(f"Transit: {data['estimated_transit']['min_days']}-{data['estimated_transit']['max_days']} days")PHP / cURL
$ch = curl_init('https://api.dropzon.app/api/v1/rates/estimate');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer dzk_your_api_key',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'origin' => 'DZ#001-01',
'destination' => 'DZ#002-05',
'weight_kg' => 2.5,
]),
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
$pricing = $response['data']['pricing'];
echo "Total: " . $pricing['total'] . " " . $pricing['currency'];Common Use Cases
Checkout Shipping Costs
Display accurate shipping costs at checkout. When a customer enters their DZ code, call the rate estimator with the warehouse origin and customer destination to show the price before they pay.
Pricing Tables
Build dynamic pricing pages that show shipping costs for different zones and weight brackets. Use the /rates/scopes endpoint to get scope definitions and call /rates/estimate for specific prices.
Bulk Quote Comparison
Compare shipping costs across different origin Dropzons to find the cheapest or fastest route for your logistics workflow.
Mobile Shipping Calculator
Let users estimate shipping costs in your mobile app before they commit to a shipment. The API supports all DZ code formats for easy input.
Limits and Notes
| Limit | Value |
|---|---|
| Max weight | 500 kg |
| Rate limit | 100 requests/minute (per API key) |
| Required scope | "read" (default on all keys) |
| Currency | TZS (rates reflect destination country) |
Estimates are not binding quotes. Actual shipping costs are calculated at the time of shipment creation and may differ slightly if zone pricing has been updated between the estimate and the shipment. Always use the rate estimate for display purposes and confirm the final price at shipment creation.
Bulk Dispatch API
Programmatically create and manage parcel shipments
Overview
The Bulk Dispatch API lets e-commerce platforms, logistics companies, and enterprise shippers create package dispatches programmatically. Send a single parcel or batch up to 50 parcels in one API call. Each dispatch gets a tracking number, triggers webhooks, and enters the Dropzon logistics pipeline automatically.
dispatchSetup
Get an API key
Go to the Developer Console and create an API key if you don't have one.
Enable the dispatch scope
When creating or editing your API key, add the "dispatch" scope. Without it, dispatch endpoints return 403.
Know your DZ codes
Every dispatch requires a sender DZ code (your warehouse or pickup location) and a recipient DZ code. Use the Addresses API to look up or validate codes.
Package Lifecycle
Every dispatched parcel follows the same lifecycle through the Dropzon network. You can track status changes via the Tracking API or receive real-time updates via Webhooks.
Additional terminal statuses: returned, lost
Single Dispatch
Create one parcel dispatch per request. Perfect for on-demand orders.
JavaScript
const response = await fetch('https://api.dropzon.app/api/v1/dispatch', {
method: 'POST',
headers: {
'Authorization': 'Bearer dzk_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
sender_dz_code: 'DZ#001-01',
recipient_name: 'Amina Hassan',
recipient_phone: '+255712345678',
recipient_dz_code: 'DZ#002-05',
weight_kg: 1.5,
description: 'Electronics - phone case',
client_reference: 'ORD-2026-0042',
}),
});
const { data } = await response.json();
console.log(data.tracking_number); // DZL-A1B2C3D4
console.log(data.pricing.total_price, data.pricing.currency); // 5000 TZSPython
import requests
resp = requests.post(
'https://api.dropzon.app/api/v1/dispatch',
headers={'Authorization': 'Bearer dzk_your_api_key'},
json={
'sender_dz_code': 'DZ#001-01',
'recipient_name': 'Baraka Ngowi',
'recipient_phone': '+255621000000',
'recipient_dz_code': 'DZ#003-12',
'weight_kg': 3.0,
'client_reference': 'SHOP-9001',
},
)
pkg = resp.json()['data']
print(f"Tracking: {pkg['tracking_number']}")
print(f"Price: {pkg['pricing']['total_price']} {pkg['pricing']['currency']}")Bulk Dispatch
Send up to 50 parcels in a single request. Ideal for end-of-day batch processing or syncing orders from your e-commerce platform. Partial success is supported - each parcel is processed independently and errors don't block other items.
const response = await fetch('https://api.dropzon.app/api/v1/dispatch/bulk', {
method: 'POST',
headers: {
'Authorization': 'Bearer dzk_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
parcels: [
{
sender_dz_code: 'DZ#001-01',
recipient_name: 'Amina Hassan',
recipient_phone: '+255712345678',
recipient_dz_code: 'DZ#002-05',
weight_kg: 1.5,
client_reference: 'ORD-001',
},
{
sender_dz_code: 'DZ#001-01',
recipient_name: 'Baraka Ngowi',
recipient_phone: '+255621000000',
recipient_dz_code: 'DZ#003-12',
weight_kg: 3.0,
client_reference: 'ORD-002',
},
],
}),
});
const { data } = await response.json();
console.log(`Dispatched: ${data.dispatched}/${data.total}`);
console.log(`Grand total: ${data.grand_total} ${data.currency}`);
// Check for any failures
if (data.errors) {
data.errors.forEach(err => {
console.error(`Parcel ${err.index} (${err.client_reference}) failed: ${err.error}`);
});
}Client References
Pass your own order ID in the client_reference field. Dropzon stores it and returns it in dispatch responses and webhook payloads. This lets you map Dropzon tracking numbers back to your internal orders without maintaining a separate lookup table.
// Request
{
"sender_dz_code": "DZ#001-01",
"recipient_name": "Customer",
"recipient_phone": "+255700000000",
"recipient_dz_code": "DZ#005-03",
"weight_kg": 2.0,
"client_reference": "SHOPIFY-ORD-12345"
}
// Response
{
"id": "pkg-uuid",
"tracking_number": "DZL-A1B2C3D4",
"client_reference": "SHOPIFY-ORD-12345",
"status": "received",
...
}Combine with Webhooks
For the best integration experience, use the Dispatch API together with Webhooks. Register a webhook for package status events, then dispatch parcels. You'll receive real-time updates as each parcel moves through the Dropzon network.
Register a webhook for package.in_transit, package.delivered, and package.collected events
Dispatch parcels via POST /dispatch or POST /dispatch/bulk
Receive webhook payloads at your URL as status changes happen
Update your order status in your system based on webhook events
Limits and Notes
| Limit | Value |
|---|---|
| Max parcels per bulk request | 50 |
| Max weight per parcel | 500 kg |
| Min weight per parcel | 0.1 kg |
| Rate limit | 100 requests/minute (per API key) |
| Required scope | "dispatch" |
| Currency | TZS (Tanzania Shilling) |
| Sender | Always the API key owner |
Pricing is final at dispatch time. The shipping price returned in the dispatch response is the actual charge, not an estimate. Zone-based pricing is applied based on the origin and destination Dropzons at the moment of creation.
Ready to integrate?
Get your API key and start building with the Dropzon address network. Free tier available for development and testing.