---
title: "Minds v1 API Endpoint Catalog"
description: "Complete customer-facing catalog of Minds v1 REST endpoints, response modes, and corresponding MCP capabilities."
---

# Minds v1 API Endpoint Catalog

This catalog lists the complete customer-facing v1 route surface. Use it with the live [OpenAPI 3.1 document](/docs/api/openapi) for schemas and the [agent integration guide](/docs/api/agents) for safe orchestration patterns.

## Conventions

- Apex base URL: `https://getminds.ai/api/v1`
- API-subdomain base URL: `https://api.getminds.ai/v1`
- Authentication: `Authorization: Bearer minds_…`
- JSON requests: `Content-Type: application/json`
- Most JSON responses use `{ "data": ... }`; validation and access failures use the shared error envelope documented under [Errors & Limits](/docs/api/errors).
- `{id}`, `{mindId}`, `{studyId}`, and similar tokens are path parameters, not literal strings.
- `DELETE` operations are destructive. Confirm the exact resource identifiers before calling them.
- MCP mappings identify the equivalent capability. An MCP tool may compose more than one v1 request or expose several routes through an `action` parameter.

## Meta, identity, and credentials

<table>
<thead>
  <tr>
    <th>
      Method
    </th>
    
    <th>
      Path
    </th>
    
    <th>
      Purpose
    </th>
    
    <th>
      MCP equivalent
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/openapi.json
      </code>
    </td>
    
    <td>
      Return the public OpenAPI 3.1 document; authentication is not required
    </td>
    
    <td>
      MCP publishes protocol-native <code>
        tools/list
      </code>
      
       discovery
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/auth/me
      </code>
    </td>
    
    <td>
      Read the authenticated account identity and plan context
    </td>
    
    <td>
      Intentionally transport context, not an MCP research tool
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/api-keys
      </code>
    </td>
    
    <td>
      List API-key metadata; secret values are never returned again
    </td>
    
    <td>
      Intentionally excluded from MCP
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/api-keys
      </code>
    </td>
    
    <td>
      Mint an API key and return its secret once
    </td>
    
    <td>
      Intentionally excluded from MCP
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        DELETE
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/api-keys/{keyId}
      </code>
    </td>
    
    <td>
      Revoke an API key
    </td>
    
    <td>
      Intentionally excluded from MCP
    </td>
  </tr>
</tbody>
</table>

MCP sessions cannot mint, rotate, or revoke their own bearer credentials. Manage credentials only through authenticated account settings or the REST endpoints above.

## Minds

The historical API resource name for a Mind is `spark`.

<table>
<thead>
  <tr>
    <th>
      Method
    </th>
    
    <th>
      Path
    </th>
    
    <th>
      Purpose
    </th>
    
    <th>
      MCP equivalent
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds
      </code>
    </td>
    
    <td>
      List owned Minds with pagination and filters
    </td>
    
    <td>
      <code>
        list_minds
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds
      </code>
    </td>
    
    <td>
      Create and queue training for a Mind
    </td>
    
    <td>
      <code>
        create_mind
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        DELETE
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds
      </code>
    </td>
    
    <td>
      Batch-delete confirmed Minds through canonical cleanup
    </td>
    
    <td>
      <code>
        manage_mind
      </code>
      
       with <code>
        action: "delete_many"
      </code>
      
       (1–100 IDs per tool call)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/library
      </code>
    </td>
    
    <td>
      Read the first-party owned/shared library projection
    </td>
    
    <td>
      <code>
        list_minds
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/{mindId}
      </code>
    </td>
    
    <td>
      Read one visible Mind
    </td>
    
    <td>
      <code>
        manage_mind
      </code>
      
       with <code>
        action: "get"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        PUT
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/{mindId}
      </code>
    </td>
    
    <td>
      Update editable Mind fields and sharing configuration
    </td>
    
    <td>
      <code>
        manage_mind
      </code>
      
       with <code>
        action: "update"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        DELETE
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/{mindId}
      </code>
    </td>
    
    <td>
      Delete one confirmed Mind and associated resources
    </td>
    
    <td>
      <code>
        manage_mind
      </code>
      
       with <code>
        action: "delete"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/{mindId}/training
      </code>
    </td>
    
    <td>
      Read training status and progress
    </td>
    
    <td>
      <code>
        get_mind_status
      </code>
      
       or <code>
        manage_mind
      </code>
      
       with <code>
        action: "get_training"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/{mindId}/retrain
      </code>
    </td>
    
    <td>
      Queue retraining and rebuild the complete stored knowledge index before ready
    </td>
    
    <td>
      <code>
        manage_mind
      </code>
      
       with <code>
        action: "retrain"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/{mindId}/regenerate-image
      </code>
    </td>
    
    <td>
      Regenerate the Mind profile image
    </td>
    
    <td>
      <code>
        manage_mind
      </code>
      
       with <code>
        action: "regenerate_image"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/{mindId}/regenerate-prompt
      </code>
    </td>
    
    <td>
      Regenerate the system prompt from current knowledge
    </td>
    
    <td>
      <code>
        manage_mind
      </code>
      
       with <code>
        action: "regenerate_prompt"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/{mindId}/regenerate-embeddings
      </code>
    </td>
    
    <td>
      Queue a fresh vector rebuild for every stored knowledge item
    </td>
    
    <td>
      <code>
        manage_mind
      </code>
      
       with <code>
        action: "regenerate_embeddings"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/{mindId}/patterns
      </code>
    </td>
    
    <td>
      Read raw Mind patterns when access permits
    </td>
    
    <td>
      <code>
        manage_mind
      </code>
      
       with <code>
        action: "get_patterns"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/{mindId}/completion
      </code>
    </td>
    
    <td>
      Run a stateless completion with one Mind
    </td>
    
    <td>
      <code>
        chat_with_mind
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/{mindId}/export
      </code>
    </td>
    
    <td>
      Start or return a Mind profile export
    </td>
    
    <td>
      <code>
        export_mind
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/{mindId}/export-status
      </code>
    </td>
    
    <td>
      Poll an asynchronous Mind export
    </td>
    
    <td>
      <code>
        export_mind
      </code>
      
       returns status information
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/{mindId}/export-download
      </code>
    </td>
    
    <td>
      Download a completed Mind export
    </td>
    
    <td>
      <code>
        export_mind
      </code>
      
       returns the artifact or link
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/user/shareable-sparks
      </code>
    </td>
    
    <td>
      List Minds eligible for a sharing picker
    </td>
    
    <td>
      <code>
        list_minds
      </code>
    </td>
  </tr>
