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

# Async and webhooks

> Run a Canvas in the background, poll the result or receive it on your URL, and list past runs

A synchronous completion holds the HTTP connection until the Canvas finishes. For long documents, batches, or any caller that should not wait, run the completion in the background instead. There are two ways, and they share the same objects:

* **`async: true`**: the API answers at once with the run and you poll `GET /v2/chat/completions/{id}` until it finishes.
* **`webhook_url`**: the run is asynchronous automatically, and Tela POSTs the result to your URL when it finishes.

Either way, the run is recorded as a **completion run** that you can fetch later or find with `GET /v1/completion-run`.

> [!NOTE]
>
> The `webhook_url` of a completion reports that one run. It is not the same as [Workstation Events](https://docs.tela.com/en/workstation/events/overview.md), which are subscriptions configured on an App and fire for every task of that App.

## The run object

Asynchronous calls answer with the run instead of a `chat.completion`. The same object is returned by polling and posted to your webhook. Its `status` moves from `created` to `running`, then to `succeeded` or `failed`.

| Field | Meaning |
| - | - |
| `id` | The run ID. Use it to poll with `GET /v2/chat/completions/{id}`. |
| `status` | `created`, `running`, `succeeded` or `failed`. |
| `input_content` | The normalized inputs: `variables`, `messages` and the `files` found in them. |
| `output_content` | The answer once the run succeeds: `role` and `content`, with the fields of your Output Format when there is one. May include `information` with `duration`, `cost`, `input_tokens` and `output_tokens`. |
| `raw_input` | The body you sent (`canvas_id` or `version_id`, `variables`, `messages`, `webhook_url`, `override`). |
| `raw_output` | The raw output of the run. |
| `prompt_version_id` | The version that ran. |
| `prompt_application_id` | The App, when the run is a task; `null` for completions. |
| `compatibility_date`, `workspace_id`, `created_at`, `updated_at` | Metadata of the run. |

```json Just created theme={null}
{
    "id": "b83b1558-de5b-4e45-b517-7bececbdd154",
    "status": "created",
    "input_content": {
        "variables": null,
        "messages": [],
        "files": []
    },
    "output_content": null,
    "raw_input": {
        "canvas_id": "95cec438-6080-44fe-8275-28b16bd40178",
        "variables": {
            "document": { "file_url": "https://example.com/document.pdf" }
        },
        "webhook_url": "https://example.com/webhook"
    },
    "raw_output": null,
    "compatibility_date": "2024-10-14",
    "prompt_version_id": "76065caa-c519-441a-8fe2-cc43e29664ab",
    "prompt_application_id": null,
    "workspace_id": "3f55cfd9-4427-4887-a181-d9ad39967802",
    "created_at": "2024-10-14T19:00:53.664Z",
    "updated_at": "2024-10-14T19:00:53.664Z"
}
```

```json Succeeded theme={null}
{
    "id": "b83b1558-de5b-4e45-b517-7bececbdd154",
    "status": "succeeded",
    "input_content": {
        "variables": {
            "document": { "file_url": "https://example.com/document.pdf" }
        },
        "messages": [],
        "files": [
            {
                "name": "document",
                "url": "https://example.com/document.pdf",
                "used_at": "variables",
                "type": "file"
            }
        ]
    },
    "output_content": {
        "role": "assistant",
        "content": {
            "fileSummary": "This document contains quarterly financial reports showing a 15% revenue increase compared to last quarter."
        }
    },
    "raw_input": {
        "canvas_id": "95cec438-6080-44fe-8275-28b16bd40178",
        "variables": {
            "document": { "file_url": "https://example.com/document.pdf" }
        },
        "webhook_url": "https://example.com/webhook"
    },
    "raw_output": {
        "fileSummary": "This document contains quarterly financial reports showing a 15% revenue increase compared to last quarter."
    },
    "compatibility_date": "2024-10-14",
    "prompt_version_id": "76065caa-c519-441a-8fe2-cc43e29664ab",
    "prompt_application_id": null,
    "workspace_id": "3f55cfd9-4427-4887-a181-d9ad39967802",
    "created_at": "2024-10-14T19:00:53.664Z",
    "updated_at": "2024-10-14T19:01:15.892Z"
}
```

> [!NOTE]
>
> What a run with `status: "failed"` carries about the failure is not documented here. Treat `failed` as terminal and check the run in the Canvas **Usage**.

## Using the async flag

Add `async: true` to the body. The response is the run with `status: "created"`.

```sh cURL theme={null}
curl -X POST https://api.tela.com/v2/chat/completions \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $TELA_API_KEY" \
    -d '{
        "canvas_id": "95cec438-6080-44fe-8275-28b16bd40178",
        "variables": {
            "document": { "file_url": "https://example.com/document.pdf" }
        },
        "async": true
    }'
```

```typescript TypeScript (fetch) theme={null}
const response = await fetch('https://api.tela.com/v2/chat/completions', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${process.env.TELA_API_KEY}`,
    },
    body: JSON.stringify({
        canvas_id: process.env.TELA_CANVAS_ID,
        variables: {
            document: { file_url: 'https://example.com/document.pdf' },
        },
        async: true,
    }),
})

