Skip to main content

Report

This section contains 1 subgroups.

Delivery Report

Reporting, Audit & Webhooks / Report
GET/report/delivery

Summary GET /report/delivery Delivery dashboard report. Storage model - One row per tenant in public.delivery_report (refreshed nightly). - API reads the stored lifetime payload; there is no range query param. - If stored zone_stats are still on the legacy shape, the API rebuilds zone stats live from public.deliveries. Auth - Header Authorization: Bearer {{access_token}} - All auth failures come from middleware and return { "success": false, "message": string, "data": null } with 401/403. 200 Response (JSON) Body is wrapped: Zone stats rules - All tenant zones are returned in zone_stats, even when a zone has no deliveries in the reporting window - Zones without deliveries return 0 for all totals - total_inbound_deliveries counts deliveries where pickup_zone_id = zone_id - total_outbound_deliveries counts deliveries where dropoff_zone_id = zone_id - total_deliveries counts unique deliveries touching the zone by pickup and/or dropoff - If both pickup and dropoff point to the same zone, that delivery is counted once in total_deliveries - total_completed_deliveries counts delivered rows from those unique deliveries Error responses - 401/403 (auth/middleware): { "success": false, "message": string, "data": null } - 404 (report not ready): { "success": false, "message": "report_not_ready", "data": null } - 404 (unknown module): { "success": false, "message": string, "data": null } - 405 (method not allowed): { "success": false, "message": string, "data": null } - 500: { "success": false, "message": string, "data": null }

Send a bearer token in the Authorization header for an authenticated OnDi user session.
Request
curl
1curl --request GET "$ONDI_BASE_URL/report/delivery" \2  --header "Authorization: Bearer {{access_token}}"

Headers

AuthorizationOptional
header string

Bearer {{access_token}}

Responses

200 OK200OK
Response body
json
1{2  "success": true,3  "message": "",4  "data": {5    "kpis": {6      "total_deliveries": {7        "value": 0,8        "trend_percentage": 0,9        "trend_direction": "up",10        "chart_data": [11          0,12          0,13          0,14          0,15          0,16          0,17          018        ]19      },20      "completed": {21        "value": 0,22        "trend_percentage": 0,23        "trend_direction": "up",24        "chart_data": [25          0,26          0,27          0,28          0,29          0,30          0,31          032        ]33      },34      "in_progress": {35        "value": 0,36        "trend_percentage": 0,37        "trend_direction": "up",38        "chart_data": []39      },40      "failed": {41        "value": 0,42        "trend_percentage": 0,43        "trend_direction": "up",44        "chart_data": [45          0,46          0,47          0,48          0,49          0,50          0,51          052        ]53      }54    },55    "charts": {56      "status_breakdown": {57        "completed": 0,58        "in_progress": 0,59        "pending": 0,60        "failed": 061      },62      "weekly_performance": {63        "dates": [64          "2025-12-08",65          "2025-12-09",66          "2025-12-10",67          "2025-12-11",68          "2025-12-12",69          "2025-12-13",70          "2025-12-14"71        ],72        "completed": [73          0,74          0,75          0,76          0,77          0,78          0,79          080        ],81        "failed": [82          0,83          0,84          0,85          0,86          0,87          0,88          089        ]90      },91      "metrics_trends": {92        "success_rate": [93          0,94          0,95          0,96          0,97          0,98          0,99          0100        ],101        "avg_delivery_time": [102          0,103          0,104          0,105          0,106          0,107          0,108          0109        ],110        "active_drivers": [111          0,112          0,113          0,114          0,115          0,116          0,117          0118        ]119      }120    },121    "performance": {122      "success_rate": {123        "value": 0,124        "percentage": 0125      },126      "on_time_delivery": {127        "value": 0,128        "percentage": 0129      },130      "customer_satisfaction": {131        "value": 0,132        "percentage": 0133      },134      "first_attempt_success": {135        "value": 0,136        "percentage": 0137      }138    },139    "cod": {140      "collected": 0,141      "remitted": 0,142      "outstanding": 0,143      "wallet_settled": 0144    },145    "top_drivers": [146      {147        "driver_id": "00000000-0000-0000-0000-000000000001",148        "name": "",149        "avatar_url": "",150        "completed_jobs": 0,151        "failed_jobs": 0,152        "avg_time_minutes": 0153      }154    ],155    "zone_stats": [156      {157        "zone_id": "11111111-1111-1111-1111-111111111111",158        "zone_code": "ZN-001",159        "zone_name": "Central Zone",160        "total_deliveries": 0,161        "total_inbound_deliveries": 0,162        "total_outbound_deliveries": 0,163        "total_completed_deliveries": 0164      }165    ],166    "recent_activity": [167      {168        "id": "00000000-0000-0000-0000-000000000100",169        "order_ref": "",170        "type": "pending",171        "message": "",172        "timestamp": "2025-12-14T00:00:00.000Z"173      }174    ]175  }176}
401 Unauthorized (middleware/auth)401Unauthorized
Response body
json
1{2  "success": false,3  "message": "invalid_token",4  "data": null5}
404 Not Found (unknown module)404Not Found
Response body
json
1{2  "success": false,3  "message": "Endpoint for module 'delivery' not found",4  "data": null5}
405 Method Not Allowed405Method Not Allowed
Response body
json
1{2  "success": false,3  "message": "method_not_allowed",4  "data": null5}
500 Internal Server Error (router catch)500Internal Server Error
Response body
json
1{2  "success": false,3  "message": "Internal Server Error",4  "data": null5}
200 Response (JSON)200
Response body
json
1type ResponseEnvelope<T> = { success: boolean; message: string; data: T | null };23type DeliveryDashboardEnvelope = ResponseEnvelope<DeliveryDashboardResponse>;45interface DeliveryDashboardResponse {6  kpis: {7    total_deliveries: KPIItem;8    completed: KPIItem;9    in_progress: KPIItem;10    failed: KPIItem;11  };12  charts: {13    status_breakdown: { completed: number; in_progress: number; pending: number; failed: number };14    weekly_performance: { dates: string[]; completed: number[]; failed: number[] };15    metrics_trends: { success_rate: number[]; avg_delivery_time: number[]; active_drivers: number[] };16  };17  performance: {18    success_rate: { value: number; percentage: number };19    on_time_delivery: { value: number; percentage: number };20    customer_satisfaction: { value: number; percentage: number };21    first_attempt_success: { value: number; percentage: number };22  };23  cod: { collected: number; remitted: number; outstanding: number; wallet_settled: number };24  top_drivers: Array<{ driver_id: string; name: string; avatar_url: string; completed_jobs: number; failed_jobs: number; avg_time_minutes: number }>;25  zone_stats: Array<{26    zone_id: string;27    zone_code: string;28    zone_name: string;29    total_deliveries: number;30    total_inbound_deliveries: number;31    total_outbound_deliveries: number;32    total_completed_deliveries: number;33  }>;34  recent_activity: Array<{ id: string; order_ref: string; type: 'completed'|'failed'|'in_transit'|'pending'|'assigned'; message: string; timestamp: string }>;35}3637type KPIItem = {38  value: number;39  trend_percentage: number;40  trend_direction: 'up' | 'down';41  chart_data: number[];42};

