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

# Overview

Eversince is a creative agent that plans and executes across image, video, and audio. It orchestrates the latest AI models and operates in a purpose-built environment with tools, skills, and memory. Works for one-off tasks or as a creative employee in any agent-to-agent workflow.

## Base URL

```
https://eversince.ai/api/v1
```

## Authentication

All requests require an API key in the `Authorization` header.

```
Authorization: Bearer YOUR_API_KEY
```

API keys start with `es_live_` and can be created in your [account settings](https://eversince.ai/app/settings) or via the [API](/api/account#api-keys). Each account can have up to 10 active keys.

## Quick start

Create a project, poll until it's done, then get the result.

<CodeGroup>
  ```bash curl theme={"dark"}
  # 1. Create a project
  curl -X POST https://eversince.ai/api/v1/projects \
    -H "Authorization: Bearer $EVERSINCE_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "brief": "Your brief here",
      "mode": "autonomous"
    }'

  # Response: { "id": "proj_abc123", "status": "queued", ... }

  # 2. Poll for status
  curl https://eversince.ai/api/v1/projects/proj_abc123 \
    -H "Authorization: Bearer $EVERSINCE_API_KEY"

  # 3. When status is "idle", trigger a render
  curl -X POST https://eversince.ai/api/v1/projects/proj_abc123/render \
    -H "Authorization: Bearer $EVERSINCE_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "quality": "1080p" }'

  # 4. Poll again until status is "idle", then get the assembled_url
  ```

  ```python Python theme={"dark"}
  import requests
  import time

  API_KEY = "YOUR_API_KEY"
  BASE = "https://eversince.ai/api/v1"
  headers = {
      "Authorization": f"Bearer {API_KEY}",
      "Content-Type": "application/json"
  }

  # 1. Create a project
  resp = requests.post(f"{BASE}/projects", headers=headers, json={
      "brief": "Your brief here",
      "mode": "autonomous"
  })
  project = resp.json()
  project_id = project["id"]

  # 2. Poll for completion
  while True:
      resp = requests.get(f"{BASE}/projects/{project_id}", headers=headers)
      status = resp.json()["status"]

      if status in ("idle", "failed", "cancelled"):
          break

      interval = {"queued": 5, "running": 30, "generating": 45, "rendering": 30}
      time.sleep(interval.get(status, 10))

  # 3. Render the final video
  if status == "idle":
      requests.post(f"{BASE}/projects/{project_id}/render",
                    headers=headers, json={"quality": "1080p"})

      # Poll until rendering completes
      while True:
          resp = requests.get(f"{BASE}/projects/{project_id}", headers=headers)
          data = resp.json()
          if data["status"] != "rendering":
              break
          time.sleep(30)

      print(data["assembled_url"])
  ```

  ```javascript JavaScript theme={"dark"}
  const API_KEY = "YOUR_API_KEY";
  const BASE = "https://eversince.ai/api/v1";
  const headers = {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  };

  // 1. Create a project
  const project = await fetch(`${BASE}/projects`, {
    method: "POST",
    headers,
    body: JSON.stringify({
      brief:
        "Your brief here",
      mode: "autonomous",
    }),
  }).then((r) => r.json());

  // 2. Poll for completion
  const intervals = { queued: 5000, running: 30000, generating: 45000, rendering: 30000 };
  let status;
  do {
    const data = await fetch(`${BASE}/projects/${project.id}`, { headers }).then(
      (r) => r.json()
    );
    status = data.status;
    if (!["idle", "failed", "cancelled"].includes(status)) {
      await new Promise((r) => setTimeout(r, intervals[status] || 10000));
    }
  } while (!["idle", "failed", "cancelled"].includes(status));

  // 3. Render
  if (status === "idle") {
    await fetch(`${BASE}/projects/${project.id}/render`, {
      method: "POST",
      headers,
      body: JSON.stringify({ quality: "1080p" }),
    });

    // Poll until rendering completes
    let result;
    do {
      result = await fetch(`${BASE}/projects/${project.id}`, { headers }).then(
        (r) => r.json()
      );
      if (result.status === "rendering") {
        await new Promise((r) => setTimeout(r, 30000));
      }
    } while (result.status === "rendering");

    console.log(result.assembled_url);
  }
  ```
</CodeGroup>

## Project lifecycle

Every project follows this status flow:

```
queued → running → generating → idle
                              → failed

idle → (call /render) → rendering → idle
                                  → failed

cancelled (from queued, running, generating, or idle)
```

| Status       | Meaning                                                              | Poll interval |
| ------------ | -------------------------------------------------------------------- | ------------- |
| `queued`     | Waiting to start                                                     | 5s            |
| `running`    | Agent is planning and executing                                      | 30s           |
| `generating` | Waiting for model outputs                                            | 30-60s        |
| `rendering`  | Compositing final video                                              | 30s           |
| `idle`       | Ready for next action (see below)                                    | Stop          |
| `failed`     | Something went wrong. Check `error_message`. Send a message to retry | Stop          |
| `cancelled`  | Stopped by user                                                      | Stop          |

<Warning>
  The `idle` status appears twice in a typical flow. **First idle**: the agent finished its work. `assembled_url` is `null`, the project is ready for feedback or rendering. **Second idle** (after you call `/render`): rendering is complete and `assembled_url` contains the video URL. Always check `assembled_url` to distinguish between the two.
</Warning>

## Two modes

**Autonomous** (API default). The agent handles everything start to finish. Create the project, poll until `idle`, get the result.

**Collaborative**. The agent stops at decision points and returns `idle` with a message in `agent_message`. Review the message, send feedback via `POST /projects/:id/messages`, and the agent continues. Good for guiding creative direction.

<Note>
  The API defaults to autonomous mode. In the studio, the default is collaborative.
</Note>

Switch modes anytime via `PATCH /projects/:id/settings`.

## Rate limits

| Limit               | Value                   |
| ------------------- | ----------------------- |
| Requests            | 120 per minute per user |
| Concurrent projects | 5 active simultaneously |
| Renders             | 50 per day              |
| Request payload     | 1 MB max                |

For higher limits, contact us at [support@eversince.ai](mailto:support@eversince.ai)

Rate limit headers are included on every response:

```
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
X-RateLimit-Reset: 1711929600
```

When rate limited, you'll receive a `429` response with a `Retry-After` header.

## Errors

All errors follow the same format:

```json theme={"dark"}
{
  "error": {
    "code": "validation_error",
    "message": "brief: brief is required",
    "status": 400
  }
}
```

| Code                   | Status | Meaning                                            |
| ---------------------- | ------ | -------------------------------------------------- |
| `validation_error`     | 400    | Invalid request parameters                         |
| `unauthorized`         | 401    | Missing or invalid API key                         |
| `insufficient_credits` | 402    | Not enough credits                                 |
| `forbidden`            | 403    | Permission denied                                  |
| `not_found`            | 404    | Resource doesn't exist                             |
| `conflict`             | 409    | Status conflict (e.g., project is already running) |
| `payload_too_large`    | 413    | Request body exceeds 1 MB                          |
| `project_limit`        | 429    | Concurrent project or daily project limit exceeded |
| `rate_limited`         | 429    | Too many requests                                  |
| `internal_error`       | 500    | Server error                                       |
| `service_unavailable`  | 503    | Temporarily unavailable                            |

Validation errors join multiple field errors with semicolons: `"brief: brief is required; mode: mode must be autonomous or collaborative"`.

## Response headers

Every response includes:

| Header                  | Description                             |
| ----------------------- | --------------------------------------- |
| `X-Request-Id`          | Unique request identifier for debugging |
| `X-RateLimit-Limit`     | Requests allowed per window             |
| `X-RateLimit-Remaining` | Requests remaining                      |
| `X-RateLimit-Reset`     | Unix timestamp when the window resets   |

## Minimum credit requirements

| Action         | Minimum credits                                 |
| -------------- | ----------------------------------------------- |
| Create project | 50                                              |
| Send message   | 10                                              |
| Render (1080p) | Free (rate-limited per day: 10 non-sub, 50 sub) |
| Render (4K)    | Charges credits                                 |

## Best practices

<AccordionGroup>
  <Accordion title="Use idempotency keys for project creation">
    Network retries can duplicate projects. Include an `idempotency_key` to prevent this. Existing projects return `200`, new projects return `202`. Handle both as success.
  </Accordion>

  <Accordion title="Combine webhooks with polling">
    Webhooks are delivered once. Use them as a trigger, then confirm state with `GET /projects/:id`.
  </Accordion>

  <Accordion title="Download or share videos within 24 hours">
    `assembled_url` expires after 24 hours. Create a share link for a permanent URL, or download the video. Check `assembled_url_expires_at` for the exact expiry.
  </Accordion>

  <Accordion title="Handle the credits_warning field">
    When balance drops below 100 credits, responses include `credits_warning`. Use this to trigger top-up flows before the agent runs out mid-project.
  </Accordion>

  <Accordion title="Use cursor-based message polling">
    Store the last message ID and pass it as `?after=msg_id` to get only new messages.
  </Accordion>

  <Accordion title="Log request IDs">
    Every response includes an `X-Request-Id` header. Log these alongside your requests for debugging with support.
  </Accordion>
</AccordionGroup>
