> Adsly docs index: https://adsly.pro/docs/llms.txt · every page in one file: https://adsly.pro/docs/llms-full.txt · OpenAPI: https://adsly.pro/docs/api/openapi.yaml

# Tasks (async operations)

Creating, copying and bulk changes run in the background. How to follow a task with GET /v1/tasks/{id}, what its statuses mean and how to read its result.

Some operations talk to Telegram once per campaign, so they can take longer than an HTTP request should wait. These return `202 Accepted` with a **task** instead of the final result:

- `POST /v1/campaigns` — create
- `POST /v1/campaigns/{ad_id}/copy` — copy
- `POST /v1/campaigns/bulk` — pause, resume, budget, CPM or delete many

Single-campaign changes (`PATCH`, pause, resume, budget, delete) answer directly — no task.

## The 202 response

```http
HTTP/1.1 202 Accepted
Location: /api/v1/tasks/81234
Retry-After: 2
```

```json
{
  "success": true,
  "data": {
    "id": 81234,
    "type": "create",
    "status": "pending",
    "account_id": 53,
    "progress": 0,
    "total": 3,
    "succeeded": null,
    "failed": null,
    "ad_ids": [],
    "errors": [],
    "paused_reason": null,
    "error": null,
    "created_at": "2026-10-08T10:15:02.411Z",
    "started_at": null,
    "completed_at": null
  }
}
```

## GET /v1/tasks/{id}

```bash
curl -s https://app.adsly.pro/api/v1/tasks/81234 -H "X-API-Key: $ADSLY_API_KEY"
```

Same shape as above. Poll it until `status` is final; while a task is `pending` or `running` the response carries `Retry-After: 2` — wait that long between polls. Any key of the same owner can read the task.

| Field | Meaning |
|---|---|
| `type` | `create`, `copy`, `status` (bulk pause/resume), `budget`, `cpm`, `delete` |
| `status` | see below |
| `progress` / `total` | campaigns processed so far / in total |
| `succeeded` / `failed` | counts, once known |
| `ad_ids` | campaigns the task created (create, copy) or changed (bulk) |
| `errors[]` | up to 50 failures: `{ ad_id, index, error }` — `index` is the position in your `campaigns` array for creates |
| `paused_reason` | why a task stopped to wait, e.g. the cabinet ran out of balance |
| `error` | why the whole task failed |

### Statuses

| Status | Meaning | Final? |
|---|---|---|
| `pending` | queued — the cabinet runs one create and one other operation at a time | no |
| `running` | in progress | no |
| `paused` | stopped to wait — usually the cabinet balance ran out mid-way. Top up the cabinet; resume it from the panel | no |
| `completed` | finished. Check `failed` and `errors` — a completed task can contain individual failures | yes |
| `failed` | the task as a whole failed — see `error` | yes |
| `cancelled` | cancelled from the panel | yes |

## Things to know about creates

- New campaign ids appear in `ad_ids` when the task completes (they are found by syncing the cabinet after the last campaign is created).
- If the **first** campaign of a batch fails, the task stops there rather than repeat the same mistake 100 times. Fix the input and send again.
- If Telegram can't resolve any of a campaign's channels or bots, that campaign fails with `No targets found: …` listing them.
- Telegram reviews every new ad. A created campaign usually starts `In Review`; a [webhook](https://adsly.pro/docs/api/webhooks.md) tells you when that changes.

## A polling loop

```js
async function waitForTask(id) {
  for (;;) {
    const res = await fetch(`https://app.adsly.pro/api/v1/tasks/${id}`, {
      headers: { 'X-API-Key': process.env.ADSLY_API_KEY },
    });
    const { data } = await res.json();
    if (['completed', 'failed', 'cancelled'].includes(data.status)) return data;
    if (data.status === 'paused') throw new Error(`Task paused: ${data.paused_reason}`);
    await new Promise(r => setTimeout(r, Number(res.headers.get('Retry-After') || 2) * 1000));
  }
}
```

```python
import time, requests

def wait_for_task(task_id):
    while True:
        r = requests.get(f"https://app.adsly.pro/api/v1/tasks/{task_id}",
                         headers={"X-API-Key": API_KEY}, timeout=30)
        task = r.json()["data"]
        if task["status"] in ("completed", "failed", "cancelled"):
            return task
        if task["status"] == "paused":
            raise RuntimeError(f"Task paused: {task['paused_reason']}")
        time.sleep(int(r.headers.get("Retry-After", 2)))
```

---
Page: https://adsly.pro/docs/api/tasks/ · Updated: 2026-10-08
