> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ontora.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Send Test Campaign Invitation

> Send the campaign invitation to the author as a disposable test run.

Unlike the in-builder agent preview (which jumps straight into a session),
this exercises the real participant flow end to end: the author receives
the actual invitation email, opens the booking page, and takes the
interview. The minted contact carries the ``TEST_LAB_DEPARTMENT`` marker,
so the run is excluded from participant lists, stats, reports, and
insights by construction, is skipped by the completion pipeline, and is
never picked up by the real invitation send.

Works on drafts (the review page finalizes the draft before offering the
button) and always sends the email invitation, regardless of the
campaign's delivery channel — Slack/Teams delivery falls back to email
for real participants too, so the rendered invitation is authentic.

Runs as a sequence of short transactions, each touching only one row
class, on purpose: booking requests lock contact→conversation while a
running session's save locks conversation→contact, so any transaction
here that held both would deadlock against one of them. This also keeps
the outbound email call outside every conversation lock, and defers the
disposal of the previous test run until the new invitation actually went
out — a failed send must not destroy anything. A crash between the send
and the disposal leaves the old run visible ("already completed" on the
emailed link); the next test-send click repairs that state (after the
send-claim cooldown of up to _TEST_SEND_COOLDOWN_SECONDS, since the
crashed request already claimed its send).



## OpenAPI

````yaml /openapi.json post /v1/interviews/{interview_id}/test-send
openapi: 3.1.0
info:
  title: Ontora API
  description: >-
    Ontora's public API. Covers interview campaigns, transcripts, synthesis
    reports, graph queries, webhooks, and workspace management. 


    **Authentication**: endpoints accept either a Clerk JWT (`Authorization:
    Bearer <token>`) or a workspace API key (`X-API-Key: ont_...`). Workspace
    keys are managed via `/v1/api-keys`.
  version: 0.1.0
servers: []
security:
  - ClerkJWT: []
  - WorkspaceApiKey: []
tags:
  - name: interviews
    description: Campaign CRUD, lifecycle, contacts, question sets.
  - name: campaign-insights
    description: Synthesis outputs — cartography, roadmap, personas, conversations.
  - name: interview-query
    description: Graph RAG query over campaign data.
  - name: campaign-reports
    description: Versioned synthesis Reports — manual regenerate, version history.
  - name: interview-export
    description: Markdown / ZIP exports of transcripts and reports.
  - name: api-keys
    description: Manage workspace-scoped API keys for programmatic access.
  - name: webhook-endpoints
    description: Outbound webhook endpoints for automation.
  - name: booking
    description: Public self-scheduling endpoints for interview participants.
  - name: join
    description: Public QR-code / share-link self-registration for campaign participants.
  - name: workspaces
    description: Workspace and membership management.
  - name: vaults
    description: Context vault management.
paths:
  /v1/interviews/{interview_id}/test-send:
    post:
      tags:
        - interviews
      summary: Send Test Campaign Invitation
      description: >-
        Send the campaign invitation to the author as a disposable test run.


        Unlike the in-builder agent preview (which jumps straight into a
        session),

        this exercises the real participant flow end to end: the author receives

        the actual invitation email, opens the booking page, and takes the

        interview. The minted contact carries the ``TEST_LAB_DEPARTMENT``
        marker,

        so the run is excluded from participant lists, stats, reports, and

        insights by construction, is skipped by the completion pipeline, and is

        never picked up by the real invitation send.


        Works on drafts (the review page finalizes the draft before offering the

        button) and always sends the email invitation, regardless of the

        campaign's delivery channel — Slack/Teams delivery falls back to email

        for real participants too, so the rendered invitation is authentic.


        Runs as a sequence of short transactions, each touching only one row

        class, on purpose: booking requests lock contact→conversation while a

        running session's save locks conversation→contact, so any transaction

        here that held both would deadlock against one of them. This also keeps

        the outbound email call outside every conversation lock, and defers the

        disposal of the previous test run until the new invitation actually went

        out — a failed send must not destroy anything. A crash between the send

        and the disposal leaves the old run visible ("already completed" on the

        emailed link); the next test-send click repairs that state (after the

        send-claim cooldown of up to _TEST_SEND_COOLDOWN_SECONDS, since the

        crashed request already claimed its send).
      operationId: >-
        send_test_campaign_invitation_v1_interviews__interview_id__test_send_post
      parameters:
        - name: interview_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
            title: Interview Id
        - name: X-API-Key
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: 'null'
            title: X-Api-Key
      requestBody:
        content:
          application/json:
            schema:
              anyOf:
                - $ref: '#/components/schemas/TestSendRequest'
                - type: 'null'
              title: Payload
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TestSendResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      security:
        - HTTPBearer: []
components:
  schemas:
    TestSendRequest:
      properties:
        display_name:
          anyOf:
            - type: string
              maxLength: 255
            - type: 'null'
          title: Display Name
        email:
          anyOf:
            - type: string
              maxLength: 255
            - type: 'null'
          title: Email
      type: object
      title: TestSendRequest
    TestSendResponse:
      properties:
        sent_to:
          type: string
          title: Sent To
        contact_id:
          type: string
          format: uuid
          title: Contact Id
      type: object
      required:
        - sent_to
        - contact_id
      title: TestSendResponse
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
        input:
          title: Input
        ctx:
          type: object
          title: Context
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError
  securitySchemes:
    ClerkJWT:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Clerk-issued RS256 JWT.
    WorkspaceApiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: 'Workspace-scoped API key (prefix: `ont_`).'
    HTTPBearer:
      type: http
      scheme: bearer

````