{
    "openapi": "3.0.3",
    "info": {
        "title": "Rostera API",
        "version": "2.0.0",
        "description": "Shift rota, employees and hours. Bearer API keys; HMAC-signed webhooks."
    },
    "servers": [
        {
            "url": "http://rostera.ownware.io"
        }
    ],
    "components": {
        "securitySchemes": {
            "bearerAuth": {
                "type": "http",
                "scheme": "bearer"
            }
        },
        "schemas": {
            "Shift": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "employee_id": {
                        "type": [
                            "integer",
                            "null"
                        ]
                    },
                    "employee_name": {
                        "type": "string"
                    },
                    "open": {
                        "type": "boolean"
                    },
                    "date": {
                        "type": "string"
                    },
                    "start": {
                        "type": "string"
                    },
                    "end": {
                        "type": "string"
                    },
                    "crosses_midnight": {
                        "type": "boolean"
                    },
                    "duration_minutes": {
                        "type": "integer"
                    },
                    "duration": {
                        "type": "string"
                    },
                    "hours": {
                        "type": "string"
                    },
                    "label": {
                        "type": "string"
                    },
                    "status": {
                        "type": "string"
                    }
                }
            }
        }
    },
    "security": [
        {
            "bearerAuth": {}
        }
    ],
    "paths": {
        "/api/ping": {
            "get": {
                "summary": "Verify a key",
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/api/rota": {
            "get": {
                "summary": "One week of the rota",
                "parameters": [
                    {
                        "name": "week",
                        "in": "query",
                        "schema": {
                            "type": "string"
                        },
                        "description": "Any date inside the week (Y-m-d); snapped to the configured week start."
                    },
                    {
                        "name": "status",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "draft",
                                "published"
                            ]
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The week grid"
                    }
                }
            }
        },
        "/api/shifts": {
            "post": {
                "summary": "Schedule a shift",
                "description": "Runs the same validation and double-booking guard as the rota screen. Returns 409 when the shift overlaps one already scheduled for that employee.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "date",
                                    "start",
                                    "end"
                                ],
                                "properties": {
                                    "employee_id": {
                                        "type": "integer",
                                        "description": "Omit or 0 for an OPEN shift."
                                    },
                                    "date": {
                                        "type": "string",
                                        "example": "2026-08-10"
                                    },
                                    "start": {
                                        "type": "string",
                                        "example": "09:00"
                                    },
                                    "end": {
                                        "type": "string",
                                        "example": "17:00"
                                    },
                                    "label": {
                                        "type": "string"
                                    },
                                    "status": {
                                        "type": "string",
                                        "enum": [
                                            "draft",
                                            "published"
                                        ]
                                    },
                                    "note": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Created"
                    },
                    "409": {
                        "description": "Double-booking"
                    },
                    "422": {
                        "description": "Invalid"
                    }
                }
            }
        },
        "/api/employees": {
            "get": {
                "summary": "List employees",
                "parameters": [
                    {
                        "name": "active",
                        "in": "query",
                        "schema": {
                            "type": "string"
                        },
                        "description": "0 to include archived staff."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/api/hours": {
            "get": {
                "summary": "Hours report",
                "parameters": [
                    {
                        "name": "from",
                        "in": "query",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "to",
                        "in": "query",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Per-employee totals"
                    }
                }
            }
        }
    }
}
