Overview
Only the endpoints and parameters documented here are supported. All base paths are relative to the API base URL.
https://app.askcontinua.com/api/v1Authentication
Every request requires a Continua AI API token in the Authorization header.
Authorization: Bearer YOUR_CONTINUA_AI_API_TOKENKeep API tokens on your server. Do not expose them in browser code, mobile applications, public repositories, or client-side logs.
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, orcancelled. - 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
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
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.
| Model | Model identifier | Best suited for |
|---|---|---|
| Auto | auto | Continua picks the best model for the task |
| DeepSeek Flash | deepseek-flash | Fast, everyday analysis |
| DeepSeek Pro | deepseek-pro | Deeper reasoning |
| GLM Flash | glm-flash | Fast, cost-efficient work |
| GLM | glm | Balanced performance |
| GPT Luna | gpt-luna | Fast, cost-efficient work |
| GPT Terra | gpt-terra | Balanced performance |
| GPT Sol | gpt-sol | Advanced reasoning |
| GPT Astra | gpt-astra | Most capable reasoning |
| Claude Haiku | claude-haiku | Fast responses |
| Claude Sonnet | claude-sonnet | Balanced speed and depth |
| Claude Opus | claude-opus | Deep analysis |
| Claude Fable | claude-fable | Frontier reasoning |
| Gemini Flash | gemini-flash | Fast multimodal work |
| Gemini Pro | gemini-pro | Long-context work |
| Grok | grok | General-purpose reasoning |
| Kimi | kimi | Long-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.
Form fields
| Field | Type | Required | Description |
|---|---|---|---|
file | File | Yes | One file. Send one file per upload request. |
purpose | String | Yes | Must be user_data. |
Supported filename extensions
.txt, .md, .markdown, .json, .pdf, .docx, .xlsx, .csv,
.eml, .msg, .pptx, .html, .htm, .xhtmlThe maximum file size depends on the account plan.
cURL example
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
{
"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"
}| Field | Type | Description |
|---|---|---|
id | String | File identifier to use in a response request. |
object | String | Always file. |
bytes | Integer | File size in bytes. |
created_at | String | Creation time as a UTC date-time in YYYY-MM-DD HH:mm format. |
expires_at | Integer | Deadline for using an unsubmitted upload. |
filename | String | Original filename. |
purpose | String | Always user_data. |
status | String | Always 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.
Send a unique Idempotency-Key header with every new request.
Idempotency-Key: response-7c86b96c5a4d48c58bbdb6bca75e1098Request body
{
"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
| Parameter | Type | Required | Description |
|---|---|---|---|
model | String | Yes | A full model identifier, or auto. |
background | Boolean | Recommended | Must be true when provided. Responses always run asynchronously. |
input | String or array | Yes | The user's request. |
previous_response_id | String | No | Identifier of a completed response to continue. |
report | Object | No | Optional report configuration. |
tools | Array | No | Web search and connected MCP plugins, including the Continua Agent plugin for Agent mode. See Web search and tools. |
tool_choice | String | No | Use 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
inputmust contain exactly one message.- The message
rolemust beuser. contentmust contain exactly one non-emptyinput_textitem.contentmay contain up to 200 uniqueinput_fileitems.- Each
input_filemust reference a valid, unexpired file identifier owned by the same account. - For the first response, provide at least one
input_file, a project identifier inreport.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.
{
"model": "deepseek-flash",
"background": true,
"previous_response_id": "resp_1234567890abcdef",
"input": "Which of these risks should we address first?"
}Report configuration
| Parameter | Type | Required | Description |
|---|---|---|---|
project_ids | Array of strings | No | Unique Continua AI project identifiers to include as sources. |
findings_criteria | String | No | Criteria used to select relevant findings. |
composition_criteria | String | No | Instructions for organizing and presenting the response. |
guardrail_mode | String | No | Validation level: off, lite, or strict. |
Each referenced project must belong to the authenticated account and contain at least one document that has finished processing.
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
{
"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.
curl \
"${CONTINUA_AI_API_BASE_URL}/responses/${RESPONSE_ID}" \
--header "Authorization: Bearer ${CONTINUA_AI_API_KEY}"Status values
| Status | Meaning |
|---|---|
queued | Accepted and waiting to run. |
in_progress | Currently running. |
completed | Finished successfully. The result is available. |
failed | Could not be completed. See error. |
cancelled | Cancelled 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
{
"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
| Field | Type | Description |
|---|---|---|
id | String | Response identifier. |
object | String | Always response. |
created_at | String | Creation time as a UTC date-time in YYYY-MM-DD HH:mm format. |
model | String | Model used for the response. |
status | String | Current response status. |
previous_response_id | String or null | Parent response identifier for a follow-up. |
background | Boolean | Always true. |
output_text | String | Complete generated answer when status is completed. |
output | Array | Generated messages in Responses API format. |
report | Object | Output file names and credits charged, available after completion. |
error | Object or null | Failure 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:
| Field | Type | Description |
|---|---|---|
finding_index | Integer | One-based finding number. |
record_id | String | Finding record identifier. |
claim | String | Finding or conclusion supported by the source. |
quote | String | Supporting source text, when available. |
document | String | Source document or source URL, when available. |
position | String | Page or paragraph location, when available. |
relevance | Number | Relevance score from 0 to 1, when available. |
confidence | Number | Confidence score from 0 to 1, when available. |
evidence | Number | Evidence strength score from 0 to 1, when available. |
materiality | Number | Materiality 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
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
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.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | Integer | No | Number of responses to return, from 1 to 25. Defaults to 25. |
status | String | No | Comma-separated status values to include. |
cursor | String | No | Opaque cursor returned by the previous page. |
curl \
"${CONTINUA_AI_API_BASE_URL}/responses?limit=10&status=completed,failed" \
--header "Authorization: Bearer ${CONTINUA_AI_API_KEY}"{
"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:
{
"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_searchtool. The legacyweb_search_previewtype is also accepted. Search options such as location, context size, and domain filters are currently unsupported and rejected. - At most 20
mcptools, each with a uniqueplugin_idreturned by the plugin list. Each ID must belong to the authenticated user. Responses requests and returned tools reference plugins by ID only;server_urlandserver_labelare not accepted. tool_choicesupportsauto(default) andnone.nonedisables 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:
{
"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
| Method | Path | Behavior |
|---|---|---|
| GET | /api/v1/plugins | List the current user's plugins. |
| POST | /api/v1/plugins | Connect a new MCP server. |
| PATCH | /api/v1/plugins/:pluginId | Update plugin details or reconnect. |
| DELETE | /api/v1/plugins/:pluginId | Delete 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.
{
"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:
{
"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:
{
"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.
| Method | Path | Action |
|---|---|---|
| POST | /v1/scheduled-tasks | Create a schedule |
| GET | /v1/scheduled-tasks | List 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}/runs | List runs and their response IDs |
| GET | /v1/projects/{projectId}/scheduled-tasks | List a Project's schedules |
| POST | /v1/projects/{projectId}/scheduled-tasks | Create a schedule in a Project |
Create a schedule
{
"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"
}
}responseaccepts the same parameters asPOST /v1/responses, exceptprevious_response_idandinput_fileitems. Each run starts a new, independent response.schedule.cronis a standard five-field cron expression.timezoneaccepts IANA names and defaults toUTC.- Every schedule belongs to one Project.
project_idis 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 asPATCH {"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
| Method | Endpoint | Parameters |
|---|---|---|
| POST | /api/v1/projects | JSON: required name, optional team_id |
| GET | /api/v1/projects | Optional 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:
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:
{ "object": "list", "data": [] }scope | Projects returned |
|---|---|
all or omitted | All accessible projects |
personal | Personal projects |
team | Team projects accessible to the whole team |
selected | Team 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:
{
"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 field | Meaning |
|---|---|
name | Rename the project; 1–200 characters after trimming |
team_id | Move an owned, non-default personal project into a team; send separately from other changes |
access | Change a team project's membership access; send separately from other changes |
share | true enables sharing and returns a server-issued share ID; null revokes sharing |
reader_scope | ask allows shared readers to ask follow-up questions; view allows viewing only |
Rename a project:
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.
{
"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.
| Operation | Required access |
|---|---|
| Create a team project or move a personal project into a team | Current team membership and a team plan permitting writes |
| Rename, upload, compile, or remove files | Project owner or editor with project access |
| Change a team project's access, sharing, or reader permissions | Project creator or team administrator |
| Delete a project | Personal project owner or team administrator |
Team uploads and compilation also require the team plan to permit writes.
Project files
| Method | Endpoint | Result |
|---|---|---|
| POST | /api/v1/projects/{project_id}/files | Upload multipart files and start compilation |
| GET | /api/v1/projects/{project_id}/files | List 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}/compiled | Read 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=true | Retry 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.
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:
{
"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 status | Meaning |
|---|---|
uploading | Upload in progress |
pending_compile | Waiting for compilation |
compiling | Compilation in progress |
ready | Ready for use |
failed | Compilation 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:
curl "${CONTINUA_AI_API_BASE_URL}/projects/${PROJECT_ID}/files/${FILE_ID}/compiled" \
-H "Authorization: Bearer ${CONTINUA_AI_API_KEY}" \
--output compiled.mdThe 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:
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:
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.
- 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 throughreport.project_ids. - 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 asreport.user_question,report.findings_criteria, andreport.composition_criteriaon every status — and followprevious_response_idto review the task chain and user feedback in follow-up questions. For completed tasks, inspectoutput_text, the rationale in the output message text, and output files; retrievefindings.jsonthrough 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. - Recurring workflows → scheduled tasks: create a schedule using project
context, plugins, or web search. Include the Continua Agent plugin ID in
response.toolswhen scheduled runs need Agent mode. Retrieve run history to find eachresponse_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 status | Error code | Action |
|---|---|---|
| 400 | unknown_parameter, invalid_request, invalid_scope, invalid_team_id, invalid_project_id, invalid_file_id | Correct the request using message and param |
| 400 | duplicate_filename | Rename colliding files within the upload batch |
| 403 | access_denied | Check project membership and the role required by the operation |
| 403 | team_plan_expired | Renew the team plan before the restricted operation |
| 404 | project_not_found, project_file_not_found | Check project/file IDs and whether the source still exists |
| 409 | file_not_compiled | Wait until the file has ready compiled content |
| 409 | compile_in_progress | Wait for compilation to finish before deleting the affected project or file |
| 409 | no_retryable_files | Query current status and choose failed files |
| 413 | file_too_large | Reduce upload size to fit the account plan |
| 415 | unsupported_file_type | Use a supported extension |
| 429 | compile_concurrency_limit | Wait for active compilation to finish before retrying |
Account and billing
| Method | Endpoint | Result |
|---|---|---|
| GET | /api/v1/account | user_id, subscription_plan, and active teams with their IDs, names, roles, and paid_through dates |
| GET | /api/v1/billing | user_id, total_balance, subscription_balance, purchased_balance, subscription status, renews_at, free_tasks_left, and active teams |
| GET | /api/v1/billing/activity?limit=24 | Recent 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.
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.
| Tools | Operations |
|---|---|
learn_how_to_use | Read the usage skill for Agent mode, analysis, deliverables, project knowledge, plugins, and scheduled tasks |
account_get, billing_get, billing_activity_list | Account and billing reads |
responses_create, responses_get, responses_list | Create, poll, and list responses |
response_files_get | Get an output download URL or converted content |
files_upload | Upload a staged response input file |
projects_list, projects_create, projects_get, projects_update, projects_delete | Project operations |
project_files_list, project_files_get, project_files_compiled_get | File metadata and compiled Markdown |
project_files_upload, project_files_retry, project_files_delete | Upload 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.
{
"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
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_model | The requested model identifier is not supported. |
| 400 | missing_file_source | A first response did not include a file or project. |
| 400 | insufficient_credits | The account does not have enough credits to create the response. |
| 400 | unsupported_parameter | The request uses an unsupported option, such as streaming. |
| 401 | invalid_api_key | The bearer token is missing, invalid, disabled, or rotated. |
| 403 | access_denied | The authenticated account cannot access the requested resource. |
| 404 | file_not_found | The file does not exist or belongs to another account. |
| 404 | project_not_found | The project does not exist or belongs to another account. |
| 404 | response_not_found | The response does not exist, is no longer available, or belongs to another account. |
| 409 | file_already_used | The uploaded file was already used in another response request. |
| 409 | response_not_complete | A follow-up references a response that is not complete. |
| 409 | idempotency_key_reused | An idempotency key was reused with different parameters. |
| 410 | file_expired | The uploaded file has expired. |
| 413 | file_too_large | The file exceeds the account's current size limit. |
| 415 | unsupported_file_type | The file extension is not supported. |
| 429 | concurrency_limit_exceeded | The account's concurrent response limit has been reached. |
| 500 | server_error | The request could not be completed because of a temporary service error. |
Retry guidance
- Poll response status no more frequently than directed by the
Retry-Afterheader. When the header is unavailable, wait at least five seconds. - For HTTP
429, wait for the duration specified byRetry-Afterbefore retrying. - For temporary HTTP
500errors, retry with exponential backoff and random jitter. - If a create request times out, retry with the same
Idempotency-Keyand the exact same request body. - Do not retry other
400,401,403,404,409,410, or415errors without correcting the request or application state.
Integration checklist
- Keep the API token on your server.
- Use the
/api/v1base 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
failedandcancelledresponses 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.