Jenova - The AI Agent PlatformAPI Platform

Jenova Agent API Reference

Base URL: https://api.jenova.ai/v1

Authentication: Bearer token in Authorization header


Table of Contents


Overview

Build and run production-ready AI agents without assembling the underlying stack yourself. Jenova Agent API brings every core capability together in a single managed service:

The Full Agent Stack

  • Agent Orchestration: A unified orchestration layer coordinates models, tools, memory, and retrieval across complex workflows.
  • Memory & Context: Unlimited conversation memory and context built into every session. No external state management required.
  • Tools & MCP: Unlimited tool integrations with platform-native tools and any remote MCP server, ready out of the box.
  • Use Any Model: Power your agents with models from OpenAI, Anthropic, Google, xAI, Qwen, and more through a single integration.
  • Fully-Managed Storage: Managed relational and vector databases with built-in RAG. No infrastructure to provision or scale.
  • Production-Grade: Used by hundreds of thousands of users. Fully-managed infrastructure, stable APIs, built for production traffic.

Quick Start

1. Get Your API Key

Generate an API key from the developer dashboard at www.jenova.ai/platform. Keys use the format jnv_sk_* and are passed as Bearer tokens.

2. Pick or Create an Agent

Choose a pre-built agent from the platform, or create a custom agent in the dashboard with instructions, model settings, knowledge base files, tools, and MCP servers.

3. Send Your First Message

Create a session and send a message in one call:

curl -N -X POST https://api.jenova.ai/v1/messages \
  -H "Authorization: Bearer jnv_sk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "agent": "my-support-agent",
    "user": "user_12345",
    "content": "What can you help me with?"
  }'

The response streams back as Server-Sent Events:

event: stream_started
data: {"session_id":"ses_xyz789","run_id":"run_abc123","agent":"my-support-agent"}

event: stream_delta
data: {"session_id":"ses_xyz789","run_id":"run_abc123","chunk_content":"I can help you with ","seq":1}

event: stream_ended
data: {"session_id":"ses_xyz789","run_id":"run_abc123","success":true,"stop_reason":"end_run","usage":{"cost":"0.0032"}}

Capture session_id from stream_started for follow-up requests. For synchronous JSON responses, see Send Message.


Core Concepts

Agent

An AI agent you interact with through the API. Each agent has a unique slug (e.g., my-support-agent) used as the agent value in API calls.

  • Pre-built: Select from existing agents on the platform.
  • Custom: Configure your own in the dashboard with instructions, model, knowledge base, tools, and MCP servers.

Session

An independent conversation thread between an end user and an agent.

  • Identifier: Prefixed ID (e.g., ses_abc123)
  • Scope: Multiple sessions can exist for the same agent and end user, each with independent conversation state
  • Lifecycle: Sessions persist indefinitely until deleted via API. For no-storage one-shot tasks, use POST /messages with ephemeral: true
  • Platform isolation: API sessions are separate from conversations in the Jenova web app. End users, session history, and billing are independent between the API and web app.

Message

A single entry in a session's conversation history, returned by the Messages endpoints. Each message includes a structured from object with type ("user" or "agent") and name, plus a message type:

  • external - a conversation message intended to be displayed as chat content.
  • internal - an optional message representing agent work steps during a run, such as tool calls or retrieval.

Run

A single agent execution created when you send a message. A run has a run_id, may stream events while it is active, and produces one or more completed messages. Each session can have only one active run at a time.

End User

The user field scopes sessions to an end user in your application. Use a stable opaque ID, such as your internal user ID or UUID. Avoid emails or other PII unless your application requires them. Sessions created with the same user value are grouped together, enabling per-user session listing.

If user is omitted, the session is scoped to your developer account and cannot be filtered by end user later. Pass user in production.

For existing-session requests, user is an optional ownership guard. If you provide it, it must match the user value used when the session was created; otherwise the API returns 404 session_not_owned. Send it as a query parameter on GET and DELETE requests, and in the JSON body on POST and PATCH requests.


Authentication

Authenticate every request with a Bearer token in the Authorization header:

Authorization: Bearer jnv_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

API keys are generated from the developer dashboard.

User-Agent is optional. SDKs may set it for diagnostics, but the API does not require it.

Key format: Keys start with the prefix jnv_sk_ followed by a base62-encoded random string.

Limits: Each developer account can have up to 10 active API keys.


API Endpoints

Messages

