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

# Plan a campaign

> Step one of two. Turns a plain-language brief plus a set of searches into a reviewable campaign spec, and preflights the workspace against it.

Nothing is pushed to Smartlead or HeyReach here. The `issues` array is the point: it surfaces every setup problem — missing API key, no connected inbox or sender, no eligible leads — before anyone can publish.



## OpenAPI

````yaml /openapi.json post /api/campaigns/plan
openapi: 3.1.0
info:
  title: Revcenter API
  description: >-
    Search, enrich, and activate people and companies. Two authentication
    surfaces share one host: the bearer-token API under `/v1` for
    server-to-server and agent use, and the workspace-session API under `/api`
    that the Revcenter app itself runs on.
  version: '2026-08-12'
servers:
  - url: https://api.revcenter.ai
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Search
    description: Web search, search runs, results, exports, and shareable shortlists.
  - name: People
    description: Person search, profile enrichment, and the workspace contact pool.
  - name: Companies
    description: Company search, company enrichment, and org-chart people.
  - name: Campaigns
    description: Plan, publish, and launch outbound sequences on Smartlead and HeyReach.
  - name: Account
    description: Health, usage totals, and cost events.
paths:
  /api/campaigns/plan:
    post:
      tags:
        - Campaigns
      summary: Plan a campaign
      description: >-
        Step one of two. Turns a plain-language brief plus a set of searches
        into a reviewable campaign spec, and preflights the workspace against
        it.


        Nothing is pushed to Smartlead or HeyReach here. The `issues` array is
        the point: it surfaces every setup problem — missing API key, no
        connected inbox or sender, no eligible leads — before anyone can
        publish.
      operationId: planCampaign
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - prompt
                - scanIds
              properties:
                prompt:
                  type: string
                  description: What the campaign should do, in plain language.
                  examples:
                    - >-
                      Four-touch LinkedIn sequence for the Lévis clinic
                      managers, warm and short, two days apart.
                scanIds:
                  type: array
                  minItems: 1
                  items:
                    type: string
                    format: uuid
                  description: >-
                    The searches to draw leads from. Searches outside your
                    workspace are ignored.
                model:
                  $ref: '#/components/schemas/ExtractModel'
      responses:
        '200':
          description: The proposed spec and its eligibility preview.
          content:
            application/json:
              schema:
                type: object
                properties:
                  spec:
                    $ref: '#/components/schemas/CampaignSpec'
                  totalLeads:
                    type: integer
                    description: Unique leads across the selected searches.
                  eligibleLeads:
                    type: integer
                    description: >-
                      Leads reachable on the chosen platform — with an email for
                      Smartlead, a LinkedIn URL for HeyReach.
                  withEmail:
                    type: integer
                  withLinkedIn:
                    type: integer
                  issues:
                    type: array
                    items:
                      type: string
                    description: >-
                      Blocking setup problems, in plain language. Empty means
                      the campaign is publishable.
        '400':
          $ref: '#/components/responses/AppBadRequest'
        '401':
          $ref: '#/components/responses/AppUnauthorized'
        '404':
          $ref: '#/components/responses/AppNotFound'
        '424':
          $ref: '#/components/responses/AppProviderUnavailable'
        '502':
          $ref: '#/components/responses/AppUpstreamError'
      security:
        - sessionCookie: []
components:
  schemas:
    ExtractModel:
      type: string
      enum:
        - claude-haiku-4-5
        - claude-sonnet-5
        - claude-opus-5
      description: >-
        Which model extracts criteria. Anything outside this allowlist is
        rejected. Omit to use the workspace default.
    CampaignSpec:
      type: object
      description: >-
        The reviewable campaign plan. Produced by the plan endpoint, sent back
        verbatim to publish.
      properties:
        platform:
          type: string
          enum:
            - smartlead
            - heyreach
          description: >-
            `smartlead` sends email and needs leads with emails. `heyreach`
            sends LinkedIn and needs leads with profile URLs.
        name:
          type: string
        steps:
          type: array
          minItems: 1
          description: The sequence, in order.
          items:
            type: object
            properties:
              subject:
                type: string
                description: Email only.
              body:
                type: string
              delayDays:
                type: integer
                description: Wait before this step.
            additionalProperties: true
      additionalProperties: true
    AppError:
      type: object
      description: >-
        The error shape used by every `/api` endpoint. Flatter than the `/v1`
        shape, and `code` appears only where the UI branches on it.
      properties:
        error:
          type: string
        code:
          type: string
          examples:
            - search_unavailable
  responses:
    AppBadRequest:
      description: Missing or invalid input.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/AppError'
          examples:
            missing:
              value:
                error: criteria (CriteriaV2) is required
    AppUnauthorized:
      description: >-
        Not signed in, no active workspace, or the account lacks the role this
        action needs. A frozen account gets `403` with code `frozen`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/AppError'
          examples:
            signIn:
              value:
                error: Sign in required
    AppNotFound:
      description: >-
        The record does not exist, or belongs to another workspace. The two are
        deliberately indistinguishable.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/AppError'
          examples:
            notFound:
              value:
                error: Scan not found
    AppProviderUnavailable:
      description: >-
        A capability this action needs is not connected for the workspace. The
        `code` names which one.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/AppError'
          examples:
            search:
              value:
                error: Search is unavailable for this workspace.
                code: search_unavailable
            scoring:
              value:
                error: AI scoring is unavailable for this workspace.
                code: ai_scoring_unavailable
            heyreach:
              value:
                error: No HeyReach API key configured — add one in Credentials.
                code: heyreach_not_configured
    AppUpstreamError:
      description: A downstream service or the database failed.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/AppError'
          examples:
            upstream:
              value:
                error: Spec extraction failed
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        A Revcenter API key: `Authorization: Bearer rvc_live_...`. Keys are
        scoped, rate limited per key, and metered per workspace.
    sessionCookie:
      type: apiKey
      in: cookie
      name: session
      description: >-
        A signed-in workspace session, sent as a browser cookie. Requests must
        include credentials, and act on the session's active workspace.

````