Storefront Report

Reporting, Audit & Webhooks / Report
GET/report/storefront

Summary GET /report/storefront Storefront dashboard report. Storage model - One row per tenant in public.storefront_report (refreshed nightly). - API reads the stored lifetime payload; there is no range query param. Auth - Header Authorization: Bearer {{access_token}} 200 Response (JSON) Body is wrapped: Error responses Same envelope: - { "success": false, "message": string, "data": null }

Send a bearer token in the Authorization header for an authenticated OnDi user session.
Request
curl
1curl --request GET "$ONDI_BASE_URL/report/storefront" \2  --header "Authorization: Bearer {{access_token}}"

Headers

AuthorizationOptional
header string

Bearer {{access_token}}

Responses

200 OK200OK
Response body
json
1{2  "success": true,3  "message": "",4  "data": {5    "kpis": {6      "weekly_sales": {7        "value": 0,8        "trend_percentage": 0,9        "trend_direction": "up",10        "chart_data": [11          0,12          0,13          0,14          0,15          0,16          0,17          018        ]19      },20      "new_customers": {21        "value": 0,22        "trend_percentage": 0,23        "trend_direction": "up",24        "chart_data": []25      },26      "purchase_orders": {27        "value": 0,28        "trend_percentage": 0,29        "trend_direction": "up",30        "chart_data": []31      },32      "messages": {33        "value": 0,34        "trend_percentage": 0,35        "trend_direction": "up",36        "chart_data": []37      }38    },39    "summary": {40      "product_sold": {41        "value": 0,42        "trend_percentage": 043      },44      "total_balance": {45        "value": 0,46        "trend_percentage": 047      },48      "sales_profit": {49        "value": 0,50        "trend_percentage": 051      }52    },53    "charts": {54      "sale_by_gender": [55        {56          "label": "Men",57          "value": 058        },59        {60          "label": "Women",61          "value": 062        },63        {64          "label": "Kids",65          "value": 066        }67      ],68      "yearly_sales": {69        "categories": [70          "Jan",71          "Feb",72          "Mar",73          "Apr",74          "May",75          "Jun",76          "Jul",77          "Aug",78          "Sep",79          "Oct",80          "Nov",81          "Dec"82        ],83        "series": [84          {85            "name": "2023",86            "data": [87              0,88              0,89              0,90              0,91              0,92              0,93              0,94              0,95              0,96              0,97              0,98              099            ]100          },101          {102            "name": "2024",103            "data": [104              0,105              0,106              0,107              0,108              0,109              0,110              0,111              0,112              0,113              0,114              0,115              0116            ]117          }118        ]119      }120    },121    "finances": {122      "sales_overview": {123        "total_profit": {124          "value": 0,125          "percentage": 0126        },127        "total_income": {128          "value": 0,129          "percentage": 0130        },131        "total_expenses": {132          "value": 0,133          "percentage": 0134        }135      },136      "current_balance": {137        "total": 0,138        "breakdown": {139          "order_total": 0,140          "earning": 0,141          "refunded": 0142        }143      }144    },145    "best_sellers": [146      {147        "seller_id": "00000000-0000-0000-0000-000000000001",148        "name": "",149        "avatar_url": "",150        "product_category": "",151        "country_code": "",152        "total_sales": 0,153        "rank": 1154      }155    ],156    "latest_products": [157      {158        "id": "00000000-0000-0000-0000-000000000010",159        "name": "",160        "image_url": "",161        "price": 0,162        "original_price": 0,163        "is_active": true164      }165    ]166  }167}
401 Unauthorized (middleware/auth)401Unauthorized
Response body
json
1{2  "success": false,3  "message": "invalid_token",4  "data": null5}
405 Method Not Allowed405Method Not Allowed
Response body
json
1{2  "success": false,3  "message": "method_not_allowed",4  "data": null5}
500 Internal Server Error (router catch)500Internal Server Error
Response body
json
1{2  "success": false,3  "message": "Internal Server Error",4  "data": null5}
200 Response (JSON)200
Response body
json
1type ResponseEnvelope<T> = { success: boolean; message: string; data: T | null };23type StorefrontDashboardEnvelope = ResponseEnvelope<StorefrontDashboardResponse>;45interface StorefrontDashboardResponse {6  kpis: {7    weekly_sales: KPIItem;8    new_customers: KPIItem;9    purchase_orders: KPIItem;10    messages: KPIItem;11  };12  summary: {13    product_sold: { value: number; trend_percentage: number };14    total_balance: { value: number; trend_percentage: number };15    sales_profit: { value: number; trend_percentage: number };16  };17  charts: {18    sale_by_gender: Array<{ label: string; value: number }>;19    yearly_sales: { categories: string[]; series: Array<{ name: string; data: number[] }> };20  };21  finances: {22    sales_overview: {23      total_profit: { value: number; percentage: number };24      total_income: { value: number; percentage: number };25      total_expenses: { value: number; percentage: number };26    };27    current_balance: {28      total: number;29      breakdown: { order_total: number; earning: number; refunded: number };30    };31  };32  best_sellers: Array<{ seller_id: string; name: string; avatar_url: string; product_category: string; country_code: string; total_sales: number; rank: number }>;33  latest_products: Array<{ id: string; name: string; image_url: string; price: number; original_price?: number; is_active: boolean }>;34}3536type KPIItem = { value: number; trend_percentage: number; trend_direction: 'up'|'down'; chart_data: number[] };