Message sends are the primary API path. Use POST /messages for the first message; it creates the session and starts the run in one request. Use POST /sessions/{session_id}/messages when continuing a captured session_id.

Send Message

POST /messages

Creates a persistent session and sends the first message in one atomic request. Set ephemeral: true for a no-storage streaming one-shot request that stores no session or message history, returns no session ID, and cannot be continued.

Request body

FieldTypeRequiredDefaultDescription
agentstringYes-The agent's slug identifier
contentstringConditional-Message text. Required unless file_urls is provided
file_urlsstring[]Conditional-URLs of files to attach. Required unless content is provided
userstringNo-Your external end user identifier (max 255 characters). If omitted, defaults to your developer account
session_namestringNo-Display name for the new session (max 200 characters)
ephemeralbooleanNofalseNo-storage, streaming-only one-shot request. Stores no session or message history, returns no session ID, and cannot be continued
streambooleanNotruetrue for SSE streaming, false for JSON. MCP authorization requires streaming
modelstringNo-One-time model override for this request only. Use a stable model ID such as claude-sonnet-5. Does not change the session's default model

Example - Streaming (default)

curl -N -X POST https://api.jenova.ai/v1/messages \
  -H "Authorization: Bearer jnv_sk_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "agent": "my-support-agent",
    "user": "user_12345",
    "content": "Hello, I need help with my account"
  }'

The response is an SSE stream (see Streaming for event format). Persistent requests include the new session_id; ephemeral: true requests omit session_id and must use streaming.

Example - JSON (non-streaming)

curl -X POST https://api.jenova.ai/v1/messages \
  -H "Authorization: Bearer jnv_sk_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "agent": "my-support-agent",
    "user": "user_12345",
    "content": "Hello, I need help with my account",
    "stream": false
  }'

Response

Streaming responses emit the SSE events documented in Streaming. Non-streaming responses return the JSON message shape shown in Continue Session, including stop_reason and usage for the completed request.

If a non-streaming run is still processing after 90 seconds, the API returns 202 Accepted with status: "processing", session_id, run_id, and message. The run continues after the response or client disconnect; check the session's messages, or use streaming for longer workflows.

Errors

StatusCodeCondition
400missing_required_fieldagent is required
400invalid_payloadMalformed JSON or a field has an invalid type
400bad_requestInvalid ephemeral mode, or user/session_name exceeds its maximum length
400content_or_uploaded_files_requiredNeither content nor file_urls provided
400content_too_longMessage content exceeds maximum token length
400exceed_max_upload_filesMore than 10 file URLs in a single request
400unsupported_file_formatA file URL has an unsupported file extension
400invalid_file_urlA file URL is malformed or not HTTPS
400invalid_model_selectionModel override is not a valid production model
402insufficient_creditsNot enough credits to create the session or send the message
404agent_not_foundAgent does not exist or is not accessible to your account

Continue Session

POST /sessions/{session_id}/messages

Sends a message to an existing persistent session and receives the agent's response. Responses stream via SSE by default; set stream: false for JSON.

Request body

FieldTypeRequiredDefaultDescription
contentstringConditional-Message text. Required unless file_urls is provided
file_urlsstring[]Conditional-URLs of files to attach. Required unless content is provided
streambooleanNotruetrue for SSE streaming, false for JSON. MCP authorization requires streaming
modelstringNo-One-time model override for this request only. Use a stable model ID such as claude-sonnet-5. Does not change the session's default model

Example - Streaming (default)

curl -N -X POST https://api.jenova.ai/v1/sessions/ses_abc123/messages \
  -H "Authorization: Bearer jnv_sk_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "How do I reset my password?"
  }'

Example - JSON (non-streaming)

curl -X POST https://api.jenova.ai/v1/sessions/ses_abc123/messages \
  -H "Authorization: Bearer jnv_sk_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "How do I reset my password?",
    "stream": false
  }'

Response 200 OK

{
  "id": "msg_xyz789",
  "session_id": "ses_abc123",
  "sequence": 4,
  "from": {
    "type": "agent",
    "name": "my-support-agent"
  },
  "type": "external",
  "time": "2026-05-19T10:31:05Z",
  "content": "To reset your password, go to Settings > Security > Change Password...",
  "model": "claude-sonnet-5",
  "files": [],
  "stop_reason": "end_run",
  "usage": {
    "cost": "0.0015"
  }
}

