---
title: "Minds API Integration Guide for AI Agents"
description: "Agent-oriented guide to discovering, authenticating, safely calling, retrying, polling, and presenting Minds v1 API workflows."
---

# Minds API Integration Guide for AI Agents

This guide is the operating contract for an AI agent that calls the Minds v1 REST API. It complements the [OpenAPI document](/api/openapi), [complete endpoint catalog](/api/reference), and domain walkthroughs.

## Choose REST or MCP

<table>
<thead>
  <tr>
    <th>
      Need
    </th>
    
    <th>
      Prefer
    </th>
    
    <th>
      Why
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Generate a typed SDK, run a backend integration, or control HTTP details
    </td>
    
    <td>
      v1 REST API
    </td>
    
    <td>
      Stable resource URLs, response envelopes, streaming, and durable job endpoints
    </td>
  </tr>
  
  <tr>
    <td>
      Let ChatGPT, Claude, Cursor, or another compatible assistant select research tools
    </td>
    
    <td>
      MCP
    </td>
    
    <td>
      Protocol-native tool schemas, annotations, OAuth, widgets, and presentation contracts
    </td>
  </tr>
  
  <tr>
    <td>
      Build a long-running service that resumes after process restarts
    </td>
    
    <td>
      v1 REST API
    </td>
    
    <td>
      Persist run, Study, draft, and export IDs in your own job state
    </td>
  </tr>
  
  <tr>
    <td>
      Run research interactively from a user conversation
    </td>
    
    <td>
      MCP
    </td>
    
    <td>
      Tool descriptions encode routing, confirmation, and safe presentation behavior
    </td>
  </tr>
</tbody>
</table>

Both surfaces call the same v1 application boundaries for supported research actions. Do not combine REST and MCP in the same logical operation unless you have an explicit reason and a stable resource ID.

## Discovery checklist

Read these resources before generating code or planning calls:

1. `https://getminds.ai/_openapi.json` — live OpenAPI 3.1 document for routes with detailed operation metadata.
2. `https://getminds.ai/api/reference` — complete v1 route catalog, including routes whose detailed OpenAPI schemas are still being expanded.
3. `https://getminds.ai/api/errors` — error envelope, plan limits, and retry behavior.
4. `https://getminds.ai/llms.txt` and `https://getminds.ai/llms-full.txt` — agent-oriented documentation discovery and consolidated reference text.
5. `https://getminds.ai/mcp/agents` — use this instead when your host supports MCP tools.

Do not infer that a route is unavailable merely because it is not yet present in the generated OpenAPI document. The endpoint catalog is the completeness authority; the OpenAPI document is the schema authority for operations it includes.

## Authentication and secret handling

Send a personal Minds API key as a bearer token:

```http
Authorization: Bearer minds_…
```

Agent rules:

- Read the key from a secret manager or environment variable. Never place it in prompts, source code, URLs, logs, traces, or error messages.
- Use separate keys for development and production.
- Treat the value returned by `POST /api/v1/api-keys` as write-only: it is shown once.
- Do not let an autonomous agent rotate or revoke the key it is currently using.
- On `401`, stop and request credential repair. Do not repeatedly retry.
- On `403`, do not disguise an access-policy failure as “not found.” Explain that the authenticated account lacks access.

## Stable request wrapper

Centralize authentication, JSON handling, timeouts, and error parsing:

```ts
const MINDS_BASE_URL = 'https://getminds.ai/api/v1'

type MindsError = {
  statusCode?: number
  statusMessage?: string
  message?: string
  data?: unknown
}

async function mindsRequest<T>(
  path: string,
  init: RequestInit = {},
): Promise<T> {
  const response = await fetch(`${MINDS_BASE_URL}${path}`, {
    ...init,
    headers: {
      Authorization: `Bearer ${process.env.MINDS_API_KEY}`,
      ...(init.body ? { 'Content-Type': 'application/json' } : {}),
      ...init.headers,
    },
    signal: init.signal ?? AbortSignal.timeout(120_000),
  })

  const contentType = response.headers.get('content-type') || ''
  const payload = contentType.includes('application/json')
    ? await response.json()
    : await response.text()

  if (!response.ok) {
    const error = payload as MindsError
    throw new Error(
      `Minds ${response.status}: ${error.message || error.statusMessage || response.statusText}`,
    )
  }

  return payload as T
}
```

Use the apex form (`https://getminds.ai/api/v1`) if your infrastructure does not follow same-origin `307` redirects for API-subdomain `GET` requests.

## Plan before mutating

Classify each proposed call:

<table>
<thead>
  <tr>
    <th>
      Class
    </th>
    
    <th>
      Examples
    </th>
    
    <th>
      Agent behavior
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Read-only
    </td>
    
    <td>
      List/read resources, status, analytics, methods
    </td>
    
    <td>
      May call when needed to answer the request
    </td>
  </tr>
  
  <tr>
    <td>
      Reversible creation/update
    </td>
    
    <td>
      Create a private draft, update a name
    </td>
    
    <td>
      State the intended target; preserve returned IDs
    </td>
  </tr>
  
  <tr>
    <td>
      Costly execution
    </td>
    
    <td>
      Ask an Audience, run a Study, retrain, regenerate
    </td>
    
    <td>
      Ensure it matches the user's request and avoid duplicate calls
    </td>
  </tr>
  
  <tr>
    <td>
      Public exposure
    </td>
    
    <td>
      Enable link sharing
    </td>
    
    <td>
      Require explicit intent; explain what becomes public
    </td>
  </tr>
  
  <tr>
    <td>
      Destructive
    </td>
    
    <td>
      Delete Minds, Audiences, Studies, chats, Formations, drafts
    </td>
    
    <td>
      Obtain explicit confirmation of exact IDs immediately before the call
    </td>
  </tr>
  
  <tr>
    <td>
      Credential control
    </td>
    
    <td>
      Mint/revoke API keys
    </td>
    
    <td>
      Keep outside autonomous research flows
    </td>
  </tr>
</tbody>
</table>

Never enable `isLinkSharingEnabled` merely to make a result convenient to access. Private is the default.

## Use IDs as durable state

Names are suitable for user interaction, but IDs are the integration contract. Persist:

- `sparkId` after Mind creation;
- `audienceId` after Audience creation;
- `studyId` after Study creation;
- `draftPlanId` and `revision` during research planning;
- `studyId` or `runId` during execution;
- `jobId` for exports;
- `itemId` for knowledge ingestion;
- `draftId` and `expectedRevision` for sidebar Study drafts.

Do not re-list by fuzzy name after a successful create response. Doing so can select the wrong resource when names are duplicated.

## Asynchronous operations

Several operations return before work is complete.

<table>
<thead>
  <tr>
    <th>
      Start operation
    </th>
    
    <th>
      Persist
    </th>
    
    <th>
      Poll/read
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Create Mind
    </td>
    
    <td>
      <code>
        sparkId
      </code>
    </td>
    
    <td>
      <code>
        GET /minds/{mindId}/training
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Create grounded Audience
    </td>
    
    <td>
      <code>
        audienceId
      </code>
    </td>
    
    <td>
      <code>
        GET /audiences/{id}/progress
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Add file/link knowledge
    </td>
    
    <td>
      <code>
        itemId
      </code>
    </td>
    
    <td>
      <code>
        GET /minds/{mindId}/knowledge/{itemId}/status
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Ask a Study in queued mode
    </td>
    
    <td>
      <code>
        runId
      </code>
    </td>
    
    <td>
      <code>
        GET /runs/{runId}
      </code>
      
       and <code>
        /events
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Run confirmed multi-question block
    </td>
    
    <td>
      <code>
        runId
      </code>
    </td>
    
    <td>
      <code>
        GET /studies/{studyId}/research-runs/{runId}
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Export
    </td>
    
    <td>
      <code>
        jobId
      </code>
    </td>
    
    <td>
      entity-specific <code>
        export-status
      </code>
      
      , then <code>
        export-download
      </code>
    </td>
  </tr>
</tbody>
</table>

Recommended polling behavior:

1. Honor `Retry-After` when returned.
2. Otherwise start at 1–2 seconds and exponentially back off to 10–15 seconds.
3. Add jitter when many jobs run concurrently.
4. Persist the last event cursor for `/runs/{runId}/events`.
5. Stop on terminal success, failure, cancellation, or `plan_limited`.
6. Set a workflow-level deadline; a client timeout does not prove the server operation failed.

## Idempotency and retries

- Retry `GET` requests after transient `429`, `502`, `503`, or `504` responses using bounded exponential backoff.
- Retry a mutation only when the endpoint documents idempotency or you supplied a stable idempotency key.
- `create_audience_from_brief` derives an idempotency key from its arguments. Retry with exactly the same arguments after a timeout to recover the original Audience.
- `run_study_questions` supports an explicit stable idempotency key through MCP; REST callers should preserve the confirmed draft ID/revision and returned Study ID.
- Optimistic-concurrency failures on Study drafts require a fresh read and human/agent reconciliation. Do not overwrite with a guessed revision.
- Never retry destructive calls merely because the client did not receive the response. Read the resource first to determine whether deletion already succeeded.

## Direct questions versus guided Studies

Use this routing table:

<table>
<thead>
  <tr>
    <th>
      User request
    </th>
    
    <th>
      Route
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      One direct question to an existing Study
    </td>
    
    <td>
      <code>
        POST /studies/{studyId}/ask
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Knowledge-only durable direct run
    </td>
    
    <td>
      <code>
        POST /studies/{studyId}/runs
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Multiple questions, a broad objective, an asset audit, structured evidence outputs, or an explicit method
    </td>
    
    <td>
      Plan → confirm → Study lifecycle
    </td>
  </tr>
  
  <tr>
    <td>
      Export existing evidence
    </td>
    
    <td>
      <code>
        POST /studies/{studyId}/export
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Show existing evidence differently
    </td>
    
    <td>
      Read status/summary/analytics; do not recruit a new run
    </td>
  </tr>