Warehouse Report

Reporting, Audit & Webhooks / Report
GET/report/warehouse

Summary GET /report/warehouse Warehouse dashboard report. Storage model - One row per tenant in public.warehouse_report (refreshed nightly). - API reads the stored lifetime payload; there is no range query param. Auth - Header Authorization: Bearer {{access_token}} 200 Response (JSON) Body is wrapped: Error responses Same envelope: - { "success": false, "message": string, "data": null }

Send a bearer token in the Authorization header for an authenticated OnDi user session.
Request
curl
1curl --request GET "$ONDI_BASE_URL/report/warehouse" \2  --header "Authorization: Bearer {{access_token}}"

Headers

AuthorizationOptional
header string

Bearer {{access_token}}

Responses

200 OK200OK
Response body
json
1{2  "success": true,3  "message": "",4  "data": {5    "kpis": {6      "items_received": {7        "value": 0,8        "trend_percentage": 0,9        "trend_direction": "up",10        "chart_data": [11          0,12          0,13          0,14          0,15          0,16          0,17          018        ]19      },20      "items_dispatched": {21        "value": 0,22        "trend_percentage": 0,23        "trend_direction": "up",24        "chart_data": [25          0,26          0,27          0,28          0,29          0,30          0,31          032        ]33      },34      "total_stock": {35        "value": 0,36        "trend_percentage": 0,37        "trend_direction": "up",38        "chart_data": []39      },40      "picking_orders": {41        "value": 0,42        "trend_percentage": 0,43        "trend_direction": "up",44        "chart_data": []45      }46    },47    "charts": {48      "weekly_receiving": [49        0,50        0,51        0,52        0,53        0,54        0,55        056      ],57      "weekly_dispatching": [58        0,59        0,60        0,61        0,62        0,63        0,64        065      ],66      "completed_audits": [67        0,68        0,69        0,70        0,71        0,72        0,73        074      ],75      "capacity_utilization": {76        "used": 0,77        "available": 0,78        "percentage": 079      },80      "inventory_movement": {81        "dates": [82          "2025-12-08",83          "2025-12-09",84          "2025-12-10",85          "2025-12-11",86          "2025-12-12",87          "2025-12-13",88          "2025-12-14"89        ],90        "received": [91          0,92          0,93          0,94          0,95          0,96          0,97          098        ],99        "dispatched": [100          0,101          0,102          0,103          0,104          0,105          0,106          0107        ]108      }109    },110    "performance": {111      "items_received": {112        "current": 0,113        "target": 0,114        "percentage": 0115      },116      "items_dispatched": {117        "current": 0,118        "target": 0,119        "percentage": 0120      },121      "picking_orders": {122        "current": 0,123        "target": 0,124        "percentage": 0125      },126      "audits_completed": {127        "current": 0,128        "target": 0,129        "percentage": 0130      }131    },132    "summary": {133      "total_stock_items": 0,134      "total_stock_value": 0,135      "capacity_used_percentage": 0136    },137    "stock_levels": [138      {139        "item_name": "",140        "sku": "",141        "quantity": 0,142        "capacity_max": 0,143        "status": "In Stock",144        "status_color": "green"145      }146    ],147    "recent_activity": [148      {149        "id": "00000000-0000-0000-0000-000000000001",150        "type": "received",151        "message": "",152        "timestamp": "2025-12-14T00:00:00.000Z",153        "reference_id": ""154      }155    ]156  }157}
401 Unauthorized (middleware/auth)401Unauthorized
Response body
json
1{2  "success": false,3  "message": "invalid_token",4  "data": null5}
405 Method Not Allowed405Method Not Allowed
Response body
json
1{2  "success": false,3  "message": "method_not_allowed",4  "data": null5}
500 Internal Server Error (router catch)500Internal Server Error
Response body
json
1{2  "success": false,3  "message": "Internal Server Error",4  "data": null5}
200 Response (JSON)200
Response body
json
1type ResponseEnvelope<T> = { success: boolean; message: string; data: T | null };23type WarehouseDashboardEnvelope = ResponseEnvelope<WarehouseDashboardResponse>;45interface WarehouseDashboardResponse {6  kpis: {7    items_received: KPIItem;8    items_dispatched: KPIItem;9    total_stock: KPIItem;10    picking_orders: KPIItem;11  };12  charts: {13    weekly_receiving: number[];14    weekly_dispatching: number[];15    completed_audits: number[];16    capacity_utilization: { used: number; available: number; percentage: number };17    inventory_movement: { dates: string[]; received: number[]; dispatched: number[] };18  };19  performance: {20    items_received: { current: number; target: number; percentage: number };21    items_dispatched: { current: number; target: number; percentage: number };22    picking_orders: { current: number; target: number; percentage: number };23    audits_completed: { current: number; target: number; percentage: number };24  };25  summary: {26    total_stock_items: number;27    total_stock_value: number;28    capacity_used_percentage: number;29  };30  stock_levels: Array<{ item_name: string; sku: string; quantity: number; capacity_max: number; status: 'In Stock'|'Low Stock'|'Out of Stock'; status_color: 'green'|'yellow'|'red' }>;31  recent_activity: Array<{ id: string; type: 'received'|'dispatched'|'picking'|'audit'|'transfer'; message: string; timestamp: string; reference_id: string }>;32}3334type KPIItem = { value: number; trend_percentage: number; trend_direction: 'up'|'down'; chart_data: number[] };