</tbody>
</table>

Batch deletion accepts:

```json
{
  "mindIds": [
    "550e8400-e29b-41d4-a716-446655440000",
    "550e8400-e29b-41d4-a716-446655440001"
  ]
}
```

The response reports `deleted`, `notFound`, and `failed` IDs independently so one cleanup failure does not conceal the rest of the batch.

## Mind knowledge

<table>
<thead>
  <tr>
    <th>
      Method
    </th>
    
    <th>
      Path
    </th>
    
    <th>
      Purpose
    </th>
    
    <th>
      MCP equivalent
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/{mindId}/knowledge
      </code>
    </td>
    
    <td>
      List knowledge items
    </td>
    
    <td>
      <code>
        manage_mind_knowledge
      </code>
      
       with <code>
        action: "list"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/{mindId}/knowledge
      </code>
    </td>
    
    <td>
      Add a link, keyword search, multipart file, or URL-referenced file
    </td>
    
    <td>
      <code>
        manage_mind_knowledge
      </code>
      
       with <code>
        action: "add"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        PUT
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/{mindId}/knowledge/{itemId}
      </code>
    </td>
    
    <td>
      Update description, watch state, or supported metadata
    </td>
    
    <td>
      <code>
        manage_mind_knowledge
      </code>
      
       with <code>
        action: "update"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        DELETE
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/{mindId}/knowledge/{itemId}
      </code>
    </td>
    
    <td>
      Delete a knowledge item and associated stored artifact
    </td>
    
    <td>
      <code>
        manage_mind_knowledge
      </code>
      
       with <code>
        action: "delete"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/{mindId}/knowledge/{itemId}/status
      </code>
    </td>
    
    <td>
      Poll knowledge processing
    </td>
    
    <td>
      <code>
        manage_mind_knowledge
      </code>
      
       with <code>
        action: "status"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/{mindId}/knowledge/enrich
      </code>
    </td>
    
    <td>
      Convenience keyword-enrichment operation
    </td>
    
    <td>
      <code>
        manage_mind_knowledge
      </code>
      
       with <code>
        action: "enrich"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/minds/{mindId}/knowledge/patterns
      </code>
    </td>
    
    <td>
      Read knowledge patterns by framework
    </td>
    
    <td>
      <code>
        manage_mind_knowledge
      </code>
      
       with <code>
        action: "patterns"
      </code>
    </td>
  </tr>
</tbody>
</table>

For agent-originated files, pass a public, short-lived signed, or Minds workspace-upload URL rather than base64 content:

```json
{
  "file": {
    "name": "research-brief.pdf",
    "url": "https://signed.example/research-brief.pdf",
    "type": "application/pdf"
  },
  "description": "Primary customer research brief",
  "regeneratePrompt": true
}
```

Remote retrieval is SSRF-guarded, limited to 50 MB, and time-bounded. Multipart and URL-referenced files enter the same storage and processing queue.

Canonical product resources are **Audiences** and **Studies**. The former `/api/v1/groups` and `/api/v1/panels` paths remain callable during the compatibility window, but return `Deprecation: true` and a `Link` header pointing to the canonical successor. New integrations should not use the legacy paths or names.

## Audiences and grounding

