CODEXIS AI

REST API v2

Create a project and chat, upload an attachment, submit a message and stream the response through REST API v2.

Use the Codexis AI agent API to create a project and chat, attach a file, and read both the streamed and completed response. This guide requires an API key, Bash, curl, jq and uuidgen. Run the commands in the same terminal session.

The flow uses POST project and chat creation, POST an attachment, POST a message and GET a streamed response. The complete contract is available in OpenAPI v2.

Connect your account

Use your account's API key in the X-Api-Key header. The v2 base URL is https://vm.codexis.ai/api/v2. Enter the key without displaying it in the terminal.

set -o pipefail
read -r -s -p 'API key: ' CODEXIS_API_KEY
printf '\n'
export CODEXIS_API_KEY
CODEXIS_API_URL='https://vm.codexis.ai/api/v2'

GET /api/v2/me

Check which account you are using. The response contains the key owner's id, email and name.

curl --fail-with-body -sS \
  -H "X-Api-Key: $CODEXIS_API_KEY" \
  "$CODEXIS_API_URL/me" | jq

Choose a model and available capabilities

GET /api/v2/models

The catalog includes models, their availability for your account, the default selection and supported capabilities. Read a model's settings with GET /api/v2/models/{modelId}. Send a fields object to POST /api/v2/models/{modelId}/configuration to obtain the resolved configuration and a cost estimate.

curl --fail-with-body -sS \
  -H "X-Api-Key: $CODEXIS_API_KEY" \
  "$CODEXIS_API_URL/models?limit=10" | jq

Paginated lists return items and nextCursor. Read the next page using the same filters and pass nextCursor as the cursor parameter; null marks the end of the list. You can also read /jurisdictions, /tools, /workflow-templates and account permissions at /me/features, all relative to the v2 base URL.

Create a project and chat

POST /api/v2/projects

Create a project and store its id. A successful creation returns HTTP 201 and the project data.

