API Reference

Continua AI API documentation

Create document-based responses, retrieve results asynchronously, and continue a completed response with follow-up questions. Call the API directly over HTTPS, or use the OpenAI Python or JavaScript client library with a custom base URL.

Overview

Only the endpoints and parameters documented here are supported. All base paths are relative to the API base URL.

Base URL
https://app.askcontinua.com/api/v1

Authentication

Every request requires a Continua AI API token in the Authorization header.

HTTP
Authorization: Bearer YOUR_CONTINUA_AI_API_TOKEN

Keep API tokens on your server. Do not expose them in browser code, mobile applications, public repositories, or client-side logs.

Bash
export CONTINUA_AI_API_KEY="YOUR_CONTINUA_AI_API_TOKEN"
export CONTINUA_AI_API_BASE_URL="https://app.askcontinua.com/api/v1"

How requests work

All responses are created asynchronously.

  • Upload one or more files and retain each returned file_id.
  • Create a response with a prompt and at least one file or project.
  • Poll the response until its status becomes completed, failed, or cancelled.
  • Read the generated answer from output_text.
  • To continue the conversation, create another response with previous_response_id.

Streaming is not currently supported. Set background to true and do not send stream: true.

Quickstart

Install the client library with python -m pip install openai or npm install openai. The first response must include at least one uploaded file or a Continua AI project.

Python

Python
import os
import time
from uuid import uuid4

from openai import OpenAI

client = OpenAI(
    api_key=os.environ["CONTINUA_AI_API_KEY"],
    base_url=os.environ["CONTINUA_AI_API_BASE_URL"].rstrip("/"),
)

uploaded_file = client.files.create(
    file=("hello.txt", b"Hello world.", "text/plain"),
    purpose="user_data",
)

response = client.responses.create(
    model="deepseek-flash",
    background=True,
    input=[
        {
            "role": "user",
            "content": [
                {"type": "input_file", "file_id": uploaded_file.id},
                {"type": "input_text", "text": "Summarize this file in one sentence."},
            ],
        }
    ],
    extra_headers={"Idempotency-Key": f"response-{uuid4().hex}"},
)

terminal_statuses = {"completed", "failed", "cancelled"}

while response.status not in terminal_statuses:
    time.sleep(5)
    response = client.responses.retrieve(response.id)

if response.status != "completed":
    raise RuntimeError(f"Response ended with status={response.status}")

print(response.output_text)

Node.js

TypeScript
import { randomUUID } from "node:crypto";
import OpenAI, { toFile } from "openai";

const client = new OpenAI({
  apiKey: process.env.CONTINUA_AI_API_KEY!,
  baseURL: process.env.CONTINUA_AI_API_BASE_URL!.replace(/\/$/, ""),
});

const uploadedFile = await client.files.create({
  file: await toFile(Buffer.from("Hello world."), "hello.txt", { type: "text/plain" }),
  purpose: "user_data",
});

let response = await client.responses.create(
  {
    model: "deepseek-flash",
    background: true,
    input: [
      {
        role: "user",
        content: [
          { type: "input_file", file_id: uploadedFile.id },
          { type: "input_text", text: "Summarize this file in one sentence." },
        ],
      },
    ],
  },
  { headers: { "Idempotency-Key": `response-${randomUUID()}` } },
);

const terminalStatuses = new Set(["completed", "failed", "cancelled"]);

while (!terminalStatuses.has(response.status)) {
  await new Promise((resolve) => setTimeout(resolve, 5000));
  response = await client.responses.retrieve(response.id);
}

if (response.status !== "completed") {
  throw new Error(`Response ended with status=${response.status}`);
}

console.log(response.output_text);

Models

Use a full model identifier in every create request, or auto to let Continua choose the model.

ModelModel identifierBest suited for
AutoautoContinua picks the best model for the task
DeepSeek Flashdeepseek-flashFast, everyday analysis
DeepSeek Prodeepseek-proDeeper reasoning
GLM Flashglm-flashFast, cost-efficient work
GLMglmBalanced performance
GPT Lunagpt-lunaFast, cost-efficient work
GPT Terragpt-terraBalanced performance
GPT Solgpt-solAdvanced reasoning
GPT Astragpt-astraMost capable reasoning
Claude Haikuclaude-haikuFast responses
Claude Sonnetclaude-sonnetBalanced speed and depth
Claude Opusclaude-opusDeep analysis
Claude Fableclaude-fableFrontier reasoning
Gemini Flashgemini-flashFast multimodal work
Gemini Progemini-proLong-context work
GrokgrokGeneral-purpose reasoning
KimikimiLong-context analysis

Model access may depend on your account plan. See pricing for plan information and benchmarks for published evaluations on long-document investigations.

Upload a file

Uploads one file for use in a response.

POST/files

Form fields

FieldTypeRequiredDescription
fileFileYesOne file. Send one file per upload request.
purposeStringYesMust be user_data.