<table>
<thead>
  <tr>
    <th>
      Method
    </th>
    
    <th>
      Path
    </th>
    
    <th>
      Purpose
    </th>
    
    <th>
      MCP equivalent
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/limits
      </code>
    </td>
    
    <td>
      Read private account/team Audience size ceilings; unavailable entitlements return <code>
        503
      </code>
    </td>
    
    <td>
      <code>
        get_audience_limits
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences
      </code>
    </td>
    
    <td>
      List visible Audiences
    </td>
    
    <td>
      <code>
        list_audiences
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences
      </code>
    </td>
    
    <td>
      Create an Audience from explicit Mind IDs
    </td>
    
    <td>
      <code>
        create_audience
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/library
      </code>
    </td>
    
    <td>
      Read the first-party owned/followed/shared library projection
    </td>
    
    <td>
      <code>
        list_audiences
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}
      </code>
    </td>
    
    <td>
      Read members, access, grounding, and Formations
    </td>
    
    <td>
      <code>
        get_audience
      </code>
      
       or <code>
        manage_audience
      </code>
      
       with <code>
        action: "get"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        PUT
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}
      </code>
    </td>
    
    <td>
      Update supported Audience fields and sharing configuration
    </td>
    
    <td>
      <code>
        manage_audience
      </code>
      
       with <code>
        action: "update"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        DELETE
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}
      </code>
    </td>
    
    <td>
      Delete a confirmed Audience
    </td>
    
    <td>
      <code>
        manage_audience
      </code>
      
       with <code>
        action: "delete"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/members
      </code>
    </td>
    
    <td>
      Add one or more Minds to an Audience
    </td>
    
    <td>
      <code>
        manage_audience
      </code>
      
       with <code>
        action: "add_member"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        DELETE
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/members/{mindId}
      </code>
    </td>
    
    <td>
      Remove one Mind from an Audience
    </td>
    
    <td>
      <code>
        manage_audience
      </code>
      
       with <code>
        action: "remove_member"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/follow
      </code>
    </td>
    
    <td>
      Follow/save a visible public Audience
    </td>
    
    <td>
      <code>
        manage_audience
      </code>
      
       with <code>
        action: "follow"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        DELETE
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/follow
      </code>
    </td>
    
    <td>
      Unfollow a saved Audience
    </td>
    
    <td>
      <code>
        manage_audience
      </code>
      
       with <code>
        action: "unfollow"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/progress
      </code>
    </td>
    
    <td>
      Read settled creation progress, background research state, and grounding coverage
    </td>
    
    <td>
      <code>
        manage_audience
      </code>
      
       with <code>
        action: "get_progress"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/active-progress
      </code>
    </td>
    
    <td>
      Read active Audience/Mind build progress for the UI
    </td>
    
    <td>
      Use per-Audience or per-Mind MCP status tools
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/lifecycle/stream
      </code>
    </td>
    
    <td>
      Stream user-scoped invalidation hints; clients re-read durable state after each event
    </td>
    
    <td>
      MCP uses durable status reads instead of a browser SSE subscription
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/recalibrate
      </code>
    </td>
    
    <td>
      Replace stored grounding with refreshed authoritative research; <code>
        deepen
      </code>
      
       researches the dimensions still missing or assumed plus up to 8 topics
    </td>
    
    <td>
      <code>
        recalibrate_audience
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/recalibrations/{operationId}
      </code>
    </td>
    
    <td>
      <code>
        status
      </code>
      
      , <code>
        result
      </code>
      
      , <code>
        error
      </code>
    </td>
    
    <td>
      <code>
        get_audience
      </code>
      
       (<code>
        calibration.operation
      </code>
      
      )
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/regenerate-images
      </code>
    </td>
    
    <td>
      Regenerate Audience member images with optional limits/dry run
    </td>
    
    <td>
      <code>
        manage_audience
      </code>
      
       with <code>
        action: "regenerate_images"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/source-import
      </code>
    </td>
    
    <td>
      Import account-owned research files without creating an Audience; up to 20 files, 5 MiB each and 12 MiB supplied content, with a 24 MiB JSON body ceiling. Private responses; malformed input returns <code>
        400
      </code>
      
      , oversized requests <code>
        413
      </code>
      
      , storage failures <code>
        502
      </code>
      
      . Identical uploads can be retried.
    </td>
    
    <td>
      <code>
        import_audience_sources
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/from-brief
      </code>
    </td>
    
    <td>
      Review a grounded Audience draft, then create only the accepted snapshot
    </td>
    
    <td>
      <code>
        create_audience_from_brief
      </code>
      
      ; app-only <code>
        submit_audience_review
      </code>
      
       reuses this workflow
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/from-brief/jobs/{jobId}
      </code>
    </td>
    
    <td>
      Read an asynchronous Audience operation
    </td>
    
    <td>
      Resume <code>
        create_audience_from_brief
      </code>
      
       with the returned operation ID
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/from-brief/jobs/{jobId}/progress
      </code>
    </td>
    
    <td>
      Read owned Audience review, creation and training progress
    </td>
    
    <td>
      <code>
        get_audience_creation_progress
      </code>
      
       / <code>
        render_audience_review
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/from-brief/segmentation-preview
      </code>
    </td>
    
    <td>
      Review respondent variables with a 256 KiB JSON request limit; private responses, safe failures and role-based defaults
    </td>
    
    <td>
      <code>
        preview_audience_dataset_segmentation
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/allocation-preview
      </code>
    </td>
    
    <td>
      Non-mutating deterministic cohort-allocation preview
    </td>
    
    <td>
      <code>
        create_audience_from_brief
      </code>
      
       cohort-allocation workflow
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/export
      </code>
    </td>
    
    <td>
      Start or return a branded Audience brief export
    </td>
    
    <td>
      <code>
        export_audience
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/export-status
      </code>
    </td>
    
    <td>
      Poll an asynchronous Audience export
    </td>
    
    <td>
      <code>
        export_audience
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/export-download
      </code>
    </td>
    
    <td>
      Download a completed Audience export
    </td>
    
    <td>
      <code>
        export_audience
      </code>
      
       returns the artifact
    </td>
  </tr>
</tbody>
</table>

Audiences and Studies are private by default. Set `isLinkSharingEnabled: true` only when the user explicitly requests a public link; doing so can make persisted grounding and attached research context readable through the shared surface.

