{
  "openapi": "3.1.0",
  "info": {
    "title": "Swelo Partner API",
    "version": "1.0.0",
    "description": "Bidirectional fulfillment API for warehouse partners. Partners call the REST endpoints below to push inventory, confirm ASN receipt, and push tracking; Swelo sends signed webhooks (see the `webhooks` section) for fulfillment requests and inbound shipment notices. All requests require a bearer key issued by Swelo; each key carries scopes and a call is rejected 401 if the key lacks the scope for that endpoint.",
    "contact": { "name": "Swelo", "email": "spencer@swelo.co" }
  },
  "servers": [
    { "url": "https://hub.swelo.co/api/partners/v1", "description": "Production" }
  ],
  "security": [ { "bearerAuth": [] } ],
  "tags": [
    { "name": "Inventory", "description": "Push stock levels (scope: inventory:write)." },
    { "name": "Inbound shipments", "description": "Confirm ASN receipt (scope: inbound:write)." },
    { "name": "Shipments", "description": "Push tracking / delivery status (scope: shipments:write)." },
    { "name": "General" }
  ],
  "paths": {
    "/ping": {
      "get": {
        "tags": ["General"],
        "summary": "Verify key",
        "description": "Confirms the key works and returns the scopes it holds. Requires any valid key.",
        "responses": {
          "200": {
            "description": "Key is valid.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PingResult" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/inventory": {
      "post": {
        "tags": ["Inventory"],
        "summary": "Push inventory",
        "description": "Send current stock levels keyed on the partner's warehouse SKU. Swelo upserts on sku + warehouse_code. Scope: inventory:write.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["items"],
                "properties": {
                  "items": {
                    "type": "array",
                    "maxItems": 5000,
                    "items": { "$ref": "#/components/schemas/InventoryItem" }
                  }
                }
              },
              "example": { "items": [ { "sku": "MOSH-TEE-BLK-L", "warehouse_code": "MOSH-1", "quantity_on_hand": 500, "quantity_available": 480, "quantity_inbound": 0 } ] }
            }
          }
        },
        "responses": {
          "200": { "description": "Applied.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InventoryResult" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/inbound-shipments/{reference}/receive": {
      "post": {
        "tags": ["Inbound shipments"],
        "summary": "Confirm ASN receipt",
        "description": "Confirm receipt of the goods described in an asn.created webhook. Records counted quantities and marks the ASN received. Does not set inventory (report on-hand via /inventory). Scope: inbound:write.",
        "parameters": [
          { "name": "reference", "in": "path", "required": true, "schema": { "type": "string" }, "description": "The ASN reference, e.g. ASN-000123.", "example": "ASN-000123" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["items"],
                "properties": {
                  "items": { "type": "array", "items": { "$ref": "#/components/schemas/ReceiveItem" } },
                  "received_at": { "type": "string", "format": "date-time", "description": "Defaults to now." },
                  "notes": { "type": "string" }
                }
              },
              "example": { "received_at": "2026-08-01T15:00:00Z", "notes": "2 units damaged", "items": [ { "warehouse_sku": "MOSH-TEE-BLK-L", "quantity_received": 198 } ] }
            }
          }
        },
        "responses": {
          "200": { "description": "Received.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReceiveResult" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": { "description": "The ASN is cancelled.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/shipments": {
      "post": {
        "tags": ["Shipments"],
        "summary": "Push shipment status",
        "description": "Advance a shipment (from a fulfillment.requested webhook) and attach tracking. Routed by reference prefix: SWL- storefront order, SND- Send-Products. Scope: shipments:write. tracking_url and pod_url must be absolute http(s) URLs — anything else (javascript:, data:, relative paths) is rejected with 400 and nothing is written. A tracking_url on an unrecognized carrier host is accepted, but shoppers won't see the link (the milestone email and storefront order pages omit it); it stays visible to staff in the Swelo admin.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ShipmentUpdate" },
              "example": { "reference": "SWL-004215", "milestone": "shipped", "tracking_url": "https://www.ups.com/track?tracknum=1Z...", "tracking_carrier": "UPS" }
            }
          }
        },
        "responses": {
          "200": { "description": "Updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ShipmentResult" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    }
  },
  "webhooks": {
    "fulfillment.requested": {
      "post": {
        "summary": "An order needs to be fulfilled from the warehouse.",
        "description": "Sent when a storefront order or Send-Products request is ready to ship. Fulfill it, then report status via POST /shipments using data.reference.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  { "$ref": "#/components/schemas/WebhookEnvelope" },
                  { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/FulfillmentRequest" } } }
                ]
              }
            }
          }
        },
        "responses": { "200": { "description": "Acknowledged. Return any 2xx." } }
      }
    },
    "asn.created": {
      "post": {
        "summary": "Stock is inbound to the warehouse (Advanced Shipping Notice).",
        "description": "Sent when products are shipped into the warehouse to be received and inventoried. After receiving, confirm via POST /inbound-shipments/{reference}/receive.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  { "$ref": "#/components/schemas/WebhookEnvelope" },
                  { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/AdvancedShippingNotice" } } }
                ]
              }
            }
          }
        },
        "responses": { "200": { "description": "Acknowledged. Return any 2xx." } }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key Swelo issues you (format swlo_live_…). Send as `Authorization: Bearer <key>`. Each key holds scopes: inventory:write, inbound:write, shipments:write."
      }
    },
    "responses": {
      "Unauthorized": { "description": "Missing/invalid/revoked key, or the key lacks the required scope.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "ok": false, "error": "Unauthorized." } } } },
      "BadRequest": { "description": "Malformed body or invalid field.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "NotFound": { "description": "The referenced order / ASN / shipment was not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": { "ok": { "type": "boolean", "const": false }, "error": { "type": "string" } },
        "required": ["ok", "error"]
      },
      "PingResult": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean" },
          "partner": { "type": "string" },
          "key": { "type": "string" },
          "scopes": { "type": "array", "items": { "type": "string" } },
          "timestamp": { "type": "string", "format": "date-time" }
        }
      },
      "InventoryItem": {
        "type": "object",
        "required": ["sku"],
        "properties": {
          "sku": { "type": "string", "description": "The partner's warehouse SKU." },
          "warehouse_code": { "type": "string", "default": "DEFAULT", "description": "Location code." },
          "quantity_on_hand": { "type": "integer", "minimum": 0 },
          "quantity_available": { "type": "integer", "minimum": 0, "description": "Sellable units. Defaults to on_hand, then 0." },
          "quantity_inbound": { "type": "integer", "minimum": 0 }
        }
      },
      "InventoryResult": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean" },
          "item_count": { "type": "integer" },
          "updated": { "type": "integer" },
          "unmatched": { "type": "array", "items": { "type": "string" }, "description": "SKUs Swelo couldn't map (skipped)." }
        }
      },
      "ReceiveItem": {
        "type": "object",
        "required": ["warehouse_sku", "quantity_received"],
        "properties": {
          "warehouse_sku": { "type": "string" },
          "quantity_received": { "type": "integer", "minimum": 0 }
        }
      },
      "ReceiveResult": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean" },
          "reference": { "type": "string" },
          "status": { "type": "string", "const": "received" },
          "matched": { "type": "integer" },
          "unmatched": { "type": "array", "items": { "type": "string" } }
        }
      },
      "ShipmentUpdate": {
        "type": "object",
        "required": ["reference", "milestone"],
        "properties": {
          "reference": { "type": "string", "description": "SWL- (order) or SND- (Send-Products)." },
          "order_id": { "type": "string", "description": "Alternative to reference for storefront orders." },
          "milestone": { "$ref": "#/components/schemas/Milestone" },
          "tracking_url": { "type": "string", "format": "uri", "description": "On shipped. Must be an absolute http(s) URL or the request is rejected with 400. Links whose host is not a known carrier domain are accepted and stored, but are not shown to shoppers (milestone email and storefront order pages omit them)." },
          "tracking_carrier": { "type": "string", "description": "On shipped." },
          "pod_url": { "type": "string", "format": "uri", "description": "Proof-of-delivery link. On delivered. Must be an absolute http(s) URL or the request is rejected with 400." },
          "delivered_at": { "type": "string", "format": "date-time", "description": "On delivered." }
        }
      },
      "ShipmentResult": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean" },
          "kind": { "type": "string", "enum": ["order", "fulfillment_order"] },
          "reference": { "type": "string" },
          "status": { "type": "string" }
        }
      },
      "Milestone": {
        "type": "string",
        "enum": ["order_confirmed", "processing", "shipped", "out_for_delivery", "delivered", "exception"],
        "description": "The shipment status to advance to."
      },
      "WebhookEnvelope": {
        "type": "object",
        "required": ["id", "type", "created", "data"],
        "properties": {
          "id": { "type": "string", "description": "Unique event id (evt_…). Use for idempotency." },
          "type": { "type": "string", "description": "Event type, e.g. asn.created." },
          "created": { "type": "integer", "description": "Unix seconds." },
          "data": { "type": "object" }
        }
      },
      "LineItem": {
        "type": "object",
        "required": ["product_name", "quantity"],
        "properties": {
          "warehouse_sku": { "type": ["string", "null"], "description": "The partner SKU. May be null for newly sourced products." },
          "product_name": { "type": "string" },
          "variant_name": { "type": ["string", "null"], "description": "e.g. \"Black / L\"." },
          "quantity": { "type": "integer", "minimum": 1 }
        }
      },
      "ShipTo": {
        "type": "object",
        "properties": {
          "recipient_name": { "type": ["string", "null"] },
          "line1": { "type": ["string", "null"] },
          "line2": { "type": ["string", "null"] },
          "city": { "type": ["string", "null"] },
          "state": { "type": ["string", "null"] },
          "postal_code": { "type": ["string", "null"] },
          "country": { "type": ["string", "null"] },
          "phone": { "type": ["string", "null"] }
        }
      },
      "FulfillmentRequest": {
        "type": "object",
        "properties": {
          "type": { "type": "string", "enum": ["storefront_order", "send_products"] },
          "reference": { "type": "string" },
          "client": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } } },
          "ship_to": { "$ref": "#/components/schemas/ShipTo" },
          "items": { "type": "array", "items": { "$ref": "#/components/schemas/LineItem" } },
          "shipping_method": { "type": ["string", "null"], "enum": ["economy", "expedited", "overnight", null], "description": "Buyer-requested service level on send_products shipments. economy = least expensive qualifying (ground) service. Absent or null on events emitted before this field existed — treat as economy." },
          "notes": { "type": ["string", "null"] }
        }
      },
      "AdvancedShippingNotice": {
        "type": "object",
        "properties": {
          "reference": { "type": "string" },
          "po_number": { "type": ["string", "null"] },
          "client": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } } },
          "expected_arrival": { "type": ["string", "null"], "format": "date" },
          "carrier": { "type": ["string", "null"] },
          "tracking_url": { "type": ["string", "null"], "format": "uri" },
          "items": { "type": "array", "items": { "$ref": "#/components/schemas/LineItem" } },
          "notes": { "type": ["string", "null"] }
        }
      }
    }
  }
}
