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

# Quick Start

> Create your first AI-generated image in under a minute. Two paths: one-call creation or full control.

**Base URL:** `https://app.uselamina.ai`
**Auth:** `x-api-key: lma_your_api_key`

Lamina is an agentic creative API for generating videos, movies, and images for products, brands, social, and ads. You send a brief, it creates content. Two paths depending on how much control you need.

***

## Path 1: One-call creation (recommended for agents)

The fastest way to create content. Describe what you want, the engine handles everything.

### Create content from a brief

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST \
    -H "x-api-key: lma_your_api_key" \
    -H "Content-Type: application/json" \
    -d '{"brief": "Product hero shot of white sneakers on a marble surface, premium lighting"}' \
    https://app.uselamina.ai/v1/content/create
  ```

  ```python Python theme={null}
  import requests

  API_KEY = "lma_your_key"
  BASE = "https://app.uselamina.ai/v1"
  H = {"x-api-key": API_KEY, "Content-Type": "application/json"}

  r = requests.post(f"{BASE}/content/create", headers=H,
      json={"brief": "Product hero shot of white sneakers on a marble surface, premium lighting"})
  exec_id = r.json()["data"]["runId"]
  ```

  ```javascript JavaScript theme={null}
  const API_KEY = 'lma_your_key';
  const BASE = 'https://app.uselamina.ai/v1';
  const headers = { 'x-api-key': API_KEY, 'Content-Type': 'application/json' };

  const res = await fetch(`${BASE}/content/create`, {
    method: 'POST', headers,
    body: JSON.stringify({ brief: 'Product hero shot of white sneakers on a marble surface, premium lighting' }),
  });
  const { data } = await res.json();
  const execId = data.runId;
  ```
</CodeGroup>

Response:

```json theme={null}
{
  "data": {
    "runId": "<execution-id>",
    "workflowId": "<selected-app-id>",
    "status": "queued"
  }
}
```

### Wait for results

<CodeGroup>
  ```bash curl theme={null}
  curl -H "x-api-key: lma_your_api_key" \
    "https://app.uselamina.ai/v1/runs/<execution-id>/wait?timeout=60"
  ```

  ```python Python theme={null}
  import requests

  API_KEY = "lma_your_key"
  BASE = "https://app.uselamina.ai/v1"
  H = {"x-api-key": API_KEY, "Content-Type": "application/json"}

  result = requests.get(f"{BASE}/runs/{exec_id}/wait?timeout=60", headers=H).json()
  for output in result["data"]["outputs"]:
      print(f"{output['label']}: {output['value']}")
  ```

  ```javascript JavaScript theme={null}
  const API_KEY = 'lma_your_key';
  const BASE = 'https://app.uselamina.ai/v1';
  const headers = { 'x-api-key': API_KEY, 'Content-Type': 'application/json' };

  const result = await fetch(
    `${BASE}/runs/${execId}/wait?timeout=60`, { headers }
  ).then(r => r.json());

  for (const output of result.data.outputs) {
    console.log(`${output.label}: ${output.value}`);
  }
  ```
</CodeGroup>

When `status` is `"completed"`, outputs contain your generated assets (image URLs, video URLs, text).

```json theme={null}
{
  "data": {
    "runId": "<execution-id>",
    "status": "completed",
    "outputs": [
      {
        "id": "node-1",
        "label": "AI Designer",
        "type": "image",
        "value": "https://storage.example.com/generated/hero-shot.png",
        "status": "completed",
        "error": null
      }
    ],
    "startedAt": "2026-03-23T12:22:41.694Z",
    "completedAt": "2026-03-23T12:24:15.000Z"
  },
  "timeout": false
}
```

### That's it

Two calls: create and wait. The engine selected the best app, resolved brand context, mapped inputs, and executed the workflow.

***

## Path 2: Full control (choose the app yourself)

When you need to pick a specific app, inspect its parameters, and provide exact inputs.

### 1. Discover an app

<CodeGroup>
  ```bash curl theme={null}
  curl -H "x-api-key: lma_your_api_key" \
    https://app.uselamina.ai/v1/apps
  ```

  ```python Python theme={null}
  import requests

  API_KEY = "lma_your_key"
  BASE = "https://app.uselamina.ai/v1"
  H = {"x-api-key": API_KEY, "Content-Type": "application/json"}

  apps = requests.get(f"{BASE}/apps", headers=H).json()["data"]
  ```

  ```javascript JavaScript theme={null}
  const API_KEY = 'lma_your_key';
  const BASE = 'https://app.uselamina.ai/v1';
  const headers = { 'x-api-key': API_KEY, 'Content-Type': 'application/json' };

  const apps = await fetch(`${BASE}/apps`, { headers }).then(r => r.json());
  ```
</CodeGroup>

Use `search` when you know the capability you want to build around:

<CodeGroup>
  ```bash curl theme={null}
  curl -H "x-api-key: lma_your_api_key" \
    "https://app.uselamina.ai/v1/apps?search=catalog"
  ```

  ```python Python theme={null}
  apps = requests.get(f"{BASE}/apps?search=catalog", headers=H).json()["data"]
  app_id = apps[0]["appId"]
  ```

  ```javascript JavaScript theme={null}
  const searchRes = await fetch(`${BASE}/apps?search=catalog`, { headers }).then(r => r.json());
  const appId = searchRes.data[0].appId;
  ```
</CodeGroup>

Pick an `appId` from the response.

### 2. Inspect the input schema

<CodeGroup>
  ```bash curl theme={null}
  curl -H "x-api-key: lma_your_api_key" \
    https://app.uselamina.ai/v1/apps/{appId}
  ```

  ```python Python theme={null}
  app = requests.get(f"{BASE}/apps/{app_id}", headers=H).json()["data"]
  print(app["parameters"])  # shows what inputs to provide
  ```

  ```javascript JavaScript theme={null}
  const app = await fetch(`${BASE}/apps/${appId}`, { headers }).then(r => r.json());
  console.log(app.data.parameters);  // shows what inputs to provide
  ```
</CodeGroup>

```json theme={null}
{
  "data": {
    "appId": "{appId}",
    "name": "Example Catalog App",
    "description": "Generate catalog images from product inputs",
    "parameters": [
      {
        "id": "front-image",
        "name": "Front",
        "type": "url",
        "required": true,
        "accept": ["image"]
      },
      {
        "id": "location",
        "name": "Location",
        "type": "options",
        "required": true,
        "options": ["Studio", "Urban", "Park"],
        "default": "Studio"
      },
      {
        "id": "prompt",
        "name": "Prompt",
        "type": "text",
        "required": true
      }
    ]
  }
}
```

Use the parameter `name` as the key when sending `inputs`. For `options`, send the label from the `options` array. This lets your backend or UI adapt to different apps without changing the execution contract.

### 3. Start execution

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST \
    -H "x-api-key: lma_your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "inputs": {
        "Front": "https://example.com/front.jpg",
        "Location": "Studio"
      }
    }' \
    "https://app.uselamina.ai/v1/apps/{appId}/runs?webhook=https://your-server.com/callback"
  ```

  ```python Python theme={null}
  r = requests.post(f"{BASE}/apps/{app_id}/runs?webhook=https://your-server.com/cb",
      headers=H, json={"inputs": {"Front": "https://example.com/front.jpg", "Location": "Studio"}})
  exec_id = r.json()["data"]["runId"]
  ```

  ```javascript JavaScript theme={null}
  const res = await fetch(`${BASE}/apps/${appId}/runs?webhook=${encodeURIComponent('https://your-server.com/callback')}`, {
    method: 'POST', headers,
    body: JSON.stringify({ inputs: { "Front": "https://example.com/front.jpg", "Location": "Studio" } }),
  });
  const { data } = await res.json();
  const execId = data.runId;
  ```
