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

# List the company's keywords.

> Returns every keyword the company has metrics for - including the ones a
research run harvested - sorted by search volume. Pass savedOnly=true to
narrow to keywords a user explicitly kept.



## OpenAPI

````yaml /openapi.json get /api/v0/seo/keywords
openapi: 3.0.3
info:
  title: API Documentation
  version: 1.0.0
  description: >

    # Introduction


    Welcome to the API. This API provides programmatic access to all platform
    features including AI agents, smart lists, documents, sequences, and media
    generation.


    ## Authentication


    The API supports two types of authentication:


    1. **Company API Keys** - Scoped to a specific company

    2. **Personal Access Keys** - User-level keys that work across all your
    companies


    ---


    ### Option 1: Company API Key


    Company API keys are tied to a specific company. Use this when you only need
    to access one company.


    **Getting a Company API Key:**


    1. Log in to your dashboard

    2. Navigate to **Integrations** page

    3. Find the **API Keys** section and click **Generate New Key**

    4. Copy and securely store your API key (it won't be shown again)


    **Usage:**


    Include your API key in the `X-API-Key` header:


    ```bash

    curl -X GET "https://api.example.com/api/v0/agents" \
      -H "X-API-Key: YOUR_COMPANY_API_KEY" \
      -H "Content-Type: application/json"
    ```


    Or use the `Authorization` header (OpenAI-compatible):


    ```bash

    curl -X GET "https://api.example.com/api/v0/agents" \
      -H "Authorization: Bearer YOUR_COMPANY_API_KEY" \
      -H "Content-Type: application/json"
    ```


    ---


    ### Option 2: Personal Access Key


    Personal access keys are tied to your user account and can access any
    company you belong to. Use this when you need to access multiple companies
    or build integrations that work across companies.


    **Getting a Personal Access Key:**


    1. Log in to your dashboard

    2. Click on your avatar/profile icon and go to **Profile**

    3. Find the **Personal Access Keys** section

    4. Click **Create New Key**

    5. Give it a name and optionally set an expiration date

    6. Copy and securely store your key (it won't be shown again)


    **Usage:**


    Personal access keys start with `pak_` and **require** you to specify which
    company to access using the `X-Company-ID` header:


    ```bash

    curl -X GET "https://api.example.com/api/v0/agents" \
      -H "Authorization: Bearer pak_your_personal_access_key" \
      -H "X-Company-ID: your_company_id" \
      -H "Content-Type: application/json"
    ```


    Or using the `X-API-Key` header:


    ```bash

    curl -X GET "https://api.example.com/api/v0/agents" \
      -H "X-API-Key: pak_your_personal_access_key" \
      -H "X-Company-ID: your_company_id" \
      -H "Content-Type: application/json"
    ```


    You can also pass the company ID as a query parameter:


    ```bash

    curl -X GET
    "https://api.example.com/api/v0/agents?companyId=your_company_id" \
      -H "Authorization: Bearer pak_your_personal_access_key" \
      -H "Content-Type: application/json"
    ```


    **Finding Your Company ID:**


    Your company ID can be found in the dashboard URL when viewing a company, or
    via the Settings page.


    ---


    ### Which Key Type Should I Use?


    | Use Case | Recommended Key Type |

    |----------|---------------------|

    | Single company integration | Company API Key |

    | Multi-company dashboard | Personal Access Key |

    | CI/CD pipelines | Company API Key |

    | Personal automation scripts | Personal Access Key |

    | Third-party app integration | Company API Key |


    ---


    ## Response Format


    All responses are returned in JSON format. Successful responses include the
    requested data:


    ```json

    {
      "id": "abc123",
      "name": "My Agent",
      "created": "2024-01-15T10:30:00Z"
    }

    ```


    Error responses follow a consistent structure:


    ```json

    {
      "error": "Description of what went wrong"
    }

    ```


    ## HTTP Status Codes


    | Code | Description |

    |------|-------------|

    | 200 | Success |

    | 201 | Created |

    | 400 | Bad Request - Invalid parameters |

    | 401 | Unauthorized - Invalid or missing API key |

    | 403 | Forbidden - Insufficient permissions |

    | 404 | Not Found - Resource doesn't exist |

    | 429 | Too Many Requests - Rate limit exceeded |

    | 500 | Internal Server Error |


    ## Pagination


    List endpoints support pagination using `page` and `pageSize` parameters:


    ```

    GET /api/v0/agents?page=1&pageSize=20

    ```


    Paginated responses include:

    - `items`: Array of results

    - `total`: Total number of items

    - `pages`: Total number of pages

    - `hasNext`: Whether more pages exist


    ## OpenAI Compatibility


    The Chat API (`/api/v0/chat/completions`) is fully compatible with the
    OpenAI API specification.


    ---


    ## Anthropic Compatibility & Claude Code


    The platform also exposes an **Anthropic Messages API-compatible** endpoint,
    so any Anthropic SDK — including **Claude Code** — can use the platform as
    its LLM provider. Requests are billed against your platform credits like any
    other API usage.


    ### Endpoints


    | Endpoint | Description |

    |----------|-------------|

    | `POST /api/v0/claude/v1/messages` | Create a message (streaming and
    non-streaming, tool use supported) |

    | `POST /api/v0/claude/v1/messages/count_tokens` | Estimate token count for
    a request |

    | `GET /api/v0/claude/v1/models` | List available models |


    Authentication works exactly like the rest of the API: send your API key as
    `Authorization: Bearer <key>` or `x-api-key: <key>`. Personal access keys
    (`pak_...`) also work but require the `X-Company-ID` header, which Claude
    Code cannot send by default — use a **Company API Key** for Claude Code.


    ### Setting up Claude Code


    1. **Install Claude Code** (skip if already installed):


    ```bash

    # macOS / Linux / WSL — native installer

    curl -fsSL https://claude.ai/install.sh | bash


    # Or via npm (requires Node.js 18+)

    npm install -g @anthropic-ai/claude-code

    ```


    On Windows (PowerShell):


    ```powershell

    irm https://claude.ai/install.ps1 | iex

    ```


    Verify the install with `claude --version`. See the official docs at
    https://code.claude.com/docs for more install options.


    2. **Get a Company API Key**: Dashboard → **Integrations** → **API Keys** →
    **Generate New Key**.


    3. **Configure Claude Code.** Either export environment variables in your
    shell:


    ```bash

    export ANTHROPIC_BASE_URL="https://api.example.com/api/v0/claude"

    export ANTHROPIC_AUTH_TOKEN="YOUR_COMPANY_API_KEY"

    claude

    ```


    Or persist the settings in `~/.claude/settings.json` (applies to all
    projects) or `.claude/settings.local.json` in a project. Set the model
    variables to any platform model names — for example:


    ```json

    {
      "env": {
        "ANTHROPIC_BASE_URL": "https://api.example.com/api/v0/claude",
        "ANTHROPIC_AUTH_TOKEN": "YOUR_COMPANY_API_KEY",
        "ANTHROPIC_MODEL": "qwen3.8-max",
        "ANTHROPIC_DEFAULT_OPUS_MODEL": "kimi-k3",
        "ANTHROPIC_DEFAULT_SONNET_MODEL": "qwen3.8-max",
        "ANTHROPIC_DEFAULT_HAIKU_MODEL": "deepseek-v4-flash-0731"
      }
    }

    ```


    4. **Verify** with `/status` inside Claude Code — it should show your custom
    base URL — or test the endpoint directly:


    ```bash

    curl -X POST "https://api.example.com/api/v0/claude/v1/messages" \
      -H "Authorization: Bearer YOUR_COMPANY_API_KEY" \
      -H "anthropic-version: 2023-06-01" \
      -H "Content-Type: application/json" \
      -d '{"model": "qwen3.8-max", "max_tokens": 64, "messages": [{"role": "user", "content": "Hello"}]}'
    ```


    ### Model names


    The `model` field accepts any model name from `GET /api/v0/models` — any
    provider, not just Anthropic — so you can run Claude Code on any model the
    platform offers. When Claude Code sends its default Anthropic model ids
    (e.g. `claude-sonnet-4-5-20250929`), they are routed by tier to platform
    models: opus-class requests run on the platform's highest-capability model,
    sonnet-class on the balanced default, and haiku-class (background tasks) on
    the fastest low-cost model. Set `ANTHROPIC_MODEL` and the
    `ANTHROPIC_DEFAULT_*_MODEL` variables to exact platform model names for full
    control over routing.


    Optionally set `CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1` so Claude Code
    lists the platform's models in its `/model` picker.


    ### Notes and limitations


    - **Streaming** is fully supported (Anthropic SSE event format with
    keep-alive pings).

    - **Tool use** is supported: Claude Code's client-side tools (file edits,
    bash, etc.) are forwarded to the model and executed locally by Claude Code —
    never on the platform's servers.

    - Server-side Anthropic tools (web search, code execution), extended
    thinking blocks, and prompt caching directives are accepted but ignored.

    - Usage is billed per request based on real token counts and appears in your
    usage dashboard under the chat feature.


    ### Using the Anthropic SDK directly


    ```python

    import anthropic


    client = anthropic.Anthropic(
        base_url="https://api.example.com/api/v0/claude",
        auth_token="YOUR_COMPANY_API_KEY",
    )


    response = client.messages.create(
        model="kimi-k3",
        max_tokens=1024,
        messages=[{"role": "user", "content": "Hello"}],
    )

    print(response.content[0].text)

    ```


    ---


    ## MCP (Model Context Protocol) Integration


    The platform provides an MCP server that allows AI assistants like Claude
    Desktop, Cursor, Windsurf, and other MCP-compatible tools to interact with
    the full platform API.


    ### What is MCP?


    Model Context Protocol (MCP) is an open standard that allows AI assistants
    to securely connect to external tools and data sources. With MCP, your AI
    assistant can:


    - Browse and manage your AI agents

    - Create and monitor outreach sequences

    - Search your knowledge base documents

    - Generate AI content, images, audio, and video

    - Execute browser automation tasks

    - And access every other platform feature


    ### MCP Server URL


    ```

    https://api.example.com/api/v0/mcp

    ```


    This endpoint implements the **MCP Streamable HTTP Transport** (JSON-RPC
    2.0) and is compatible with `mcp-remote` and other MCP clients.


    ### Available Tools


    The MCP server provides 3 tools that give access to the entire platform API:


    | Tool | Description |

    |------|-------------|

    | `platform_list_capabilities` | Browse available API endpoints by category
    (Agents, Sequences, Lists, Documents, etc.) |

    | `platform_get_action_details` | Get the full specification for an endpoint
    including required parameters |

    | `platform_execute_action` | Execute any API endpoint with automatic
    authentication |


    ### Connecting Claude Desktop


    1. Open your Claude Desktop configuration file:
       - **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
       - **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`

    2. Add the MCP server configuration:


    ```json

    {
      "mcpServers": {
        "platform": {
          "command": "npx",
          "args": ["-y", "mcp-remote", "https://api.example.com/api/v0/mcp"],
          "env": {
            "API_KEY": "your_api_key_here"
          }
        }
      }
    }

    ```


    3. Restart Claude Desktop


    4. You can now ask Claude to interact with your platform data, for example:
       - "List my AI agents"
       - "Show me my active sequences"
       - "Create a new email sequence for cold outreach"
       - "Search my documents for pricing information"

    ### Connecting Cursor / Windsurf


    1. Open Settings and navigate to the MCP section


    2. Add a new MCP server with:
       - **URL**: `https://api.example.com/api/v0/mcp`
       - **Authentication**: Bearer token with your API key

    3. The platform tools will now be available in your AI assistant


    ### Connecting via mcp-remote (Generic)


    For any MCP-compatible client, you can use the `mcp-remote` package:


    ```bash

    npx mcp-remote https://api.example.com/api/v0/mcp --header "Authorization:
    Bearer YOUR_API_KEY"

    ```


    ### Authentication for MCP


    MCP requests use the same authentication as the REST API:


    **Using Company API Key:**

    ```

    Authorization: Bearer YOUR_COMPANY_API_KEY

    ```


    **Using Personal Access Key:**

    ```

    Authorization: Bearer pak_your_personal_access_key

    X-Company-ID: your_company_id

    ```


    ### Example MCP Interactions


    **List available capabilities:**

    ```json

    {
      "name": "platform_list_capabilities",
      "arguments": {
        "category": "Sequences"
      }
    }

    ```


    **Get action details:**

    ```json

    {
      "name": "platform_get_action_details",
      "arguments": {
        "operationId": "post_sequences.create_sequence"
      }
    }

    ```


    **Execute an action:**

    ```json

    {
      "name": "platform_execute_action",
      "arguments": {
        "operationId": "get_agents.list_agents"
      }
    }

    ```


    ### MCP Endpoints


    | Endpoint | Method | Description |

    |----------|--------|-------------|

    | `/api/v0/mcp/info` | GET | Get MCP server information and capabilities |

    | `/api/v0/mcp/tools/list` | GET | List all available MCP tools |

    | `/api/v0/mcp/tools/call` | POST | Execute an MCP tool call |
servers:
  - url: https://api.parallellabs.app
security:
  - CompanyApiKey: []
  - BearerAuth: []
    CompanyId: []
tags:
  - name: Agents
    description: >-
      Create, manage, and interact with AI agents that handle customer
      interactions across multiple channels including website chat, email, SMS,
      and voice.
  - name: Audio
    description: Generate text-to-speech audio using ElevenLabs.
  - name: Browser
    description: Execute browser automation tasks with natural language.
  - name: Canvas
    description: >
      Create canvas art as shareable images or looping videos — social posts,
      story graphics, quote cards, announcements, link-preview art. Workflow:
      author a complete standalone HTML canvas (large text over a photo, color,
      or gradient, optionally with Lucide icons), then canvas_createCanvas
      renders it at exact pixel size and returns a permanent public URL. Pass
      animated=true to record the canvas as a looping MP4 instead of a still PNG
      — animate it with infinite CSS keyframes per the animation guidelines
      below, and the response returns videoId and an MP4 url. A still render
      costs 1 data credit and an animated recording 3; both are only charged on
      success. Author the HTML to these design guidelines:


      ## What You Are Making

      A canvas is a piece of visual art, not a document. Museum-quality,
      magazine-quality —

      never cartoony, never clip-art, never a slide. Before writing any HTML,
      decide a design

      direction in one sentence (e.g. "Swiss formalism: one monumental word,
      hard grid,

      red/black/paper" or "quiet editorial: thin serif, vast negative space, one
      photograph").

      Every choice then serves that direction.


      ## Brand Style (check first)

      - Before choosing a direction, check whether the company has brand images
      saved in
        the content engine (GET /content/brand-images — content_listBrandImages on the platform
        API/MCP). Do this BEFORE the first render, not after.
      - If a brand image exists, its `imageStylePrompt` is the company's
      established visual
        style (palette, mood, typography feel, photographic treatment) and its `images` list
        holds brand assets (logos, product shots, approved photography) with public URLs.
        Treat these as the source of truth: derive the palette and type choices from the
        style prompt, and embed brand assets by their exact URL instead of stock or
        AI-generated stand-ins when a suitable one exists. Never alter, recolor, or
        distort a logo.
      - If no brand images exist, or the session already supplies brand context
      in its
        instructions, skip the lookup and design from the brief.

      ## Canvas Setup (exact rules)

      - Output one complete standalone HTML document. No external CSS/JS files;
      inline a
        `<style>` block. Plain CSS preferred, but these shared platform libraries may be loaded
        from their CDNs:
        - Tailwind CSS: `<script src="https://cdn.tailwindcss.com"></script>` — utility classes; no build step
        - Lucide Icons: `<script src="https://unpkg.com/lucide@1.33.0/dist/umd/lucide.min.js"></script>` — place `<i data-lucide="arrow-right"></i>`, then call `lucide.createIcons();` after the last icon
        - Google Fonts: `<link href="https://fonts.googleapis.com/css2?family=...&display=swap" rel="stylesheet">` — pick faces that match the brand
      - Size the canvas EXACTLY to the chosen format:
        `html,body{margin:0;padding:0}` and `body{width:<W>px;height:<H>px;overflow:hidden;position:relative}`
      - Formats: square 1080x1080 (feed), portrait 1080x1350 (feed, default),
      story 1080x1920
        (Stories/Reels), landscape 1200x630 (link previews), slide 1920x1080 (16:9 presentation
        slide background — decorative art only, no text; editable text is layered on later).
      - A canvas is rendered one of two ways, and you are told which one this
      session wants:
        - STILL (default): the canvas is photographed the instant it loads. Design it static —
          no animations or transitions, since only the first frame is ever seen.
        - ANIMATED: the canvas is recorded to a short MP4. Follow the animation rules below.

      ## Typography (the heart of the canvas)

      - Google Fonts only, loaded via `<link
      href="https://fonts.googleapis.com/css2?family=...&display=swap"
      rel="stylesheet">`.

      - Choose from this curated library — at most TWO fonts per canvas:
        - Monumental display: Boldonse, Big Shoulders, Erica One, Archivo Black, Bebas Neue
        - Elegant serif: Gloock, Italiana, Young Serif, Instrument Serif, Playfair Display, Libre Baskerville
        - Modern sans: Bricolage Grotesque, Outfit, Work Sans, Instrument Sans, Space Grotesk
        - Editorial body serif: Lora, Crimson Pro, IBM Plex Serif
        - Technical mono: JetBrains Mono, IBM Plex Mono, DM Mono, Tektur, Jura
        - Retro pixel: Silkscreen, Pixelify Sans
        - Handwritten: Nothing You Could Do, Smooch Sans; thin deco: Poiret One
      - Text is minimal and visual-first: usually one dominant word or short
      phrase plus tiny
        supporting labels. Rarely more than a dozen words on the whole canvas.
      - Make the hero text HUGE — roughly 10-25% of the canvas height. Uppercase
      display type
        wants letter-spacing (0.02-0.15em); tiny labels want wide tracking (0.2-0.4em).
      - Line-height on stacked display lines: some display fonts draw far taller
      than their
        em box and collide when stacked (Boldonse needs line-height ~1.3). Use 1.2-1.3 for
        multi-line display type and only go tighter for fonts you know sit inside their box.
      - Contrast in scale is the composition: one enormous element, a few
      whisper-small ones.
        Nothing in between.

      ## Icons (Lucide)

      - When the design calls for an icon — a directional arrow, a play glyph, a
      small
        functional mark next to a label — use Lucide, never emoji and never clip-art.
      - Load it once, at the END of the body, then render the icons:
        `<script src="https://unpkg.com/lucide@1.33.0/dist/umd/lucide.min.js"></script>` followed by `<script>lucide.createIcons();</script>`
      - Place an icon with `<i data-lucide="arrow-right"></i>`. Size and color
      it through CSS on
        the element — Lucide SVGs inherit `currentColor` and take `width`/`height`:
        `i[data-lucide]{width:64px;height:64px;stroke-width:1.5}` (the stroke weight matters as
        much as the size — hairline strokes read as refined, thick ones as cartoonish).
      - Use real Lucide names (arrow-right, arrow-up-right, sparkles, zap,
      check, star, play,
        quote, trending-up, circle-dot). A misspelled name renders nothing at all.
      - Icons are accents, not the subject: one or two per canvas, small against
      the hero type.
        An icon never substitutes for a real design idea.

      ## Animation (ANIMATED canvases only)

      Only when the session asks for an animated canvas. A still canvas must
      have no animation

      at all — it is photographed on the first frame.

      - Animate with CSS `@keyframes` on the elements themselves. The recording
      seeks the page's
        animation timeline frame by frame, so what is recorded is exactly your CSS timeline.
      - Every animation MUST be `infinite` and MUST tile seamlessly: the state
      at the end of one
        iteration has to match the state at the start, or the loop visibly jumps. The cleanest
        way is a single cycle that returns home (`0%` and `100%` identical).
      - Give the whole piece ONE shared cycle length — set every animation to
      the same
        `animation-duration` (or an exact divisor of it, e.g. 2s inside an 8s piece) so the
        composition repeats as a whole rather than drifting out of phase.
      - Stagger with `animation-delay` (negative delays start an element
      mid-cycle), never by
        giving elements unrelated durations.
      - Animate `transform` and `opacity` above all — they are what animates
      smoothly. Also fine:
        `filter`, `clip-path`, gradient positions, `stroke-dashoffset` on SVG.
      - Keep it restrained and slow: a drifting gradient, a slow parallax push,
      type that breathes,
        a single element sliding through. Elegant motion is subtle — nothing bouncing, spinning
        fast, or flashing. The canvas must still look composed in any single frozen frame.

      ### What the recorder can and cannot capture

      The recording pauses the browser's animation timeline and steps through it
      — it does NOT

      watch the page play in real time. Only animations the browser itself
      drives are captured:

      - CAPTURED: CSS `@keyframes`, CSS transitions, SVG SMIL, and the Web
      Animations API
        (`element.animate(...)`) — everything that appears in `document.getAnimations()`.
      - NOT CAPTURED (records as a frozen video, with no error): anything driven
      by JavaScript
        frame by frame — `requestAnimationFrame`, `setInterval`/`setTimeout` loops, canvas 2D/WebGL
        draw loops, and scroll-triggered libraries. Also no `<video>` and no animated GIFs.
      - This is the ONE rule that differs from the landing page builder, whose
      pages play live in
        a real browser and can use anything. Do not carry those habits over.

      ### Animation libraries

      CSS keyframes are the default and are always enough for a canvas — reach
      for a library only

      when the motion genuinely needs orchestration (staggered sequences,
      timelines).

      - The ONLY animation library available here is Anime.js, and ONLY through
      its WAAPI entry
        point, which puts animations on the browser timeline:
        - Anime.js (advanced animation): `<script src="https://cdn.jsdelivr.net/npm/animejs/dist/bundles/anime.umd.min.js"></script>` — staggered reveals, timelines, kinetic type. `anime.waapi.animate(...)` puts the animation on the browser timeline; `anime.animate(...)` / `anime.Timeline` drive it from JS frame by frame
        Call it as `anime.waapi.animate('.el', { translateX: 300, duration: 2000, loop: true })`.
        Its classic `anime.animate(...)` / `anime.Timeline` API is JS-driven and records as a
        FROZEN video — if you use Anime.js at all, every animation must go through
        `anime.waapi.animate`.
      - AOS (used by the landing page builder) is scroll-triggered and USELESS
      here: a canvas is a
        fixed viewport with nothing to scroll, so its animations never fire. Never load it.
      - Whatever you use, the seamless-loop and shared-cycle rules above still
      apply.


      ## Color & Background

      - A limited, intentional palette: 2-3 colors total. Pick them for the
      design direction,
        not decoration. High text/background contrast is non-negotiable.
      - Backgrounds: a flat color, a subtle gradient, or a photo. For photos use
        `background:url(...) center/cover` and ALWAYS add a scrim over photo areas behind text
        (e.g. `background:rgba(0,0,0,.35)` or a directional gradient) so type stays legible.
        The scrim serves the text, not the whole canvas: keep it light (≤ ~0.5 opacity) or
        directional so the photo stays clearly visible — a photo buried under a near-opaque
        overlay reads as a plain dark field and wastes the imagery.
      - Photo URLs must be public http(s) images. Two ways to get one:
        - Stock photography: search the stock image library (Unsplash/Pexels via the stock
          image search — images_searchStockImages on the platform API/MCP) and use a
          result's fullUrl.
        - AI-generated imagery: generate an image with the platform's AI image models
          (images_createImage on the platform API/MCP — describe the scene, pick a model,
          and use the returned public URL). Best for imagery no stock photo can match;
          request it WITHOUT text, since the canvas type layer supplies all text.

      ## Composition (non-negotiable craft rules)

      - Keep a safe margin of at least 5% of the canvas on every side. NOTHING
      touches or
        overflows the canvas edge, and elements never overlap illegibly.
      - Compose deliberately: strong asymmetry or a strict grid both work;
      accidental
        centering of everything does not. Let negative space breathe — empty space is a
        design element, not waste.
      - Repetition rewards attention: thin rules, small registration marks, an
      index number,
        a tiny caption line — sparse, clinical details make the piece feel designed.
      - The finished canvas should look like it took a top-of-field designer
      hours of
        painstaking refinement: aligned edges, consistent spacing rhythm, optically balanced.
        Double-check every element is inside the canvas and nothing collides before finishing.
  - name: Chat
    description: OpenAI-compatible chat completions API.
  - name: Companies
    description: >-
      Manage companies and workspaces. Each company has its own agents, lists,
      sequences, and other resources.
  - name: Company Users
    description: Manage team members and their access to companies.
  - name: Content
    description: >-
      AI content engine for generating and scheduling social media posts,
      articles, and marketing content.
  - name: Content Research
    description: >-
      SEO/AEO opportunity engine. Analyzes a domain against its competitors,
      search results, and AI assistant answers (ChatGPT, Google AI) to surface
      scored content opportunities, each with search volumes and a
      ready-to-generate brief. Workflow: 1) Create a project per ideal customer
      profile with content_research_createResearchProject — each project
      researches one domain against one ICP, so separate audiences stay
      separated rather than being blended into one result set. 2) Start a run
      with content_research_runResearchProject (charges credits up front, not
      refunded on failure; only one run per project can be active at a time). 3)
      Poll content_research_listResearchRuns until the newest run reports
      completed or failed. 4) Read results with
      content_research_listOpportunities, scoped by projectId. 5) Act on one
      with content_research_generateFromOpportunity to write the piece,
      content_research_trackOpportunity to monitor its keywords, or
      content_research_dismissOpportunity to hide it.
  - name: Departments
    description: Organize employees into departments for better team structure.
  - name: Documentation
    description: >-
      Browse and search platform documentation. Workflow: 1) Call
      documentation_listDocumentationCategories to see the top-level categories.
      2) Drill into one with documentation_listDocumentationPages?parentId={id}
      to see its child pages. 3) Call documentation_getDocumentationPage with a
      page id to read the full Markdown content. 4) Use
      documentation_searchDocumentation for keyword search across pages — it
      returns short snippets so you can decide which page to open in full.
      IMPORTANT: If the documentation you need does not exist or is incomplete
      after you browse and search, call documentation_submitDocumentationRequest
      to file a request so the docs team can add it.
  - name: Documents
    description: Manage your knowledge base documents for AI training and reference.
  - name: Employees
    description: >-
      AI employees are customizable personas you can chat with. To chat with an
      employee: 1) Use platform_list_companies to find your company ID, 2) List
      employees with employees_listEmployeesApi or employees_getEmployees, 3)
      Chat using employees_chatEmployee with the employeeId and your message.
  - name: Images
    description: Generate AI images using DALL-E, Leonardo, and other providers.
  - name: Inbox
    description: Manage incoming messages and communications across channels.
  - name: Integrations
    description: >-
      Look up IDs from connected third-party accounts (HeyReach, Salesforge,
      Twilio, Meta Ads, CRMs). These are discovery endpoints: call them to
      resolve the campaignId, workspaceId, sequenceId, audienceId, phone number,
      or crmIntegrationId that other actions require. Start with
      integrations_getIntegrationsByType to find a valid integration ID, then
      call the provider-specific endpoint with it.
  - name: Keys
    description: Manage API keys for programmatic access to the platform.
  - name: Landing Pages
    description: Create and manage landing pages for lead capture and campaigns.
  - name: Leads
    description: >-
      Lead generation and contact enrichment. Generate leads from
      demographic/firmographic filters, premade intent segments, and
      buyer-intent keywords, and enrich a person or company from any identifiers
      (name, email, company, domain, LinkedIn URL). Workflow: 1) Call
      leads_listFieldOptions with describe=true to learn the filter surface,
      then re-call with field= or group= for valid enum values — never guess
      enum strings. 2) Call leads_searchSegments to find premade segment
      externalIds (e.g. "b2b_12636") for interest/intent targeting. 3) Call
      leads_generateLeads with filters, segment ids, and/or audience
      buyer-intent keywords to fetch leads. 4) Call leads_enrichContact to get
      full contact details (emails, phones, ~75 fields) for a specific person or
      company — prefer it over web search for contact lookups. 5) To keep the
      leads, call leads_importLeadsToList with the same criteria plus a
      destination listId: it builds the full audience and writes every matching
      contact into that smart list as rows. The list must already exist (create
      one with lists_saveList first), and the import runs in the background — it
      returns a taskId immediately. Generating leads costs 1 data credit per
      call and a matched enrichment costs 2; importing costs 1 per contact
      imported and is gated on the FULL match count (the whole audience is built
      regardless of how many rows you import), so check totalAvailable with
      leads_generateLeads before importing; the field-options and segments
      endpoints are free.
  - name: Lists
    description: Manage smart lists for organizing contacts and data.
  - name: Marketing Campaigns
    description: >-
      Create, schedule, and send one-shot marketing email campaigns to a smart
      list, and track open/click performance.
  - name: Mcp
    description: >-
      Model Context Protocol (MCP) server for AI assistants like Claude Desktop,
      Cursor, and Windsurf. Provides tools for full platform API access.
  - name: Memories
    description: >-
      Store and retrieve conversation context and user preferences for AI
      personalization.
  - name: Models
    description: List available AI models.
  - name: Monitors
    description: >-
      Track keywords across social media platforms (Reddit, Hacker News,
      LinkedIn, X, Quora) and discover relevant conversations. Create monitors
      to track specific keywords, receive notifications when mentions are found,
      and generate AI-powered reply suggestions to engage with your audience.
  - name: Organizations
    description: >-
      List the organizations the authenticated user belongs to. An organization
      is the top-level grouping above companies — use it to discover which
      company workspaces are reachable before calling platform_list_companies.
  - name: People
    description: >-
      Identity graph for finding people in the system. One resolved profile per
      real human, merged across sequence members, list rows, agent
      conversations, and marketing campaign recipients, so the same person found
      in four places is one result instead of four. Start with
      people_peopleSearch — a name, email, or phone in one call, resolved
      through the graph rather than scanning each source separately. It returns
      a `people` array of resolved profiles plus the matching records bucketed
      by source (sequenceMembers, listRows, agentConversations,
      marketingCampaignRecipients). Search works even before the graph is
      populated: it falls back to a direct scan. From a search hit,
      people_getPerson returns one person’s full profile (every identifier,
      touchpoints grouped by source) and people_getPersonActivity returns their
      behavioural timeline (opens, clicks, bookings, calls), newest first, with
      bot traffic excluded by default. people_getPeople browses and filters the
      whole graph, and people_getPeopleStats summarizes it. The graph is rebuilt
      by a nightly background job — there is no API to trigger it.
  - name: Projects
    description: Organize work into projects for better task management.
  - name: SEO
    description: >-
      On-demand SEO lookups: keyword demand, domain authority and a saved
      keyword list. These are single-question tools, as opposed to the scored,
      scheduled analysis in Content Research. Workflow: 1)
      seo_researchKeywordsRoute expands a seed keyword into related terms and
      returns search volume, CPC and competition for each, optionally with the
      live SERP. 2) seo_domainOverviewRoute returns domain rating, estimated
      organic traffic, backlinks and referring domains for any domain;
      seo_compareDomainsRoute does the same for several at once, for putting a
      domain next to its competitors. 3) seo_saveKeywordsRoute keeps interesting
      keywords on a list that seo_listSavedKeywordsRoute reads back with fresh
      metrics joined on. Cost: results are served from a shared cache whenever
      possible, and every response reports `purchased` and `creditCost` — a
      cache hit costs nothing. Only set refresh=true when the data is known to
      be stale, since it forces a re-purchase.
  - name: Sequences
    description: >-
      Manage automated outreach sequences for email, SMS, and multi-channel
      campaigns.
  - name: Support
    description: >-
      Create and manage support tickets, post follow-up messages on tickets, and
      track their status.
  - name: Tags
    description: Organize and categorize resources with custom tags.
  - name: Usage
    description: Track API usage, credits, and billing information.
  - name: Video
    description: Generate AI videos using fal.ai models.
  - name: Voices
    description: Manage voice clones and text-to-speech voice options.
  - name: Web
    description: >-
      Real-time web tools: search the web and news, read any page, and pull
      jobs, local-business, and trending-topic data. Workflow: 1) web_searchWeb
      finds pages and web_searchNews finds recent coverage
      (freshness=pd/pw/pm/py); both return titles, URLs, and snippets. 2)
      web_scrapePage reads a URL as clean markdown (or raw HTML with
      responseType=html) — use it to read any result in full, including
      JavaScript-heavy and LinkedIn pages. 3) web_searchPlaces finds local
      businesses; take a result's placeId or dataId into web_getPlace for full
      details, web_getPlaceReviews for reviews (paginate with nextPageToken),
      and web_getPlacePhotos for photos. 4) web_searchJobs finds job listings
      and web_autocompleteTrends maps a phrase to trending topics. 5)
      web_screenshotPage captures a page as a PNG and returns a public image URL
      — useful for visual checks and reports. Every call costs 1 data credit and
      is only charged on success.