Audience member additions require a JSON object within 64 KiB, including ignored fields. A `mindIds` array accepts at most 1,000 entries before deduplication; split larger additions into separate requests. Invalid JSON or excess IDs returns `400`; excess body bytes returns `413`. Legacy Group URLs enforce the same limits. Audience member additions use `mindId` or `mindIds` (`mindId` takes precedence). UUID case variants count as one ID; `data.added` includes IDs that were already members. The owner-only write rechecks ownership, Mind access, and remaining capacity in one transaction. Concurrent additions cannot both consume the last slot; a rejected batch adds nothing. Responses are private and non-cacheable, with generic `500` errors for unexpected storage or plan-resolution failures.

Member removal is owner-only and atomically deletes the Audience membership plus that Mind’s subgroup assignments on the same Audience. It preserves the Mind itself and other Audiences. Ownership is rechecked within the write; transaction failure preserves both membership and assignments. Success is an empty `204`, even when membership was already absent. Missing/unowned Audiences return `404`, unexpected storage failures a generic `500`; responses are private and non-cacheable.

## Audience validation

Audience Validation checks an Audience against real, published surveys and scores how close its Minds' answers are to the published answers. See [Validate an Audience against real surveys](/docs/api/audiences) for request options, results, costs, and failure codes.

<table>
<thead>
  <tr>
    <th>
      Method
    </th>
    
    <th>
      Path
    </th>
    
    <th>
      Purpose
    </th>
    
    <th>
      MCP equivalent
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/validations
      </code>
    </td>
    
    <td>
      Start a validation against found surveys, 1–5 listed surveys, uploaded survey files, a dataset, or an existing Study run; optional <code>
        requestKey
      </code>
      
       or <code>
        Idempotency-Key
      </code>
      
      ; returns <code>
        202
      </code>
    </td>
    
    <td>
      <code>
        validate_audience
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/validations
      </code>
    </td>
    
    <td>
      List validations newest first with overall validity, included validations, responses, and ready Minds
    </td>
    
    <td>
      <code>
        get_audience_validation
      </code>
      
       without <code>
        batchId
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/validations/batches/{batchId}
      </code>
    </td>
    
    <td>
      Read one validation: status, progress, survey scores and ranges, combined score, question selection, and failure
    </td>
    
    <td>
      <code>
        get_audience_validation
      </code>
      
       with <code>
        batchId
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/validations/{validationId}
      </code>
    </td>
    
    <td>
      Read one survey's scoring with every KPI and question
    </td>
    
    <td>
      Summarized per survey by <code>
        get_audience_validation
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/validations/batches/{batchId}/cancel
      </code>
    </td>
    
    <td>
      Stop a validation while <code>
        canCancel
      </code>
      
       is true
    </td>
    
    <td>
      <code>
        validate_audience
      </code>
      
       with <code>
        action: "cancel"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/validation-benchmarks?audienceId={id}
      </code>
    </td>
    
    <td>
      Preview the surveys that would be used and how they fit
    </td>
    
    <td>
      <code>
        validate_audience
      </code>
      
       finds fitting surveys itself
    </td>
  </tr>
</tbody>
</table>

## Audience Formations

A Formation is a persisted segmentation of one Audience into defined subgroups.

<table>
<thead>
  <tr>
    <th>
      Method
    </th>
    
    <th>
      Path
    </th>
    
    <th>
      Purpose
    </th>
    
    <th>
      MCP equivalent
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/formations
      </code>
    </td>
    
    <td>
      List visible shared/personal Formations with private, non-cacheable responses; unexpected lookup/seeding/storage failures return <code>
        500
      </code>
    </td>
    
    <td>
      <code>
        list_formations
      </code>
      
       or <code>
        manage_formation
      </code>
      
       with <code>
        action: "list"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/formations
      </code>
    </td>
    
    <td>
      Create and compute a Formation
    </td>
    
    <td>
      <code>
        manage_formation
      </code>
      
       with <code>
        action: "create"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/formations/preview
      </code>
    </td>
    
    <td>
      Generate a non-persisted segmentation hypothesis
    </td>
    
    <td>
      <code>
        manage_formation
      </code>
      
       with <code>
        action: "preview"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/formations/{formationId}
      </code>
    </td>
    
    <td>
      Read one Formation
    </td>
    
    <td>
      <code>
        manage_formation
      </code>
      
       with <code>
        action: "get"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        DELETE
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/formations/{formationId}
      </code>
    </td>
    
    <td>
      Delete a confirmed Formation
    </td>
    
    <td>
      <code>
        manage_formation
      </code>
      
       with <code>
        action: "delete"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/audiences/{id}/formations/{formationId}/recompute
      </code>
    </td>
    
    <td>
      Recompute member assignments
    </td>
    
    <td>
      <code>
        manage_formation
      </code>
      
       with <code>
        action: "recompute"
      </code>
    </td>
  </tr>
</tbody>
</table>

Formation listing is paginated: `limit` defaults to 100 (maximum 100), `offset` defaults to 0, and `pagination` returns `total`, `limit`, `offset`, `hasMore` and `nextOffset`. Select auto-dimension winners before counting or paging; timestamp ties use descending IDs. Follow `nextOffset` until `hasMore` is false. Each page is internally consistent, while concurrent changes can shift later offsets. Only offset 0 seeds defaults and checks stalled builds. Recovery checks at most 100 pending builds, oldest first; a failed attempt does not stop other selected attempts or prevent the list response. Reopen offset 0 to retry pending work. Both `list_formations` and `manage_formation` (`action: "list"`) expose the same page controls.