Restaurant Report

Reporting, Audit & Webhooks / Report
GET/report/restaurant

Summary GET /report/restaurant Restaurant dashboard report. Storage model - One row per tenant in public.restaurant_report (refreshed nightly). - API reads the stored lifetime payload; there is no range query param. Auth - Header Authorization: Bearer {{access_token}} 200 Response (JSON) Body is wrapped: Error responses Same envelope: - { "success": false, "message": string, "data": null }

Send a bearer token in the Authorization header for an authenticated OnDi user session.
Request
curl
1curl --request GET "$ONDI_BASE_URL/report/restaurant" \2  --header "Authorization: Bearer {{access_token}}"

Headers

AuthorizationOptional
header string

Bearer {{access_token}}

Responses

200 OK200OK
Response body
json
1{2  "success": true,3  "message": "",4  "data": {5    "kpis": {6      "todays_orders": {7        "value": 0,8        "trend_percentage": 0,9        "trend_direction": "up",10        "chart_data": [11          0,12          0,13          0,14          0,15          0,16          0,17          018        ]19      },20      "todays_revenue": {21        "value": 0,22        "trend_percentage": 0,23        "trend_direction": "up",24        "chart_data": [25          0,26          0,27          0,28          0,29          0,30          0,31          032        ]33      },34      "active_tables": {35        "value": 0,36        "trend_percentage": 0,37        "trend_direction": "up",38        "chart_data": []39      },40      "avg_prep_time": {41        "value": 0,42        "trend_percentage": 0,43        "trend_direction": "up",44        "chart_data": []45      }46    },47    "charts": {48      "weekly_orders": [49        0,50        0,51        0,52        0,53        0,54        0,55        056      ],57      "weekly_revenue": [58        0,59        0,60        0,61        0,62        0,63        0,64        065      ],66      "avg_order_value": [67        0,68        0,69        0,70        0,71        0,72        0,73        074      ],75      "order_status_breakdown": {76        "completed": 0,77        "in_progress": 0,78        "pending": 0,79        "cancelled": 080      },81      "revenue_by_type": {82        "dates": [83          "2025-12-08",84          "2025-12-09",85          "2025-12-10",86          "2025-12-11",87          "2025-12-12",88          "2025-12-13",89          "2025-12-14"90        ],91        "dine_in": [92          0,93          0,94          0,95          0,96          0,97          0,98          099        ],100        "takeaway": [101          0,102          0,103          0,104          0,105          0,106          0,107          0108        ]109      }110    },111    "performance": {112      "total_revenue": {113        "current": 0,114        "target": 0,115        "percentage": 0116      },117      "total_orders": {118        "current": 0,119        "target": 0,120        "percentage": 0121      },122      "total_expenses": {123        "current": 0,124        "target": 0,125        "percentage": 0126      },127      "todays_summary": {128        "revenue": 0,129        "orders": 0,130        "aov": 0131      }132    },133    "popular_items": [134      {135        "item_id": "00000000-0000-0000-0000-000000000001",136        "name": "",137        "image_url": "",138        "category": "",139        "orders_count": 0,140        "revenue": 0,141        "status": "Available",142        "status_color": "green"143      }144    ],145    "recent_orders": [146      {147        "id": "00000000-0000-0000-0000-000000000010",148        "order_ref": "",149        "description": "",150        "status": "Pending",151        "timestamp": "2025-12-14T00:00:00.000Z"152      }153    ]154  }155}
401 Unauthorized (middleware/auth)401Unauthorized
Response body
json
1{2  "success": false,3  "message": "invalid_token",4  "data": null5}
405 Method Not Allowed405Method Not Allowed
Response body
json
1{2  "success": false,3  "message": "method_not_allowed",4  "data": null5}
500 Internal Server Error (router catch)500Internal Server Error
Response body
json
1{2  "success": false,3  "message": "Internal Server Error",4  "data": null5}
200 Response (JSON)200
Response body
json
1type ResponseEnvelope<T> = { success: boolean; message: string; data: T | null };23type RestaurantDashboardEnvelope = ResponseEnvelope<RestaurantDashboardResponse>;45interface RestaurantDashboardResponse {6  kpis: {7    todays_orders: KPIItem;8    todays_revenue: KPIItem;9    active_tables: KPIItem;10    avg_prep_time: KPIItem;11  };12  charts: {13    weekly_orders: number[];14    weekly_revenue: number[];15    avg_order_value: number[];16    order_status_breakdown: { completed: number; in_progress: number; pending: number; cancelled: number };17    revenue_by_type: { dates: string[]; dine_in: number[]; takeaway: number[] };18  };19  performance: {20    total_revenue: { current: number; target: number; percentage: number };21    total_orders: { current: number; target: number; percentage: number };22    total_expenses: { current: number; target: number; percentage: number };23    todays_summary: { revenue: number; orders: number; aov: number };24  };25  popular_items: Array<{ item_id: string; name: string; image_url: string; category: string; orders_count: number; revenue: number; status: 'Available'|'Low Stock'|'Out of Stock'; status_color: 'green'|'orange'|'red' }>;26  recent_orders: Array<{ id: string; order_ref: string; description: string; status: 'Completed'|'In Progress'|'Preparing'|'Pending'|'Cancelled'; timestamp: string }>;27}2829type KPIItem = { value: number; trend_percentage: number; trend_direction: 'up'|'down'; chart_data: number[] };