If the session already has an active run or queued messages, the API returns JSON 202 Accepted with status: "queued", session_id, run_id, and message_id. The user message is processed by the active run.

Errors

StatusCodeCondition
400invalid_payloadMalformed JSON or a field has an invalid type
400content_or_uploaded_files_requiredNeither content nor file_urls provided
400content_too_longMessage content exceeds maximum token length
400exceed_max_upload_filesMore than 10 file URLs in a single request
400unsupported_file_formatA file URL has an unsupported file extension
400invalid_file_urlA file URL is malformed or not HTTPS
400invalid_model_selectionModel override is not a valid production model
402insufficient_creditsNot enough credits to send a message
404session_not_foundSession does not exist
404session_not_ownedSession belongs to another developer or does not match the supplied user

Idempotency

POST /messages and POST /sessions/{session_id}/messages accept an optional Idempotency-Key header. Use a unique key for each logical user send so network retries or double-submits do not create duplicate runs.

curl -N -X POST https://api.jenova.ai/v1/messages \
  -H "Authorization: Bearer jnv_sk_xxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: send-user_12345-2026-05-19T10:30:00Z" \
  -d '{
    "agent": "my-support-agent",
    "user": "user_12345",
    "content": "Hello"
  }'

Non-streaming: Retrying returns the saved 200 or 202 response with the Idempotent-Replayed: true header.

Streaming: The stream is not replayed. Retrying while running or after completion returns an idempotency error with the original run_id and, for persistent requests, session_id. Use GET /sessions/{session_id}/runs/{run_id} to check an active run, or GET /sessions/{session_id}/messages to fetch persisted results.

Completed streaming retry example:

HTTP/1.1 409 Conflict
Content-Type: application/json
{
  "error": {
    "code": "idempotency_key_reused",
    "message": "This Idempotency-Key has already completed. Streaming responses cannot be replayed; use the returned session_id and run_id to retrieve the result."
  },
  "idempotency": {
    "status": "completed",
    "session_id": "ses_abc123",
    "run_id": "run_abc123"
  }
}

Idempotency errors

StatusCodeCondition
409idempotency_key_reusedThe same key was used with a different request
409idempotency_key_in_useThe original request is still running
409idempotency_key_reusedThe original streaming request already completed and cannot be replayed

List Messages

GET /sessions/{session_id}/messages

Returns a paginated list of visible conversation messages. The first page contains the most recent messages; within each page, messages are in chronological order (oldest first). sequence is a stable ordering number within the session.

Query parameters

ParameterTypeDefaultMaxDescription
limitinteger20100Messages per page
cursorstring--Pagination cursor

Example

curl "https://api.jenova.ai/v1/sessions/ses_abc123/messages?limit=50" \
  -H "Authorization: Bearer jnv_sk_xxx"

Response 200 OK

{
  "items": [
    {
      "id": "msg_002",
      "session_id": "ses_abc123",
      "sequence": 4,
      "from": {
        "type": "agent",
        "name": "my-support-agent"
      },
      "type": "external",
      "time": "2026-05-19T10:31:05Z",
      "content": "To reset your password, go to Settings > Security...",
      "model": "claude-sonnet-5",
      "files": []
    }
  ],
  "next_cursor": "",
  "has_more": false
}

Message object

FieldTypeDescription
idstringMessage ID (prefixed with msg_)
session_idstringParent session ID
sequenceintegerStable ordering number within the session
fromobjectSender object with type ("user" or "agent") and name
typestringMessage type, usually external for visible conversation messages
timestringISO 8601 timestamp
contentstringText content. Present on external messages
modelstringStable model ID that generated the response. Only present on agent messages
filesarrayAttached or generated files included with the message. Each entry includes file_id, name, url, format, and size when known
stop_reasonstringPresent on completed agent messages. Current value is end_run
agentstringExecuting agent slug, when available
agent_namestringExecuting agent display name, when available

File object

FieldTypeDescription
file_idstringJenova file ID, when available
namestringFile name
urlstringFile URL, when available
formatstringLowercase file format, such as pdf, png, or csv
sizeintegerFile size in bytes, when known

Errors

StatusCodeCondition
400bad_requestInvalid query parameter
404session_not_foundSession does not exist
404session_not_ownedSession belongs to another developer or does not match the supplied user

Get Message

GET /sessions/{session_id}/messages/{message_id}

Retrieves a single visible message by ID.

Response 200 OK

