openapi: 3.1.0
info:
  title: NotiQ Public API
  version: "1.0.0"
  description: |
    NotiQ Public API v1. Markdown docs are the primary source for humans and agents.
    Import this file into Postman/Insomnia for a generated collection.
    Authenticate with header X-API-Key (sk_live_…).
    Success responses are bare JSON (no data wrapper).
    Every response includes header X-Request-Id.
  contact:
    name: NotiQ
    url: https://app.notiq.io/api-mcp
servers:
  - url: https://api.notiq.io/public/v1
    description: NotiQ Public API
security:
  - ApiKeyAuth: []
tags:
  - name: Credits
  - name: Accounts
  - name: Campaigns
  - name: Leads
  - name: Inbox
paths:
  /credits:
    get:
      tags: [Credits]
      summary: Get credits
      operationId: getCredits
      responses:
        "200":
          description: OK
          headers:
            X-Request-Id:
              $ref: "#/components/headers/XRequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Credits"
        default:
          $ref: "#/components/responses/Error"
  /accounts:
    get:
      tags: [Accounts]
      summary: List Gmail accounts
      operationId: getAccounts
      parameters:
        - $ref: "#/components/parameters/limit"
        - $ref: "#/components/parameters/cursor"
      responses:
        "200":
          description: OK
          headers:
            X-Request-Id:
              $ref: "#/components/headers/XRequestId"
          content:
            application/json:
              schema:
                type: object
                required: [accounts, nextCursor]
                properties:
                  accounts:
                    type: array
                    items:
                      $ref: "#/components/schemas/Account"
                  nextCursor:
                    type: string
                    nullable: true
        default:
          $ref: "#/components/responses/Error"
  /campaigns:
    get:
      tags: [Campaigns]
      summary: List campaigns
      operationId: getCampaigns
      parameters:
        - name: status
          in: query
          schema: { type: string }
        - $ref: "#/components/parameters/limit"
        - $ref: "#/components/parameters/cursor"
      responses:
        "200":
          description: OK
          headers:
            X-Request-Id:
              $ref: "#/components/headers/XRequestId"
          content:
            application/json:
              schema:
                type: object
                required: [campaigns, nextCursor]
                properties:
                  campaigns:
                    type: array
                    items:
                      $ref: "#/components/schemas/Campaign"
                  nextCursor:
                    type: string
                    nullable: true
        default:
          $ref: "#/components/responses/Error"
    post:
      tags: [Campaigns]
      summary: Launch Maps search campaign
      operationId: createCampaign
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [userQuery]
              additionalProperties: false
              properties:
                userQuery: { type: string, maxLength: 2000 }
                numberOfSearch: { type: integer, minimum: 1, maximum: 500, default: 25 }
                leadDuplicateProtectionAcrossCampaigns: { type: boolean }
                acceptedBroadening: { type: boolean }
                previewId: { type: string, maxLength: 128 }
      responses:
        "200":
          description: OK
          headers:
            X-Request-Id:
              $ref: "#/components/headers/XRequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateCampaignResponse"
        default:
          $ref: "#/components/responses/Error"
  /campaigns/{campaignId}:
    get:
      tags: [Campaigns]
      summary: Get campaign
      operationId: getCampaign
      parameters:
        - $ref: "#/components/parameters/campaignId"
      responses:
        "200":
          description: OK
          headers:
            X-Request-Id:
              $ref: "#/components/headers/XRequestId"
          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:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties:
                templateId: { type: string }
      responses:
        "200":
          description: OK
          headers:
            X-Request-Id:
              $ref: "#/components/headers/XRequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CampaignActionResponse"
        default:
          $ref: "#/components/responses/Error"
  /campaigns/{campaignId}/pause:
    post:
      tags: [Campaigns]
      summary: Pause campaign
      operationId: pauseCampaign
      parameters:
        - $ref: "#/components/parameters/campaignId"
      responses:
        "200":
          description: OK
          headers:
            X-Request-Id:
              $ref: "#/components/headers/XRequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CampaignActionResponse"
        default:
          $ref: "#/components/responses/Error"
  /campaigns/{campaignId}/leads:
    get:
      tags: [Leads]
      summary: List leads
      operationId: getLeads
      parameters:
        - $ref: "#/components/parameters/campaignId"
        - $ref: "#/components/parameters/limit"
        - $ref: "#/components/parameters/cursor"
        - name: tag
          in: query
          schema: { type: string }
        - name: replied
          in: query
          schema: { type: boolean }
        - name: needsReply
          in: query
          schema: { type: boolean }
      responses:
        "200":
          description: OK
          headers:
            X-Request-Id:
              $ref: "#/components/headers/XRequestId"
          content:
            application/json:
              schema:
                type: object
                required: [leads, nextCursor]
                properties:
                  leads:
                    type: array
                    items:
                      $ref: "#/components/schemas/Lead"
                  nextCursor:
                    type: string
                    nullable: true
        default:
          $ref: "#/components/responses/Error"
    post:
      tags: [Leads]
      summary: Add lead by email
      operationId: addLead
      parameters:
        - $ref: "#/components/parameters/campaignId"
        - $ref: "#/components/parameters/idempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email]
              additionalProperties: false
              properties:
                email: { type: string, maxLength: 254 }
                fullName: { type: string, maxLength: 120 }
                companyName: { type: string, maxLength: 200 }
                website: { type: string, maxLength: 500 }
      responses:
        "200":
          description: OK
          headers:
            X-Request-Id:
              $ref: "#/components/headers/XRequestId"
          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
          headers:
            X-Request-Id:
              $ref: "#/components/headers/XRequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LeadDetail"
        default:
          $ref: "#/components/responses/Error"
    patch:
      tags: [Leads]
      summary: Update lead
      operationId: patchLead
      parameters:
        - $ref: "#/components/parameters/campaignId"
        - $ref: "#/components/parameters/leadId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties:
                tag:
                  type: string
                  nullable: true
                excluded:
                  type: boolean
      responses:
        "200":
          description: OK
          headers:
            X-Request-Id:
              $ref: "#/components/headers/XRequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PatchLeadResponse"
        default:
          $ref: "#/components/responses/Error"
  /inbox/messages:
    post:
      tags: [Inbox]
      summary: Send or reply via Gmail
      operationId: sendMessage
      parameters:
        - $ref: "#/components/parameters/idempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [campaignId, leadId, text]
              additionalProperties: false
              properties:
                campaignId: { type: string }
                leadId: { type: string }
                text: { type: string, maxLength: 8000 }
                subject: { type: string, maxLength: 500 }
                accountId: { type: string }
      responses:
        "200":
          description: OK
          headers:
            X-Request-Id:
              $ref: "#/components/headers/XRequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SendMessageResponse"
        default:
          $ref: "#/components/responses/Error"