Express Shipping Report

Reporting, Audit & Webhooks / Report
GET/report/express

Summary GET /report/express Express (shipping) dashboard report. Storage model - One row per tenant in public.express_report (refreshed nightly). - API reads the stored lifetime payload; there is no range query param. Auth - Header Authorization: Bearer {{access_token}} 200 Response (JSON) Body is wrapped: Error responses Same envelope: - { "success": false, "message": string, "data": null }

Send a bearer token in the Authorization header for an authenticated OnDi user session.
Request
curl
1curl --request GET "$ONDI_BASE_URL/report/express" \2  --header "Authorization: Bearer {{access_token}}"

Headers

AuthorizationOptional
header string

Bearer {{access_token}}

Responses

200 OK200OK
Response body
json
1{2  "success": true,3  "message": "",4  "data": {5    "kpis": {6      "total_packages": {7        "value": 0,8        "trend_percentage": 0,9        "trend_direction": "up",10        "chart_data": [11          0,12          0,13          0,14          0,15          0,16          0,17          018        ]19      },20      "delivered": {21        "value": 0,22        "trend_percentage": 0,23        "trend_direction": "up",24        "chart_data": []25      },26      "in_transit": {27        "value": 0,28        "trend_percentage": 0,29        "trend_direction": "up",30        "chart_data": []31      },32      "pending_claim": {33        "value": 0,34        "trend_percentage": 0,35        "trend_direction": "up",36        "chart_data": []37      }38    },39    "charts": {40      "weekly_volume": [41        0,42        0,43        0,44        0,45        0,46        0,47        048      ],49      "total_weight_kg": [50        0,51        0,52        0,53        0,54        0,55        0,56        057      ],58      "avg_delivery_time": [59        0,60        0,61        0,62        0,63        0,64        0,65        066      ],67      "status_distribution": {68        "delivered": 0,69        "in_transit": 0,70        "processing": 0,71        "received": 0,72        "pending_claim": 073      },74      "shipping_volume_bar": {75        "dates": [76          "2025-12-08",77          "2025-12-09",78          "2025-12-10",79          "2025-12-11",80          "2025-12-12",81          "2025-12-13",82          "2025-12-14"83        ],84        "received": [85          0,86          0,87          0,88          0,89          0,90          0,91          092        ],93        "delivered": [94          0,95          0,96          0,97          0,98          0,99          0,100          0101        ]102      }103    },104    "revenue": {105      "domestic_fees": 0,106      "international_fees": 0,107      "delivery_fees": 0,108      "total_revenue": 0109    },110    "metrics": {111      "delivered": {112        "value": 0,113        "percentage": 0114      },115      "in_transit": {116        "value": 0,117        "percentage": 0118      },119      "processing": {120        "value": 0,121        "percentage": 0122      },123      "pending_claim": {124        "value": 0,125        "percentage": 0126      }127    },128    "top_stores": [129      {130        "store_name": "",131        "package_count": 0132      }133    ],134    "recent_packages": [135      {136        "tracking_number": "",137        "store_name": "",138        "customer_name": "",139        "weight_kg": 0,140        "status": "Received",141        "status_color": "orange",142        "payment_status": "Pending",143        "created_at": "2025-12-14T00:00:00.000Z"144      }145    ],146    "recent_activity": [147      {148        "id": "00000000-0000-0000-0000-000000000001",149        "message": "",150        "type": "received",151        "timestamp": "2025-12-14T00:00:00.000Z"152      }153    ]154  }155}
401 Unauthorized (middleware/auth)401Unauthorized
Response body
json
1{2  "success": false,3  "message": "invalid_token",4  "data": null5}
405 Method Not Allowed405Method Not Allowed
Response body
json
1{2  "success": false,3  "message": "method_not_allowed",4  "data": null5}
500 Internal Server Error (router catch)500Internal Server Error
Response body
json
1{2  "success": false,3  "message": "Internal Server Error",4  "data": null5}
200 Response (JSON)200
Response body
json
1type ResponseEnvelope<T> = { success: boolean; message: string; data: T | null };23type ExpressDashboardEnvelope = ResponseEnvelope<ExpressDashboardResponse>;45interface ExpressDashboardResponse {6  kpis: {7    total_packages: KPIItem;8    delivered: KPIItem;9    in_transit: KPIItem;10    pending_claim: KPIItem;11  };12  charts: {13    weekly_volume: number[];14    total_weight_kg: number[];15    avg_delivery_time: number[];16    status_distribution: { delivered: number; in_transit: number; processing: number; received: number; pending_claim: number };17    shipping_volume_bar: { dates: string[]; received: number[]; delivered: number[] };18  };19  revenue: { domestic_fees: number; international_fees: number; delivery_fees: number; total_revenue: number };20  metrics: {21    delivered: { value: number; percentage: number };22    in_transit: { value: number; percentage: number };23    processing: { value: number; percentage: number };24    pending_claim: { value: number; percentage: number };25  };26  top_stores: Array<{ store_name: string; package_count: number }>;27  recent_packages: Array<{ tracking_number: string; store_name: string; customer_name: string; weight_kg: number; status: 'Delivered'|'In transit'|'Processing'|'Received'|'Claimed'; status_color: 'green'|'red'|'blue'|'orange'|'purple'; payment_status: 'Paid'|'Pending'|'Unpaid'; created_at: string }>;28  recent_activity: Array<{ id: string; message: string; type: 'delivered'|'in_transit'|'processing'|'received'|'claimed'; timestamp: string }>;29}3031type KPIItem = { value: number; trend_percentage: number; trend_direction: 'up'|'down'; chart_data: number[] };