Returns a single message object with the same structure as the list response.

Errors

StatusCodeCondition
404session_not_foundSession does not exist
404session_not_ownedSession belongs to another developer or does not match the supplied user
404not_foundMessage does not exist in this session

File Attachments

Pass publicly accessible HTTPS URLs in the file_urls field.

LimitValue
Max files per message10
Max file size20 MB per file

Supported formats

  • Images: JPG, JPEG, PNG, WebP
  • Documents: PDF, DOCX, XLSX, PPTX, TXT, CSV, RTF, MD, HTML, XML, JSON, LOG
  • Code: JS, TS, TSX, JSX, PY, Java, Go, C, CPP, H, HPP, CS, RB, PHP, RS, Swift, KT, Scala, SQL, CSS, YAML, YML

When listing messages, attached files appear in the message's files array.


Sessions

Sessions are persistent conversations between an end user and an agent. Most integrations can create them implicitly with POST /messages.

Create Session

POST /sessions

Creates an empty persistent session bound to a specific agent. Use this when you need a session ID before the first message; otherwise prefer POST /messages.

Note: ephemeral is not accepted on POST /sessions; use POST /messages with ephemeral: true for no-storage one-off requests.

Request body

FieldTypeRequiredDescription
agentstringYesThe agent's slug identifier
userstringNoYour external end user identifier (max 255 characters). If omitted, defaults to your developer account
session_namestringNoDisplay name for the session (max 200 characters)

Example

curl -X POST https://api.jenova.ai/v1/sessions \
  -H "Authorization: Bearer jnv_sk_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "agent": "my-support-agent",
    "user": "user_12345",
    "session_name": "Billing inquiry"
  }'

Response 201 Created

{
  "id": "ses_abc123",
  "session_name": "Billing inquiry",
  "agent": "my-support-agent",
  "user": "user_12345",
  "model": "claude-sonnet-5",
  "created_at": "2026-05-19T10:30:00Z",
  "updated_at": "2026-05-19T10:30:00Z"
}

Errors

StatusCodeCondition
400invalid_payloadMalformed JSON or a field has an invalid type
400missing_required_fieldagent is required
400bad_requestephemeral was provided, or user/session_name exceeds its maximum length
402insufficient_creditsNot enough credits to create a session
404agent_not_foundAgent does not exist or is not accessible to your account

List Sessions

GET /sessions

Returns a paginated list of your sessions, ordered by most recently updated.

Query parameters

ParameterTypeDescription
limitintegerItems per page (default 20, max 100)
cursorstringPagination cursor
userstringFilter by end user identifier (max 255 characters)
agentstringFilter by agent slug

Example

curl "https://api.jenova.ai/v1/sessions?user=user_12345&limit=10" \
  -H "Authorization: Bearer jnv_sk_xxx"

Response 200 OK

{
  "items": [
    {
      "id": "ses_abc123",
      "session_name": "Billing inquiry",
      "agent": "my-support-agent",
      "user": "user_12345",
      "model": "claude-sonnet-5",
      "created_at": "2026-05-19T10:30:00Z",
      "updated_at": "2026-05-19T11:15:00Z"
    }
  ],
  "next_cursor": "eyJ2IjoxLCJrIjoiY3VyXzAxIn0",
  "has_more": true
}

Errors

StatusCodeCondition
400bad_requestInvalid query parameter

Get Session

GET /sessions/{session_id}

Retrieves a single session by ID.

Response 200 OK

Returns a session object with the same structure as the create response.

Errors

StatusCodeCondition
404session_not_foundSession does not exist
404session_not_ownedSession belongs to another developer or does not match the supplied user

Rename Session

PATCH /sessions/{session_id}

Updates the display name of a session.

Request body

FieldTypeRequiredDescription
session_namestringYesNew display name (max 200 characters)

Response 200 OK

Returns the updated session object.

Errors

StatusCodeCondition
400invalid_payloadMalformed JSON or a field has an invalid type
400missing_required_fieldsession_name is required
400bad_requestsession_name exceeds its maximum length
404session_not_foundSession does not exist
404session_not_ownedSession belongs to another developer or does not match the supplied user

Delete Session

DELETE /sessions/{session_id}

Permanently deletes a session and all of its messages. The session must not have an active run.

Response 204 No Content

Errors

StatusCodeCondition
404session_not_foundSession does not exist
404session_not_ownedSession belongs to another developer or does not match the supplied user
409busySession has an active run - cancel it first