</tbody>
</table>

The guided lifecycle is:

```text
POST research-plans/preview
  -> present the exact draft and every confirmation question
  -> revise the same draft if the user changes or answers anything
  -> receive explicit confirmation of the exact revision
  -> POST studies with that draft ID and revision
  -> poll GET studies/{studyId}
  -> GET or POST summary
```

Planning is non-executing. Never claim that research has started after a preview call.

## Method availability

Call `GET /api/v1/research-methods` before promising a named method. Treat fields as follows:

- `executable: true` — the registered server adapter can run the method.
- `executable: false` — the method may be represented or planned but is not an execution promise.
- Advanced methods require explicit opt-in at execution time.
- Preserve deterministic calculations returned by the Study endpoint; do not recompute them with an LLM.

## File and URL inputs

- Upload interactive browser files with multipart form data.
- For autonomous agents, prefer public, short-lived signed, or Minds workspace-upload URLs.
- Do not embed large binary files as base64 JSON.
- Give every file a meaningful `name` and MIME `type` when known.
- URLs may be rejected by SSRF and redirect guards even when they are syntactically valid.
- Knowledge retrieval is bounded to 50 MB and a server timeout; split larger sources before ingestion.
- For respondent datasets, preview segmentation before creating a representative cohort.

## Error decisions

<table>
<thead>
  <tr>
    <th>
      Signal
    </th>
    
    <th>
      Meaning
    </th>
    
    <th>
      Agent response
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        400
      </code>
      
       / validation details
    </td>
    
    <td>
      Request does not satisfy the schema
    </td>
    
    <td>
      Correct the payload; do not retry unchanged
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        401
      </code>
    </td>
    
    <td>
      Missing/invalid/expired credential
    </td>
    
    <td>
      Stop and repair authentication
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        403
      </code>
    </td>
    
    <td>
      Authenticated but not allowed
    </td>
    
    <td>
      Explain access/ownership/plan boundary
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        404
      </code>
    </td>
    
    <td>
      Resource is absent or not visible
    </td>
    
    <td>
      Verify the persisted ID and account context
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        409
      </code>
    </td>
    
    <td>
      Revision/idempotency/state conflict
    </td>
    
    <td>
      Read current state and reconcile
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        415
      </code>
    </td>
    
    <td>
      Unsupported request media type
    </td>
    
    <td>
      Correct <code>
        Content-Type
      </code>
      
       or file form
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        429
      </code>
    </td>
    
    <td>
      Rate or usage limit
    </td>
    
    <td>
      Honor <code>
        Retry-After
      </code>
      
      ; distinguish rate limiting from <code>
        plan_limited
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        plan_limited
      </code>
      
       before execution
    </td>
    
    <td>
      Nothing started
    </td>
    
    <td>
      Explain the required plan/allowance change
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        status: plan_limited
      </code>
      
       during a Study
    </td>
    
    <td>
      Partial artifacts were preserved
    </td>
    
    <td>
      Report completed versus remaining questions; never call it complete
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        5xx
      </code>
    </td>
    
    <td>
      Transient or server failure
    </td>
    
    <td>
      Bounded retry for safe/idempotent calls, otherwise read state first
    </td>
  </tr>
</tbody>
</table>

## Result presentation

- Preserve citations, source URLs, shared links, download links, and resource IDs exactly.
- Clearly label synthetic responses and distinguish them from primary human research.
- Report status as queued/running/partial/completed/failed based on returned fields, not elapsed time.
- For categorical or multiselect results, preserve the server's aggregation semantics instead of forcing percentages to sum to 100 when multiple selections are allowed.
- Do not invent missing Mind answers or fill incomplete Study artifacts.
- Use the authenticated workspace link for the owner and a returned shared link only for external handoff.

## Minimal autonomous Study example

```ts
const studies = await mindsRequest<{ data: Array<{ id: string; name: string }> }>(
  '/studies',
)

const study = studies.data.find(item => item.name === 'Launch research')
if (!study) throw new Error('Study not found')

const started = await mindsRequest<{ data: { runId: string } }>(
  `/studies/${study.id}/runs`,
  {
    method: 'POST',
    body: JSON.stringify({
      question: 'What are the strongest objections to this positioning?',
      sourcePolicy: 'knowledge_only',
    }),
  },
)

// Persist started.data.runId before polling.
```

## Preflight checklist

Before a mutation, verify:

- the authenticated workspace and plan context;
- exact resource IDs and ownership/access;
- whether the request is direct research or requires a confirmed plan;
- whether sharing remains private;
- whether a previous timed-out call may already have succeeded;
- whether the operation consumes an allowance or triggers generation;
- whether explicit confirmation is required.

After a mutation, persist returned IDs, poll the documented status endpoint, and present only terminal or explicitly partial results.
