openapi: 3.1.0
info:
  title: Emplorea HR API
  version: 1.3.0
  description: >-
    Emplorea is an HR-as-a-Service platform for businesses of every size in
    Australia, New Zealand and the United Kingdom. This API gives AI agents and
    integrations programmatic access to the live platform: company details;
    employee management (update, terminate and invite - creating an employee is
    web-only); leave management (request, approve, decline, cancel, balances,
    leave types and public holidays; sensitive family-violence leave is never
    returned); documents with in-house e-sign; the compliance engine (items,
    results, policies, training and incidents, including WHS); managed payroll
    (reads plus set the pay schedule); the performance area (probation reviews,
    performance reviews, goals and 1:1s); recruitment (pipeline, roles,
    application stages and offers - candidate personal details are never
    returned); offboarding (plans and tasks); permissions (presets and
    overrides, read-only); advisor-conversation metadata (message content is
    never returned); company branding; engagement (surveys and recognition, with
    aggregate, k-anonymous results only); wellbeing (aggregate team and company
    scores and the EAP directory - individual data is web-only); the Learn
    training library (courses, assignments, completions and verifiable
    certificates; quiz answer keys are never returned); and webhooks. Some
    operations are intentionally web-only for safety (creating employees,
    managed payroll processing, compliance award and rule ingestion,
    career-affecting performance writes, terminations, and anonymity- or
    individual-sensitive engagement and wellbeing writes) and are not on this
    surface by construction.


    FOR AI AGENTS: authenticate every request with an API key as a Bearer token
    (Authorization: Bearer emp_live_...). Keys are created by a company admin in
    Settings -> Integrations and are scoped (read / write / admin / webhooks).
    Start with GET /company to understand the company context, then GET
    /employees and GET /leave-types before creating resources. Money is always
    integer cents. Dates are ISO YYYY-MM-DD. List endpoints paginate with ?page
    and ?limit and return a meta object. Errors always have the shape {error:
    {code, message, field?}}. Rate limit: 100 requests/minute per key, reported
    on every response in X-RateLimit-* headers.


    More: https://app.emplorea.com/docs and /llms.txt. MCP server:
    https://app.emplorea.com/mcp (manifest at /.well-known/mcp.json).
  contact:
    name: Emplorea support
    email: hr@emplorea.com
servers:
  - url: https://app.emplorea.com/api/v1
security:
  - BearerAuth: []
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: API key from Settings -> Integrations, e.g. emp_live_...
  schemas:
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              enum:
                - UNAUTHORISED
                - FORBIDDEN
                - NOT_FOUND
                - VALIDATION_ERROR
                - CONFLICT
                - RATE_LIMITED
                - NOT_IMPLEMENTED
                - INTERNAL_ERROR
            message:
              type: string
            field:
              type: string
          required:
            - code
            - message
      required:
        - error
    ListMeta:
      type: object
      properties:
        total:
          type: integer
        page:
          type: integer
        limit:
          type: integer
        has_more:
          type: boolean
      required:
        - total
        - page
        - limit
        - has_more
    Employee:
      type: object
      properties:
        id:
          type: string
          format: uuid
        first_name:
          type: string
        last_name:
          type: string
        email:
          type: string
          format: email
        job_title:
          type:
            - string
            - "null"
        department:
          type:
            - string
            - "null"
        employment_type:
          type: string
          enum:
            - full_time
            - part_time
            - casual
            - contractor
        start_date:
          type: string
          format: date
        base_salary:
          type:
            - integer
            - "null"
          description: Integer cents - 9200000 = $92,000.
        status:
          type: string
          enum:
            - active
            - on_leave
            - probation
            - terminated
        probation_end_date:
          type:
            - string
            - "null"
          format: date
        manager_id:
          type:
            - string
            - "null"
          format: uuid
        jurisdiction:
          type: string
          enum:
            - AU
            - NZ
            - UK
        created_at:
          type: string
          format: date-time
    EmployeeCreate:
      type: object
      required:
        - first_name
        - last_name
        - email
        - employment_type
        - start_date
        - jurisdiction
        - role
      properties:
        first_name:
          type: string
        last_name:
          type: string
        email:
          type: string
          format: email
        job_title:
          type: string
        department:
          type: string
        employment_type:
          type: string
          enum:
            - full_time
            - part_time
            - casual
            - contractor
        start_date:
          type: string
          format: date
        jurisdiction:
          type: string
          enum:
            - AU
            - NZ
            - UK
        base_salary:
          type: integer
          description: Integer cents.
        manager_id:
          type: string
          format: uuid
        role:
          type: string
          enum:
            - employee
            - manager
            - company_admin
          description: Portal role. Takes effect when the person is invited via POST
            /employees/{id}/invite.
    LeaveRequest:
      type: object
      properties:
        id:
          type: string
          format: uuid
        employeeId:
          type: string
          format: uuid
        employeeName:
          type: string
        typeId:
          type: string
          format: uuid
        typeName:
          type: string
        startDate:
          type: string
          format: date
        endDate:
          type: string
          format: date
        days:
          type: number
          description: Working days - weekends and public holidays excluded; half days are
            0.5.
        halfDay:
          type: boolean
        halfDayPeriod:
          type:
            - string
            - "null"
        notes:
          type:
            - string
            - "null"
        status:
          type: string
          enum:
            - pending
            - approved
            - declined
            - cancelled
            - auto_approved
        declineReason:
          type:
            - string
            - "null"
        createdAt:
          type: string
          format: date-time
    LeaveCreate:
      type: object
      required:
        - employee_id
        - leave_type_id
        - start_date
        - end_date
      properties:
        employee_id:
          type: string
          format: uuid
        leave_type_id:
          type: string
          format: uuid
          description: From GET /leave-types.
        start_date:
          type: string
          format: date
        end_date:
          type: string
          format: date
        half_day:
          type: boolean
          default: false
        half_day_period:
          type: string
          enum:
            - morning
            - afternoon
        notes:
          type: string
          maxLength: 500
    LeaveBalance:
      type: object
      properties:
        employee_id:
          type: string
          format: uuid
        name:
          type: string
        annual_available:
          type: number
        sick_available:
          type: number
        pending_days:
          type: number
        taken_days:
          type: number
    LeaveType:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        is_paid:
          type: boolean
        requires_approval:
          type: boolean
          description: When false, requests of this type are approved automatically on
            submission.
        allow_half_days:
          type: boolean
        min_notice_days:
          type: integer
        max_days_per_year:
          type:
            - integer
            - "null"
        is_default:
          type: boolean
    PublicHoliday:
      type: object
      properties:
        date:
          type: string
          format: date
        name:
          type: string
    Webhook:
      type: object
      properties:
        id:
          type: string
          format: uuid
        url:
          type: string
          format: uri
        events:
          type: array
          items:
            type: string
        is_active:
          type: boolean
        created_at:
          type: string
          format: date-time
        secret:
          type: string
          description: Returned ONCE on creation - used to verify the X-Emplorea-Signature
            header (HMAC-SHA256, format sha256=<hex>).
    Document:
      type: object
      properties:
        id:
          type: string
          format: uuid
        employee_id:
          type:
            - string
            - "null"
          format: uuid
        type:
          type: string
          enum:
            - contract
            - payslip
            - policy
            - certificate
            - other
        title:
          type: string
        file_size:
          type: integer
          description: Bytes.
        mime_type:
          type: string
        signed:
          type: boolean
        signed_at:
          type:
            - string
            - "null"
          format: date-time
        awaiting_signature:
          type: boolean
          description: True while an unexpired one-time signing link is out.
        version:
          type: integer
        created_at:
          type: string
          format: date-time
