openapi: 3.1.0
info:
  title: MyCulture Client API
  version: 1.0.0-preview.2
  summary: Integrate MyCulture assessments into any hiring workflow.
  description: |
    The provider-neutral MyCulture Client API lets an authorized organization
    discover assessment templates, invite candidates individually or in a batch,
    reconcile progress, retrieve versioned structured results, and receive
    retryable signed lifecycle webhooks.

    Assessment completion and result readiness are separate. A candidate can
    finish an assessment while AI-assisted modules are still processing. Poll
    the invitation `result_status` or consume `assessment.result_ready` before
    retrieving results.

    This contract does not expose MyCulture database records or administrative
    endpoints. The Greenhouse Assessment Partner API remains available as a
    compatibility adapter and is not part of this specification.
  contact:
    name: MyCulture integration support
    email: hello@myculture.ai
    url: https://www.myculture.ai/contact-us
  license:
    name: Proprietary
    url: https://www.myculture.ai/terms-and-conditions
servers:
  - url: https://api.myculture.ai/api/v1
    description: Production
tags:
  - name: API
    description: API capability and version information.
  - name: Assessment templates
    description: Assessments available to the authenticated organization.
  - name: Invitations
    description: Candidate invitations and completion state.
  - name: Results
    description: Stable result envelopes for completed assessments.
  - name: Webhook endpoints
    description: Signed server-to-server lifecycle notifications.
  - name: Webhook deliveries
    description: Test, inspect, replay, and troubleshoot notification delivery.
security:
  - BearerApiKey: []
  - HeaderApiKey: []