Supported filename extensions

Text
.txt, .md, .markdown, .json, .pdf, .docx, .xlsx, .csv,
.eml, .msg, .pptx, .html, .htm, .xhtml

The maximum file size depends on the account plan.

cURL example

Bash
curl --request POST \
  "${CONTINUA_AI_API_BASE_URL}/files" \
  --header "Authorization: Bearer ${CONTINUA_AI_API_KEY}" \
  --form "purpose=user_data" \
  --form "file=@report.pdf"

Response

JSON
{
  "id": "file_1234567890abcdef",
  "object": "file",
  "bytes": 24831,
  "created_at": "2026-10-17 15:42",
  "expires_at": 1789430400,
  "filename": "report.pdf",
  "purpose": "user_data",
  "status": "processed"
}
FieldTypeDescription
idStringFile identifier to use in a response request.
objectStringAlways file.
bytesIntegerFile size in bytes.
created_atStringCreation time as a UTC date-time in YYYY-MM-DD HH:mm format.
expires_atIntegerDeadline for using an unsubmitted upload.
filenameStringOriginal filename.
purposeStringAlways user_data.
statusStringAlways processed after a successful upload.

Files uploaded for an API response are temporary and follow the Zero Document Retention workflow: the documents are deleted after the task completes. Each uploaded file can be used in one response creation request; upload the document again to obtain a new file identifier. Files intentionally added to a Project remain available for reuse. See Trust & Security for the complete retention policy.

Create a response

Creates an asynchronous response.

POST/responses

Send a unique Idempotency-Key header with every new request.

HTTP
Idempotency-Key: response-7c86b96c5a4d48c58bbdb6bca75e1098

Request body

JSON
{
  "model": "deepseek-flash",
  "background": true,
  "input": [
    {
      "role": "user",
      "content": [
        { "type": "input_file", "file_id": "file_1234567890abcdef" },
        { "type": "input_text", "text": "Summarize the main risks in this document." }
      ]
    }
  ]
}

Parameters

ParameterTypeRequiredDescription
modelStringYesA full model identifier, or auto.
backgroundBooleanRecommendedMust be true when provided. Responses always run asynchronously.
inputString or arrayYesThe user's request.
previous_response_idStringNoIdentifier of a completed response to continue.
reportObjectNoOptional report configuration.
toolsArrayNoWeb search and connected MCP plugins, including the Continua Agent plugin for Agent mode. See Web search and tools.
tool_choiceStringNoUse auto (default) to allow tools or none to disable them.

Only the parameters documented here affect a response. Omit other OpenAI Responses API parameters. In particular, streaming is unavailable.

Input rules

  • input must contain exactly one message.
  • The message role must be user.
  • content must contain exactly one non-empty input_text item.
  • content may contain up to 200 unique input_file items.
  • Each input_file must reference a valid, unexpired file identifier owned by the same account.
  • For the first response, provide at least one input_file, a project identifier in report.project_ids, or an enabled tool such as web search or the Continua Agent plugin.

For a follow-up response, input can be a string and no new file or project is required.

JSON
{
  "model": "deepseek-flash",
  "background": true,
  "previous_response_id": "resp_1234567890abcdef",
  "input": "Which of these risks should we address first?"
}

Report configuration

ParameterTypeRequiredDescription
project_idsArray of stringsNoUnique Continua AI project identifiers to include as sources.
findings_criteriaStringNoCriteria used to select relevant findings.
composition_criteriaStringNoInstructions for organizing and presenting the response.
guardrail_modeStringNoValidation level: off, lite, or strict.

Each referenced project must belong to the authenticated account and contain at least one document that has finished processing.

Python
from uuid import uuid4

response = client.responses.create(
    model="deepseek-flash",
    background=True,
    input="Identify the most material risks in this project.",
    extra_body={
        "report": {
            "project_ids": ["project_1234567890abcdef"],
            "findings_criteria": "Material risks supported by source evidence",
            "composition_criteria": "Present a concise, prioritized report",
            "guardrail_mode": "lite",
        }
    },
    extra_headers={"Idempotency-Key": f"project-{uuid4().hex}"},
)

Idempotency

An idempotency key must contain 1 to 255 visible ASCII characters. Use a new key for every new response, and reuse the same key only when retrying an identical request.

Create response payload

JSON
{
  "id": "resp_1234567890abcdef",
  "object": "response",
  "created_at": "2026-10-17 15:42",
  "status": "queued",
  "previous_response_id": null,
  "background": true,
  "output_text": "",
  "output": [],
  "error": null
}

The create response is not the final result. Save id and retrieve the response until it reaches a terminal status.

Retrieve a response

Retrieves the current state and result of a response.

GET/responses/{response_id}
Bash
curl \
  "${CONTINUA_AI_API_BASE_URL}/responses/${RESPONSE_ID}" \
  --header "Authorization: Bearer ${CONTINUA_AI_API_KEY}"

Status values

