{
    "openapi": "3.0.3",
    "info": {
        "title": "Leavora API",
        "version": "2.0.0",
        "description": "Leave requests, approvals, balances and the team calendar. Leave length is integer CENTIDAYS (hundredths of a day) \u2014 half-days are why a float would be wrong."
    },
    "servers": [
        {
            "url": "http://leavora.ownware.io"
        }
    ],
    "components": {
        "securitySchemes": {
            "bearer": {
                "type": "http",
                "scheme": "bearer"
            }
        }
    },
    "security": [
        {
            "bearer": []
        }
    ],
    "paths": {
        "/api/ping": {
            "get": {
                "summary": "Verify a key",
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/api/requests": {
            "get": {
                "summary": "List leave requests",
                "parameters": [
                    {
                        "name": "employee_id",
                        "in": "query",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "leave_type_id",
                        "in": "query",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "status",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "pending",
                                "approved",
                                "rejected"
                            ]
                        }
                    },
                    {
                        "name": "from",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "format": "date"
                        }
                    },
                    {
                        "name": "to",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "format": "date"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            },
            "post": {
                "summary": "Book leave",
                "responses": {
                    "201": {
                        "description": "Created"
                    },
                    "422": {
                        "description": "Refused by the same rules the form applies"
                    }
                }
            }
        },
        "/api/requests/{id}": {
            "get": {
                "summary": "One request",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "404": {
                        "description": "Not found"
                    }
                }
            }
        },
        "/api/requests/{id}/approve": {
            "post": {
                "summary": "Approve a pending request",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "409": {
                        "description": "Already decided, or overlaps approved leave"
                    }
                }
            }
        },
        "/api/requests/{id}/reject": {
            "post": {
                "summary": "Reject a pending request",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "409": {
                        "description": "Already decided"
                    }
                }
            }
        },
        "/api/employees": {
            "get": {
                "summary": "Roster; ?balances=1 adds balances",
                "parameters": [
                    {
                        "name": "balances",
                        "in": "query",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "as_of",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "format": "date"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/api/employees/{id}": {
            "get": {
                "summary": "One employee with balances",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "404": {
                        "description": "Not found"
                    }
                }
            }
        },
        "/api/leave-types": {
            "get": {
                "summary": "Leave types and their accrual",
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/api/calendar": {
            "get": {
                "summary": "Who is off this month, plus weekends and holidays",
                "parameters": [
                    {
                        "name": "month",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "example": "2026-03"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    }
}
