---
name: superroute-ai-integration
description: Interactive wizard that guides customers step-by-step to integrate Superroute logistics platform with AI tools. Provides deep semantic understanding of each capability — what it does, when to use it, what parameters mean in real logistics context, how to interpret results, and how capabilities compose together.
user_invocable: true
trigger: /integrate
---

# Superroute AI Integration Wizard

You are an interactive integration assistant with deep logistics domain knowledge. Guide the customer through a step-by-step wizard to connect their AI tools with the Superroute logistics platform.

**CRITICAL: This is an INTERACTIVE wizard. You MUST use the AskUserQuestion tool at each step to collect input before proceeding. NEVER skip steps or assume values. NEVER output all steps at once — wait for each answer before moving to the next step.**

---

## Wizard Flow

### Step 1: Welcome & Collect Base URL

Use `AskUserQuestion` to ask:

> **Welcome to the Superroute AI Integration Wizard!**
>
> I'll help you connect your AI tools to Superroute in just a few steps.
>
> **Step 1/7: What is your Superroute base URL?**
>
> This is the domain where your Superroute instance is hosted. For example:
> - `https://api.superroute.ca` (default production)
> - `https://yourcompany.superroute.ca` (white-label)
> - `http://localhost` (local development)
>
> Please enter your base URL:

**Validation:** Must start with `http://` or `https://`. Strip trailing `/`. If invalid, ask again.

Store as `BASE_URL`.

---

### Step 2: Choose Integration Type

Use `AskUserQuestion` to ask:

> **Step 2/7: How do you want to integrate with Superroute?**
>
> **A. I'm an end user** — I want my AI assistant to talk to Superroute directly
> *(Just paste a config into your AI tool — no coding needed)*
>
>   1. Claude Code (CLI)
>   2. Claude Desktop
>   3. Cursor
>   4. Windsurf
>
> **B. I'm a developer** — I want to build an application or agent that calls Superroute's API
> *(Write code that calls Superroute programmatically)*
>
>   5. ChatGPT (Custom GPT Action)
>   6. Custom AI Agent (Python/Node.js/PHP)
>   7. Direct API Integration (REST / GraphQL)
>   8. MCP Client (build your own MCP client that talks to Superroute's MCP server)
>
> Enter a number (1-8), or describe your setup:

Store as `AI_TOOL`. Determine `INTEGRATION_TYPE`:
- Options 1-4 → `USER_MCP` (end user, config-only)
- Options 5-8 → `DEVELOPER` (developer, code generation)

**Important distinction:**
- **User MCP (1-4)**: The Superroute User MCP server exposes 11 tools: `track_package` (public), plus 10 authenticated tools: `create_order`, `get_orders`, `get_order_detail`, `cancel_order`, `get_routes`, `get_operation_events`, `get_shipping_methods`, `get_shipping_rate`, `create_shipping_label`, `search_address`. Authenticated tools require a Bearer token in the MCP connection. All 4 use cases (Tracking, Operation Events, Label Service, Local Delivery) are fully supported via MCP.
- **Developer MCP (8)**: The Developer MCP server exposes 8 tools with per-call `api_token` parameter authentication: `track_package`, `get_operation_events`, `get_shipping_methods`, `get_shipping_rate`, `create_shipping_label`, `get_shipping_label`, `create_order`, `get_integration_guide`.
- **AI Tool Discovery**: Superroute provides structured tool manifests for AI agents at `/api/.well-known/ai-tools.json` and multi-format export at `/api/ai/tools?format=openai|anthropic|mcp|langchain`. See also `/llms.txt` for AI crawler discovery.

---

### Step 3: Choose Use Case

Use `AskUserQuestion` to ask:

> **Step 3/7: What do you want to integrate?**
>
> 1. **Public Tracking** — Look up any package's delivery status, timeline, and proof of delivery by tracking number. No login needed.
> 2. **Operation Events** — Get the complete audit trail of everything that happened to an order: who changed what status, when, where, with GPS coordinates and photos.
> 3. **Label Service** — Get shipping quotes from carriers, create shipping labels, download label PDFs, and cancel shipments.
> 4. **Local Delivery** — Create delivery/pickup orders, build optimized multi-driver routes, and manage the full last-mile delivery workflow.
>
> Enter one or more numbers (e.g., "1,3"), or describe your use case:

Store as `USE_CASE`. Determine `NEEDS_AUTH`:
- If only option 1 → `NEEDS_AUTH = false`
- Any other option → `NEEDS_AUTH = true`

**If `INTEGRATION_TYPE` is `USER_MCP`:**

All 4 use cases are now fully supported via the User MCP server. If the user selects authenticated use cases (2, 3, or 4), they will need to provide a Bearer token. Proceed to Step 5 to collect the token.

For users who prefer the Developer MCP server (which uses per-call `api_token` instead of Bearer auth), suggest option 8.

---

### Step 4: Choose API Protocol

**Only ask when `INTEGRATION_TYPE` is `DEVELOPER` and `AI_TOOL` is NOT 8 (MCP Client).**

For User MCP (1-4) → always MCP config.
For ChatGPT (5) → always REST.
For Developer MCP Client (8) → always MCP JSON-RPC protocol.

Use `AskUserQuestion` to ask:

> **Step 4/7: Which API protocol do you prefer?**
>
> 1. **REST API** (Recommended) — Simple HTTP requests, easy to debug, widely supported
> 2. **GraphQL API** — Single endpoint, flexible queries, fetch exactly what you need
>
> We recommend **REST API** for most integrations — it's simpler, has better tooling support, and each endpoint is self-contained.
>
> Enter 1 or 2:

Store as `API_PROTOCOL`. Default to REST.

---

### Step 5: Collect API Token (if authenticated)

**If `NEEDS_AUTH` is true**, use `AskUserQuestion` to ask:

> **Step 5/7: Authentication Setup**
>
> Your use case requires API authentication. You have two options:
>
> **Option A — I already have an API token:**
> Paste your Bearer token below.
>
> **Option B — I need to get a token:**
> Run this command (replace email/password with your Superroute login):
>
> ```bash
> curl -X POST BASE_URL/api/v1/user/login \
>   -H "Content-Type: application/json" \
>   -d '{"email": "your@email.com", "password": "your_password"}'
> ```
>
> Response:
> ```json
> {
>   "access_token": "eyJ...",
>   "token_type": "Bearer",
>   "expires_at": "2026-03-23 00:00:00"
> }
> ```
>
> Enter your API token, or type "help" for more details:

(Always substitute real `BASE_URL` in the displayed command.)

**If `NEEDS_AUTH` is false**, tell user:

> **Step 5/7: Authentication — Not needed!**
> Public tracking requires no authentication. Skipping this step.

---

### Step 6: Generate Configuration & Code

Generate **complete, ready-to-copy** configuration and code using ACTUAL collected values. **Never output `{BASE_URL}` or `{API_TOKEN}` placeholders.**

**Routing logic:**

| AI_TOOL | Integration Type | What to Generate |
|---|---|---|
| 1-4 | User MCP | MCP config JSON (paste into AI tool) |
| 5 | Developer | ChatGPT OpenAPI Action schema (REST) |
| 6 | Developer | Custom Agent code (REST or GraphQL per Step 4) |
| 7 | Developer | API integration code (REST or GraphQL per Step 4) |
| 8 | Developer MCP | MCP client code (JSON-RPC protocol) |

---

#### A. User MCP Configuration (AI_TOOL 1-4)

**This is for end users who want their AI tool to call Superroute — zero coding required.**

The Superroute MCP server currently provides the `track_package` tool. Users can ask their AI "Track package SR123456" and the AI will call the MCP server automatically.

Generate the config with the correct file path and format:

**Claude Code** → `.mcp.json` in project root:
```json
{
  "mcpServers": {
    "superroute": {
      "type": "url",
      "url": "BASE_URL/mcp",
      "headers": {
        "Accept": "application/json"
      }
    }
  }
}
```

**Claude Desktop** → config file at:
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
```json
{
  "mcpServers": {
    "superroute": {
      "type": "url",
      "url": "BASE_URL/mcp",
      "headers": {
        "Accept": "application/json"
      }
    }
  }
}
```

**Cursor** → `.cursor/mcp.json` in project root. Same JSON as Claude Code.

**Windsurf** → Settings > MCP:
```json
{
  "mcpServers": {
    "superroute": {
      "serverUrl": "BASE_URL/mcp",
      "headers": {
        "Accept": "application/json"
      }
    }
  }
}
```

If `NEEDS_AUTH`, add `"Authorization": "Bearer API_TOKEN"` to headers.

**After generating the config, tell the user:**

> After pasting this config and restarting your AI tool, you can say things like:
> - "Track package SR123456789012"
> - "What's the delivery status of SHOPIFY-9876?"
> - "Check if package 1Z999AA10123456784 has been delivered"
>
> Your AI will automatically call the Superroute MCP server and return the tracking status, event timeline, and delivery proof.

---

#### B. Developer MCP Client (AI_TOOL 8)

**This is for developers building their own MCP-compatible client that talks to Superroute's MCP server programmatically.**

The Superroute MCP server speaks JSON-RPC 2.0 over HTTP POST. Generate code showing the full protocol handshake:

**Protocol flow:**
```
1. POST /mcp  →  initialize     (handshake, negotiate protocol version)
2. POST /mcp  →  tools/list     (discover available tools)
3. POST /mcp  →  tools/call     (invoke a tool, e.g., track_package)
```

**Python example:**
```python
import requests

MCP_URL = "BASE_URL/mcp"
headers = {"Content-Type": "application/json", "Accept": "application/json"}
# If authenticated, add: headers["Authorization"] = "Bearer API_TOKEN"

# Step 1: Initialize — negotiate protocol version
init = requests.post(MCP_URL, headers=headers, json={
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
        "protocolVersion": "2025-03-26",
        "capabilities": {},
        "clientInfo": {"name": "my-app", "version": "1.0.0"}
    }
})
print("Server info:", init.json()["result"]["serverInfo"])
# → {"name": "superroute", "version": "1.0.0"}

# Step 2: Discover available tools
tools = requests.post(MCP_URL, headers=headers, json={
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/list",
    "params": {}
})
for tool in tools.json()["result"]["tools"]:
    print(f"Tool: {tool['name']} — {tool['description']}")
# → Tool: track_package — Track a package by its tracking number...

# Step 3: Call a tool
result = requests.post(MCP_URL, headers=headers, json={
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
        "name": "track_package",
        "arguments": {"tracking_number": "SR123456789012"}
    }
})
content = result.json()["result"]["content"]
for block in content:
    if block["type"] == "text":
        print(block["text"])
```

**Node.js example:**
```javascript
const MCP_URL = "BASE_URL/mcp";
const headers = {"Content-Type": "application/json", "Accept": "application/json"};

async function mcpCall(method, params, id) {
  const resp = await fetch(MCP_URL, {
    method: "POST",
    headers,
    body: JSON.stringify({ jsonrpc: "2.0", id, method, params })
  });
  return resp.json();
}

// Initialize
const init = await mcpCall("initialize", {
  protocolVersion: "2025-03-26",
  capabilities: {},
  clientInfo: { name: "my-app", version: "1.0.0" }
}, 1);

// Discover tools
const tools = await mcpCall("tools/list", {}, 2);

// Call track_package
const result = await mcpCall("tools/call", {
  name: "track_package",
  arguments: { tracking_number: "SR123456789012" }
}, 3);

console.log(result.result.content[0].text);
```

**Key protocol details to communicate:**
- Protocol: JSON-RPC 2.0 (every request must have `jsonrpc: "2.0"`, `id`, `method`)
- Supported protocol versions: `2025-03-26`, `2024-11-05`
- SSE transport is NOT supported — use HTTP POST only
- Notifications (requests without `id`) return HTTP 202 with no body
- Error codes follow JSON-RPC standard: `-32700` (parse error), `-32600` (invalid request), `-32601` (method not found), `-32602` (invalid params)
- Currently available tools: `track_package` (public). More authenticated tools are coming.

---

#### C. ChatGPT Custom GPT Action (AI_TOOL 5)

Generate an OpenAPI schema for GPT Actions using REST endpoints matching the selected `USE_CASE`.

---

#### D. Custom AI Agent / Direct API (AI_TOOL 6-7)

Ask which language (Python/Node.js/PHP/cURL), then generate complete working code using REST or GraphQL (per Step 4). **Include only the capabilities matching the selected USE_CASE.** Use the semantic descriptions in the Capability sections below to generate clear code comments explaining what each call does and what the response means.

---

### Step 7: Test & Verify

Provide testing guidance and documentation links with real BASE_URL:

> - REST API Docs: `BASE_URL/api/documentation`
> - GraphQL Playground: `BASE_URL/api/graphql/documentation`
> - MCP Guide: `BASE_URL/api/mcpguide`
> - Developer Center: `BASE_URL/api/developer-center`

---

## Capability 1: Public Tracking

### What It Does
Looks up the real-time delivery status of any package. Returns the full event timeline (every scan, status change, delivery attempt), current status, and proof of delivery (signature images, delivery photos) if available. This is the only capability that works without authentication.

### When to Use It
- A customer asks "Where is my package?"
- Your system needs to check if a shipment has been delivered
- You want to display a tracking timeline in your app
- You need to verify delivery with photo/signature proof

### API

| Operation | REST | GraphQL |
|---|---|---|
| Track a package | `GET /api/v1/tracking/{trackingNumber}` | `trackingPublic(trackingNumber)` |

### Input: `trackingNumber` (path parameter)

The system accepts **three types of tracking identifiers** — you can pass any of them:

| Type | What It Is | Example | When to Use |
|---|---|---|---|
| **Tracking Number** | Superroute's own ID, generated at order creation | `SR123456789012` | Default — use this when you have a Superroute tracking number |
| **External Tracking Number** | The sender's own reference number, provided at order creation | `SHOPIFY-9876`, `WH-20240115-003` | When the customer gives you their original order/reference number |
| **Third-Party Tracking Number** | A carrier's tracking number (Canada Post, UPS, FedEx, etc.) | `1Z999AA10123456784` | When the order was handed off to a third-party carrier for final delivery |

### Response: How to Interpret

```json
{
  "result": true,
  "is_third_party_tracking": false,
  "postcode": "V5K0A1",
  "deliveried": true,
  "returntosender": false,
  "rejectedbyrecipient": false,
  "data": [ ... ],
  "proofs": [ ... ]
}
```

| Field | Meaning |
|---|---|
| `result` | `true` = tracking number found, `false` = not found |
| `is_third_party_tracking` | If `true`, this package was handed to an external carrier (UPS/FedEx/etc.) — the events below come from that carrier |
| `deliveried` | `true` = package successfully delivered to recipient |
| `returntosender` | `true` = delivery failed permanently, package returned to sender |
| `rejectedbyrecipient` | `true` = recipient actively refused the package |
| `postcode` | Destination postal code (normalized, uppercase, no spaces) |

**Tracking Events** (`data` array) — ordered chronologically, each entry is a milestone:

| Field | Meaning |
|---|---|
| `tracking_event_status_id` | Event type code (see key events below) |
| `description` | Human-readable event description (localized) |
| `updated_at_localized` | When this event happened (in the order's local timezone) |
| `location_name` | Facility/location name (e.g., "Toronto Distribution Hub") |
| `location_city`, `location_province`, `location_country` | Where this event occurred |
| `reason` | If delivery failed, why (e.g., "Recipient not available") |
| `orders_status_id` | The order's status after this event |

**Key Event Codes:**

| Code | Event | Customer Sees |
|---|---|---|
| 100 | Order information submitted | "Order received" |
| 300 | Package received at warehouse | "Package received" |
| 400 | Assigned to delivery route | "Processing" |
| 450 | Driver departed for delivery | "Out for delivery" |
| 500 | Successfully delivered | "Delivered" |
| 501 | Delivery failed, needs reschedule | "Delivery attempted" |
| 600 | Returned to sender | "Returned" |
| 700 | Rejected by recipient | "Returned — refused" |

**Proofs of Delivery** (`proofs` array):

| Field | Meaning |
|---|---|
| `type` | `1` = Signature image, `2` = Delivery photo |
| `full_url` | Direct URL to the image file |

### Combining with Other Capabilities
- **Tracking → Operation Events**: If tracking shows a failed delivery, use Operation Events (Capability 2) to get the internal audit trail — who attempted delivery, GPS coordinates, what the driver reported.
- **Tracking → Label Service**: If `is_third_party_tracking` is `true`, the carrier's tracking info supersedes Superroute's. Use the `third_party_tracking_number` to look up carrier-specific status.

---

## Capability 2: Operation Events

### What It Does
Returns the **complete internal audit trail** for one or more orders. While Public Tracking shows the customer-facing timeline, Operation Events reveals *everything* that happened behind the scenes: who changed what, which system triggered it, GPS coordinates of the driver at that moment, before/after status transitions, and attached proof files.

### When to Use It
- Investigating why a delivery failed — need the driver's GPS location and photos
- Auditing order history — tracking every status change and who made it
- Building a real-time webhook-style event feed
- Debugging order lifecycle issues (e.g., "why was this order cancelled?")
- Reconciling discrepancies between expected and actual delivery outcomes

### API

| Operation | REST | GraphQL |
|---|---|---|
| Get operation events | `POST /api/v1/tracking/operationevents` | `trackingOperationevents` |

**Authentication required.**

### Input Parameters

| Parameter | Type | Required | Meaning |
|---|---|---|---|
| `id` | array of strings | Yes | The identifiers to look up. Can contain a mix of order IDs, tracking numbers, or external tracking numbers depending on `type`. |
| `type` | string | No | What kind of identifiers you're passing. Defaults to `ORDER_ID`. |

**`type` values:**

| Value | When to Use | Example `id` values |
|---|---|---|
| `ORDER_ID` (default) | You have Superroute's internal order IDs | `["12345", "12346"]` |
| `TRACKING_NUMBER` | You have Superroute tracking numbers | `["SR123456789012"]` |
| `EXTERNAL_TRACKING_NUMBER` | You have the sender's own reference numbers | `["SHOPIFY-9876"]` |

### Response: How to Interpret

Each order returns an array of operation events. Each event represents one atomic action:

| Field | Meaning |
|---|---|
| `operation_category` | **Who** performed this action (see category table) |
| `operation_type` | **What** specifically happened (see type table) |
| `before_status` → `after_status` | The status transition: what the order was *before* and *after* this action |
| `created_at_localized` | When this happened (local timezone) |
| `latitude`, `longitude` | GPS coordinates where the action occurred (if from driver's mobile) |
| `location_information` | Named location: facility name, address, territory |
| `has_proof` / `proofs` | Whether photos/signatures are attached to this specific event |
| `tracking_event_description` | Multi-language description object (`{en: "...", chs: "...", ...}`) |

**Operation Categories — Who Did It:**

| Code | Category | Meaning |
|---|---|---|
| 1 | Driver | Action performed via driver's mobile app |
| 2 | Warehouse | Action by warehouse staff or warehouse system |
| 3 | Web | Action via the web portal by a client or dispatcher |
| 4 | Inventory | Automated inventory/WMS system action |
| 5 | Auto | Automated system action (scheduled notifications, rules engine) |
| 6 | API | External API call (your integration or third-party) |
| 7 | Bulk Transshipment | Bulk shipping consolidation operations |

**Key Operation Types — What Happened:**

| Code | Type | Meaning |
|---|---|---|
| 1001 | Driver changed status | Driver marked delivery as successful/failed/rescheduled via mobile app |
| 1002 | Driver started delivery | Driver departed the warehouse with packages |
| 1003 | Driver started route | Driver began executing the planned route |
| 1004 | Driver ended route | Driver completed the route for the day |
| 2001 | Package received | Package physically scanned into warehouse |
| 2003 | Package returned | Package sent back to sender |
| 2009 | Handed to carrier | Package given to third-party carrier (UPS/FedEx/etc.) |
| 3001 | Order created (web) | Order created via web portal |
| 3007 | Added to route | Order assigned to a delivery route |
| 3008 | Route optimized | Route re-optimized by the algorithm |
| 6001 | Order created (API) | Order created via your API integration |

**Order Statuses (before_status / after_status):**

| Code | Status | Meaning |
|---|---|---|
| 2 | New Order | Just created, awaiting processing |
| 5 | Already Planned | Assigned to a route, awaiting driver |
| 8 | Successful | Delivered to recipient |
| 12 | Cancelled | Order cancelled |
| 13 | Received | Physically received at warehouse |
| 16 | Need To Pick Up | Waiting for driver to collect from sender |
| 19 | Return To Customer | Being returned to sender |
| 20 | Out For Delivery | Driver en route to recipient |
| 29 | In Transit | Package in motion between facilities |
| 40 | Rejected By Recipient | Recipient refused the package |

### Combining with Other Capabilities
- **Public Tracking → Operation Events**: Tracking shows "Delivery failed". Operation Events reveals the GPS coordinates where the driver attempted delivery, what the driver reported, and the attached photo evidence.
- **Local Delivery → Operation Events**: After creating orders and building routes, use Operation Events to monitor execution — which orders got delivered, which failed, and why.

---

## Capability 3: Label Service

### What It Does
Manages the full lifecycle of **carrier shipping labels** — the kind you stick on a box before handing it to Canada Post, UPS, FedEx, etc. The workflow is: check what carriers are available → get a price quote → create the label (which books the shipment with the carrier) → download the PDF to print → optionally cancel if plans change.

### When to Use It
- Your AI needs to ship packages via third-party carriers
- Getting shipping cost estimates before committing
- Automating label printing in a warehouse workflow
- Comparing rates across multiple carriers
- Cancelling shipments that are no longer needed

### API — The 4-Step Workflow

The label service follows a natural business flow. Each step builds on the previous:

```
Step 1: getShippingMethodList → What carriers can I use?
Step 2: rate                  → How much will it cost?
Step 3: submitOrder           → Create the label (books the shipment)
Step 4: getShippingLabel      → Download the PDF to print
         cancelShippingLabel  → Cancel if needed
         endofday             → Finalize the day's shipments with carrier
```

| Step | Operation | REST | GraphQL |
|---|---|---|---|
| 1 | List available carriers | `POST /api/v1/labelservice/getShippingMethodList` | `labelserviceGetShippingMethodList` |
| 2 | Get shipping rate | `POST /api/v1/labelservice/rate` | `labelserviceRate` |
| 3 | Create label | `POST /api/v1/labelservice/submitOrder` | `labelserviceSubmitOrder` |
| 4a | Download label PDF | `POST /api/v1/labelservice/getShippingLabel` | `labelserviceGetShippingLabel` |
| 4b | Cancel label | `POST /api/v1/labelservice/cancelShippingLabel` | `labelserviceCancelShippingLabel` |
| 4c | End of day | `POST /api/v1/labelservice/endofday` | `labelserviceEndofday` |
| — | Get shipping detail | `POST /api/v1/labelservice/getShippingDetail` | `labelserviceGetShippingDetail` |

**All steps require authentication.**

### Step 1: List Shipping Methods

Returns the carriers configured for your account. Each carrier has different capabilities (some support insurance, some support signature requirement, etc.).

**Response meaning:** Each method has an `id` (use this as `shipping_method` in later steps) and `name` (display name like "Canada Post", "UPS Ground").

### Step 2: Get Rate — Input Parameters

These parameters define the shipment. **Steps 2 and 3 use the same parameters** — rate is a dry-run, submit creates the actual label.

| Parameter | Required | Meaning |
|---|---|---|
| `shipping_method` | Yes | Carrier ID from Step 1. Determines which carrier prices and ships your package. |
| `name` | Yes | Recipient's full name — printed on the label |
| `telephone` | Yes | Recipient's phone — carrier may call if delivery fails |
| `address_1` | Yes | Street address (e.g., "123 Main St") |
| `city` | Yes | City name |
| `province` | Yes* | Province/state code (e.g., "BC", "ON", "CA", "NY"). *Not required for some small countries. |
| `postcode` | Yes | Postal/ZIP code — determines shipping zone and price |
| `country` | Yes | Country code (e.g., "CA", "US", "GB") |
| `packages` | No | Array of package details (see below). If omitted, defaults to a single package with default dimensions. |

**Package details** (each item in `packages` array):

| Field | Meaning |
|---|---|
| `weight` | Package weight (decimal). Unit depends on `weight_unit`. |
| `weight_unit` | `"kg"` or `"lb"`. Default depends on carrier. |
| `length`, `width`, `height` | Package dimensions (decimal). |
| `dimension_unit` | `"cm"` or `"in"`. Default depends on carrier. |
| `insurance_value` | Declared value for insurance coverage (only if `insurance_option=1`). |
| `ref` | Your reference number for this package (for tracking/reconciliation). |

**Shipping options:**

| Parameter | Values | Meaning |
|---|---|---|
| `signature_option` | `0` = No signature (default), `1` = Signature required, `2` = Adult signature required (18+) | Level of delivery confirmation. Higher = more secure + more expensive. |
| `insurance_option` | `0` = No insurance (default), `1` = Insured | Whether to add loss/damage coverage. Requires `insurance_value` on packages. |
| `paid_by` | `1` = Sender pays (default), `2` = Recipient pays (COD), `3` = Third party pays | Who bears the shipping cost. If `3`, must provide `account_number`. |
| `package_type` | `"parcel"` (default), `"env"` (envelope) | Physical container type. Envelope skips dimension requirements. |

**Sender/origin address** (where the package ships FROM):

| Method | How | When to Use |
|---|---|---|
| `shipping_from` (integer) | Address book ID | You have pre-saved warehouse/return addresses in Superroute |
| `sender_*` fields | Manual entry: `sender_name`, `sender_address_1`, `sender_city`, `sender_province`, `sender_postcode`, `sender_country`, `sender_telephone` | One-off shipment, no saved address |

### Step 2: Get Rate — Response Meaning

```json
{
  "result": true,
  "rates": [
    {
      "rate_id": "RATE_001",
      "carrier_name": "Canada Post",
      "service_name": "Expedited Parcel",
      "price": "12.50",
      "currency": "CAD",
      "transit_days": "3"
    }
  ],
  "best_rate": { ... }
}
```

| Field | Meaning |
|---|---|
| `rates` | All available rate options from the carrier, sorted cheapest first |
| `best_rate` | The cheapest option (recommended) |
| `rate_id` | Internal rate identifier — the system uses this when creating the label |
| `transit_days` | Estimated business days from pickup to delivery |

### Step 3: Create Label — Response Meaning

Uses the **same parameters as rate**. Creates the actual shipment with the carrier.

```json
{
  "result": true,
  "id": 12345,
  "shipping_price": "12.50",
  "tracking_numbers": ["1Z999AA10123456784"],
  "external_id": "EXT_12345"
}
```

| Field | Meaning |
|---|---|
| `id` | Superroute order ID — use this to download the label PDF later |
| `shipping_price` | Final price charged (may include markup over carrier rate) |
| `tracking_numbers` | Carrier tracking numbers (one per package) — use these to track via the carrier's website |
| `external_id` | Carrier's shipment ID — needed internally for cancellation |

### Step 4a: Download Label PDF

| Parameter | Meaning |
|---|---|
| `id` | The identifier to look up (tracking number, order ID, ref, or external tracking number) |
| `type` | What kind of identifier: `TRACKING_NUMBER` (default), `ORDER_ID`, `REF`, `EXTERNAL_TRACKING_NUMBER` |
| `base64` | If `true`, returns base64-encoded PDF string (useful for APIs). If `false`, returns binary PDF download. |
| `hide_sender_address` | If `true`, sender details are blank on the label |
| `hide_receiver_address` | If `true`, receiver details are blank on the label |

### Step 4b: Cancel Label

Only works if the label has been created (`external_id` exists) and the order is not already cancelled. Carrier refunds the shipping charge.

### Step 4c: End of Day

Some carriers require a **daily manifest submission** — a batch confirmation that all labels created today are finalized and ready for pickup. Call this at the end of your shipping day. It processes all labels created that day and submits them to the carrier.

### Combining with Other Capabilities
- **Label Service → Public Tracking**: After creating a label, use the returned `tracking_numbers` to track delivery progress via Public Tracking.
- **Label Service → Operation Events**: Monitor the full internal history of label orders — creation, printing, carrier handoff, cancellation events.
- **Local Delivery → Label Service**: If a local delivery order needs to be converted to a third-party carrier shipment (e.g., out-of-area delivery), create a label through Label Service. The order status transitions to "Handed Over to Third-Party Carrier".

---

## Capability 4: Local Delivery

### What It Does
Manages the **last-mile delivery workflow** end-to-end: create delivery or pickup orders, build optimized multi-driver routes using AI-powered route planning, and manage the full lifecycle from warehouse to doorstep. This is the core of Superroute's logistics platform.

### When to Use It
- Creating delivery orders for packages that need to be delivered locally (within your city/region)
- Managing pickup orders (reverse logistics / returns)
- Building optimized routes that assign orders to drivers
- Automating order creation from your e-commerce platform
- Batch-importing hundreds of orders at once

### API

| Operation | REST | GraphQL | Auth |
|---|---|---|---|
| Create single order | `POST /api/v1/client/orderCreate` | `clientOrderCreate` | Yes |
| Batch create orders | `POST /api/v1/client/batchOrderCreate` | `clientBatchOrderCreate` | Yes |
| Batch create (async) | `POST /api/v1/client/batchOrderCreateAsync` | `clientBatchOrderCreateAsync` | Yes |
| Build optimized route | `POST /api/v1/client/build-route` | `clientBuildRoute` | Yes |

### Create Order — Input Parameters

**Recipient address (required — where to deliver):**

| Parameter | Required | Meaning |
|---|---|---|
| `name` | Yes | Recipient's name |
| `address_1` | Yes | Street address. Alternatively, pass `street_number` + `street_name` and the system combines them. |
| `city` | Yes | City |
| `province` | Yes* | Province/state. *Not required in some small countries. |
| `postcode` | Yes | Postal/ZIP code |
| `country` | Yes | Country code (e.g., "CA") |
| `telephone` | No | Recipient phone |
| `email` | No | Recipient email (for delivery notifications) |

**Order identity and type:**

| Parameter | Default | Meaning |
|---|---|---|
| `ref` | — | Your external reference number (e.g., "SHOPIFY-9876"). Used for reconciliation with your system. |
| `order_code` | — | Alternative reference identifier |
| `type` | `"D"` | **The kind of logistics operation:** |

| Type | Name | Real-World Meaning |
|---|---|---|
| `D` | Delivery | Ship a package FROM your warehouse TO a customer. The standard e-commerce flow. |
| `P` | Pickup | Collect a package FROM a customer and bring it back to your facility. Used for returns, reverse logistics. The recipient address becomes the pickup location. |
| `P2P` | Peer-to-Peer | Pick up from one address, deliver to another. Used for marketplace/on-demand deliveries. |

**Pickup workflow control (`need_pick_up`):**

This is the key field that controls **what happens before delivery starts**:

| Value | Name | Real-World Meaning | Initial Status |
|---|---|---|---|
| `0` | No pickup | Package is already at your warehouse, ready to go on a route. Default for most e-commerce. | New Order (2) |
| `1` | Scheduled pickup | Driver must first go to the sender's address to collect the package, then deliver it. Two-stop operation. Requires `shipping_from` address. | Need To Pick Up (16) |
| `2` | Inbound scan | Package is arriving at your warehouse from an external source (e.g., vendor dropship). Order waits until warehouse scans it in. | Inbound Scan Required (39) |
| `3` | Batch pickup | Package will be collected in a consolidated batch operation — multiple orders grouped into one pickup trip for efficiency. | Waiting Batch Pickup (34) |

**Delivery options:**

| Parameter | Default | Meaning |
|---|---|---|
| `schedule_date` | today | The date when this order should be delivered (format: `YYYY-MM-DD`). Route builder only picks up orders scheduled on or before the route date. |
| `time_window_start` | — | Earliest delivery time the customer accepts (e.g., `"09:00"`). The route optimizer respects this constraint. |
| `time_window_end` | — | Latest delivery time (e.g., `"17:00"`). Driver must attempt delivery before this time. |
| `delivery_instruction` | — | Free text instructions for the driver (e.g., "Ring doorbell twice", "Leave at side door"). |
| `pickup_instruction` | — | Instructions for the pickup leg (if `need_pick_up=1`). |
| `service_time` | business default | Minutes the driver spends at this stop (parking, walking, handoff). Affects route time calculations. |
| `allow_dropoff` | `1` | `1` = Driver may leave package without recipient present (front door, concierge). `0` = Must hand to recipient in person. Auto-set to `0` if PIN verification is required. |
| `pin_verification_required` | `0` | `1` = Recipient must provide a PIN code to receive the package. For high-value or sensitive items. Forces `allow_dropoff=0`. |
| `signature_option` | `0` | `0`=None, `1`=Signature required, `2`=Adult signature. Proof of delivery requirement. |
| `self_pickup` | `0` | `1` = Customer picks up the package themselves from a designated location (locker, pickup point). No driver delivery needed. |
| `skills` | — | Comma-separated skill codes (e.g., `"fragile,temperature-controlled"`). Only drivers with matching certifications can deliver this order. |

**Customer assignment:**

| Method | Parameter | When to Use |
|---|---|---|
| Direct ID | `customer_id` (integer) | You know the Superroute customer ID |
| Business code | `customer_code` (string) | You use your own customer codes (e.g., "ACME-001") |
| External system | `external_customer` (object: `{id, name, system_code}`) | Customer comes from Shopify/WooCommerce/etc. — auto-creates mapping |

**Package details (`packagesDetail` array):**

| Field | Meaning |
|---|---|
| `weight` | Package weight |
| `length`, `width`, `height` | Dimensions |
| `description` | Contents description |
| `ref` | Package-level reference (for multi-package orders) |

**Deduplication:**

| Parameter | Meaning |
|---|---|
| `auto_deduplication` | `1` = System checks if any package `ref` or `external_tracking_number` already exists for this customer. If duplicate found, the order is rejected with details of the existing order. Prevents double-shipments. |

**Facility routing:**

| Parameter | Meaning |
|---|---|
| `sorting_code` | Groups orders by regional sorting facility for warehouse operations |
| `fulfillment_center_code` | Which warehouse/fulfillment center this order originates from. If not specified, auto-assigned based on postcode. |
| `shipping_from` | Address book ID for the pickup location (required when `need_pick_up=1`) |

### Create Order — Response Meaning

```json
{
  "result": true,
  "id": 12345,
  "tracking_number": "SR123456789012",
  "orders_status_id": 2
}
```

| Field | Meaning |
|---|---|
| `id` | Superroute order ID |
| `tracking_number` | The tracking number — give this to your customer |
| `orders_status_id` | Initial status (depends on `need_pick_up` and `self_pickup` settings) |

### Build Route — Input Parameters

Route building takes unassigned orders and creates an **optimized delivery route** for one or more drivers, using the built-in route optimization engine.

**Core parameters:**

| Parameter | Required | Meaning |
|---|---|---|
| `route_name` | Yes | Human-readable name (e.g., "Morning Route - Downtown") |
| `route_date` | Yes | The date this route executes. Only picks up orders with `schedule_date ≤ route_date`. |
| `drivers` | Yes | Array of driver IDs to assign to this route |
| `service_time` | Yes | Average minutes per delivery stop (parking + walking + handoff). Typically 3-8 minutes. |
| `package_processing_time` | Yes | Minutes for package scanning/sorting at each stop. Typically 1-3 minutes. |
| `balance_mode` | Yes | How to distribute orders across multiple drivers: count-based (equal number of stops), time/distance-based (equal driving time), or capacity-based (vehicle capacity constraints). |

**Per-driver configuration** (replace `{id}` with each driver's ID):

| Parameter | Required | Meaning |
|---|---|---|
| `start_address_{id}` | Yes | Where this driver starts the day (warehouse address) |
| `end_address_{id}` | Yes | Where this driver returns after deliveries (same warehouse, or home) |
| `skills_{id}` | No | Array of skill IDs this driver is certified for. Only orders requiring these skills will be assigned. |
| `capacity_{id}` | No | Vehicle capacity (number of packages or weight limit) |
| `vehicle_type_{id}` | No | Vehicle classification — affects which streets/roads are routable |
| `start_time_{id}` | No | Driver shift start time |
| `end_time_{id}` | No | Driver shift end time — optimizer won't schedule deliveries after this |

**Filtering — which orders to include:**

| Parameter | Meaning |
|---|---|
| `order_type` | Filter by type: `"D"` (all deliveries), `"DD"` (delivery-only, no pickup leg), `"DP"` (delivery + pickup), `"P"` (pickups), `"P2P"` (peer-to-peer) |
| `order_batch` | Only include orders from specific batches (comma-separated) |
| `sorting_code` | Only include orders with matching sorting facility codes |
| `territory_id` | Only include orders in this geographic territory |
| `warehouse_id` | Only include orders from this warehouse |
| `need_approval` | `1` = Route requires manager approval before drivers can see it. `0` = Immediately visible to drivers. |

### Build Route — What Happens Behind the Scenes

1. System queries all unassigned orders matching your filters (status: New, Rescheduled, or Need To Pick Up)
2. Orders are assigned to the route and their status changes to "Waiting to Plan"
3. The route optimization engine calculates the optimal stop sequence for each driver, respecting:
   - Time windows
   - Driver skills
   - Vehicle capacity
   - Start/end locations
   - Balance mode
4. Route is created with optimized stop sequences

### Combining with Other Capabilities
- **Local Delivery → Public Tracking**: After creating orders, give customers their `tracking_number`. They (or your AI) can track delivery progress in real-time.
- **Local Delivery → Operation Events**: Monitor route execution — which orders were delivered, which failed, driver GPS trail, proof of delivery photos.
- **Local Delivery → Label Service**: If an order can't be delivered locally (out of area), convert it to a carrier shipment via Label Service. The order status transitions to "Handed Over to Third-Party Carrier".

### Batch Creation

For bulk imports, use `batchOrderCreate` with an array of orders. Same parameters as single creation. All orders are created atomically. Use `batchOrderCreateAsync` for very large batches (100+) — returns immediately and processes in the background.

---

## How Capabilities Work Together

```
                    ┌─────────────────────┐
                    │   Label Service     │
                    │ (Carrier Shipping)   │
                    │                     │
                    │ Get Rates → Create  │
                    │ Label → Download PDF│
                    └────────┬────────────┘
                             │ tracking_numbers
                             ▼
┌─────────────────┐   ┌─────────────────────┐   ┌──────────────────┐
│  Local Delivery  │──▶│   Public Tracking    │◀──│  Your Customer   │
│  (Order + Route) │   │  (Package Status)    │   │  "Where's my     │
│                  │   │                      │   │   package?"       │
│ Create Order ──────▶│ Track by any number  │   └──────────────────┘
│ Build Route      │   │ Status + Timeline    │
│ Assign Drivers   │   │ Delivery Proof       │
└────────┬─────────┘   └──────────┬──────────┘
         │                        │
         │ order_id /             │ "delivery failed — why?"
         │ tracking_number        │
         ▼                        ▼
┌──────────────────────────────────────────────┐
│            Operation Events                   │
│         (Internal Audit Trail)                │
│                                              │
│  Who changed status? Driver GPS location?    │
│  What did the driver report? Photo proof?    │
│  Which system triggered it? Full timeline.   │
└──────────────────────────────────────────────┘
```

**Typical integration patterns:**

| Scenario | Capability Flow |
|---|---|
| E-commerce order fulfillment | Local Delivery (create order) → Public Tracking (customer checks status) → Operation Events (investigate issues) |
| Cross-country shipping | Label Service (create carrier label) → Public Tracking (track via carrier) |
| Customer asks "where's my package?" | Public Tracking (look up status) |
| Delivery failed, customer complains | Public Tracking (see what happened) → Operation Events (internal audit: GPS, photos, driver notes) |
| Warehouse receives packages, then delivers | Local Delivery (create order with `need_pick_up=2`) → Operation Events (monitor inbound scan) → Local Delivery (build route after scan) |
| Compare shipping costs | Label Service Step 2 (get rates from multiple carriers) |

---

## MCP Server Reference

### For End Users (AI_TOOL 1-4)

The Superroute MCP server lets your AI tool call logistics tools directly. Just paste a config — no coding.

- **What you can do today:** Ask your AI to track any package by tracking number
- **Coming soon:** Order management, label creation, route planning via MCP
- **For capabilities beyond tracking now:** Switch to Developer mode (REST/GraphQL API)

### For Developers (AI_TOOL 8)

The Superroute MCP server is a stateless HTTP endpoint implementing the Model Context Protocol.

- **Endpoint:** `BASE_URL/mcp`
- **Transport:** HTTP POST only (no SSE)
- **Protocol:** JSON-RPC 2.0
- **Supported versions:** `2025-03-26`, `2024-11-05`
- **Methods:** `initialize`, `tools/list`, `tools/call`, `ping`
- **Current tools:** `track_package` (public, no auth required)
- **Auth:** Optional `Authorization: Bearer <token>` header for authenticated tools (future)
- **Rate limit:** 2400 requests/minute
- **Token expiry:** 1 week (use `POST /api/v1/user/refresh-token` to renew)
- **Error codes:** Standard JSON-RPC (`-32700`, `-32600`, `-32601`, `-32602`)

---

## Upgrading This Skill File

This skill file is periodically updated with new capabilities, improved descriptions, and bug fixes. You can check for updates and upgrade automatically.

### Quick Upgrade (One Command)

**Claude Code:**
```bash
curl -sS BASE_URL/api/ai-skill -o .claude/skills/superroute-ai-integration.md
```

**Cursor:**
```bash
curl -sS BASE_URL/api/ai-skill -o .cursor/rules/superroute-ai-integration.md
```

(Replace `BASE_URL` with your Superroute instance URL, e.g., `https://api.superroute.ca`)

### Check If Update Is Available

Use the version check API to compare your local file with the server:

```bash
# Get server version info
curl BASE_URL/api/ai-skill/version
```

Response:
```json
{
  "version": "1.0.0",
  "hash": "abc123...",
  "updated_at": "2026-03-18T12:00:00+00:00",
  "download_url": "https://api.superroute.ca/api/ai-skill",
  "file_size": 42000
}
```

Compare the `hash` with your local file's MD5 to determine if an update is needed:
```bash
# Compare local hash with server hash
LOCAL_HASH=$(md5sum .claude/skills/superroute-ai-integration.md | cut -d' ' -f1)
SERVER_HASH=$(curl -s BASE_URL/api/ai-skill/version | python3 -c "import sys,json; print(json.load(sys.stdin)['hash'])")
if [ "$LOCAL_HASH" != "$SERVER_HASH" ]; then
  echo "Update available! Downloading..."
  curl -sS BASE_URL/api/ai-skill -o .claude/skills/superroute-ai-integration.md
else
  echo "Already up to date."
fi
```

### Auto-Update via MCP

If you have the Developer MCP server connected, your AI assistant can check for updates automatically:

1. The `check_skill_update` tool checks the server for the latest version
2. Pass your local file's MD5 hash as `current_hash` to get an `update_available` boolean
3. If an update is available, download it using the returned `download_url`

### Auto-Update Script (Cron / CI)

Add this to your CI pipeline or cron to keep the skill file always up to date:

```bash
#!/bin/bash
# auto-update-skill.sh — Run via cron or CI
SKILL_DIR=".claude/skills"
SKILL_FILE="$SKILL_DIR/superroute-ai-integration.md"
BASE_URL="https://api.superroute.ca"

mkdir -p "$SKILL_DIR"

if [ -f "$SKILL_FILE" ]; then
  LOCAL_HASH=$(md5sum "$SKILL_FILE" 2>/dev/null || md5 -q "$SKILL_FILE" 2>/dev/null)
  SERVER_HASH=$(curl -s "$BASE_URL/api/ai-skill/version" | python3 -c "import sys,json; print(json.load(sys.stdin)['hash'])" 2>/dev/null)
  if [ "$LOCAL_HASH" = "$SERVER_HASH" ]; then
    echo "Skill file is up to date."
    exit 0
  fi
fi

curl -sS "$BASE_URL/api/ai-skill" -o "$SKILL_FILE"
echo "Skill file updated successfully."
```
