openapi: 3.0.3
info:
  title: HireOrSell API
  version: "1.0"
  description: |
    The HireOrSell API provides programmatic access to manage clients, contacts, projects, invoices, workflows, time tracking, transactions, emails, calendar events, and more.

    ## Authentication
    The API supports multiple authentication methods:
    - **API Key**: Pass your key via `X-API-Key` header
    - **Bearer Token**: Pass a session token via `Authorization: Bearer <token>`
    - **OAuth 2.0**: Use authorization code flow for third-party integrations
    - **Developer Token**: For developer portal access via `Authorization: Bearer <developer_token>`

    ## Rate Limiting
    | Tier | Requests/min | Daily Limit |
    |------|-------------|-------------|
    | Developer | 60 | 1,000 |
    | Professional | 300 | 10,000 |
    | Enterprise | 1,000 | 100,000 |

    ## Pagination
    List endpoints return paginated results. Use `page` and `limit` query parameters.

    ## Errors
    All errors are JSON. They follow the `ApiError` schema: `error.code`, `error.message`, `error.type`, `error.request_id`, and where useful `error.details`, `error.documentation_url` and `error.hint`. The OAuth token and revoke endpoints follow RFC 6749 instead (`error` and `error_description`).
  contact:
    name: HireOrSell Support
    email: info@hireorsell.com
servers:
  - url: https://hireorsell.com/api/v1
    description: Production
  - url: /api/v1
    description: Relative to the current host

tags:
  - name: Authentication
    description: Token-based authentication
  - name: OAuth
    description: OAuth 2.0 authorization flows
  - name: Developer Portal
    description: Developer account management, API keys, subscriptions, and usage
  - name: Clients
    description: Client management with contacts, notes, invoices, and projects
  - name: Contacts
    description: Contact management with activity, comments, notes, tags, and tasks
  - name: Projects
    description: Project management with members, files, milestones, and reports
  - name: Invoices
    description: Invoice creation, payments, PDF generation, and delivery
  - name: Workflows
    description: Workflow management with issues, pause/resume, and archiving
  - name: Time Entries
    description: Time tracking with clock in/out and break management
  - name: Transactions
    description: Financial transactions and categories
  - name: Emails
    description: Email management, drafts, sending, sync, and threads
  - name: Calendar
    description: Calendar event management
  - name: Organization
    description: Organization settings, members, invitations, and business hours
  - name: Webhooks
    description: Webhook management, testing, deliveries, and verification
  - name: Reports
    description: Financial, project, time, and lead reports
  - name: User
    description: User notifications and preferences
  - name: Favorites
    description: Favorite items management

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: API key for authentication. Prefix with `xmp_live_sk_` for production.
    BearerAuth:
      type: http
      scheme: bearer
      description: Session bearer token
    DeveloperAuth:
      type: http
      scheme: bearer
      description: Developer portal bearer token
    OAuth2:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: /api/v1/oauth/authorize
          tokenUrl: /api/v1/oauth/token
          scopes:
            read:clients: Read client data
            write:clients: Create and modify clients
            read:contacts: Read contact data
            write:contacts: Create and modify contacts
            read:projects: Read project data
            write:projects: Create and modify projects
            read:invoices: Read invoice data
            write:invoices: Create and modify invoices
            read:workflows: Read workflow data
            write:workflows: Create and modify workflows

  parameters:
    PageParam:
      name: page
      in: query
      schema:
        type: integer
        minimum: 1
        default: 1
      description: Page number
    PerPageParam:
      name: limit
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20
      description: Items per page
    SortParam:
      name: sort
      in: query
      schema:
        type: string
      description: "Sort field. Prefix with `-` for descending (e.g. `-createdAt`)"
    SearchParam:
      name: search
      in: query
      schema:
        type: string
      description: Search query string
    IdempotencyKeyParam:
      name: Idempotency-Key
      in: header
      schema:
        type: string
      description: Unique key to prevent duplicate operations

  schemas:
    PaginationMeta:
      type: object
      properties:
        request_id:
          type: string
        timestamp:
          type: string
          format: date-time
        pagination:
          type: object
          properties:
            page:
              type: integer
            limit:
              type: integer
            total:
              type: integer
            total_pages:
              type: integer
    PaginationLinks:
      type: object
      properties:
        self:
          type: string
        first:
          type: string
        last:
          type: string
        prev:
          type: string
          nullable: true
        next:
          type: string
          nullable: true
    ApiError:
      type: object
      description: >-
        The JSON body of every error response from the API, except the OAuth token
        and revoke endpoints, which follow RFC 6749 (see OAuthError).
      required: [error]
      properties:
        error:
          type: object
          required: [code, message, type, request_id]
          properties:
            code:
              type: string
              description: Stable, machine-readable error code.
              example: not_found
            message:
              type: string
              description: Human-readable explanation of what went wrong.
            type:
              type: string
              description: The broad category of the error.
              enum:
                - invalid_request_error
                - authentication_error
                - permission_error
                - not_found_error
                - conflict_error
                - validation_error
                - rate_limit_error
                - api_error
                - gateway_error
                - service_unavailable
            request_id:
              type: string
              description: Quote this id when contacting support.
              example: req_062640ded97646218de63288
            details:
              type: array
              description: Field-level problems. Present on validation errors.
              items:
                $ref: '#/components/schemas/ApiErrorDetail'
            documentation_url:
              type: string
              format: uri
              description: Where the error format is documented.
            hint:
              type: string
              description: What the caller can do to fix the problem.
    ApiErrorDetail:
      type: object
      required: [field, code, message]
      properties:
        field:
          type: string
          description: The request field the problem refers to.
        code:
          type: string
          description: Machine-readable code for this field problem.
        message:
          type: string
          description: Human-readable explanation.
    OAuthError:
      type: object
      description: OAuth 2.0 error response (RFC 6749, section 5.2).
      required: [error]
      properties:
        error:
          type: string
          description: A standard OAuth 2.0 error code.
          example: invalid_request
        error_description:
          type: string
          description: Human-readable explanation.
    Meta:
      type: object
      properties:
        request_id:
          type: string
        timestamp:
          type: string
          format: date-time

  responses:
    Unauthorized:
      description: Authentication required
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          example:
            error:
              code: unauthorized
              message: Missing or invalid authentication credentials
              type: authentication_error
              request_id: req_abc123
    Forbidden:
      description: Insufficient permissions
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          example:
            error:
              code: forbidden
              message: You do not have permission to perform this action
              type: permission_error
              request_id: req_abc123
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          example:
            error:
              code: not_found
              message: The requested resource was not found
              type: not_found_error
              request_id: req_abc123
    ValidationError:
      description: Validation failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          example:
            error:
              code: validation_error
              message: The request body contains invalid fields
              type: validation_error
              request_id: req_abc123
    RateLimited:
      description: Rate limit exceeded
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          example:
            error:
              code: rate_limit_exceeded
              message: Too many requests. Please try again later.
              type: rate_limit_error
              request_id: req_abc123
    InternalError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          example:
            error:
              code: internal_error
              message: An internal server error occurred
              type: api_error
              request_id: req_abc123
    BadRequest:
      description: The request is malformed or a required parameter is missing
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          example:
            error:
              code: bad_request
              message: The request body is not valid JSON
              type: invalid_request_error
              request_id: req_abc123
    OAuthBadRequest:
      description: The OAuth request is invalid (RFC 6749, section 5.2)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/OAuthError'
          example:
            error: invalid_request
            error_description: grant_type is required
    OAuthInvalidClient:
      description: OAuth client authentication failed (RFC 6749, section 5.2)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/OAuthError'
          example:
            error: invalid_client
            error_description: Client not found
    OAuthServerError:
      description: Unexpected OAuth server error (RFC 6749, section 5.2)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/OAuthError'
          example:
            error: server_error
            error_description: An unexpected error occurred