StatusMeaning
queuedAccepted and waiting to run.
in_progressCurrently running.
completedFinished successfully. The result is available.
failedCould not be completed. See error.
cancelledCancelled before completion.

While a response is not terminal, output_text is empty and output is normally empty. The server returns a Retry-After header; wait at least that many seconds before retrieving the response again.

Completed response

JSON
{
  "id": "resp_1234567890abcdef",
  "object": "response",
  "created_at": "2026-10-17 15:42",
  "status": "completed",
  "previous_response_id": null,
  "background": true,
  "output_text": "The document identifies supplier concentration as its primary risk.",
  "output": [
    {
      "id": "msg_1234567890abcdef",
      "type": "message",
      "role": "assistant",
      "status": "completed",
      "content": [
        {
          "type": "output_text",
          "text": "The document identifies supplier concentration as its primary risk.",
          "annotations": [],
          "logprobs": []
        }
      ]
    }
  ],
  "report": {
    "files": ["findings.json", "risk-memo.md"],
    "billing": {
      "status": "charged",
      "credits": 0.42
    }
  },
  "error": null
}

Response fields

FieldTypeDescription
idStringResponse identifier.
objectStringAlways response.
created_atStringCreation time as a UTC date-time in YYYY-MM-DD HH:mm format.
modelStringModel used for the response.
statusStringCurrent response status.
previous_response_idString or nullParent response identifier for a follow-up.
backgroundBooleanAlways true.
output_textStringComplete generated answer when status is completed.
outputArrayGenerated messages in Responses API format.
reportObjectOutput file names and credits charged, available after completion.
errorObject or nullFailure details when status is failed.

Findings file

Completed responses list their output files in report.files, including findings.json. Download it with GET /responses/{response_id}/files?name=findings.json. Each finding in the file has these fields:

FieldTypeDescription
finding_indexIntegerOne-based finding number.
record_idStringFinding record identifier.
claimStringFinding or conclusion supported by the source.
quoteStringSupporting source text, when available.
documentStringSource document or source URL, when available.
positionStringPage or paragraph location, when available.
relevanceNumberRelevance score from 0 to 1, when available.
confidenceNumberConfidence score from 0 to 1, when available.
evidenceNumberEvidence strength score from 0 to 1, when available.
materialityNumberMateriality score from 0 to 1, when available.

Continue a response

To ask a follow-up question, create another response and set previous_response_id to the latest completed response identifier. Every follow-up creates a new response identifier. The parent response must be completed; a parent that is still queued or in_progress returns HTTP 409 with code response_not_complete.

Python

Python
import time
from uuid import uuid4

follow_up = client.responses.create(
    model="deepseek-flash",
    background=True,
    previous_response_id=response.id,
    input="Give me one concise recommendation based on the findings.",
    extra_headers={"Idempotency-Key": f"follow-up-{uuid4().hex}"},
)

while follow_up.status not in {"completed", "failed", "cancelled"}:
    time.sleep(5)
    follow_up = client.responses.retrieve(follow_up.id)

if follow_up.status != "completed":
    raise RuntimeError(f"Follow-up ended with status={follow_up.status}")

print(follow_up.output_text)

Node.js

TypeScript
import { randomUUID } from "node:crypto";

let followUp = await client.responses.create(
  {
    model: "deepseek-flash",
    background: true,
    previous_response_id: response.id,
    input: "Give me one concise recommendation based on the findings.",
  },
  { headers: { "Idempotency-Key": `follow-up-${randomUUID()}` } },
);

while (!["completed", "failed", "cancelled"].includes(followUp.status)) {
  await new Promise((resolve) => setTimeout(resolve, 5000));
  followUp = await client.responses.retrieve(followUp.id);
}

if (followUp.status !== "completed") {
  throw new Error(`Follow-up ended with status=${followUp.status}`);
}

console.log(followUp.output_text);

A follow-up may contain text only; you do not need to upload the original files again. Use the newest completed response identifier for each subsequent follow-up.

List responses

Returns responses for the authenticated account, newest first.

GET/responses

Query parameters

ParameterTypeRequiredDescription
limitIntegerNoNumber of responses to return, from 1 to 25. Defaults to 25.
statusStringNoComma-separated status values to include.
cursorStringNoOpaque cursor returned by the previous page.
Bash
curl \
  "${CONTINUA_AI_API_BASE_URL}/responses?limit=10&status=completed,failed" \
  --header "Authorization: Bearer ${CONTINUA_AI_API_KEY}"
JSON
{
  "responses": [
    {
      "id": "resp_1234567890abcdef",
      "object": "response",
      "status": "completed",
      "previous_response_id": null,
      "created_at": "2026-10-17 15:42",
      "last_updated_at": "2026-10-17 15:47",
      "report": {
        "user_question": "What are the material risks in this supplier agreement?",
        "project_ids": ["proj_1234567890abcdef"]
      }
    }
  ],
  "next_cursor": "eyJzY2hlbWEiOiIuLi4ifQ"
}

