{
  "openapi": "3.1.0",
  "info": {
    "title": "ShineByGrace API",
    "version": "1.0.0",
    "description": "Cleaning agent management system API for ShineByGrace. Supports bearer token authentication for AI agent integrations.",
    "contact": {
      "email": "shinebygrace@protonmail.com"
    }
  },
  "servers": [
    {
      "url": "https://shinebygrace.co.uk",
      "description": "Production"
    }
  ],
  "components": {
    "securitySchemes": {
      "StaffBearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "Staff API token (Admin only). Prefix: sbg_st_"
      },
      "CustomerBearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "Customer-scoped token. Prefix: sbg_cu_"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable error message"
          }
        },
        "required": [
          "error"
        ]
      },
      "Job": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "customerId": {
            "type": "string",
            "format": "uuid"
          },
          "agentId": {
            "type": "string",
            "format": "uuid"
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "YYYY-MM-DD"
          },
          "startTime": {
            "type": "string",
            "description": "HH:MM"
          },
          "endTime": {
            "type": "string",
            "description": "HH:MM"
          },
          "status": {
            "type": "string",
            "enum": [
              "scheduled",
              "in_progress",
              "completed",
              "cancelled"
            ]
          },
          "hoursWorked": {
            "type": "number"
          },
          "notes": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "customerId",
          "agentId",
          "date",
          "startTime",
          "endTime",
          "status"
        ]
      },
      "Customer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "address": {
            "type": "string"
          },
          "town": {
            "type": "string"
          },
          "postcode": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "isActive": {
            "type": "boolean"
          },
          "hasAccount": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "address",
          "town",
          "postcode",
          "isActive"
        ]
      },
      "Booking": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "customerId": {
            "type": "string",
            "format": "uuid"
          },
          "slotId": {
            "type": "string",
            "format": "uuid"
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "startTime": {
            "type": "string"
          },
          "endTime": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "confirmed",
              "converted",
              "cancelled"
            ]
          },
          "notes": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "customerId",
          "slotId",
          "date",
          "status"
        ]
      },
      "TokenMeta": {
        "type": "object",
        "properties": {
          "tokenId": {
            "type": "string",
            "format": "uuid"
          },
          "label": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "tokenId",
          "label",
          "createdAt",
          "expiresAt"
        ]
      },
      "Conversation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "enum": [
              "public",
              "customer"
            ]
          },
          "customerId": {
            "type": "string",
            "format": "uuid"
          },
          "customerName": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "resolved",
              "escalated"
            ]
          },
          "lastMessage": {
            "type": "string",
            "description": "First 100 characters of the most recent message"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "type",
          "status",
          "createdAt",
          "updatedAt"
        ]
      },
      "ChatMessage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "conversationId": {
            "type": "string",
            "format": "uuid"
          },
          "role": {
            "type": "string",
            "enum": [
              "user",
              "assistant",
              "staff"
            ]
          },
          "content": {
            "type": "string"
          },
          "staffName": {
            "type": "string"
          },
          "staffRole": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "conversationId",
          "role",
          "content",
          "createdAt"
        ]
      }
    }
  },
  "paths": {
    "/api/jobs": {
      "get": {
        "operationId": "listJobs",
        "summary": "List jobs",
        "description": "Returns jobs visible to the authenticated staff user. Admins and Directors see all jobs; Managers see their team's jobs; Agents see only their own. Optionally filter by date.",
        "security": [
          {
            "StaffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "date",
            "in": "query",
            "required": false,
            "description": "Filter jobs by date (YYYY-MM-DD)",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of jobs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobs": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Job"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createJob",
        "summary": "Create a job",
        "description": "Creates a new job assignment. Requires Manager role or above.",
        "security": [
          {
            "StaffBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "customerId",
                  "agentId",
                  "date",
                  "startTime",
                  "endTime"
                ],
                "properties": {
                  "customerId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "agentId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "date": {
                    "type": "string",
                    "format": "date"
                  },
                  "startTime": {
                    "type": "string",
                    "description": "HH:MM"
                  },
                  "endTime": {
                    "type": "string",
                    "description": "HH:MM"
                  },
                  "notes": {
                    "type": "string"
                  },
                  "allowPast": {
                    "type": "boolean",
                    "description": "Set true to deliberately schedule a start time that is already in the past (backfilling). Otherwise a past startTime is rejected with 400 and code PAST_START."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Job created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "job": {
                      "$ref": "#/components/schemas/Job"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — insufficient role",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/jobs/{jobId}": {
      "get": {
        "operationId": "getJob",
        "summary": "Get a job",
        "description": "Returns details of a specific job by ID.",
        "security": [
          {
            "StaffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "description": "Job UUID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Job details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Job not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "updateJob",
        "summary": "Update a job",
        "description": "Updates a job. Managers can update jobs for their team; Admins/Directors can update any job.",
        "security": [
          {
            "StaffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "description": "Job UUID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "date": {
                    "type": "string",
                    "format": "date"
                  },
                  "startTime": {
                    "type": "string"
                  },
                  "endTime": {
                    "type": "string"
                  },
                  "notes": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "scheduled",
                      "in_progress",
                      "completed",
                      "cancelled"
                    ]
                  },
                  "allowPast": {
                    "type": "boolean",
                    "description": "Set true to deliberately reschedule a job into the past (backfilling). Otherwise a past effective start is rejected with 400 and code PAST_START."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Job updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Job not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteJob",
        "summary": "Delete a job",
        "description": "Deletes a job. Requires Manager role or above.",
        "security": [
          {
            "StaffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "description": "Job UUID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Job deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Job not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/jobs/{jobId}/complete": {
      "post": {
        "operationId": "completeJob",
        "summary": "Mark job as complete",
        "description": "Marks a job as completed. Agents can complete their own jobs; Managers/Admins can complete any job.",
        "security": [
          {
            "StaffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "description": "Job UUID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "hoursWorked": {
                    "type": "number"
                  },
                  "notes": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Job marked complete",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Job not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/customers": {
      "get": {
        "operationId": "listCustomers",
        "summary": "List customers",
        "description": "Returns all active customers visible to the authenticated staff user.",
        "security": [
          {
            "StaffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "List of customers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "customers": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Customer"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createCustomer",
        "summary": "Create a customer",
        "description": "Creates a new customer record. Requires Agent role or above.",
        "security": [
          {
            "StaffBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "address",
                  "town",
                  "postcode"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "address": {
                    "type": "string"
                  },
                  "town": {
                    "type": "string"
                  },
                  "postcode": {
                    "type": "string"
                  },
                  "phone": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "accessInfo": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Customer created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "customer": {
                      "$ref": "#/components/schemas/Customer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/customers/{customerId}": {
      "get": {
        "operationId": "getCustomer",
        "summary": "Get a customer",
        "description": "Returns a customer record. Agents can only access customers they have been granted access to.",
        "security": [
          {
            "StaffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "customerId",
            "in": "path",
            "required": true,
            "description": "Customer UUID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Customer details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — no access to this customer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Customer not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "updateCustomer",
        "summary": "Update a customer",
        "description": "Updates a customer record. Requires Agent role or above.",
        "security": [
          {
            "StaffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "customerId",
            "in": "path",
            "required": true,
            "description": "Customer UUID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "address": {
                    "type": "string"
                  },
                  "town": {
                    "type": "string"
                  },
                  "postcode": {
                    "type": "string"
                  },
                  "phone": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "accessInfo": {
                    "type": "string"
                  },
                  "isActive": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Customer updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Customer not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/customer/jobs": {
      "get": {
        "operationId": "listCustomerJobs",
        "summary": "List customer's jobs",
        "description": "Returns upcoming and past jobs for the authenticated customer.",
        "security": [
          {
            "CustomerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "List of the customer's jobs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobs": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "date": {
                            "type": "string",
                            "format": "date"
                          },
                          "startTime": {
                            "type": "string"
                          },
                          "endTime": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "notes": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/customer/profile": {
      "get": {
        "operationId": "getCustomerProfile",
        "summary": "Get customer profile",
        "description": "Returns the profile of the authenticated customer.",
        "security": [
          {
            "CustomerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Customer profile",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string"
                    },
                    "town": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/customer/bookings": {
      "get": {
        "operationId": "listCustomerBookings",
        "summary": "List customer bookings",
        "description": "Returns all bookings (confirmed, cancelled, converted) for the authenticated customer.",
        "security": [
          {
            "CustomerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "List of bookings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "bookings": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Booking"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createCustomerBooking",
        "summary": "Create a booking",
        "description": "Books an available time slot for the authenticated customer.",
        "security": [
          {
            "CustomerBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "slotId"
                ],
                "properties": {
                  "slotId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "ID of the available time slot to book"
                  },
                  "notes": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Booking confirmed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "booking": {
                      "$ref": "#/components/schemas/Booking"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Slot not available or validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/customer/bookings/{bookingId}": {
      "delete": {
        "operationId": "cancelCustomerBooking",
        "summary": "Cancel a booking",
        "description": "Cancels an existing booking and re-opens the time slot. Only the customer who made the booking can cancel it.",
        "security": [
          {
            "CustomerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "bookingId",
            "in": "path",
            "required": true,
            "description": "Booking UUID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Booking cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — not your booking",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Booking not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/auth/token": {
      "get": {
        "operationId": "listStaffTokens",
        "summary": "List staff API tokens",
        "description": "Lists all staff API tokens for the authenticated Admin. Raw token values are never returned.",
        "security": [
          {
            "StaffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "List of token metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tokens": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TokenMeta"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — Admin only",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createStaffToken",
        "summary": "Generate a staff API token",
        "description": "Generates a new staff bearer token. The token value is returned only once — store it securely. Requires Admin role.",
        "security": [
          {
            "StaffBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "label"
                ],
                "properties": {
                  "label": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 50,
                    "description": "Human-readable label for this token"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token generated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "token": {
                      "type": "string",
                      "description": "The raw token value. Store securely — shown only once."
                    },
                    "tokenId": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "expiresAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — Admin only",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "revokeStaffToken",
        "summary": "Revoke a staff API token",
        "description": "Revokes a staff API token by tokenId. Requires Admin role.",
        "security": [
          {
            "StaffBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "tokenId"
                ],
                "properties": {
                  "tokenId": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token revoked",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — Admin only",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Token not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/customer/auth/tokens": {
      "get": {
        "operationId": "listCustomerTokens",
        "summary": "List customer API tokens",
        "description": "Lists all API tokens for the authenticated customer. Raw token values are never returned.",
        "security": [
          {
            "CustomerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "List of token metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tokens": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TokenMeta"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createCustomerToken",
        "summary": "Generate a customer API token",
        "description": "Generates a new customer bearer token (max 5 per customer). The raw token is returned only once.",
        "security": [
          {
            "CustomerBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "label"
                ],
                "properties": {
                  "label": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 50,
                    "description": "Human-readable label for this token"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token generated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "token": {
                      "type": "string",
                      "description": "The raw token value. Store securely — shown only once."
                    },
                    "tokenId": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "expiresAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error or token limit reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/customer/auth/tokens/{tokenId}": {
      "delete": {
        "operationId": "revokeCustomerToken",
        "summary": "Revoke a customer API token",
        "description": "Revokes one of the authenticated customer's API tokens.",
        "security": [
          {
            "CustomerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "tokenId",
            "in": "path",
            "required": true,
            "description": "Token UUID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Token revoked",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Token not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/chat/message": {
      "post": {
        "operationId": "sendChatMessage",
        "summary": "Send a chat message",
        "description": "Send a message to the AI chat assistant. Public (anonymous) users get FAQ-only responses. Authenticated customers get full tool use (bookings, cancellations, messaging). Public sessions are limited to 10 messages; customers to 20 messages per conversation.",
        "tags": [
          "Chat"
        ],
        "security": [
          {
            "CustomerBearer": []
          },
          {}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "message": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "The user's message text"
                  }
                },
                "required": [
                  "message"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "AI assistant reply",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reply": {
                      "type": "string"
                    },
                    "conversationId": {
                      "type": "string",
                      "format": "uuid"
                    }
                  },
                  "required": [
                    "reply"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Message limit reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/chat/conversations": {
      "get": {
        "operationId": "listConversations",
        "summary": "List all conversations",
        "description": "Returns all chat conversations with customer names. Requires Manager role or above.",
        "tags": [
          "Chat"
        ],
        "security": [
          {
            "StaffBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Conversation list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Conversation"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — requires Manager+ role",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/chat/history": {
      "get": {
        "operationId": "getConversationHistory",
        "summary": "Get conversation message history",
        "description": "Returns the full message thread for a conversation. Requires Manager role or above.",
        "tags": [
          "Chat"
        ],
        "security": [
          {
            "StaffBearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "description": "Conversation UUID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Message thread",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "messages": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ChatMessage"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing id parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Conversation not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/chat/status": {
      "post": {
        "operationId": "updateConversationStatus",
        "summary": "Update conversation status",
        "description": "Change a conversation's status to open, resolved, or escalated. When resolved, the customer is notified via push and email. Requires Manager role or above.",
        "tags": [
          "Chat"
        ],
        "security": [
          {
            "StaffBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "conversationId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "open",
                      "resolved",
                      "escalated"
                    ]
                  }
                },
                "required": [
                  "conversationId",
                  "status"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Status updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/chat/reply": {
      "post": {
        "operationId": "sendStaffReply",
        "summary": "Send a staff reply to a conversation",
        "description": "Post a reply from a staff member into a customer conversation. Managers can reply to any conversation; agents can reply only to conversations belonging to their assigned customers. The customer is notified via push and email.",
        "tags": [
          "Chat"
        ],
        "security": [
          {
            "StaffBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "conversationId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "message": {
                    "type": "string",
                    "maxLength": 1000,
                    "description": "Staff reply text"
                  }
                },
                "required": [
                  "conversationId",
                  "message"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reply sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — agent not assigned to this customer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Conversation not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/chat/unread": {
      "get": {
        "operationId": "getUnreadCount",
        "summary": "Get count of open conversations",
        "description": "Returns the number of open (unresolved) conversations. Returns 0 if the caller is not authenticated. Used by the dashboard sidebar badge.",
        "tags": [
          "Chat"
        ],
        "security": [
          {
            "StaffBearer": []
          },
          {}
        ],
        "responses": {
          "200": {
            "description": "Unread count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "count"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/chat/delete": {
      "post": {
        "operationId": "deleteConversations",
        "summary": "Delete conversations",
        "description": "Permanently delete one or more conversations and all their messages. Uses POST (not DELETE) because it accepts a JSON body with multiple IDs. Requires Manager role or above.",
        "tags": [
          "Chat"
        ],
        "security": [
          {
            "StaffBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "conversationIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "minItems": 1,
                    "description": "UUIDs of conversations to delete"
                  }
                },
                "required": [
                  "conversationIds"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Conversations deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "deleted": {
                      "type": "integer",
                      "description": "Number of conversations actually deleted"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  }
}