</CodeGroup>

For local testing or simpler integrations, you can omit `?webhook=` and poll or wait instead.

```json theme={null}
{
  "data": {
    "runId": "<execution-id>",
    "workflowId": "{appId}",
    "workflowName": "Example Catalog App",
    "status": "queued",
    "outputs": [
      { "id": "node-1", "label": "Catalog Output", "type": "pending", "value": null, "status": "pending", "error": null }
    ]
  }
}
```

### 4. Get results

Three options depending on your integration:

**Via wait (simplest for agents):** Block until the execution finishes or the timeout expires. No polling loop needed.

<CodeGroup>
  ```bash curl theme={null}
  curl -H "x-api-key: lma_your_api_key" \
    "https://app.uselamina.ai/v1/runs/<execution-id>/wait?timeout=60"
  ```

  ```python Python theme={null}
  result = requests.get(f"{BASE}/runs/{exec_id}/wait?timeout=60", headers=H).json()
  for o in result["data"]["outputs"]:
      print(f"{o['label']}: {o['value']}")
  ```

  ```javascript JavaScript theme={null}
  const result = await fetch(
    `${BASE}/runs/${execId}/wait?timeout=60`, { headers }
  ).then(r => r.json());

  for (const output of result.data.outputs) {
    console.log(`${output.label}: ${output.value}`);
  }
  ```
