# Kampányok

> Piszkozatok Custom HTML tartalommal, tesztlevél, időzítés, eredmények.

Olvasáshoz Kampányok: megtekintés, minden módosításhoz Kampányok: kezelés jog kell.

## Mire képes az API

Az API **Custom HTML tartalmú, normál kampányokat** hoz létre. Az A/B, a többváltozós és az újraküldő kampányok, a drag & drop és az egyszerű szerkesztő, valamint a sablonok a felületen készülnek. Az API ettől még minden kampányt listáz és olvas, és bármelyiket időzítheti, visszavonhatja vagy törölheti.

| Metódus és útvonal | Mit csinál |
|---|---|
| `GET /campaigns` | Listázza a kampányokat, a legutóbb módosítottal kezdve. |
| `POST /campaigns` | Piszkozatot hoz létre. |
| `GET /campaigns/{id}` | Egy kampány (Custom HTML esetén a tartalmával). |
| `PATCH /campaigns/{id}` | Módosít egy piszkozatot. |
| `DELETE /campaigns/{id}` | Töröl egy kampányt. |
| `POST /campaigns/{id}/test` | Tesztlevelet küld. |
| `POST /campaigns/{id}/schedule` | Azonnal kiküld vagy időzít egy piszkozatot. |
| `POST /campaigns/{id}/cancel` | Visszaveszi az időzített kampányt, vagy leállítja a küldés alatt állót. |
| `GET /campaigns/{id}/activity` | A címzettek és amit tettek. |

## A kampány objektum

```json
{
  "id": "30bc6133-16ad-4074-b311-204127a80b62",
  "name": "Winter-ready garden (EN)",
  "type": "regular",
  "status": "sent",
  "editor": "dragdrop",
  "subject": "Get your garden ready for winter",
  "from_name": "Kertlak Garden Shop",
  "from_email": "news@news.kertlak.example.com",
  "reply_to": "info@kertlak.example.com",
  "recipients": {"all": false, "groups": ["5e30c6ff-0462-47d1-9a6c-7db4c33d6b9a"], "segments": [], "exclude_groups": [], "exclude_segments": [], "has_rules": false},
  "delivery": "instant",
  "scheduled_for": null,
  "created_at": "2026-10-15T07:00:00Z",
  "updated_at": "2026-10-20T08:02:25.333333Z",
  "sent_at": "2026-10-20T08:02:25.333333Z",
  "stats": {"recipients": 1272, "sent": 1272, "delivered": 1266, "opened": 337, "clicked": 57, "unsubscribed": 4, "bounced": 0, "soft_bounced": 6, "complaints": 0, "open_rate": 26.49, "click_rate": 4.48}
}
```

| Kulcs | Jelentés |
|---|---|
| `type` | `regular`, `ab`, `multivariate` vagy `resend`. A felületen készült kampányokat az API a típusuk saját beállításai nélkül mutatja. |
| `status` | `draft`, `ready` (időzítve, a Kimenő mappában), `sending`, `paused`, `sent` vagy `canceled`. |
| `editor` | `dragdrop`, `simple`, `html` (Custom HTML) vagy `null` (még nincs tartalom). |
| `recipients` | `all` (minden aktív feliratkozó), vagy a csoport- és szegmenslisták a kizárásokkal. A `has_rules` értéke `true`, ha a címzetteket a felületen összeállított szabálykészlet választja ki; ilyenkor a listák üresek. |
| `delivery` | `instant`, `scheduled`, `timezone_based`, piszkozatnál `null`. |
| `scheduled_for` | A küldés kezdete UTC-ben. `timezone_based` esetén az, amikor az első időzóna eléri a választott helyi időt. |
| `sent_at` | Kiküldött kampánynál a küldés befejezése. |
| `stats` | Az `opened` és a `clicked` egyedi; a `bounced` a végleges visszapattanás; az `open_rate` és a `click_rate` a `sent` százaléka. |
| `content` | Csak Custom HTML kampány egyedi lekérésénél: `html` és `text`. |

## Kampányok listázása

