{
    "openapi": "3.0.3",
    "info": {
        "title": "TRANSiA Platform API",
        "version": "1.0.0",
        "description": "Sovereign multi-modal mobility APIs. Generated from the live route table."
    },
    "servers": [
        {
            "url": "https://transia1.com",
            "description": "This deployment"
        }
    ],
    "components": {
        "securitySchemes": {
            "ApiKeyAuth": {
                "type": "http",
                "scheme": "bearer",
                "description": "Vendor API key. May also be sent as X-API-Key."
            },
            "BearerJwt": {
                "type": "http",
                "scheme": "bearer",
                "bearerFormat": "JWT",
                "description": "Device-bound access token. Send X-Device-Id alongside it."
            }
        }
    },
    "paths": {
        "/api/auth/me": {
            "get": {
                "summary": "The account behind the current access token.",
                "operationId": "api.auth.me",
                "tags": [
                    "Auth"
                ],
                "security": [
                    {
                        "BearerJwt": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "example": {
                                    "user": {
                                        "id": 42,
                                        "name": "Jose Ramirez",
                                        "email": "jose@example.ca",
                                        "role": "citizen",
                                        "region": "ON",
                                        "kyc_level": 2
                                    },
                                    "token": {
                                        "jti": "b1f0...",
                                        "expires_at": 1767225600,
                                        "device_bound": true
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing, invalid or revoked credentials"
                    },
                    "422": {
                        "description": "Validation failed"
                    },
                    "429": {
                        "description": "Rate limit exceeded"
                    }
                }
            }
        },
        "/api/auth/token": {
            "post": {
                "summary": "Exchange credentials for a device-bound access + refresh token.",
                "description": "The device id is bound into the token, so presenting it from another device is rejected. Send otp as well when the account has two-factor enabled.",
                "operationId": "api.auth.token",
                "tags": [
                    "Auth"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "email": {
                                        "type": "string",
                                        "description": "required \u00b7 account email"
                                    },
                                    "password": {
                                        "type": "string",
                                        "description": "required"
                                    },
                                    "device_id": {
                                        "type": "string",
                                        "description": "required \u00b7 stable id for this install, max 128 chars"
                                    },
                                    "otp": {
                                        "type": "string",
                                        "description": "optional \u00b7 needed when the account has 2FA on"
                                    }
                                },
                                "required": [
                                    "email",
                                    "password",
                                    "device_id"
                                ]
                            },
                            "example": {
                                "email": "ops@carrier.ca",
                                "password": "your-password",
                                "device_id": "ios-9f3c1b7e"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "example": {
                                    "token_type": "Bearer",
                                    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
                                    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
                                    "expires_in": 900,
                                    "device_id": "ios-9f3c1b7e"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing, invalid or revoked credentials"
                    },
                    "422": {
                        "description": "Validation failed"
                    },
                    "429": {
                        "description": "Rate limit exceeded"
                    }
                }
            }
        },
        "/api/auth/token/refresh": {
            "post": {
                "summary": "Trade a refresh token for a fresh access token.",
                "description": "Refresh tokens are bound to the same device as the token they replace.",
                "operationId": "api.auth.refresh",
                "tags": [
                    "Auth"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "refresh_token": {
                                        "type": "string",
                                        "description": "required \u00b7 the refresh token from /api/auth/token"
                                    },
                                    "device_id": {
                                        "type": "string",
                                        "description": "required \u00b7 must match the device the pair was issued to, max 128 chars"
                                    }
                                },
                                "required": [
                                    "refresh_token",
                                    "device_id"
                                ]
                            },
                            "example": {
                                "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
                                "device_id": "ios-9f3c1b7e"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "example": {
                                    "token_type": "Bearer",
                                    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
                                    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
                                    "expires_in": 900,
                                    "device_id": "ios-9f3c1b7e"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing, invalid or revoked credentials"
                    },
                    "422": {
                        "description": "Validation failed"
                    },
                    "429": {
                        "description": "Rate limit exceeded"
                    }
                }
            }
        },
        "/api/v1/bookings": {
            "get": {
                "summary": "Bookings sold against this carrier.",
                "description": "Newest first. Only bookings on your own routes are visible \u2014 the scope comes from the key, not from a filter you pass.",
                "operationId": "api.v1.bookings",
                "tags": [
                    "V1"
                ],
                "security": [
                    {
                        "ApiKeyAuth": []
                    }
                ],
                "parameters": [
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer"
                        },
                        "description": "optional \u00b7 1-based page number, 50 rows per page"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "reference": "TRX-58001",
                                            "status": "confirmed",
                                            "passengers": 2,
                                            "total": 128,
                                            "currency": "CAD",
                                            "created_at": "2026-01-01T09:14:02+00:00"
                                        }
                                    ],
                                    "meta": {
                                        "page": 1,
                                        "per_page": 50,
                                        "total": 63,
                                        "last_page": 2
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing, invalid or revoked credentials"
                    },
                    "422": {
                        "description": "Validation failed"
                    },
                    "429": {
                        "description": "Rate limit exceeded"
                    }
                }
            }
        },
        "/api/v1/freight-orders": {
            "get": {
                "summary": "Freight orders assigned to this carrier.",
                "operationId": "api.v1.freight-orders",
                "tags": [
                    "V1"
                ],
                "security": [
                    {
                        "ApiKeyAuth": []
                    }
                ],
                "parameters": [
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer"
                        },
                        "description": "optional \u00b7 1-based page number, 50 rows per page"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "tracking_number": "TRF-90210",
                                            "origin": "Halifax Port",
                                            "destination": "Montreal Yard",
                                            "plan": "Rail 1 leg \u00b7 1,247 km",
                                            "status": "in_transit",
                                            "total_cost": 4820,
                                            "eta": "2026-01-04T06:00:00+00:00"
                                        }
                                    ],
                                    "meta": {
                                        "page": 1,
                                        "per_page": 50,
                                        "total": 4,
                                        "last_page": 1
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing, invalid or revoked credentials"
                    },
                    "422": {
                        "description": "Validation failed"
                    },
                    "429": {
                        "description": "Rate limit exceeded"
                    }
                }
            }
        },
        "/api/v1/parcels": {
            "get": {
                "summary": "Parcels this carrier is carrying.",
                "operationId": "api.v1.parcels",
                "tags": [
                    "V1"
                ],
                "security": [
                    {
                        "ApiKeyAuth": []
                    }
                ],
                "parameters": [
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer"
                        },
                        "description": "optional \u00b7 1-based page number, 50 rows per page"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "tracking_number": "TRP-4471903",
                                            "status": "in_transit",
                                            "recipient": "A. Tremblay",
                                            "origin": "Toronto Hub",
                                            "destination": "Ottawa Hub",
                                            "price": 24.6,
                                            "eta": "2026-01-02T17:00:00+00:00"
                                        }
                                    ],
                                    "meta": {
                                        "page": 1,
                                        "per_page": 50,
                                        "total": 12,
                                        "last_page": 1
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing, invalid or revoked credentials"
                    },
                    "422": {
                        "description": "Validation failed"
                    },
                    "429": {
                        "description": "Rate limit exceeded"
                    }
                }
            }
        },
        "/api/v1/parcels/{parcel}/status": {
            "patch": {
                "summary": "Move a parcel to its next state.",
                "description": "Runs the parcel state machine, so an illegal transition is refused with 422 rather than silently written. The legal moves are the ones drawn in the lifecycle below.",
                "operationId": "api.v1.parcels.status",
                "tags": [
                    "V1"
                ],
                "security": [
                    {
                        "ApiKeyAuth": []
                    }
                ],
                "parameters": [
                    {
                        "name": "parcel",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Identifier of the parcel. Records belonging to another carrier answer 404."
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "status": {
                                        "type": "string",
                                        "description": "required \u00b7 one of the parcel statuses"
                                    },
                                    "location": {
                                        "type": "string",
                                        "description": "optional \u00b7 free text, max 160 chars"
                                    },
                                    "note": {
                                        "type": "string",
                                        "description": "optional \u00b7 free text, max 240 chars"
                                    }
                                },
                                "required": [
                                    "status"
                                ]
                            },
                            "example": {
                                "status": "out_for_delivery",
                                "location": "Ottawa Hub",
                                "note": "On van 12"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": {
                                        "tracking_number": "TRP-4471903",
                                        "status": "out_for_delivery",
                                        "recipient": "A. Tremblay",
                                        "origin": "Toronto Hub",
                                        "destination": "Ottawa Hub",
                                        "price": 24.6,
                                        "eta": "2026-01-02T17:00:00+00:00"
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing, invalid or revoked credentials"
                    },
                    "422": {
                        "description": "Validation failed"
                    },
                    "429": {
                        "description": "Rate limit exceeded"
                    }
                }
            }
        },
        "/api/v1/ping": {
            "get": {
                "summary": "Confirm a key works and see which carrier it is scoped to.",
                "description": "The cheapest way to verify a newly issued or rotated key before you deploy it.",
                "operationId": "api.v1.ping",
                "tags": [
                    "V1"
                ],
                "security": [
                    {
                        "ApiKeyAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "example": {
                                    "ok": true,
                                    "operator": {
                                        "id": 3,
                                        "name": "Maple Transit"
                                    },
                                    "time": "2026-01-01T09:00:00+00:00"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing, invalid or revoked credentials"
                    },
                    "422": {
                        "description": "Validation failed"
                    },
                    "429": {
                        "description": "Rate limit exceeded"
                    }
                }
            }
        },
        "/api/v1/routes": {
            "get": {
                "summary": "Every route this carrier operates.",
                "operationId": "api.v1.routes",
                "tags": [
                    "V1"
                ],
                "security": [
                    {
                        "ApiKeyAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": 17,
                                            "code": "B502",
                                            "origin": "Toronto Union",
                                            "destination": "Ottawa Station",
                                            "mode": "bus",
                                            "base_fare": "64.00",
                                            "distance_km": "450.00",
                                            "status": "active"
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing, invalid or revoked credentials"
                    },
                    "422": {
                        "description": "Validation failed"
                    },
                    "429": {
                        "description": "Rate limit exceeded"
                    }
                }
            }
        },
        "/api/v1/schedules": {
            "get": {
                "summary": "Upcoming departures, soonest first. Paginated, 50 per page.",
                "description": "Only departures still in the future come back, so polling this endpoint never walks backwards through history.",
                "operationId": "api.v1.schedules",
                "tags": [
                    "V1"
                ],
                "security": [
                    {
                        "ApiKeyAuth": []
                    }
                ],
                "parameters": [
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer"
                        },
                        "description": "optional \u00b7 1-based page number, 50 rows per page"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": 8821,
                                            "route": "B502",
                                            "origin": "Toronto Union",
                                            "destination": "Ottawa Station",
                                            "departs_at": "2026-01-01T14:30:00+00:00",
                                            "available_seats": 18,
                                            "price": 64,
                                            "status": "scheduled",
                                            "delay_minutes": 0
                                        }
                                    ],
                                    "meta": {
                                        "page": 1,
                                        "per_page": 50,
                                        "total": 214,
                                        "last_page": 5
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing, invalid or revoked credentials"
                    },
                    "422": {
                        "description": "Validation failed"
                    },
                    "429": {
                        "description": "Rate limit exceeded"
                    }
                }
            }
        },
        "/api/v1/telemetry/positions": {
            "post": {
                "summary": "Push real GPS/AVL positions. Batched, up to 1000 per call.",
                "description": "Rows are validated individually: a bad row comes back reported by index and the rest of the batch still lands, so one malformed vehicle never costs you the whole push.",
                "operationId": "api.v1.telemetry.positions",
                "tags": [
                    "V1"
                ],
                "security": [
                    {
                        "ApiKeyAuth": []
                    }
                ],
                "requestBody": {
                    "required": false,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "positions": {
                                        "type": "array",
                                        "items": {
                                            "type": "object"
                                        }
                                    }
                                }
                            },
                            "example": {
                                "positions": [
                                    {
                                        "vehicle_ref": "BUS-114",
                                        "route_code": "B502",
                                        "lat": 43.6452,
                                        "lng": -79.3806,
                                        "heading": 84,
                                        "speed_kmh": 47,
                                        "occupancy_pct": 62,
                                        "status": "in_transit_to",
                                        "reported_at": "2026-01-01T09:00:00Z"
                                    }
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "example": {
                                    "accepted": 998,
                                    "rejected": 2,
                                    "errors": [
                                        {
                                            "index": 41,
                                            "errors": [
                                                "reported_at is in the future \u2014 check the sender clock."
                                            ]
                                        },
                                        {
                                            "index": 77,
                                            "errors": [
                                                "The lat field must be between -90 and 90."
                                            ]
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing, invalid or revoked credentials"
                    },
                    "422": {
                        "description": "Validation failed"
                    },
                    "429": {
                        "description": "Rate limit exceeded"
                    }
                }
            }
        }
    }
}