Each entry includes previous_response_id (null for root tasks), report.user_question, and report.project_ids to identify the task, its source projects, and its place in a task chain. created_at and last_updated_at are UTC date-times in YYYY-MM-DD HH:mm format.

When next_cursor is not null, pass it unchanged in the next request and keep limit and status unchanged while following a cursor. Continue with next_cursor until it is null, even if a page is empty.

Enable tools for a response

POST /api/v1/responses uses the standard Responses API tools array:

json
{
  "model": "deepseek-flash",
  "background": true,
  "input": "Research the latest customer feedback and compare it with our internal notes.",
  "tools": [
    { "type": "web_search" },
    {
      "type": "mcp",
      "plugin_id": "<plugin ID from the list endpoint>"
    }
  ]
}

Web search follows the OpenAI web search tool shape. Plugins use MCP entries in the same tools array, referencing stored connections by plugin_id. The following options are supported:

  • At most one web_search tool. The legacy web_search_preview type is also accepted. Search options such as location, context size, and domain filters are currently unsupported and rejected.
  • At most 20 mcp tools, each with a unique plugin_id returned by the plugin list. Each ID must belong to the authenticated user. Responses requests and returned tools reference plugins by ID only; server_url and server_label are not accepted.
  • tool_choice supports auto (default) and none. none disables the tools for this response. Forced tool choices are currently unsupported.

Search and MCP tools can be combined with uploaded files, projects, and a previous response. Enabling either also allows requests without those other sources. The tool configuration is returned in the response's tools and tool_choice. Legacy web search is reported as web_search. Tools apply to each request, including follow-ups, so include the needed entries again.

Access (read or write) and write mode (auto, always, or approval) are read and updated through the plugin API. Responses tool entries do not override those settings. Per-request MCP options such as require_approval, authorization, headers, and allowed_tools are unsupported and rejected.

When a response proposes an action requiring approval, approve it through a normal follow-up response after the proposal response completes:

json
{
  "model": "deepseek-flash",
  "previous_response_id": "<completed proposal response ID>",
  "input": "I approve the proposed update to the customer notes. Proceed with that update.",
  "tools": [
    {
      "type": "mcp",
      "plugin_id": "<plugin ID from the list endpoint>"
    }
  ]
}

The follow-up continues from the parent response and applies the plugin's current permission settings. Approval is given in the follow-up input; the API does not use structured mcp_approval_request / mcp_approval_response items or a separate paused-response state.

Manage plugins

MethodPathBehavior
GET/api/v1/pluginsList the current user's plugins.
POST/api/v1/pluginsConnect a new MCP server.
PATCH/api/v1/plugins/:pluginIdUpdate plugin details or reconnect.
DELETE/api/v1/plugins/:pluginIdDelete the connection.

The public MCP server exposes the same operations as plugins_list, plugins_create, plugins_update, and plugins_delete. Create uses the same fields as POST; update and delete take plugin_id, with update fields alongside it. responses_create accepts body.tools and body.tool_choice. Call learn_how_to_use for the complete workflow.

POST requires name, url, access (read or write), and write (auto, always, or approval), matching the plugin settings in the Continua AI app. Read access stores write as auto. The server URL must use HTTPS.

For Agent mode, connect the Continua Agent plugin using https://app.askcontinua.com/mcp as url. This connection uses your current account without an additional API token or OAuth step. Select the returned plugin_id in a response's tools array to run tasks with project prompts, rules, policies, knowledge, and memory; review past results and user feedback; save reviewed knowledge back to projects; and set up scheduled tasks. See Agent mode in Projects for the workflow.

json
{
  "name": "Internal notes",
  "url": "https://example.com/mcp",
  "access": "read",
  "write": "auto"
}

PATCH accepts one or more of those fields, plus reconnect: true. To reconnect an expired plugin by ID, send { "reconnect": true } to its PATCH endpoint; the previously configured server address is used. Including url configures a server address and also starts a connection and authorization flow. Without url or reconnect, PATCH updates only the name and permission settings.

Connected plugins are returned as:

json
{
  "id": "<plugin ID>",
  "object": "plugin",
  "name": "Internal notes",
  "access": "read",
  "write": "auto",
  "auth_expired": false
}

Plugin objects, including list and create/update results, do not expose connection URLs. URLs are supplied only when configuring a connection through POST or PATCH. GET returns { "object": "list", "data": [ ... ], "has_more": false }. DELETE returns { "id": "...", "object": "plugin", "deleted": true }. There is no global enable/disable setting; select plugins on each response.

If OAuth is required, POST or PATCH instead returns:

json
{
  "object": "plugin.authorization",
  "authorization_url": "https://example.com/oauth/authorize?..."
}

Open this URL in a browser signed in to Continua AI as the same user as the API token. Completing authorization saves the plugin; list plugins again after it finishes. The pending authorization expires after 10 minutes. The connection is only created or replaced after authorization succeeds.