Successful build completion checks for both missing and newly added Audience members. If the classified snapshot no longer matches, the worker requests a retry without replacing the saved buckets. A short database lock closes the gap between that check and committing the ready state. Empty or degenerate failure decisions also require an unchanged population; a cap failure requires the current member count to remain above the limit. Queued execution errors preserve `building` until the last configured attempt; exhaustion records a sanitized failure. The synchronous no-Redis path has one attempt and settles as failed on execution errors.

Incremental classification commits a replacement assignment set for one Mind and one Formation; it does not accumulate the results of earlier classifications. Provider outages leave the saved set untouched. Numeric-balanced dimensions accept additional members only through explicit recomputation.

Formation detail responses are private and non-cacheable. Only documented fields are exposed, and failure reasons are sanitized. Invalid legacy proposals become `{}`, malformed building progress becomes `null`, and ready builds always return `progress: null`. Invalid persisted detail metadata and unexpected storage errors return a generic `500`. Recompute can retain the previous completed subgroups until replacement succeeds, so check `status` before treating assignments as final.

Formation creation accepts at most 64 KiB of JSON and returns private, non-cacheable responses. Its hypothesis must have non-empty text and 2–15 subgroups with unique trimmed IDs and labels; `unanswered`, `__unanswered`, and `__other` are reserved IDs. Invalid input returns `400`, oversized requests `413`, and unexpected storage errors a generic `500`. `name` is optional but must be a string. A user-created split requires two populated primary buckets to become `ready`. Deletion before acknowledgement returns `404`.

Formation delete and recompute responses are private (`Cache-Control: private, no-store`), including refusals. Unexpected database or queue failures return a generic `500`; a missing or non-mutable Formation remains `404`. After an uncertain recompute response, inspect the Formation’s status before retrying.

Preview request contract: body ≤64 KiB; optional `userInput` ≤2000 trimmed characters; optional `priorHypothesis` matching the creation schema (`null` also means no prior proposal). Empty requests use the default prompt. Input refusals are `400` or `413`; storage errors fail before streaming with a generic `500`. Responses prohibit caching. Generated output is validated and stripped to creation fields, including its serialized size. Generation errors use HTTP `500` for JSON and `error` → `done` events for an established NDJSON stream. Client disconnects and stream cancellation propagate an abort signal to the model executor.

The preview endpoint content-negotiates. Use `Accept: application/x-ndjson` for incremental browser progress or `Accept: application/json` for a single agent-friendly hypothesis. Both representations use the same generator. Negotiation honors `q` weights, specific ranges, wildcards and UTF-8 media parameters; explicit rejection cannot be overridden by a less-specific wildcard. No header selects NDJSON. A request that accepts neither available representation receives a private `406` before the model is called.

## Studies, analytics, and exports

<table>
<thead>
  <tr>
    <th>
      Method
    </th>
    
    <th>
      Path
    </th>
    
    <th>
      Purpose
    </th>
    
    <th>
      MCP equivalent
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies
      </code>
    </td>
    
    <td>
      List Studies
    </td>
    
    <td>
      <code>
        list_studies
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies
      </code>
    </td>
    
    <td>
      Create a Study from existing Audiences or inline <code>
        audienceConfigs
      </code>
    </td>
    
    <td>
      <code>
        create_study
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies/{studyId}
      </code>
    </td>
    
    <td>
      Read Study composition and history
    </td>
    
    <td>
      <code>
        get_study_status
      </code>
      
       or <code>
        manage_study
      </code>
      
       with <code>
        action: "get"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        DELETE
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies/{studyId}
      </code>
    </td>
    
    <td>
      Delete a confirmed Study
    </td>
    
    <td>
      <code>
        manage_study
      </code>
      
       with <code>
        action: "delete"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies/{studyId}/ask
      </code>
    </td>
    
    <td>
      Submit one direct question; supports SSE and queued modes
    </td>
    
    <td>
      <code>
        ask_study
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies/{studyId}/analytics
      </code>
    </td>
    
    <td>
      Compute scale, categorical, and qualitative analytics
    </td>
    
    <td>
      <code>
        get_study_analytics
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies/{studyId}/invitations
      </code>
    </td>
    
    <td>
      Invite Study collaborators without exposing invitation tokens
    </td>
    
    <td>
      <code>
        manage_study
      </code>
      
       with <code>
        action: "invite"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies/{studyId}/export
      </code>
    </td>
    
    <td>
      Start an export in a supported format
    </td>
    
    <td>
      <code>
        export_study
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies/{studyId}/export-status
      </code>
    </td>
    
    <td>
      Poll export status
    </td>
    
    <td>
      <code>
        export_study
      </code>
      
       / <code>
        get_study_status
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies/{studyId}/export-download
      </code>
    </td>
    
    <td>
      Download a completed export
    </td>
    
    <td>
      <code>
        export_study
      </code>
      
       returns an artifact or link
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies/{studyId}/heatmaps/{messageId}/export
      </code>
    </td>
    
    <td>
      Start or return a website-heatmap ZIP export
    </td>
    
    <td>
      <code>
        export_heatmap
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies/{studyId}/heatmaps/{messageId}/export-status
      </code>
    </td>
    
    <td>
      Poll a website-heatmap ZIP export
    </td>
    
    <td>
      <code>
        export_heatmap
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies/{studyId}/heatmaps/{messageId}/export-download
      </code>
    </td>
    
    <td>
      Download a completed website-heatmap ZIP
    </td>
    
    <td>
      <code>
        export_heatmap
      </code>
      
       returns a signed URL
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies/{studyId}/summary
      </code>
    </td>
    
    <td>
      Read the persisted flexible semantic summary
    </td>
    
    <td>
      <code>
        get_study_summary
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies/{studyId}/summary
      </code>
    </td>
    
    <td>
      Generate or refresh the semantic summary
    </td>
    
    <td>
      <code>
        get_study_summary
      </code>
      
       with <code>
        refresh: true
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        PUT
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies/{studyId}/sharing
      </code>
    </td>
    
    <td>
      Enable or disable the public Study link
    </td>
    
    <td>
      <code>
        manage_study
      </code>
      
       with <code>
        action: "set_link_sharing"
      </code>
    </td>
  </tr>
