# ELTEX Elixir API

Elixir is ELTEX's separate balance for image and video generation. It does not consume Agent Plan capacity.

## Base URL and authentication

```text
Base URL: https://eltexlabs.com/v1/elixir
Authorization: Bearer sk-elixir-...
Content-Type: application/json
```

Create an Elixir API key from **Dashboard → Elixir**. Keep it server-side. Do not expose it in browser code, mobile binaries, logs, or repositories.

Agents can install the ready-to-use Elixir skill from `https://eltexlabs.com/downloads/elixir-skill.md`. The skill discovers the live model catalog, checks balance, uses idempotency, and polls asynchronous video jobs safely.

OAuth is also supported with the relevant scopes:

- `elixir.models:read`
- `elixir.balance:read`
- `elixir.images:generate`
- `elixir.videos:generate`
- `elixir.generations:read`

## Important behavior

- Always call `GET /models` and use a returned model `id`. Model availability and pricing can change.
- Unknown request parameters are rejected with `INVALID_PARAMETER`; they are not silently forwarded.
- Include an `Idempotency-Key` on generation requests. Reusing the same key returns the original job and does not charge Elixir twice.
- If provider submission fails, reserved Elixir is refunded automatically.
- Image generation and editing are synchronous. Video generation is asynchronous and must be polled.

## List available models

```bash
curl https://eltexlabs.com/v1/elixir/models \
  -H "Authorization: Bearer $ELIXIR_API_KEY"
```

Response:

```json
{
  "models": [
    {
      "id": "provider-model-id",
      "eltex_model_id": "elx_model_id",
      "name": "Display name",
      "media_type": "image",
      "operation_type": "generate",
      "duration_seconds": null,
      "elixir_cost": 1,
      "max_count": 4,
      "accepted_parameters": ["model", "prompt", "count", "aspect_ratio", "resolution"],
      "supports": { "i2v": false, "t2v": false }
    }
  ]
}
```

Use either `id` or `eltex_model_id` as the `model` request value.

## Check balance

```bash
curl https://eltexlabs.com/v1/elixir/balance \
  -H "Authorization: Bearer $ELIXIR_API_KEY"
```

```json
{
  "balance_elixir": 25,
  "next_expiry": null
}
```

## Generate an image

`POST /images/generations`

```bash
curl -X POST https://eltexlabs.com/v1/elixir/images/generations \
  -H "Authorization: Bearer $ELIXIR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: image-job-001" \
  -d '{
    "model": "MODEL_ID_FROM_GET_MODELS",
    "prompt": "A cinematic product photo on a dark desk",
    "count": 1,
    "aspect_ratio": "16:9",
    "resolution": "1K",
    "quality": "standard",
    "response_format": "url"
  }'
```

Accepted parameters:

| Parameter | Type | Required | Notes |
|---|---:|---:|---|
| `model` | string | yes | ID returned by `GET /models` |
| `prompt` | string | yes | Maximum 5,000 characters |
| `count` or `n` | integer | no | 1–10 and limited by the model's `max_count` |
| `size` | string | no | Explicit `WIDTHxHEIGHT`, for example `1024x1024` |
| `aspect_ratio` | string | no | `W:H`, `landscape`, `portrait`, or `square` |
| `resolution` | string | no | `1K`, `2K`, or `4K`; combined with `aspect_ratio` into upstream `size` |
| `quality` | string | no | `auto`, `low`, `medium`, `high`; aliases: `standard`, `normal`, `hd` |
| `response_format` | string | no | `url` or `b64_json` |
| `style` | string | no | Passed only on the image contract |

If both `size` and `aspect_ratio` are present, `size` wins.

Successful response:

```json
{
  "generation_id": "elx_gen_...",
  "id": "elx_gen_...",
  "status": "success",
  "elixir_charged": 1,
  "poll_url": null,
  "cancel_url": null,
  "data": [{ "url": "https://..." }]
}
```

With `response_format: "b64_json"`, `data` contains `b64_json` instead of `url`.

## Edit an image

`POST /images/edits`

Single source image:

```bash
curl -X POST https://eltexlabs.com/v1/elixir/images/edits \
  -H "Authorization: Bearer $ELIXIR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: edit-job-001" \
  -d '{
    "model": "EDIT_MODEL_ID_FROM_GET_MODELS",
    "prompt": "Make the sky a dramatic sunset",
    "image_url": "https://example.com/source.png",
    "response_format": "url"
  }'
```

Multiple source images:

```json
{
  "model": "EDIT_MODEL_ID_FROM_GET_MODELS",
  "prompt": "Apply the style of the second image to the first",
  "input_images": [
    { "url": "https://example.com/base.png", "role": "start" },
    { "url": "https://example.com/style.png", "role": "reference" }
  ]
}
```

Image inputs may be an `https://` URL or a `data:image/...;base64,...` URI. Bare base64 and `http://` URLs are rejected. `input_images` accepts 1–8 entries. Image editing returns one result, so `count` and `n` are not accepted.

## Generate a text-to-video job

`POST /video/generations`

