> ## Documentation Index
> Fetch the complete documentation index at: https://www.krea.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent API

> Manage Krea Agent sessions over HTTP: start a session, wait for a turn, read its results, and send follow-ups with your API key.

Use the Agent API to give Krea Agent a task from your own application. Requests and responses use camelCase JSON fields. The Agent works in the background while your application reads its progress and results.

| Endpoint | Purpose |
| - | - |
| [`POST /agent/send-message`](/docs/api-reference/agent/send-agent-message) | Start a session or submit a follow-up turn. |
| [`GET /agent/session/{sessionId}`](/docs/api-reference/agent/get-agent-session) | Read the current session without waiting. |
| [`POST /agent/wait-for-session`](/docs/api-reference/agent/wait-for-agent-session) | Wait for a turn to finish, up to a timeout. |

<Warning>
  The Agent works in the background. It does not ask questions, and cost and permission prompts are approved automatically. Include all requirements and constraints in your prompt. Review the task before submitting it.
</Warning>

## Authentication, access, and billing

Send `Authorization: Bearer KREA_API_KEY` with every request to `https://api.krea.ai`. Personal, service, and legacy API keys use the standard [public API authentication](/docs/developers/api-keys-and-billing).

You can only read or continue sessions owned by the authenticated caller in the authenticated workspace. Knowing a session ID does not grant access. Krea file attachments must also be accessible to that caller in that workspace.

<Info>
  Agent turns use the workspace's compute unit balance. They do **not** use the API balance. Agent plan entitlements and Free plan message limits still apply. See [Agent plans, access, and credits](/docs/user-guide/agent/plans-and-credits).
</Info>

Each response includes a `url` where you can open the session in the Krea app with the appropriate account and workspace access.

## Start a session and wait for its result

Set `KREA_API_KEY` in your environment first. Choose Bash with `curl` and `jq`, Python with `requests`, or Node.js with built-in `fetch` or the Krea SDK.

<Steps>
  <Step title="Submit the first turn">
    Omit `sessionId` to create a session. Save the returned `sessionId` and `runId`. The response returns immediately with `status: "running"`.
  </Step>

  <Step title="Wait for this turn">
    Pass both IDs to `wait-for-session`. If `timedOut` is `true`, call it again with the same IDs. A timeout does not cancel the turn.
  </Step>

  <Step title="Read the result">
    Check `status` when `finished` is `true`. Inspect `text`, `media`, and any `error`, or open the returned `url`.
  </Step>
