Skip to content

Runs API ​

Log and retrieve runs.

Renamed in 0.8.0

This resource was /api/v1/conversations. It is now /api/v1/runs, and the webhook events are run.created / run.updated. The old paths and event names no longer work — there is no alias. Update any integration that calls them.

The conversationId field keeps its name: it is an identifier, and identifiers do not get renamed.

Create Run ​

http
POST /api/v1/runs

Request Body ​

json
{
  "promptId": "prompt_123",
  "content": "User: Hello, I need help\nAssistant: Hi! How can I help you today?",
  "status": "completed",
  "llmModel": "gpt-4o"
}

Required Fields ​

FieldTypeDescription
promptIdstringAssociated prompt ID
contentstringRun transcript (see Content Format)

Optional Fields ​

FieldTypeDescription
statusstringcompleted, abandoned, in_progress
llmModelstringModel used
companyNamestringCustomer company name
idempotencyKeystringUnique key to prevent duplicate submissions (see Idempotency)
toolDefinitionsarrayTools the agent can call — { name, description?, parameters? }[]. Unlocks tool-contract grading (see Tool Definitions).

Response ​

json
{
  "id": "conv_456",
  "promptId": "prompt_123",
  "status": "completed",
  "createdAt": "2025-01-20T14:30:00Z",
  "_links": {
    "appendMessages": {
      "href": "/api/v1/runs/conv_456/messages",
      "method": "POST",
      "hint": "Append turns to this run. Send status: 'completed' on the final call to trigger analysis."
    }
  }
}

Status: 200 OK

Append Messages ​

Append turns to an existing run. Use this when you want the run to appear in Converra mid-flight (long chats, voice, streaming agents) rather than batching the full transcript at the end.

http
POST /api/v1/runs/:id/messages

Pair this with POST /api/v1/runs called once with status: "active" to open the run. Keep the returned id and append to it as turns happen. Flip status to "completed" on the final call — that's what triggers analysis.

Request Body ​

json
{
  "messages": [
    { "role": "user", "content": "Can you check my order status?" },
    { "role": "assistant", "content": "Sure — what's your order number?" }
  ],
  "status": "active",
  "metadata": { "sessionId": "sess_abc" }
}

Fields ​

FieldTypeRequiredDescription
messagesarrayyesNew turns to append. At least one.
statusstringno"active" (default — no change) or "completed" (triggers analysis). Send "completed" exactly once per run.
metadataobjectnoShallow-merged into the run's existing metadata.
toolDefinitionsarraynoTools the agent can call (see Tool Definitions). Set once; later appends can add newly-seen tools.

Each message: { role: "user" | "assistant" | "system" | "tool", content: string, timestamp?: string, model?: string, tokens?: number, toolCalls?: ToolCall[], usage?: { promptTokens, completionTokens }, latencyMs?: number, metadata?: object }.

The tool role represents a tool/function-call result. For tool messages, content may be empty — the call details go in toolCalls. All other roles require non-empty content.

Response ​

json
{
  "id": "conv_456",
  "userId": "user_789",
  "title": "...",
  "content": "User: ...\nAssistant: ...",
  "messages": [ /* full accumulated messages */ ],
  "status": "active",
  "createdAt": "2025-01-20T14:30:00Z",
  "updatedAt": "2025-01-20T14:31:12Z",
  "metadata": { /* merged metadata */ }
}

Status: 200 OK

Behavior ​

  • Atomic append. The underlying update pushes the new messages and recomputes content server-side in the same operation — concurrent appends are safe.
  • Insights trigger. When status transitions to "completed" and the run is linked to a prompt (agentId / systemPromptId), insights are generated asynchronously. Runs created with only a free-text promptName will complete but not be analyzed — link a prompt if you want insights.
  • Webhooks. Fires run.updated with the list of changed fields on every append.
  • 404 if the run doesn't exist or isn't owned by your account.

Tool Definitions ​

If your agent calls tools, send their definitions so Converra can grade the tool contract (description, parameters, response) — not just whether the agent used the tool. This powers the Tool Calling view's "Fix the tool vs. Fix the agent" split: a confusing tool API surfaces as Fix the tool; a prompt misusing a sound tool as Fix the agent.

Send toolDefinitions on create (or the first append) — an array of:

json
{
  "name": "lookup_order",
  "description": "Look up an order by its ID. Returns status and line items.",
  "parameters": {
    "type": "object",
    "properties": { "order_id": { "type": "string" } },
    "required": ["order_id"]
  }
}
FieldTypeRequiredDescription
namestringyesTool name — must match the name used in the run's toolCalls.
descriptionstringnoThe description the model sees. Graded for clarity.
parametersobjectnoJSON Schema for the arguments. Graded for clarity, and used to validate the arguments the agent passed.
  • Without definitions, tool behavior is still graded (whether calls succeeded and were well-formed), but the description/parameter dimensions are marked unevaluable — Converra never invents a contract.
  • Send once. Definitions are deduped by name (descriptions trimmed, sensitive values redacted), so it's safe to resend the full list; later appends can add newly-seen tools. Up to 500 per run.
  • Most SDK paths fill this in for you — see Capturing tool definitions in the SDK Integration guide.

List Runs ​

http
GET /api/v1/runs

Query Parameters ​

ParameterTypeDescription
promptIdstringFilter by prompt
statusstringFilter by status
hasInsightsbooleanFilter to runs with insights
limitnumberMax results (default: 20)
offsetnumberPagination offset

Response ​

json
{
  "data": [
    {
      "id": "conv_456",
      "promptId": "prompt_123",
      "status": "completed",
      "hasInsights": true,
      "createdAt": "2025-01-20T14:30:00Z"
    }
  ],
  "pagination": {
    "total": 150,
    "limit": 20,
    "offset": 0
  }
}

Get Run ​

http
GET /api/v1/runs/:id

Query Parameters ​

ParameterTypeDescription
includeInsightsbooleanInclude insights (default: true)

Response ​

json
{
  "id": "conv_456",
  "promptId": "prompt_123",
  "content": "User: Hello...\nAssistant: Hi!...",
  "status": "completed",
  "llmModel": "gpt-4o",
  "insights": {
    "sentiment": "positive",
    "taskCompleted": true,
    "topics": ["greeting", "product inquiry"],
    "summary": "User asked about product availability..."
  },
  "createdAt": "2025-01-20T14:30:00Z"
}

Get Run Insights ​

http
GET /api/v1/runs/:id/insights

Response ​

json
{
  "conversationId": "conv_456",
  "sentiment": "positive",
  "sentimentScore": 0.82,
  "taskCompleted": true,
  "topics": ["greeting", "product inquiry", "availability"],
  "summary": "User inquired about product availability. Agent provided accurate information and the user was satisfied.",
  "issues": [],
  "createdAt": "2025-01-20T14:35:00Z"
}

Content Format ​

The content field accepts two formats:

JSON string containing a messages array. This format provides the richest data for insights.

json
{
  "content": "{\"messages\":[{\"role\":\"user\",\"content\":\"Hello\"},{\"role\":\"assistant\",\"content\":\"Hi! How can I help?\"}]}"
}

Full schema:

typescript
interface StructuredContent {
  messages: Array<{
    role: 'user' | 'assistant' | 'system';
    content: string;
    timestamp?: string;    // ISO 8601
    model?: string;        // Model used for this response
    tokens?: number;       // Token count
  }>;
  metadata?: {
    userId?: string;       // Your user ID
    sessionId?: string;    // Session identifier
    channel?: string;      // web, mobile, api, etc.
    language?: string;     // ISO language code
    [key: string]: any;    // Custom fields
  };
}

Text Format ​

Plain text with role labels:

json
{
  "content": "User: Hello\nAssistant: Hi! How can I help?"
}

Accepted role labels: User/Assistant, Human/AI, Customer/Agent

Content Limits ​

LimitValueDescription
Max content size512 KBMaximum size of the content field
Max messages500Maximum messages in structured format
Max message length100 KBMaximum size per individual message

Exceeding these limits returns a 413 Payload Too Large error.

See the Logging Guide for detailed examples.


Idempotency ​

Prevent duplicate run submissions using the idempotencyKey field.

json
{
  "promptId": "prompt_123",
  "content": "User: Hello\nAssistant: Hi!",
  "idempotencyKey": "session_abc123_1705764600"
}

Behavior:

  • If a run with the same idempotencyKey already exists, the existing run is returned (no duplicate created)
  • Keys are scoped to your account
  • Keys expire after 24 hours

Recommended key formats:

  • {sessionId}_{timestamp} - For session-based logging
  • {externalId} - If you have your own run IDs
  • {hash of content} - For content-based deduplication

Error Responses ​

Not Found ​

json
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Conversation not found"
  }
}

Validation Error ​

json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request body",
    "details": {
      "promptId": "Required field"
    }
  }
}