{
  "openapi": "3.1.0",
  "info": {
    "title": "TrackLogy API",
    "version": "1.0.0",
    "description": "REST API for TrackLogy — a shipment tracking platform for ecommerce brands.\n\nBase URL: `https://api.tracklogy.com/v1`\n\nFull documentation: https://docs.tracklogy.com/api-reference\n\n**Authentication**\n\nAll Cloud API endpoints require an API key issued from the TrackLogy dashboard (my.tracklogy.com → Settings → API Keys). Send it as the `X-API-Key` request header.\n\n```\nX-API-Key: tlg_your_api_key_here\n```\n\n**Response envelope**\n\nEvery response is a JSON object:\n```json\n{ \"success\": true, \"data\": { ... }, \"message\": \"\" }\n```\nor on error:\n```json\n{ \"success\": false, \"error\": { \"code\": 422, \"message\": \"tracking_number is required\" } }\n```",
    "contact": {
      "name": "TrackLogy Support",
      "url": "https://tracklogy.com/contact",
      "email": "info@tracklogy.com"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://tracklogy.com/legal/terms/"
    },
    "x-deprecation-policy": "https://tracklogy.com/legal/api-versioning/",
    "x-versioning-policy": "URL path versioning (/v1, /v2…). Minimum 12-month deprecation notice. Sunset and Deprecation headers on deprecated endpoints (RFC 8594). See https://tracklogy.com/legal/api-versioning/ for full policy."
  },
  "servers": [
    {
      "url": "https://api.tracklogy.com/v1",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Shipments",
      "description": "Create, list, retrieve, and update shipments for a connected store."
    },
    {
      "name": "Carriers",
      "description": "Browse the list of supported carriers."
    },
    {
      "name": "Public",
      "description": "Unauthenticated endpoints — carrier detection, public plans, contact form."
    }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "API key from TrackLogy dashboard → Settings → API Keys. The key carries named permission scopes; see ScopedAccess for the scope list."
      },
      "ScopedAccess": {
        "type": "oauth2",
        "description": "Named permission scopes for TrackLogy API access. TrackLogy authenticates via X-API-Key; these scopes declare the least-privilege access each endpoint requires so agents can request minimal permissions.",
        "flows": {
          "clientCredentials": {
            "tokenUrl": "https://my.tracklogy.com/api/auth/token",
            "scopes": {
              "shipments:read":      "Read shipment list and tracking events",
              "shipments:write":     "Create and update shipments",
              "carriers:read":       "Read the supported carrier directory",
              "notifications:write": "Trigger delivery notification emails to customers"
            }
          }
        }
      }
    },
    "headers": {
      "RateLimitLimit": {
        "description": "Maximum number of requests allowed in the current window.",
        "schema": { "type": "integer", "example": 600 }
      },
      "RateLimitRemaining": {
        "description": "Number of requests remaining in the current window.",
        "schema": { "type": "integer", "example": 598 }
      },
      "RateLimitReset": {
        "description": "Unix timestamp (seconds) when the rate-limit window resets.",
        "schema": { "type": "integer", "example": 1756147200 }
      }
    },
    "responses": {
      "TooManyRequests": {
        "description": "Rate limit exceeded. Back off until the Retry-After interval has elapsed.",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait before retrying.",
            "schema": { "type": "integer", "example": 60 }
          },
          "RateLimit-Limit":     { "$ref": "#/components/headers/RateLimitLimit" },
          "RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
          "RateLimit-Reset":     { "$ref": "#/components/headers/RateLimitReset" }
        },
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": { "type": "boolean", "example": false },
                "error": {
                  "type": "object",
                  "properties": {
                    "code": { "type": "integer", "example": 429 },
                    "message": { "type": "string", "example": "Rate limit exceeded. Retry after 60 seconds." }
                  }
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "Shipment": {
        "type": "object",
        "description": "A tracked shipment record.",
        "properties": {
          "id": { "type": "integer", "example": 1234 },
          "tracking_number": { "type": "string", "example": "1Z999AA10123456784" },
          "tracking_provider": { "type": "string", "nullable": true, "example": "ups", "description": "Carrier slug (e.g. ups, fedex, dhl). null when not yet resolved." },
          "status": {
            "type": "string",
            "enum": [
              "pending", "unknown", "pre_transit", "in_transit",
              "available_for_pickup", "out_for_delivery", "delivered",
              "failure", "on_hold", "exception", "return_to_sender",
              "cancelled", "expired"
            ],
            "example": "in_transit"
          },
          "source": { "type": "string", "example": "api" },
          "order_id": { "type": "string", "nullable": true, "example": "ORD-5001" },
          "order_number": { "type": "string", "nullable": true, "example": "#5001" },
          "customer_name": { "type": "string", "nullable": true, "example": "Jane Smith" },
          "customer_email": { "type": "string", "format": "email", "nullable": true, "example": "jane@example.com" },
          "customer_phone": { "type": "string", "nullable": true, "example": "+1-555-0100" },
          "destination_country": { "type": "string", "nullable": true, "example": "US" },
          "est_delivery_date": { "type": "string", "format": "date", "nullable": true, "example": "2026-09-01" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "Carrier": {
        "type": "object",
        "properties": {
          "id": { "type": "integer", "example": 7 },
          "name": { "type": "string", "example": "FedEx" },
          "slug": { "type": "string", "example": "fedex" },
          "website": { "type": "string", "nullable": true, "example": "https://www.fedex.com" },
          "image_url": { "type": "string", "nullable": true }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": false },
          "error": {
            "type": "object",
            "properties": {
              "code": { "type": "integer", "example": 422 },
              "message": { "type": "string", "example": "tracking_number is required" }
            }
          }
        }
      }
    }
  },
  "security": [
    { "ApiKeyAuth": [], "ScopedAccess": ["shipments:read", "shipments:write", "carriers:read"] }
  ],
  "paths": {
    "/api/shipments": {
      "post": {
        "tags": ["Shipments"],
        "operationId": "createShipment",
        "summary": "Create a shipment",
        "description": "Add a new shipment for tracking. TrackLogy will auto-detect the carrier from the tracking number if `tracking_provider` is omitted. The shipment enters the queue immediately and the first status update is retrieved within the next polling cycle (usually < 5 minutes).\n\nRequires `store_name` to identify which connected store this shipment belongs to.",
        "security": [{ "ApiKeyAuth": [], "ScopedAccess": ["shipments:write"] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["tracking_number", "store_name"],
                "properties": {
                  "tracking_number": { "type": "string", "example": "1Z999AA10123456784", "description": "The carrier tracking number." },
                  "store_name": { "type": "string", "example": "My WooCommerce Store", "description": "Name of the connected TrackLogy store. Must already exist in your account." },
                  "store_url": { "type": "string", "example": "https://mystore.com", "description": "Alternative to store_name — URL of the connected store." },
                  "tracking_provider": { "type": "string", "example": "ups", "description": "Carrier slug. If omitted, TrackLogy auto-detects from the tracking number format." },
                  "order_id": { "type": "string", "example": "ORD-5001" },
                  "order_number": { "type": "string", "example": "#5001" },
                  "customer_name": { "type": "string", "example": "Jane Smith" },
                  "customer_email": { "type": "string", "format": "email", "example": "jane@example.com" },
                  "customer_phone": { "type": "string", "example": "+1-555-0100" },
                  "customer_address": { "type": "string", "example": "123 Main St, New York, NY 10001" },
                  "destination_country": { "type": "string", "example": "US", "description": "ISO 3166-1 alpha-2 country code." },
                  "postal_code": { "type": "string", "example": "10001" },
                  "est_delivery_date": { "type": "string", "format": "date", "example": "2026-09-01" },
                  "notes": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Shipment created successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean", "example": true },
                    "data": {
                      "type": "object",
                      "properties": {
                        "shipment": { "$ref": "#/components/schemas/Shipment" },
                        "detected_tracking_provider": { "type": "string", "nullable": true, "example": "ups" }
                      }
                    }
                  }
                }
              }
            }
          },
          "402": { "description": "Tracker balance exhausted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
          "404": { "description": "Store not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
          "422": { "description": "Validation error — missing required field.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      },
      "get": {
        "tags": ["Shipments"],
        "operationId": "listShipments",
        "summary": "List shipments",
        "description": "Returns a paginated list of all shipments for the authenticated store.",
        "security": [{ "ApiKeyAuth": [], "ScopedAccess": ["shipments:read"] }],
        "parameters": [
          { "name": "store_name", "in": "query", "required": true, "schema": { "type": "string" }, "example": "My WooCommerce Store" },
          { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50, "maximum": 100 } }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of shipments.",
            "headers": {
              "RateLimit-Limit":     { "$ref": "#/components/headers/RateLimitLimit" },
              "RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
              "RateLimit-Reset":     { "$ref": "#/components/headers/RateLimitReset" }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean", "example": true },
                    "data": {
                      "type": "object",
                      "properties": {
                        "shipments": { "type": "array", "items": { "$ref": "#/components/schemas/Shipment" } },
                        "total": { "type": "integer", "example": 142 },
                        "page": { "type": "integer", "example": 1 },
                        "limit": { "type": "integer", "example": 50 }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/api/shipments/show": {
      "get": {
        "tags": ["Shipments"],
        "operationId": "getShipment",
        "summary": "Get a single shipment",
        "description": "Retrieve a single shipment by tracking number or internal ID.",
        "security": [{ "ApiKeyAuth": [], "ScopedAccess": ["shipments:read"] }],
        "parameters": [
          { "name": "store_name", "in": "query", "required": true, "schema": { "type": "string" }, "example": "My WooCommerce Store" },
          { "name": "tracking_number", "in": "query", "schema": { "type": "string" }, "example": "1Z999AA10123456784", "description": "Provide either tracking_number or id." },
          { "name": "id", "in": "query", "schema": { "type": "integer" }, "example": 1234, "description": "Provide either id or tracking_number." }
        ],
        "responses": {
          "200": {
            "description": "Shipment found.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean", "example": true },
                    "data": { "type": "object", "properties": { "shipment": { "$ref": "#/components/schemas/Shipment" } } }
                  }
                }
              }
            }
          },
          "404": { "description": "Shipment not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/api/shipments/update": {
      "put": {
        "tags": ["Shipments"],
        "operationId": "updateShipment",
        "summary": "Update a shipment",
        "description": "Update mutable fields on an existing shipment. If `status` changes, a tracking event is recorded and customer notifications are dispatched.",
        "security": [{ "ApiKeyAuth": [], "ScopedAccess": ["shipments:write"] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["store_name"],
                "properties": {
                  "store_name": { "type": "string", "example": "My WooCommerce Store" },
                  "tracking_number": { "type": "string", "example": "1Z999AA10123456784", "description": "Provide tracking_number or id." },
                  "id": { "type": "integer", "example": 1234, "description": "Provide id or tracking_number." },
                  "status": {
                    "type": "string",
                    "enum": [
                      "pending", "unknown", "pre_transit", "in_transit",
                      "available_for_pickup", "out_for_delivery", "delivered",
                      "failure", "on_hold", "exception", "return_to_sender",
                      "cancelled", "expired"
                    ],
                    "example": "delivered"
                  },
                  "customer_name": { "type": "string" },
                  "customer_email": { "type": "string", "format": "email" },
                  "customer_phone": { "type": "string" },
                  "est_delivery_date": { "type": "string", "format": "date" },
                  "notes": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Shipment updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean", "example": true },
                    "data": { "type": "object", "properties": { "shipment": { "$ref": "#/components/schemas/Shipment" } } }
                  }
                }
              }
            }
          },
          "404": { "description": "Shipment not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
          "422": { "description": "No updatable fields provided.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/api/carriers": {
      "get": {
        "tags": ["Carriers"],
        "operationId": "listCarriers",
        "summary": "List supported carriers",
        "description": "Returns all carriers supported by TrackLogy for the authenticated account.",
        "security": [{ "ApiKeyAuth": [], "ScopedAccess": ["carriers:read"] }],
        "parameters": [
          { "name": "q", "in": "query", "schema": { "type": "string" }, "example": "fedex", "description": "Search by carrier name or slug." },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50, "maximum": 200 } }
        ],
        "responses": {
          "200": {
            "description": "List of carriers.",
            "headers": {
              "RateLimit-Limit":     { "$ref": "#/components/headers/RateLimitLimit" },
              "RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
              "RateLimit-Reset":     { "$ref": "#/components/headers/RateLimitReset" }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean", "example": true },
                    "data": {
                      "type": "object",
                      "properties": {
                        "carriers": { "type": "array", "items": { "$ref": "#/components/schemas/Carrier" } }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/public/carriers/detect": {
      "post": {
        "tags": ["Public"],
        "operationId": "detectCarrier",
        "summary": "Detect carrier from tracking number",
        "description": "Identify which carrier a tracking number belongs to using pattern matching. No authentication required.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["tracking_number"],
                "properties": {
                  "tracking_number": { "type": "string", "example": "1Z999AA10123456784" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Detection result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean", "example": true },
                    "data": {
                      "type": "object",
                      "properties": {
                        "status": { "type": "string", "enum": ["matched", "no_match"], "example": "matched" },
                        "carrier": { "$ref": "#/components/schemas/Carrier" }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/plans": {
      "get": {
        "tags": ["Public"],
        "operationId": "listPlans",
        "summary": "List pricing plans",
        "description": "Returns all active TrackLogy subscription plans with pricing and feature limits. No authentication required.",
        "security": [],
        "responses": {
          "200": {
            "description": "Available plans.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean", "example": true },
                    "data": {
                      "type": "object",
                      "properties": {
                        "plans": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": { "type": "integer" },
                              "name": { "type": "string", "example": "Starter" },
                              "slug": { "type": "string", "example": "starter" },
                              "price_monthly": { "type": "number", "example": 29 },
                              "price_yearly": { "type": "number", "example": 290 },
                              "limits": {
                                "type": "object",
                                "properties": {
                                  "trackers_per_month": { "type": "integer", "example": 500 },
                                  "stores": { "type": "integer", "example": 3 },
                                  "team_members": { "type": "integer", "example": 2 }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