| Paraméter | Jelentés |
|---|---|
| `status` | `draft`, `outbox` (ready, sending, paused) vagy `sent` (sent, canceled). Nélküle minden kampány. |
| `type` | `regular`, `ab`, `multivariate`, `resend`. |
| `search` | A név vagy a tárgy tartalmazza. |
| `limit`, `cursor` | Lapozás. A `total` mindig ott van. |

```bash
curl -H "Authorization: Bearer $ECHO_TOKEN" "$ECHO_API/campaigns?status=outbox"
```

```json
{"data":[{"id":"f8ab784a-0a6a-45f0-8dd8-38ecd146826a","name":"Novemberi hírlevél: téli madáretetés","type":"regular","status":"ready","editor":"dragdrop","subject":"Téli madáretetés: mikor kezdjük és mivel?","from_name":"Kertlak Webshop","from_email":"hirlevel@news.kertlak.example.com","reply_to":"info@kertlak.example.com","recipients":{"all":false,"groups":["d5478c4a-97d3-4fa9-b38d-dd32c41dbac3"],"segments":[],"exclude_groups":[],"exclude_segments":[],"has_rules":false},"delivery":"scheduled","scheduled_for":"2026-11-05T08:00:00Z","created_at":"2026-10-19T05:20:29.127968Z","updated_at":"2026-10-21T09:20:29.127968Z","sent_at":null,"stats":{"recipients":0,"sent":0,"delivered":0,"opened":0,"clicked":0,"unsubscribed":0,"bounced":0,"soft_bounced":0,"complaints":0,"open_rate":0,"click_rate":0}}],"meta":{"next_cursor":null,"prev_cursor":null,"limit":25,"total":1}}
```

A listákban sosincs `content`.

## Piszkozat létrehozása

`POST /campaigns`. A `name` és a `content.html` kötelező.

| Kulcs | Jelentés |
|---|---|
| `name` | Belső név (legfeljebb 255 karakter). |
| `subject`, `from_name`, `from_email`, `reply_to` | A levél adatai. Nélkülük a munkaterület alapértelmezései érvényesek. A `from_email` címnek ellenőrzött küldő domainen kell lennie; ezt az időzítéskor ellenőrzi az Echo, előtte nem. |
| `recipients` | `all`, `groups`, `segments`, `exclude_groups`, `exclude_segments` (azonosítók). Lecseréli a listákat. |
| `content.html` | A Custom HTML. Ugyanúgy tisztítja az Echo, mint a felületen, és minden benne lévő címkének léteznie kell. |
| `content.text` | A sima szöveges változat. Nélküle az Echo a HTML-ből készíti. |

```bash
curl -X POST "$ECHO_API/campaigns" \
  -H "Authorization: Bearer $ECHO_TOKEN" -H "Content-Type: application/json" \
  -d '{"name":"Őszi kertápolás","subject":"Őszi kertápolás: öt tipp","from_name":"Kertlak Webshop","from_email":"hirlevel@news.kertlak.example.com","recipients":{"groups":["d5478c4a-97d3-4fa9-b38d-dd32c41dbac3"]},"content":{"html":"<h1>Kedves {$name},</h1><p>itt az őszi kertápolási tippsorunk.</p><p><a href=\"{$unsubscribe}\">Leiratkozás</a></p>"}}'
```

`201 Created`:

```json
{"data":{"id":"899d27ec-f6f3-4551-a7c5-7fc993cd4002","name":"Őszi kertápolás","type":"regular","status":"draft","editor":"html","subject":"Őszi kertápolás: öt tipp","from_name":"Kertlak Webshop","from_email":"hirlevel@news.kertlak.example.com","reply_to":"info@kertlak.example.com","recipients":{"all":false,"groups":["d5478c4a-97d3-4fa9-b38d-dd32c41dbac3"],"segments":[],"exclude_groups":[],"exclude_segments":[],"has_rules":false},"delivery":null,"scheduled_for":null,"created_at":"2026-10-22T06:08:40.347097Z","updated_at":"2026-10-22T06:08:40.363886Z","sent_at":null,"stats":{"recipients":0,"sent":0,"delivered":0,"opened":0,"clicked":0,"unsubscribed":0,"bounced":0,"soft_bounced":0,"complaints":0,"open_rate":0,"click_rate":0},"content":{"html":"<h1>Kedves {$name},</h1><p>itt az őszi kertápolási tippsorunk.</p><p><a href=\"{$unsubscribe}\">Leiratkozás</a></p>","text":"***************\nKedves {$name},\n***************\n\nitt az őszi kertápolási tippsorunk.\n\nLeiratkozás ( {$unsubscribe} )"}}}
```