project=$(curl --fail-with-body -sS \
  -H "X-Api-Key: $CODEXIS_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"name":"API example","description":"Document review"}' \
  "$CODEXIS_API_URL/projects")
project_id=$(printf '%s' "$project" | jq -er '.id')

POST /api/v2/chats

Create a chat in the project with READ_ONLY capabilities. The model follows your account defaults. The HTTP 201 response contains the new chat's id; submitting a message in the next step starts the agent's work.

chat_request=$(jq -n --arg projectId "$project_id" \
  '{title: "Document review", projectId: $projectId, capabilities: "READ_ONLY"}')
chat=$(curl --fail-with-body -sS \
  -H "X-Api-Key: $CODEXIS_API_KEY" \
  -H 'Content-Type: application/json' \
  -d "$chat_request" \
  "$CODEXIS_API_URL/chats")
chat_id=$(printf '%s' "$chat" | jq -er '.id')

Update chat metadata with PATCH /api/v2/chats/{chatId}, specifying individual title, pinned or capabilities fields. Update a project with PUT /api/v2/projects/{projectId}, supplying all editable fields defined by PublicProjectReplacement.

Upload an attachment

POST /api/v2/attachments

Upload one file in the multipart field file. This example creates a short text document and stores the uploaded attachment's ID. To attach several files, upload each separately and pass their IDs together in attachmentIds.

printf '%s\n' 'Review notes: the agreement renews on 1 January. Notice is due 30 days earlier.' > api-example.txt
attachment=$(curl --fail-with-body -sS \
  -H "X-Api-Key: $CODEXIS_API_KEY" \
  -F 'file=@api-example.txt' \
  "$CODEXIS_API_URL/attachments")
attachment_id=$(printf '%s' "$attachment" | jq -er '.id')

HTTP 201 returns attachment metadata, including id, name, size and contentUrl. The default request limit is 50 MiB including multipart headers. The uploaded attachment is staged until message preparation consumes its ID. Remove an unused attachment with DELETE /api/v2/attachments/{attachmentId}.

Submit a message and track its acceptance

POST /api/v2/chats/{chatId}/messages

Generate a UUID in the Idempotency-Key header for each new message. Store it with the request body so you can look up the result after an interrupted connection. Supply text in message, files in attachmentIds, or both.

request_id=$(uuidgen | tr '[:upper:]' '[:lower:]')
message_request=$(jq -n --arg attachmentId "$attachment_id" \
  '{message: "Read the attachment and summarize its contents.", attachmentIds: [$attachmentId]}')
if receipt=$(curl --fail-with-body -sS \
  -H "X-Api-Key: $CODEXIS_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $request_id" \
  -d "$message_request" \
  "$CODEXIS_API_URL/chats/$chat_id/messages"); then
  message_id=$(printf '%s' "$receipt" | jq -er '.assistantMessageId')
  printf '%s\n' "$receipt" | jq
else
  printf 'Submission was not confirmed. Retrieve the stored receipt using the original requestId %s as shown in the next step.\n' "$request_id"
fi

Returns

{
  "status": "accepted",
  "requestId": "f07b5a7d-fd34-40ad-9957-3251d9f26841",
  "chatId": "6fecc96e-8f93-4d93-b7c7-0b7591c9a2d1",
  "createdAt": "2026-10-02T10:00:00Z",
  "userMessageId": "0cb83f6a-85c1-426a-9372-51b4c48ee67f",
  "assistantMessageId": "b647181c-ec42-4993-a17d-d2233078a70c"
}

HTTP 202 confirms acceptance. Use assistantMessageId to observe and retrieve the response. Determine whether the work has finished from the execution state described below.

GET /api/v2/chats/{chatId}/message-requests/{requestId}

After an interrupted submission, retrieve the stored receipt using the same UUID. An accepted receipt contains the accepted message IDs. A rejected receipt contains the rejection code and errorMessageId. An unresolved receipt requires checking the submission outcome before sending again.

curl --fail-with-body -sS \
  -H "X-Api-Key: $CODEXIS_API_KEY" \
  "$CODEXIS_API_URL/chats/$chat_id/message-requests/$request_id" | jq

For a previously accepted message, repeating the same key and input returns the original receipt. Repeating a rejected or unresolved request returns HTTP 409; check its result with GET /api/v2/chats/{chatId}/message-requests/{requestId}. Changed input under the same key returns HTTP 409; use a new key for a new question. Keep the original attachment IDs when repeating a submission.

Read the streamed and completed response

GET /api/v2/chats/{chatId}/messages/{messageId}/events

Observe the response through Server-Sent Events. The -N option disables curl buffering so incoming data appears as it arrives.

curl --fail-with-body -sS -N \
  -H "X-Api-Key: $CODEXIS_API_KEY" \
  -H 'Accept: text/event-stream' \
  "$CODEXIS_API_URL/chats/$chat_id/messages/$message_id/events"

Each snapshot event contains the current execution state, message, and progress of subagents and workflows. Text parts have type: "text" and Markdown with citations in citedMarkdown. Replace the previous rendered content with each new snapshot. Reconnecting gives you the current state; disconnecting leaves the agent's work running.

GET /api/v2/chats/{chatId}/messages/{messageId}/status

You can also read the state with a single request. active means work is in progress, waiting means input is needed, completed means success, failed means an error and stopped means the work was stopped. For unknown, check again before deciding the outcome. The stream closes after a terminal state: completed, failed or stopped.

curl --fail-with-body -sS \
  -H "X-Api-Key: $CODEXIS_API_KEY" \
  "$CODEXIS_API_URL/chats/$chat_id/messages/$message_id/status" | jq '.execution'

GET /api/v2/chats/{chatId}/messages/{messageId}

After completed, retrieve the finished message. This example prints its text parts as Markdown with citations; the full response also contains the other message parts. Read the history with GET /api/v2/chats/{chatId}/messages.

curl --fail-with-body -sS \
  -H "X-Api-Key: $CODEXIS_API_KEY" \
  "$CODEXIS_API_URL/chats/$chat_id/messages/$message_id" \
  | jq -r '.parts[] | select(.type == "text") | .citedMarkdown'

Saved agent definitions

GET /api/v2/agents

List your own agents and those available from plugins. Check editable and deletable for each definition. Create an owned definition with POST /api/v2/agents, update it with PUT /api/v2/agents/{agentId} and remove it with DELETE /api/v2/agents/{agentId}.

curl --fail-with-body -sS \
  -H "X-Api-Key: $CODEXIS_API_KEY" \
  "$CODEXIS_API_URL/agents?limit=10" | jq

Supply every field in PublicAgentInput when creating or updating a definition. Updates merge translations in displayName, summary and examplePrompts by language, using cs, en or sk; other values are replaced. Read a definition with all translations through GET /api/v2/agents/{agentId}.

Set agentId when creating a chat to use the definition's settings. Add its ID to agentIds in an individual message to include its instructions for that turn.

Questions and approvals during execution

GET /api/v2/chats/{chatId}/messages/{messageId}/interactions

When the agent is waiting for your input, retrieve its open questions and workflow proposals. Keep each returned toolCallId and revision with your answer so it refers to the exact proposal you reviewed.

curl --fail-with-body -sS \
  -H "X-Api-Key: $CODEXIS_API_KEY" \
  "$CODEXIS_API_URL/chats/$chat_id/messages/$message_id/interactions" | jq

Answer a question through POST /api/v2/chats/{chatId}/messages/{messageId}/interactions/{toolCallId}/answer using PublicQuestionAnswerRequest. Resolve a proposal through the corresponding /decision path using PublicWorkflowDecisionRequest: choose RUN, REQUEST_CHANGES with feedback, or CANCEL. After HTTP 202, continue observing the message state. For HTTP 409, retrieve the interaction and its current revision again.

Errors and next steps

Check the HTTP status on failure. An application/problem+json response includes code, a detail description and a requestId to include when contacting support.

The downloadable OpenAPI v2 schema contains all paths, required fields, limits and response types. Continue to CLI for terminal usage, or MCP to connect an AI client.