Tenant App Report (Common)

Reporting, Audit & Webhooks / Report
GET/report/app

Summary GET /report/app Tenant application (common) report that returns the exact dashboard widget payload the portal uses. Storage model - One row per tenant in public.tenant_app_report (refreshed nightly). Auth - Header Authorization: Bearer {{access_token}} 200 Response (JSON) Body is wrapped: Error responses Same envelope: - { "success": false, "message": string, "data": null }

Send a bearer token in the Authorization header for an authenticated OnDi user session.
Request
curl
1curl --request GET "$ONDI_BASE_URL/report/app" \2  --header "Authorization: Bearer {{access_token}}"

Headers

AuthorizationOptional
header string

Bearer {{access_token}}

Responses

200 OK200OK
Response body
json
1{2  "success": true,3  "message": "",4  "data": {5    "delivery_overview": {6      "total_deliveries_period": 0,7      "completed_deliveries_period": 0,8      "pending_deliveries_current": 0,9      "in_progress_deliveries_current": 0,10      "failed_deliveries_period": 0,11      "delivery_success_rate_period": 0,12      "avg_delivery_time_minutes_period": 0,13      "avg_pickup_to_delivery_time_minutes_period": 014    },15    "cod_summary": {16      "total_cod_collected_period": 0,17      "total_cod_remitted_period": 0,18      "total_cod_outstanding_current": 0,19      "total_cod_settled_by_wallet_period": 020    },21    "warehouse_overview": {22      "total_items_received_period": 0,23      "total_items_dispatched_period": 0,24      "current_total_stock_items": 0,25      "current_total_stock_value": 0,26      "overall_capacity_utilization_percent_current": 0,27      "total_picking_orders_completed_period": 0,28      "total_batches_dispatched_period": 029    },30    "warehouse_activity_by_warehouse": {31      "data": [32        {33          "warehouse_id": "00000000-0000-0000-0000-000000000001",34          "warehouse_name": "",35          "items_received_period": 0,36          "items_dispatched_period": 0,37          "current_stock_items": 0,38          "capacity_utilization_percent": 039        }40      ],41      "columns": [42        {43          "field": "warehouse_name",44          "label": "Warehouse",45          "description": "Warehouse name",46          "type": "string"47        },48        {49          "field": "items_received_period",50          "label": "Received",51          "description": "Items received in period",52          "type": "number"53        },54        {55          "field": "items_dispatched_period",56          "label": "Dispatched",57          "description": "Items dispatched in period",58          "type": "number"59        },60        {61          "field": "current_stock_items",62          "label": "Stock",63          "description": "Current stock items",64          "type": "number"65        },66        {67          "field": "capacity_utilization_percent",68          "label": "Capacity %",69          "description": "Capacity utilization percent",70          "type": "number"71        }72      ]73    },74    "warehouse_stock_by_category": {75      "data": [76        {77          "category_id": "00000000-0000-0000-0000-000000000010",78          "category_name": {79            "en": ""80          },81          "stock_items": 082        }83      ],84      "columns": [85        {86          "field": "category_name",87          "label": "Category",88          "description": "Inventory category",89          "type": "jsonb"90        },91        {92          "field": "stock_items",93          "label": "Stock Items",94          "description": "Stock items in category",95          "type": "number"96        }97      ]98    }99  }100}
401 Unauthorized (middleware/auth)401Unauthorized
Response body
json
1{2  "success": false,3  "message": "invalid_token",4  "data": null5}
404 Not Found (report not ready)404Not Found
Response body
json
1{2  "success": false,3  "message": "report_not_ready",4  "data": null5}
405 Method Not Allowed405Method Not Allowed
Response body
json
1{2  "success": false,3  "message": "method_not_allowed",4  "data": null5}
500 Internal Server Error (router catch)500Internal Server Error
Response body
json
1{2  "success": false,3  "message": "Internal Server Error",4  "data": null5}
200 Response (JSON)200
Response body
json
1type ResponseEnvelope<T> = { success: boolean; message: string; data: T | null };23type TenantAppDashboardEnvelope = ResponseEnvelope<TenantAppDashboardResponse>;45interface TenantAppDashboardResponse {6  delivery_overview: {7    total_deliveries_period: number;8    completed_deliveries_period: number;9    pending_deliveries_current: number;10    in_progress_deliveries_current: number;11    failed_deliveries_period: number;12    delivery_success_rate_period: number;13    avg_delivery_time_minutes_period: number;14    avg_pickup_to_delivery_time_minutes_period: number;15  };16  cod_summary: {17    total_cod_collected_period: number;18    total_cod_remitted_period: number;19    total_cod_outstanding_current: number;20    total_cod_settled_by_wallet_period: number;21  };22  warehouse_overview: {23    total_items_received_period: number;24    total_items_dispatched_period: number;25    current_total_stock_items: number;26    current_total_stock_value: number;27    overall_capacity_utilization_percent_current: number;28    total_picking_orders_completed_period: number;29    total_batches_dispatched_period: number;30  };31  warehouse_activity_by_warehouse: {32    data: Array<{33      warehouse_id: string;34      warehouse_name: string;35      items_received_period: number;36      items_dispatched_period: number;37      current_stock_items: number;38      capacity_utilization_percent: number;39    }>;40    columns: Array<{ field: string; label: string; description: string; type: string }>;41  };42  warehouse_stock_by_category: {43    data: Array<{ category_id: string; category_name: unknown; stock_items: number }>;44    columns: Array<{ field: string; label: string; description: string; type: string }>;45  };46}