tags:
  - name: Employees
    description: Team member records, profiles and invitations.
  - name: Leave
    description: Leave requests, balances, leave types and public holidays.
  - name: Documents
    description: Document storage, downloads and built-in e-signature.
  - name: Payroll
    description: Pay schedule, pay runs and payslips (read access; managed figures
      are entered by Emplorea).
  - name: Performance
    description: Probation, reviews, goals and one-on-ones.
  - name: Recruitment
    description: Roles, candidates, pipeline stages, offers and public job listings.
  - name: Onboarding
    description: New-hire onboarding plans and their tasks.
  - name: Offboarding
    description: Offboarding plans and their tasks.
  - name: Compliance
    description: Compliance score, results, policies, training and incidents.
  - name: Engage
    description: Surveys, survey programmes, eNPS and recognition.
  - name: Wellbeing
    description: Team wellbeing scores, absence patterns, WHS incidents and the EAP
      directory.
  - name: Learn
    description: Courses, lessons, assignments, completions, certificates and
      learning paths.
  - name: Ask HR
    description: Ask Vera an HR question and read past answers.
  - name: Advisor messaging
    description: Conversation with the company named HR advisor.
  - name: Company & settings
    description: Company profile, branding and setup completeness.
  - name: Permissions
    description: Permission presets and per-user overrides.
  - name: Billing
    description: Plan, add-ons and subscription status (read only).
  - name: Analytics
    description: People-analytics summary and exports.
  - name: Authentication
    description: Company SSO and MFA policy (read only).
  - name: Webhooks
    description: Manage webhook endpoints and the events they receive.
