# Pathao Webhook Integration Reference

## Overview

Pathao sends a `POST` request to your Callback URL for each delivery status event. You configure a **Callback URL** and a **Webhook Secret** in the Pathao merchant dashboard.

---

## URL Requirements

| # | Requirement |
|---|-------------|
| 1 | URL must be publicly reachable |
| 2 | Resolved within 3 redirections |
| 3 | Valid SSL certificate (if using HTTPS) |
| 4 | Must respond within **10 seconds** |
| 5 | Must return **HTTP 202** for the handshake event (`webhook_integration`) |
| 6 | Must return response header `X-Pathao-Merchant-Webhook-Integration-Secret` on every request |

---

## Handshake Flow

When you register your webhook URL, Pathao sends a verification request:

**Request body:**
```json
{ "event": "webhook_integration" }
```

**Required response:**
```
HTTP 202 Accepted
X-Pathao-Merchant-Webhook-Integration-Secret: f3992ecc-59da-4cbe-a049-a13da2018d51
```

> The secret header value must match **exactly**: `f3992ecc-59da-4cbe-a049-a13da2018d51`

---

## Headers Received on Every Event

| Header | Value |
|--------|-------|
| `X-PATHAO-Signature` | Your webhook secret (set during integration) |
| `Content-Type` | `application/json` |

---

## Required Response Header (All Events)

Every event response (not just handshake) must include:

```
X-Pathao-Merchant-Webhook-Integration-Secret: f3992ecc-59da-4cbe-a049-a13da2018d51
```

---

## Field Reference

### Common fields (present on all order events)

| Field | Type | Description |
|-------|------|-------------|
| `consignment_id` | string | Pathao consignment ID |
| `merchant_order_id` | string | Your order ID |
| `store_id` | integer | Pathao store ID |
| `event` | string | Event key (see below) |
| `updated_at` | string | `YYYY-MM-DD HH:mm:ss` |
| `timestamp` | string | ISO 8601 with timezone |

### Conditional fields

| Field | Type | Present on | Notes |
|-------|------|-----------|-------|
| `delivery_fee` | float | `order.created`, `order.updated`, `order.pickup-requested` | |
| `collected_amount` | float | `order.delivered`, `order.partial-delivery`, `order.returned`, `order.paid-return`, `order.exchanged`, `order.return-id-created`, `order.return-in-transit`, `order.returned-to-merchant` | |
| `reason` | string | `order.partial-delivery`, `order.returned`, `order.delivery-failed`, `order.on-hold`, `order.paid-return`, `order.exchanged`, `order.return-id-created`, `order.return-in-transit`, `order.returned-to-merchant` | **Optional** — may be absent even when supported |
| `invoice_id` | string | `order.paid` | |
| `return_consignment_id` | string | `order.return-id-created`, `order.return-in-transit`, `order.returned-to-merchant` | |
| `return_type` | string | `order.return-id-created`, `order.return-in-transit`, `order.returned-to-merchant` | Enum: `return \| paid-return \| exchange \| partial-delivery` |
| `store_name` | string | `store.created`, `store.updated` | |
| `store_address` | string | `store.created`, `store.updated` | |
| `is_active` | integer | `store.created`, `store.updated` | `0` or `1` |

---

## All Events & Payloads

### Order Lifecycle

#### `order.created`
```json
{
  "consignment_id": "DL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "updated_at": "2024-12-27 23:49:43",
  "timestamp": "2024-12-27T17:49:43+00:00",
  "store_id": 130820,
  "event": "order.created",
  "delivery_fee": 83.46
}
```

#### `order.updated`
```json
{
  "consignment_id": "DL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "updated_at": "2024-12-27 23:50:16",
  "timestamp": "2024-12-27T17:50:16+00:00",
  "store_id": 130820,
  "event": "order.updated",
  "delivery_fee": 83.46
}
```

#### `order.pickup-requested`
```json
{
  "consignment_id": "DL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "updated_at": "2024-12-27 23:50:32",
  "timestamp": "2024-12-27T17:50:32+00:00",
  "store_id": 130820,
  "event": "order.pickup-requested",
  "delivery_fee": 83.46
}
```