Operations

These endpoints are recovery and editing controls for persistent sessions. Most integrations only need Cancel; use the other operations when you intentionally want to alter or recover session state. All operations support the optional user ownership guard described in End User.

Cancel Active Run

POST /sessions/{session_id}/cancel

Cancels the current in-progress agent run. This does not delete messages that were already completed before cancellation.

Request body

FieldTypeRequiredDescription
run_idstringNoOptional stale-run guard. If supplied and it does not match the active run, the API returns 409 stale_run

Response 204 No Content

Errors

StatusCodeCondition
400cancel_not_allowedNo active run to cancel, or cancellation not permitted
404session_not_foundSession does not exist
404session_not_ownedSession belongs to another developer or does not match the supplied user
409stale_runProvided run_id does not match the active run

Undo Active Run

POST /sessions/{session_id}/undo

Cancels the active run, waits for it to stop, then removes any messages it already added. No additional output is persisted after the undo is issued.

Request body

FieldTypeRequiredDescription
run_idstringNoOptional stale-run guard. If supplied and it does not match the active run, the API returns 409 stale_run

Response 200 OK

{
  "session_id": "ses_abc123",
  "run_id": "run_abc123",
  "deleted": ["msg_002", "msg_001"]
}

If the run is cancelled before any message is completed, deleted is an empty array.

Errors

StatusCodeCondition
400cancel_not_allowedNo active run to undo, or cancellation not permitted
404session_not_foundSession does not exist
404session_not_ownedSession belongs to another developer or does not match the supplied user
409stale_runProvided run_id does not match the active run
409busyThe session is temporarily unavailable because another update is in progress

Delete Recent Messages

POST /sessions/{session_id}/messages/delete

Removes the N most recent messages from an idle session. The session must not have an active run.

Request body

FieldTypeRequiredDescription
countintegerYesNumber of recent messages to delete from the end (must be greater than 0)

Response 200 OK

{
  "deleted": ["msg_002", "msg_001"]
}

Errors

StatusCodeCondition
400bad_requestcount missing, zero, or negative; or no messages to delete
404session_not_foundSession does not exist
404session_not_ownedSession belongs to another developer or does not match the supplied user
409busySession has an active run

Fork Session

POST /sessions/{session_id}/fork

Creates a new session by copying the source session up to a specific message. The source session must not have an active run.

Request body

FieldTypeRequiredDescription
message_idstringNoFork point message ID. If omitted, forks from the last message

Response 201 Created

Returns the newly created session object with the same structure as session creation.

Errors

StatusCodeCondition
400bad_requestmessage_id is invalid
402insufficient_creditsNot enough credits to fork a session
404not_foundmessage_id does not exist in this session
404session_not_foundSession does not exist
404session_not_ownedSession belongs to another developer or does not match the supplied user
409busySource session has an active run

Get Run Status

GET /sessions/{session_id}/runs/{run_id}

Returns the current state of an active run. Use this after a dropped SSE connection or an idempotency response that returned a run_id. After a run finishes, fetch results with GET /sessions/{session_id}/messages.

Response 200 OK

{
  "session_id": "ses_abc123",
  "run_id": "run_abc123",
  "status": "streaming",
  "started_at": "2026-05-19T10:30:00Z",
  "updated_at": "2026-05-19T10:30:06Z",
  "content": "Partial visible response text so far"
}

The response may also include recent progress hints while the run is active.

Errors

StatusCodeCondition
400bad_requestThe session is ephemeral
404session_not_foundSession does not exist
404session_not_ownedSession belongs to another developer or does not match the supplied user
404not_foundThe run is not active for this session

Credits

Get Balance

GET /credits/balance

Returns your current credit balance.

Response 200 OK

{
  "balance": "123.45"
}

Agents

Create and edit custom agents in the dashboard. API support for creating and editing agents is coming soon.

Scheduled/background workflows are not currently supported through the API. Support is coming soon.

List Agents

GET /agents

Returns the agents available to your API key. Use the agent value when creating sessions or sending messages.

Response 200 OK

{
  "agents": [
    {
      "agent": "jenova",
      "display_name": "Jenova",
      "description": "General-purpose Jenova agent"
    },
    {
      "agent": "my-support-agent",
      "display_name": "Support Agent",
      "description": "Answers customer questions"
    }
  ]
}
FieldTypeDescription
agentstringStable agent slug to pass as the agent value
display_namestringHuman-readable display name
descriptionstringAgent description