</Steps>

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light-default","dark":"vesper"}}
  set -euo pipefail
  : "${KREA_API_KEY:?Set your Krea API key}"

  START=$(curl --fail-with-body --silent --show-error --max-time 60 \
    https://api.krea.ai/agent/send-message \
    -H "Authorization: Bearer $KREA_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "prompt": "Create three square product images for a ceramic coffee mug on a neutral studio background. Use no text or logos.",
      "name": "Coffee mug campaign"
    }')

  SESSION_ID=$(printf '%s' "$START" | jq -er '.sessionId')
  RUN_ID=$(printf '%s' "$START" | jq -er '.runId')
  printf '%s' "$START" | jq -r '.url'

  DEADLINE=$((SECONDS + 300))

  while (( SECONDS < DEADLINE - 10 )); do
    WAIT_SECONDS=$((DEADLINE - SECONDS - 10))
    if (( WAIT_SECONDS > 60 )); then WAIT_SECONDS=60; fi
    WAIT_BODY=$(jq -n --arg sessionId "$SESSION_ID" --arg runId "$RUN_ID" \
      --argjson timeoutSeconds "$WAIT_SECONDS" \
      '{sessionId: $sessionId, runId: $runId, timeoutSeconds: $timeoutSeconds}')
    RESULT=$(curl --fail-with-body --silent --show-error --max-time "$((WAIT_SECONDS + 10))" \
      https://api.krea.ai/agent/wait-for-session \
      -H "Authorization: Bearer $KREA_API_KEY" \
      -H "Content-Type: application/json" \
      -d "$WAIT_BODY")
    printf '%s' "$RESULT" | jq '{status, finished, timedOut, text, media, error, url}'
    if printf '%s' "$RESULT" | jq -e '.finished' > /dev/null; then
      if printf '%s' "$RESULT" | jq -e '.status == "completed"' > /dev/null; then
        exit 0
      fi
      printf '%s' "$RESULT" | jq -r '"Turn ended: \(.status). \(.error // "No error details")"' >&2
      exit 1
    fi
  done

  printf 'Wait budget exhausted; the turn continues. Resume waiting with sessionId=%s runId=%s\n' \
    "$SESSION_ID" "$RUN_ID" >&2
  exit 1
  ```

  ```python Python theme={"theme":{"light":"github-light-default","dark":"vesper"}}
  import json
  import os
  import time
  import requests

  api = "https://api.krea.ai"
  headers = {"Authorization": f"Bearer {os.environ['KREA_API_KEY']}"}

  response = requests.post(
      f"{api}/agent/send-message",
      headers=headers,
      json={
          "prompt": "Create three square product images for a ceramic coffee mug on a neutral studio background. Use no text or logos.",
          "name": "Coffee mug campaign",
      },
      timeout=60,
  )
  response.raise_for_status()
  start = response.json()
  session_id, run_id = start["sessionId"], start["runId"]
  print(start["url"])

  deadline = time.monotonic() + 300
  while True:
      wait_seconds = min(60, int(deadline - time.monotonic()) - 10)
      if wait_seconds < 1:
          raise TimeoutError(
              "Wait budget exhausted; the turn continues. Resume waiting with "
              f"sessionId={session_id} runId={run_id}"
          )
      response = requests.post(
          f"{api}/agent/wait-for-session",
          headers=headers,
          json={
              "sessionId": session_id,
              "runId": run_id,
              "timeoutSeconds": wait_seconds,
          },
          timeout=wait_seconds + 10,
      )
      response.raise_for_status()
      result = response.json()
      print(json.dumps(result, indent=2))
      if result["finished"]:
          if result["status"] != "completed":
              raise RuntimeError(
                  f"Turn ended: {result['status']}. {result.get('error', 'No error details')}"
              )
          break
  ```

  ```javascript Node.js theme={"theme":{"light":"github-light-default","dark":"vesper"}}
  import { performance } from "node:perf_hooks";

  const token = process.env.KREA_API_KEY;
  if (!token) throw new Error("Set your Krea API key");
  const api = "https://api.krea.ai";

  async function post(path, body, timeoutMs) {
    const response = await fetch(`${api}${path}`, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${token}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(body),
      signal: AbortSignal.timeout(timeoutMs),
    });
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${await response.text()}`);
    }
    return response.json();
  }

  const start = await post("/agent/send-message", {
    prompt: "Create three square product images for a ceramic coffee mug on a neutral studio background. Use no text or logos.",
    name: "Coffee mug campaign",
  }, 60_000);
  const { sessionId, runId } = start;
  console.log(start.url);

  const deadline = performance.now() + 300_000;
  while (true) {
    const timeoutSeconds = Math.min(60, Math.floor((deadline - performance.now()) / 1000) - 10);
    if (timeoutSeconds < 1) {
      throw new Error(
        `Wait budget exhausted; the turn continues. Resume waiting with sessionId=${sessionId} runId=${runId}`,
      );
    }
    const result = await post("/agent/wait-for-session", {
      sessionId,
      runId,
      timeoutSeconds,
    }, (timeoutSeconds + 10) * 1000);
    console.log(JSON.stringify(result, null, 2));
    if (result.finished) {
      if (result.status !== "completed") {
        throw new Error(`Turn ended: ${result.status}. ${result.error ?? "No error details"}`);
      }
      break;
    }
  }
  ```

  ```javascript Node.js (SDK) theme={"theme":{"light":"github-light-default","dark":"vesper"}}
  import { performance } from "node:perf_hooks";
  import { Krea } from "@krea-ai/sdk";

  const token = process.env.KREA_API_KEY;
  if (!token) throw new Error("Set your Krea API key");
  const krea = new Krea({ apiKey: token });

  const start = await krea.requestRaw("/agent/send-message", {
    method: "POST",
    retry: false,
    signal: AbortSignal.timeout(60_000),
    body: {
      prompt: "Create three square product images for a ceramic coffee mug on a neutral studio background. Use no text or logos.",
      name: "Coffee mug campaign",
    },
  });
  const { sessionId, runId } = start;
  console.log(start.url);

  const deadline = performance.now() + 300_000;
  while (true) {
    const timeoutSeconds = Math.min(60, Math.floor((deadline - performance.now()) / 1000) - 15);
    if (timeoutSeconds < 1) {
      throw new Error(
        `Wait budget exhausted; the turn continues. Resume waiting with sessionId=${sessionId} runId=${runId}`,
      );
    }
    const result = await krea.requestRaw("/agent/wait-for-session", {
      method: "POST",
      retry: false,
      signal: AbortSignal.timeout((timeoutSeconds + 15) * 1000),
      body: { sessionId, runId, timeoutSeconds },
    });
    console.log(JSON.stringify(result, null, 2));
    if (result.finished) {
      if (result.status !== "completed") {
        throw new Error(`Turn ended: ${result.status}. ${result.error ?? "No error details"}`);
      }
      break;
    }
  }
  ```
</CodeGroup>

For Python, install `requests` with `pip install requests`. For Node.js, use Node.js 22+ and save the example as `example.mjs`. The SDK example requires `npm install @krea-ai/sdk` and uses `requestRaw` to call the HTTP endpoints.

These examples submit one message and wait for up to five minutes. They stop on HTTP errors and failed or cancelled turns. They never retry a send automatically. If a send request times out, check the session in Krea before submitting again; the first request may have started a turn.

When the wait budget expires, the turn continues in the background. Save the printed session and run IDs to resume waiting later.