A címkék a mezők címkéi (`{$name}`, `{$city}`, a saját mezőid), valamint a rendszerlinkek: `{$unsubscribe}`, `{$preferences}`, `{$webview}` és `{$forward}`. Ismeretlen címke:

```json
{"error":{"code":"unknown_tags","message":"Unknown personalization tags: {$keresztnev}","fields":{"content.html":"Unknown personalization tags: {$keresztnev}"}}}
```

Ha a piszkozat létrehozása utáni lépés hibázik (például a tartalom), az Echo törli a piszkozatot, és annak a lépésnek a hibájával válaszol.

## Piszkozat lekérése és módosítása

A `GET /campaigns/{id}` visszaadja a kampányt; Custom HTML kampánynál a `content` is benne van.

A `PATCH /campaigns/{id}` egy **piszkozat** megadott kulcsait módosítja (ugyanazok a kulcsok, mint létrehozáskor). A `content` a teljes tartalmat lecseréli, és Custom HTML kampányoknál, illetve még tartalom nélküli piszkozatoknál fogadja el az API.

```bash
curl -X PATCH "$ECHO_API/campaigns/899d27ec-f6f3-4551-a7c5-7fc993cd4002" \
  -H "Authorization: Bearer $ECHO_TOKEN" -H "Content-Type: application/json" \
  -d '{"subject":"Őszi kertápolás: öt tipp a hidegek előtt"}'
```

| Hiba | Mikor |
|---|---|
| `409 not_editable` | A kampány nem piszkozat (átnevezésnél is), vagy más szerkesztőben készült kampánynak küldtél `content` kulcsot. |
| `409 editor_chosen` | Egy párhuzamos kérés ugyanekkor más szerkesztőt választott a piszkozathoz. |

A PATCH nem atomi: az adatok a tartalom előtt mentődnek, így egy tartalomhiba után a többi módosítás megmaradhat.

## Tesztlevél küldése

A `POST /campaigns/{id}/test` 1-5 címre küldi el a kampányt, a tárgy elején `[Test]` jelöléssel. A feladónak ellenőrzött küldő domainen kell lennie, és a tartalomnak meg kell lennie.

```bash
curl -X POST "$ECHO_API/campaigns/899d27ec-f6f3-4551-a7c5-7fc993cd4002/test" \
  -H "Authorization: Bearer $ECHO_TOKEN" -H "Content-Type: application/json" \
  -d '{"emails":["teszt@example.com"]}'
```

`202 Accepted`, üres törzzsel. Egy munkaterület bármely 24 órában 50 tesztlevelet küldhet, a felületről küldötteket is beleértve; fölötte a válasz `422`.

## Időzítés és visszavonás

A `POST /campaigns/{id}/schedule` kiveszi a piszkozatot a piszkozatok közül:

