# Quickstart

> Create an API key, start a workflow run from your code, and read its result. About five minutes.

Web page: https://docs.lodol.com/docs/api/quickstart

You need a Lodol workspace with at least one workflow, and permission to create API keys there: workspace owners and admins can.

<Steps>
<Step>

### Create an API key

In Lodol, go to **Settings › API Keys** and create a key with the `workflows:read`, `workflows:execute` and `executions:read` scopes. Copy it: the full key is shown only once.

Keep it in an environment variable rather than in your code:

```bash
export LODOL_API_KEY="sk_live_..."
```

</Step>
<Step>

### Find your workflow

List your workflows to get the `id` of the one to run.

```bash tab="cURL" tab-group="lang"
curl https://api-prod.lodol.com/api/v1/workflows \
  -H "Authorization: Bearer $LODOL_API_KEY"
```

```js tab="JavaScript"
const response = await fetch('https://api-prod.lodol.com/api/v1/workflows', {
  headers: { Authorization: `Bearer ${process.env.LODOL_API_KEY}` },
});
const { workflows } = await response.json();
console.log(workflows.map((w) => `${w.id}  ${w.name}`).join('\n'));
```

```python tab="Python"
import os
import requests

response = requests.get(
    "https://api-prod.lodol.com/api/v1/workflows",
    headers={"Authorization": f"Bearer {os.environ['LODOL_API_KEY']}"},
)
for workflow in response.json()["workflows"]:
    print(workflow["id"], workflow["name"])
```

Each workflow lists the `inputs` it declares, if any:

```json
{
  "workflows": [
    {
      "id": "665f1a2b3c4d5e6f7a8b9c0d",
      "name": "Monthly Client Report",
      "description": "Builds a client's monthly report and emails it to them",
      "inputs": [
        { "name": "client", "type": "text", "required": true, "default": null, "description": "The client to report on" }
      ]
    }
  ]
}
```

</Step>
<Step>

### Start a run

Start the workflow with its inputs. The `Idempotency-Key` makes the request safe to retry: sending it again returns the same run instead of starting another.

```bash tab="cURL" tab-group="lang"
curl -X POST https://api-prod.lodol.com/api/v1/workflows/665f1a2b3c4d5e6f7a8b9c0d/run-async \
  -H "Authorization: Bearer $LODOL_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: acme-report-2026-05" \
  -d '{"inputs": {"client": "Acme"}}'
```

```js tab="JavaScript"
const response = await fetch(
  'https://api-prod.lodol.com/api/v1/workflows/665f1a2b3c4d5e6f7a8b9c0d/run-async',
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.LODOL_API_KEY}`,
      'Content-Type': 'application/json',
      'Idempotency-Key': 'acme-report-2026-05',
    },
    body: JSON.stringify({ inputs: { client: 'Acme' } }),
  },
);
const run = await response.json();
console.log(run.execution_id, run.status);
```

```python tab="Python"
response = requests.post(
    "https://api-prod.lodol.com/api/v1/workflows/665f1a2b3c4d5e6f7a8b9c0d/run-async",
    headers={
        "Authorization": f"Bearer {os.environ['LODOL_API_KEY']}",
        "Idempotency-Key": "acme-report-2026-05",
    },
    json={"inputs": {"client": "Acme"}},
)
run = response.json()
print(run["execution_id"], run["status"])
```

The API answers straight away with `202 Accepted` and the run's ID, while the run carries on in the background:

```json
{
  "execution_id": "683b2c3d4e5f6a7b8c9d0e1f",
  "workflow_id": "665f1a2b3c4d5e6f7a8b9c0d",
  "version_id": "6683e1c4b2a9d7f0e5c3a1b2",
  "status": "queued",
  "is_trigger_listener": false,
  "created_at": "2026-04-14T10:30:00+00:00"
}
```

A workflow with no required inputs can be started without a body.

</Step>
<Step>

### Wait for the result

Check the run until it finishes: `success`, `failed` or `stopped`. A run that succeeded has what the workflow handed back in `outputs`, one field per output it declares; one that failed says why in `error`.

```bash tab="cURL" tab-group="lang"
curl https://api-prod.lodol.com/api/v1/executions/683b2c3d4e5f6a7b8c9d0e1f \
  -H "Authorization: Bearer $LODOL_API_KEY"
```

```js tab="JavaScript"
const headers = { Authorization: `Bearer ${process.env.LODOL_API_KEY}` };
const url = `https://api-prod.lodol.com/api/v1/executions/${run.execution_id}`;

const finished = ['success', 'failed', 'stopped'];
let result;
do {
  await new Promise((resolve) => setTimeout(resolve, 5000));
  result = await (await fetch(url, { headers })).json();
} while (!finished.includes(result.status));

console.log(result.status, result.outputs, result.error);
```

```python tab="Python"
import time

headers = {"Authorization": f"Bearer {os.environ['LODOL_API_KEY']}"}
url = f"https://api-prod.lodol.com/api/v1/executions/{run['execution_id']}"

while True:
    time.sleep(5)
    result = requests.get(url, headers=headers).json()
    if result["status"] in ("success", "failed", "stopped"):
        break

print(result["status"], result["outputs"], result["error"])
```

```json
{
  "execution_id": "683b2c3d4e5f6a7b8c9d0e1f",
  "workflow_id": "665f1a2b3c4d5e6f7a8b9c0d",
  "version_id": "6683e1c4b2a9d7f0e5c3a1b2",
  "workflow_name": "Monthly Client Report",
  "status": "success",
  "is_trigger_listener": false,
  "created_at": "2026-04-14T10:30:00+00:00",
  "started_at": "2026-04-14T10:30:00+00:00",
  "completed_at": "2026-04-14T10:30:45+00:00",
  "error": null,
  "outputs": { "total": 1250, "summary": "A good month for Acme" }
}
```

Every check counts toward your [rate limit](https://docs.lodol.com/docs/api/limits.md), so poll every few seconds rather than in a tight loop. To see what each step produced along the way, add `include_step_results=true`.

</Step>
</Steps>

## Next steps

<Cards>
  <Card title="Runs" description="Every field of a run, and how inputs are checked and converted." href="/docs/api/runs" />
  <Card title="Errors and retries" description="What each error means and which ones to retry." href="/docs/api/errors" />
  <Card title="Limits" description="Requests per minute, and the other limits your plan sets." href="/docs/api/limits" />
</Cards>
