{
  "info": {
    "_postman_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "name": "Lynq Partner API",
    "description": "Lynq **partner HTTP API** (quotes, orders, webhooks).\n\n**`baseUrl`:** Partner API base URL for your environment (scheme + host + `/partner`, no trailing slash). Example UAT: `https://uat-api.lynq-sa.com/partner`. Request paths are appended directly (for example `POST {{baseUrl}}/quotes`).\n\n**Local docker-compose:** `baseUrl` = `http://localhost:8003/api/v1` (direct to order-worker, includes the internal prefix).\n\n**`apiKey` and `clientId`:** Use the values from your Lynq onboarding package. Send them on every request as headers `X-API-Key` and `X-Client-ID`.\n\n**Multi-leg orders:** Use **`POST …/quotes/multi-leg`** then **`POST …/orders/multi-leg`** for 1–10 pickups and one dropoff. Per-leg webhooks: `order.leg.picked_up`, `order.leg.skipped`.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "variable": [
    { "key": "baseUrl", "value": "https://uat-api.lynq-sa.com/partner" },
    { "key": "apiKey", "value": "" },
    { "key": "clientId", "value": "" },
    { "key": "order_ulid", "value": "01REPLACE_WITH_ORDER_ULID000000" },
    { "key": "partner_order_number", "value": "my-idempotency-key-001" },
    { "key": "multi_leg_quote_id", "value": "" },
    { "key": "webhook_id", "value": "01REPLACE_WITH_WEBHOOK_ULID00000" }
  ],
  "item": [
    {
      "name": "Quotes",
      "item": [
        {
          "name": "Create quote",
          "request": {
            "method": "POST",
            "header": [
              { "key": "X-API-Key", "value": "{{apiKey}}" },
              { "key": "X-Client-ID", "value": "{{clientId}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"pickup\": {\n    \"lat\": 24.7136,\n    \"lng\": 46.6753,\n    \"address\": \"RRRD2929\"\n  },\n  \"dropoff\": {\n    \"lat\": 24.768,\n    \"lng\": 46.709,\n    \"address\": \"RRRR1234\"\n  },\n  \"vehicle_type\": \"motorcycle\"\n}"
            },
            "url": "{{baseUrl}}/quotes",
            "description": "Single pickup → single dropoff. Returns `quote_id`, fee breakdown, `expires_at`. **503** with `distance_unavailable` when pricing or distance inputs cannot be resolved."
          }
        },
        {
          "name": "Create multi-leg quote",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (pm.response.code === 200) {",
                  "  const body = pm.response.json();",
                  "  if (body.quote_id) {",
                  "    pm.collectionVariables.set('multi_leg_quote_id', body.quote_id);",
                  "  }",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "X-API-Key", "value": "{{apiKey}}" },
              { "key": "X-Client-ID", "value": "{{clientId}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"pickups\": [\n    {\n      \"lat\": 24.7136,\n      \"lng\": 46.6753,\n      \"short_address\": \"RRRD2929\",\n      \"address\": \"Pickup A — Al Olaya\",\n      \"district\": \"Al Olaya\",\n      \"city\": \"Riyadh\",\n      \"country\": \"Saudi Arabia\",\n      \"contact_name\": \"Sender A\",\n      \"contact_phone\": \"+966500000001\"\n    },\n    {\n      \"lat\": 24.72,\n      \"lng\": 46.68,\n      \"short_address\": \"RRRD2930\",\n      \"address\": \"Pickup B — secondary stop\",\n      \"district\": \"Al Olaya\",\n      \"city\": \"Riyadh\",\n      \"country\": \"Saudi Arabia\",\n      \"contact_name\": \"Sender B\",\n      \"contact_phone\": \"+966500000002\"\n    }\n  ],\n  \"dropoff\": {\n    \"lat\": 24.768,\n    \"lng\": 46.709,\n    \"short_address\": \"RRRR1234\",\n    \"address\": \"Riyadh dropoff\",\n    \"district\": \"Al Malqa\",\n    \"city\": \"Riyadh\",\n    \"country\": \"Saudi Arabia\",\n    \"contact_name\": \"Recipient\",\n    \"contact_phone\": \"+966501234567\"\n  },\n  \"vehicle_type\": \"motorcycle\",\n  \"package\": {\n    \"description\": \"Documents\",\n    \"weight_kg\": 2.0,\n    \"declared_value_sar\": 150\n  }\n}"
            },
            "url": "{{baseUrl}}/quotes/multi-leg",
            "description": "Multi-pickup (1–10) → single dropoff. Returns `quote_id`, `ordered_stops` (optimized pickup sequence + dropoff), `distance_km`, `total_sar`, and fee `breakdown` (includes optional `pickup_surcharge_sar` when configured for your account).\n\n**422** if `pickups` is empty or has more than 10 stops. **503** `distance_unavailable` when route distance cannot be computed."
          }
        }
      ]
    },
    {
      "name": "Orders",
      "item": [
        {
          "name": "List orders",
          "request": {
            "method": "GET",
            "header": [
              { "key": "X-API-Key", "value": "{{apiKey}}" },
              { "key": "X-Client-ID", "value": "{{clientId}}" }
            ],
            "url": "{{baseUrl}}/orders?skip=0&limit=50&status_filter=pending",
            "description": "Optional query params: `skip` (default 0), `limit` (max 100), `status_filter` (omit for all statuses; e.g. pending, confirmed, delivered)."
          }
        },
        {
          "name": "Create order (202 Accepted)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "X-API-Key", "value": "{{apiKey}}" },
              { "key": "X-Client-ID", "value": "{{clientId}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"quote_id\": null,\n  \"pickup\": {\n    \"lat\": 24.7136,\n    \"lng\": 46.6753,\n    \"short_address\": \"RRRD2929\",\n    \"contact_name\": \"Ahmed\",\n    \"contact_phone\": \"0594939405\"\n  },\n  \"dropoff\": {\n    \"lat\": 24.768,\n    \"lng\": 46.709,\n    \"short_address\": \"RRRR1234\",\n    \"contact_name\": \"Saleem\",\n    \"contact_phone\": \"+966595848384\"\n  }\n}"
            },
            "url": "{{baseUrl}}/orders",
            "description": "Single pickup → single dropoff. Set `quote_id` to a 26-char ULID from **Create quote** (or `null` to re-quote). **Required per stop:** `lat`, `lng`, `short_address`. **Required dropoff only:** `contact_name`, `contact_phone`.\n\n**Optional top-level:** `partner_order_number`, `notes`, `package` (`description`, `weight_kg`, `declared_value_sar` — drives Lynq order **total**).\n\n**Optional per stop (pickup + dropoff):** `address`, `building_no`, `street`, `secondary_no`, `district`, `postal_code`, `city`, `country`; pickup may omit `contact_*`. Omitted address components may be filled server-side when configured."
          }
        },
        {
          "name": "Create multi-leg order (202 Accepted)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (pm.response.code === 202) {",
                  "  const body = pm.response.json();",
                  "  if (body.id) {",
                  "    pm.collectionVariables.set('order_ulid', body.id);",
                  "  }",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "X-API-Key", "value": "{{apiKey}}" },
              { "key": "X-Client-ID", "value": "{{clientId}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"partner_order_number\": \"{{partner_order_number}}-multi\",\n  \"quote_id\": \"{{multi_leg_quote_id}}\",\n  \"pickups\": [\n    {\n      \"lat\": 24.7136,\n      \"lng\": 46.6753,\n      \"short_address\": \"RRRD2929\",\n      \"address\": \"Pickup A — Al Olaya\",\n      \"district\": \"Al Olaya\",\n      \"city\": \"Riyadh\",\n      \"country\": \"Saudi Arabia\",\n      \"contact_name\": \"Sender A\",\n      \"contact_phone\": \"+966500000001\"\n    },\n    {\n      \"lat\": 24.72,\n      \"lng\": 46.68,\n      \"short_address\": \"RRRD2930\",\n      \"address\": \"Pickup B — secondary stop\",\n      \"district\": \"Al Olaya\",\n      \"city\": \"Riyadh\",\n      \"country\": \"Saudi Arabia\",\n      \"contact_name\": \"Sender B\",\n      \"contact_phone\": \"+966500000002\"\n    }\n  ],\n  \"dropoff\": {\n    \"lat\": 24.768,\n    \"lng\": 46.709,\n    \"short_address\": \"RRRR1234\",\n    \"address\": \"Riyadh dropoff\",\n    \"district\": \"Al Malqa\",\n    \"city\": \"Riyadh\",\n    \"country\": \"Saudi Arabia\",\n    \"contact_name\": \"Recipient\",\n    \"contact_phone\": \"+966501234567\"\n  },\n  \"notes\": \"Multi-leg partner order\",\n  \"package\": {\n    \"description\": \"Documents\",\n    \"weight_kg\": 2.0,\n    \"declared_value_sar\": 150\n  }\n}"
            },
            "url": "{{baseUrl}}/orders/multi-leg",
            "description": "Submit a multi-pickup order (1–10 pickups, one dropoff). Run **Create multi-leg quote** first; `quote_id` is saved to `{{multi_leg_quote_id}}` (omit or set `null` to price live — must match quote inputs for same fee).\n\n**Required:** `pickups[]`, `dropoff` with `lat`, `lng`, `short_address`; **dropoff** also requires `contact_name`, `contact_phone`.\n\n**422** if pickup count is 0 or >10. Response **202** includes order `id` (ULID) — saved to `{{order_ulid}}`. Intake publishes `snapshot.stops` for Lynq fulfillment; partners receive per-leg webhooks `order.leg.picked_up` / `order.leg.skipped` plus existing order-level events."
          }
        },
        {
          "name": "Get order by ULID",
          "request": {
            "method": "GET",
            "header": [
              { "key": "X-API-Key", "value": "{{apiKey}}" },
              { "key": "X-Client-ID", "value": "{{clientId}}" }
            ],
            "url": "{{baseUrl}}/orders/{{order_ulid}}",
            "description": "Replace `order_ulid` collection variable with a real ULID from create order response."
          }
        },
        {
          "name": "Get order by partner ref",
          "request": {
            "method": "GET",
            "header": [
              { "key": "X-API-Key", "value": "{{apiKey}}" },
              { "key": "X-Client-ID", "value": "{{clientId}}" }
            ],
            "url": "{{baseUrl}}/orders/by-partner-ref/{{partner_order_number}}"
          }
        },
        {
          "name": "Get order by legacy id (deprecated)",
          "request": {
            "method": "GET",
            "header": [
              { "key": "X-API-Key", "value": "{{apiKey}}" },
              { "key": "X-Client-ID", "value": "{{clientId}}" }
            ],
            "url": "{{baseUrl}}/orders/by-legacy-id/ORD-P-EXAMPLE",
            "description": "Deprecated ORD-P- style id. Prefer ULID."
          }
        },
        {
          "name": "Cancel order",
          "request": {
            "method": "POST",
            "header": [
              { "key": "X-API-Key", "value": "{{apiKey}}" },
              { "key": "X-Client-ID", "value": "{{clientId}}" }
            ],
            "url": "{{baseUrl}}/orders/{{order_ulid}}/cancel",
            "description": "Allowed only before pickup (typically while pending or confirmed)."
          }
        }
      ]
    },
    {
      "name": "Webhooks",
      "item": [
        {
          "name": "Create webhook subscription",
          "request": {
            "method": "POST",
            "header": [
              { "key": "X-API-Key", "value": "{{apiKey}}" },
              { "key": "X-Client-ID", "value": "{{clientId}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"url\": \"https://your-company.example.com/webhooks/lynq\",\n  \"description\": \"Webhook listener\",\n  \"event_filters\": []\n}"
            },
            "url": "{{baseUrl}}/webhooks",
            "description": "Response includes one-time `secret` for `X-Lynq-Signature` verification. Store it immediately.\n\nSupported event types include order lifecycle events plus multi-leg **`order.leg.picked_up`** and **`order.leg.skipped`** (per pickup stop). Use `event_filters` to subscribe to a subset."
          }
        },
        {
          "name": "List webhooks",
          "request": {
            "method": "GET",
            "header": [
              { "key": "X-API-Key", "value": "{{apiKey}}" },
              { "key": "X-Client-ID", "value": "{{clientId}}" }
            ],
            "url": "{{baseUrl}}/webhooks"
          }
        },
        {
          "name": "Get webhook by id",
          "request": {
            "method": "GET",
            "header": [
              { "key": "X-API-Key", "value": "{{apiKey}}" },
              { "key": "X-Client-ID", "value": "{{clientId}}" }
            ],
            "url": "{{baseUrl}}/webhooks/{{webhook_id}}"
          }
        },
        {
          "name": "Update webhook",
          "request": {
            "method": "PUT",
            "header": [
              { "key": "X-API-Key", "value": "{{apiKey}}" },
              { "key": "X-Client-ID", "value": "{{clientId}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"url\": \"https://your-company.example.com/webhooks/lynq-v2\",\n  \"description\": \"Updated\",\n  \"event_filters\": [],\n  \"active\": true,\n  \"rotate_secret\": false\n}"
            },
            "url": "{{baseUrl}}/webhooks/{{webhook_id}}",
            "description": "`rotate_secret: true` rotates the signing key; plaintext secret is not returned on PUT — plan accordingly."
          }
        },
        {
          "name": "Delete webhook",
          "request": {
            "method": "DELETE",
            "header": [
              { "key": "X-API-Key", "value": "{{apiKey}}" },
              { "key": "X-Client-ID", "value": "{{clientId}}" }
            ],
            "url": "{{baseUrl}}/webhooks/{{webhook_id}}"
          }
        },
        {
          "name": "Test webhook delivery",
          "request": {
            "method": "POST",
            "header": [
              { "key": "X-API-Key", "value": "{{apiKey}}" },
              { "key": "X-Client-ID", "value": "{{clientId}}" }
            ],
            "url": "{{baseUrl}}/webhooks/{{webhook_id}}/test"
          }
        },
        {
          "name": "Replay webhook (enqueue historical events)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "X-API-Key", "value": "{{apiKey}}" },
              { "key": "X-Client-ID", "value": "{{clientId}}" }
            ],
            "url": "{{baseUrl}}/webhooks/{{webhook_id}}/replay?limit=100",
            "description": "Optional query params: `since` (ISO8601), `event_type`, `limit`."
          }
        },
        {
          "name": "Recent webhook deliveries",
          "request": {
            "method": "GET",
            "header": [
              { "key": "X-API-Key", "value": "{{apiKey}}" },
              { "key": "X-Client-ID", "value": "{{clientId}}" }
            ],
            "url": "{{baseUrl}}/webhooks/deliveries/recent?limit=50"
          }
        }
      ]
    }
  ]
}