#### `order.assigned-for-pickup`
```json
{
  "consignment_id": "DL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "updated_at": "2024-12-27 23:51:01",
  "timestamp": "2024-12-27T17:51:01+00:00",
  "store_id": 130820,
  "event": "order.assigned-for-pickup"
}
```

#### `order.picked`
```json
{
  "consignment_id": "DL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "updated_at": "2024-12-27 23:51:17",
  "timestamp": "2024-12-27T17:51:17+00:00",
  "store_id": 130820,
  "event": "order.picked"
}
```

#### `order.pickup-failed`
```json
{
  "consignment_id": "DL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "updated_at": "2024-12-27 23:51:33",
  "timestamp": "2024-12-27T17:51:33+00:00",
  "store_id": 130820,
  "event": "order.pickup-failed"
}
```

#### `order.pickup-cancelled`
```json
{
  "consignment_id": "DL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "updated_at": "2024-12-27 23:51:49",
  "timestamp": "2024-12-27T17:51:49+00:00",
  "store_id": 130820,
  "event": "order.pickup-cancelled"
}
```

#### `order.at-the-sorting-hub`
```json
{
  "consignment_id": "DL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "updated_at": "2024-12-27 23:52:15",
  "timestamp": "2024-12-27T17:52:15+00:00",
  "store_id": 130820,
  "event": "order.at-the-sorting-hub"
}
```

#### `order.in-transit`
```json
{
  "consignment_id": "DL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "updated_at": "2024-12-27 23:52:32",
  "timestamp": "2024-12-27T17:52:32+00:00",
  "store_id": 130820,
  "event": "order.in-transit"
}
```

#### `order.received-at-last-mile-hub`
```json
{
  "consignment_id": "DL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "updated_at": "2024-12-27 23:52:48",
  "timestamp": "2024-12-27T17:52:48+00:00",
  "store_id": 130820,
  "event": "order.received-at-last-mile-hub"
}
```

#### `order.assigned-for-delivery`
```json
{
  "consignment_id": "DL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "updated_at": "2024-12-27 23:53:05",
  "timestamp": "2024-12-27T17:53:05+00:00",
  "store_id": 130820,
  "event": "order.assigned-for-delivery"
}
```

#### `order.delivered`
```json
{
  "consignment_id": "DL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "updated_at": "2024-12-27 23:53:23",
  "timestamp": "2024-12-27T17:53:23+00:00",
  "store_id": 130820,
  "event": "order.delivered",
  "collected_amount": 60
}
```

#### `order.partial-delivery`
```json
{
  "consignment_id": "DL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "updated_at": "2024-12-27 23:53:45",
  "timestamp": "2024-12-27T17:53:45+00:00",
  "store_id": 130820,
  "event": "order.partial-delivery",
  "collected_amount": 60,
  "reason": "(optional — may be absent)"
}
```

#### `order.delivery-failed`
```json
{
  "consignment_id": "DL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "updated_at": "2024-12-27 23:54:21",
  "timestamp": "2024-12-27T17:54:21+00:00",
  "store_id": 130820,
  "event": "order.delivery-failed",
  "reason": "(optional — may be absent)"
}
```

#### `order.on-hold`
```json
{
  "consignment_id": "DL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "updated_at": "2024-12-27 23:54:46",
  "timestamp": "2024-12-27T17:54:46+00:00",
  "store_id": 130820,
  "event": "order.on-hold",
  "reason": "(optional — may be absent)"
}
```

---

### Payment

#### `order.paid`
```json
{
  "consignment_id": "DL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "updated_at": "2024-12-27 23:55:01",
  "timestamp": "2024-12-27T17:55:01+00:00",
  "store_id": 130820,
  "event": "order.paid",
  "invoice_id": "121224IBW19790"
}
```

---

### Return & Exchange

#### `order.returned`
```json
{
  "consignment_id": "DL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "updated_at": "2024-12-27 23:54:02",
  "timestamp": "2024-12-27T17:54:02+00:00",
  "store_id": 130820,
  "event": "order.returned",
  "reason": "(optional — may be absent)"
}
```

#### `order.paid-return`
```json
{
  "consignment_id": "DL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "updated_at": "2024-12-27 23:55:17",
  "timestamp": "2024-12-27T17:55:17+00:00",
  "store_id": 130820,
  "event": "order.paid-return",
  "collected_amount": 60,
  "reason": "(optional — may be absent)"
}
```