Models

List Models

GET /models

Returns all models available for use in the model field when sending messages.

Response 200 OK

{
  "models": [
    {
      "id": "claude-opus-4-8",
      "name": "Claude Opus 4.8",
      "thinking_variant": "claude-opus-4-8-thinking"
    },
    {
      "id": "claude-opus-4-8-thinking",
      "name": "Claude Opus 4.8 (Thinking)"
    },
    {
      "id": "kimi-k2.6",
      "name": "Kimi K2.6",
      "thinking_variant": "kimi-k2.6-thinking"
    }
  ]
}
FieldTypeDescription
idstringStable model identifier. Pass this as the model value in Send Message
namestringHuman-readable display name
thinking_variantstringModel ID of the thinking/reasoning variant. Present only on base models that support reasoning

Models with a thinking_variant support extended reasoning. Use the variant ID directly in the model field to enable it.

If no model is specified when sending a message, the agent's default model is used.


Docs

GET /docs?lang=en
GET /doc?lang=en

Returns this reference as Markdown. Use lang to select the language.


Streaming (SSE)

When stream is omitted or true (the default), message responses are delivered as Server-Sent Events. Use message_completed events to identify messages that are ready to fetch or render.

Timeout: SSE connections stay open for up to 60 minutes. Non-streaming requests wait up to 90 seconds, then return 202 Accepted while the run continues.

Connection headers

The SSE response sets these headers:

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
X-Accel-Buffering: no
X-Run-Id: run_abc123

X-Run-Id is available before the first SSE event.

Reconnect and recovery

SSE streams are not replayed. If the connection drops, use the captured session_id and run_id to recover state:

curl "https://api.jenova.ai/v1/sessions/ses_abc123/runs/run_abc123" \
  -H "Authorization: Bearer jnv_sk_xxx"

If the run is still active, this returns the current status, partial text, and recent progress. If it returns 404 not_found, the run is no longer active; fetch the session's messages to reconcile completed output:

curl "https://api.jenova.ai/v1/sessions/ses_abc123/messages?limit=20" \
  -H "Authorization: Bearer jnv_sk_xxx"

Frame format

Each SSE frame follows the standard format:

event: <event_type>
data: <json_payload>

Two newlines terminate each frame.

Event types

Streams include lifecycle, text delta, thinking, progress, warning, message completion, MCP connection, error, final, and ping events. Some event types are only sent when relevant.

For ephemeral requests (ephemeral: true), every SSE event omits session_id. Use run_id only to correlate events inside that one stream.

For final reconciliation on persistent requests, wait for stream_ended, then call List Messages.

Common fields on run-scoped events:

FieldDescription
session_idSession ID. Omitted for ephemeral streams. Capture this from stream_started for follow-up requests when using persistent POST /messages
run_idCurrent run ID, when available

stream_started

Sent once when the run starts.

FieldDescription
agentSession agent slug, when available

stream_delta

Sent repeatedly as the agent generates visible response text. Concatenate chunk_content values in seq order to build the streamed response.

event: stream_delta
data: {"session_id":"ses_abc123","run_id":"run_abc123","chunk_content":"To reset your ","seq":1}
FieldDescription
chunk_contentText chunk
seqMonotonic chunk sequence within the stream

stream_thinking

Sent repeatedly while the agent emits thinking output. Use this for a separate thinking indicator or trace view; do not concatenate it into the final response text.

FieldDescription
contentThinking text chunk

stream_progress

Reports user-visible activity during generation, such as reading a document, searching the web, or waiting for a user action. These events are intended for transient UI display; ignore unknown fields. Use completed messages for the authoritative message history.

Advanced: message requests accept include_progress: false to omit only stream_progress. Lifecycle events, ping, message_completed, errors, and terminal events are still sent when relevant.

FieldDescription
stateLifecycle state: running, in-progress, success, failed, skipped, complete, cancelled, among others. Handle unknown values gracefully
labelHuman-readable activity label

Some progress events may include url, file_name, or server_name as optional display hints.

message_completed

Sent each time a message is complete and ready to fetch or render.

This is a boundary marker, not the full message object. Fetch the message if you need content or metadata.