components:
  headers:
    XRequestId:
      description: Unique request correlation id (always returned)
      schema: { type: string }
  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 }
    limit:
      name: limit
      in: query
      schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
    cursor:
      name: cursor
      in: query
      schema: { type: string, nullable: true }
    idempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      schema: { type: string, maxLength: 128 }
  responses:
    Error:
      description: Error
      headers:
        X-Request-Id:
          $ref: "#/components/headers/XRequestId"
        Retry-After:
          schema: { type: integer }
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorBody"
  schemas:
    ErrorBody:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message]
          properties:
            code: { type: string }
            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 }
    Account:
      type: object
      required: [id, name, status, active, today]
      properties:
        id: { type: string }
        name: { type: string }
        status:
          type: string
          enum: [connected, connecting, failed, disconnected]
        active: { type: boolean }
        today:
          type: object
          required: [emailsSent, emailLimit]
          properties:
            emailsSent: { type: number }
            emailLimit: { type: number }
    Campaign:
      type: object
      required: [id, name, status, totalLeads, assignedTemplateId, stats]
      properties:
        id: { type: string }
        name: { type: string }
        status: { type: string }
        totalLeads: { type: number }
        assignedTemplateId: { type: string, nullable: true }
        stats:
          type: object
          properties:
            emailsSent: { type: number }
            repliesReceived: { type: number }
            replyRate: { type: number, nullable: true }
    CreateCampaignResponse:
      type: object
      required: [campaignId, status, requestedLeadTarget, creditsReservedEstimate]
      properties:
        campaignId: { type: string }
        status: { type: string }
        requestedLeadTarget: { type: number }
        creditsReservedEstimate: { type: number }
    CampaignActionResponse:
      type: object
      required: [campaignId, status, action]
      properties:
        campaignId: { type: string }
        status: { type: string }
        action: { type: string }
    Lead:
      type: object
      properties:
        id: { type: string }
        fullName: { type: string, nullable: true }
        firstName: { type: string, nullable: true }
        lastName: { type: string, nullable: true }
        email: { type: string, nullable: true }
        companyName: { type: string, nullable: true }
        website: { type: string, nullable: true }
        jobTitle: { type: string, nullable: true }
        location: { type: string, nullable: true }
        tag: { type: string, nullable: true }
        replied: { type: boolean }
        needsReply: { type: boolean }
        stage: { type: string }
    LeadDetail:
      allOf:
        - $ref: "#/components/schemas/Lead"
        - type: object
          properties:
            phone: { type: string, nullable: true }
            messageSent: { type: boolean }
            excluded: { type: boolean }
    AddLeadResponse:
      type: object
      required: [leadId, status, creditsCharged]
      properties:
        leadId: { type: string }
        status:
          type: string
          enum: [created, duplicate, excluded]
        creditsCharged: { type: number }
    PatchLeadResponse:
      type: object
      required: [id, tag, excluded]
      properties:
        id: { type: string }
        tag: { type: string, nullable: true }
        excluded: { type: boolean }
    SendMessageResponse:
      type: object
      required: [ok, messageId, conversationId]
      properties:
        ok: { type: boolean }
        messageId: { type: string }
        conversationId: { type: string }
        warning: { type: string }