</tbody>
</table>

Composite creation through `audienceConfigs` is atomic: if any inline Audience fails, the operation rolls back the Study and Audiences created by that request. The legacy `groupIds` and `groupConfigs` request fields remain accepted on deprecated routes; canonical Study routes use `audienceIds` and `audienceConfigs` and return `studyMode: "study"` with an `audiences` collection.

## Guided Studies and durable runs

For any broader objective or more than one question, plan and execute one cohesive multi-question block. Related questions should be organized into named modules or sections and run together; reserve the direct ask endpoint for a genuinely standalone question.

<table>
<thead>
  <tr>
    <th>
      Method
    </th>
    
    <th>
      Path
    </th>
    
    <th>
      Purpose
    </th>
    
    <th>
      MCP equivalent
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies/{studyId}/research-plans/preview
      </code>
    </td>
    
    <td>
      Create or revise a versioned research-plan draft
    </td>
    
    <td>
      <code>
        plan_study_questions
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies/{studyId}/research-runs
      </code>
    </td>
    
    <td>
      Confirm and run the exact reviewed plan revision
    </td>
    
    <td>
      <code>
        run_study_questions
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies/{studyId}/research-runs/{runId}
      </code>
    </td>
    
    <td>
      Poll Study progress, methods, artifacts, and calculations
    </td>
    
    <td>
      <code>
        get_study_run
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies/{studyId}/runs
      </code>
    </td>
    
    <td>
      Start a durable direct Study run
    </td>
    
    <td>
      <code>
        ask_study
      </code>
      
       for direct questions or <code>
        run_study_questions
      </code>
      
       for planned research
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/studies/{studyId}/runs
      </code>
    </td>
    
    <td>
      List durable runs for a Study
    </td>
    
    <td>
      <code>
        get_study_status
      </code>
      
       / <code>
        get_study_run
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/runs/{runId}
      </code>
    </td>
    
    <td>
      Read durable run status
    </td>
    
    <td>
      <code>
        get_study_status
      </code>
      
       / <code>
        get_study_run
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/runs/{runId}/events
      </code>
    </td>
    
    <td>
      Read ordered run events using <code>
        after
      </code>
      
       and <code>
        limit
      </code>
      
       cursors
    </td>
    
    <td>
      MCP status tools return durable progress
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/runs/{runId}/cancel
      </code>
    </td>
    
    <td>
      Cancel a running durable job
    </td>
    
    <td>
      No dedicated curated MCP tool
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/model-connections
      </code>
    </td>
    
    <td>
      Page active team model connections and verified capability flags; pass <code>
        pagination.nextCursor
      </code>
      
       as <code>
        cursor
      </code>
    </td>
    
    <td>
      <code>
        list_model_connections
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/research-methods
      </code>
    </td>
    
    <td>
      List versioned methods, execution support, requirements, and fallbacks
    </td>
    
    <td>
      <code>
        list_research_methods
      </code>
    </td>
  </tr>
</tbody>
</table>

Creating a plan does not start research. Start only after the user explicitly confirms the exact returned revision. `executable: false` means a represented method is informational or planned, not runnable.

MCP presentation tools read the same owned state without starting new research. `render_audience_review` reads operation progress for one Audience or a batch. `render_study_plan` reads the saved draft through the research-plan service and rejects a stale revision. `render_study_results` requires one completed question or run with complete response coverage; queued, running, partial and failed results are refused. Poll the existing operation or run first, then show one final widget. Routed `not_asked` items are settled without fabricated responses, preserving the original questionnaire count and wording. See the [MCP tool schemas and review flow](/mcp/tools).

### Study templates

Template responses are private and non-cacheable. Save and update accept JSON bodies up to 1 MiB; use accepts up to 4 KiB. Oversized requests return `413`, while malformed JSON or invalid configuration returns `400`. The configuration itself is limited to 512 KiB. Expected ownership, revision and file refusals retain their documented statuses; unexpected storage/provider failures return a generic `500`.