paths:
  # ── Authentication ──────────────────────────────────────
  /auth/token:
    post:
      tags: [Authentication]
      summary: Create authentication token
      operationId: createAuthToken
      description: Exchange credentials for a bearer token.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, password]
              properties:
                email:
                  type: string
                  format: email
                password:
                  type: string
      responses:
        '200':
          description: Token created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      token:
                        type: string
                      expires_at:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'

  # ── OAuth 2.0 ───────────────────────────────────────────
  /oauth/authorize:
    get:
      tags: [OAuth]
      summary: Authorization page
      operationId: getOAuthAuthorize
      description: Renders the OAuth authorization consent page.
      parameters:
        - name: client_id
          in: query
          required: true
          schema:
            type: string
        - name: redirect_uri
          in: query
          required: true
          schema:
            type: string
            format: uri
        - name: response_type
          in: query
          required: true
          schema:
            type: string
            enum: [code]
        - name: scope
          in: query
          schema:
            type: string
        - name: state
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Authorization page HTML
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [OAuth]
      summary: Submit authorization decision
      operationId: postOAuthAuthorize
      description: User approves or denies the OAuth authorization request.
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [client_id, redirect_uri, approved]
              properties:
                client_id:
                  type: string
                redirect_uri:
                  type: string
                  format: uri
                scope:
                  type: string
                state:
                  type: string
                approved:
                  type: boolean
      responses:
        '200':
          description: Authorization code returned
          content:
            application/json:
              schema:
                type: object
                properties:
                  redirect_url:
                    type: string
                    format: uri
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'

  /oauth/token:
    post:
      tags: [OAuth]
      summary: Exchange code for tokens
      operationId: createOAuthToken
      description: Exchange an authorization code for access and refresh tokens.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [grant_type]
              properties:
                grant_type:
                  type: string
                  enum: [authorization_code, refresh_token]
                code:
                  type: string
                redirect_uri:
                  type: string
                  format: uri
                client_id:
                  type: string
                client_secret:
                  type: string
                refresh_token:
                  type: string
      responses:
        '200':
          description: Tokens issued
          content:
            application/json:
              schema:
                type: object
                properties:
                  access_token:
                    type: string
                  token_type:
                    type: string
                    example: bearer
                  expires_in:
                    type: integer
                  refresh_token:
                    type: string
                  scope:
                    type: string
        '400':
          $ref: '#/components/responses/OAuthBadRequest'
        '401':
          $ref: '#/components/responses/OAuthInvalidClient'
        '500':
          $ref: '#/components/responses/OAuthServerError'

  /oauth/revoke:
    post:
      tags: [OAuth]
      summary: Revoke a token
      operationId: revokeOAuthToken
      description: Revoke an access or refresh token.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [token]
              properties:
                token:
                  type: string
                token_type_hint:
                  type: string
                  enum: [access_token, refresh_token]
      responses:
        '200':
          description: Token revoked
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
        '400':
          $ref: '#/components/responses/OAuthBadRequest'
        '401':
          $ref: '#/components/responses/OAuthInvalidClient'
        '500':
          $ref: '#/components/responses/OAuthServerError'

  /oauth/tokeninfo:
    get:
      tags: [OAuth]
      summary: Get token info
      operationId: getOAuthTokenInfo
      description: Retrieve information about an access token.
      security:
        - OAuth2: []
      responses:
        '200':
          description: Token information
          content:
            application/json:
              schema:
                type: object
                properties:
                  active:
                    type: boolean
                  scope:
                    type: string
                  client_id:
                    type: string
                  expires_at:
                    type: string
                    format: date-time
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'

  # ── Developer Portal ────────────────────────────────────
  /developer/register:
    post:
      tags: [Developer Portal]
      summary: Register developer account
      operationId: registerDeveloper
      description: Create a new developer account for API access.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, password, name, company]
              properties:
                email:
                  type: string
                  format: email
                password:
                  type: string
                  minLength: 8
                name:
                  type: string
                company:
                  type: string
                website:
                  type: string
                  format: uri
      responses:
        '200':
          description: Developer account created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      email:
                        type: string
                      name:
                        type: string
                      token:
                        type: string
                  meta:
                    $ref: '#/components/schemas/Meta'
        '422':
          $ref: '#/components/responses/ValidationError'
        '500':
          $ref: '#/components/responses/InternalError'

  /developer/login:
    post:
      tags: [Developer Portal]
      summary: Developer login
      operationId: loginDeveloper
      description: Authenticate and receive a developer bearer token.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, password]
              properties:
                email:
                  type: string
                  format: email
                password:
                  type: string
      responses:
        '200':
          description: Login successful
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      token:
                        type: string
                      developer:
                        type: object
                        properties:
                          id:
                            type: string
                          email:
                            type: string
                          name:
                            type: string
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'

  /developer/me:
    get:
      tags: [Developer Portal]
      summary: Get developer profile
      operationId: getDeveloperProfile
      description: Retrieve the authenticated developer's profile and subscription info.
      security:
        - DeveloperAuth: []
      responses:
        '200':
          description: Developer profile
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      email:
                        type: string
                      name:
                        type: string
                      company:
                        type: string
                      subscription:
                        type: object
                        properties:
                          plan:
                            type: string
                          credits_monthly:
                            type: integer
                          credits_remaining:
                            type: integer
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'
    patch:
      tags: [Developer Portal]
      summary: Update developer profile
      operationId: updateDeveloperProfile
      description: "Update developer profile. PATCH /developer/me in the Developer Portal group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - DeveloperAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                company:
                  type: string
                website:
                  type: string
                  format: uri
      responses:
        '200':
          description: Profile updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'

  /developer/api-keys:
    get:
      tags: [Developer Portal]
      summary: List API keys
      operationId: listDeveloperApiKeys
      description: "List API keys. GET /developer/api-keys in the Developer Portal group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - DeveloperAuth: []
      responses:
        '200':
          description: List of API keys
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        name:
                          type: string
                        prefix:
                          type: string
                        scopes:
                          type: array
                          items:
                            type: string
                        last_used_at:
                          type: string
                          format: date-time
                          nullable: true
                        created_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Developer Portal]
      summary: Create API key
      operationId: createDeveloperApiKey
      description: "Create API key. POST /developer/api-keys in the Developer Portal group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - DeveloperAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                scopes:
                  type: array
                  items:
                    type: string
      responses:
        '200':
          description: API key created (key shown only once)
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      name:
                        type: string
                      key:
                        type: string
                        description: Full API key (only shown once)
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationError'
        '500':
          $ref: '#/components/responses/InternalError'

  /developer/api-keys/{id}:
    get:
      tags: [Developer Portal]
      summary: Get API key details
      operationId: getDeveloperApiKey
      description: "Get API key details. GET /developer/api-keys/{id} in the Developer Portal group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - DeveloperAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: API key details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Developer Portal]
      summary: Delete API key
      operationId: deleteDeveloperApiKey
      description: "Delete API key. DELETE /developer/api-keys/{id} in the Developer Portal group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - DeveloperAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: API key deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'

  /developer/api-keys/{id}/rotate:
    post:
      tags: [Developer Portal]
      summary: Rotate API key
      operationId: rotateDeveloperApiKey
      description: "Rotate API key. POST /developer/api-keys/{id}/rotate in the Developer Portal group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - DeveloperAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: New key generated (old key invalidated)
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      key:
                        type: string
                        description: New API key (shown only once)
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'

  /developer/subscriptions:
    get:
      tags: [Developer Portal]
      summary: Get subscription details
      operationId: getDeveloperSubscription
      description: "Get subscription details. GET /developer/subscriptions in the Developer Portal group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - DeveloperAuth: []
      responses:
        '200':
          description: Subscription information
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      plan:
                        type: string
                      status:
                        type: string
                      credits_monthly:
                        type: integer
                      credits_remaining:
                        type: integer
                      rate_limit:
                        type: integer
                      current_period_start:
                        type: string
                        format: date-time
                      current_period_end:
                        type: string
                        format: date-time
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'

  /developer/subscriptions/upgrade:
    post:
      tags: [Developer Portal]
      summary: Upgrade subscription
      operationId: upgradeDeveloperSubscription
      description: "Upgrade subscription. POST /developer/subscriptions/upgrade in the Developer Portal group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - DeveloperAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [plan]
              properties:
                plan:
                  type: string
                  enum: [professional, enterprise]
      responses:
        '200':
          description: Subscription upgraded
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationError'
        '500':
          $ref: '#/components/responses/InternalError'

  /developer/usage:
    get:
      tags: [Developer Portal]
      summary: Get API usage statistics
      operationId: getDeveloperUsage
      description: "Get API usage statistics. GET /developer/usage in the Developer Portal group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - DeveloperAuth: []
      parameters:
        - name: period
          in: query
          schema:
            type: string
            enum: [24h, 7d, 30d]
            default: 30d
      responses:
        '200':
          description: Usage statistics
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      object:
                        type: string
                        example: usage_report
                      period:
                        type: object
                        properties:
                          start:
                            type: string
                            format: date-time
                          end:
                            type: string
                            format: date-time
                          duration:
                            type: string
                      summary:
                        type: object
                        properties:
                          total_requests:
                            type: integer
                          total_credits_used:
                            type: integer
                          average_response_time_ms:
                            type: integer
                          success_rate:
                            type: number
                      by_endpoint:
                        type: array
                        items:
                          type: object
                          properties:
                            endpoint:
                              type: string
                            requests:
                              type: integer
                            credits:
                              type: integer
                      daily:
                        type: array
                        items:
                          type: object
                          properties:
                            date:
                              type: string
                            requests:
                              type: integer
                            credits:
                              type: integer
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'

  # ── Clients ─────────────────────────────────────────────
  /clients:
    get:
      tags: [Clients]
      summary: List clients
      operationId: listClients
      description: Retrieve a paginated list of clients with optional filtering and sorting.
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
        - OAuth2: [read:clients]
      parameters:
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
        - $ref: '#/components/parameters/SortParam'
        - $ref: '#/components/parameters/SearchParam'
        - name: type
          in: query
          schema:
            type: string
            enum: [individual, company]
          description: Filter by client type
      responses:
        '200':
          description: Paginated list of clients
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                        object:
                          type: string
                          example: client
                        name:
                          type: string
                        email:
                          type: string
                          format: email
                        phone:
                          type: string
                          nullable: true
                        type:
                          type: string
                          enum: [individual, company]
                        status:
                          type: string
                        created_at:
                          type: string
                          format: date-time
                        updated_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
                  links:
                    $ref: '#/components/schemas/PaginationLinks'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Clients]
      summary: Create a client
      operationId: createClient
      description: "Create a client. POST /clients in the Clients group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
        - OAuth2: [write:clients]
      parameters:
        - $ref: '#/components/parameters/IdempotencyKeyParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, email]
              properties:
                name:
                  type: string
                email:
                  type: string
                  format: email
                phone:
                  type: string
                type:
                  type: string
                  enum: [individual, company]
                  default: individual
                company:
                  type: string
                address:
                  type: object
                  properties:
                    street:
                      type: string
                    city:
                      type: string
                    state:
                      type: string
                    zip:
                      type: string
                    country:
                      type: string
            example:
              name: "Acme Corp"
              email: "billing@acme.com"
              type: "company"
              phone: "+1-555-0100"
      responses:
        '200':
          description: Client created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      object:
                        type: string
                        example: client
                      name:
                        type: string
                      email:
                        type: string
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /clients/bulk:
    post:
      tags: [Clients]
      summary: Bulk create clients
      operationId: bulkCreateClients
      description: "Bulk create clients. POST /clients/bulk in the Clients group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [clients]
              properties:
                clients:
                  type: array
                  items:
                    type: object
                    required: [name, email]
                    properties:
                      name:
                        type: string
                      email:
                        type: string
                        format: email
                      phone:
                        type: string
                      type:
                        type: string
      responses:
        '200':
          description: Bulk operation result
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      created:
                        type: integer
                      errors:
                        type: array
                        items:
                          type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /clients/{id}:
    get:
      tags: [Clients]
      summary: Get a client
      operationId: getClient
      description: "Get a client. GET /clients/{id} in the Clients group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
        - OAuth2: [read:clients]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Client details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    patch:
      tags: [Clients]
      summary: Update a client
      operationId: updateClient
      description: "Update a client. PATCH /clients/{id} in the Clients group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
        - OAuth2: [write:clients]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                email:
                  type: string
                  format: email
                phone:
                  type: string
                type:
                  type: string
                status:
                  type: string
      responses:
        '200':
          description: Client updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Clients]
      summary: Delete a client
      operationId: deleteClient
      description: "Delete a client. DELETE /clients/{id} in the Clients group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
        - OAuth2: [write:clients]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Client deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                      id:
                        type: string
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /clients/{id}/contacts:
    get:
      tags: [Clients]
      summary: List client contacts
      operationId: listClientContacts
      description: "List client contacts. GET /clients/{id}/contacts in the Clients group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
      responses:
        '200':
          description: List of contacts for this client
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Clients]
      summary: Add contact to client
      operationId: addClientContact
      description: "Add contact to client. POST /clients/{id}/contacts in the Clients group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [contact_id]
              properties:
                contact_id:
                  type: string
                  format: uuid
      responses:
        '200':
          description: Contact linked
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /clients/{id}/contacts/{contactId}:
    delete:
      tags: [Clients]
      summary: Remove contact from client
      operationId: removeClientContact
      description: "Remove contact from client. DELETE /clients/{id}/contacts/{contactId} in the Clients group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: contactId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Contact unlinked
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /clients/{id}/invoices:
    get:
      tags: [Clients]
      summary: List client invoices
      operationId: listClientInvoices
      description: "List client invoices. GET /clients/{id}/invoices in the Clients group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
      responses:
        '200':
          description: Paginated list of invoices
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /clients/{id}/notes:
    get:
      tags: [Clients]
      summary: List client notes
      operationId: listClientNotes
      description: "List client notes. GET /clients/{id}/notes in the Clients group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: List of notes
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        content:
                          type: string
                        created_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Clients]
      summary: Create client note
      operationId: createClientNote
      description: "Create client note. POST /clients/{id}/notes in the Clients group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [content]
              properties:
                content:
                  type: string
                  maxLength: 8000
      responses:
        '200':
          description: Note created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /clients/{id}/notes/{noteId}:
    patch:
      tags: [Clients]
      summary: Update client note
      operationId: updateClientNote
      description: "Update client note. PATCH /clients/{id}/notes/{noteId} in the Clients group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: noteId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [content]
              properties:
                content:
                  type: string
      responses:
        '200':
          description: Note updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Clients]
      summary: Delete client note
      operationId: deleteClientNote
      description: "Delete client note. DELETE /clients/{id}/notes/{noteId} in the Clients group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: noteId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Note deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /clients/{id}/projects:
    get:
      tags: [Clients]
      summary: List client projects
      operationId: listClientProjects
      description: "List client projects. GET /clients/{id}/projects in the Clients group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: List of projects
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  # ── Contacts ────────────────────────────────────────────
  /contacts:
    get:
      tags: [Contacts]
      summary: List contacts
      operationId: listContacts
      description: "List contacts. GET /contacts in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
        - OAuth2: [read:contacts]
      parameters:
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
        - $ref: '#/components/parameters/SortParam'
        - $ref: '#/components/parameters/SearchParam'
        - name: status
          in: query
          schema:
            type: string
            enum: [lead, prospect, active, inactive, churned]
      responses:
        '200':
          description: Paginated list of contacts
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                        object:
                          type: string
                          example: contact
                        first_name:
                          type: string
                        last_name:
                          type: string
                        email:
                          type: string
                          format: email
                        phone:
                          type: string
                          nullable: true
                        status:
                          type: string
                        company:
                          type: string
                          nullable: true
                        created_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
                  links:
                    $ref: '#/components/schemas/PaginationLinks'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Contacts]
      summary: Create a contact
      operationId: createContact
      description: "Create a contact. POST /contacts in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
        - OAuth2: [write:contacts]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [firstName, lastName, email]
              properties:
                firstName:
                  type: string
                lastName:
                  type: string
                email:
                  type: string
                  format: email
                phone:
                  type: string
                company:
                  type: string
                status:
                  type: string
                  enum: [lead, prospect, active, inactive, churned]
                  default: lead
            example:
              firstName: "John"
              lastName: "Doe"
              email: "john@example.com"
              status: "lead"
      responses:
        '200':
          description: Contact created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /contacts/bulk:
    post:
      tags: [Contacts]
      summary: Bulk create contacts
      operationId: bulkCreateContacts
      description: "Bulk create contacts. POST /contacts/bulk in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [contacts]
              properties:
                contacts:
                  type: array
                  items:
                    type: object
      responses:
        '200':
          description: Bulk result
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      created:
                        type: integer
                      errors:
                        type: array
                        items:
                          type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /contacts/{id}:
    get:
      tags: [Contacts]
      summary: Get a contact
      operationId: getContact
      description: "Get a contact. GET /contacts/{id} in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
        - OAuth2: [read:contacts]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Contact details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    patch:
      tags: [Contacts]
      summary: Update a contact
      operationId: updateContact
      description: "Update a contact. PATCH /contacts/{id} in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
        - OAuth2: [write:contacts]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                firstName:
                  type: string
                lastName:
                  type: string
                email:
                  type: string
                phone:
                  type: string
                status:
                  type: string
                company:
                  type: string
      responses:
        '200':
          description: Contact updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Contacts]
      summary: Delete a contact
      operationId: deleteContact
      description: "Delete a contact. DELETE /contacts/{id} in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
        - OAuth2: [write:contacts]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Contact deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                      id:
                        type: string
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /contacts/{id}/activity:
    get:
      tags: [Contacts]
      summary: Get contact activity
      operationId: getContactActivity
      description: "Get contact activity. GET /contacts/{id}/activity in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
      responses:
        '200':
          description: Contact activity log
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /contacts/{id}/comments:
    get:
      tags: [Contacts]
      summary: List contact comments
      operationId: listContactComments
      description: "List contact comments. GET /contacts/{id}/comments in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: List of comments
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Contacts]
      summary: Create contact comment
      operationId: createContactComment
      description: "Create contact comment. POST /contacts/{id}/comments in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [content]
              properties:
                content:
                  type: string
      responses:
        '200':
          description: Comment created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /contacts/{id}/comments/{commentId}:
    patch:
      tags: [Contacts]
      summary: Update contact comment
      operationId: updateContactComment
      description: "Update contact comment. PATCH /contacts/{id}/comments/{commentId} in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: commentId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [content]
              properties:
                content:
                  type: string
      responses:
        '200':
          description: Comment updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Contacts]
      summary: Delete contact comment
      operationId: deleteContactComment
      description: "Delete contact comment. DELETE /contacts/{id}/comments/{commentId} in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: commentId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Comment deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /contacts/{id}/emails:
    get:
      tags: [Contacts]
      summary: List contact emails
      operationId: listContactEmails
      description: "List contact emails. GET /contacts/{id}/emails in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: List of emails
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /contacts/{id}/notes:
    get:
      tags: [Contacts]
      summary: List contact notes
      operationId: listContactNotes
      description: "List contact notes. GET /contacts/{id}/notes in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: List of notes
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Contacts]
      summary: Create contact note
      operationId: createContactNote
      description: "Create contact note. POST /contacts/{id}/notes in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [content]
              properties:
                content:
                  type: string
                  maxLength: 8000
      responses:
        '200':
          description: Note created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /contacts/{id}/notes/{noteId}:
    patch:
      tags: [Contacts]
      summary: Update contact note
      operationId: updateContactNote
      description: "Update contact note. PATCH /contacts/{id}/notes/{noteId} in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: noteId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [content]
              properties:
                content:
                  type: string
      responses:
        '200':
          description: Note updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Contacts]
      summary: Delete contact note
      operationId: deleteContactNote
      description: "Delete contact note. DELETE /contacts/{id}/notes/{noteId} in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: noteId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Note deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /contacts/{id}/tags:
    get:
      tags: [Contacts]
      summary: List contact tags
      operationId: listContactTags
      description: "List contact tags. GET /contacts/{id}/tags in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: List of tags
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        name:
                          type: string
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    put:
      tags: [Contacts]
      summary: Update contact tags
      operationId: updateContactTags
      description: "Update contact tags. PUT /contacts/{id}/tags in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [tags]
              properties:
                tags:
                  type: array
                  items:
                    type: string
      responses:
        '200':
          description: Tags updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /contacts/{id}/tasks:
    get:
      tags: [Contacts]
      summary: List contact tasks
      operationId: listContactTasks
      description: "List contact tasks. GET /contacts/{id}/tasks in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
        - name: status
          in: query
          schema:
            type: string
            enum: [open, in_progress, completed, cancelled]
      responses:
        '200':
          description: List of tasks
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Contacts]
      summary: Create contact task
      operationId: createContactTask
      description: "Create contact task. POST /contacts/{id}/tasks in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [title]
              properties:
                title:
                  type: string
                description:
                  type: string
                status:
                  type: string
                  enum: [open, in_progress, completed, cancelled]
                  default: open
                due_date:
                  type: string
                  format: date-time
      responses:
        '200':
          description: Task created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /contacts/{id}/tasks/{taskId}:
    patch:
      tags: [Contacts]
      summary: Update contact task
      operationId: updateContactTask
      description: "Update contact task. PATCH /contacts/{id}/tasks/{taskId} in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: taskId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                status:
                  type: string
                due_date:
                  type: string
                  format: date-time
                  nullable: true
      responses:
        '200':
          description: Task updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Contacts]
      summary: Delete contact task
      operationId: deleteContactTask
      description: "Delete contact task. DELETE /contacts/{id}/tasks/{taskId} in the Contacts group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: taskId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Task deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  # ── Projects ────────────────────────────────────────────
  /projects:
    get:
      tags: [Projects]
      summary: List projects
      operationId: listProjects
      description: "List projects. GET /projects in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
        - OAuth2: [read:projects]
      parameters:
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
        - $ref: '#/components/parameters/SortParam'
        - $ref: '#/components/parameters/SearchParam'
        - name: status
          in: query
          schema:
            type: string
            enum: [active, completed, on_hold, cancelled]
      responses:
        '200':
          description: Paginated list of projects
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                        name:
                          type: string
                        status:
                          type: string
                        budget:
                          type: number
                          nullable: true
                        start_date:
                          type: string
                          format: date-time
                          nullable: true
                        end_date:
                          type: string
                          format: date-time
                          nullable: true
                        created_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
                  links:
                    $ref: '#/components/schemas/PaginationLinks'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Projects]
      summary: Create a project
      operationId: createProject
      description: "Create a project. POST /projects in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
        - OAuth2: [write:projects]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                description:
                  type: string
                status:
                  type: string
                  default: active
                budget:
                  type: number
                start_date:
                  type: string
                  format: date-time
                end_date:
                  type: string
                  format: date-time
            example:
              name: "Website Redesign"
              budget: 15000
              status: "active"
      responses:
        '200':
          description: Project created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /projects/{id}:
    get:
      tags: [Projects]
      summary: Get a project
      operationId: getProject
      description: "Get a project. GET /projects/{id} in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Project details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    patch:
      tags: [Projects]
      summary: Update a project
      operationId: updateProject
      description: "Update a project. PATCH /projects/{id} in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
                status:
                  type: string
                budget:
                  type: number
      responses:
        '200':
          description: Project updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Projects]
      summary: Delete a project
      operationId: deleteProject
      description: "Delete a project. DELETE /projects/{id} in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Project deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /projects/{id}/activity:
    get:
      tags: [Projects]
      summary: Get project activity
      operationId: getProjectActivity
      description: "Get project activity. GET /projects/{id}/activity in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Activity log
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /projects/{id}/clients:
    get:
      tags: [Projects]
      summary: List project clients
      operationId: listProjectClients
      description: "List project clients. GET /projects/{id}/clients in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: List of clients
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Projects]
      summary: Add client to project
      operationId: addProjectClient
      description: "Add client to project. POST /projects/{id}/clients in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [client_id]
              properties:
                client_id:
                  type: string
                  format: uuid
      responses:
        '200':
          description: Client added
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /projects/{id}/clients/{clientId}:
    delete:
      tags: [Projects]
      summary: Remove client from project
      operationId: removeProjectClient
      description: "Remove client from project. DELETE /projects/{id}/clients/{clientId} in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: clientId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Client removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /projects/{id}/files:
    get:
      tags: [Projects]
      summary: List project files
      operationId: listProjectFiles
      description: "List project files. GET /projects/{id}/files in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
      responses:
        '200':
          description: List of files
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Projects]
      summary: Upload file to project
      operationId: uploadProjectFile
      description: "Upload file to project. POST /projects/{id}/files in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: string
                  format: binary
                category:
                  type: string
                description:
                  type: string
      responses:
        '200':
          description: File uploaded
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /projects/{id}/files/{fileId}:
    get:
      tags: [Projects]
      summary: Get file details
      operationId: getProjectFile
      description: "Get file details. GET /projects/{id}/files/{fileId} in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: fileId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: File details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Projects]
      summary: Delete file
      operationId: deleteProjectFile
      description: "Delete file. DELETE /projects/{id}/files/{fileId} in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: fileId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: File deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /projects/{id}/members:
    get:
      tags: [Projects]
      summary: List project members
      operationId: listProjectMembers
      description: "List project members. GET /projects/{id}/members in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: List of members
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Projects]
      summary: Add member to project
      operationId: addProjectMember
      description: "Add member to project. POST /projects/{id}/members in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [user_id]
              properties:
                user_id:
                  type: string
                  format: uuid
                role:
                  type: string
      responses:
        '200':
          description: Member added
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /projects/{id}/members/{memberId}:
    patch:
      tags: [Projects]
      summary: Update member role
      operationId: updateProjectMember
      description: "Update member role. PATCH /projects/{id}/members/{memberId} in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: memberId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                role:
                  type: string
      responses:
        '200':
          description: Member updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Projects]
      summary: Remove member
      operationId: removeProjectMember
      description: "Remove member. DELETE /projects/{id}/members/{memberId} in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: memberId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Member removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /projects/{id}/milestones:
    get:
      tags: [Projects]
      summary: List milestones
      operationId: listProjectMilestones
      description: "List milestones. GET /projects/{id}/milestones in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: List of milestones
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        name:
                          type: string
                        status:
                          type: string
                        due_date:
                          type: string
                          format: date-time
                          nullable: true
                        budget_amount:
                          type: number
                          nullable: true
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Projects]
      summary: Create milestone
      operationId: createProjectMilestone
      description: "Create milestone. POST /projects/{id}/milestones in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                description:
                  type: string
                due_date:
                  type: string
                  format: date-time
                amount:
                  type: number
      responses:
        '200':
          description: Milestone created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /projects/{id}/milestones/{milestoneId}:
    patch:
      tags: [Projects]
      summary: Update milestone
      operationId: updateProjectMilestone
      description: "Update milestone. PATCH /projects/{id}/milestones/{milestoneId} in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: milestoneId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                due_date:
                  type: string
                  format: date-time
                  nullable: true
                amount:
                  type: number
      responses:
        '200':
          description: Milestone updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Projects]
      summary: Delete milestone
      operationId: deleteProjectMilestone
      description: "Delete milestone. DELETE /projects/{id}/milestones/{milestoneId} in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: milestoneId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Milestone deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /projects/{id}/report:
    get:
      tags: [Projects]
      summary: Get project report
      operationId: getProjectReport
      description: "Get project report. GET /projects/{id}/report in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Project report with financials, hours, and milestone data
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      financials:
                        type: object
                        properties:
                          estimated_budget:
                            type: number
                          actual_cost:
                            type: number
                          total_invoiced:
                            type: number
                      hours:
                        type: object
                        properties:
                          total_hours:
                            type: number
                          billable_hours:
                            type: number
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /projects/{id}/timeline:
    get:
      tags: [Projects]
      summary: Get project timeline
      operationId: getProjectTimeline
      description: "Get project timeline. GET /projects/{id}/timeline in the Projects group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Timeline data
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  # ── Invoices ────────────────────────────────────────────
  /invoices:
    get:
      tags: [Invoices]
      summary: List invoices
      operationId: listInvoices
      description: "List invoices. GET /invoices in the Invoices group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
        - OAuth2: [read:invoices]
      parameters:
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
        - $ref: '#/components/parameters/SortParam'
        - name: status
          in: query
          schema:
            type: string
            enum: [draft, sent, paid, overdue, cancelled]
      responses:
        '200':
          description: Paginated list of invoices
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                        invoice_number:
                          type: string
                        status:
                          type: string
                        total:
                          type: number
                        due_date:
                          type: string
                          format: date-time
                        client:
                          type: object
                          properties:
                            id:
                              type: string
                            name:
                              type: string
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Invoices]
      summary: Create an invoice
      operationId: createInvoice
      description: "Create an invoice. POST /invoices in the Invoices group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
        - OAuth2: [write:invoices]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [client_id, items]
              properties:
                client_id:
                  type: string
                  format: uuid
                due_date:
                  type: string
                  format: date-time
                items:
                  type: array
                  items:
                    type: object
                    properties:
                      description:
                        type: string
                      quantity:
                        type: number
                      rate:
                        type: number
                notes:
                  type: string
                tax_rate:
                  type: number
            example:
              client_id: "550e8400-e29b-41d4-a716-446655440000"
              items:
                - description: "Web Development"
                  quantity: 40
                  rate: 150
      responses:
        '200':
          description: Invoice created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /invoices/{id}:
    get:
      tags: [Invoices]
      summary: Get an invoice
      operationId: getInvoice
      description: "Get an invoice. GET /invoices/{id} in the Invoices group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Invoice details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    patch:
      tags: [Invoices]
      summary: Update an invoice
      operationId: updateInvoice
      description: "Update an invoice. PATCH /invoices/{id} in the Invoices group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                status:
                  type: string
                due_date:
                  type: string
                  format: date-time
                notes:
                  type: string
                items:
                  type: array
                  items:
                    type: object
      responses:
        '200':
          description: Invoice updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Invoices]
      summary: Delete an invoice
      operationId: deleteInvoice
      description: "Delete an invoice. DELETE /invoices/{id} in the Invoices group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Invoice deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /invoices/{id}/payment:
    post:
      tags: [Invoices]
      summary: Record payment
      operationId: recordInvoicePayment
      description: "Record payment. POST /invoices/{id}/payment in the Invoices group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [amount]
              properties:
                amount:
                  type: number
                payment_method:
                  type: string
                payment_date:
                  type: string
                  format: date-time
                notes:
                  type: string
      responses:
        '200':
          description: Payment recorded
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /invoices/{id}/pdf:
    get:
      tags: [Invoices]
      summary: Download invoice PDF
      operationId: getInvoicePdf
      description: "Download invoice PDF. GET /invoices/{id}/pdf in the Invoices group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: PDF file
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /invoices/{id}/send:
    post:
      tags: [Invoices]
      summary: Send invoice via email
      operationId: sendInvoice
      description: "Send invoice via email. POST /invoices/{id}/send in the Invoices group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                message:
                  type: string
                  description: Optional custom message
      responses:
        '200':
          description: Invoice sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      sent:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  # ── Workflows ───────────────────────────────────────────
  /workflows:
    get:
      tags: [Workflows]
      summary: List workflows
      operationId: listWorkflows
      description: "List workflows. GET /workflows in the Workflows group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
        - OAuth2: [read:workflows]
      parameters:
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
        - $ref: '#/components/parameters/SortParam'
        - name: status
          in: query
          schema:
            type: string
            enum: [active, paused, completed, archived]
      responses:
        '200':
          description: Paginated list of workflows
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                        name:
                          type: string
                        status:
                          type: string
                        project_id:
                          type: string
                          nullable: true
                        created_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Workflows]
      summary: Create a workflow
      operationId: createWorkflow
      description: "Create a workflow. POST /workflows in the Workflows group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
        - OAuth2: [write:workflows]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                description:
                  type: string
                project_id:
                  type: string
                  format: uuid
                status:
                  type: string
                  default: active
      responses:
        '200':
          description: Workflow created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /workflows/{id}:
    get:
      tags: [Workflows]
      summary: Get a workflow
      operationId: getWorkflow
      description: "Get a workflow. GET /workflows/{id} in the Workflows group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Workflow details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    patch:
      tags: [Workflows]
      summary: Update a workflow
      operationId: updateWorkflow
      description: "Update a workflow. PATCH /workflows/{id} in the Workflows group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
                status:
                  type: string
      responses:
        '200':
          description: Workflow updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Workflows]
      summary: Delete a workflow
      operationId: deleteWorkflow
      description: "Delete a workflow. DELETE /workflows/{id} in the Workflows group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Workflow deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /workflows/{id}/archive:
    post:
      tags: [Workflows]
      summary: Archive workflow
      operationId: archiveWorkflow
      description: "Archive workflow. POST /workflows/{id}/archive in the Workflows group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Workflow archived
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /workflows/{id}/pause:
    post:
      tags: [Workflows]
      summary: Pause workflow
      operationId: pauseWorkflow
      description: "Pause workflow. POST /workflows/{id}/pause in the Workflows group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Workflow paused
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /workflows/{id}/resume:
    post:
      tags: [Workflows]
      summary: Resume workflow
      operationId: resumeWorkflow
      description: "Resume workflow. POST /workflows/{id}/resume in the Workflows group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Workflow resumed
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /workflows/{id}/issues:
    get:
      tags: [Workflows]
      summary: List workflow issues
      operationId: listWorkflowIssues
      description: "List workflow issues. GET /workflows/{id}/issues in the Workflows group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
      responses:
        '200':
          description: List of issues
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Workflows]
      summary: Create workflow issue
      operationId: createWorkflowIssue
      description: "Create workflow issue. POST /workflows/{id}/issues in the Workflows group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [title]
              properties:
                title:
                  type: string
                description:
                  type: string
                priority:
                  type: string
                  enum: [low, medium, high, critical]
                assignee_id:
                  type: string
                  format: uuid
      responses:
        '200':
          description: Issue created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /workflows/{id}/issues/{issueId}:
    patch:
      tags: [Workflows]
      summary: Update workflow issue
      operationId: updateWorkflowIssue
      description: "Update workflow issue. PATCH /workflows/{id}/issues/{issueId} in the Workflows group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: issueId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                status:
                  type: string
                priority:
                  type: string
      responses:
        '200':
          description: Issue updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Workflows]
      summary: Delete workflow issue
      operationId: deleteWorkflowIssue
      description: "Delete workflow issue. DELETE /workflows/{id}/issues/{issueId} in the Workflows group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: issueId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Issue deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  # ── Time Entries ────────────────────────────────────────
  /time-entries:
    get:
      tags: [Time Entries]
      summary: List time entries
      operationId: listTimeEntries
      description: "List time entries. GET /time-entries in the Time Entries group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
        - name: project_id
          in: query
          schema:
            type: string
            format: uuid
        - name: start_date
          in: query
          schema:
            type: string
            format: date
        - name: end_date
          in: query
          schema:
            type: string
            format: date
      responses:
        '200':
          description: Paginated list of time entries
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        description:
                          type: string
                        hours:
                          type: number
                        billable:
                          type: boolean
                        project_id:
                          type: string
                          nullable: true
                        date:
                          type: string
                          format: date
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Time Entries]
      summary: Create time entry
      operationId: createTimeEntry
      description: "Create time entry. POST /time-entries in the Time Entries group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [description, hours]
              properties:
                description:
                  type: string
                hours:
                  type: number
                billable:
                  type: boolean
                  default: true
                project_id:
                  type: string
                  format: uuid
                date:
                  type: string
                  format: date
      responses:
        '200':
          description: Time entry created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /time-entries/{id}:
    get:
      tags: [Time Entries]
      summary: Get time entry
      operationId: getTimeEntry
      description: "Get time entry. GET /time-entries/{id} in the Time Entries group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Time entry details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    patch:
      tags: [Time Entries]
      summary: Update time entry
      operationId: updateTimeEntry
      description: "Update time entry. PATCH /time-entries/{id} in the Time Entries group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                description:
                  type: string
                hours:
                  type: number
                billable:
                  type: boolean
      responses:
        '200':
          description: Time entry updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Time Entries]
      summary: Delete time entry
      operationId: deleteTimeEntry
      description: "Delete time entry. DELETE /time-entries/{id} in the Time Entries group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Time entry deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /time-entries/clock-in:
    post:
      tags: [Time Entries]
      summary: Clock in
      operationId: clockIn
      description: Start a new time tracking session.
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                project_id:
                  type: string
                  format: uuid
                description:
                  type: string
      responses:
        '200':
          description: Clocked in
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /time-entries/clock-out:
    post:
      tags: [Time Entries]
      summary: Clock out
      operationId: clockOut
      description: End the current time tracking session.
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        '200':
          description: Clocked out
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /time-entries/{id}/break/start:
    post:
      tags: [Time Entries]
      summary: Start break
      operationId: startBreak
      description: "Start break. POST /time-entries/{id}/break/start in the Time Entries group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Break started
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /time-entries/{id}/break/end:
    post:
      tags: [Time Entries]
      summary: End break
      operationId: endBreak
      description: "End break. POST /time-entries/{id}/break/end in the Time Entries group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Break ended
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  # ── Transactions ────────────────────────────────────────
  /transactions:
    get:
      tags: [Transactions]
      summary: List transactions
      operationId: listTransactions
      description: "List transactions. GET /transactions in the Transactions group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
        - $ref: '#/components/parameters/SortParam'
        - name: type
          in: query
          schema:
            type: string
            enum: [income, expense]
        - name: category_id
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Paginated list of transactions
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Transactions]
      summary: Create transaction
      operationId: createTransaction
      description: "Create transaction. POST /transactions in the Transactions group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [amount, type, description]
              properties:
                amount:
                  type: number
                type:
                  type: string
                  enum: [income, expense]
                description:
                  type: string
                category_id:
                  type: string
                date:
                  type: string
                  format: date
      responses:
        '200':
          description: Transaction created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /transactions/bulk:
    post:
      tags: [Transactions]
      summary: Bulk create transactions
      operationId: bulkCreateTransactions
      description: "Bulk create transactions. POST /transactions/bulk in the Transactions group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [transactions]
              properties:
                transactions:
                  type: array
                  items:
                    type: object
      responses:
        '200':
          description: Bulk result
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /transactions/{id}:
    get:
      tags: [Transactions]
      summary: Get transaction
      operationId: getTransaction
      description: "Get transaction. GET /transactions/{id} in the Transactions group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Transaction details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    patch:
      tags: [Transactions]
      summary: Update transaction
      operationId: updateTransaction
      description: "Update transaction. PATCH /transactions/{id} in the Transactions group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                amount:
                  type: number
                description:
                  type: string
                category_id:
                  type: string
      responses:
        '200':
          description: Transaction updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Transactions]
      summary: Delete transaction
      operationId: deleteTransaction
      description: "Delete transaction. DELETE /transactions/{id} in the Transactions group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Transaction deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /transactions/categories:
    get:
      tags: [Transactions]
      summary: List categories
      operationId: listTransactionCategories
      description: "List categories. GET /transactions/categories in the Transactions group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        '200':
          description: List of categories
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Transactions]
      summary: Create category
      operationId: createTransactionCategory
      description: "Create category. POST /transactions/categories in the Transactions group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                type:
                  type: string
                  enum: [income, expense]
      responses:
        '200':
          description: Category created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /transactions/categories/{id}:
    patch:
      tags: [Transactions]
      summary: Update category
      operationId: updateTransactionCategory
      description: "Update category. PATCH /transactions/categories/{id} in the Transactions group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
      responses:
        '200':
          description: Category updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Transactions]
      summary: Delete category
      operationId: deleteTransactionCategory
      description: "Delete category. DELETE /transactions/categories/{id} in the Transactions group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Category deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  # ── Emails ──────────────────────────────────────────────
  /emails/drafts:
    post:
      tags: [Emails]
      summary: Create draft
      operationId: createEmailDraft
      description: "Create draft. POST /emails/drafts in the Emails group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                to:
                  type: array
                  items:
                    type: string
                    format: email
                subject:
                  type: string
                body:
                  type: string
                cc:
                  type: array
                  items:
                    type: string
                    format: email
                bcc:
                  type: array
                  items:
                    type: string
                    format: email
      responses:
        '200':
          description: Draft created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /emails/send:
    post:
      tags: [Emails]
      summary: Send email
      operationId: sendEmail
      description: "Send email. POST /emails/send in the Emails group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [to, subject, body]
              properties:
                to:
                  type: array
                  items:
                    type: string
                    format: email
                subject:
                  type: string
                body:
                  type: string
                cc:
                  type: array
                  items:
                    type: string
                bcc:
                  type: array
                  items:
                    type: string
      responses:
        '200':
          description: Email sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      sent:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /emails/sync:
    post:
      tags: [Emails]
      summary: Sync emails
      operationId: syncEmails
      description: Trigger email synchronization with configured email provider.
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        '200':
          description: Sync triggered
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      synced:
                        type: boolean
                      count:
                        type: integer
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /emails/{id}:
    get:
      tags: [Emails]
      summary: Get email
      operationId: getEmail
      description: "Get email. GET /emails/{id} in the Emails group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Email details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    patch:
      tags: [Emails]
      summary: Update email
      operationId: updateEmail
      description: "Update email. PATCH /emails/{id} in the Emails group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                read:
                  type: boolean
                starred:
                  type: boolean
                labels:
                  type: array
                  items:
                    type: string
      responses:
        '200':
          description: Email updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Emails]
      summary: Delete email
      operationId: deleteEmail
      description: "Delete email. DELETE /emails/{id} in the Emails group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Email deleted (moved to trash)
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /emails/{id}/restore:
    post:
      tags: [Emails]
      summary: Restore email
      operationId: restoreEmail
      description: Restore an email from the trash.
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Email restored
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /emails/threads:
    get:
      tags: [Emails]
      summary: List email threads
      operationId: listEmailThreads
      description: "List email threads. GET /emails/threads in the Emails group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
      responses:
        '200':
          description: List of threads
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /emails/threads/{id}:
    get:
      tags: [Emails]
      summary: Get email thread
      operationId: getEmailThread
      description: "Get email thread. GET /emails/threads/{id} in the Emails group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Thread with messages
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  # ── Calendar ────────────────────────────────────────────
  /calendar/events:
    get:
      tags: [Calendar]
      summary: List events
      operationId: listCalendarEvents
      description: "List events. GET /calendar/events in the Calendar group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: start
          in: query
          schema:
            type: string
            format: date-time
        - name: end
          in: query
          schema:
            type: string
            format: date-time
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
      responses:
        '200':
          description: List of events
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        title:
                          type: string
                        start:
                          type: string
                          format: date-time
                        end:
                          type: string
                          format: date-time
                        all_day:
                          type: boolean
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Calendar]
      summary: Create event
      operationId: createCalendarEvent
      description: "Create event. POST /calendar/events in the Calendar group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [title, start, end]
              properties:
                title:
                  type: string
                start:
                  type: string
                  format: date-time
                end:
                  type: string
                  format: date-time
                all_day:
                  type: boolean
                  default: false
                description:
                  type: string
                location:
                  type: string
      responses:
        '200':
          description: Event created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /calendar/events/{id}:
    get:
      tags: [Calendar]
      summary: Get event
      operationId: getCalendarEvent
      description: "Get event. GET /calendar/events/{id} in the Calendar group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Event details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    patch:
      tags: [Calendar]
      summary: Update event
      operationId: updateCalendarEvent
      description: "Update event. PATCH /calendar/events/{id} in the Calendar group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                start:
                  type: string
                  format: date-time
                end:
                  type: string
                  format: date-time
                description:
                  type: string
      responses:
        '200':
          description: Event updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Calendar]
      summary: Delete event
      operationId: deleteCalendarEvent
      description: "Delete event. DELETE /calendar/events/{id} in the Calendar group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Event deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  # ── Organization ────────────────────────────────────────
  /organization:
    get:
      tags: [Organization]
      summary: Get organization
      operationId: getOrganization
      description: "Get organization. GET /organization in the Organization group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        '200':
          description: Organization details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    patch:
      tags: [Organization]
      summary: Update organization
      operationId: updateOrganization
      description: "Update organization. PATCH /organization in the Organization group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                website:
                  type: string
                phone:
                  type: string
                address:
                  type: object
      responses:
        '200':
          description: Organization updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /organization/business-hours:
    get:
      tags: [Organization]
      summary: Get business hours
      operationId: getBusinessHours
      description: "Get business hours. GET /organization/business-hours in the Organization group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        '200':
          description: Business hours settings
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    put:
      tags: [Organization]
      summary: Update business hours
      operationId: updateBusinessHours
      description: "Update business hours. PUT /organization/business-hours in the Organization group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                timezone:
                  type: string
                schedule:
                  type: object
      responses:
        '200':
          description: Business hours updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /organization/members:
    get:
      tags: [Organization]
      summary: List members
      operationId: listOrganizationMembers
      description: "List members. GET /organization/members in the Organization group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
      responses:
        '200':
          description: List of members
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /organization/members/invite:
    post:
      tags: [Organization]
      summary: Invite member
      operationId: inviteOrganizationMember
      description: "Invite member. POST /organization/members/invite in the Organization group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, role]
              properties:
                email:
                  type: string
                  format: email
                role:
                  type: string
      responses:
        '200':
          description: Invitation sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /organization/members/{id}:
    patch:
      tags: [Organization]
      summary: Update member role
      operationId: updateOrganizationMember
      description: "Update member role. PATCH /organization/members/{id} in the Organization group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                role:
                  type: string
      responses:
        '200':
          description: Member updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Organization]
      summary: Remove member
      operationId: removeOrganizationMember
      description: "Remove member. DELETE /organization/members/{id} in the Organization group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Member removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /organization/invitations:
    get:
      tags: [Organization]
      summary: List invitations
      operationId: listOrganizationInvitations
      description: "List invitations. GET /organization/invitations in the Organization group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        '200':
          description: List of pending invitations
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /organization/invitations/{id}:
    get:
      tags: [Organization]
      summary: Get invitation
      operationId: getOrganizationInvitation
      description: "Get invitation. GET /organization/invitations/{id} in the Organization group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Invitation details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Organization]
      summary: Delete invitation
      operationId: deleteOrganizationInvitation
      description: "Delete invitation. DELETE /organization/invitations/{id} in the Organization group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Invitation deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /organization/invitations/{id}/resend:
    post:
      tags: [Organization]
      summary: Resend invitation
      operationId: resendOrganizationInvitation
      description: "Resend invitation. POST /organization/invitations/{id}/resend in the Organization group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Invitation resent
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /organization/invitations/{id}/revoke:
    post:
      tags: [Organization]
      summary: Revoke invitation
      operationId: revokeOrganizationInvitation
      description: "Revoke invitation. POST /organization/invitations/{id}/revoke in the Organization group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Invitation revoked
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  # ── Webhooks ────────────────────────────────────────────
  /webhooks:
    get:
      tags: [Webhooks]
      summary: List webhooks
      operationId: listWebhooks
      description: "List webhooks. GET /webhooks in the Webhooks group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        '200':
          description: List of webhooks
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        url:
                          type: string
                          format: uri
                        events:
                          type: array
                          items:
                            type: string
                        active:
                          type: boolean
                        created_at:
                          type: string
                          format: date-time
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Webhooks]
      summary: Create webhook
      operationId: createWebhook
      description: "Create webhook. POST /webhooks in the Webhooks group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url, events]
              properties:
                url:
                  type: string
                  format: uri
                events:
                  type: array
                  items:
                    type: string
                  description: "Events to subscribe to (e.g. client.created, invoice.paid)"
                secret:
                  type: string
                  description: Shared secret for HMAC-SHA256 signature verification
            example:
              url: "https://example.com/webhooks"
              events: ["client.created", "invoice.paid"]
      responses:
        '200':
          description: Webhook created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /webhooks/verify:
    post:
      tags: [Webhooks]
      summary: Verify webhook signature
      operationId: verifyWebhookSignature
      description: Utility endpoint to verify a webhook payload's HMAC signature.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [payload, signature, secret]
              properties:
                payload:
                  type: string
                signature:
                  type: string
                secret:
                  type: string
      responses:
        '200':
          description: Verification result
          content:
            application/json:
              schema:
                type: object
                properties:
                  valid:
                    type: boolean
        '400':
          $ref: '#/components/responses/BadRequest'
        '422':
          $ref: '#/components/responses/ValidationError'
        '500':
          $ref: '#/components/responses/InternalError'

  /webhooks/{id}:
    get:
      tags: [Webhooks]
      summary: Get webhook
      operationId: getWebhook
      description: "Get webhook. GET /webhooks/{id} in the Webhooks group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Webhook details
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    patch:
      tags: [Webhooks]
      summary: Update webhook
      operationId: updateWebhook
      description: "Update webhook. PATCH /webhooks/{id} in the Webhooks group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                url:
                  type: string
                  format: uri
                events:
                  type: array
                  items:
                    type: string
                active:
                  type: boolean
      responses:
        '200':
          description: Webhook updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Webhooks]
      summary: Delete webhook
      operationId: deleteWebhook
      description: "Delete webhook. DELETE /webhooks/{id} in the Webhooks group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Webhook deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /webhooks/{id}/test:
    post:
      tags: [Webhooks]
      summary: Test webhook
      operationId: testWebhook
      description: Send a test payload to the webhook URL.
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Test result
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      success:
                        type: boolean
                      status_code:
                        type: integer
                      response_time_ms:
                        type: integer
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /webhooks/{id}/deliveries:
    get:
      tags: [Webhooks]
      summary: List deliveries
      operationId: listWebhookDeliveries
      description: "List deliveries. GET /webhooks/{id}/deliveries in the Webhooks group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
      responses:
        '200':
          description: List of deliveries
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /webhooks/{id}/deliveries/{deliveryId}/retry:
    post:
      tags: [Webhooks]
      summary: Retry delivery
      operationId: retryWebhookDelivery
      description: "Retry delivery. POST /webhooks/{id}/deliveries/{deliveryId}/retry in the Webhooks group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: deliveryId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Delivery retried
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  # ── Reports ─────────────────────────────────────────────
  /reports/financial:
    get:
      tags: [Reports]
      summary: Financial report
      operationId: getFinancialReport
      description: "Financial report. GET /reports/financial in the Reports group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: start_date
          in: query
          schema:
            type: string
            format: date
        - name: end_date
          in: query
          schema:
            type: string
            format: date
      responses:
        '200':
          description: Financial report data
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /reports/projects:
    get:
      tags: [Reports]
      summary: Projects report
      operationId: getProjectsReport
      description: "Projects report. GET /reports/projects in the Reports group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: start_date
          in: query
          schema:
            type: string
            format: date
        - name: end_date
          in: query
          schema:
            type: string
            format: date
      responses:
        '200':
          description: Projects report data
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /reports/time:
    get:
      tags: [Reports]
      summary: Time report
      operationId: getTimeReport
      description: "Time report. GET /reports/time in the Reports group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: start_date
          in: query
          schema:
            type: string
            format: date
        - name: end_date
          in: query
          schema:
            type: string
            format: date
        - name: project_id
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Time report data
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /reports/leads:
    get:
      tags: [Reports]
      summary: Leads report
      operationId: getLeadsReport
      description: "Leads report. GET /reports/leads in the Reports group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: start_date
          in: query
          schema:
            type: string
            format: date
        - name: end_date
          in: query
          schema:
            type: string
            format: date
      responses:
        '200':
          description: Leads report data
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  # ── User ────────────────────────────────────────────────
  /user/notifications:
    get:
      tags: [User]
      summary: List notifications
      operationId: listUserNotifications
      description: "List notifications. GET /user/notifications in the User group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PerPageParam'
        - name: unread
          in: query
          schema:
            type: boolean
      responses:
        '200':
          description: List of notifications
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /user/notifications/{id}:
    patch:
      tags: [User]
      summary: Mark notification read
      operationId: updateUserNotification
      description: "Mark notification read. PATCH /user/notifications/{id} in the User group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                read:
                  type: boolean
      responses:
        '200':
          description: Notification updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  /user/preferences:
    get:
      tags: [User]
      summary: Get preferences
      operationId: getUserPreferences
      description: "Get preferences. GET /user/preferences in the User group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        '200':
          description: User preferences
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    patch:
      tags: [User]
      summary: Update preferences
      operationId: updateUserPreferences
      description: "Update preferences. PATCH /user/preferences in the User group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                theme:
                  type: string
                  enum: [light, dark, system]
                language:
                  type: string
                timezone:
                  type: string
                notifications:
                  type: object
      responses:
        '200':
          description: Preferences updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'

  # ── Favorites ───────────────────────────────────────────
  /favorites:
    get:
      tags: [Favorites]
      summary: List favorites
      operationId: listFavorites
      description: "List favorites. GET /favorites in the Favorites group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      responses:
        '200':
          description: List of favorites
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags: [Favorites]
      summary: Add favorite
      operationId: addFavorite
      description: "Add favorite. POST /favorites in the Favorites group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [type, id]
              properties:
                type:
                  type: string
                  description: "Entity type (e.g. client, contact, project)"
                id:
                  type: string
      responses:
        '200':
          description: Favorite added
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags: [Favorites]
      summary: Remove favorite
      operationId: removeFavorite
      description: "Remove favorite. DELETE /favorites in the Favorites group of the HireOrSell API. Authenticate with an X-API-Key header or an Authorization Bearer token."
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [type, id]
              properties:
                type:
                  type: string
                id:
                  type: string
      responses:
        '200':
          description: Favorite removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                  meta:
                    $ref: '#/components/schemas/Meta'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