Scheduled tasks

Scheduled tasks run a saved response on a recurring schedule — for example, a morning briefing every weekday. They use the same Bearer authentication as /v1/responses.

MethodPathAction
POST/v1/scheduled-tasksCreate a schedule
GET/v1/scheduled-tasksList schedules in Projects you can access
GET/v1/scheduled-tasks/{id}Retrieve a schedule
PATCH/v1/scheduled-tasks/{id}Change the name, response, schedule, or status
DELETE/v1/scheduled-tasks/{id}Cancel future runs and keep the record
GET/v1/scheduled-tasks/{id}/runsList runs and their response IDs
GET/v1/projects/{projectId}/scheduled-tasksList a Project's schedules
POST/v1/projects/{projectId}/scheduled-tasksCreate a schedule in a Project

Create a schedule

json
{
  "project_id": "P-example",
  "name": "Daily research",
  "response": {
    "model": "deepseek-flash",
    "input": "Summarize today's relevant developments.",
    "background": true,
    "tools": [{ "type": "web_search" }]
  },
  "schedule": {
    "cron": "0 9 * * 1-5",
    "timezone": "Asia/Singapore"
  }
}
  • response accepts the same parameters as POST /v1/responses, except previous_response_id and input_file items. Each run starts a new, independent response.
  • schedule.cron is a standard five-field cron expression. timezone accepts IANA names and defaults to UTC.
  • Every schedule belongs to one Project. project_id is required on /v1/scheduled-tasks; the Project endpoint takes it from the URL. A schedule cannot be moved to another Project.
  • Each schedule must draw on Project files, web search, or MCP plugins. Temporary uploaded files are not supported because they expire.

Access

Schedules follow the same access rules as their Project. In Team Projects, members with write access can manage each other's schedules; Personal Projects are owner-only. Deleting a Project also deletes its schedules.

Pause, resume, and cancel

  • Pause: PATCH {"status": "paused"}
  • Resume: PATCH {"status": "active"}
  • Cancel: DELETE (same as PATCH {"status": "cancelled"})

Other PATCH fields are optional; response and schedule each replace the whole object. Changes apply to future runs. A run already in progress can still finish after cancelling. To repeat a cancelled task, create a new schedule.

Lists and run history

Lists accept limit (1–25, default 25) and cursor, and return next_cursor. The schedule list also accepts status (active, paused, or cancelled) and project_id. Add group_by=project to receive projects: [{project_id, scheduled_tasks}] for the current page.

Each run reports running, success, or failed, plus response_id, error, and ran_at (Unix seconds). success means the response was started, not that it has finished — use GET /v1/responses/{response_id} to follow it. The latest 100 runs are kept; older responses and their output files remain available.

Credits

Each run uses credits like a normal response. If a schedule repeatedly can't run because of insufficient credits or an expired team plan, it pauses automatically and shows a pause_reason. After you add credits or renew, resume it with PATCH {"status": "active"}.

Projects

Projects hold reusable source files for analysis. Use the IDs returned by the API for subsequent requests. The examples use the CONTINUA_AI_API_KEY and CONTINUA_AI_API_BASE_URL variables from Authentication; PROJECT_ID and FILE_ID come from API responses.

Project management

MethodEndpointParameters
POST/api/v1/projectsJSON: required name, optional team_id
GET/api/v1/projectsOptional query parameters scope, team_id
GET/api/v1/projects/{project_id}None
PATCH/api/v1/projects/{project_id}JSON: fields to update
DELETE/api/v1/projects/{project_id}None

Create a personal project:

sh
curl "${CONTINUA_AI_API_BASE_URL}/projects" \
  -H "Authorization: Bearer ${CONTINUA_AI_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"name":"Risk review knowledge base"}'

name must contain 1–200 characters after trimming. To create a team project, also pass team_id, obtained from the account or billing endpoint. A new team project initially grants access to its creator and team administrators.

Project listing returns only accessible projects, with no pagination:

json
{ "object": "list", "data": [] }
scopeProjects returned
all or omittedAll accessible projects
personalPersonal projects
teamTeam projects accessible to the whole team
selectedTeam projects accessible to selected team members

Use team_id to further filter the list by team.

Create, retrieve, and update operations return HTTP 200 with a project object:

json
{
  "id": "project_example",
  "object": "project",
  "name": "Risk review knowledge base",
  "files": [],
  "created_at": 1791936000,
  "last_updated_at": 1791936000
}

Times are Unix seconds. Depending on the project, the response can also contain is_default, role (owner or editor), team_id, team (id and name), access, share, reader_scope, can_write, and write_blocked_reason. can_write indicates whether the project currently accepts changes. Project detail additionally includes members, each with email, role, and you, and optional user_id, team_role, and has_access.

Deleting a project returns HTTP 200 with { "id": "...", "object": "project", "deleted": true }.

Updates, access, and sharing