paths:
  /:
    get:
      tags: [API]
      operationId: getApiInformation
      summary: Get API information
      responses:
        "200":
          description: API version and credential environment.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiInformation"
        "401":
          $ref: "#/components/responses/Unauthorized"
  /assessment-templates:
    get:
      tags: [Assessment templates]
      operationId: listAssessmentTemplates
      summary: List assessment templates
      description: Returns active templates belonging to the authenticated organization.
      parameters:
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/PageToken"
      responses:
        "200":
          description: A page of assessment templates ordered by name.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AssessmentTemplateList"
              example:
                data:
                  - id: tpl_culture_alignment
                    name: Culture Alignment
                    description: Understand alignment with the team's working values.
                    sections: [values_and_traits, acceptable_behaviors]
                    status: active
                    updated_at: "2026-08-01T09:15:00.000Z"
                pagination:
                  next_page_token: null
                  has_more: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
  /assessment-templates/{templateId}:
    parameters:
      - $ref: "#/components/parameters/TemplateId"
    get:
      tags: [Assessment templates]
      operationId: getAssessmentTemplate
      summary: Get template capabilities
      description: Describes the selected modules and the result shape each module produces.
      responses:
        "200":
          description: Template and versioned module capabilities.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AssessmentTemplateDetail"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
  /invitations:
    get:
      tags: [Invitations]
      operationId: listInvitations
      summary: List and reconcile invitations
      parameters:
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/PageToken"
        - name: status
          in: query
          schema:
            $ref: "#/components/schemas/AssessmentStatus"
        - name: client_reference
          in: query
          schema:
            type: string
        - name: candidate_external_id
          in: query
          schema:
            type: string
      responses:
        "200":
          description: A newest-first page of invitations.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvitationList"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
    post:
      tags: [Invitations]
      operationId: createInvitation
      summary: Invite a candidate
      description: |
        Creates a unique candidate assessment and optionally sends the invitation
        email. Repeat the same request with the same `Idempotency-Key` to receive
        the original response without creating another invitation.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateInvitationRequest"
            example:
              assessment_template_id: tpl_culture_alignment
              candidate:
                first_name: Jane
                last_name: Smith
                email: jane.smith@example.com
                external_id: candidate_4821
              client_reference: application_9017
              send_email: true
              metadata:
                job_id: product-lead
      responses:
        "201":
          description: Invitation created.
          headers:
            Location:
              description: Relative URL of the invitation resource.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Invitation"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "422":
          $ref: "#/components/responses/ValidationError"
        "429":
          $ref: "#/components/responses/RateLimited"
  /invitation-batches:
    post:
      tags: [Invitations]
      operationId: createInvitationBatch
      summary: Invite up to 100 candidates
      description: Each candidate is independently idempotent and can succeed or fail without rolling back the others.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateInvitationBatchRequest"
      responses:
        "201":
          description: Batch processed, possibly with per-candidate errors.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvitationBatch"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "422":
          $ref: "#/components/responses/ValidationError"
        "429":
          $ref: "#/components/responses/RateLimited"
  /invitations/{invitationId}:
    parameters:
      - $ref: "#/components/parameters/InvitationId"
    get:
      tags: [Invitations]
      operationId: getInvitation
      summary: Get invitation status
      responses:
        "200":
          description: Current invitation and assessment status.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvitationStatus"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      tags: [Invitations]
      operationId: updateInvitation
      summary: Cancel or expire an invitation
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [status]
              properties:
                status:
                  type: string
                  enum: [cancelled, expired]
      responses:
        "200":
          description: Updated invitation.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvitationStatus"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "422":
          $ref: "#/components/responses/ValidationError"
  /invitations/{invitationId}/email-deliveries:
    parameters:
      - $ref: "#/components/parameters/InvitationId"
    post:
      tags: [Invitations]
      operationId: resendInvitationEmail
      summary: Send the invitation email again
      responses:
        "202":
          description: Email was queued or the queue attempt failed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvitationStatus"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
  /assessment-results:
    get:
      tags: [Results]
      operationId: listAssessmentResults
      summary: List ready results
      description: Use this endpoint for reconciliation or incremental synchronization after missed webhooks.
      parameters:
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/PageToken"
        - name: client_reference
          in: query
          schema:
            type: string
        - name: candidate_external_id
          in: query
          schema:
            type: string
      responses:
        "200":
          description: A newest-first page of ready result snapshots.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AssessmentResultList"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
  /assessment-results/{assessmentId}:
    parameters:
      - $ref: "#/components/parameters/AssessmentId"
    get:
      tags: [Results]
      operationId: getAssessmentResult
      summary: Get a completed assessment result
      description: |
        Returns a versioned, module-aware result snapshot after result processing
        completes. Raw candidate answers are never returned.
      responses:
        "200":
          description: Structured result and report URL.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AssessmentResult"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          description: Assessment is incomplete or its results are still processing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /webhook-endpoints:
    get:
      tags: [Webhook endpoints]
      operationId: listWebhookEndpoints
      summary: List webhook endpoints
      responses:
        "200":
          description: Webhook endpoints belonging to the organization.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/WebhookEndpoint"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
    post:
      tags: [Webhook endpoints]
      operationId: createWebhookEndpoint
      summary: Create a webhook endpoint
      description: |
        The signing secret is returned once in the creation response. An
        organization can have up to 20 active endpoints.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateWebhookEndpointRequest"
      responses:
        "201":
          description: Webhook endpoint created.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/WebhookEndpoint"
                  - type: object
                    required: [signing_secret]
                    properties:
                      signing_secret:
                        type: string
                        writeOnly: true
                        description: Store securely; it cannot be retrieved later.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
        "422":
          $ref: "#/components/responses/ValidationError"
  /webhook-endpoints/{endpointId}:
    parameters:
      - $ref: "#/components/parameters/EndpointId"
    patch:
      tags: [Webhook endpoints]
      operationId: updateWebhookEndpoint
      summary: Update a webhook endpoint
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              minProperties: 1
              properties:
                url:
                  type: string
                  format: uri
                description:
                  type: [string, "null"]
                  maxLength: 250
                status:
                  type: string
                  enum: [active, disabled]
                events:
                  type: array
                  minItems: 1
                  uniqueItems: true
                  items:
                    $ref: "#/components/schemas/WebhookEventType"
      responses:
        "200":
          description: Updated endpoint.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpoint"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
  /webhook-endpoints/{endpointId}/secret-rotations:
    parameters:
      - $ref: "#/components/parameters/EndpointId"
    post:
      tags: [Webhook endpoints]
      operationId: rotateWebhookSecret
      summary: Rotate an endpoint signing secret
      description: The new secret is returned once. Existing signatures stop using the old secret immediately.
      responses:
        "201":
          description: Secret rotated.
          content:
            application/json:
              schema:
                type: object
                required: [id, signing_secret]
                properties:
                  id:
                    type: string
                  signing_secret:
                    type: string
                    writeOnly: true
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
  /webhook-endpoints/{endpointId}/test-deliveries:
    parameters:
      - $ref: "#/components/parameters/EndpointId"
    post:
      tags: [Webhook endpoints, Webhook deliveries]
      operationId: createWebhookTestDelivery
      summary: Queue a signed test event
      responses:
        "202":
          description: Test delivery queued.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookDelivery"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
  /webhook-deliveries:
    get:
      tags: [Webhook deliveries]
      operationId: listWebhookDeliveries
      summary: Inspect delivery attempts
      parameters:
        - $ref: "#/components/parameters/Limit"
        - name: endpoint_id
          in: query
          schema:
            type: string
        - name: status
          in: query
          schema:
            type: string
            enum: [pending, retry, processing, delivered, failed]
      responses:
        "200":
          description: Recent deliveries and their retry state.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/WebhookDelivery"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
  /webhook-deliveries/{deliveryId}/replays:
    parameters:
      - $ref: "#/components/parameters/DeliveryId"
    post:
      tags: [Webhook deliveries]
      operationId: replayWebhookDelivery
      summary: Replay a webhook delivery
      responses:
        "202":
          description: Delivery queued again with its attempt counter reset.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookDelivery"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/ValidationError"
    delete:
      tags: [Webhook endpoints]
      operationId: deleteWebhookEndpoint
      summary: Delete a webhook endpoint
      responses:
        "204":
          description: Endpoint deleted.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
