openapi: 3.1.0
info:
  title: ScaliQ Public API
  version: '1.0.0'
  description: |
    ScaliQ Public API v1. Reference docs are the primary source for humans and agents.
    Import this file into Postman/Insomnia if you want a generated collection.
    Authenticate with header X-API-Key (sk_live_…).
  contact:
    name: ScaliQ
    url: https://app.scaliq.ai/api-mcp
servers:
  - url: https://api.scaliq.ai/public/v1
    description: Production
security:
  - ApiKeyAuth: []
tags:
  - name: Credits
  - name: Accounts
  - name: Campaigns
  - name: Leads
  - name: Inbox
  - name: LinkedIn
paths:
  /credits:
    get:
      tags: [Credits]
      summary: Get credits
      operationId: getCredits
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Credits'
        default:
          $ref: '#/components/responses/Error'
  /accounts:
    get:
      tags: [Accounts]
      summary: List LinkedIn accounts
      operationId: getAccounts
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                required: [accounts]
                properties:
                  accounts:
                    type: array
                    items:
                      $ref: '#/components/schemas/Account'
        default:
          $ref: '#/components/responses/Error'
  /campaigns:
    get:
      tags: [Campaigns]
      summary: List campaigns
      operationId: getCampaigns
      parameters:
        - name: status
          in: query
          schema: { type: string }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
        - name: cursor
          in: query
          schema: { type: string, nullable: true }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                required: [campaigns, cursor]
                properties:
                  campaigns:
                    type: array
                    items:
                      $ref: '#/components/schemas/Campaign'
                  cursor:
                    type: string
                    nullable: true
        default:
          $ref: '#/components/responses/Error'
  /campaigns/{campaignId}:
    get:
      tags: [Campaigns]
      summary: Get campaign
      operationId: getCampaign
      parameters:
        - $ref: '#/components/parameters/campaignId'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Campaign'
        default:
          $ref: '#/components/responses/Error'
  /campaigns/{campaignId}/start:
    post:
      tags: [Campaigns]
      summary: Start or resume campaign
      operationId: startCampaign
      parameters:
        - $ref: '#/components/parameters/campaignId'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
      responses:
        '200':
          description: started | resumed | noop
          content:
            application/json:
              schema:
                type: object
                required: [campaignId, action, status, campaign]
                properties:
                  campaignId: { type: string }
                  action:
                    type: string
                    enum: [started, resumed, noop]
                  status: { type: string }
                  campaign:
                    $ref: '#/components/schemas/Campaign'
        default:
          $ref: '#/components/responses/Error'
  /campaigns/{campaignId}/pause:
    post:
      tags: [Campaigns]
      summary: Pause campaign
      operationId: pauseCampaign
      parameters:
        - $ref: '#/components/parameters/campaignId'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
      responses:
        '200':
          description: paused | noop
          content:
            application/json:
              schema:
                type: object
                required: [campaignId, action, status, campaign]
                properties:
                  campaignId: { type: string }
                  action:
                    type: string
                    enum: [paused, noop]
                  status: { type: string }
                  campaign:
                    $ref: '#/components/schemas/Campaign'
        default:
          $ref: '#/components/responses/Error'
  /campaigns/{campaignId}/leads:
    get:
      tags: [Leads]
      summary: List leads
      operationId: getLeads
      parameters:
        - $ref: '#/components/parameters/campaignId'
        - name: tag
          in: query
          schema:
            $ref: '#/components/schemas/LeadTag'
        - name: replied
          in: query
          schema: { type: boolean }
        - name: needsReply
          in: query
          schema: { type: boolean }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
        - name: cursor
          in: query
          schema: { type: string, nullable: true }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                required: [leads, cursor]
                properties:
                  leads:
                    type: array
                    items:
                      $ref: '#/components/schemas/LeadListItem'
                  cursor:
                    type: string
                    nullable: true
        default:
          $ref: '#/components/responses/Error'
    post:
      tags: [Leads]
      summary: Add lead
      operationId: addLead
      parameters:
        - $ref: '#/components/parameters/campaignId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddLeadBody'
      responses:
        '200':
          description: created | duplicate | excluded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AddLeadResponse'
        default:
          $ref: '#/components/responses/Error'
  /campaigns/{campaignId}/leads/{leadId}:
    get:
      tags: [Leads]
      summary: Get lead
      operationId: getLead
      parameters:
        - $ref: '#/components/parameters/campaignId'
        - $ref: '#/components/parameters/leadId'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LeadDetail'
        default:
          $ref: '#/components/responses/Error'
    patch:
      tags: [Leads]
      summary: Update lead tag or excluded
      operationId: patchLead
      parameters:
        - $ref: '#/components/parameters/campaignId'
        - $ref: '#/components/parameters/leadId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchLeadBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  leadId: { type: string }
                  tag:
                    oneOf:
                      - $ref: '#/components/schemas/LeadTag'
                      - type: 'null'
                  excluded: { type: boolean }
        default:
          $ref: '#/components/responses/Error'
  /inbox/messages:
    post:
      tags: [Inbox]
      summary: Send LinkedIn message
      operationId: sendMessage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SendMessageBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean }
                  chatId: { type: string }
                  messageId: { type: string }
        default:
          $ref: '#/components/responses/Error'
  /linkedin/profiles/lookup:
    post:
      tags: [LinkedIn]
      summary: Lookup profile by URL
      operationId: lookupProfile
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LookupBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProfileLookup'
        default:
          $ref: '#/components/responses/Error'
  /linkedin/search:
    post:
      tags: [LinkedIn]
      summary: Search LinkedIn people (one page)
      operationId: searchLinkedIn
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SearchBody'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                required: [results, cursor]
                properties:
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/SearchResult'
                  cursor:
                    type: string
                    nullable: true
        default:
          $ref: '#/components/responses/Error'
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
  parameters:
    campaignId:
      name: campaignId
      in: path
      required: true
      schema: { type: string }
    leadId:
      name: leadId
      in: path
      required: true
      schema: { type: string }
  responses:
    Error:
      description: Error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorBody'
  schemas:
    ErrorBody:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message, details]
          properties:
            code:
              type: string
              enum:
                - unauthorized
                - plan_required
                - validation_error
                - not_found
                - conflict
                - insufficient_credits
                - account_disconnected
                - sales_nav_required
                - rate_limited
                - linkedin_daily_quota
                - send_failed
                - upstream_unavailable
            message: { type: string }
            details:
              type: object
              additionalProperties: true
    Credits:
      type: object
      required: [credits, topUpCredits, planId]
      properties:
        credits: { type: number }
        topUpCredits: { type: number }
        planId: { type: string }
    AccountToday:
      type: object
      properties:
        connectionRequestsSent: { type: integer }
        connectionRequestsAccepted: { type: integer }
        connectionRequestLimit: { type: integer }
        messagesSent: { type: integer }
        messageLimit: { type: integer }
    Account:
      type: object
      properties:
        id: { type: string }
        name: { type: string }
        status:
          type: string
          enum: [connected, connecting, failed, disconnected]
        active: { type: boolean }
        hasSalesNavigator: { type: boolean }
        today:
          $ref: '#/components/schemas/AccountToday'
    CampaignStats:
      type: object
      properties:
        connectionRequestsSent: { type: integer }
        connectionRequestsAccepted: { type: integer }
        acceptanceRate: { type: number, nullable: true }
        messagesSent: { type: integer }
        repliesReceived: { type: integer }
        replyRate: { type: number, nullable: true }
    Campaign:
      type: object
      properties:
        id: { type: string }
        name: { type: string }
        status: { type: string }
        totalLeads: { type: integer }
        assignedTemplateId: { type: string, nullable: true }
        stats:
          $ref: '#/components/schemas/CampaignStats'
    LeadTag:
      type: string
      enum:
        - Meeting request
        - Interested
        - Information request
        - Not interested
        - Wrong person
        - To be defined
    LeadStage:
      type: string
      enum:
        - new
        - connection_sent
        - connected
        - messaged
        - replied
        - tagged
        - excluded
        - failed
        - handed_off
    LeadListItem:
      type: object
      properties:
        id: { type: string }
        fullName: { type: string, nullable: true }
        firstName: { type: string, nullable: true }
        lastName: { type: string, nullable: true }
        linkedinUrl: { type: string, nullable: true }
        jobTitle: { type: string, nullable: true }
        location: { type: string, nullable: true }
        companyName: { type: string, nullable: true }
        companyLinkedin: { type: string, nullable: true }
        website: { type: string, nullable: true }
        email: { type: string, nullable: true }
        tag:
          oneOf:
            - $ref: '#/components/schemas/LeadTag'
            - type: 'null'
        replied: { type: boolean }
        needsReply: { type: boolean }
        stage:
          $ref: '#/components/schemas/LeadStage'
    WorkExperience:
      type: object
      properties:
        company: { type: string, nullable: true }
        position: { type: string, nullable: true }
        location: { type: string, nullable: true }
        description: { type: string, nullable: true }
        start: { type: string, nullable: true }
        end: { type: string, nullable: true }
    LeadDetail:
      allOf:
        - $ref: '#/components/schemas/LeadListItem'
        - type: object
          properties:
            headline: { type: string, nullable: true }
            workExperience:
              type: array
              items:
                $ref: '#/components/schemas/WorkExperience'
            connectionRequestAccepted: { type: boolean }
            messageSent: { type: boolean }
    AddLeadBody:
      type: object
      required: [linkedinUrl]
      additionalProperties: false
      properties:
        linkedinUrl: { type: string }
        fullName: { type: string, maxLength: 120 }
        firstName: { type: string, maxLength: 120 }
        lastName: { type: string, maxLength: 120 }
        jobTitle: { type: string, maxLength: 200 }
        companyName: { type: string, maxLength: 200 }
        email: { type: string, maxLength: 254 }
        location: { type: string, maxLength: 200 }
        headline: { type: string, maxLength: 500 }
        personalization: { type: string, maxLength: 2000 }
    AddLeadResponse:
      type: object
      required: [leadId, status, creditsCharged]
      properties:
        leadId: { type: string }
        status:
          type: string
          enum: [created, duplicate, excluded]
        creditsCharged: { type: integer, enum: [0, 1] }
    PatchLeadBody:
      type: object
      additionalProperties: false
      properties:
        tag:
          oneOf:
            - $ref: '#/components/schemas/LeadTag'
            - type: 'null'
        excluded: { type: boolean }
    SendMessageBody:
      type: object
      required: [campaignId, leadId, text]
      additionalProperties: false
      properties:
        campaignId: { type: string, maxLength: 128 }
        leadId: { type: string, maxLength: 128 }
        text: { type: string, maxLength: 8000 }
        accountId: { type: string, maxLength: 128 }
    LookupBody:
      type: object
      required: [accountId, linkedinUrl]
      additionalProperties: false
      properties:
        accountId: { type: string }
        linkedinUrl: { type: string }
        depth:
          type: string
          enum: [basic, full]
          default: basic
    ProfileLookup:
      type: object
      properties:
        fullName: { type: string, nullable: true }
        firstName: { type: string, nullable: true }
        lastName: { type: string, nullable: true }
        headline: { type: string, nullable: true }
        jobTitle: { type: string, nullable: true }
        companyName: { type: string, nullable: true }
        companyLinkedin: { type: string, nullable: true }
        website: { type: string, nullable: true }
        location: { type: string, nullable: true }
        linkedinUrl: { type: string, nullable: true }
        providerId: { type: string, nullable: true }
        profilePictureUrl: { type: string, nullable: true }
        about: { type: string, nullable: true }
        experience:
          type: array
          items:
            type: object
            additionalProperties: true
    SearchBody:
      type: object
      required: [accountId]
      additionalProperties: false
      properties:
        accountId: { type: string }
        type:
          type: string
          enum: [classic, sales_nav]
          default: classic
        query: { type: string, maxLength: 2000 }
        url: { type: string, maxLength: 2000 }
        limit: { type: integer }
        cursor: { type: string, maxLength: 4000 }
    SearchResult:
      type: object
      properties:
        fullName: { type: string, nullable: true }
        headline: { type: string, nullable: true }
        jobTitle: { type: string, nullable: true }
        companyName: { type: string, nullable: true }
        location: { type: string, nullable: true }
        linkedinUrl: { type: string, nullable: true }
        providerId: { type: string, nullable: true }
        profilePictureUrl: { type: string, nullable: true }
    WebhookEvent:
      type: object
      description: Outbound webhook payload (not a REST request)
      properties:
        id: { type: string }
        type:
          type: string
          enum:
            - reply.received
            - connection.accepted
            - lead.tagged
            - account.disconnected
        createdAt: { type: string, format: date-time }
        data:
          type: object
          properties:
            campaignId: { type: string, nullable: true }
            leadId: { type: string, nullable: true }
            accountId: { type: string, nullable: true }
            linkedinUrl: { type: string, nullable: true }
            fullName: { type: string, nullable: true }
            messageText: { type: string, nullable: true }
            tag: { type: string, nullable: true }