paths:
  /api/v0/seo/keywords:
    get:
      tags:
        - SEO
      summary: List the company's keywords.
      description: |-
        Returns every keyword the company has metrics for - including the ones a
        research run harvested - sorted by search volume. Pass savedOnly=true to
        narrow to keywords a user explicitly kept.
      operationId: seo_listKeywordsRoute
      parameters:
        - name: companyId
          in: query
          required: true
          schema:
            type: string
            description: Company ID
          description: Company ID
        - name: page
          in: query
          required: false
          schema:
            type: integer
            description: 1-indexed page number
          description: 1-indexed page number
        - name: pageSize
          in: query
          required: false
          schema:
            type: integer
            description: Rows per page, max 100
          description: Rows per page, max 100
        - name: search
          in: query
          required: false
          schema:
            type: string
            description: Filter to keywords containing this text
          description: Filter to keywords containing this text
        - name: tag
          in: query
          required: false
          schema:
            type: string
            description: Filter to keywords carrying this tag
          description: Filter to keywords carrying this tag
        - name: savedOnly
          in: query
          required: false
          schema:
            type: boolean
            description: Only keywords a user saved. Defaults to false (all keywords).
          description: Only keywords a user saved. Defaults to false (all keywords).
        - name: sortBy
          in: query
          required: false
          schema:
            type: string
            description: Sort field. Defaults to volume, highest first.
            enum:
              - volume
              - cpc
              - competition
              - keyword
          description: Sort field. Defaults to volume, highest first.
        - name: country
          in: query
          required: false
          schema:
            type: string
            description: Country code. Defaults to "us".
          description: Country code. Defaults to "us".
        - name: language
          in: query
          required: false
          schema:
            type: string
            description: Language code. Defaults to "en".
          description: Language code. Defaults to "en".
      responses:
        '200':
          description: Keywords with metrics
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          description: Keyword ID (UUID)
                        keyword:
                          type: string
                          description: The keyword, lowercased
                        country:
                          type: string
                          description: Country code the metrics were pulled for
                        language:
                          type: string
                          description: Language code the metrics were pulled for
                        saved:
                          type: boolean
                          description: Whether a user explicitly saved this keyword
                        savedAt:
                          type: string
                          format: date-time
                          description: When it was saved, or null
                        tags:
                          type: array
                          items:
                            type: string
                          description: Free-text labels for grouping
                        notes:
                          type: string
                          description: Free-text note
                        metrics:
                          type: object
                          properties:
                            keyword:
                              type: string
                              description: The keyword, lowercased
                            volume:
                              type: integer
                              description: Monthly search volume, or null if unknown
                            volumeSource:
                              type: string
                              description: >-
                                Which network reported the volume: "google" or
                                "bing". Never compare volumes across sources -
                                Bing absolute volumes run well below Google.
                            cpc:
                              type: number
                              description: Average cost per click in USD
                            competition:
                              type: string
                              description: Advertiser competition label
                            competitionIndex:
                              type: integer
                              description: Advertiser competition, 0-100
                            monthlySearches:
                              type: array
                              items:
                                type: object
                              description: 12-month volume series
                            trend:
                              type: string
                              description: 'Direction of demand: rising, stable or falling'
                            momChangePct:
                              type: number
                              description: Month-over-month change, percent
                            saved:
                              type: boolean
                              description: >-
                                Whether this keyword is on the company saved
                                list
                            isSeed:
                              type: boolean
                              description: >-
                                Whether this is the keyword that was searched
                                for
                            fetchedAt:
                              type: string
                              format: date-time
                              description: When these metrics were last purchased
                          description: Cached metrics for this keyword
                        created_date:
                          type: string
                          format: date-time
                          description: When the keyword was first harvested
                      description: >-
                        A keyword and its metrics. Rows are created when a
                        research run or a manual lookup harvests the keyword, so
                        everything the company has paid to discover is listed.
                        `saved` records whether a user explicitly kept it.
                  total:
                    type: integer
                    description: Total keywords matching the filters
                  savedCount:
                    type: integer
                    description: How many of the company's keywords are saved
                  tags:
                    type: array
                    items:
                      type: string
                    description: All tags currently in use
                description: One page of keywords
        '400':
          description: Company ID required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Invalid credentials
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    Error:
      type: object
      properties:
        error:
          type: string
          description: Error message
  securitySchemes:
    CompanyApiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: >-
        Company API key - scoped to a specific company. Generate from the
        Integrations page in your dashboard.
    BearerAuth:
      type: http
      scheme: bearer
      description: >-
        Bearer token authentication. Can use either a Company API Key or a
        Personal Access Key (starting with pak_). Personal Access Keys require
        X-Company-ID header.
    CompanyId:
      type: apiKey
      in: header
      name: X-Company-ID
      description: >-
        Required when using Personal Access Keys. Specifies which company to
        access.

````