components:
  securitySchemes:
    BearerApiKey:
      type: http
      scheme: bearer
      bearerFormat: MyCulture API key
      description: "Send `Authorization: Bearer mc_live_...`."
    HeaderApiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: Alternative to the Bearer authorization header.
  parameters:
    Limit:
      name: limit
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 25
    PageToken:
      name: page_token
      in: query
      schema:
        type: string
      description: Opaque token returned by the previous page.
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: Unique key for safely retrying this invitation request for 24 hours.
      schema:
        type: string
        minLength: 1
        maxLength: 128
    InvitationId:
      name: invitationId
      in: path
      required: true
      schema:
        type: string
    TemplateId:
      name: templateId
      in: path
      required: true
      schema:
        type: string
    AssessmentId:
      name: assessmentId
      in: path
      required: true
      schema:
        type: string
    EndpointId:
      name: endpointId
      in: path
      required: true
      schema:
        type: string
    DeliveryId:
      name: deliveryId
      in: path
      required: true
      schema:
        type: string
  schemas:
    ApiInformation:
      type: object
      required: [name, version, environment, documentation]
      properties:
        name:
          type: string
        version:
          type: string
        environment:
          type: string
          enum: [live, test]
        documentation:
          type: string
          format: uri
    AssessmentTemplate:
      type: object
      required: [id, name, sections, status]
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: [string, "null"]
        sections:
          type: array
          items:
            type: string
        status:
          type: string
          const: active
        updated_at:
          type: [string, "null"]
          format: date-time
    AssessmentTemplateDetail:
      type: object
      required: [id, name, status, schema_version, sections]
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: [string, "null"]
        status:
          type: string
          const: active
        schema_version:
          type: string
        sections:
          type: array
          items:
            type: object
            required: [key, score_kind]
            properties:
              key:
                type: string
              score_kind:
                type: string
                enum: [alignment, ability, readiness, trait, categorical, none, module_specific]
              returns_dimensions:
                type: boolean
              returns_benchmark:
                type: boolean
              returns_profile:
                type: boolean
              supports_partial:
                type: boolean
              raw_answers_included:
                type: boolean
        updated_at:
          type: [string, "null"]
          format: date-time
    AssessmentTemplateList:
      type: object
      required: [data, pagination]
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/AssessmentTemplate"
        pagination:
          $ref: "#/components/schemas/Pagination"
    Pagination:
      type: object
      required: [next_page_token, has_more]
      properties:
        next_page_token:
          type: [string, "null"]
        has_more:
          type: boolean
    Candidate:
      type: object
      required: [first_name, last_name, email]
      additionalProperties: false
      properties:
        first_name:
          type: string
          maxLength: 100
        last_name:
          type: string
          maxLength: 100
        email:
          type: string
          format: email
          maxLength: 254
        external_id:
          type: string
          maxLength: 256
    CreateInvitationRequest:
      type: object
      required: [assessment_template_id, candidate]
      additionalProperties: false
      properties:
        assessment_template_id:
          type: string
        candidate:
          $ref: "#/components/schemas/Candidate"
        send_email:
          type: boolean
          default: true
        client_reference:
          type: string
          maxLength: 256
        metadata:
          type: object
          maxProperties: 25
          additionalProperties:
            type: [string, number, boolean, "null"]
    CreateInvitationBatchRequest:
      type: object
      additionalProperties: false
      required: [assessment_template_id, candidates]
      properties:
        assessment_template_id:
          type: string
        candidates:
          type: array
          minItems: 1
          maxItems: 100
          items:
            type: object
            additionalProperties: false
            required: [candidate]
            properties:
              candidate:
                $ref: "#/components/schemas/Candidate"
              send_email:
                type: boolean
                default: true
              client_reference:
                type: string
                maxLength: 256
              metadata:
                type: object
                maxProperties: 25
                additionalProperties:
                  type: [string, number, boolean, "null"]
    Invitation:
      type: object
      required: [id, assessment_id, assessment_template_id, status, assessment_url, email_status, created_at]
      properties:
        id:
          type: string
        assessment_id:
          type: string
        assessment_template_id:
          type: string
        status:
          $ref: "#/components/schemas/AssessmentStatus"
        result_status:
          $ref: "#/components/schemas/ResultStatus"
        assessment_url:
          type: string
          format: uri
        email_status:
          type: string
          enum: [queued, not_requested, failed]
          description: Whether MyCulture queued the invitation email.
        client_reference:
          type: [string, "null"]
        created_at:
          type: string
          format: date-time
    InvitationStatus:
      allOf:
        - $ref: "#/components/schemas/Invitation"
        - type: object
          properties:
            started_at:
              type: [string, "null"]
              format: date-time
            completed_at:
              type: [string, "null"]
              format: date-time
            updated_at:
              type: [string, "null"]
              format: date-time
            candidate:
              type: object
              properties:
                external_id:
                  type: [string, "null"]
                email:
                  type: [string, "null"]
                  format: email
                first_name:
                  type: [string, "null"]
                last_name:
                  type: [string, "null"]
            metadata:
              type: object
              additionalProperties: true
    InvitationList:
      type: object
      required: [data, pagination]
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/InvitationStatus"
        pagination:
          $ref: "#/components/schemas/Pagination"
    InvitationBatch:
      type: object
      required: [id, status, requested, succeeded, failed, data]
      properties:
        id:
          type: string
        status:
          type: string
          enum: [completed, completed_with_errors]
        requested:
          type: integer
        succeeded:
          type: integer
        failed:
          type: integer
        data:
          type: array
          items:
            oneOf:
              - $ref: "#/components/schemas/Invitation"
              - type: object
                required: [error]
                properties:
                  candidate_external_id:
                    type: [string, "null"]
                  client_reference:
                    type: [string, "null"]
                  error:
                    type: object
                    required: [code, message]
                    properties:
                      code:
                        type: string
                      message:
                        type: string
    AssessmentStatus:
      type: string
      enum: [invited, in_progress, completed, expired, cancelled, deleted]
    ResultStatus:
      type: [string, "null"]
      enum: [waiting_for_completion, processing, ready, failed, null]
    AssessmentResult:
      type: object
      required:
        - assessment_id
        - assessment_template_id
        - invitation_id
        - client_reference
        - candidate_external_id
        - status
        - schema_version
        - overall_score
        - sections
        - started_at
        - completed_at
        - ready_at
      properties:
        assessment_id:
          type: string
        assessment_template_id:
          type: [string, "null"]
        invitation_id:
          type: [string, "null"]
        client_reference:
          type: [string, "null"]
        candidate_external_id:
          type: [string, "null"]
        status:
          type: string
          const: ready
        schema_version:
          type: string
        overall_score:
          oneOf:
            - $ref: "#/components/schemas/ResultScore"
            - type: "null"
        sections:
          type: array
          items:
            $ref: "#/components/schemas/ResultSection"
        report_url:
          type: [string, "null"]
          format: uri
        started_at:
          type: [string, "null"]
          format: date-time
        completed_at:
          type: [string, "null"]
          format: date-time
        ready_at:
          type: string
          format: date-time
    AssessmentResultList:
      type: object
      required: [data, pagination]
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/AssessmentResult"
        pagination:
          $ref: "#/components/schemas/Pagination"
    ResultScore:
      type: object
      required: [value, scale, kind]
      properties:
        value:
          type: number
          minimum: 0
          maximum: 100
        scale:
          type: string
          const: percent
        kind:
          type: string
          enum: [alignment, ability, readiness, trait, composite]
    ResultSection:
      type: object
      required: [key, type, schema_version, status, score, dimensions, narrative, data]
      properties:
        key:
          type: string
        type:
          type: string
        schema_version:
          type: string
        status:
          type: string
          enum: [complete, partial]
        score:
          oneOf:
            - $ref: "#/components/schemas/ResultScore"
            - type: "null"
        dimensions:
          type: array
          items:
            $ref: "#/components/schemas/ResultDimension"
        narrative:
          type: [string, "null"]
        data:
          type: object
          additionalProperties: true
    ResultDimension:
      type: object
      required: [key, name, score]
      properties:
        key:
          type: string
        name:
          type: string
        score:
          oneOf:
            - $ref: "#/components/schemas/ResultScore"
            - type: "null"
        candidate_value:
          type: [number, "null"]
        benchmark_value:
          type: [number, "null"]
    CreateWebhookEndpointRequest:
      type: object
      required: [url]
      additionalProperties: false
      properties:
        url:
          type: string
          format: uri
          description: Public HTTPS destination. Private network addresses are rejected.
        description:
          type: string
          maxLength: 250
        events:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            $ref: "#/components/schemas/WebhookEventType"
          default: [assessment.result_ready]
    WebhookEndpoint:
      type: object
      required: [id, url, description, events, status, created_at, updated_at]
      properties:
        id:
          type: string
        url:
          type: string
          format: uri
        description:
          type: [string, "null"]
        events:
          type: array
          items:
            $ref: "#/components/schemas/WebhookEventType"
        status:
          type: string
          enum: [active, disabled]
        created_at:
          type: [string, "null"]
          format: date-time
        updated_at:
          type: [string, "null"]
          format: date-time
    WebhookEventType:
      type: string
      enum:
        - invitation.created
        - invitation.email_queued
        - invitation.email_failed
        - invitation.cancelled
        - invitation.expired
        - assessment.started
        - assessment.completed
        - assessment.result_ready
    WebhookDelivery:
      type: object
      required: [id, endpoint_id, event_id, event_type, status, attempts]
      properties:
        id:
          type: string
        endpoint_id:
          type: [string, "null"]
        event_id:
          type: [string, "null"]
        event_type:
          oneOf:
            - $ref: "#/components/schemas/WebhookEventType"
            - type: "null"
        status:
          type: [string, "null"]
          enum: [pending, retry, processing, delivered, failed, null]
        attempts:
          type: integer
          minimum: 0
        response_status:
          type: [integer, "null"]
        last_error:
          type: [string, "null"]
        next_attempt_at:
          type: [string, "null"]
          format: date-time
        delivered_at:
          type: [string, "null"]
          format: date-time
        failed_at:
          type: [string, "null"]
          format: date-time
        created_at:
          type: [string, "null"]
          format: date-time
        updated_at:
          type: [string, "null"]
          format: date-time
    ErrorResponse:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message, request_id]
          properties:
            code:
              type: string
            message:
              type: string
            details:
              type: array
              items:
                type: object
                required: [message]
                properties:
                  field:
                    type: string
                  message:
                    type: string
            request_id:
              type: string
  responses:
    BadRequest:
      description: Request parameter is invalid.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    Unauthorized:
      description: API key is missing, invalid, expired, or inactive.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    Forbidden:
      description: The credential does not have the required scope.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    NotFound:
      description: Resource was not found within the authenticated organization.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    Conflict:
      description: Request conflicts with a resource or idempotent request in progress.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    ValidationError:
      description: JSON body failed validation.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    RateLimited:
      description: Too many requests.
      headers:
        Retry-After:
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