PATCH fieldMeaning
nameRename the project; 1–200 characters after trimming
team_idMove an owned, non-default personal project into a team; send separately from other changes
accessChange a team project's membership access; send separately from other changes
sharetrue enables sharing and returns a server-issued share ID; null revokes sharing
reader_scopeask allows shared readers to ask follow-up questions; view allows viewing only

Rename a project:

sh
curl --request PATCH "${CONTINUA_AI_API_BASE_URL}/projects/${PROJECT_ID}" \
  -H "Authorization: Bearer ${CONTINUA_AI_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"name":"Risk review knowledge base v2"}'

access requires both access_scope (team or selected) and allowed_user_ids (up to 1,000 non-empty user IDs). Selected users must be current team members. For whole-team access, use an empty array. A creator who is not a team administrator retains their own access when changing the list.

json
{
  "access": {
    "access_scope": "selected",
    "allowed_user_ids": ["user_example"]
  }
}

Membership access and public-link sharing are independent settings. Moving a project into a team initially grants access to the actor and team administrators.

OperationRequired access
Create a team project or move a personal project into a teamCurrent team membership and a team plan permitting writes
Rename, upload, compile, or remove filesProject owner or editor with project access
Change a team project's access, sharing, or reader permissionsProject creator or team administrator
Delete a projectPersonal project owner or team administrator

Team uploads and compilation also require the team plan to permit writes.

Project files

MethodEndpointResult
POST/api/v1/projects/{project_id}/filesUpload multipart files and start compilation
GET/api/v1/projects/{project_id}/filesList file metadata and compilation status
GET/api/v1/projects/{project_id}/files/{file_id}Retrieve one file's metadata
GET/api/v1/projects/{project_id}/files/{file_id}/compiledRead compiled Markdown suitable for AI consumption
DELETE/api/v1/projects/{project_id}/files/{file_id}Remove a file
POST/api/v1/projects/{project_id}/files?retry=trueRetry failed compilation without uploading again

Upload 1–200 files using repeated multipart file fields. No other form fields are accepted. Size limits follow the account plan. Filenames in the same batch must remain unique after sanitization; for example, a b.pdf and a-b.pdf conflict. Supported extensions are .txt, .md, .markdown, .json, .pdf, .docx, .xlsx, .xlsm, .csv, .eml, .msg, .pptx, .html, .htm, and .xhtml.

sh
curl "${CONTINUA_AI_API_BASE_URL}/projects/${PROJECT_ID}/files" \
  -H "Authorization: Bearer ${CONTINUA_AI_API_KEY}" \
  -F "file=@./contract.pdf" \
  -F "file=@./notes.md"

HTTP 200 confirms the upload; it does not guarantee compilation succeeded. The response contains only the uploaded batch:

json
{
  "object": "list",
  "project_id": "project_example",
  "data": [
    {
      "id": "file_example",
      "object": "project.file",
      "filename": "contract.pdf",
      "bytes": 4096,
      "status": "compiling",
      "created_at": 1791936000
    }
  ],
  "has_more": false,
  "compile_error": null
}

If the files were saved but compilation could not start, compile_error contains code and message while the HTTP status remains 200. Check file status before retrying compilation. Do not repeat the upload merely to poll progress.

File statusMeaning
uploadingUpload in progress
pending_compileWaiting for compilation
compilingCompilation in progress
readyReady for use
failedCompilation failed and can be retried

File listing returns { "object": "list", "data": [...], "has_more": false } without pagination. Single-file metadata has the same fields as each upload item; optional compiled_bytes gives the UTF-8 size of compiled Markdown. bytes is the original file size, and created_at is Unix seconds. File deletion returns { "id": "...", "object": "project.file", "deleted": true }. Successful list, retrieve, read, and delete operations return HTTP 200.

Read one ready file as Markdown:

sh
curl "${CONTINUA_AI_API_BASE_URL}/projects/${PROJECT_ID}/files/${FILE_ID}/compiled" \
  -H "Authorization: Bearer ${CONTINUA_AI_API_KEY}" \
  --output compiled.md

The response is the Markdown body with Content-Type: text/markdown;charset=utf-8, rather than a JSON metadata object. The file must be ready and have compiled content available.

Retry failed compilation with an empty body:

sh
curl --request POST \
  "${CONTINUA_AI_API_BASE_URL}/projects/${PROJECT_ID}/files?retry=true&retry_ids=${FILE_ID}" \
  -H "Authorization: Bearer ${CONTINUA_AI_API_KEY}"

retry_ids accepts comma-separated IDs or repeated query parameters. Omit it to retry all failed files. Supplying retry_ids requires retry=true; retry requests must not use a multipart upload body. Only failed files are retried. The response is a list with project_id, data containing the retried files, and has_more: false. Continue checking file status until ready or failed.

Analyze a whole project and retrieve its responses

For a focused lookup, use the compiled-file endpoint above. To analyze many files together, pass one or more project IDs in report.project_ids when creating a response:

sh
curl "${CONTINUA_AI_API_BASE_URL}/responses" \
  -H "Authorization: Bearer ${CONTINUA_AI_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-flash",
    "input": "Review the project materials together. Reconcile contract obligations, identify conflicting evidence, and produce a cited risk report.",
    "report": { "project_ids": ["<project_id>"] }
  }'

Replace <project_id> with a returned project ID. The API includes all ready compiled files from the referenced projects as analysis sources. Each project must have at least one such file; wait until every intended source file is ready when full coverage is required. Poll GET /api/v1/responses/{response_id} for the response status and result.

GET /api/v1/responses?project_id={project_id} lists responses the user can access in that project, including responses created by other members. Keep project_id and status filters unchanged when following next_cursor, and continue until next_cursor is null even if a page is empty. Each entry includes previous_response_id (null for root tasks), report.user_question, and report.project_ids, with created_at and last_updated_at as UTC date-times in YYYY-MM-DD HH:mm format. Accessible response IDs can be used to retrieve details and output files through GET /api/v1/responses/{response_id} and GET /api/v1/responses/{response_id}/files?name={filename}. Output files require a completed response and unlocked results.

Agent mode

The Continua Agent plugin, also called Agent mode, combines analysis tasks, reviewed project knowledge, and recurring workflows. Connect it through POST /api/v1/plugins using https://app.askcontinua.com/mcp, then select the returned plugin_id in tools when creating a response. This connection uses your current account without an additional API token or OAuth step.

  1. Project context → analysis task: read prompts, rules, policies, knowledge, and memory stored as project files (GET /api/v1/projects/{project_id}/files/{file_id}/compiled), apply them when building the response input, and attach the source projects through report.project_ids.
  2. Past tasks and feedback → reviewed project knowledge: list a project's responses (GET /api/v1/responses?project_id={project_id}), review each task's saved question and criteria — returned as report.user_question, report.findings_criteria, and report.composition_criteria on every status — and follow previous_response_id to review the task chain and user feedback in follow-up questions. For completed tasks, inspect output_text, the rationale in the output message text, and output files; retrieve findings.json through the response files endpoint to review the evidence. Distill useful knowledge and memory into a reviewed, versioned Markdown file and upload it back to the project.
  3. Recurring workflows → scheduled tasks: create a schedule using project context, plugins, or web search. Include the Continua Agent plugin ID in response.tools when scheduled runs need Agent mode. Retrieve run history to find each response_id, then use the Responses API to check completion and read results. See Scheduled tasks for endpoints and examples.

Results are not saved to projects automatically: download the artifact or prepare a reviewed summary, then upload its bytes as a project file. Record the source response ID, approval status, version, and review date, and mark superseded versions explicitly rather than deleting them.

Common project errors

Errors use the public API envelope { "error": { "message": "...", "type": "...", "param": null, "code": "..." } }. param may identify the invalid field.

HTTP statusError codeAction
400unknown_parameter, invalid_request, invalid_scope, invalid_team_id, invalid_project_id, invalid_file_idCorrect the request using message and param
400duplicate_filenameRename colliding files within the upload batch
403access_deniedCheck project membership and the role required by the operation
403team_plan_expiredRenew the team plan before the restricted operation
404project_not_found, project_file_not_foundCheck project/file IDs and whether the source still exists
409file_not_compiledWait until the file has ready compiled content
409compile_in_progressWait for compilation to finish before deleting the affected project or file
409no_retryable_filesQuery current status and choose failed files
413file_too_largeReduce upload size to fit the account plan
415unsupported_file_typeUse a supported extension
429compile_concurrency_limitWait for active compilation to finish before retrying

Account and billing

MethodEndpointResult
GET/api/v1/accountuser_id, subscription_plan, and active teams with their IDs, names, roles, and paid_through dates
GET/api/v1/billinguser_id, total_balance, subscription_balance, purchased_balance, subscription status, renews_at, free_tasks_left, and active teams
GET/api/v1/billing/activity?limit=24Recent billing transactions, newest first; limit defaults to 24 and accepts 1–100

subscription_plan is null, pro, or team. teams contains effective paid team memberships; it is empty without an active team plan. Multiple active teams are returned separately. Balances match those shown in the Continua AI app. Team renewal dates are in teams[].paid_through; renews_at describes the personal subscription.

Activity returns an object: "list" envelope with data and has_more. Each item contains id, object: "billing.activity", type (GRANT, CONSUME, EXPIRE, or REFUND), signed numeric amount, Unix-second created_at, and a nullable response_id. Records are individual credit transactions, so a response charged against multiple buckets can have multiple items. Only the authenticated user's transactions are included. This endpoint returns recent activity without a pagination cursor; increase limit up to 100 to retrieve more records.

sh
curl "${CONTINUA_AI_API_BASE_URL}/account" \
  -H "Authorization: Bearer ${CONTINUA_AI_API_KEY}"