| Kulcs | Jelentés |
|---|---|
| `delivery` | `instant` (azonnali küldés), `scheduled` vagy `timezone_based` (mindenkinek ugyanabban a helyi időben). |
| `date` | `YYYY-MM-DD`, `scheduled` és `timezone_based` esetén. |
| `time` | `HH:MM`, 15 perces lépésekben, `scheduled` és `timezone_based` esetén. |
| `timezone` | IANA-név a [`GET /timezones`](https://docs.uptools.io/hu/echo/developer-guide/groups-segments-fields/#időzónák) listából, `scheduled` esetén. |

Előbb a felület küldés előtti ellenőrzései futnak: ellenőrzött küldő domainen lévő feladó, a cégadatok, leiratkozási link a tartalomban (ha nincs bekapcsolva az automatikus footer) és a címzettek. A ki nem küldhető kampány `422 validation` választ ad a mezőkkel. Az időpont legfeljebb egy évvel későbbi lehet; a `timezone_based` normál kampányoknál, legalább 24 órával előre működik.

```bash
curl -X POST "$ECHO_API/campaigns/899d27ec-f6f3-4551-a7c5-7fc993cd4002/schedule" \
  -H "Authorization: Bearer $ECHO_TOKEN" -H "Content-Type: application/json" \
  -d '{"delivery":"scheduled","date":"2027-09-30","time":"09:00","timezone":"Europe/Budapest"}'
```

`200 OK` a kampánnyal, immár `"status":"ready"`, `"delivery":"scheduled"` és `"scheduled_for":"2027-09-30T07:00:00Z"` értékkel. A 15 perces rácsra nem eső időpont:

```json
{"error":{"code":"validation","message":"Please check the highlighted fields.","fields":{"schedule":"Choose a time in the future, in 15-minute steps."}}}
```

Az `instant` valóban elkezdi a küldést minden címzettnek. Előtte küldj tesztlevelet, vagy próbáld ki egy kis tesztcsoporton.

A `POST /campaigns/{id}/cancel` (törzs nélkül): az időzített kampány visszakerül a piszkozatok közé (`"status":"draft"`, `"delivery":null`), a küldés alatt álló vagy szüneteltetett leáll, és `canceled` lesz. Más állapotban a válasz `409 not_editable`.

```bash
curl -X POST "$ECHO_API/campaigns/899d27ec-f6f3-4551-a7c5-7fc993cd4002/cancel" -H "Authorization: Bearer $ECHO_TOKEN"
```

## Kampány törlése

```bash
curl -X DELETE "$ECHO_API/campaigns/899d27ec-f6f3-4551-a7c5-7fc993cd4002" -H "Authorization: Bearer $ECHO_TOKEN"
```

`204 No Content`. A küldés alatt álló vagy szüneteltetett kampányt előbb vissza kell vonni (`409 not_editable`), a 30 napon belül kiküldött kampány pedig még nem törölhető (`409 recently_sent`).

## A címzettek tevékenysége

A `GET /campaigns/{id}/activity` a már küldés alatt álló vagy kiküldött kampány címzettjeit listázza (korábban üres oldalt ad).

| Paraméter | Jelentés |
|---|---|
| `type` | `all` (alapértelmezett), `opened`, `clicked`, `forwarded`, `unopened`, `unsubscribed`, `hard_bounce`, `soft_bounce`, `complaints`. |
| `search` | Az e-mail-cím tartalmazza (Feliratkozók: megtekintés jog kell hozzá). |
| `limit`, `cursor` | Lapozás. |

```bash
curl -H "Authorization: Bearer $ECHO_TOKEN" \
  "$ECHO_API/campaigns/30bc6133-16ad-4074-b311-204127a80b62/activity?type=clicked&limit=2"
```

```json
{"data":[{"subscriber":{"id":"04393fa0-81ff-44a3-82e2-67af141df99a","email":"alice.clarke7@example.com"},"opens":1,"clicks":1,"sent_at":"2026-10-20T08:00:23.444444Z","opened_at":"2026-10-20T08:34:16.826706Z","clicked_at":"2026-10-20T08:47:18.826706Z"},{"subscriber":{"id":"f56a4743-a5db-4d64-b9dc-bb69afdf0659","email":"alice.hughes92@example.com"},"opens":1,"clicks":1,"sent_at":"2026-10-20T08:02:19.666667Z","opened_at":"2026-10-20T09:58:56.816405Z","clicked_at":"2026-10-20T10:09:11.816405Z"}],"meta":{"next_cursor":"cDoy","prev_cursor":null,"limit":2,"total":57}}
```

Az `opened_at` és a `clicked_at` az első megnyitás és kattintás. Feliratkozók: megtekintés jog nélkül a `subscriber` objektumban csak az `id` van.