#### `order.exchanged`
```json
{
  "consignment_id": "DL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "updated_at": "2024-12-27 23:55:34",
  "timestamp": "2024-12-27T17:55:34+00:00",
  "store_id": 130820,
  "event": "order.exchanged",
  "collected_amount": 60,
  "reason": "(optional — may be absent)"
}
```

#### `order.return-id-created`
```json
{
  "event": "order.return-id-created",
  "consignment_id": "DL121224VS8TTJ",
  "return_consignment_id": "RL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "store_id": 130820,
  "collected_amount": 60,
  "return_type": "return|paid-return|exchange|partial-delivery",
  "reason": "(optional — may be absent)",
  "updated_at": "2024-12-27 23:55:34",
  "timestamp": "2024-12-27T17:55:34+00:00"
}
```

#### `order.return-in-transit`
```json
{
  "event": "order.return-in-transit",
  "consignment_id": "DL121224VS8TTJ",
  "return_consignment_id": "RL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "store_id": 130820,
  "collected_amount": 60,
  "return_type": "return|paid-return|exchange|partial-delivery",
  "reason": "(optional — may be absent)",
  "updated_at": "2024-12-27 23:55:34",
  "timestamp": "2024-12-27T17:55:34+00:00"
}
```

#### `order.returned-to-merchant`
```json
{
  "event": "order.returned-to-merchant",
  "consignment_id": "DL121224VS8TTJ",
  "return_consignment_id": "RL121224VS8TTJ",
  "merchant_order_id": "TS-123",
  "store_id": 130820,
  "collected_amount": 60,
  "return_type": "return|paid-return|exchange|partial-delivery",
  "reason": "(optional — may be absent)",
  "updated_at": "2024-12-27 23:55:34",
  "timestamp": "2024-12-27T17:55:34+00:00"
}
```

---

### Store

#### `store.created`
```json
{
  "store_id": 1,
  "store_name": "Test Store",
  "store_address": "Test store address",
  "is_active": 1,
  "event": "store.created",
  "updated_at": "2024-12-27 23:55:34",
  "timestamp": "2024-12-27T17:55:34+00:00"
}
```

#### `store.updated`
```json
{
  "store_id": 1,
  "store_name": "Test Store",
  "store_address": "Test store address",
  "is_active": 1,
  "event": "store.updated",
  "updated_at": "2024-12-27 23:55:34",
  "timestamp": "2024-12-27T17:55:34+00:00"
}
```

---

## Event Key Quick Reference

| Event name | Event key |
|------------|-----------|
| Order created | `order.created` |
| Order updated | `order.updated` |
| Pickup requested | `order.pickup-requested` |
| Assigned for pickup | `order.assigned-for-pickup` |
| Pickup | `order.picked` |
| Pickup failed | `order.pickup-failed` |
| Pickup cancelled | `order.pickup-cancelled` |
| At the sorting hub | `order.at-the-sorting-hub` |
| In transit | `order.in-transit` |
| Received at last mile hub | `order.received-at-last-mile-hub` |
| Assigned for delivery | `order.assigned-for-delivery` |
| Delivered | `order.delivered` |
| Partial delivery | `order.partial-delivery` |
| Return | `order.returned` |
| Delivery failed | `order.delivery-failed` |
| On hold | `order.on-hold` |
| Payment invoice | `order.paid` |
| Paid return | `order.paid-return` |
| Exchange | `order.exchanged` |
| Store created | `store.created` |
| Store updated | `store.updated` |
| Return id created | `order.return-id-created` |
| Return in transit | `order.return-in-transit` |
| Returned to merchant | `order.returned-to-merchant` |

---

## Implementation Notes

- Always use optional chaining / null checks for the `reason` field — it may be absent even on events that support it.
- `return_type` is a union: `"return" | "paid-return" | "exchange" | "partial-delivery"` — model as an enum in typed languages.
- The handshake event (`webhook_integration`) requires **exactly HTTP 202**; all other events require any **2xx** with the secret header.
- Echo back `X-Pathao-Merchant-Webhook-Integration-Secret` on **every** response, not just the handshake.
- Keep your handler under 10 seconds — offload heavy processing to a queue.