const run = await response.json()
console.log(run.id, run.status) // "b83b1558-...", "created"
```

```python Python (requests) theme={null}
import os
import requests

response = requests.post(
    "https://api.tela.com/v2/chat/completions",
    headers={"Authorization": f"Bearer {os.getenv('TELA_API_KEY')}"},
    json={
        "canvas_id": os.getenv("TELA_CANVAS_ID"),
        "variables": {
            "document": {"file_url": "https://example.com/document.pdf"},
        },
        "async": True,
    },
)

run = response.json()
print(run["id"], run["status"])  # "b83b1558-...", "created"
```

### Polling the result

Call `GET /v2/chat/completions/{id}` with the run ID until `status` is `succeeded` or `failed`, then read `output_content.content`. The examples below start a run and poll it in one script.

```typescript TypeScript (fetch) theme={null}
const headers = {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${process.env.TELA_API_KEY}`,
}

// Start the run in the background
const created = await fetch('https://api.tela.com/v2/chat/completions', {
    method: 'POST',
    headers,
    body: JSON.stringify({
        canvas_id: process.env.TELA_CANVAS_ID,
        variables: {
            document: { file_url: 'https://example.com/document.pdf' },
        },
        async: true,
    }),
})

// Then poll it until it finishes
let run = await created.json()

while (!['succeeded', 'failed'].includes(run.status)) {
    await new Promise((resolve) => setTimeout(resolve, 2000))
    const poll = await fetch(`https://api.tela.com/v2/chat/completions/${run.id}`, {
        headers,
    })
    run = await poll.json()
}

if (run.status === 'succeeded') {
    console.log(run.output_content.content)
}
```

```python Python (requests) theme={null}
import os
import time
import requests

headers = {"Authorization": f"Bearer {os.getenv('TELA_API_KEY')}"}

# Start the run in the background
created = requests.post(
    "https://api.tela.com/v2/chat/completions",
    headers=headers,
    json={
        "canvas_id": os.getenv("TELA_CANVAS_ID"),
        "variables": {
            "document": {"file_url": "https://example.com/document.pdf"},
        },
        "async": True,
    },
)

# Then poll it until it finishes
run = created.json()

while run["status"] not in ("succeeded", "failed"):
    time.sleep(2)
    poll = requests.get(
        f"https://api.tela.com/v2/chat/completions/{run['id']}",
        headers=headers,
    )
    run = poll.json()

if run["status"] == "succeeded":
    print(run["output_content"]["content"])
```

```sh cURL theme={null}
curl https://api.tela.com/v2/chat/completions/b83b1558-de5b-4e45-b517-7bececbdd154 \
    -H "Authorization: Bearer $TELA_API_KEY"
```

## Using a webhook URL

Pass a `webhook_url` and the run becomes asynchronous without the `async` flag. The immediate response is the run with `status: "created"`; when the run finishes, Tela sends a `POST` request to your URL.

```typescript TypeScript Tela SDK theme={null}
const run = await tela.completions.create<{ document: TelaFile }, { fileSummary: string }>({
    canvasId: process.env.TELA_CANVAS_ID,
    variables: {
        document: tela.createFile('https://www.wmaccess.com/downloads/sample-invoice.pdf'),
    },
    webhookUrl: 'https://example.com/webhook',
})
```

```javascript JavaScript Tela SDK theme={null}
const run = await tela.completions.create({
    canvasId: process.env.TELA_CANVAS_ID,
    variables: {
        document: tela.createFile('https://www.wmaccess.com/downloads/sample-invoice.pdf'),
    },
    webhookUrl: 'https://example.com/webhook',
})
```

```python Python Tela SDK theme={null}
run = client.completions.create(
    canvas_id=TELA_CANVAS_ID,
    variables={
        "document": client.create_file("https://www.wmaccess.com/downloads/sample-invoice.pdf"),
    },
    webhook_url="https://example.com/webhook",
)
```

```sh cURL theme={null}
curl -X POST https://api.tela.com/v2/chat/completions \
    -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $TELA_API_KEY" \
    -d '{
        "canvas_id": "95cec438-6080-44fe-8275-28b16bd40178",
        "variables": {
            "document": { "file_url": "https://www.wmaccess.com/downloads/sample-invoice.pdf" }
        },
        "webhook_url": "https://example.com/webhook"
    }'