paths:
  /billing:
    get:
      tags:
        - Billing
      summary: Get billing status
      operationId: getBillingStatus
      x-agent-hints: "Read-only billing status: plan, subscription status, billing
        interval, trial info, seat counts (active/billed/minimum) and active
        add-ons. Plan changes are web-only - there is no checkout/portal or
        write endpoint here. Read scope."
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
  /onboarding:
    get:
      tags:
        - Company & settings
      summary: Get setup completeness
      operationId: getSetupStatus
      x-agent-hints: "Read-only company setup completeness: onboarding_complete,
        employee_count, and whether the team is invited, leave types configured,
        a document uploaded and (where the plan includes payroll) a pay schedule
        set, plus setup_complete for the core tasks. Any plan. Per-user
        walkthrough state is not exposed (a key carries no user). Read scope."
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
  /analytics:
    get:
      tags:
        - Analytics
      summary: Get people-analytics summary
      operationId: getAnalyticsSummary
      x-agent-hints: "Read-only aggregate people analytics (Growth+): current
        headcount + the active/on_leave/probation split, the 12-month headcount
        trend, average tenure, and per-jurisdiction turnover (rate, leavers,
        voluntary/involuntary mix) + department headcount, plus
        mandatory-training completion with a per-department breakdown under
        `training.byDepartment`. AGGREGATES ONLY - department cells below the
        anonymity floor (5 people) are suppressed (with a complementary cell so
        a hidden one cannot be back-solved), and turnover is never blended
        across jurisdictions. The same suppressed figures the dashboard and
        export show. Read scope."
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
        "403":
          description: People analytics is available on the Growth plan and above
  /analytics/export:
    get:
      tags:
        - Analytics
      summary: Export people-analytics summary as CSV
      operationId: exportAnalyticsCsv
      x-agent-hints: "Read-only CSV download of the same people-analytics summary
        (Growth+). The cohort floor carries through identically: a suppressed
        department cell exports as the literal '<5 - hidden', never blank and
        never the number. Returns text/csv. Read scope."
      responses:
        "200":
          description: CSV download
          content:
            text/csv:
              schema:
                type: string
        "401":
          description: Missing or invalid API key
        "403":
          description: People analytics is available on the Growth plan and above
  /learning-paths:
    get:
      tags:
        - Learn
      summary: List published learning paths
      operationId: listLearningPaths
      x-agent-hints: "Read-only: the company's PUBLISHED Vera Plus learning paths
        (ordered course sets). Building and publishing are web-only - a
        company-scoped key cannot generate or publish. Read scope."
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
        "401":
          description: Missing or invalid API key
  /survey-programmes:
    get:
      tags:
        - Engage
      summary: List published survey programmes
      operationId: listSurveyProgrammes
      x-agent-hints: "Read-only: the company's PUBLISHED Vera Plus survey programmes
        (ordered survey sets plus a cadence). Building and publishing are
        web-only. Read scope."
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
        "401":
          description: Missing or invalid API key
  /recruitment/roles:
    get:
      tags:
        - Recruitment
      summary: List job roles
      operationId: listJobRoles
      x-agent-hints: Lists open roles with status and active candidate counts. No
        candidate personal details. Read scope.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
    post:
      tags:
        - Recruitment
      summary: Create a job role
      operationId: createJobRole
      x-agent-hints: "Creates an open role. Required: title, employment_type,
        jurisdiction. Write scope."
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
        "401":
          description: Missing or invalid API key
  /recruitment/roles/{id}:
    get:
      tags:
        - Recruitment
      summary: Get a job role
      operationId: getJobRole
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      x-agent-hints: One role + per-stage candidate counts. No candidate personal
        details. Read scope.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
    patch:
      tags:
        - Recruitment
      summary: Update a job role status
      operationId: updateJobRole
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      x-agent-hints: Updates the role status (draft/open/on_hold/closed/filled). Write scope.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
  /recruitment/roles/{id}/pipeline:
    get:
      tags:
        - Recruitment
      summary: Get a role pipeline
      operationId: getRolePipeline
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      x-agent-hints: Stages + each application as an opaque candidate_ref with stage,
        status and days in stage. Candidate names, emails and CVs are NEVER
        returned. Read scope.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
  /recruitment/stages:
    get:
      tags:
        - Recruitment
      summary: List pipeline stages
      operationId: listPipelineStages
      x-agent-hints: The company's configurable pipeline stages. Read scope.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
  /recruitment/candidates:
    post:
      tags:
        - Recruitment
      summary: Create a candidate
      operationId: createCandidate
      x-agent-hints: Creates a candidate + application. PII is input only; the
        response returns just { id } and never echoes it. No GET, no CV endpoint
        (web-only). Write scope.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
        "401":
          description: Missing or invalid API key
  /recruitment/applications/{id}/stage:
    patch:
      tags:
        - Recruitment
      summary: Move an application stage
      operationId: moveApplicationStage
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      x-agent-hints: Moves an application to another active or rejected stage. Cannot
        move to Hired (hiring is web-only). Write scope.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
  /recruitment/offers:
    post:
      tags:
        - Recruitment
      summary: Create an offer
      operationId: createOffer
      x-agent-hints: Creates a structured offer (salary in integer cents). Sending and
        accepting are web-only. Write scope.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
        "401":
          description: Missing or invalid API key
  /onboarding/plans:
    get:
      tags:
        - Onboarding
      summary: List onboarding plans
      operationId: listOnboardingPlans
      x-agent-hints: Lists new-hire onboarding plans with the starter's name, start
        date, status and task progress. Plans are created automatically when an
        employee is added (web-only). Growth plan and above. Read scope.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      plans:
                        type: array
                        items:
                          type: object
        "401":
          description: Missing or invalid API key
  /onboarding/plans/{id}:
    get:
      tags:
        - Onboarding
      summary: Get an onboarding plan
      operationId: getOnboardingPlan
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      x-agent-hints: "One plan: its tasks (title, category, whether required or
        Emplorea-managed, completed flag, due date). Read scope."
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
  /onboarding/plans/{id}/tasks/{taskId}:
    patch:
      tags:
        - Onboarding
      summary: Complete an onboarding task
      operationId: completeOnboardingTask
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: taskId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      x-agent-hints: "Completes or reopens an ordinary task (body: { completed:
        boolean }). Emplorea-managed tasks are refused; plan creation is
        automatic at hire and completing the onboarding itself is web-only.
        Write scope."
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
        "401":
          description: Missing or invalid API key
  /offboarding/plans:
    get:
      tags:
        - Offboarding
      summary: List offboarding plans
      operationId: listOffboardingPlans
      x-agent-hints: Lists offboarding plans with the leaver's name, last working day,
        status and task progress. Exit-interview responses are never returned.
        Initiating a plan is web-only. Read scope.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      plans:
                        type: array
                        items:
                          type: object
        "401":
          description: Missing or invalid API key
  /offboarding/plans/{id}:
    get:
      tags:
        - Offboarding
      summary: Get an offboarding plan
      operationId: getOffboardingPlan
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      x-agent-hints: "One plan: its tasks and the exit-interview STATUS only.
        Exit-interview responses are private and never returned. Read scope."
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
  /offboarding/plans/{id}/tasks/{taskId}:
    patch:
      tags:
        - Offboarding
      summary: Complete an offboarding task
      operationId: completeOffboardingTask
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: taskId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      x-agent-hints: "Completes or reopens an ordinary task (body: { completed:
        boolean }). Emplorea-managed tasks are refused; completing the
        offboarding itself (which terminates the employee) is web-only. Write
        scope."
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
        "401":
          description: Missing or invalid API key
  /surveys:
    post:
      tags:
        - Engage
      summary: Create a survey
      operationId: createSurvey
      x-agent-hints: "Creates a DRAFT engagement survey (eNPS, pulse or check-in).
        Required: title, type, anonymity. Publishing it to the audience is a
        separate web step. Growth plan and above. Write scope."
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
        "401":
          description: Missing or invalid API key
  /surveys/{id}/results:
    get:
      tags:
        - Engage
      summary: Get survey results
      operationId: getSurveyResults
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      x-agent-hints: "AGGREGATES ONLY: response rate, eNPS and per-question summaries.
        Never an individual response or a respondent id; below the k-anonymity
        floor no breakdown is returned at all. Read scope."
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
  /surveys/{id}/respond:
    post:
      tags:
        - Engage
      summary: Submit a survey response
      operationId: submitSurveyResponse
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      x-agent-hints: Submits a response to an open survey. ANONYMOUS surveys are
        refused over the API - they can only be answered in the app (a
        company-scoped key must never fabricate anonymous responses at scale).
        An attributed survey requires employee_id in the body. Write scope.
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
        "401":
          description: Missing or invalid API key
  /recognition:
    get:
      tags:
        - Engage
      summary: List recognition
      operationId: listRecognition
      x-agent-hints: The recognition feed - PUBLIC posts only for a roleless API key
        (private posts are never returned). Each carries giver, recipient,
        message, value tag, kind and reaction count. Read scope.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
        "401":
          description: Missing or invalid API key
    post:
      tags:
        - Engage
      summary: Post recognition
      operationId: postRecognition
      x-agent-hints: "Posts recognition from one team member to a colleague. Required:
        from_employee_id, to_employee_id, message. Manager-kind requires the
        author to be the recipient's manager. You cannot recognise yourself.
        Write scope."
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
        "401":
          description: Missing or invalid API key
  /messaging/conversation:
    get:
      tags:
        - Advisor messaging
      summary: Get advisor conversation metadata
      operationId: getAdvisorConversation
      x-agent-hints: "Returns metadata about the company's HR advisor conversation:
        status, whether an advisor is assigned, advisor name/status, unread
        count, last message time. Message content is human-to-human and NEVER
        exposed - no content, no message list, no send. Read scope."
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      status:
                        type: string
                      advisor_assigned:
                        type: boolean
                      advisor_name:
                        type: string
                        nullable: true
                      advisor_status:
                        type: string
                        nullable: true
                      unread_count:
                        type: integer
                      last_message_at:
                        type: string
                        nullable: true
                      last_message_sender:
                        type: string
                        nullable: true
        "401":
          description: Missing or invalid API key
  /permissions/presets:
    get:
      tags:
        - Permissions
      summary: List permission presets
      operationId: listPermissionPresets
      x-agent-hints: Lists the named manager presets (team_lead, hr_manager) and the
        capability keys each grants. No personal data. Read scope.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      presets:
                        type: array
                        items:
                          type: object
                          properties:
                            preset:
                              type: string
                            capabilities:
                              type: array
                              items:
                                type: string
        "401":
          description: Missing or invalid API key
  /permissions/company:
    get:
      tags:
        - Permissions
      summary: Get company manager permission overrides
      operationId: getCompanyPermissions
      x-agent-hints: Returns the per-company capability overrides applied to managers
        (capability key to granted boolean). No personal data. Read scope.
        Changing permissions is web-only.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      manager:
                        type: object
                        additionalProperties:
                          type: boolean
        "401":
          description: Missing or invalid API key
  /permissions/users/{id}:
    get:
      tags:
        - Permissions
      summary: Get a user's resolved permissions
      operationId: getUserPermissions
      x-agent-hints: Returns one user's role, preset and resolved capability keys plus
        team scope. Capability keys only, never personal data. Read scope.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      user_id:
                        type: string
                      role:
                        type: string
                      preset:
                        type: string
                        nullable: true
                      capabilities:
                        type: array
                        items:
                          type: string
                      scope:
                        type: string
        "401":
          description: Missing or invalid API key
        "404":
          description: User not found in this company
  /company/branding:
    get:
      tags:
        - Company & settings
      summary: Get company branding
      operationId: getCompanyBranding
      x-agent-hints: "Returns the company's branding: primary_colour, accent_colour,
        brand_name, logo_url, email_branding and published_at. Growth plan and
        above (FORBIDDEN below). Company-scoped - a key only reads its own
        company. Read scope."
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "403":
          description: Branding is a Growth-plan feature
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
    put:
      tags:
        - Company & settings
      summary: Update company branding
      operationId: updateCompanyBranding
      x-agent-hints: "Sets primary_colour / accent_colour (6-digit hex like #1B6CA8),
        brand_name (null = use the company name) and email_branding, then
        publishes immediately. The logo is uploaded at /company/branding/logo.
        Growth plan and above. Write scope."
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                primary_colour:
                  type:
                    - string
                    - "null"
                accent_colour:
                  type:
                    - string
                    - "null"
                brand_name:
                  type:
                    - string
                    - "null"
                email_branding:
                  type: boolean
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "403":
          description: Branding is a Growth-plan feature
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
  /company/branding/logo:
    post:
      tags:
        - Company & settings
      summary: Upload a branding logo
      operationId: uploadCompanyBrandingLogo
      x-agent-hints: multipart/form-data with a 'file' field (PNG, JPEG, WEBP or SVG;
        2MB max; SVG is sanitised server-side). Publishes immediately. Growth
        plan and above. Write scope.
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
              required:
                - file
      responses:
        "201":
          description: Logo stored
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
    delete:
      tags:
        - Company & settings
      summary: Remove the branding logo
      operationId: removeCompanyBrandingLogo
      x-agent-hints: Removes the logo and reverts to the Emplorea mark. Colours are
        kept. Growth plan and above. Write scope.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
  /company/branding/reset:
    post:
      tags:
        - Company & settings
      summary: Reset company branding
      operationId: resetCompanyBranding
      x-agent-hints: Clears all branding (colours, brand name and logo) back to
        Emplorea defaults. Growth plan and above. Write scope.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "403":
          description: Branding is a Growth-plan feature
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
  /company:
    get:
      tags:
        - Company & settings
      summary: Get the company
      operationId: getCompany
      x-agent-hints: Call this first. It returns the company name, plan, jurisdiction,
        currency, timezone, financial year start and the read-only Vera company
        context - the context every other call depends on.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      name:
                        type: string
                      jurisdiction:
                        type: array
                        items:
                          type: string
                      plan:
                        type: string
                      company_size:
                        type: string
                        nullable: true
                      industry:
                        type: string
                        nullable: true
                      vera_context:
                        type: string
                        nullable: true
                        description: Admin-authored "About your business" context that steers Vera Plus
                          generation. Read-only here; edited in company settings
                          (Vera Plus, company_admin). Write is web-only.
                      website:
                        type: string
                        nullable: true
                      currency:
                        type: string
                      timezone:
                        type: string
                      financial_year_start:
                        type: string
                      primary_domain:
                        type: string
                        nullable: true
                        description: The company email domain detected at sign-up. Read-only (never
                          writable here).
                      allowed_domains:
                        type: array
                        items:
                          type: string
                        description: The invite domain allowlist. Editable via PATCH; the primary domain
                          is always kept and a blocked personal domain is
                          rejected.
                      domain_enforcement_enabled:
                        type: boolean
                        description: When true, a new team member can only be invited on an allowed
                          domain.
                      created_at:
                        type: string
                        format: date-time
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "429":
          description: Rate limited (100 requests/minute per key)
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
    patch:
      tags:
        - Company & settings
      summary: Update company details
      operationId: updateCompany
      x-agent-hints: Updates name, website, industry, phone, and the domain controls
        (allowed_domains, domain_enforcement_enabled). primary_domain is
        detected at sign-up and is not writable. A blocked personal domain in
        allowed_domains returns VALIDATION_ERROR. Requires the admin scope.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                website:
                  type:
                    - string
                    - "null"
                industry:
                  type:
                    - string
                    - "null"
                phone:
                  type:
                    - string
                    - "null"
                allowed_domains:
                  type: array
                  items:
                    type: string
                  description: The invite domain allowlist. A blocked personal domain returns
                    VALIDATION_ERROR (field allowed_domains); the primary domain
                    is always kept.
                domain_enforcement_enabled:
                  type: boolean
                  description: Turn invite domain enforcement on or off.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "429":
          description: Rate limited (100 requests/minute per key)
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
  /employees:
    get:
      tags:
        - Employees
      summary: List employees
      operationId: listEmployees
      x-agent-hints: Lists active employees by default. Use ?status=terminated for
        former employees or ?status=all for everyone. Filter by ?department.
        Paginated.
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
            maximum: 100
        - name: status
          in: query
          schema:
            type: string
            enum:
              - active
              - terminated
              - all
        - name: department
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Employee"
                  meta:
                    type: object
                    properties:
                      total:
                        type: integer
                      page:
                        type: integer
                      limit:
                        type: integer
                      has_more:
                        type: boolean
                    required:
                      - total
                      - page
                      - limit
                      - has_more
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "429":
          description: Rate limited
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
  /employees/{id}:
    get:
      tags:
        - Employees
      summary: Get an employee
      operationId: getEmployee
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      x-agent-hints: Full employee record including salary (integer cents) and status.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Employee"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "404":
          description: Not found
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "429":
          description: Rate limited (100 requests/minute per key)
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
    patch:
      tags:
        - Employees
      summary: Update an employee
      operationId: updateEmployee
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      x-agent-hints: Patch any editable field (job_title, department, employment_type,
        base_salary in cents, manager_id...). Terminated employees cannot be
        edited.
      requestBody:
        content:
          application/json:
            schema:
              type: object
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "429":
          description: Rate limited (100 requests/minute per key)
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
    delete:
      tags:
        - Employees
      summary: Terminate an employee
      operationId: terminateEmployee
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      x-agent-hints: "Terminates the employment - Emplorea never hard-deletes people
        records (they are kept for compliance). Optional JSON body:
        termination_date (defaults today) and termination_reason (resignation,
        redundancy, performance, end_of_contract or other - defaults other)."
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                termination_date:
                  type: string
                  format: date
                termination_reason:
                  type: string
                  enum:
                    - resignation
                    - redundancy
                    - performance
                    - end_of_contract
                    - other
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "429":
          description: Rate limited (100 requests/minute per key)
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
  /employees/{id}/invite:
    post:
      tags:
        - Employees
      operationId: inviteEmployee
      summary: Send an invite email to an employee
      description: Generates a Supabase invite link and sends the Emplorea welcome
        email so the employee can set a password and access the portal. Requires
        write scope.
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Invite sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      employeeId:
                        type: string
                        format: uuid
                      invited:
                        type: boolean
  /leave:
    get:
      tags:
        - Leave
      summary: List leave requests
      operationId: listLeave
      x-agent-hints: Pending requests sort first. Filter with ?employee_id, ?status
        (pending, approved, declined or all) and a ?from / ?to date range.
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
            maximum: 100
        - name: employee_id
          in: query
          schema:
            type: string
            format: uuid
        - name: status
          in: query
          schema:
            type: string
            enum:
              - pending
              - approved
              - declined
              - all
        - name: from
          in: query
          schema:
            type: string
            format: date
        - name: to
          in: query
          schema:
            type: string
            format: date
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/LeaveRequest"
                  meta:
                    type: object
                    properties:
                      total:
                        type: integer
                      page:
                        type: integer
                      limit:
                        type: integer
                      has_more:
                        type: boolean
                    required:
                      - total
                      - page
                      - limit
                      - has_more
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "429":
          description: Rate limited
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
    post:
      tags:
        - Leave
      summary: Request leave
      operationId: requestLeave
      x-agent-hints: Use this to request leave on behalf of an employee. Check GET
        /leave/balances first to confirm the employee has sufficient
        entitlement. Day counts exclude weekends and public holidays
        automatically. Types with requires_approval=false are approved
        instantly. The response includes non-blocking warnings (insufficient
        balance, team overlaps) - the request is saved regardless and the
        manager is notified.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LeaveCreate"
            example:
              employee_id: uuid-here
              leave_type_id: uuid-here
              start_date: 2026-07-14
              end_date: 2026-07-18
              notes: Family holiday
      responses:
        "201":
          description: Created (warnings are non-blocking)
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      days:
                        type: number
                      status:
                        type: string
                  warnings:
                    type: array
                    items:
                      type: string
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
  /leave/{id}:
    get:
      tags:
        - Leave
      summary: Get a leave request
      operationId: getLeave
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/LeaveRequest"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "404":
          description: Not found
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "429":
          description: Rate limited (100 requests/minute per key)
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
    delete:
      tags:
        - Leave
      summary: Cancel a leave request
      operationId: cancelLeave
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      x-agent-hints: "Cancels (soft-deletes) the request. Optional body: reason."
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "429":
          description: Rate limited (100 requests/minute per key)
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
  /leave/{id}/approve:
    patch:
      tags:
        - Leave
      summary: Approve a pending leave request
      operationId: approveLeave
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      x-agent-hints: Only pending requests can be approved. The employee is notified
        and the days deduct from their balance.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "409":
          description: Not pending
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "429":
          description: Rate limited (100 requests/minute per key)
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
  /leave/{id}/decline:
    patch:
      tags:
        - Leave
      summary: Decline a pending leave request
      operationId: declineLeave
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      x-agent-hints: A reason is required and is shown to the employee.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - reason
              properties:
                reason:
                  type: string
                  maxLength: 500
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "429":
          description: Rate limited (100 requests/minute per key)
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
  /leave/balances:
    get:
      tags:
        - Leave
      summary: Leave balances
      operationId: getLeaveBalances
      x-agent-hints: Annual and sick balances for every active employee (or one with
        ?employee_id). Annual accrues 20 days/year from the start date; sick
        accrues 10 days/year and resets at the company financial year start.
        Check this before requesting leave.
      parameters:
        - name: employee_id
          in: query
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/LeaveBalance"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "429":
          description: Rate limited (100 requests/minute per key)
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
  /leave-types:
    get:
      tags:
        - Leave
      summary: List leave types
      operationId: listLeaveTypes
      x-agent-hints: Get leave_type_id values for POST /leave. requires_approval=false
        types auto-approve.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/LeaveType"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "429":
          description: Rate limited (100 requests/minute per key)
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
  /public-holidays:
    get:
      tags:
        - Leave
      summary: List public holidays
      operationId: listPublicHolidays
      x-agent-hints: 2026 seed data for AU (national plus state days with ?region=NSW
        etc), NZ and UK (England/Wales). Leave day counts already exclude these.
      parameters:
        - name: jurisdiction
          in: query
          schema:
            type: string
            enum:
              - AU
              - NZ
              - UK
            default: AU
        - name: region
          in: query
          schema:
            type: string
        - name: year
          in: query
          schema:
            type: integer
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/PublicHoliday"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "429":
          description: Rate limited (100 requests/minute per key)
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
  /webhooks:
    get:
      tags:
        - Webhooks
      summary: List webhook endpoints
      operationId: listWebhooks
      x-agent-hints: Requires the webhooks scope.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Webhook"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "429":
          description: Rate limited (100 requests/minute per key)
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
    post:
      tags:
        - Webhooks
      summary: Create a webhook endpoint
      operationId: createWebhook
      x-agent-hints: "Subscribe an HTTPS URL to events. Topics: employee, leave,
        document, hr_question, advisor, compliance, payroll, payslip,
        performance, permissions, messaging, recruitment, offboarding, branding,
        engage, wellbeing, learn, vera, email, auth, billing. See the events
        enum for the full list of subscribable event names. The signing secret
        is returned ONCE - verify every delivery's X-Emplorea-Signature header
        (HMAC-SHA256 of the raw body, format sha256=<hex>). Deliveries retry 3
        times with exponential backoff."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - url
                - events
              properties:
                url:
                  type: string
                  format: uri
                  description: Must be HTTPS.
                events:
                  type: array
                  items:
                    type: string
                    enum:
                      - employee.created
                      - employee.updated
                      - employee.terminated
                      - leave.requested
                      - leave.approved
                      - leave.declined
                      - leave.cancelled
                      - document.uploaded
                      - document.sent_for_signature
                      - document.signed
                      - hr_question.asked
                      - hr_question.answered
                      - employee.invited
                      - advisor.assigned
                      - compliance.item_created
                      - compliance.item_resolved
                      - compliance.incident_logged
                      - compliance.incident_resolved
                      - compliance.policy_acknowledged
                      - compliance.score_threshold_crossed
                      - payroll.run_ready
                      - payroll.run_paid
                      - payroll.pay_date_approaching
                      - payslip.available
                      - performance.probation_completed
                      - performance.review_shared
                      - performance.review_acknowledged
                      - performance.goal_created
                      - performance.goal_status_changed
                      - performance.one_on_one_logged
                      - performance.goal_overdue
                      - performance.one_on_one_overdue
                      - permissions.preset_changed
                      - permissions.feature_toggled
                      - messaging.message.sent
                      - messaging.message.read
                      - recruitment.role_created
                      - recruitment.role_closed
                      - recruitment.candidate_applied
                      - recruitment.candidate_stage_changed
                      - recruitment.offer_sent
                      - recruitment.offer_accepted
                      - recruitment.offer_declined
                      - recruitment.employee_created
                      - offboarding.plan_created
                      - offboarding.task_completed
                      - offboarding.plan_completed
                      - offboarding.exit_interview_completed
                      - onboarding.created
                      - onboarding.task_completed
                      - branding.published
                      - branding.reset
                      - engage.survey.sent
                      - engage.survey.completed
                      - engage.survey.closed
                      - engage.recognition.posted
                      - wellbeing.score.updated
                      - wellbeing.incident.action_completed
                      - learn.course.assigned
                      - learn.lesson.completed
                      - learn.course.completed
                      - learn.certificate.issued
                      - learn.assignment.overdue
                      - vera.learning_path.published
                      - vera.survey_programme.published
                      - email.bounced
                      - email.complained
                      - auth.sso.enabled
                      - auth.mfa.enrolled
                      - auth.session.revoked
                      - billing.plan.upgraded
                      - billing.plan.downgraded
                      - billing.trial.started
                      - billing.trial.ending_soon
                      - billing.trial.expired
                      - billing.payment.failed
                      - billing.payment.recovered
                      - billing.subscription.cancelled
            example:
              url: https://example.com/hooks/emplorea
              events:
                - employee.created
                - leave.approved
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Webhook"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "429":
          description: Rate limited (100 requests/minute per key)
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
  /webhooks/{id}:
    delete:
      tags:
        - Webhooks
      summary: Delete a webhook endpoint
      operationId: deleteWebhook
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
        "429":
          description: Rate limited (100 requests/minute per key)
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
  /documents:
    get:
      tags:
        - Documents
      summary: List documents
      operationId: listDocuments
      x-agent-hints: Contracts, payslips, policies and certificates. Filter with
        employee_id, type or signed=true|false. awaiting_signature means a live
        sign link is out.
      parameters:
        - name: employee_id
          in: query
          schema:
            type: string
            format: uuid
        - name: type
          in: query
          schema:
            type: string
            enum:
              - contract
              - payslip
              - policy
              - certificate
              - other
        - name: signed
          in: query
          schema:
            type: boolean
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
      responses:
        "200":
          description: Documents
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Document"
                  meta:
                    $ref: "#/components/schemas/ListMeta"
        "401":
          description: Unauthorised
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      tags:
        - Documents
      summary: Upload a document
      operationId: uploadDocument
      x-agent-hints: "multipart/form-data: file (PDF for contracts; PDF/PNG/JPEG
        otherwise, 10MB max) + type + title. employee_id links it to a person
        (required for payslips, with pay_period like 'June 2026' - the title is
        composed for you). Requires the write scope."
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - file
                - type
                - title
              properties:
                file:
                  type: string
                  format: binary
                type:
                  type: string
                  enum:
                    - contract
                    - payslip
                    - policy
                    - certificate
                    - other
                title:
                  type: string
                employee_id:
                  type: string
                  format: uuid
                pay_period:
                  type: string
                  example: June 2026
      responses:
        "201":
          description: Uploaded
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Document"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Missing write scope
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /auth/policy:
    get:
      tags:
        - Authentication
      summary: Get the company SSO/MFA policy
      operationId: getAuthPolicy
      description: "The company's authentication policy: whether single sign-on is
        required, the allowed SSO email domain, and whether two-factor is
        required for all team members. Read scope. Personal security actions
        (MFA enrolment, SSO linking, session revocation) are deliberately not on
        the API - a company key carries no user identity."
      x-agent-hints: A read-only company-level fact. There are no auth writes on the
        API or MCP by design; SSO/MFA setup and session management are web-only,
        session-authenticated actions.
      responses:
        "200":
          description: The company's SSO and MFA policy
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      sso_required:
                        type: boolean
                      sso_domain:
                        type: string
                        nullable: true
                      mfa_required:
                        type: boolean
  /payroll:
    get:
      tags:
        - Payroll
      summary: Get payroll state
      operationId: getPayroll
      description: "The company's managed payroll state: the pay schedule, the current
        run, upcoming pay dates and recent paid runs. The client sets their own
        cycle (POST /payroll/schedule); processing runs - figures, status,
        payslips - is the Emplorea team's job and stays web-only. Available on
        the Essentials plan and above. Read scope."
      x-agent-hints: "Read the company's payroll at a glance: schedule, current run,
        the next pay dates and recent paid runs. For the full run list use GET
        /payroll/runs; for one run's per-employee payslip rows use GET
        /payroll/runs/{id}."
      responses:
        "200":
          description: Payroll schedule, current run, upcoming pay dates and recent paid
            runs
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      schedule:
                        type: object
                        nullable: true
                        properties:
                          frequency:
                            type: string
                            enum:
                              - weekly
                              - fortnightly
                              - monthly
                          first_pay_date:
                            type: string
                            format: date
                      next_pay_date:
                        type: string
                        format: date
                        nullable: true
                      current_run:
                        type: object
                        nullable: true
                        properties:
                          status:
                            type: string
                            enum:
                              - upcoming
                              - in_progress
                              - ready
                              - paid
                              - cancelled
                          pay_date:
                            type: string
                            format: date
                          gross_cents:
                            type: integer
                          net_cents:
                            type: integer
                          employee_count:
                            type: integer
                      upcoming:
                        type: array
                        items:
                          type: object
                          properties:
                            pay_date:
                              type: string
                              format: date
                            period_start:
                              type: string
                              format: date
                            period_end:
                              type: string
                              format: date
                            status:
                              type: string
                      recent_paid:
                        type: array
                        items:
                          type: object
                          properties:
                            pay_date:
                              type: string
                              format: date
                            gross_cents:
                              type: integer
                            net_cents:
                              type: integer
  /payroll/schedule:
    get:
      tags:
        - Payroll
      summary: Get the pay schedule
      operationId: getPaySchedule
      description: "The company's pay cycle: frequency, first pay date, pay-day rule
        and timezone. Null when the client hasn't set their cycle yet. Available
        on the Essentials plan and above. Read scope."
      x-agent-hints: The client's own pay-cycle setting. Use POST /payroll/schedule to
        set or reset it; processing runs (figures, status, payslips) is the
        Emplorea team's job and is web-only.
      responses:
        "200":
          description: The active pay schedule, or null
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      schedule:
                        type: object
                        nullable: true
                        properties:
                          frequency:
                            type: string
                            enum:
                              - weekly
                              - fortnightly
                              - monthly
                          first_pay_date:
                            type: string
                            format: date
                          pay_day_rule:
                            type: string
                          timezone:
                            type: string
    post:
      tags:
        - Payroll
      summary: Set the pay schedule
      operationId: setPaySchedule
      description: Sets (or resets) the company's pay cycle and generates the upcoming
        pay calendar. The one client-writable payroll op - the schedule is the
        client's own setting. Available on the Essentials plan and above. Write
        scope.
      x-agent-hints: The only payroll write on the API. Provide frequency and
        first_pay_date (YYYY-MM-DD); optional pay_day_rule. Resetting rebuilds
        the upcoming calendar. Entering figures, advancing a run and uploading
        payslips are the Emplorea team's job and stay web-only.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - frequency
                - first_pay_date
              properties:
                frequency:
                  type: string
                  enum:
                    - weekly
                    - fortnightly
                    - monthly
                first_pay_date:
                  type: string
                  format: date
                pay_day_rule:
                  type: string
                  enum:
                    - on_period_end
                    - fixed_weekday
                    - last_business_day
      responses:
        "201":
          description: Schedule created; the calendar is generated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      schedule_id:
                        type: string
                      periods:
                        type: integer
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Payroll requires the Essentials plan or above
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /payroll/runs:
    get:
      tags:
        - Payroll
      summary: List pay runs
      operationId: listPayRuns
      description: Every pay run in the calendar (earliest first) with status and
        figures in integer cents. Read only - the Emplorea team processes runs
        in the web app. Available on the Essentials plan and above. Read scope.
      x-agent-hints: The full calendar of runs, not the capped slices on GET /payroll.
        Each run carries gross/tax/super/deductions/net in integer cents plus
        status and employee_count.
      responses:
        "200":
          description: All pay runs with figures
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      runs:
                        type: array
                        items:
                          type: object
                          properties:
                            run_id:
                              type: string
                            pay_date:
                              type: string
                              format: date
                            period_start:
                              type: string
                              format: date
                            period_end:
                              type: string
                              format: date
                            status:
                              type: string
                              enum:
                                - upcoming
                                - in_progress
                                - ready
                                - paid
                                - cancelled
                            gross_cents:
                              type: integer
                            tax_cents:
                              type: integer
                            super_cents:
                              type: integer
                            deductions_cents:
                              type: integer
                            net_cents:
                              type: integer
                            employee_count:
                              type: integer
                            prepared_at:
                              type: string
                              format: date-time
                              nullable: true
                            paid_at:
                              type: string
                              format: date-time
                              nullable: true
  /payroll/runs/{id}:
    get:
      tags:
        - Payroll
      summary: Get a pay run
      operationId: getPayRun
      description: One pay run with its per-employee payslip rows (name, status, net
        in cents). Payslip files are never returned here - the employee
        downloads them from their portal. Read only. Available on the Essentials
        plan and above. Read scope.
      x-agent-hints: Per-employee payslip status and net pay for one run. To get the
        file, the employee uses their portal - the API never returns payslip
        bytes.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: The run and its payslip rows
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      run:
                        type: object
                        properties:
                          run_id:
                            type: string
                          pay_date:
                            type: string
                            format: date
                          period_start:
                            type: string
                            format: date
                          period_end:
                            type: string
                            format: date
                          status:
                            type: string
                            enum:
                              - upcoming
                              - in_progress
                              - ready
                              - paid
                              - cancelled
                          gross_cents:
                            type: integer
                          tax_cents:
                            type: integer
                          super_cents:
                            type: integer
                          deductions_cents:
                            type: integer
                          net_cents:
                            type: integer
                          employee_count:
                            type: integer
                          prepared_at:
                            type: string
                            format: date-time
                            nullable: true
                          paid_at:
                            type: string
                            format: date-time
                            nullable: true
                      payslips:
                        type: array
                        items:
                          type: object
                          properties:
                            employee_id:
                              type: string
                            employee_name:
                              type: string
                            status:
                              type: string
                              enum:
                                - pending
                                - issued
                                - none
                            net_cents:
                              type: integer
        "404":
          description: Pay run not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /performance/probation:
    get:
      tags:
        - Performance
      summary: List probation reviews
      operationId: listProbationReviews
      description: "The company's probation picture: open_cases (employees currently
        on probation, with probation_end_date and whether a review has been
        recorded) and reviews (recorded decisions
        confirmed/extended/not_confirmed). READ ONLY: completing a probation
        review changes employment status and resolves a compliance item, so it
        is web-only. Review notes are never returned. Growth plan and above.
        Read scope."
      responses:
        "200":
          description: Open probation cases and recorded decisions
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      open_cases:
                        type: array
                        items:
                          type: object
                          properties:
                            employee_id:
                              type: string
                              format: uuid
                            employee_name:
                              type: string
                              nullable: true
                            probation_end_date:
                              type: string
                              format: date
                              nullable: true
                            reviewed:
                              type: boolean
                      reviews:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                              format: uuid
                            employee_id:
                              type: string
                              format: uuid
                            employee_name:
                              type: string
                              nullable: true
                            status:
                              type: string
                              enum:
                                - pending
                                - completed
                            outcome:
                              type: string
                              enum:
                                - confirmed
                                - extended
                                - not_confirmed
                              nullable: true
                            extended_to_date:
                              type: string
                              format: date
                              nullable: true
                            decided_at:
                              type: string
                              format: date-time
                              nullable: true
                            created_at:
                              type: string
                              format: date-time
  /performance/reviews:
    get:
      tags:
        - Performance
      summary: List performance reviews
      operationId: listPerformanceReviews
      description: "Shared performance reviews (metadata: employee, review_type,
        rating 1-5, status shared/acknowledged, dates), newest first. DRAFT
        reviews are NEVER returned. Creating, sharing and acknowledging are web
        actions. Growth plan and above. Read scope."
      responses:
        "200":
          description: Shared review metadata
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      reviews:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                              format: uuid
                            employee_id:
                              type: string
                              format: uuid
                            employee_name:
                              type: string
                              nullable: true
                            review_type:
                              type: string
                              enum:
                                - probation
                                - quarterly
                                - annual
                                - ad_hoc
                            status:
                              type: string
                              enum:
                                - shared
                                - acknowledged
                            rating:
                              type: integer
                              nullable: true
                            period_label:
                              type: string
                              nullable: true
                            shared_at:
                              type: string
                              format: date-time
                              nullable: true
                            acknowledged_at:
                              type: string
                              format: date-time
                              nullable: true
                            created_at:
                              type: string
                              format: date-time
  /performance/reviews/{id}:
    get:
      tags:
        - Performance
      summary: Get a performance review
      operationId: getPerformanceReview
      description: One shared review with its body (strengths, development, summary).
        Only shared/acknowledged reviews resolve; a draft returns 404 and is
        never exposed. Growth plan and above. Read scope.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: One shared review with its body
        "404":
          description: Not found, or the review is still a draft
  /performance/goals:
    get:
      tags:
        - Performance
      summary: List goals
      operationId: listGoals
      description: The company's goals (employee, title, status, progress 0-100,
        dates), newest first. Growth plan and above. Read scope.
      responses:
        "200":
          description: Goals
    post:
      tags:
        - Performance
      summary: Create a goal
      operationId: createGoal
      description: Creates a goal for an employee. One of the two SAFE, low-stakes
        performance writes exposed to agents (the career-affecting writes stay
        web-only). Growth plan and above. Write scope.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - employee_id
                - title
              properties:
                employee_id:
                  type: string
                  format: uuid
                title:
                  type: string
                description:
                  type: string
                status:
                  type: string
                  enum:
                    - draft
                    - active
                    - achieved
                    - missed
                    - cancelled
                progress:
                  type: integer
                  minimum: 0
                  maximum: 100
                start_date:
                  type: string
                  format: date
                target_date:
                  type: string
                  format: date
      responses:
        "201":
          description: Goal created
        "400":
          description: Validation error
  /performance/goals/{id}:
    get:
      tags:
        - Performance
      summary: Get a goal
      operationId: getGoal
      description: One goal by id. Growth plan and above. Read scope.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: One goal
        "404":
          description: Not found
    patch:
      tags:
        - Performance
      summary: Update goal progress
      operationId: setGoalProgress
      description: Updates a goal's progress (0-100) and/or status. The other SAFE
        agent write. Growth plan and above. Write scope.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                progress:
                  type: integer
                  minimum: 0
                  maximum: 100
                status:
                  type: string
                  enum:
                    - draft
                    - active
                    - achieved
                    - missed
                    - cancelled
      responses:
        "200":
          description: Goal updated
        "404":
          description: Not found
  /performance/one-on-ones:
    get:
      tags:
        - Performance
      summary: List 1:1s
      operationId: listOneOnOnes
      description: The company's 1:1s, SHARED fields only (employee, scheduled date,
        status, shared notes, action items, next date). PRIVATE manager notes
        are NEVER exposed over the API (no employee RLS policy, no API path).
        Logging a 1:1 is a web action. Growth plan and above. Read scope.
      responses:
        "200":
          description: 1:1s, shared fields only
  /compliance:
    get:
      tags:
        - Compliance
      summary: List compliance items
      operationId: listCompliance
      description: The company's open compliance items, the current compliance score,
        and items resolved in the last 30 days. Severity is
        critical/warning/info; the score is 100 minus weighted open items
        (critical -20, warning -5, info -1). Available on the Essentials plan
        and above. Read scope.
      responses:
        "200":
          description: Compliance items and score
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      score:
                        type: object
                        properties:
                          score:
                            type: integer
                          critical:
                            type: integer
                          warning:
                            type: integer
                          info:
                            type: integer
                          total:
                            type: integer
                          categories:
                            type: object
                            description: Per-category breakdown keyed by pay/records/safety/leave; each has
                              score, critical, warning, info, total.
                      items:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                              format: uuid
                            type:
                              type: string
                            severity:
                              type: string
                              enum:
                                - critical
                                - warning
                                - info
                            title:
                              type: string
                            description:
                              type: string
                              nullable: true
                            due_date:
                              type: string
                              format: date
                              nullable: true
                            employee_id:
                              type: string
                              format: uuid
                              nullable: true
                            employee_name:
                              type: string
                              nullable: true
                            auto_generated:
                              type: boolean
                            category:
                              type: string
                              enum:
                                - pay
                                - records
                                - safety
                                - leave
                              nullable: true
                            detail:
                              type: object
                              description: Finding-specific data, e.g. the pay gap (actual/minimum/gap cents)
                                or a cannot-verify reason.
                            created_at:
                              type: string
                              format: date-time
                            resolved_at:
                              type: string
                              format: date-time
                              nullable: true
                      resolved:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                              format: uuid
                            type:
                              type: string
                            severity:
                              type: string
                              enum:
                                - critical
                                - warning
                                - info
                            title:
                              type: string
                            resolved_at:
                              type: string
                              format: date-time
                              nullable: true
  /compliance/{id}/resolve:
    post:
      tags:
        - Compliance
      summary: Resolve a compliance item
      operationId: resolveComplianceItem
      description: Marks one compliance item resolved. Auto-generated items reappear
        on the next check if the underlying condition still holds. Write scope.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Resolved
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      status:
                        type: string
  /compliance/refresh:
    post:
      tags:
        - Compliance
      summary: Re-run compliance checks
      operationId: refreshCompliance
      description: Re-runs the compliance rules (probation reviews, contract expiry,
        missing emergency contacts) for the company and returns the fresh score.
        Idempotent. Write scope.
      responses:
        "200":
          description: Fresh score
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      score:
                        type: object
                        properties:
                          score:
                            type: integer
                          critical:
                            type: integer
                          warning:
                            type: integer
                          info:
                            type: integer
                          total:
                            type: integer
  /compliance/policies:
    get:
      tags:
        - Compliance
      summary: List policies
      operationId: listPolicies
      description: The company's HR policies and acknowledgement coverage. Available
        on the Essentials plan and above. Read scope.
      x-agent-hints:
        - A required policy the business has not activated is flagged by the
          compliance engine.
      responses:
        "200":
          description: Policies and acknowledgement coverage
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      policies:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                              format: uuid
                            policyKey:
                              type: string
                            title:
                              type: string
                            status:
                              type: string
                              enum:
                                - draft
                                - active
                                - archived
                            ackDone:
                              type: integer
                            ackTotal:
                              type: integer
  /compliance/training:
    get:
      tags:
        - Compliance
      summary: List training
      operationId: listTraining
      description: The company's required training and completion coverage, with
        renewals flagged before they lapse. Essentials plan and above. Read
        scope.
      x-agent-hints:
        - Coverage is per in-scope employee; expiries are flagged before they
          lapse.
      responses:
        "200":
          description: Training requirements and coverage
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      training:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                              format: uuid
                            title:
                              type: string
                            completed:
                              type: integer
                            total:
                              type: integer
                            expiryMonths:
                              type: integer
                              nullable: true
  /compliance/incidents:
    get:
      tags:
        - Compliance
      summary: List incidents
      operationId: listIncidents
      description: The company's WHS/HR incident register. Sensitive - a
        company-scoped key reads only its own register. Essentials plan and
        above. Read scope.
      responses:
        "200":
          description: Incident register
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      incidents:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                              format: uuid
                            reference:
                              type: string
                            type:
                              type: string
                            severity:
                              type: string
                              enum:
                                - critical
                                - warning
                                - info
                            status:
                              type: string
                              enum:
                                - open
                                - investigating
                                - resolved
                                - closed
                            title:
                              type: string
    post:
      tags:
        - Compliance
      summary: Log an incident
      operationId: logIncident
      description: Logs a workplace incident and returns its server-generated
        reference. severity critical is treated as notifiable. The one
        compliance write an agent performs - award and rule data is
        Emplorea-managed and never writable here. Write scope.
      x-agent-hints:
        - Provide type, severity and title; the reference is generated
          server-side.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - type
                - severity
                - title
              properties:
                type:
                  type: string
                severity:
                  type: string
                  enum:
                    - critical
                    - warning
                    - info
                title:
                  type: string
                description:
                  type: string
                occurred_on:
                  type: string
                  format: date
                location:
                  type: string
                affected_employee_id:
                  type: string
                  format: uuid
      responses:
        "201":
          description: Logged incident
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      reference:
                        type: string
  /compliance/incidents/{id}:
    post:
      tags:
        - Compliance
      summary: Update an incident
      operationId: updateIncident
      description: Update an incident's status (and resolution). Closing it records
        the resolution. Write scope.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - status
              properties:
                status:
                  type: string
                  enum:
                    - open
                    - investigating
                    - resolved
                    - closed
                resolution:
                  type: string
      responses:
        "200":
          description: Updated incident
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      status:
                        type: string
  /compliance/results:
    get:
      tags:
        - Compliance
      summary: List compliance run snapshots
      operationId: listComplianceResults
      description: Recent compliance run snapshots (the score trend, newest first) so
        a result can be traced over time. Defaults to the blended company-wide
        series (jurisdiction null, one point per run-day); pass
        ?jurisdiction=AU|NZ|UK to read that jurisdiction's attributed series
        instead (only multi-jurisdiction companies have those rows). Essentials
        plan and above. Read scope.
      parameters:
        - name: jurisdiction
          in: query
          required: false
          description: Read a single jurisdiction's attributed trend instead of the
            blended company-wide series. Only multi-jurisdiction companies have
            per-jurisdiction rows.
          schema:
            type: string
            enum:
              - AU
              - NZ
              - UK
      responses:
        "200":
          description: Run snapshots
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      results:
                        type: array
                        items:
                          type: object
                          properties:
                            score:
                              type: integer
                            as_of:
                              type: string
                              format: date
                            created_at:
                              type: string
                              format: date-time
                            jurisdiction:
                              type: string
                              nullable: true
                              enum:
                                - AU
                                - NZ
                                - UK
                                - null
                              description: null for the blended company-wide snapshot; a value for a
                                per-jurisdiction snapshot.
  /ask-hr:
    post:
      tags:
        - Ask HR
      summary: Ask an HR question
      operationId: askHr
      description: Submit a question to Vera. Requires the Essentials plan or above.
        Do not include real names, salaries or tax file numbers - company data
        is tokenised server-side. Write scope.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - question
              properties:
                question:
                  type: string
                  maxLength: 2000
                conversation_id:
                  type: string
                  format: uuid
      responses:
        "200":
          description: Vera's answer
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      answer:
                        type: string
                      conversationId:
                        type: string
                        format: uuid
                      cached:
                        type: boolean
    get:
      tags:
        - Ask HR
      summary: List HR Q&A conversations
      operationId: listHrConversations
      description: Lists the company's Vera conversations, newest first. Read scope.
      responses:
        "200":
          description: Conversations
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                        title:
                          type: string
                          nullable: true
                        message_count:
                          type: integer
                        created_at:
                          type: string
                          format: date-time
  /ask-hr/{id}:
    get:
      tags:
        - Ask HR
      summary: Get an HR Q&A conversation
      operationId: getHrConversation
      description: Returns one Vera conversation with all its messages. Message
        content is tokenised. Read scope.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Conversation with messages
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      conversation:
                        type: object
                        properties:
                          id:
                            type: string
                            format: uuid
                          title:
                            type: string
                            nullable: true
                          created_at:
                            type: string
                            format: date-time
                      messages:
                        type: array
                        items:
                          type: object
                          properties:
                            role:
                              type: string
                              enum:
                                - user
                                - assistant
                            content:
                              type: string
                            created_at:
                              type: string
                              format: date-time
  /jobs:
    get:
      tags:
        - Recruitment
      summary: Public job listings
      operationId: listJobs
      x-status: coming-soon
      x-agent-hints: The job board API arrives in Phase 3.
      responses:
        "501":
          description: Coming soon
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - UNAUTHORISED
                          - FORBIDDEN
                          - NOT_FOUND
                          - VALIDATION_ERROR
                          - CONFLICT
                          - RATE_LIMITED
                          - NOT_IMPLEMENTED
                          - INTERNAL_ERROR
                      message:
                        type: string
                      field:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - error
  /documents/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      tags:
        - Documents
      summary: Get a document
      operationId: getDocument
      x-agent-hints: Metadata only - call /documents/{id}/download for the file itself.
      responses:
        "200":
          description: Document
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Document"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    delete:
      tags:
        - Documents
      summary: Remove a document
      operationId: deleteDocument
      x-agent-hints: Soft delete - the record is kept and the file stays in storage
        for legal retention. Requires the write scope.
      responses:
        "200":
          description: Removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Document"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /documents/{id}/download:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      tags:
        - Documents
      summary: Get a download URL
      operationId: getDocumentDownloadUrl
      x-agent-hints: Returns a signed URL that expires after 15 minutes
        (expires_in_seconds). Fetch it right before downloading - never store
        it.
      responses:
        "200":
          description: Signed URL
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      url:
                        type: string
                      expires_in_seconds:
                        type: integer
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /documents/{id}/send-for-signature:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    post:
      tags:
        - Documents
      summary: Send a document for signature
      operationId: sendDocumentForSignature
      x-agent-hints: Creates a one-time signing link valid for 7 days (in-house e-sign
        - signing captures timestamp, IP and user agent). Returns sign_path -
        prefix with the app origin and share it with the signer. The document
        must be linked to an employee. Requires the write scope.
      responses:
        "200":
          description: Signing link created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      sign_path:
                        type: string
                        example: /sign/abc123
                      expires_at:
                        type: string
                        format: date-time
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Already signed or no employee linked
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /wellbeing/score:
    get:
      tags:
        - Wellbeing
      summary: Get aggregate wellbeing score
      operationId: getTeamWellbeingScore
      parameters:
        - name: team_key
          in: query
          required: false
          schema:
            type: string
          description: A team key (manager id). Omit for the company score.
      x-agent-hints: "AGGREGATES ONLY: a team or company wellbeing score (0-100, band,
        contributor count). NEVER returns an individual - an employee_id query
        param is refused with 403 by construction. A team below the anonymity
        floor returns { insufficient: true }. Growth plan and above. Read
        scope."
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
        "403":
          description: Plan does not include Wellbeing, or an individual score was requested
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /wellbeing/incidents:
    post:
      tags:
        - Wellbeing
      summary: Log a WHS incident
      operationId: logWhsIncident
      x-agent-hints: "Logs a workplace health and safety incident and returns its
        server-generated reference (INC-YYYY-NNNN). Required: type, severity
        (critical/warning/info), title. Optional WHS detail: injury_type,
        body_part, treatment_required, witness_details, notifiable. The
        compliance engine reconciles it on its next run. Growth plan and above.
        Write scope."
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      reference:
                        type: string
        "401":
          description: Missing or invalid API key
  /wellbeing/resources:
    get:
      tags:
        - Wellbeing
      summary: Get the EAP directory
      operationId: getWellbeingResources
      x-agent-hints: "The company's EAP directory - published support providers,
        categories and resources (Emplorea-curated plus the company's own).
        Help, not personal data: no per-employee EAP access is ever recorded or
        returned. Growth plan and above. Read scope."
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
  /wellbeing/absence-patterns:
    get:
      tags:
        - Wellbeing
      summary: Get aggregate absence patterns
      operationId: getAbsencePatterns
      x-agent-hints: "AGGREGATES ONLY: the latest run's TEAM absence-pattern flags.
        NEVER returns an individual - the core reads team-scoped flags
        exclusively. Growth plan and above. Read scope."
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
  /learn/courses:
    get:
      tags:
        - Learn
      summary: List courses
      operationId: listCourses
      x-agent-hints: The training library the company can see - its own courses (any
        status) plus the global Emplorea-published library, each with a lesson
        count, jurisdiction and category. Quiz answer keys are never included.
        Growth plan with the Learn add-on. Read scope.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
    post:
      tags:
        - Learn
      summary: Create a course
      operationId: createCourse
      x-agent-hints: "Creates a DRAFT company course. Required: title. Optional:
        description, summary, category_id, estimated_minutes, jurisdiction,
        pass_score. Adding lessons and publishing are separate steps. Growth
        plan with the Learn add-on. Write scope."
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
        "401":
          description: Missing or invalid API key
  /learn/courses/{id}:
    get:
      tags:
        - Learn
      summary: Get a course
      operationId: getCourse
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      x-agent-hints: "One course with its ordered lessons, returned as a LEARNER:
        every quiz lesson's answer key (correct_index and explanation) is
        stripped server-side, so the API never exposes quiz answers. Growth plan
        with the Learn add-on. Read scope."
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
        "401":
          description: Missing or invalid API key
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /learn/assignments:
    post:
      tags:
        - Learn
      summary: Assign a course
      operationId: assignCourse
      x-agent-hints: "Assigns a course to one or more employees with an optional
        deadline. Idempotent per course + employee. Required: course_id,
        employee_ids[]. Optional: is_mandatory, due_date. Only this company's
        own employees are assigned. Growth plan with the Learn add-on. Write
        scope."
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      assigned:
                        type: integer
                      assignment_ids:
                        type: array
                        items:
                          type: string
        "401":
          description: Missing or invalid API key
  /learn/completions:
    post:
      tags:
        - Learn
      summary: Mark a lesson complete
      operationId: markLessonComplete
      x-agent-hints: "Marks a single lesson complete for an employee's own assignment.
        Required: assignment_id, lesson_id, employee_id. When every lesson of
        the course is then complete the course completes and a certificate is
        issued. Growth plan with the Learn add-on. Write scope."
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      lesson_id:
                        type: string
                      course_completed:
                        type: boolean
                      certificate_id:
                        type: string
                        nullable: true
        "401":
          description: Missing or invalid API key
  /learn/certificates/{id}:
    get:
      tags:
        - Learn
      summary: Get a certificate
      operationId: getCertificate
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      x-agent-hints: One certificate's metadata (holder name, course title, issue
        date, revoked flag) plus its public verify_url
        (https://app.emplorea.com/verify/cert/{token}). Scoped to the caller's
        company. The token in verify_url is the verification capability. Growth
        plan with the Learn add-on. Read scope.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      course_title:
                        type: string
                      holder_name:
                        type: string
                      issued_on:
                        type: string
                      revoked:
                        type: boolean
                      verify_url:
                        type: string
        "401":
          description: Missing or invalid API key
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
