{
    "openapi": "3.0.3",
    "info": {
        "title": "Turship Tracking API",
        "version": "1.0.0",
        "description": "Bulk shipment tracking, shipment creation and webhooks. Documentation: https://flytfrance.com/developers/tracking-api",
        "contact": {
            "name": "Turship Developer Support",
            "email": "info@turship.com"
        }
    },
    "servers": [
        {
            "url": "https://flytfrance.com/api/v1"
        }
    ],
    "security": [
        {
            "bearerAuth": []
        }
    ],
    "tags": [
        {
            "name": "Tracking"
        },
        {
            "name": "Shipments"
        },
        {
            "name": "Reference"
        }
    ],
    "paths": {
        "/track": {
            "get": {
                "tags": [
                    "Tracking"
                ],
                "summary": "Track up to 25 shipments",
                "operationId": "trackGet",
                "parameters": [
                    {
                        "name": "tracking_numbers",
                        "in": "query",
                        "required": true,
                        "description": "Comma-separated tracking numbers (max 25)",
                        "schema": {
                            "type": "string"
                        },
                        "example": "TS4827193056,TS6388578830"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Tracking results",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/TrackResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Invalid request",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing or invalid API key",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "API key lacks the required scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded (see Retry-After header)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "500": {
                        "description": "Server error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "tags": [
                    "Tracking"
                ],
                "summary": "Bulk track up to 100 shipments",
                "operationId": "trackBulk",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "tracking_numbers"
                                ],
                                "properties": {
                                    "tracking_numbers": {
                                        "type": "array",
                                        "maxItems": 100,
                                        "items": {
                                            "type": "string"
                                        },
                                        "example": [
                                            "TS4827193056",
                                            "TS6388578830"
                                        ]
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Tracking results (same order as requested, duplicates removed)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/TrackResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Invalid request",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing or invalid API key",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "API key lacks the required scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded (see Retry-After header)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "500": {
                        "description": "Server error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/shipments": {
            "get": {
                "tags": [
                    "Shipments"
                ],
                "summary": "List shipments owned by your API key",
                "operationId": "listShipments",
                "parameters": [
                    {
                        "name": "status",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "label_created",
                                "picked_up",
                                "in_transit",
                                "arrived_at_facility",
                                "departed_facility",
                                "customs_clearance",
                                "customs_xray_scan",
                                "customs_documents_required",
                                "customs_duties_due",
                                "customs_hold",
                                "customs_cleared",
                                "out_for_delivery",
                                "delivery_attempted",
                                "on_hold",
                                "delivered",
                                "returned_to_sender",
                                "cancelled"
                            ]
                        }
                    },
                    {
                        "name": "created_from",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "format": "date"
                        }
                    },
                    {
                        "name": "created_to",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "format": "date"
                        }
                    },
                    {
                        "name": "limit",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 100,
                            "default": 25
                        }
                    },
                    {
                        "name": "offset",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 0,
                            "default": 0
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Paginated shipments"
                    },
                    "400": {
                        "description": "Invalid request",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing or invalid API key",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "API key lacks the required scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded (see Retry-After header)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "500": {
                        "description": "Server error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "tags": [
                    "Shipments"
                ],
                "summary": "Create a shipment and get a tracking number",
                "operationId": "createShipment",
                "parameters": [
                    {
                        "name": "Idempotency-Key",
                        "in": "header",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "maxLength": 80
                        },
                        "description": "Retry safely: the same key returns the original shipment instead of creating a duplicate."
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/ShipmentInput"
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Created",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "ok": {
                                            "type": "boolean"
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/Shipment"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "200": {
                        "description": "Idempotent replay — the shipment created earlier with the same Idempotency-Key"
                    },
                    "422": {
                        "description": "Validation failed (error.details lists each invalid field)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing or invalid API key",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "API key lacks the required scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded (see Retry-After header)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "500": {
                        "description": "Server error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/shipments/{tracking_number}": {
            "get": {
                "tags": [
                    "Shipments"
                ],
                "summary": "Full details of one of your shipments",
                "operationId": "getShipment",
                "parameters": [
                    {
                        "name": "tracking_number",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "pattern": "^TS\\d{10}$"
                        },
                        "example": "TS4827193056"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Shipment",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "ok": {
                                            "type": "boolean"
                                        },
                                        "data": {
                                            "$ref": "#/components/schemas/Shipment"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found or not owned by your key",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing or invalid API key",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "API key lacks the required scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded (see Retry-After header)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "500": {
                        "description": "Server error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/shipments/{tracking_number}/cancel": {
            "post": {
                "tags": [
                    "Shipments"
                ],
                "summary": "Cancel a shipment before pickup",
                "operationId": "cancelShipment",
                "parameters": [
                    {
                        "name": "tracking_number",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "pattern": "^TS\\d{10}$"
                        },
                        "example": "TS4827193056"
                    }
                ],
                "requestBody": {
                    "required": false,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "reason": {
                                        "type": "string",
                                        "maxLength": 200
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Cancelled"
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Already picked up — cannot be cancelled via API",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing or invalid API key",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "API key lacks the required scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded (see Retry-After header)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "500": {
                        "description": "Server error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/shipments/{tracking_number}/label": {
            "get": {
                "tags": [
                    "Shipments"
                ],
                "summary": "Signed URL of the printable 4×6 label",
                "operationId": "getLabel",
                "parameters": [
                    {
                        "name": "tracking_number",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "pattern": "^TS\\d{10}$"
                        },
                        "example": "TS4827193056"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Label URL"
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing or invalid API key",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "API key lacks the required scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded (see Retry-After header)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "500": {
                        "description": "Server error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/statuses": {
            "get": {
                "tags": [
                    "Reference"
                ],
                "summary": "Status and service catalogue",
                "security": [],
                "operationId": "listStatuses",
                "responses": {
                    "200": {
                        "description": "Catalogue"
                    }
                }
            }
        }
    },
    "components": {
        "securitySchemes": {
            "bearerAuth": {
                "type": "http",
                "scheme": "bearer",
                "description": "API key from Turship, e.g. \"Authorization: Bearer tsk_live_…\" (X-API-Key header also accepted)"
            }
        },
        "schemas": {
            "Error": {
                "type": "object",
                "properties": {
                    "ok": {
                        "type": "boolean",
                        "example": false
                    },
                    "error": {
                        "type": "object",
                        "properties": {
                            "code": {
                                "type": "string",
                                "enum": [
                                    "unauthorized",
                                    "forbidden",
                                    "invalid_request",
                                    "validation_failed",
                                    "not_found",
                                    "conflict",
                                    "method_not_allowed",
                                    "rate_limited",
                                    "server_error"
                                ]
                            },
                            "message": {
                                "type": "string"
                            },
                            "details": {
                                "type": "object",
                                "additionalProperties": {
                                    "type": "string"
                                }
                            }
                        }
                    }
                }
            },
            "Event": {
                "type": "object",
                "properties": {
                    "status": {
                        "type": "string"
                    },
                    "label": {
                        "type": "string"
                    },
                    "description": {
                        "type": "string"
                    },
                    "location": {
                        "type": "string",
                        "nullable": true
                    },
                    "country": {
                        "type": "string",
                        "nullable": true
                    },
                    "country_name": {
                        "type": "string",
                        "nullable": true
                    },
                    "time": {
                        "type": "string",
                        "format": "date-time"
                    }
                }
            },
            "Tracking": {
                "type": "object",
                "properties": {
                    "found": {
                        "type": "boolean"
                    },
                    "tracking_number": {
                        "type": "string"
                    },
                    "tracking_number_display": {
                        "type": "string"
                    },
                    "reference": {
                        "type": "string",
                        "nullable": true,
                        "description": "Only for shipments owned by your key"
                    },
                    "service": {
                        "type": "object",
                        "properties": {
                            "code": {
                                "type": "string"
                            },
                            "label": {
                                "type": "string"
                            }
                        }
                    },
                    "status": {
                        "type": "object",
                        "properties": {
                            "code": {
                                "type": "string"
                            },
                            "label": {
                                "type": "string"
                            },
                            "type": {
                                "type": "string",
                                "enum": [
                                    "info",
                                    "progress",
                                    "exception",
                                    "success",
                                    "cancelled"
                                ]
                            },
                            "final": {
                                "type": "boolean"
                            },
                            "description": {
                                "type": "string"
                            }
                        }
                    },
                    "progress": {
                        "type": "object",
                        "properties": {
                            "step": {
                                "type": "integer"
                            },
                            "total": {
                                "type": "integer"
                            },
                            "steps": {
                                "type": "array",
                                "items": {
                                    "type": "string"
                                }
                            }
                        }
                    },
                    "origin": {
                        "$ref": "#/components/schemas/Place"
                    },
                    "destination": {
                        "$ref": "#/components/schemas/Place"
                    },
                    "pieces": {
                        "type": "integer"
                    },
                    "weight_kg": {
                        "type": "number",
                        "nullable": true
                    },
                    "estimated_delivery": {
                        "type": "string",
                        "format": "date",
                        "nullable": true
                    },
                    "delivered_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    },
                    "signed_by": {
                        "type": "string",
                        "nullable": true
                    },
                    "last_update": {
                        "type": "string",
                        "format": "date-time"
                    },
                    "events": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Event"
                        }
                    },
                    "tracking_url": {
                        "type": "string",
                        "format": "uri"
                    },
                    "error": {
                        "type": "object",
                        "description": "Present when found = false",
                        "properties": {
                            "code": {
                                "type": "string",
                                "enum": [
                                    "invalid_format",
                                    "not_found"
                                ]
                            },
                            "message": {
                                "type": "string"
                            }
                        }
                    }
                }
            },
            "Place": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string"
                    },
                    "country": {
                        "type": "string"
                    },
                    "country_name": {
                        "type": "string"
                    }
                }
            },
            "TrackResponse": {
                "type": "object",
                "properties": {
                    "ok": {
                        "type": "boolean"
                    },
                    "count": {
                        "type": "integer"
                    },
                    "found": {
                        "type": "integer"
                    },
                    "not_found": {
                        "type": "integer"
                    },
                    "results": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Tracking"
                        }
                    }
                }
            },
            "ShipmentInput": {
                "type": "object",
                "required": [
                    "service_type",
                    "sender_name",
                    "sender_city",
                    "sender_country",
                    "receiver_name",
                    "receiver_city",
                    "receiver_country"
                ],
                "properties": {
                    "reference": {
                        "type": "string",
                        "description": "Reference",
                        "maxLength": 100
                    },
                    "service_type": {
                        "type": "string",
                        "description": "Service",
                        "maxLength": 30,
                        "enum": [
                            "express",
                            "domestic",
                            "air_freight",
                            "sea_freight",
                            "road_freight",
                            "ecommerce"
                        ]
                    },
                    "sender_name": {
                        "type": "string",
                        "description": "Sender name",
                        "maxLength": 150
                    },
                    "sender_company": {
                        "type": "string",
                        "description": "Sender company",
                        "maxLength": 150
                    },
                    "sender_phone": {
                        "type": "string",
                        "description": "Sender phone",
                        "maxLength": 40
                    },
                    "sender_email": {
                        "type": "string",
                        "description": "Sender email",
                        "maxLength": 190,
                        "format": "email"
                    },
                    "sender_address": {
                        "type": "string",
                        "description": "Sender address",
                        "maxLength": 255
                    },
                    "sender_city": {
                        "type": "string",
                        "description": "Sender city",
                        "maxLength": 100
                    },
                    "sender_country": {
                        "type": "string",
                        "description": "Sender country",
                        "maxLength": 2,
                        "pattern": "^[A-Z]{2}$"
                    },
                    "receiver_name": {
                        "type": "string",
                        "description": "Receiver name",
                        "maxLength": 150
                    },
                    "receiver_company": {
                        "type": "string",
                        "description": "Receiver company",
                        "maxLength": 150
                    },
                    "receiver_phone": {
                        "type": "string",
                        "description": "Receiver phone",
                        "maxLength": 40
                    },
                    "receiver_email": {
                        "type": "string",
                        "description": "Receiver email",
                        "maxLength": 190,
                        "format": "email"
                    },
                    "receiver_address": {
                        "type": "string",
                        "description": "Receiver address",
                        "maxLength": 255
                    },
                    "receiver_city": {
                        "type": "string",
                        "description": "Receiver city",
                        "maxLength": 100
                    },
                    "receiver_country": {
                        "type": "string",
                        "description": "Receiver country",
                        "maxLength": 2,
                        "pattern": "^[A-Z]{2}$"
                    },
                    "pieces": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 9999,
                        "description": "Pieces",
                        "default": 1
                    },
                    "weight_kg": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Weight (kg)"
                    },
                    "dimensions_cm": {
                        "type": "string",
                        "description": "Dimensions (cm)",
                        "maxLength": 50
                    },
                    "contents": {
                        "type": "string",
                        "description": "Contents",
                        "maxLength": 255
                    },
                    "declared_value": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Declared value"
                    },
                    "currency": {
                        "type": "string",
                        "description": "Currency",
                        "maxLength": 3
                    },
                    "cod_amount": {
                        "type": "number",
                        "minimum": 0,
                        "description": "COD amount"
                    },
                    "estimated_delivery": {
                        "type": "string",
                        "description": "Estimated delivery",
                        "maxLength": 10,
                        "format": "date"
                    },
                    "notify_email": {
                        "type": "boolean",
                        "default": true,
                        "description": "Send status emails to sender and receiver",
                        "format": "email"
                    }
                }
            },
            "Shipment": {
                "allOf": [
                    {
                        "$ref": "#/components/schemas/Tracking"
                    },
                    {
                        "type": "object",
                        "properties": {
                            "sender": {
                                "type": "object"
                            },
                            "receiver": {
                                "type": "object"
                            },
                            "package": {
                                "type": "object"
                            },
                            "label_url": {
                                "type": "string",
                                "format": "uri"
                            },
                            "created_at": {
                                "type": "string",
                                "format": "date-time"
                            },
                            "notify_email": {
                                "type": "boolean"
                            }
                        }
                    }
                ]
            }
        }
    }
}