curl "${CONTINUA_AI_API_BASE_URL}/billing" \
  -H "Authorization: Bearer ${CONTINUA_AI_API_KEY}"

curl "${CONTINUA_AI_API_BASE_URL}/billing/activity?limit=50" \
  -H "Authorization: Bearer ${CONTINUA_AI_API_KEY}"

MCP

Connect a Streamable HTTP MCP client to https://app.askcontinua.com/mcp, with the same Bearer header. The endpoint is stateless, accepts POST, and returns JSON responses; GET and DELETE return HTTP 405. Clients should send Accept: application/json, text/event-stream and Content-Type: application/json, and perform the standard MCP initialization.

ToolsOperations
learn_how_to_useRead the usage skill for Agent mode, analysis, deliverables, project knowledge, plugins, and scheduled tasks
account_get, billing_get, billing_activity_listAccount and billing reads
responses_create, responses_get, responses_listCreate, poll, and list responses
response_files_getGet an output download URL or converted content
files_uploadUpload a staged response input file
projects_list, projects_create, projects_get, projects_update, projects_deleteProject operations
project_files_list, project_files_get, project_files_compiled_getFile metadata and compiled Markdown
project_files_upload, project_files_retry, project_files_deleteUpload and compile files, retry failed compilation, or remove files

Call learn_how_to_use with {} before your first task to get the usage skill as Markdown text. It covers Agent mode, project prompts, rules, policies, knowledge and memory, reviewing past results and user feedback, scheduled tasks, and complete response examples. This read-only tool does not create a response or consume analysis credits.

responses_create takes the public Responses API JSON in body, plus an optional idempotency_key. Responses run in the background; poll responses_get about every five seconds until terminal. Project tool parameters use the public API's snake_case names, including project_id, team_id, file_id, reader_scope, and access.allowed_user_ids.

files_upload takes { "filename": "input.pdf", "data": "<base64>" } and returns a file ID for an input_file item. project_files_upload takes { "project_id": "...", "files": [{ "filename": "input.pdf", "data": "<base64>" }] }. Base64 must use the standard alphabet and padding, without a data URL prefix. MCP request bodies are limited to 32 MB including base64 and JSON overhead; use the multipart upload endpoints above for larger files. File type, upload size, access, and compilation rules are the same as in the REST API.

Tool failures set isError: true and return the public API error's message, type, param, and code, plus the HTTP-equivalent status and an optional retry_after. Invalid or disabled API keys fail HTTP authentication before any MCP tools run.

Errors

Errors use a consistent JSON envelope.

JSON
{
  "error": {
    "message": "The response is not complete.",
    "type": "invalid_request_error",
    "param": "previous_response_id",
    "code": "response_not_complete"
  }
}

Every response includes an x-request-id header. Include this value when contacting support about a failed request.

Common errors

StatusCodeMeaning
400invalid_modelThe requested model identifier is not supported.
400missing_file_sourceA first response did not include a file or project.
400insufficient_creditsThe account does not have enough credits to create the response.
400unsupported_parameterThe request uses an unsupported option, such as streaming.
401invalid_api_keyThe bearer token is missing, invalid, disabled, or rotated.
403access_deniedThe authenticated account cannot access the requested resource.
404file_not_foundThe file does not exist or belongs to another account.
404project_not_foundThe project does not exist or belongs to another account.
404response_not_foundThe response does not exist, is no longer available, or belongs to another account.
409file_already_usedThe uploaded file was already used in another response request.
409response_not_completeA follow-up references a response that is not complete.
409idempotency_key_reusedAn idempotency key was reused with different parameters.
410file_expiredThe uploaded file has expired.
413file_too_largeThe file exceeds the account's current size limit.
415unsupported_file_typeThe file extension is not supported.
429concurrency_limit_exceededThe account's concurrent response limit has been reached.
500server_errorThe request could not be completed because of a temporary service error.

Retry guidance

  • Poll response status no more frequently than directed by the Retry-After header. When the header is unavailable, wait at least five seconds.
  • For HTTP 429, wait for the duration specified by Retry-After before retrying.
  • For temporary HTTP 500 errors, retry with exponential backoff and random jitter.
  • If a create request times out, retry with the same Idempotency-Key and the exact same request body.
  • Do not retry other 400, 401, 403, 404, 409, 410, or 415 errors without correcting the request or application state.

Integration checklist

  • Keep the API token on your server.
  • Use the /api/v1 base URL exactly once.
  • Use a full model identifier, or auto.
  • Include a unique idempotency key for every new response.
  • Save every returned response identifier.
  • Poll until a terminal status is reached and honor Retry-After.
  • Handle failed and cancelled responses explicitly.
  • Upload a file again if its identifier has expired or has already been used.
  • Use the latest completed response identifier for follow-up questions.

Start building with Continua AI

Create an account to generate an API token, or read the Help Center for guidance on criteria, guardrails, and Projects.