`timeoutSeconds` is an integer from `1` to `300` and defaults to `60`. Keep your HTTP client timeout longer than the requested wait. Always pass the latest `runId` when waiting after a send, so a previous turn's terminal status is not mistaken for the new turn.

### Read without waiting

Use `GET /agent/session/{sessionId}` to take a snapshot of the current session:

```bash cURL theme={"theme":{"light":"github-light-default","dark":"vesper"}}
curl --fail-with-body --silent --show-error \
  "https://api.krea.ai/agent/session/$SESSION_ID" \
  -H "Authorization: Bearer $KREA_API_KEY"
```

### Continue the session

After the turn finishes, send another message with its `sessionId`:

```bash cURL theme={"theme":{"light":"github-light-default","dark":"vesper"}}
FOLLOW_UP=$(curl --fail-with-body --silent --show-error \
  https://api.krea.ai/agent/send-message \
  -H "Authorization: Bearer $KREA_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --arg sessionId "$SESSION_ID" \
    '{sessionId: $sessionId, prompt: "Make a landscape variation of the first image."}')")

# Use this new runId in the next wait-for-session request.
RUN_ID=$(printf '%s' "$FOLLOW_UP" | jq -er '.runId')
```

A session runs one turn at a time. Sending another message while a turn is running returns `409` with `code: "conflict"`.

## Inputs and attachments

`send-message` accepts these fields. See the [reference](/docs/api-reference/agent/send-agent-message) for the full model enum and schemas.

| Field | Behavior |
| - | - |
| `sessionId` | Optional UUID. Omit for a new session; include to continue an owned session. |
| `prompt` | String, up to 100,000 characters. Defaults to `""`. A nonblank prompt or at least one attachment is required. |
| `attachments` | Up to 20 attachments. Defaults to `[]`. Each needs `url` and can include `name` and `contentType`. |
| `model` | Optional Agent LLM ID. Omit for Auto. A pinned model must be included in the workspace plan. |
| `effort` | Optional: `fast`, `default`, `high`, or `xhigh`. New sessions default to `default`; omit to retain an existing session's effort. |
| `name` | Optional session name, for new sessions only. Trimmed, nonempty, and up to 120 characters. |
| `desktopAppId` | Optional UUID identifying a connected desktop app. Runs this turn inside that app. The app must be available to the authenticated caller and workspace. |

Attachment `url` accepts an accessible HTTP(S) URL or an authorized Krea Files URI such as `file:550e8400-e29b-41d4-a716-446655440000`. Optional `name` and `contentType` are trimmed, nonempty strings of up to 255 characters. Caller-local paths and OpenAI `file_` IDs are not supported. Transfer the actual file first; an ID in prompt text does not transfer its contents.

The Agent does not inherit your application's conversation or attachments. Attach reference files that are not otherwise accessible. You do not need to repeat usable URLs in the prompt, existing Krea context, or prior session files.

## Session results

`send-message` returns `sessionId`, `threadId`, `runId`, `status` (always `"running"`), and `url`.

`GET /agent/session/{sessionId}` and `wait-for-session` return the following session view. Only `wait-for-session` adds the required `timedOut` field.

| Field | Meaning |
| - | - |
| `sessionId`, `threadId` | Session and root thread identifiers. |
| `runId` | Current turn identifier, when available. |
| `status` | `not_started`, `running`, `completed`, `failed`, or `cancelled`. |
| `finished` | `true` for a terminal turn. With `runId` supplied to a wait, the terminal turn must also match that ID. |
| `text` | Latest assistant message text. |
| `media` | Array of media produced by the session. Order is not significant. |
| `error` | Optional error text for the session. |
| `url` | Link to the session in the Krea app. |
| `timedOut` | Wait responses only: `true` if the deadline elapsed before the requested turn finished. |

Each `media` item can represent a generation (`jobId` and optional `mediaKind`: `image`, `video`, `audio`, or `3d`), rehosted media (`url`, with optional `mimeType`), or a Krea file (`uri`, `name`, `mimeType`). Fetch generation details through `GET /jobs/{jobId}`. You can reuse a Krea file URI as an attachment or open the session's `url` to view its files. Media can appear while the turn is still running.

## Errors

Endpoint errors use `{ "error": "...", "code": "...", "allowedModels": ["..."] }`. Only `error` is required. A turn that fails after submission appears as `status: "failed"` in a successful session response instead.

| HTTP status | Meaning |
| - | - |
| `400` | Malformed JSON body. |
| `401` | Missing or invalid authentication. |
| `402` | Insufficient compute unit balance in the workspace (`compute_limit`) or Free plan message limit reached (`free_message_limit`). |
| `403` | Pinned model is not entitled (`model_not_entitled`); `allowedModels` lists the permitted IDs. |
| `404` | Session is missing or inaccessible (`not_found`), or a Krea file attachment is missing or inaccessible (`attachment_not_found`). |
| `409` | A turn is already in progress (`conflict`). |
| `422` | Invalid input, or the selected desktop app is unavailable. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.