<table>
<thead>
  <tr>
    <th>
      Method
    </th>
    
    <th>
      Path
    </th>
    
    <th>
      Purpose
    </th>
    
    <th>
      MCP equivalent
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/study-templates
      </code>
    </td>
    
    <td>
      List personal and team-shared templates
    </td>
    
    <td>
      <code>
        list_study_templates
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/study-templates/{id}
      </code>
    </td>
    
    <td>
      Read a visible template and its revision
    </td>
    
    <td>
      <code>
        list_study_templates
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/study-templates
      </code>
    </td>
    
    <td>
      Save a template; preserve <code>
        requestId
      </code>
      
       for retries
    </td>
    
    <td>
      <code>
        manage_study_template
      </code>
      
       (<code>
        save
      </code>
      
      )
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        PATCH
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/study-templates/{id}
      </code>
    </td>
    
    <td>
      Update an owned template using <code>
        expectedRevision
      </code>
    </td>
    
    <td>
      <code>
        manage_study_template
      </code>
      
       (<code>
        update
      </code>
      
      )
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        DELETE
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/study-templates/{id}
      </code>
    </td>
    
    <td>
      Delete an owned template
    </td>
    
    <td>
      <code>
        manage_study_template
      </code>
      
       (<code>
        delete
      </code>
      
      )
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/study-templates/{id}/use
      </code>
    </td>
    
    <td>
      Create an independent app-editable draft using <code>
        expectedRevision
      </code>
      
       and <code>
        requestId
      </code>
      
      ; retries return the existing draft
    </td>
    
    <td>
      <code>
        manage_study_template
      </code>
      
       (<code>
        use
      </code>
      
      )
    </td>
  </tr>
</tbody>
</table>

## Durable Study drafts

These endpoints persist the same resumable planner state used by the first-party Study sidebar.

<table>
<thead>
  <tr>
    <th>
      Method
    </th>
    
    <th>
      Path
    </th>
    
    <th>
      Purpose
    </th>
    
    <th>
      MCP equivalent
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/study-drafts
      </code>
    </td>
    
    <td>
      List owned resumable drafts
    </td>
    
    <td>
      <code>
        list_study_drafts
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/study-drafts
      </code>
    </td>
    
    <td>
      Create a durable draft
    </td>
    
    <td>
      <code>
        save_study_draft
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/study-drafts/{id}
      </code>
    </td>
    
    <td>
      Read one complete draft
    </td>
    
    <td>
      <code>
        list_study_drafts
      </code>
      
       with <code>
        draftId
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        PATCH
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/study-drafts/{id}
      </code>
    </td>
    
    <td>
      Revise a draft using optimistic concurrency
    </td>
    
    <td>
      <code>
        save_study_draft
      </code>
      
       with <code>
        draftId
      </code>
      
       and <code>
        expectedRevision
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        DELETE
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/study-drafts/{id}
      </code>
    </td>
    
    <td>
      Delete a confirmed draft
    </td>
    
    <td>
      <code>
        manage_study_draft
      </code>
      
       with <code>
        action: "delete"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/study-drafts/{id}/consume
      </code>
    </td>
    
    <td>
      Mark a precise revision consumed and optionally attach it to a Study
    </td>
    
    <td>
      <code>
        manage_study_draft
      </code>
      
       with <code>
        action: "consume"
      </code>
    </td>
  </tr>
</tbody>
</table>

Draft responses use `Cache-Control: private, no-store`. Creation accepts JSON requests up to 1 MiB; update accepts up to 2 MiB so it can carry both an edited checkpoint and its base. Each versioned `payload`, including `checkpointBase.payload`, has a separate 512 KiB limit. Consume requests are limited to 4 KiB. Malformed JSON or invalid fields return `400`, oversized input returns `413`, and unexpected storage failures return a generic `500`. A changed snapshot increments the revision and returns the snapshot written by that update, even if another writer immediately saves a newer revision. An identical save, including acknowledgement of a new `checkpointId`, preserves its `revision` and `updatedAt`.

`POST /api/v1/study-drafts` accepts `idempotencyKey`: an optional string whose trimmed length is 1–200 characters. A matching key resolves to that caller’s previously saved draft, so a retry does not apply a replacement payload. Clients that omit this field have no key-based protection against duplicate creation. To edit an existing draft, send `PATCH /api/v1/study-drafts/{id}` with its latest `expectedRevision` in the JSON body; identify the draft in the URL rather than sending the MCP-only `draftId` argument.

For strict optimistic concurrency, send the exact `expectedRevision` returned by the last read and omit `checkpointBase`. A stale changed snapshot returns `409`; replaying a snapshot that already matches the saved state succeeds.

For concurrent editors or an offline checkpoint queue, send these optional fields on the same PATCH request (or `save_study_draft` call):

<table>
<thead>
  <tr>
    <th>
      Field
    </th>
    
    <th>
      Contract
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        checkpointBase
      </code>
    </td>
    
    <td>
      The complete checkpoint the editor observed before its edit: <code>
        { name, mode, currentStep, payload }
      </code>
      
      . Keep this base unchanged on retries.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        checkpointId
      </code>
    </td>
    
    <td>
      A UUID identifying this exact save operation. Generate it once and reuse it after a lost response or reconnect. Use a new UUID for a new authored edit.
    </td>
  </tr>
</tbody>
</table>

With `checkpointBase`, the server reconciles changes to independent object fields against the latest saved state. Arrays and strings are whole values. Competing changes to the same field follow commit order; displaced alternatives are retained privately and are not included in draft responses. A repeated `checkpointId` acknowledges the existing operation and cannot reapply its old snapshot over later edits. Preserve authored save order when replaying a client queue; a network retry does not authorize replacing the original base with a newer read. Use the returned revision for the next new edit.