event: message_completed
data: {"session_id":"ses_abc123","run_id":"run_abc123","message_id":"msg_abc123","sequence":4,"from":{"type":"agent","name":"Jenova"},"type":"external"}
FieldDescription
message_idCompleted message ID
sequenceStable ordering number within the session
fromSender object with type (user or agent) and name
typeMessage type: external or internal

mcp_connection

Sent when the agent needs the end user to connect or authorize one or more MCP servers before it can continue. This event is only available in streaming mode.

FieldDescription
connection_server_listMCP servers that need a connection action. Each server includes mcp_server_id, mcp_server_name, and optional auth_url
user_action_deadline_unixUnix timestamp when the connection user action expires

mcp_connection_resolved

Sent when the MCP connection user action has been resolved or has expired.

No additional fields beyond the common run-scoped fields.

warning

Sent for non-fatal warnings during a run.

FieldDescription
messageHuman-readable non-fatal warning
codeOptional warning code

stream_error

Sent when a run fails. A final stream_ended event may follow with success:false and stop_reason:"error".

FieldDescription
codeError code
messageHuman-readable error message

stream_ended

Sent once when the run finishes. This is the final event for the stream.

event: stream_ended
data: {"session_id":"ses_abc123","run_id":"run_abc123","success":true,"stop_reason":"end_run","usage":{"cost":"0.0032"}}

Failure example:

event: stream_ended
data: {"session_id":"ses_abc123","run_id":"run_abc123","success":false,"stop_reason":"user_cancelled","usage":{"cost":"0.0012"}}
FieldDescription
successWhether the run completed successfully
stop_reasonTerminal run reason: end_run, user_cancelled, user_action_timeout, or error
usageUsage object for this request. Currently includes cost when available

ping

Keepalive frames sent every 15 seconds to prevent proxy/CDN timeouts. Ignore these in your client.


MCP Server Integration

Connect your agents to external tools via the Model Context Protocol (MCP):

  • Jenova-managed MCP servers: Jenova-hosted servers for search, content retrieval, document generation, and other built-in capabilities
  • Remote MCP servers: Other remote MCP servers configured for your agent

MCP servers must be configured in the dashboard when creating or editing your agent. To use your own MCP server, add it to a custom agent, then call that agent through the API. The API executes tools enabled in the agent's configuration; no additional setup is required in API requests.

When an agent uses MCP tools during a response, progress events are sent in the stream as they occur:

event: stream_progress
data: {"session_id":"ses_...","run_id":"run_...","state":"running","label":"Searching Google"}

If the agent needs the end user to connect or authorize an MCP server during the run, streaming responses may include mcp_connection and mcp_connection_resolved events. Present the connection server list to your end user and open the provided auth_url when present. Keep the SSE stream open while the user connects or authorizes the server.

After authorization, Jenova stores the token, the authorization window shows a completion page, and the same run continues automatically. The end user does not need to resend the message.

If the end user does not connect, authorize, skip, or mute before user_action_deadline_unix, the run ends with stop_reason:"user_action_timeout". You may also cancel the active run with POST /sessions/{session_id}/cancel.

Store the auth_url client-side. If the client disconnects during authorization, the URL remains valid until user_action_deadline_unix. For persistent requests, reconnect with Get Run Status, or fetch messages after the run finishes.

Non-streaming requests (stream: false) do not support MCP connection user actions; use streaming for agents that may need this interaction.

Skip MCP Connection

POST /sessions/{session_id}/mcp/connection/skip

Dismisses a pending MCP connection user action and lets the run continue without that connection.

To mute future connection prompts for one server for the same API user, include mcp_server_id and mute. Supported values are 24h and forever.

Request body

FieldTypeRequiredDescription
run_idstringYesActive run ID from the mcp_connection event
mcp_server_idstringNoRequired with mute; use the mcp_server_id from the event
mutestringNo24h or forever

Response 204 No Content

The stream emits mcp_connection_resolved, then the run continues with the same run_id.

Errors

StatusCodeCondition
400bad_requestMissing run_id, no active run, or no MCP connection to skip
400bad_requestInvalid mute, or missing mcp_server_id when mute is set
404session_not_foundSession does not exist
404session_not_ownedDoes not match the supplied user
409stale_runrun_id does not match the active run

Billing

All costs are deducted from your developer credit balance. View usage and top up credits at www.jenova.ai/platform.

Pricing

OperationCost
Create Session$0.01 flat for every persistent session, including sessions created implicitly by POST /messages
Fork Session$0.05 flat
Send MessageVariable (see below)

