openapi: 3.0.3
info:
  title: VerifyRef API
  version: "2025-05-19"
  description: |
    REST API for AI agents and ATS integrations.
    Primary agent job: find and rank recruits via the talent pool, then run reference checks.
    Hosted MCP: https://verifyref.com/api/mcp
    Docs: https://verifyref.com/docs/api · https://verifyref.com/docs/agents · https://verifyref.com/llms.txt

    Exit interviews and voice reference completion are dashboard-only (not in this API).
servers:
  - url: https://verifyref.com/api/v1
    description: Production
tags:
  - name: Account
  - name: Checks
  - name: Talent pool
  - name: Jobs
  - name: Templates
  - name: Credits
  - name: Video interviews
  - name: Surveys
  - name: Webhooks
paths:
  /account:
    get:
      tags: [Account]
      summary: Team account and credits
      security: [{ bearerAuth: [] }]
      responses:
        "200":
          description: Account details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccountResponse"
              example:
                data:
                  team: Acme Corp
                  credits: 150
  /checks:
    get:
      tags: [Checks]
      summary: List reference checks
      security: [{ bearerAuth: [] }]
      parameters:
        - name: externalId
          in: query
          schema: { type: string }
        - name: externalSource
          in: query
          schema:
            type: string
            enum: [bullhorn, zoho_recruit, sap_successfactors, workday, greenhouse, lever, other]
        - name: status
          in: query
          schema:
            type: string
            enum: [AWAITING_CONSENT, SENT, IN_PROGRESS, COMPLETED, EXPIRED]
        - name: jobId
          in: query
          schema: { type: string }
        - name: createdAfter
          in: query
          schema: { type: string, format: date-time }
        - name: page
          in: query
          schema: { type: integer, default: 1 }
        - name: limit
          in: query
          schema: { type: integer, default: 20, maximum: 100 }
      responses:
        "200":
          description: Paginated checks
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaginatedChecksResponse"
    post:
      tags: [Checks]
      summary: Create a reference check
      security: [{ bearerAuth: [] }]
      parameters:
        - name: Idempotency-Key
          in: header
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateCheckRequest"
      responses:
        "201":
          description: Check created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateCheckResponse"
        "402":
          description: Insufficient credits
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "409":
          description: Duplicate externalId
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /checks/{id}:
    get:
      tags: [Checks]
      summary: Get check with references and responses
      security: [{ bearerAuth: [] }]
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Check detail including dashboardUrl and reportUrl
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CheckDetailResponse"
    patch:
      tags: [Checks]
      summary: Update check metadata or cancel
      security: [{ bearerAuth: [] }]
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PatchCheckRequest"
      responses:
        "200":
          description: Updated check
  /jobs:
    get:
      tags: [Jobs]
      summary: List jobs
      security: [{ bearerAuth: [] }]
      responses:
        "200":
          description: Job list
    post:
      tags: [Jobs]
      summary: Create a job
      security: [{ bearerAuth: [] }]
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateJobRequest"
      responses:
        "201":
          description: Job created
  /templates:
    get:
      tags: [Templates]
      summary: List questionnaire templates
      security: [{ bearerAuth: [] }]
      responses:
        "200":
          description: Template list
  /talent-pool/candidates:
    get:
      tags: [Talent pool]
      summary: List talent pool candidates
      security: [{ bearerAuth: [] }]
      parameters:
        - name: search
          in: query
          schema: { type: string }
        - name: location
          in: query
          schema: { type: string }
        - name: skills
          in: query
          schema: { type: array, items: { type: string } }
        - name: certifications
          in: query
          schema: { type: array, items: { type: string } }
      responses:
        "200":
          description: Candidates
    post:
      tags: [Talent pool]
      summary: Upload resume and add candidate (multipart, free)
      security: [{ bearerAuth: [] }]
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              required: [resume]
              properties:
                resume:
                  type: string
                  format: binary
      responses:
        "201":
          description: Candidate created or updated
  /talent-pool/candidates/{id}:
    get:
      tags: [Talent pool]
      summary: Get candidate
      security: [{ bearerAuth: [] }]
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Candidate detail
    patch:
      tags: [Talent pool]
      summary: Update candidate
      security: [{ bearerAuth: [] }]
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PatchCandidateRequest"
      responses:
        "200":
          description: Updated
    delete:
      tags: [Talent pool]
      summary: Remove candidate
      security: [{ bearerAuth: [] }]
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Removed
  /talent-pool/candidates/{id}/summary:
    post:
      tags: [Talent pool]
      summary: Regenerate AI candidate summary
      security: [{ bearerAuth: [] }]
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Updated candidate
  /talent-pool/match:
    post:
      tags: [Talent pool]
      summary: AI match candidates to a job description (free)
      security: [{ bearerAuth: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [jobDescription]
              properties:
                jobDescription:
                  type: string
                  maxLength: 20000
            example:
              jobDescription: Senior React engineer with TypeScript…
      responses:
        "200":
          description: Top matches with fit scores
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MatchCandidatesResponse"
  /talent-pool/filters:
    get:
      tags: [Talent pool]
      summary: Unique skills, certifications, locations
      security: [{ bearerAuth: [] }]
      responses:
        "200":
          description: Filter options
  /credits/purchase:
    post:
      tags: [Credits]
      summary: Purchase credit pack (MPP SPT or Checkout URL)
      security: [{ bearerAuth: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [package]
              properties:
                package: { type: string, enum: ["5", "20", "50", "100"] }
                currency: { type: string, enum: [USD, AUD], default: USD }
      responses:
        "200":
          description: Purchase completed or checkoutUrl returned
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PurchaseCreditsResponse"
        "402":
          description: MPP payment required (Shared Payment Token challenge)
  /video-interviews:
    get:
      tags: [Video interviews]
      summary: List AI video interviews
      security: [{ bearerAuth: [] }]
      responses:
        "200":
          description: Interview list
    post:
      tags: [Video interviews]
      summary: Create AI video interview (1 credit)
      security: [{ bearerAuth: [] }]
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateVideoInterviewRequest"
      responses:
        "201":
          description: Interview created
        "402":
          description: Insufficient credits
  /video-interviews/{id}:
    get:
      tags: [Video interviews]
      summary: Get video interview
      security: [{ bearerAuth: [] }]
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Interview detail
  /surveys:
    get:
      tags: [Surveys]
      summary: List employee surveys
      security: [{ bearerAuth: [] }]
      responses:
        "200":
          description: Survey list
    post:
      tags: [Surveys]
      summary: Create employee survey
      security: [{ bearerAuth: [] }]
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateSurveyRequest"
      responses:
        "201":
          description: Survey created
  /surveys/{id}:
    get:
      tags: [Surveys]
      summary: Get survey
      security: [{ bearerAuth: [] }]
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Survey detail
  /webhooks:
    get:
      tags: [Webhooks]
      summary: List webhook endpoints
      security: [{ bearerAuth: [] }]
      responses:
        "200":
          description: Webhooks
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookListResponse"
    post:
      tags: [Webhooks]
      summary: Create webhook endpoint
      description: Requires a prior credit purchase on the team. Secret is returned once.
      security: [{ bearerAuth: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateWebhookRequest"
      responses:
        "201":
          description: Webhook created with secret
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateWebhookResponse"
        "403":
          description: Team has not purchased credits
  /webhooks/{id}:
    patch:
      tags: [Webhooks]
      summary: Update webhook
      security: [{ bearerAuth: [] }]
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PatchWebhookRequest"
      responses:
        "200":
          description: Updated
    delete:
      tags: [Webhooks]
      summary: Delete webhook
      security: [{ bearerAuth: [] }]
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Deleted
  /webhooks/{id}/test:
    post:
      tags: [Webhooks]
      summary: Send test webhook payload
      security: [{ bearerAuth: [] }]
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Test sent
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API key (rc_live_ or rc_test_). Create in Dashboard → Developer settings. No key CRUD API.
  schemas:
    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          properties:
            code: { type: string }
            message: { type: string }
            details: { type: object, additionalProperties: true }
    AccountResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            team: { type: string }
            credits: { type: integer }
    Question:
      type: object
      required: [text, type, order]
      properties:
        text: { type: string }
        type:
          type: string
          enum: [TEXT, TEXTAREA, RATING, MULTIPLE_CHOICE, YES_NO]
        required: { type: boolean, default: true }
        order: { type: number }
        options:
          type: string
          description: Required for MULTIPLE_CHOICE (serialized choices)
    ReferenceInput:
      type: object
      required: [name, email, relationship]
      properties:
        name: { type: string }
        email: { type: string, format: email }
        relationship: { type: string }
    NewJob:
      type: object
      properties:
        title: { type: string }
        description: { type: string }
        status: { type: string, enum: [OPEN, CLOSED], default: OPEN }
        location: { type: string }
        department: { type: string }
    CreateCheckRequest:
      type: object
      required: [candidateName, candidateEmail]
      description: |
        Provide either questionnaireId OR questionnaireTitle + questions[].
        Provide references[] unless candidateProvidedReferences is true.
        externalId and externalSource must be provided together.
      properties:
        candidateName: { type: string }
        candidateEmail: { type: string, format: email }
        candidateId:
          type: string
          description: Talent pool candidate ID
        addToTalentPool: { type: boolean, default: false }
        candidateProvidedReferences: { type: boolean, default: false }
        externalId: { type: string, maxLength: 255 }
        externalSource:
          type: string
          enum: [bullhorn, zoho_recruit, sap_successfactors, workday, greenhouse, lever, other]
        metadata: { type: object, additionalProperties: true }
        jobId: { type: string }
        newJob:
          $ref: "#/components/schemas/NewJob"
        questionnaireId: { type: string }
        questionnaireTitle: { type: string }
        questionnaireDescription: { type: string }
        questions:
          type: array
          items:
            $ref: "#/components/schemas/Question"
        references:
          type: array
          items:
            $ref: "#/components/schemas/ReferenceInput"
    CreateCheckResponse:
      type: object
      properties:
        success: { type: boolean }
        checkId: { type: string }
        status: { type: string }
        jobId: { type: string, nullable: true }
        externalId: { type: string, nullable: true }
        externalSource: { type: string, nullable: true }
        sandbox: { type: boolean }
        idempotentReplay: { type: boolean }
    PatchCheckRequest:
      type: object
      properties:
        metadata: { type: object, additionalProperties: true }
        status: { type: string, enum: [EXPIRED] }
        emailRemindersEnabled: { type: boolean }
    CheckDetailResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            id: { type: string }
            status: { type: string }
            candidate:
              type: object
              properties:
                name: { type: string }
                email: { type: string }
            externalId: { type: string, nullable: true }
            externalSource: { type: string, nullable: true }
            metadata: { type: object, additionalProperties: true }
            dashboardUrl: { type: string, format: uri }
            reportUrl:
              type: string
              format: uri
              description: PDF export; accepts Bearer API key
            references:
              type: array
              items:
                type: object
                properties:
                  name: { type: string }
                  relationship: { type: string }
                  status: { type: string }
                  sentiment: { type: object, additionalProperties: true }
                  redFlags: { type: object, additionalProperties: true }
                  responses:
                    type: array
                    items:
                      type: object
                      properties:
                        question: { type: string }
                        answer: { type: string }
    PaginatedChecksResponse:
      type: object
      properties:
        data:
          type: array
          items: { type: object, additionalProperties: true }
        page: { type: integer }
        limit: { type: integer }
        total: { type: integer }
    CreateJobRequest:
      type: object
      required: [title]
      properties:
        title: { type: string }
        description: { type: string }
        location: { type: string }
        department: { type: string }
        status: { type: string, enum: [OPEN, CLOSED] }
    PatchCandidateRequest:
      type: object
      properties:
        name: { type: string }
        email: { type: string }
        phone: { type: string }
        location: { type: string }
        skills:
          type: array
          items: { type: string }
        certifications:
          type: array
          items: { type: string }
        notes: { type: string, maxLength: 2000 }
        references:
          type: array
          items:
            type: object
            properties:
              name: { type: string }
              email: { type: string, nullable: true }
              phone: { type: string, nullable: true }
              relationship: { type: string, nullable: true }
              company: { type: string, nullable: true }
    MatchCandidatesResponse:
      type: object
      properties:
        matches:
          type: array
          items:
            type: object
            properties:
              candidateId: { type: string }
              fitScore: { type: number }
              fitSummary: { type: string }
              name: { type: string }
    PurchaseCreditsResponse:
      type: object
      properties:
        success: { type: boolean }
        credits: { type: integer }
        checkoutUrl:
          type: string
          format: uri
          description: Present when the user must complete Stripe Checkout
    CreateVideoInterviewRequest:
      type: object
      required: [candidateId, title, questions]
      properties:
        candidateId: { type: string }
        jobId: { type: string }
        title: { type: string, maxLength: 120 }
        description: { type: string, maxLength: 500 }
        questions:
          type: array
          minItems: 1
          maxItems: 12
          items:
            type: object
            required: [text, order]
            properties:
              text: { type: string }
              required: { type: boolean }
              order: { type: integer }
    CreateSurveyRequest:
      type: object
      required: [title, recipients, questions]
      properties:
        title: { type: string }
        description: { type: string }
        anonymous: { type: boolean }
        recipients:
          type: array
          minItems: 1
          maxItems: 500
          items:
            type: object
            required: [email]
            properties:
              email: { type: string }
              name: { type: string }
        questions:
          type: array
          minItems: 1
          maxItems: 50
          items:
            $ref: "#/components/schemas/Question"
    WebhookEvent:
      type: string
      enum:
        - check.created
        - check.completed
        - check.consent_given
        - check.status_changed
        - reference.responded
        - email.bounced
    CreateWebhookRequest:
      type: object
      required: [url, events]
      properties:
        url:
          type: string
          format: uri
          description: Must be HTTPS
        events:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/WebhookEvent"
    CreateWebhookResponse:
      type: object
      properties:
        webhook:
          type: object
          properties:
            id: { type: string }
            url: { type: string }
            events:
              type: array
              items:
                $ref: "#/components/schemas/WebhookEvent"
            active: { type: boolean }
            secret:
              type: string
              description: Shown only on creation — store securely
            createdAt: { type: string, format: date-time }
        message: { type: string }
    PatchWebhookRequest:
      type: object
      properties:
        active: { type: boolean }
        events:
          type: array
          items:
            $ref: "#/components/schemas/WebhookEvent"
    WebhookListResponse:
      type: object
      properties:
        webhooks:
          type: array
          items:
            type: object
            properties:
              id: { type: string }
              url: { type: string }
              events:
                type: array
                items:
                  $ref: "#/components/schemas/WebhookEvent"
              active: { type: boolean }
              createdAt: { type: string, format: date-time }
              updatedAt: { type: string, format: date-time }
