{
  "openapi": "3.0.3",
  "info": {
    "title": "DispoTag API",
    "description": "API for managing DispoTag package tracking with disposable BLE beacon tags, carrier integrations (USPS, UPS, FedEx), and dispute evidence.\n\n## Authentication\n\nMost endpoints require a WorkOS JWT token in the `Authorization: Bearer <token>` header.\n\nSome endpoints (risk scoring, webhook subscribe) also accept an API key via the `X-API-Key` header.\n\nPublic tracking endpoints require no authentication and are rate-limited by IP.\n\n## Rate Limits\n\n- Dashboard endpoints: 100 requests/minute per user\n- Public tracking: 60 requests/minute per IP\n\n## Tag States\n\nTags move through a lifecycle as packages are tracked:\n\n```\nawaiting_first_ping -> at_origin -> in_transit -> approaching ->\ndelivered -> delivery_verification -> completed\n                        |\n              disputed_in_transit -> completed\n```\n\n## Response Envelope\n\nAll responses are wrapped in a standard envelope:\n\n```json\n{\n  \"success\": true,\n  \"data\": { ... }\n}\n```\n\nError responses:\n\n```json\n{\n  \"success\": false,\n  \"error\": \"Description of what went wrong\"\n}\n```",
    "version": "1.0.0",
    "contact": {
      "name": "DispoTag Support",
      "email": "support@dispotag.com"
    }
  },
  "servers": [
    {
      "url": "https://api.dispotag.com",
      "description": "Production"
    },
    {
      "url": "https://devapi.dispotag.com",
      "description": "Staging"
    }
  ],
  "tags": [
    {
      "name": "Tags",
      "description": "View and manage BLE beacon tags attached to packages"
    },
    {
      "name": "Shipments",
      "description": "Create and manage shipments with carrier tracking"
    },
    {
      "name": "Disputes",
      "description": "Delivery dispute resolution -- view, resolve, and generate evidence"
    },
    {
      "name": "Alerts",
      "description": "Notification alerts for delivery events, geofence crossings, and disputes"
    },
    {
      "name": "Analytics",
      "description": "Dashboard metrics and delivery statistics"
    },
    {
      "name": "Risk",
      "description": "Address risk scoring for dispute and fraud prevention"
    },
    {
      "name": "Public Tracking",
      "description": "Unauthenticated endpoints for shared tracking links"
    },
    {
      "name": "Webhooks",
      "description": "Webhook subscriptions for event delivery (Zapier-compatible REST hooks)"
    }
  ],
  "paths": {
    "/v1/tags": {
      "get": {
        "tags": ["Tags"],
        "summary": "List tags with filtering",
        "description": "Returns a paginated list of non-archived tags for the authenticated user's organization. Supports filtering by state, search text, and order ID.",
        "operationId": "listTags",
        "security": [{ "BearerAuth": [] }],
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "description": "Filter by tag state (comma-separated, e.g. `in_transit,approaching`)",
            "schema": { "type": "string" }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Search in order_id and ble_identifier",
            "schema": { "type": "string" }
          },
          {
            "name": "order_id",
            "in": "query",
            "description": "Filter by exact order ID",
            "schema": { "type": "string" }
          },
          { "$ref": "#/components/parameters/page" },
          { "$ref": "#/components/parameters/limit" }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of tags with last ping data",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponsePaginatedTagWithLastPing"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/tags/stats": {
      "get": {
        "tags": ["Tags"],
        "summary": "Tag counts by state",
        "description": "Returns aggregated counts of tags grouped by state for the authenticated user's organization.",
        "operationId": "tagStats",
        "security": [{ "BearerAuth": [] }],
        "responses": {
          "200": {
            "description": "Tag state counts",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/TagStats" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/tags/grouped": {
      "get": {
        "tags": ["Tags"],
        "summary": "Tags grouped by shipment",
        "description": "Returns tags grouped by their parent shipment with an aggregate state per group. Tags without a shipment become their own single-tag group. Groups are sorted by state priority (worst first), then most recently seen.",
        "operationId": "listTagsGrouped",
        "security": [{ "BearerAuth": [] }],
        "responses": {
          "200": {
            "description": "Grouped tags",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/GroupedTagsResponse" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/tags/{id}": {
      "get": {
        "tags": ["Tags"],
        "summary": "Get tag detail",
        "description": "Returns a single tag by ID with its last ping, ping count, and full ping history (all pings sorted ASC for trail rendering).",
        "operationId": "getTag",
        "security": [{ "BearerAuth": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/tagId" }
        ],
        "responses": {
          "200": {
            "description": "Tag detail with pings",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/TagWithLastPing" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      },
      "patch": {
        "tags": ["Tags"],
        "summary": "Update a tag",
        "description": "Partially update a tag's metadata. All fields are optional; only provided fields are updated. Requires manager role or above.",
        "operationId": "updateTag",
        "security": [{ "BearerAuth": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/tagId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/UpdateTagRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated tag",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/Tag" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "422": { "$ref": "#/components/responses/ValidationError" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      },
      "delete": {
        "tags": ["Tags"],
        "summary": "Archive a tag",
        "description": "Soft-deletes a tag by setting `archived_at`. Requires admin role.",
        "operationId": "deleteTag",
        "security": [{ "BearerAuth": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/tagId" }
        ],
        "responses": {
          "204": { "description": "Tag archived successfully" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/tags/{id}/journey": {
      "get": {
        "tags": ["Tags"],
        "summary": "Tag journey timeline",
        "description": "Returns the location history (beacon pings) for a tag, sorted DESC by time, limited to 1000 points. Used for map rendering.",
        "operationId": "getJourney",
        "security": [{ "BearerAuth": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/tagId" }
        ],
        "responses": {
          "200": {
            "description": "Journey points",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/TagJourneyResponse" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/tags/{id}/evidence": {
      "get": {
        "tags": ["Tags"],
        "summary": "Tag evidence package",
        "description": "Returns an evidence package for dispute resolution, including delivery locations from the last 24 hours, distance from destination, time at destination, and an evidence score (1-5 stars).",
        "operationId": "getEvidence",
        "security": [{ "BearerAuth": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/tagId" }
        ],
        "responses": {
          "200": {
            "description": "Evidence package",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/EvidencePackage" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/shipments": {
      "get": {
        "tags": ["Shipments"],
        "summary": "List shipments",
        "description": "Returns all shipments for the authenticated user's organization, each with its packages.",
        "operationId": "listShipments",
        "security": [{ "BearerAuth": [] }],
        "responses": {
          "200": {
            "description": "List of shipments with packages",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": { "$ref": "#/components/schemas/ShipmentWithPackages" }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      },
      "post": {
        "tags": ["Shipments"],
        "summary": "Create a shipment",
        "description": "Creates a shipment with one or more packages, each linked to a tag by its BLE identifier. Tags are looked up by `tag_identifier` and linked to the shipment. Carrier tracking is automatically refreshed after creation.",
        "operationId": "createShipment",
        "security": [{ "BearerAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CreateShipmentRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created shipment with packages",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/ShipmentWithPackages" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": { "$ref": "#/components/responses/Conflict" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/shipments/{id}": {
      "get": {
        "tags": ["Shipments"],
        "summary": "Get shipment detail",
        "description": "Returns a single shipment with all its packages.",
        "operationId": "getShipment",
        "security": [{ "BearerAuth": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/shipmentId" }
        ],
        "responses": {
          "200": {
            "description": "Shipment with packages",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/ShipmentWithPackages" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      },
      "patch": {
        "tags": ["Shipments"],
        "summary": "Update shipment",
        "description": "Partially update shipment metadata. All fields are optional; only provided fields are updated.",
        "operationId": "updateShipment",
        "security": [{ "BearerAuth": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/shipmentId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/UpdateShipmentRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated shipment",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/Shipment" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/shipments/search": {
      "post": {
        "tags": ["Shipments"],
        "summary": "Look up tracking number across carriers",
        "description": "Smart search: tries Shopify order lookup first, then carrier tracking number lookup, then returns a manual-mode fallback. Used by the shipment creation wizard to auto-fill details.",
        "operationId": "shipmentSearch",
        "security": [{ "BearerAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["query"],
                "properties": {
                  "query": {
                    "type": "string",
                    "description": "Shopify order number (e.g. #1234) or carrier tracking number"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Search result with source indicator",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "description": "Varies by source. Always includes `source` field (`shopify`, `carrier`, or `manual`).",
                          "properties": {
                            "source": {
                              "type": "string",
                              "enum": ["shopify", "carrier", "manual"]
                            },
                            "tracking_number": { "type": "string" },
                            "carrier": { "type": "string" },
                            "destination_address": { "type": "string" },
                            "origin_address": { "type": "string" },
                            "order_name": { "type": "string" },
                            "order_id": { "type": "integer" },
                            "customer_name": { "type": "string" },
                            "customer_email": { "type": "string" },
                            "customer_phone": { "type": "string" },
                            "amount": { "type": "string" },
                            "currency": { "type": "string" },
                            "fulfillment_status": { "type": "string" },
                            "carrier_status": { "type": "string" },
                            "estimated_delivery": { "type": "string", "format": "date-time" },
                            "reference": { "type": "string" }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/disputes": {
      "get": {
        "tags": ["Disputes"],
        "summary": "List disputes with filtering",
        "description": "Returns a paginated list of disputes for the authenticated user's organization.",
        "operationId": "listDisputes",
        "security": [{ "BearerAuth": [] }],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Filter by status: `open`, `investigating`, `resolved`",
            "schema": { "type": "string" }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Filter by dispute type: `not_delivered`, `damaged`, `wrong_location`, `other`",
            "schema": { "type": "string" }
          },
          {
            "name": "tag_id",
            "in": "query",
            "description": "Filter by tag UUID",
            "schema": { "type": "string", "format": "uuid" }
          },
          { "$ref": "#/components/parameters/page" },
          { "$ref": "#/components/parameters/limit" }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of disputes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponsePaginatedDispute"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/disputes/stats": {
      "get": {
        "tags": ["Disputes"],
        "summary": "Dispute statistics",
        "description": "Returns aggregate dispute statistics for the organization: total counts by status, average resolution time, evidence generation count, and amount at risk.",
        "operationId": "disputeStats",
        "security": [{ "BearerAuth": [] }],
        "responses": {
          "200": {
            "description": "Dispute statistics",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/DisputeStats" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/disputes/{id}": {
      "get": {
        "tags": ["Disputes"],
        "summary": "Get single dispute",
        "description": "Returns a single dispute by ID.",
        "operationId": "getDispute",
        "security": [{ "BearerAuth": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/disputeId" }
        ],
        "responses": {
          "200": {
            "description": "Dispute detail",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/Dispute" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/disputes/{id}/resolve": {
      "post": {
        "tags": ["Disputes"],
        "summary": "Resolve dispute",
        "description": "Marks a dispute as resolved with the given resolution text and optional evidence score. Requires manager role. Emits a `dispute.resolved` webhook event.",
        "operationId": "resolveDispute",
        "security": [{ "BearerAuth": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/disputeId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ResolveDisputeRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resolved dispute",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/Dispute" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/disputes/{id}/evidence/generate": {
      "post": {
        "tags": ["Disputes"],
        "summary": "Generate evidence PDF",
        "description": "Generates an evidence PDF for a dispute with enrichments (reverse geocoding, FedEx POD, Shopify order history). Uploads the PDF to S3 and stores the key on the dispute record.",
        "operationId": "generateEvidence",
        "security": [{ "BearerAuth": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/disputeId" }
        ],
        "responses": {
          "200": {
            "description": "Evidence PDF generated",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "evidence_pdf_key": {
                              "type": "string",
                              "description": "S3 key for the generated PDF"
                            },
                            "generated_at": {
                              "type": "string",
                              "format": "date-time"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "503": {
            "description": "S3 not configured",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          }
        }
      }
    },
    "/v1/disputes/{id}/evidence/pdf": {
      "get": {
        "tags": ["Disputes"],
        "summary": "Download evidence PDF",
        "description": "Returns a temporary redirect (302) to a pre-signed S3 URL for the evidence PDF. The PDF must be generated first via the generate endpoint.",
        "operationId": "getEvidencePdf",
        "security": [{ "BearerAuth": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/disputeId" }
        ],
        "responses": {
          "307": {
            "description": "Temporary redirect to pre-signed S3 download URL",
            "headers": {
              "Location": {
                "description": "Pre-signed S3 URL (valid for 15 minutes)",
                "schema": { "type": "string", "format": "uri" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/alerts": {
      "get": {
        "tags": ["Alerts"],
        "summary": "List alerts",
        "description": "Returns a paginated list of alerts for the authenticated user's organization, sorted by creation time descending.",
        "operationId": "listAlerts",
        "security": [{ "BearerAuth": [] }],
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "description": "Filter by alert type: `geofence`, `delivery`, `dispute`, `system`",
            "schema": { "type": "string" }
          },
          {
            "name": "severity",
            "in": "query",
            "description": "Filter by severity: `info`, `warning`, `critical`",
            "schema": { "type": "string" }
          },
          {
            "name": "unread_only",
            "in": "query",
            "description": "If true, only return unread alerts",
            "schema": { "type": "boolean" }
          },
          {
            "name": "tag_id",
            "in": "query",
            "description": "Filter by tag UUID",
            "schema": { "type": "string", "format": "uuid" }
          },
          { "$ref": "#/components/parameters/page" },
          { "$ref": "#/components/parameters/limit" }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of alerts",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponsePaginatedAlert"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/alerts/summary": {
      "get": {
        "tags": ["Alerts"],
        "summary": "Alert counts (unread, critical)",
        "description": "Returns summary counts: total alerts, unread count, and critical unread count.",
        "operationId": "alertSummary",
        "security": [{ "BearerAuth": [] }],
        "responses": {
          "200": {
            "description": "Alert summary",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/AlertSummary" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/alerts/{id}/read": {
      "patch": {
        "tags": ["Alerts"],
        "summary": "Mark alert as read",
        "description": "Sets `read_at` on a single alert. Returns 404 if already read or not found.",
        "operationId": "markAlertRead",
        "security": [{ "BearerAuth": [] }],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Alert UUID",
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "responses": {
          "200": {
            "description": "Alert marked as read",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/MarkReadResponse" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/alerts/read-all": {
      "post": {
        "tags": ["Alerts"],
        "summary": "Mark all alerts as read",
        "description": "Sets `read_at` on all unread alerts for the authenticated user's organization.",
        "operationId": "markAllAlertsRead",
        "security": [{ "BearerAuth": [] }],
        "responses": {
          "200": {
            "description": "Number of alerts marked as read",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "marked_read": {
                              "type": "integer",
                              "format": "int64",
                              "description": "Number of alerts that were marked as read"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/analytics/dashboard-v2": {
      "get": {
        "tags": ["Analytics"],
        "summary": "Full dashboard metrics",
        "description": "Returns all metrics for the redesigned dashboard: shipment volume, carrier breakdown with grades, top destinations, open disputes, and fulfillment pipeline timing.",
        "operationId": "dashboardV2Metrics",
        "security": [{ "BearerAuth": [] }],
        "responses": {
          "200": {
            "description": "Dashboard metrics",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/DashboardV2Metrics" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/analytics/delivery": {
      "get": {
        "tags": ["Analytics"],
        "summary": "Delivery stats",
        "description": "Returns delivery statistics for the last 30 days: total deliveries, success rate, disputed deliveries, and average transit time.",
        "operationId": "deliveryStats",
        "security": [{ "BearerAuth": [] }],
        "parameters": [
          {
            "name": "start_date",
            "in": "query",
            "description": "Start date for filtering (ISO 8601 date string)",
            "schema": { "type": "string", "format": "date" }
          },
          {
            "name": "end_date",
            "in": "query",
            "description": "End date for filtering (ISO 8601 date string)",
            "schema": { "type": "string", "format": "date" }
          }
        ],
        "responses": {
          "200": {
            "description": "Delivery statistics",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/DeliveryStats" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/risk/assess": {
      "post": {
        "tags": ["Risk"],
        "summary": "Score a single address",
        "description": "Assesses a delivery address for risk using multiple signals: dispute history, address type, crime index, GPS proof, carrier data, and risk network. Accepts both JWT (dashboard) and API key (external integrations) auth.",
        "operationId": "assessAddress",
        "security": [
          { "BearerAuth": [] },
          { "ApiKeyAuth": [] }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/AssessAddressRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Risk assessment result",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/RiskAssessmentResult" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/risk/batch": {
      "post": {
        "tags": ["Risk"],
        "summary": "Score multiple addresses",
        "description": "Batch risk assessment for up to 100 addresses. Accepts both JWT and API key auth.",
        "operationId": "assessBatch",
        "security": [
          { "BearerAuth": [] },
          { "ApiKeyAuth": [] }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/BatchAssessRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Batch assessment results",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/BatchAssessResponse" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/risk/address/{hash}": {
      "get": {
        "tags": ["Risk"],
        "summary": "Get cached assessment by address hash",
        "description": "Retrieves a previously computed risk score by its address hash. Returns 404 if the address has not been scored.",
        "operationId": "getAddressScore",
        "security": [
          { "BearerAuth": [] },
          { "ApiKeyAuth": [] }
        ],
        "parameters": [
          {
            "name": "hash",
            "in": "path",
            "required": true,
            "description": "SHA-256 hash of the normalized address",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Cached risk score",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/AddressRiskScore" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/risk/dashboard": {
      "get": {
        "tags": ["Risk"],
        "summary": "Risk dashboard metrics",
        "description": "Returns risk scoring dashboard metrics: total scored, counts by risk level, average score, and recent assessments.",
        "operationId": "riskDashboard",
        "security": [{ "BearerAuth": [] }],
        "responses": {
          "200": {
            "description": "Risk dashboard metrics",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/RiskDashboardMetrics" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/public/tracking/shipment/{name}/{tracking_number}": {
      "get": {
        "tags": ["Public Tracking"],
        "summary": "View shipment tracking",
        "description": "Returns tracking data for a multi-tag shipment by carrier name and tracking number. Includes all tags with pings, carrier events, and progress. The `name` parameter is the carrier slug (e.g. `fedex`). For shorter URLs, the frontend supports `/shipment/{trackingNumber}` which rewrites to this endpoint with a placeholder name. No authentication required.",
        "operationId": "getShipmentTracking",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "description": "Carrier name slug (e.g. `fedex`, `ups`, `usps`) or `_` placeholder for short URLs",
            "schema": { "type": "string" }
          },
          {
            "name": "tracking_number",
            "in": "path",
            "required": true,
            "description": "Master tracking number",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Shipment tracking data with all tags",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/ShipmentTrackingResponse" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/public/tracking/tag/{serial}": {
      "get": {
        "tags": ["Public Tracking"],
        "summary": "View tag tracking",
        "description": "Returns public tracking data for a tag by its BLE serial identifier. Includes pings (capped at completion time for completed tags), carrier events, origin/destination, and order context if linked to a Shopify order. No authentication required.",
        "operationId": "getTagTracking",
        "parameters": [
          {
            "name": "serial",
            "in": "path",
            "required": true,
            "description": "Tag BLE identifier (serial number)",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Tag tracking data",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/TagTrackingResponse" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/public/tracking/order/{shop_slug}/{order_number}": {
      "get": {
        "tags": ["Public Tracking"],
        "summary": "View order tracking",
        "description": "Returns tracking data for all tags linked to a Shopify order. The `shop_slug` is the Shopify store subdomain (e.g. `dispotag-dev` for `dispotag-dev.myshopify.com`). No authentication required.",
        "operationId": "getOrderTracking",
        "parameters": [
          {
            "name": "shop_slug",
            "in": "path",
            "required": true,
            "description": "Shopify store subdomain (e.g. `dispotag-dev`)",
            "schema": { "type": "string" }
          },
          {
            "name": "order_number",
            "in": "path",
            "required": true,
            "description": "Shopify order number",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Order tracking data with all tags",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/OrderTrackingResponse" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/public/tracking/tag/{serial}/subscribe": {
      "post": {
        "tags": ["Public Tracking"],
        "summary": "Subscribe to SMS alerts",
        "description": "Subscribes a phone number to geofence SMS notifications for a tag. Up to 3 subscribers per tag. Sends a confirmation MMS with a tracking link. No authentication required.",
        "operationId": "subscribeNotifications",
        "parameters": [
          {
            "name": "serial",
            "in": "path",
            "required": true,
            "description": "Tag BLE identifier (serial number)",
            "schema": { "type": "string" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["phone"],
                "properties": {
                  "phone": {
                    "type": "string",
                    "description": "Phone number (minimum 10 characters)",
                    "example": "+15551234567"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Subscription confirmed",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/SubscribeResponse" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": {
            "description": "Maximum subscribers (3) reached for this tag",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/public/tracking/shipment/{tracking_number}/subscribe": {
      "post": {
        "tags": ["Public Tracking"],
        "summary": "Subscribe to shipment SMS alerts",
        "description": "Subscribes a phone number to delivery notifications for all tags in a shipment. No authentication required.",
        "operationId": "subscribeShipmentNotifications",
        "parameters": [
          {
            "name": "tracking_number",
            "in": "path",
            "required": true,
            "description": "Master tracking number",
            "schema": { "type": "string" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["phone"],
                "properties": {
                  "phone": {
                    "type": "string",
                    "description": "Phone number (minimum 10 characters)",
                    "example": "+15551234567"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Subscription confirmed",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/SubscribeResponse" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/webhooks/subscribe": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "Register webhook callback",
        "description": "Zapier REST hook subscribe endpoint. Creates a new webhook subscription that will receive events via HTTP POST with HMAC-SHA256 signed payloads. Authenticated via API key.",
        "operationId": "webhookSubscribe",
        "security": [{ "ApiKeyAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CreateWebhookSubscriptionRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Subscription created",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "$ref": "#/components/schemas/CreateWebhookSubscriptionResponse" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/webhooks/subscribe/{id}": {
      "delete": {
        "tags": ["Webhooks"],
        "summary": "Unsubscribe webhook",
        "description": "Zapier REST hook unsubscribe endpoint. Deactivates a webhook subscription. Authenticated via API key.",
        "operationId": "webhookUnsubscribe",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Webhook subscription UUID",
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "responses": {
          "204": { "description": "Subscription deactivated" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/webhooks/events": {
      "get": {
        "tags": ["Webhooks"],
        "summary": "List recent events",
        "description": "Zapier performList endpoint. Returns recent webhook events for polling fallback and sample data during Zap setup. Returns a raw JSON array (not wrapped in ApiResponse). Authenticated via API key.",
        "operationId": "webhookListEvents",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "event_type",
            "in": "query",
            "description": "Filter by event type (e.g. `tag.delivered`, `dispute.opened`)",
            "schema": { "type": "string" }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum number of events to return (default: 50)",
            "schema": { "type": "integer", "default": 50 }
          }
        ],
        "responses": {
          "200": {
            "description": "Raw array of recent webhook events (not wrapped in ApiResponse)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "description": "Webhook event payload. Structure varies by event type.",
                    "properties": {
                      "id": { "type": "string", "format": "uuid" },
                      "event_type": { "type": "string" },
                      "organization_id": { "type": "string", "format": "uuid" },
                      "data": { "type": "object" },
                      "created_at": { "type": "string", "format": "date-time" }
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/webhook-subscriptions": {
      "get": {
        "tags": ["Webhooks"],
        "summary": "List active subscriptions",
        "description": "Returns all webhook subscriptions for the authenticated user's organization. Authenticated via JWT.",
        "operationId": "listWebhookSubscriptions",
        "security": [{ "BearerAuth": [] }],
        "responses": {
          "200": {
            "description": "List of webhook subscriptions",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ApiResponseEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": { "$ref": "#/components/schemas/WebhookSubscription" }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/v1/webhook-subscriptions/{id}/deliveries": {
      "get": {
        "tags": ["Webhooks"],
        "summary": "List delivery attempts",
        "description": "Returns paginated delivery attempts for a specific webhook subscription. Used for debugging failed deliveries.",
        "operationId": "listWebhookDeliveries",
        "security": [{ "BearerAuth": [] }],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Webhook subscription UUID",
            "schema": { "type": "string", "format": "uuid" }
          },
          { "$ref": "#/components/parameters/page" },
          { "$ref": "#/components/parameters/limit" }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of delivery attempts",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponsePaginatedWebhookDelivery"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "WorkOS JWT token. Obtain via WorkOS authentication flow."
      },
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Organization API key for programmatic access."
      }
    },
    "parameters": {
      "page": {
        "name": "page",
        "in": "query",
        "description": "Page number (default: 1)",
        "schema": { "type": "integer", "default": 1, "minimum": 1 }
      },
      "limit": {
        "name": "limit",
        "in": "query",
        "description": "Items per page (default: 50, max: 100)",
        "schema": { "type": "integer", "default": 50, "minimum": 1, "maximum": 100 }
      },
      "tagId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Tag UUID",
        "schema": { "type": "string", "format": "uuid" }
      },
      "shipmentId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Shipment UUID",
        "schema": { "type": "string", "format": "uuid" }
      },
      "disputeId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Dispute UUID",
        "schema": { "type": "string", "format": "uuid" }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Authentication required or invalid token",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" }
          }
        }
      },
      "Forbidden": {
        "description": "Insufficient permissions",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" }
          }
        }
      },
      "BadRequest": {
        "description": "Invalid request parameters",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" }
          }
        }
      },
      "Conflict": {
        "description": "Resource conflict (e.g. duplicate or already assigned)",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" }
          }
        }
      },
      "ValidationError": {
        "description": "Validation error",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" }
          }
        }
      },
      "InternalError": {
        "description": "Internal server error",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" }
          }
        }
      }
    },
    "schemas": {
      "ApiResponseEnvelope": {
        "type": "object",
        "required": ["success"],
        "properties": {
          "success": { "type": "boolean", "example": true },
          "error": { "type": "string", "description": "Error message (only present on failure)" }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": false },
          "error": { "type": "string", "example": "Resource not found" }
        }
      },
      "PaginationMeta": {
        "type": "object",
        "properties": {
          "page": { "type": "integer", "example": 1 },
          "limit": { "type": "integer", "example": 50 },
          "total": { "type": "integer", "format": "int64", "example": 142 },
          "total_pages": { "type": "integer", "example": 3 }
        }
      },
      "Tag": {
        "type": "object",
        "required": ["id", "organization_id", "ble_identifier", "state", "geofence_radius_m", "created_at", "metadata"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "organization_id": { "type": "string", "format": "uuid" },
          "ble_identifier": { "type": "string", "description": "BLE MAC address or custom serial" },
          "network_device_id": { "type": "string", "description": "Hubble network device ID (serialized from hubble_device_id)" },
          "order_id": { "type": "string" },
          "recipient_name": { "type": "string" },
          "recipient_phone": { "type": "string" },
          "recipient_email": { "type": "string" },
          "state": {
            "type": "string",
            "enum": ["awaiting_first_ping", "at_origin", "in_transit", "approaching", "delivered", "delivery_verification", "disputed_in_transit", "completed"]
          },
          "origin_lat": { "type": "number", "format": "double" },
          "origin_lng": { "type": "number", "format": "double" },
          "origin_address": { "type": "string" },
          "destination_lat": { "type": "number", "format": "double" },
          "destination_lng": { "type": "number", "format": "double" },
          "destination_address": { "type": "string" },
          "geofence_radius_m": { "type": "integer", "description": "Geofence radius in meters (default 8047 = 5 miles)" },
          "created_at": { "type": "string", "format": "date-time" },
          "shipped_at": { "type": "string", "format": "date-time" },
          "delivered_at": { "type": "string", "format": "date-time" },
          "metadata": { "type": "object", "description": "Arbitrary JSON metadata" },
          "network_last_checked": { "type": "string", "format": "date-time", "description": "Last Hubble poll time (serialized from hubble_last_checked)" },
          "archived_at": { "type": "string", "format": "date-time" },
          "shipment_package_id": { "type": "string", "format": "uuid" }
        }
      },
      "LastPing": {
        "type": "object",
        "required": ["time", "lat", "lng", "source"],
        "properties": {
          "time": { "type": "string", "format": "date-time" },
          "lat": { "type": "number", "format": "double" },
          "lng": { "type": "number", "format": "double" },
          "source": {
            "type": "string",
            "description": "Ping source (remapped: `hubble` -> `network`)",
            "enum": ["network", "sdk_ios", "sdk_android"]
          }
        }
      },
      "TagWithLastPing": {
        "description": "Tag object with all Tag fields plus last_ping, ping_count, and recent_pings flattened at the top level.",
        "allOf": [
          { "$ref": "#/components/schemas/Tag" },
          {
            "type": "object",
            "properties": {
              "last_ping": { "$ref": "#/components/schemas/LastPing" },
              "ping_count": { "type": "integer", "format": "int64", "description": "Total number of pings for this tag" },
              "recent_pings": {
                "type": "array",
                "description": "All pings sorted ASC (only populated on tag detail, empty on list)",
                "items": { "$ref": "#/components/schemas/LastPing" }
              }
            }
          }
        ]
      },
      "TagStats": {
        "type": "object",
        "properties": {
          "in_transit": { "type": "integer", "format": "int64" },
          "approaching": { "type": "integer", "format": "int64" },
          "delivered": { "type": "integer", "format": "int64" },
          "disputed": { "type": "integer", "format": "int64" },
          "at_origin": { "type": "integer", "format": "int64" },
          "total": { "type": "integer", "format": "int64" }
        }
      },
      "TagGroup": {
        "type": "object",
        "properties": {
          "shipment_id": { "type": "string", "format": "uuid", "nullable": true },
          "aggregate_state": { "type": "string", "description": "Worst state across all tags in the group" },
          "last_seen": { "type": "string", "format": "date-time", "nullable": true },
          "tags": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/TagWithLastPing" }
          },
          "carrier": { "type": "string", "nullable": true },
          "tracking_number": { "type": "string", "nullable": true },
          "destination_address": { "type": "string", "nullable": true }
        }
      },
      "GroupedTagsResponse": {
        "type": "object",
        "properties": {
          "groups": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/TagGroup" }
          }
        }
      },
      "UpdateTagRequest": {
        "type": "object",
        "description": "All fields are optional. Only provided fields are updated.",
        "properties": {
          "order_id": { "type": "string", "maxLength": 128 },
          "recipient_name": { "type": "string", "maxLength": 255 },
          "recipient_phone": { "type": "string", "maxLength": 20 },
          "recipient_email": { "type": "string", "format": "email", "maxLength": 255 },
          "origin_lat": { "type": "number", "format": "double", "minimum": -90, "maximum": 90 },
          "origin_lng": { "type": "number", "format": "double", "minimum": -180, "maximum": 180 },
          "origin_address": { "type": "string", "maxLength": 500 },
          "destination_lat": { "type": "number", "format": "double", "minimum": -90, "maximum": 90 },
          "destination_lng": { "type": "number", "format": "double", "minimum": -180, "maximum": 180 },
          "destination_address": { "type": "string", "maxLength": 500 },
          "geofence_radius_m": { "type": "integer", "minimum": 100, "maximum": 100000 },
          "metadata": { "type": "object" }
        }
      },
      "JourneyPoint": {
        "type": "object",
        "required": ["time", "lat", "lng", "source"],
        "properties": {
          "time": { "type": "string", "format": "date-time" },
          "lat": { "type": "number", "format": "double" },
          "lng": { "type": "number", "format": "double" },
          "source": { "type": "string" },
          "accuracy_m": { "type": "number", "format": "double", "description": "Horizontal GPS accuracy in meters" }
        }
      },
      "TagJourneyResponse": {
        "type": "object",
        "required": ["tag_id", "points", "total_points"],
        "properties": {
          "tag_id": { "type": "string", "format": "uuid" },
          "points": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/JourneyPoint" }
          },
          "total_points": { "type": "integer", "format": "int64" }
        }
      },
      "EvidenceLocation": {
        "type": "object",
        "required": ["time", "lat", "lng", "source"],
        "properties": {
          "time": { "type": "string", "format": "date-time" },
          "lat": { "type": "number", "format": "double" },
          "lng": { "type": "number", "format": "double" },
          "source": { "type": "string" },
          "accuracy_m": { "type": "number", "format": "double" }
        }
      },
      "EvidencePackage": {
        "type": "object",
        "required": ["tag_id", "dispute_id", "delivery_locations", "evidence_score", "summary"],
        "properties": {
          "tag_id": { "type": "string", "format": "uuid" },
          "dispute_id": { "type": "string", "format": "uuid" },
          "delivery_locations": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/EvidenceLocation" }
          },
          "time_at_destination_secs": {
            "type": "integer",
            "format": "int64",
            "nullable": true,
            "description": "Duration in seconds that the tag was within geofence of destination"
          },
          "distance_from_destination_m": {
            "type": "number",
            "format": "double",
            "nullable": true,
            "description": "Distance in meters from the most recent ping to the destination"
          },
          "evidence_score": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5,
            "description": "Evidence strength (1-5 stars)"
          },
          "summary": { "type": "string", "description": "Human-readable evidence summary" }
        }
      },
      "Shipment": {
        "type": "object",
        "required": ["id", "organization_id", "carrier", "created_at", "updated_at"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "organization_id": { "type": "string", "format": "uuid" },
          "master_tracking_number": { "type": "string" },
          "carrier": { "type": "string", "description": "Carrier identifier: `fedex`, `ups`, `usps`, `dhl`" },
          "order_id": { "type": "string" },
          "recipient_name": { "type": "string" },
          "recipient_email": { "type": "string" },
          "recipient_phone": { "type": "string" },
          "destination_address": { "type": "string" },
          "destination_lat": { "type": "number", "format": "double" },
          "destination_lng": { "type": "number", "format": "double" },
          "origin_address": { "type": "string" },
          "origin_lat": { "type": "number", "format": "double" },
          "origin_lng": { "type": "number", "format": "double" },
          "master_status": { "type": "string", "description": "Aggregate status across all child packages" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "ShipmentPackage": {
        "type": "object",
        "required": ["id", "shipment_id", "sequence_number", "carrier_events", "created_at", "updated_at"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "shipment_id": { "type": "string", "format": "uuid" },
          "tag_id": { "type": "string", "format": "uuid" },
          "sequence_number": { "type": "integer" },
          "child_tracking_number": { "type": "string" },
          "child_status": { "type": "string", "description": "Carrier status for this package" },
          "estimated_delivery": { "type": "string", "format": "date", "description": "Estimated delivery date" },
          "actual_delivery": { "type": "string", "format": "date-time" },
          "carrier_last_checked": { "type": "string", "format": "date-time" },
          "carrier_events": { "type": "object", "description": "JSON array of carrier tracking events" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "ShipmentWithPackages": {
        "description": "Shipment with all its packages. Shipment fields are flattened at the top level via serde(flatten).",
        "allOf": [
          { "$ref": "#/components/schemas/Shipment" },
          {
            "type": "object",
            "properties": {
              "packages": {
                "type": "array",
                "items": { "$ref": "#/components/schemas/ShipmentPackage" }
              }
            }
          }
        ]
      },
      "CreateShipmentRequest": {
        "type": "object",
        "required": ["carrier", "packages"],
        "properties": {
          "master_tracking_number": { "type": "string", "maxLength": 255 },
          "carrier": {
            "type": "string",
            "description": "Carrier: `fedex`, `ups`, `usps`, `dhl`",
            "maxLength": 50
          },
          "order_id": { "type": "string", "maxLength": 255 },
          "recipient_name": { "type": "string", "maxLength": 255 },
          "recipient_email": { "type": "string", "format": "email", "maxLength": 255 },
          "recipient_phone": { "type": "string", "maxLength": 20 },
          "destination_address": { "type": "string", "maxLength": 500 },
          "destination_lat": { "type": "number", "format": "double" },
          "destination_lng": { "type": "number", "format": "double" },
          "origin_address": { "type": "string", "maxLength": 500 },
          "origin_lat": { "type": "number", "format": "double" },
          "origin_lng": { "type": "number", "format": "double" },
          "packages": {
            "type": "array",
            "minItems": 1,
            "items": { "$ref": "#/components/schemas/CreatePackageEntry" }
          }
        }
      },
      "CreatePackageEntry": {
        "type": "object",
        "required": ["tag_identifier"],
        "properties": {
          "tag_identifier": {
            "type": "string",
            "description": "BLE identifier or serial number of the tag to link",
            "maxLength": 64
          },
          "child_tracking_number": {
            "type": "string",
            "description": "Per-box tracking number",
            "maxLength": 255
          }
        }
      },
      "UpdateShipmentRequest": {
        "type": "object",
        "description": "All fields optional. Only provided fields are updated.",
        "properties": {
          "master_tracking_number": { "type": "string", "maxLength": 255 },
          "carrier": { "type": "string", "maxLength": 50 },
          "order_id": { "type": "string", "maxLength": 255 },
          "recipient_name": { "type": "string", "maxLength": 255 },
          "recipient_email": { "type": "string", "format": "email", "maxLength": 255 },
          "recipient_phone": { "type": "string", "maxLength": 20 },
          "destination_address": { "type": "string", "maxLength": 500 },
          "destination_lat": { "type": "number", "format": "double" },
          "destination_lng": { "type": "number", "format": "double" },
          "origin_address": { "type": "string", "maxLength": 500 },
          "origin_lat": { "type": "number", "format": "double" },
          "origin_lng": { "type": "number", "format": "double" }
        }
      },
      "Dispute": {
        "type": "object",
        "required": ["id", "organization_id", "tag_id", "status", "opened_at", "metadata"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "organization_id": { "type": "string", "format": "uuid" },
          "tag_id": { "type": "string", "format": "uuid" },
          "status": {
            "type": "string",
            "enum": ["open", "investigating", "resolved"]
          },
          "type": {
            "type": "string",
            "enum": ["not_delivered", "damaged", "wrong_location", "other"],
            "description": "Serialized from `dispute_type` column"
          },
          "customer_claim": { "type": "string" },
          "resolution": { "type": "string" },
          "evidence_score": { "type": "integer", "minimum": 1, "maximum": 5 },
          "opened_at": { "type": "string", "format": "date-time" },
          "resolved_at": { "type": "string", "format": "date-time" },
          "resolved_by": { "type": "string", "format": "uuid", "description": "User ID who resolved the dispute" },
          "metadata": { "type": "object", "description": "Arbitrary metadata (may contain Shopify dispute info)" },
          "evidence_pdf_key": { "type": "string", "description": "S3 key of the evidence PDF" },
          "evidence_pdf_generated_at": { "type": "string", "format": "date-time" },
          "shopify_submitted_at": { "type": "string", "format": "date-time" },
          "shopify_submission_status": { "type": "string" }
        }
      },
      "ResolveDisputeRequest": {
        "type": "object",
        "required": ["resolution"],
        "properties": {
          "resolution": { "type": "string", "description": "Resolution explanation text" },
          "evidence_score": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5,
            "description": "Evidence strength score (1-5)"
          }
        }
      },
      "DisputeStats": {
        "type": "object",
        "properties": {
          "total": { "type": "integer", "format": "int64" },
          "open": { "type": "integer", "format": "int64" },
          "investigating": { "type": "integer", "format": "int64" },
          "resolved": { "type": "integer", "format": "int64" },
          "avg_resolution_hours": {
            "type": "number",
            "format": "double",
            "nullable": true,
            "description": "Average hours from opened to resolved"
          },
          "evidence_generated": { "type": "integer", "format": "int64", "description": "Count of disputes with evidence PDFs" },
          "shopify_submitted": { "type": "integer", "format": "int64", "description": "Count of disputes submitted to Shopify" },
          "amount_at_risk": { "type": "number", "format": "double", "description": "Total dollar amount of open/investigating disputes" }
        }
      },
      "Alert": {
        "type": "object",
        "required": ["id", "organization_id", "tag_id", "type", "severity", "title", "created_at", "metadata"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "organization_id": { "type": "string", "format": "uuid" },
          "tag_id": { "type": "string", "format": "uuid" },
          "type": {
            "type": "string",
            "enum": ["geofence", "delivery", "dispute", "system"],
            "description": "Alert type (serialized from alert_type column)"
          },
          "severity": {
            "type": "string",
            "enum": ["info", "warning", "critical"]
          },
          "title": { "type": "string" },
          "message": { "type": "string" },
          "read_at": { "type": "string", "format": "date-time" },
          "created_at": { "type": "string", "format": "date-time" },
          "metadata": { "type": "object" }
        }
      },
      "AlertSummary": {
        "type": "object",
        "required": ["total", "unread", "critical_unread"],
        "properties": {
          "total": { "type": "integer", "format": "int64" },
          "unread": { "type": "integer", "format": "int64" },
          "critical_unread": { "type": "integer", "format": "int64" }
        }
      },
      "MarkReadResponse": {
        "type": "object",
        "required": ["id", "read_at"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "read_at": { "type": "string", "format": "date-time" }
        }
      },
      "DashboardV2Metrics": {
        "type": "object",
        "required": ["shipments_sent", "in_transit", "delivered", "carrier_breakdown", "top_destinations", "open_disputes", "fulfillment_pipeline"],
        "properties": {
          "shipments_sent": { "type": "integer", "format": "int64", "description": "Shipments shipped this month" },
          "in_transit": { "type": "integer", "format": "int64" },
          "delivered": { "type": "integer", "format": "int64", "description": "Deliveries this month" },
          "avg_transit_time_days": { "type": "number", "format": "double", "nullable": true },
          "carrier_breakdown": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/CarrierBreakdown" }
          },
          "top_destinations": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/TopDestination" }
          },
          "open_disputes": {
            "type": "array",
            "description": "Up to 5 most recent open disputes",
            "items": { "$ref": "#/components/schemas/OpenDispute" }
          },
          "fulfillment_pipeline": { "$ref": "#/components/schemas/FulfillmentPipeline" }
        }
      },
      "CarrierBreakdown": {
        "type": "object",
        "properties": {
          "carrier": { "type": "string" },
          "volume": { "type": "integer", "format": "int64" },
          "on_time_pct": { "type": "number", "format": "double" },
          "failure_rate": { "type": "number", "format": "double" },
          "avg_transit_days": { "type": "number", "format": "double", "nullable": true },
          "grade": { "type": "string", "description": "Letter grade A-F based on performance" }
        }
      },
      "TopDestination": {
        "type": "object",
        "properties": {
          "city": { "type": "string" },
          "state": { "type": "string" },
          "count": { "type": "integer", "format": "int64" }
        }
      },
      "OpenDispute": {
        "type": "object",
        "properties": {
          "tag_id": { "type": "string", "format": "uuid" },
          "order_id": { "type": "string" },
          "destination_address": { "type": "string" },
          "carrier": { "type": "string" },
          "reason": { "type": "string" },
          "status": { "type": "string" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "FulfillmentPipeline": {
        "type": "object",
        "properties": {
          "order_to_ship_hours": { "type": "number", "format": "double", "nullable": true, "description": "Avg hours from order creation to shipment" },
          "ship_to_last_mile_days": { "type": "number", "format": "double", "nullable": true, "description": "Avg days from in_transit to approaching" },
          "last_mile_days": { "type": "number", "format": "double", "nullable": true, "description": "Avg days from approaching to delivered" },
          "total_days": { "type": "number", "format": "double", "nullable": true, "description": "Total pipeline duration in days" }
        }
      },
      "DeliveryStats": {
        "type": "object",
        "required": ["period", "total_deliveries", "successful_deliveries", "disputed_deliveries", "success_rate"],
        "properties": {
          "period": { "type": "string", "example": "last_30_days" },
          "total_deliveries": { "type": "integer", "format": "int64" },
          "successful_deliveries": { "type": "integer", "format": "int64" },
          "disputed_deliveries": { "type": "integer", "format": "int64" },
          "success_rate": { "type": "number", "format": "double", "description": "Percentage 0-100" },
          "avg_transit_time_hours": { "type": "number", "format": "double", "nullable": true }
        }
      },
      "AssessAddressRequest": {
        "type": "object",
        "required": ["street", "city", "state", "zip"],
        "properties": {
          "street": { "type": "string", "description": "Street address line 1" },
          "city": { "type": "string" },
          "state": { "type": "string", "description": "Two-letter state code" },
          "zip": { "type": "string", "description": "ZIP code" },
          "street2": { "type": "string", "description": "Unit/apt number (optional)" }
        }
      },
      "BatchAssessRequest": {
        "type": "object",
        "required": ["addresses"],
        "properties": {
          "addresses": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": { "$ref": "#/components/schemas/AssessAddressRequest" }
          }
        }
      },
      "RiskAssessmentResult": {
        "type": "object",
        "required": ["address_hash", "address_input", "score", "risk_level", "signals", "reason"],
        "properties": {
          "address_hash": { "type": "string", "description": "SHA-256 hash of the normalized address" },
          "address_input": { "type": "string", "description": "Original address as submitted" },
          "score": { "type": "integer", "format": "int16", "minimum": 0, "maximum": 100, "description": "Overall risk score 0-100" },
          "risk_level": {
            "type": "string",
            "enum": ["low", "medium", "high"],
            "description": "0-39 = low, 40-69 = medium, 70-100 = high"
          },
          "signals": { "$ref": "#/components/schemas/RiskSignals" },
          "reason": { "type": "string", "description": "Human-readable risk explanation" }
        }
      },
      "RiskSignals": {
        "type": "object",
        "description": "Breakdown of six individual risk signals",
        "properties": {
          "address_type": { "$ref": "#/components/schemas/SignalDetail" },
          "crime_index": { "$ref": "#/components/schemas/SignalDetail" },
          "disputes": { "$ref": "#/components/schemas/SignalDetail" },
          "gps_proof": { "$ref": "#/components/schemas/SignalDetail" },
          "carrier_data": { "$ref": "#/components/schemas/SignalDetail" },
          "risk_network": { "$ref": "#/components/schemas/SignalDetail" }
        }
      },
      "SignalDetail": {
        "type": "object",
        "required": ["raw_score", "weight", "weighted_score", "explanation"],
        "properties": {
          "raw_score": { "type": "integer", "format": "int16", "description": "Raw signal score 0-100" },
          "weight": { "type": "number", "format": "double", "description": "Weight applied (0.0-1.0)" },
          "weighted_score": { "type": "number", "format": "double", "description": "Contribution to final score" },
          "explanation": { "type": "string" }
        }
      },
      "BatchAssessResponse": {
        "type": "object",
        "required": ["results", "total", "cached"],
        "properties": {
          "results": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/RiskAssessmentResult" }
          },
          "total": { "type": "integer", "description": "Total addresses processed" },
          "cached": { "type": "integer", "description": "Number of results served from cache" }
        }
      },
      "AddressRiskScore": {
        "type": "object",
        "description": "Persisted risk score for a normalized address",
        "required": ["id", "organization_id", "address_hash", "address_normalized", "address_input", "score", "risk_level", "created_at", "updated_at"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "organization_id": { "type": "string", "format": "uuid" },
          "address_hash": { "type": "string" },
          "address_normalized": { "type": "string" },
          "address_input": { "type": "string" },
          "score": { "type": "integer", "format": "int16" },
          "risk_level": { "type": "string", "enum": ["low", "medium", "high"] },
          "signal_dispute_history": { "type": "integer", "format": "int16" },
          "signal_address_type": { "type": "integer", "format": "int16" },
          "signal_crime_index": { "type": "integer", "format": "int16" },
          "signal_confirmation_rate": { "type": "integer", "format": "int16" },
          "signal_gps_proof": { "type": "integer", "format": "int16" },
          "signal_carrier_data": { "type": "integer", "format": "int16" },
          "signal_risk_network": { "type": "integer", "format": "int16" },
          "signal_details": { "type": "object" },
          "reason": { "type": "string" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "RiskDashboardMetrics": {
        "type": "object",
        "required": ["addresses_scored", "high_risk_count", "medium_risk_count", "low_risk_count", "avg_score", "recent_assessments"],
        "properties": {
          "addresses_scored": { "type": "integer", "format": "int64" },
          "high_risk_count": { "type": "integer", "format": "int64" },
          "medium_risk_count": { "type": "integer", "format": "int64" },
          "low_risk_count": { "type": "integer", "format": "int64" },
          "avg_score": { "type": "number", "format": "double" },
          "recent_assessments": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/RecentAssessment" }
          }
        }
      },
      "RecentAssessment": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "address_input": { "type": "string" },
          "score": { "type": "integer", "format": "int16" },
          "risk_level": { "type": "string" },
          "reason": { "type": "string" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "LocationPoint": {
        "type": "object",
        "required": ["lat", "lng"],
        "properties": {
          "lat": { "type": "number", "format": "double" },
          "lng": { "type": "number", "format": "double" },
          "address": { "type": "string" }
        }
      },
      "PingPoint": {
        "type": "object",
        "required": ["time", "lat", "lng", "source"],
        "properties": {
          "time": { "type": "string", "format": "date-time" },
          "lat": { "type": "number", "format": "double" },
          "lng": { "type": "number", "format": "double" },
          "source": { "type": "string" }
        }
      },
      "TrackingEvent": {
        "type": "object",
        "required": ["timestamp", "status", "description"],
        "properties": {
          "timestamp": { "type": "string", "format": "date-time" },
          "status": {
            "type": "string",
            "enum": ["pending", "picked_up", "in_transit", "out_for_delivery", "delivered", "exception", "unknown"]
          },
          "location": { "type": "string" },
          "description": { "type": "string" }
        }
      },
      "FacilityPoint": {
        "type": "object",
        "required": ["lat", "lng", "name", "address"],
        "properties": {
          "lat": { "type": "number", "format": "double" },
          "lng": { "type": "number", "format": "double" },
          "name": { "type": "string" },
          "address": { "type": "string" },
          "facility_type": { "type": "string" }
        }
      },
      "OrderTagDetail": {
        "type": "object",
        "required": ["serial", "tag_id", "status", "pings", "geofence_radius_m"],
        "properties": {
          "serial": { "type": "string", "description": "BLE identifier" },
          "tag_id": { "type": "string", "description": "Tag UUID as string" },
          "status": { "type": "string" },
          "origin": { "$ref": "#/components/schemas/LocationPoint" },
          "destination": { "$ref": "#/components/schemas/LocationPoint" },
          "current_location": { "$ref": "#/components/schemas/PingPoint" },
          "pings": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/PingPoint" }
          },
          "carrier": { "type": "string" },
          "tracking_number": { "type": "string" },
          "carrier_status": { "type": "string" },
          "expected_delivery": { "type": "string", "description": "Expected delivery date as YYYY-MM-DD string" },
          "geofence_radius_m": { "type": "integer" },
          "carrier_events": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/TrackingEvent" }
          },
          "recipient_name": { "type": "string" },
          "recipient_email": { "type": "string" },
          "recipient_phone": { "type": "string" },
          "facilities": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/FacilityPoint" }
          }
        }
      },
      "OrderContext": {
        "type": "object",
        "required": ["shop_slug", "order_number", "sibling_count"],
        "properties": {
          "shop_slug": { "type": "string" },
          "order_number": { "type": "string" },
          "sibling_count": { "type": "integer", "description": "Number of other tags on the same order" }
        }
      },
      "TagTrackingResponse": {
        "type": "object",
        "required": ["tag"],
        "properties": {
          "tag": { "$ref": "#/components/schemas/OrderTagDetail" },
          "order": { "$ref": "#/components/schemas/OrderContext" }
        }
      },
      "OrderTrackingResponse": {
        "type": "object",
        "required": ["shop", "order_number", "tags"],
        "properties": {
          "shop": { "type": "string" },
          "order_number": { "type": "string" },
          "tags": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/OrderTagDetail" }
          }
        }
      },
      "SubscribeResponse": {
        "type": "object",
        "required": ["subscribed", "thresholds"],
        "properties": {
          "subscribed": { "type": "boolean" },
          "thresholds": {
            "type": "array",
            "description": "Geofence alert thresholds for this subscription",
            "items": { "type": "string" },
            "example": ["5mi", "2mi", "1mi"]
          }
        }
      },
      "CreateWebhookSubscriptionRequest": {
        "type": "object",
        "required": ["target_url"],
        "properties": {
          "target_url": {
            "type": "string",
            "format": "uri",
            "description": "HTTPS URL to receive webhook events"
          },
          "event_types": {
            "type": "array",
            "description": "Event types to subscribe to. If omitted, receives all events.",
            "items": {
              "type": "string",
              "enum": ["tag.state_changed", "tag.delivered", "tag.approaching", "tag.carrier_updated", "tag.delivery_mismatch", "dispute.opened", "dispute.resolved", "dispute.evidence_submitted", "shipment.created", "shipment.delivered", "risk.high_score"]
            }
          },
          "label": {
            "type": "string",
            "description": "Human-readable label for this subscription"
          }
        }
      },
      "CreateWebhookSubscriptionResponse": {
        "type": "object",
        "required": ["id", "target_url", "is_active", "created_at"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "target_url": { "type": "string" },
          "event_types": { "type": "array", "items": { "type": "string" } },
          "label": { "type": "string" },
          "is_active": { "type": "boolean" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "WebhookSubscription": {
        "type": "object",
        "required": ["id", "organization_id", "target_url", "is_active", "created_at", "updated_at"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "organization_id": { "type": "string", "format": "uuid" },
          "target_url": { "type": "string" },
          "event_types": {
            "type": "array",
            "items": { "type": "string" },
            "description": "Subscribed event types (null = all events)"
          },
          "label": { "type": "string" },
          "is_active": { "type": "boolean" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "required": ["id", "subscription_id", "organization_id", "event_type", "event_id", "payload", "status", "attempts", "max_attempts", "created_at"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "subscription_id": { "type": "string", "format": "uuid" },
          "organization_id": { "type": "string", "format": "uuid" },
          "event_type": { "type": "string" },
          "event_id": { "type": "string", "format": "uuid" },
          "payload": { "type": "object", "description": "The event payload that was delivered" },
          "status": {
            "type": "string",
            "enum": ["pending", "delivered", "failed"],
            "description": "Delivery status"
          },
          "attempts": { "type": "integer", "format": "int16" },
          "max_attempts": { "type": "integer", "format": "int16" },
          "last_attempt_at": { "type": "string", "format": "date-time" },
          "next_retry_at": { "type": "string", "format": "date-time" },
          "last_status_code": { "type": "integer", "format": "int16" },
          "last_response_body": { "type": "string" },
          "last_error": { "type": "string" },
          "created_at": { "type": "string", "format": "date-time" },
          "delivered_at": { "type": "string", "format": "date-time" }
        }
      },
      "ApiResponsePaginatedTagWithLastPing": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": true },
          "data": {
            "type": "object",
            "properties": {
              "items": {
                "type": "array",
                "items": { "$ref": "#/components/schemas/TagWithLastPing" }
              },
              "pagination": { "$ref": "#/components/schemas/PaginationMeta" }
            }
          }
        }
      },
      "ApiResponsePaginatedDispute": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": true },
          "data": {
            "type": "object",
            "properties": {
              "items": {
                "type": "array",
                "items": { "$ref": "#/components/schemas/Dispute" }
              },
              "pagination": { "$ref": "#/components/schemas/PaginationMeta" }
            }
          }
        }
      },
      "ApiResponsePaginatedAlert": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": true },
          "data": {
            "type": "object",
            "properties": {
              "items": {
                "type": "array",
                "items": { "$ref": "#/components/schemas/Alert" }
              },
              "pagination": { "$ref": "#/components/schemas/PaginationMeta" }
            }
          }
        }
      },
      "ApiResponsePaginatedWebhookDelivery": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": true },
          "data": {
            "type": "object",
            "properties": {
              "items": {
                "type": "array",
                "items": { "$ref": "#/components/schemas/WebhookDelivery" }
              },
              "pagination": { "$ref": "#/components/schemas/PaginationMeta" }
            }
          }
        }
      }
    }
  }
}