```bash
curl -X POST https://eltexlabs.com/v1/elixir/video/generations \
  -H "Authorization: Bearer $ELIXIR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: video-job-001" \
  -d '{
    "model": "VIDEO_MODEL_ID_FROM_GET_MODELS",
    "mode": "t2v",
    "prompt": "A glowing dashboard in motion",
    "duration_seconds": 8,
    "aspect_ratio": "16:9",
    "resolution": "1080p",
    "audio": true
  }'
```

## Generate an image-to-video job

```bash
curl -X POST https://eltexlabs.com/v1/elixir/video/generations \
  -H "Authorization: Bearer $ELIXIR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: video-i2v-001" \
  -d '{
    "model": "IMAGE_TO_VIDEO_MODEL_ID_FROM_GET_MODELS",
    "mode": "i2v",
    "prompt": "Slow cinematic camera push",
    "image_url": "https://example.com/start.png",
    "duration_seconds": 5
  }'
```

Accepted video parameters:

| Parameter | Type | Required | Notes |
|---|---:|---:|---|
| `model` | string | yes | Video model returned by `GET /models` |
| `prompt` | string | yes | Maximum 2,000 characters |
| `mode` | string | no | `t2v` or `i2v`; used by ELTEX for validation and not forwarded upstream |
| `duration_seconds` or `duration` | integer | no | Must match an available model duration when the catalog specifies one |
| `image_url` or `image` | string | for i2v | Start image as an HTTPS URL or data image URI |
| `end_image_url` | string | no | Optional transition end frame |
| `input_images` | array | no | 1–8 start, end, or reference images |
| `resolution` | string | no | Model-supported value such as `720p` or `1080p` |
| `aspect_ratio` | string | no | Model-supported ratio such as `16:9` |
| `audio` | boolean | no | Include audio when the model supports it |
| `webhook_url` | string | no | HTTPS terminal-state callback for the media generation service |
| `metadata` | object | no | Client pass-through metadata |

`mode` is optional: ELTEX infers `i2v` when an image input is present and otherwise uses `t2v`.

Submit response (`202 Accepted`):

```json
{
  "generation_id": "elx_gen_...",
  "id": "elx_gen_...",
  "status": "queued",
  "elixir_charged": 10,
  "duration_seconds": 8,
  "poll_url": "/v1/elixir/video/generations/elx_gen_...",
  "cancel_url": "/v1/elixir/video/generations/elx_gen_..."
}
```

## Poll a video job

```bash
curl https://eltexlabs.com/v1/elixir/video/generations/GENERATION_ID \
  -H "Authorization: Bearer $ELIXIR_API_KEY"
```

Status progresses through `submitted` or `processing`, then `completed`, `failed_provider`, or `cancelled`.

```json
{
  "id": "elx_gen_...",
  "type": "video:generate",
  "model": "provider-model-id",
  "status": "completed",
  "asset_url": "https://.../video.mp4",
  "thumbnail_url": null,
  "elixir_charged": 10,
  "created_at": "2026-07-16T00:00:00.000Z",
  "completed_at": "2026-07-16T00:01:00.000Z"
}
```

## Cancel a video job

```bash
curl -X DELETE https://eltexlabs.com/v1/elixir/video/generations/GENERATION_ID \
  -H "Authorization: Bearer $ELIXIR_API_KEY"
```

Cancellation is best-effort. A job that already reached a terminal state returns `409`.

## List recent generations

```bash
curl https://eltexlabs.com/v1/elixir/generations \
  -H "Authorization: Bearer $ELIXIR_API_KEY"
```

Returns up to 50 recent image and video jobs for the authenticated account.

## Idempotency

Use a stable `Idempotency-Key` for each intended generation:

```text
Idempotency-Key: project-42-scene-7-attempt-1
```

The key may contain letters, numbers, `.`, `_`, `:`, or `-`, with a maximum length of 128 characters. Retrying with the same key returns `idempotent_replay: true` and does not reserve or charge Elixir again. Use a new key only for a genuinely new generation.

## Errors

Errors use this shape:

```json
{
  "error": {
    "code": "INVALID_PARAMETER",
    "message": "Unsupported parameter: count"
  }
}
```

Common codes:

| HTTP | Code | Meaning |
|---:|---|---|
| 400 | `INVALID_PARAMETER` | Invalid or unsupported field, model, duration, image input, or payload |
| 401 | `INVALID_API_KEY` | Missing, invalid, expired, or inactive key |
| 402 | `INSUFFICIENT_ELIXIR` | The account does not have enough Elixir |
| 403 | `INACTIVE_API_KEY` | Missing OAuth scope or inactive account/key |
| 404 | `GENERATION_NOT_FOUND` | Job does not exist or is not owned by the caller |
| 409 | `INVALID_PARAMETER` | Job can no longer be cancelled |
| 429 | `RATE_LIMITED` | Request or generation rate limit exceeded |
| 502/503 | `PROVIDER_ERROR` | The media generation service failed |

Provider submission failures are recorded and the reserved Elixir is refunded automatically. Use a new idempotency key if you intentionally retry a failed generation after correcting the request or changing the model.