Message cost depends on:

  • Model - different models have different per-token costs
  • Context length - longer sessions consume more input tokens per request
  • Workflow complexity - longer workflows and heavier tool use (web search, file generation, document analysis) increase total token consumption

The actual cost is returned as stream_ended.usage.cost for streaming requests and usage.cost for non-streaming JSON requests.

Credit holds

Each new message run places a $0.50 hold on your credit balance before execution begins. This reserves funds for the run. Queued follow-up messages in an active run do not create additional holds; the active run's usage is checked against your remaining balance.

When the run completes, the hold is settled to the actual cost and the difference is released. Cancelled and failed runs are charged only for usage already incurred. If a request fails before reaching the model, the full hold is released.

This means your available balance may temporarily appear lower during in-flight requests. You need at least $0.50 in available balance to send a message to an existing session or to send an ephemeral message. A first persistent POST /messages request creates a session and needs at least $0.51 to cover the message hold plus the session creation fee.


Rate Limits

Every developer account is subject to three rate limit dimensions:

DimensionDefaultDescription
RPM (Requests Per Minute)60Fixed window per minute
RPD (Requests Per Day)1,000Fixed window per day
Concurrent5Maximum simultaneous in-flight requests

GET and HEAD requests do not consume concurrent slots. cancel, undo, and mcp/connection/skip do not consume concurrent slots, so these operations remain available when all slots are in use. These requests still count toward RPM and RPD.

Response headers

Authenticated API responses include rate limit headers:

HeaderDescription
X-RateLimit-LimitYour RPM limit
X-RateLimit-RemainingRequests remaining in the current minute window
X-RateLimit-ResetUnix timestamp when the current window resets
Retry-AfterSeconds to wait before retrying (only on 429)

When a limit is exceeded, the API returns 429 Too Many Requests:

{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded. Please retry after 12 seconds."
  }
}

Error Handling

Immediate HTTP errors and non-streaming run errors follow a consistent envelope:

{
  "error": {
    "code": "error_code_string",
    "message": "Human-readable description"
  }
}

Error messages are localized based on the lang parameter (see Localization).

Streaming run errors are delivered as stream_error events. A failed run may still send a final stream_ended event with success:false and stop_reason set. Non-streaming run errors may also include a top-level usage object when cost data is available.

Endpoint-specific errors are documented inline under each endpoint.

Run Errors After Start

Run errors surface after a message run has already started. In streaming mode, they appear as stream_error events and may be followed by stream_ended with success:false. In non-streaming mode, they are returned as a JSON error response with the HTTP status below.

Non-streaming HTTP StatusCodeDescription
400content_policy_violationThe model provider rejected the request for content policy reasons
404session_not_foundSession was deleted before the run could execute
409busySession became busy or temporarily unavailable before the run could start
413total_image_size_exceededCombined image payload exceeds the model's per-request size limit
500internal_errorUnexpected run failure
502llm_api_errorModel provider or upstream model API error

Pagination

List endpoints use cursor-based pagination:

{
  "items": [],
  "next_cursor": "eyJ2IjoxLCJrIjoiY3VyXzAyIn0",
  "has_more": true
}
ParameterTypeDefaultMaxDescription
limitinteger20100Number of items per page
cursorstring--Opaque cursor from a previous next_cursor

Pass next_cursor as the cursor query parameter to fetch the next page. When has_more is false, there are no more results.


Localization

All endpoints accept an optional lang query parameter to control the language of error messages and any localized content.

SourcePriorityExample
lang query parameterHighest?lang=zh
Accept-Language headerFallbackAccept-Language: ja
DefaultLowestEnglish (en)

You can append ?lang=xx to any request URL:

POST /sessions?lang=zh
GET /sessions/ses_abc123/messages?lang=ja

Supported languages: en, zh, ja, ko, es, fr, de, it, pt, ru, id, th, vi


Privacy and Data

Jenova does not use API prompts, outputs, conversation history, uploaded files, agent instructions, or knowledge bases to train Jenova models.

For third-party model providers, Jenova uses commercial API channels, account settings, contractual commitments, or opt-outs intended to prevent customer content from being used to train provider models.

Jenova stores and processes API data using U.S. infrastructure. Third-party providers may process data in other jurisdictions as described in the Privacy Policy and Terms of Use.

For full details, see the Terms of Use, Privacy Policy, and Usage Policy.


Support