Consuming an already consumed draft returns its existing tombstone unchanged, including its linked Study. A late base-aware checkpoint is retained privately and acknowledges the consumed draft without reopening it or changing the accepted Study. Consume closes planning state; it does not start or verify research.

## Stateful chats

<table>
<thead>
  <tr>
    <th>
      Method
    </th>
    
    <th>
      Path
    </th>
    
    <th>
      Purpose
    </th>
    
    <th>
      MCP equivalent
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/chats
      </code>
    </td>
    
    <td>
      Create a stateful single-Mind, multi-Mind, or Audience-backed chat
    </td>
    
    <td>
      <code>
        manage_chat
      </code>
      
       with <code>
        action: "create"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/chats/{chatId}/messages
      </code>
    </td>
    
    <td>
      Append a user/system message and receive the next response
    </td>
    
    <td>
      <code>
        manage_chat
      </code>
      
       with <code>
        action: "send_message"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        DELETE
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/chats/{chatId}
      </code>
    </td>
    
    <td>
      Delete a confirmed chat and its persisted history
    </td>
    
    <td>
      <code>
        manage_chat
      </code>
      
       with <code>
        action: "delete"
      </code>
    </td>
  </tr>
</tbody>
</table>

For a one-off Mind request without server-managed history, use the stateless completion endpoint instead.

## Choosing the next reference

- Request and response examples: [Minds](/docs/api/minds), [Audiences](/docs/api/audiences), [Studies](/docs/api/studies), [Knowledge](/docs/api/knowledge), and [Chat](/docs/api/chat)
- Authentication and key rotation: [Authentication](/docs/api/authentication)
- Errors, plan limits, and retry policy: [Errors & Limits](/docs/api/errors)
- Autonomous integrations: [API guide for agents](/docs/api/agents)
- Tool-native integrations: [MCP tools reference](/mcp/tools)

## Stripe billing for agents

<table>
<thead>
  <tr>
    <th>
      Method
    </th>
    
    <th>
      Path
    </th>
    
    <th>
      API / MCP
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        GET
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/billing/catalog
      </code>
    </td>
    
    <td>
      Read current subscription prices, response-credit packs and eligibility; <code>
        get_billing_catalog
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/billing/checkout
      </code>
    </td>
    
    <td>
      Start hosted checkout; buyer completes consent and payment; <code>
        start_billing_checkout
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        POST
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/billing/invoices/{invoiceId}/pay
      </code>
    </td>
    
    <td>
      Negotiate an eligible invoice payment over HTTP; MPP / x402
    </td>
  </tr>
</tbody>
</table>

ACP REST resources use the discovery document's `api_base_url` and API version `2026-04-17`:

<table>
<thead>
  <tr>
    <th>
      Method
    </th>
    
    <th>
      Resource
    </th>
    
    <th>
      Meaning
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      POST
    </td>
    
    <td>
      <code>
        /checkout_sessions
      </code>
    </td>
    
    <td>
      Create one hosted subscription or credit-pack session
    </td>
  </tr>
  
  <tr>
    <td>
      GET
    </td>
    
    <td>
      <code>
        /checkout_sessions/{id}
      </code>
    </td>
    
    <td>
      Read an owned session
    </td>
  </tr>
  
  <tr>
    <td>
      POST
    </td>
    
    <td>
      <code>
        /checkout_sessions/{id}
      </code>
    </td>
    
    <td>
      Refresh an immutable session with <code>
        {}
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      POST
    </td>
    
    <td>
      <code>
        /checkout_sessions/{id}/complete
      </code>
    </td>
    
    <td>
      Read confirmation state with <code>
        {}
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      POST
    </td>
    
    <td>
      <code>
        /checkout_sessions/{id}/cancel
      </code>
    </td>
    
    <td>
      Expire an open session
    </td>
  </tr>
</tbody>
</table>

Creation requires `line_items: [{ id: "price_…" }]`, `currency` and `capabilities: {}`. Optional metadata holds `billingCountry`, Team `quantity` and subscription `legalAcceptance`. The `API-Version` header may be omitted; if supplied it must match. All POST actions require `Idempotency-Key`. Buyer interaction happens at the returned `continue_url`; the adapter reports Stripe's state rather than collecting card details.

The OpenAPI payment offers describe dynamic invoice pricing with `amount: null`. The runtime challenge supplies the quote. Consult the [agent integration guide](/docs/api/agents) for authentication headers and uncertain-payment handling.

## Custom credit checkout fields

The credit branch of `/api/v1/billing/checkout` accepts an optional `amountMinor` budget. When omitted, `priceId` identifies an unchanged preset or a reusable purchased pack.

<table>
<thead>
  <tr>
    <th>
      Field
    </th>
    
    <th>
      Custom purchase contract
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        priceId
      </code>
    </td>
    
    <td>
      Current <code>
        responseCredits.customTopUp.priceId
      </code>
      
       from the catalog
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        amountMinor
      </code>
    </td>
    
    <td>
      Positive integer budget in cents; at least <code>
        customTopUp.unitAmount
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        billingCountry
      </code>
    </td>
    
    <td>
      Country used to read the catalog for a new workspace
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        Idempotency-Key
      </code>
    </td>
    
    <td>
      One purchase identifier; changing the amount under this key returns a conflict
    </td>
  </tr>
</tbody>
</table>

The server determines response units and the Stripe Price; callers cannot provide their own unit rate or granted balance. Checkout creation returns the usual hosted URL and session ID without granting access.