Trigger Daily Aggregation (Cron)

Reporting, Audit & Webhooks / Report
POST/report

Summary POST /report Triggers the daily aggregation job. Auth - Header Authorization: Bearer {{access_token}} Request body (JSON) - Optional: If omitted/unparseable, server defaults to yesterday (UTC via toISOString().split('T')[0]). 200 Response (JSON) Wrapped envelope: Error responses Same envelope: - { "success": false, "message": string, "data": null }

Send a bearer token in the Authorization header for an authenticated OnDi user session.
Request
curl
1curl --request POST "$ONDI_BASE_URL/report" \2  --header "Authorization: Bearer {{access_token}}" \3  --header "Content-Type: application/json" \4  --header "Content-Type: application/json" \5  --data '{6  "date": "2025-12-12"7}'
Request body
json
1{2  "date": "2025-12-12"3}

Request body fields

dateExample
string

Example field from the request body.

Headers

AuthorizationOptional
header string

Bearer {{access_token}}

Content-TypeOptional
header string

application/json

Responses

200 OK200OK
Response body
json
1{2  "success": true,3  "message": "Lifetime report refresh completed",4  "data": {5    "duration_ms": "12.34",6    "tenants_ok": 0,7    "tenants_failed": 0,8    "failed": []9  }10}
500 RPC Execution Failed500Internal Server Error
Response body
json
1{2  "success": false,3  "message": "RPC Execution",4  "data": null5}
500 Unexpected Scheduler Error500Internal Server Error
Response body
json
1{2  "success": false,3  "message": "Unknown",4  "data": null5}
401 Unauthorized (middleware/auth)401Unauthorized
Response body
json
1{2  "success": false,3  "message": "invalid_token",4  "data": null5}
200 Response (JSON)200
Response body
json
1type ResponseEnvelope<T> = { success: boolean; message: string; data: T | null };23type CronTriggerData = {4  duration_ms: string;5  tenants_ok: number;6  tenants_failed: number;7  failed: Array<{ tenant_id: string; ok: boolean; error?: string }>;8};910type CronTriggerEnvelope = ResponseEnvelope<CronTriggerData>;

Legacy - Delivery Overview

Reporting, Audit & Webhooks / Report
GET/report/tenant/delivery/overview