</CodeGroup>

Use `timeout=60` for image workflows, `timeout=120` for video. If it times out, `timeout: true` is returned alongside the current state -- call again or fall back to polling.

**Via webhook:** If you passed `?webhook=`, your callback URL receives a POST with the results when the execution completes. The payload has the same structure as the polling response below, signed with ED25519 headers for verification.

**Via polling:** Poll every 3-5 seconds:

<CodeGroup>
  ```bash curl theme={null}
  curl -H "x-api-key: lma_your_api_key" \
    https://app.uselamina.ai/v1/runs/<execution-id>
  ```

  ```python Python theme={null}
  import time

  while True:
      status = requests.get(f"{BASE}/runs/{exec_id}", headers=H).json()["data"]
      if status["status"] in ("completed", "failed"):
          for o in status["outputs"]:
              print(f"{o['label']}: {o['value']}")
          break
      time.sleep(5)
  ```

  ```javascript JavaScript theme={null}
  const poll = async (execId) => {
    while (true) {
      const r = await fetch(`${BASE}/runs/${execId}`, { headers });
      const { data } = await r.json();
      if (data.status === 'completed' || data.status === 'failed') return data;
      await new Promise(r => setTimeout(r, 5000));
    }
  };

  const finalResult = await poll(execId);
  ```
</CodeGroup>

**Status flow:** `queued` -> `running` -> `completed` or `failed`

When `status` is `"completed"`, output `type` changes from `"pending"` to `"image"`, `"video"`, or `"text"`, and `value` contains the result:

```json theme={null}
{
  "data": {
    "runId": "<execution-id>",
    "status": "completed",
    "outputs": [
      {
        "id": "node-1",
        "label": "AI Designer",
        "type": "image",
        "value": "https://storage.example.com/generated/catalog-image.png",
        "status": "completed",
        "error": null
      }
    ],
    "errorMessage": null,
    "startedAt": "2026-03-23T12:22:41.694Z",
    "completedAt": "2026-03-23T12:24:15.000Z",
    "createdAt": "2026-03-23T12:22:40.552Z"
  }
}
```

If `status` is `"failed"`, check `errorMessage` and each output's `error` field.

***

## Score and distribute (optional)

After creation, you can:

* **Score content**: `POST /v1/content/score` -- evaluate quality against brand context before publishing
* **Publish**: `POST /v1/publishing/publish` -- send to connected social channels
* **Transfer to CDN**: `POST /v1/publishing/transfer-asset` -- permanent hosting on your own infrastructure

***

## What's next

* [Key Concepts](/concepts/creative-engine) -- understand the Creative Engine mental model
* [Agent Integration Patterns](/guides/agent-integration-patterns)
* [Integration Recipes](/guides/capability-recipes)
* [Use the CLI and SDK](/guides/use-the-cli-and-sdk)