```

### The callback

Your endpoint receives a `POST` with:

* **Headers**: `Content-Type: application/json` and `x-completion-run-id`, the ID of the run. Use it to match the callback with the run you created, since it is the same `id` the creation response returned.
* **Body**: the [run object](#the-run-object) with its final `status` (`succeeded` or `failed`) and, on success, the answer in `output_content.content` and `raw_output`.

Keep the handler fast: store the payload and acknowledge with a `2xx`, then process it. Whether a delivery is retried when your endpoint fails is not documented here, so if you need a guarantee, also poll `GET /v2/chat/completions/{id}` for runs that have not reported back after a while.

> [!TIP]
>
> Use HTTPS for `webhook_url` and verify that the request came from Tela before trusting it, for example by checking `x-completion-run-id` against the runs you created. When you create tasks with `POST /task`, request headers prefixed with `x-tela-forward-` are forwarded to your webhook without the prefix, which lets you send a secret of your own; see [Authentication and headers](https://docs.tela.com/en/workstation/events/authentication.md).

## Listing past runs

Every completion, synchronous or not, is stored as a completion run. `GET /v1/completion-run` returns the runs of your Workspace, most recently updated first, so you can audit what ran, reconcile a batch or find a run whose callback you missed.

| Query parameter | Effect |
| - | - |
| `limit`, `offset` | Page size (default 10, 1 to 100) and number of runs to skip. |
| `promptId` | Only runs of this Canvas. |
| `promptVersionId` | Only runs of this version. |
| `promptApplicationId` | Only runs of this App (tasks). |
| `tags` | Only runs containing all these tags, as a JSON array (`["a","b"]`) or comma-separated (`a,b`). |
| `objectLinks` | When `true`, `meta.links.*` are objects instead of query strings. |

```sh cURL theme={null}
curl "https://api.tela.com/v1/completion-run?promptId=95cec438-6080-44fe-8275-28b16bd40178&tags=batch-1&limit=50" \
    -H "Authorization: Bearer $TELA_API_KEY"
```

The response has a `data` array and a `meta` object with `totalCount`, `limit`, `offset`, `currentPage`, `totalPages` and `links` (`first`, `last`, `next`, `previous`). Each run has the same information as the run object above, with two differences: the keys are camelCase (`inputContent`, `rawInput`, `promptVersionId`) and it adds `tags`, `creditsUsed`, `promptId` and a `metadata` snapshot of the version that ran (`modelConfigurations`, `variablesDefinitions` and, when the request used an environment, `requestedEnvironmentName`).

```json Response theme={null}
{
    "data": [
        {
            "id": "b83b1558-de5b-4e45-b517-7bececbdd154",
            "status": "succeeded",
            "inputContent": { "variables": { "document": { "files": [ { "file_url": "https://example.com/document.pdf" } ] } }, "messages": [], "files": [] },
            "outputContent": { "role": "assistant", "content": { "fileSummary": "Quarterly financial report with a 15% revenue increase." } },
            "rawInput": { "canvas_id": "95cec438-6080-44fe-8275-28b16bd40178", "variables": { "document": { "file_url": "https://example.com/document.pdf" } } },
            "rawOutput": { "fileSummary": "Quarterly financial report with a 15% revenue increase." },
            "compatibilityDate": "2024-10-14",
            "metadata": {
                "promptVersion": {
                    "modelConfigurations": { "model": "gpt-4o", "type": "chat" },
                    "variablesDefinitions": [ { "name": "document", "type": "file", "required": true } ]
                }
            },
            "tags": ["batch-1"],
            "creditsUsed": 3,
            "promptId": "95cec438-6080-44fe-8275-28b16bd40178",
            "promptVersionId": "76065caa-c519-441a-8fe2-cc43e29664ab",
            "promptApplicationId": null,
            "workspaceId": "3f55cfd9-4427-4887-a181-d9ad39967802",
            "createdAt": "2024-10-14T19:00:53.664Z",
            "updatedAt": "2024-10-14T19:01:15.892Z",
            "deletedAt": null
        }
    ],
    "meta": {
        "totalCount": 1,
        "limit": 50,
        "offset": 0,
        "currentPage": 1,
        "totalPages": 1,
        "links": { "first": "?promptId=95cec438-6080-44fe-8275-28b16bd40178&tags=batch-1&limit=50&offset=0", "last": "?promptId=95cec438-6080-44fe-8275-28b16bd40178&tags=batch-1&limit=50&offset=0", "next": null, "previous": null }
    }
}
```

Listing runs requires the `member` role or higher; Workspace API keys satisfy this. When you filter by `promptId`, `promptVersionId` or `promptApplicationId`, the resource must be accessible to your key, otherwise the call returns `404`.

> [!TIP]
>
> Send a [tag](https://docs.tela.com/en/tags.md#sending-tags-via-api) that identifies your batch or your own record ID in each request (for example `order-123`), then filter by it here. It is the simplest way to find a run again without storing every run ID.

To read one run by ID at any time, use `GET /v2/chat/completions/{id}`, as in [Polling the result](#polling-the-result). Full parameter and schema details are in the API reference: [Run Canvas](https://docs.tela.com/api-reference/completion/create.md), [Retrieve Canvas Run](https://docs.tela.com/api-reference/completion/get.md) and [List completion runs](https://docs.tela.com/api-reference/completion-run/get-v1-completion-run.md).


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