Summary GET /report/tenant/delivery/overview Legacy route. Router maps any /report/tenant/delivery/* to module delivery. Effective route - GET /report/delivery Auth - Header Authorization: Bearer {{access_token}} 200 Response (JSON) Same as Delivery Report but wrapped envelope: - { success: true, message: "", data: DeliveryDashboardResponse } Error responses Same envelope: - { success: false, message: string, data: null }

Send a bearer token in the Authorization header for an authenticated OnDi user session.
Request
curl
1curl --request GET "$ONDI_BASE_URL/report/tenant/delivery/overview" \2  --header "Authorization: Bearer {{access_token}}"

Headers

AuthorizationOptional
header string

Bearer {{access_token}}

Responses

200 OK (same as Delivery Report)200OK
Response body
json
1{2  "success": true,3  "message": "",4  "data": {5    "kpis": {6      "total_deliveries": {7        "value": 0,8        "trend_percentage": 0,9        "trend_direction": "up",10        "chart_data": [11          0,12          0,13          0,14          0,15          0,16          0,17          018        ]19      },20      "completed": {21        "value": 0,22        "trend_percentage": 0,23        "trend_direction": "up",24        "chart_data": [25          0,26          0,27          0,28          0,29          0,30          0,31          032        ]33      },34      "in_progress": {35        "value": 0,36        "trend_percentage": 0,37        "trend_direction": "up",38        "chart_data": []39      },40      "failed": {41        "value": 0,42        "trend_percentage": 0,43        "trend_direction": "up",44        "chart_data": [45          0,46          0,47          0,48          0,49          0,50          0,51          052        ]53      }54    },55    "charts": {56      "status_breakdown": {57        "completed": 0,58        "in_progress": 0,59        "pending": 0,60        "failed": 061      },62      "weekly_performance": {63        "dates": [64          "2025-12-08",65          "2025-12-09",66          "2025-12-10",67          "2025-12-11",68          "2025-12-12",69          "2025-12-13",70          "2025-12-14"71        ],72        "completed": [73          0,74          0,75          0,76          0,77          0,78          0,79          080        ],81        "failed": [82          0,83          0,84          0,85          0,86          0,87          0,88          089        ]90      },91      "metrics_trends": {92        "success_rate": [93          0,94          0,95          0,96          0,97          0,98          0,99          0100        ],101        "avg_delivery_time": [102          0,103          0,104          0,105          0,106          0,107          0,108          0109        ],110        "active_drivers": [111          0,112          0,113          0,114          0,115          0,116          0,117          0118        ]119      }120    },121    "performance": {122      "success_rate": {123        "value": 0,124        "percentage": 0125      },126      "on_time_delivery": {127        "value": 0,128        "percentage": 0129      },130      "customer_satisfaction": {131        "value": 0,132        "percentage": 0133      },134      "first_attempt_success": {135        "value": 0,136        "percentage": 0137      }138    },139    "cod": {140      "collected": 0,141      "remitted": 0,142      "outstanding": 0,143      "wallet_settled": 0144    },145    "top_drivers": [146      {147        "driver_id": "00000000-0000-0000-0000-000000000001",148        "name": "",149        "avatar_url": "",150        "completed_jobs": 0,151        "failed_jobs": 0,152        "avg_time_minutes": 0153      }154    ],155    "zone_stats": [156      {157        "zone_id": "11111111-1111-1111-1111-111111111111",158        "zone_code": "ZN-001",159        "zone_name": "Central Zone",160        "total_deliveries": 0,161        "total_inbound_deliveries": 0,162        "total_outbound_deliveries": 0,163        "total_completed_deliveries": 0164      }165    ],166    "recent_activity": [167      {168        "id": "00000000-0000-0000-0000-000000000100",169        "order_ref": "",170        "type": "pending",171        "message": "",172        "timestamp": "2025-12-14T00:00:00.000Z"173      }174    ]175  }176}

Driver Performance

Reporting, Audit & Webhooks / Report
GET/report/tenant/delivery/driver-performance?tenant_id={{tenant_id}}&language=en

Summary GET /report/tenant/delivery/driver-performance Returns driver performance metrics computed by the nightly report scheduler. Data is read from the driver_performance_metrics table (populated during cron). Auth - Header Authorization: Bearer {{access_token}} Response fields | Field | Type | Description | |-------|------|-------------| | driver_id | UUID | Driver identifier | | driver_name | string | Driver's full name | | total_deliveries | integer | Total terminal-status assignments (completed + failed) | | successful_deliveries | integer | Assignments with status completed/delivered/closed | | failed_deliveries | integer | Assignments with status failed/cancelled/rejected/returned | | on_time_deliveries | integer | Completed before scheduled_delivery_time | | late_deliveries | integer | Completed after scheduled_delivery_time | | success_rate | decimal | (successful / total) × 100 | | on_time_rate | decimal | (on_time / (on_time + late)) × 100 | | average_delivery_time_minutes | integer | Mean minutes from pickup to drop-off | | total_distance_km | decimal | Sum of distance_traveled across assignments | | km_per_delivery | decimal | total_distance / successful_deliveries | | total_delivery_value | decimal | Sum of (cod_amount + delivery_fee) for completed | KPI Cards (aggregate across all drivers) - Total Deliveries — sum of total_deliveries - Overall Success Rate — sum(successful) / sum(total) × 100 - Overall On-Time Rate — sum(on_time) / sum(on_time + late) × 100 - Avg Delivery Time — weighted average of average_delivery_time_minutes - Total Revenue Handled — sum of total_delivery_value - Active Drivers — count of drivers with total_deliveries > 0 Column Filters (planned) All numeric columns support {min, max} range filtering: - total_deliveries, success_rate, on_time_rate, average_delivery_time, km_per_delivery, total_delivery_value Error responses - 400: { "success": false, "message": "tenantId missing in request context", "data": null } - 500: { "success": false, "message": "query_failed", "data": null }

Send a bearer token in the Authorization header for an authenticated OnDi user session.
Request
curl
1curl --request GET "$ONDI_BASE_URL/report/tenant/delivery/driver-performance?tenant_id={{tenant_id}}&language=en" \2  --header "Authorization: Bearer {{access_token}}"

Query parameters

tenant_idOptional
query string

{{tenant_id}}

languageOptional
query string

en

Headers

AuthorizationOptional
header string

Bearer {{access_token}}

Responses

200 OK200OK
Response body
json
1{2  "success": true,3  "message": "",4  "data": [5    {6      "driver_id": "a1b2c3d4-0000-0000-0000-000000000001",7      "driver_name": "Ahmed Ali",8      "total_deliveries": 120,9      "successful_deliveries": 108,10      "failed_deliveries": 12,11      "on_time_deliveries": 95,12      "late_deliveries": 13,13      "success_rate": 90,14      "on_time_rate": 87.96,15      "average_delivery_time_minutes": 28,16      "total_distance_km": 840.5,17      "km_per_delivery": 7.78,18      "total_delivery_value": 4500019    },20    {21      "driver_id": "a1b2c3d4-0000-0000-0000-000000000002",22      "driver_name": "Mohammed Saeed",23      "total_deliveries": 95,24      "successful_deliveries": 90,25      "failed_deliveries": 5,26      "on_time_deliveries": 82,27      "late_deliveries": 8,28      "success_rate": 94.74,29      "on_time_rate": 91.11,30      "average_delivery_time_minutes": 24,31      "total_distance_km": 620.3,32      "km_per_delivery": 6.89,33      "total_delivery_value": 3850034    }35  ]36}
200 OK (empty - no data yet)200OK
Response body
json
1{2  "success": true,3  "message": "",4  "data": []5}
400 Bad Request (missing tenant)400Bad Request
Response body
json
1{2  "success": false,3  "message": "tenantId missing in request context",4  "data": null5}
500 Internal Server Error500Internal Server Error
Response body
json
1{2  "success": false,3  "message": "query_failed",4  "data": null5}