# Basketly API v1 — dokumentacja dla agentów AI

Pełna dokumentacja publicznego API Basketly w jednym pliku Markdown. Wersja HTML: https://basketly.io/docs/api. Specyfikacja OpenAPI 3.1: https://app.basketly.io/api/v1/openapi.json. Serwer MCP dla agentów: https://basketly.io/docs/mcp.md.

Base URL: `https://app.basketly.io/api/v1`

## Uwierzytelnianie

Każde żądanie wysyła klucz organizacji w nagłówku `Authorization: Bearer bsk_live_…`. Klucz należy do jednej organizacji i widzi tylko jej dane.

Pełny klucz pokazujemy tylko raz, przy tworzeniu. Przechowujemy wyłącznie jego skrót, więc nie da się go odzyskać: zgubiony klucz unieważnij i utwórz nowy.

Klucz możesz w każdej chwili unieważnić w panelu i możesz ustawić mu termin ważności. Unieważniony klucz dostaje `401 key_revoked`, wygasły `401 key_expired`.

Klucz ma uprawnienia (scopes). Uprawnienie `:write` obejmuje też `:read` tego samego obszaru. `contacts:*`, `tasks:*` i `orders:read` zwracają dane osobowe. Brak uprawnienia to `403 insufficient_scope` z polem `details.required_scope`.

API i serwer MCP są w planach Rozwój, Agencja i Indywidualny. W planach Darmowy i Start właściciel konta ma 5 dni na wypróbowanie od założenia konta. Potem każde żądanie dostaje `402 plan_required`, nowych kluczy nie da się utworzyć, a webhooki API nie są wysyłane. Po wyborze planu Rozwój lub wyższego w „Plan i rozliczenia” dotychczasowe klucze działają od razu.

| Uprawnienie | Na co pozwala |
| --- | --- |
| `contacts:read` | Odczyt osób ze strony Klienci (imiona, telefony, e-maile — dane osobowe). |
| `contacts:write` | Dodawanie i zmiana osób: dane, notatki, tagi, status, blokada SMS. |
| `events:read` | Odczyt konwersji i statusu ich wysyłek do Meta. |
| `events:write` | Wysyłanie konwersji serwer–serwer do kolejki. |
| `tasks:read` | Otwarte zadania (dane osobowe). |
| `tasks:write` | Dodawanie, zamykanie i przekładanie zadań. |
| `campaigns:read` | Raport kampanii. |
| `creatives:read` | Raport kreacji. |
| `automations:read` | Lista automatyzacji z ostatnim uruchomieniem i błędami z doby. |
| `integrations:read` | Przegląd integracji: stany i połączenia kluczem, bez kluczy. |
| `orders:read` | Zamówienia ze sklepu z pozycjami i kupującymi (dane osobowe: imiona kupujących). |
| `products:read` | Produkty, ich diagnoza i kartoteka produktu. |
| `products:write` | Ręczne poprawki produktów. |
| `feeds:read` | Feedy, reguły, przepływy, kategorie Google i podgląd zmian. |
| `feeds:write` | Dodawanie i zmiana feedów, zapis reguł, przepływów i kategorii, pobranie teraz. |
| `templates:read` | Szablony grafik. |
| `diagnostics:read` | Ostatnia autodiagnostyka. |
| `diagnostics:write` | Uruchamianie autodiagnostyki. |
| `jobs:read` | Odczyt zadań w tle. |
| `webhooks:read` | Odczyt webhooków i historii ich wysyłek. |
| `webhooks:write` | Dodawanie, zmiana, usuwanie i testowanie webhooków. |

## Błędy

Każdy błąd ma ten sam kształt: `{"error": {"code", "message", "details?"}}`.

`code` jest stały i nigdy nie jest tłumaczony, więc na nim opieraj obsługę błędów. `message` jest dla człowieka, w języku z nagłówka `Accept-Language`: domyślnie polski, `en` dla angielskiego. `details` pojawia się tylko tam, gdzie są dodatkowe dane, np. `required_scope`.

| Kod | Znaczenie |
| --- | --- |
| `unauthorized` | Brak klucza, zły format albo nieznany klucz. |
| `key_revoked` | Klucz został unieważniony. |
| `key_expired` | Minął termin ważności klucza. |
| `insufficient_scope` | Klucz nie ma potrzebnego uprawnienia (`details.required_scope`). |
| `plan_required` | Plan organizacji nie obejmuje API: po 5 dniach na wypróbowanie potrzebny jest plan Rozwój lub wyższy. |
| `rate_limited` | Ponad 120 żądań na minutę tym kluczem; odczekaj `Retry-After` sekund. |
| `invalid_json` | Treść nie jest poprawnym JSON-em albo zawiera nieznane pole. |
| `validation_error` | Nieprawidłowa wartość pola lub parametru; opis mówi, co poprawić. |
| `invalid_cursor` | `cursor` nie pochodzi z poprzedniej strony tej samej listy. |
| `not_found` | Brak takiego rekordu w Twojej organizacji. |
| `route_not_found` | Nie ma takiego endpointu. |
| `conflict` | Operacja kłóci się z aktualnym stanem rekordu. |
| `contact_exists` | Osoba z tym telefonem lub e-mailem już istnieje (`details.contact_id`). |
| `contact_merged` | Karta została scalona z inną (`details.merged_into`). |
| `event_conflict` | Zdarzenie koliduje z już przyjętym. |
| `source_paused` | Źródło jest wstrzymane. |
| `limit_reached` | Osiągnięty limit, np. 10 webhooków na organizację. |
| `idempotency_key_reused` | Ten sam `Idempotency-Key` z inną treścią żądania. |
| `idempotency_in_progress` | Żądanie z tym `Idempotency-Key` jeszcze trwa; ponów za chwilę. |
| `payload_too_large` | Treść żądania ponad 64 KB. |
| `internal_error` | Błąd po naszej stronie; ponów żądanie później. |

## Paginacja

Listy zwracają `{"data": [...], "next_cursor", "has_more"}`. Rozmiar strony ustawiasz parametrem `limit` (1–100, domyślnie 25).

Następną stronę pobierasz, przekazując `next_cursor` z poprzedniej odpowiedzi jako `cursor`. Gdy `has_more` jest `false`, to koniec listy.

Kolejność to `updated_at`, a przy remisie identyfikator: `order=desc` od najnowszych (domyślnie) albo `order=asc`.

`updated_since` (ISO 8601) zwraca tylko rekordy zmienione w tym czasie lub później. Do synchronizacji przyrostowej zapisz czas, w którym zaczynasz synchronizację, przejdź przez wszystkie strony do końca i użyj zapisanego czasu jako `updated_since` przy następnej synchronizacji.

## Limity i formaty

- Limit: 120 żądań na minutę na klucz. Każda odpowiedź ma nagłówki `X-RateLimit-Limit`, `X-RateLimit-Remaining` i `X-RateLimit-Reset`.
- Po przekroczeniu dostajesz `429 rate_limited` z nagłówkiem `Retry-After` (sekundy). Odczekaj tyle i ponów.
- Treść żądania może mieć najwyżej 64 KB, inaczej `413 payload_too_large`.
- Czasy w odpowiedziach są w ISO 8601 w UTC, np. `2026-10-08T10:00:00Z`.

## Idempotencja

- Każdy `POST` przyjmuje nagłówek `Idempotency-Key` (1–255 widocznych znaków ASCII). Użyj wartości związanej z operacją, np. UUID zapisanego razem z rekordem po Twojej stronie.
- Powtórzenie żądania z tym samym kluczem i tą samą treścią w ciągu 24 godzin zwraca zapisaną odpowiedź z nagłówkiem `Idempotent-Replayed: true`, bez ponownego wykonania operacji.
- Ten sam klucz z inną treścią to `422 idempotency_key_reused`. Gdy pierwsze żądanie jeszcze trwa, dostajesz `409 idempotency_in_progress`.
- Odpowiedzi 5xx i 429 nie są zapisywane, więc po nich możesz bezpiecznie ponowić żądanie z tym samym kluczem.

## Endpointy

### Klucze

Używany klucz.

#### `GET /me` — Opis używanego klucza

Organizacja, nazwa klucza, uprawnienia i termin ważności. Bezpieczne pierwsze wywołanie do sprawdzenia nowego klucza; nie wymaga uprawnień.

Uprawnienie: żadne

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `organization` | object |  |  |
| `organization.id` | string |  |  |
| `organization.name` | string |  |  |
| `key` | object |  |  |
| `key.id` | string |  |  |
| `key.name` | string |  |  |
| `key.prefix` | string |  |  |
| `key.scopes` | string[] |  |  |
| `key.expires_at` | string (date-time) \| null |  |  |
| `rate_limit` | object |  |  |
| `rate_limit.per_minute` | integer |  |  |

```bash
curl "https://app.basketly.io/api/v1/me" \
  -H "Authorization: Bearer $API_KEY"
```

### Osoby

Osoby ze strony Klienci.

#### `GET /contacts` — Lista osób

Osoby ze strony Klienci (scalone karty są pominięte). Filtruj po tagu albo dokładnym e-mailu lub telefonie.

Uprawnienie: `contacts:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `limit` | query |  | Rozmiar strony, 1–100 (domyślnie 25). |
| `cursor` | query |  | `next_cursor` z poprzedniej strony. |
| `order` | query |  | Sortowanie po `updated_at` (potem id): `desc` od najnowszych (domyślnie) lub `asc`. |
| `updated_since` | query |  | Tylko rekordy zmienione w tym czasie ISO 8601 lub później. |
| `tag` | query |  | Tag. |
| `email` | query |  | Dokładny e-mail (dopasowanie po skrócie). |
| `phone` | query |  | Dokładny telefon (dopasowanie po skrócie). |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | Contact[] | yes |  |
| `data[].id` | string |  | Identyfikator osoby. |
| `data[].name` | string |  | Imię i nazwisko. |
| `data[].phone` | string |  | Telefon w formacie E.164, pusty gdy nieznany. |
| `data[].email` | string |  | E-mail, pusty gdy nieznany. |
| `data[].tags` | string[] |  | Tagi. |
| `data[].sms_optout` | boolean |  | Prawda, gdy SMS-y do tej osoby są zablokowane. |
| `data[].first_source` | string |  | Skąd osoba przyszła po raz pierwszy („Zamówienie w sklepie”, „Ręcznie”, API…). |
| `data[].status_id` | string \| null |  | Identyfikator statusu wewnętrznego (GET /contact-statuses), null gdy brak. Nigdy nie trafia do Mety. |
| `data[].owner_id` | string \| null |  | Identyfikator użytkownika — opiekuna osoby. |
| `data[].last_activity_at` | string (date-time) \| null |  | Czas ostatniej aktywności. |
| `data[].created_at` | string (date-time) |  | Czas utworzenia. |
| `data[].updated_at` | string (date-time) |  | Ostatnia zmiana; używaj z `updated_since`. |
| `data[].orders` | object |  | Co osoba kupiła w sklepie. |
| `next_cursor` | string \| null | yes | Przekaż jako `cursor`, by pobrać następną stronę; null na ostatniej stronie. |
| `has_more` | boolean | yes | Prawda, gdy istnieje kolejna strona. |

```bash
curl "https://app.basketly.io/api/v1/contacts" \
  -H "Authorization: Bearer $API_KEY"
```

#### `POST /contacts` — Dodaj osobę

Dodaje osobę jak „Dodaj osobę” w panelu.

Uprawnienie: `contacts:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `Idempotency-Key` | header |  | Unikalny klucz (1–255 widocznych znaków ASCII). Powtórzenie POST z tym samym kluczem i treścią w ciągu 24 godzin zwraca zapisaną odpowiedź z `Idempotent-Replayed: true`. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string |  | Imię i nazwisko (do 200 znaków). Max 200 characters. |
| `phone` | string |  | Telefon z numerem kierunkowym, np. +48 600 857 482, albo 9 cyfr. |
| `email` | string |  | Adres e-mail. Max 254 characters. |
| `tags` | string[] |  | Tagi. |

Odpowiedź 201:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string |  | Identyfikator osoby. |
| `name` | string |  | Imię i nazwisko. |
| `phone` | string |  | Telefon w formacie E.164, pusty gdy nieznany. |
| `email` | string |  | E-mail, pusty gdy nieznany. |
| `tags` | string[] |  | Tagi. |
| `sms_optout` | boolean |  | Prawda, gdy SMS-y do tej osoby są zablokowane. |
| `first_source` | string |  | Skąd osoba przyszła po raz pierwszy („Zamówienie w sklepie”, „Ręcznie”, API…). |
| `status_id` | string \| null |  | Identyfikator statusu wewnętrznego (GET /contact-statuses), null gdy brak. Nigdy nie trafia do Mety. |
| `owner_id` | string \| null |  | Identyfikator użytkownika — opiekuna osoby. |
| `last_activity_at` | string (date-time) \| null |  | Czas ostatniej aktywności. |
| `created_at` | string (date-time) |  | Czas utworzenia. |
| `updated_at` | string (date-time) |  | Ostatnia zmiana; używaj z `updated_since`. |
| `orders` | object |  | Co osoba kupiła w sklepie. |
| `orders.count` | integer |  | Kupione, nieanulowane zamówienia ze sklepu. |
| `orders.value` | number |  | Ich wartość w `currency`. |
| `orders.currency` | string |  | Waluta większości zamówień. |
| `orders.first_at` | string (date-time) \| null |  | Czas pierwszego zamówienia. |
| `orders.last_at` | string (date-time) \| null |  | Czas ostatniego zamówienia. |

```bash
curl -X POST "https://app.basketly.io/api/v1/contacts" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 5f0c7a2e-8d41-4b6a-9e3f-1c2d3e4f5a6b" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Anna Nowak",
    "phone": "+48600100200",
    "email": "anna@example.com",
    "tags": [
      "vip"
    ]
  }'
```

#### `GET /contacts/{id}` — Pobierz osobę

Scalona karta zwraca 409 `contact_merged` z `details.merged_into`.

Uprawnienie: `contacts:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string |  | Identyfikator osoby. |
| `name` | string |  | Imię i nazwisko. |
| `phone` | string |  | Telefon w formacie E.164, pusty gdy nieznany. |
| `email` | string |  | E-mail, pusty gdy nieznany. |
| `tags` | string[] |  | Tagi. |
| `sms_optout` | boolean |  | Prawda, gdy SMS-y do tej osoby są zablokowane. |
| `first_source` | string |  | Skąd osoba przyszła po raz pierwszy („Zamówienie w sklepie”, „Ręcznie”, API…). |
| `status_id` | string \| null |  | Identyfikator statusu wewnętrznego (GET /contact-statuses), null gdy brak. Nigdy nie trafia do Mety. |
| `owner_id` | string \| null |  | Identyfikator użytkownika — opiekuna osoby. |
| `last_activity_at` | string (date-time) \| null |  | Czas ostatniej aktywności. |
| `created_at` | string (date-time) |  | Czas utworzenia. |
| `updated_at` | string (date-time) |  | Ostatnia zmiana; używaj z `updated_since`. |
| `orders` | object |  | Co osoba kupiła w sklepie. |
| `orders.count` | integer |  | Kupione, nieanulowane zamówienia ze sklepu. |
| `orders.value` | number |  | Ich wartość w `currency`. |
| `orders.currency` | string |  | Waluta większości zamówień. |
| `orders.first_at` | string (date-time) \| null |  | Czas pierwszego zamówienia. |
| `orders.last_at` | string (date-time) \| null |  | Czas ostatniego zamówienia. |

```bash
curl "https://app.basketly.io/api/v1/contacts/abc123" \
  -H "Authorization: Bearer $API_KEY"
```

#### `PATCH /contacts/{id}` — Zmień dane osoby

Zmienia imię, telefon, e-mail lub tagi; każda zmiana to wpis w historii.

Uprawnienie: `contacts:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string |  | Imię i nazwisko. |
| `phone` | string |  | Telefon; pusty tekst usuwa go (musi zostać telefon lub e-mail). |
| `email` | string |  | E-mail; pusty tekst usuwa go. |
| `tags` | string[] |  | Zastępuje wszystkie tagi. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string |  | Identyfikator osoby. |
| `name` | string |  | Imię i nazwisko. |
| `phone` | string |  | Telefon w formacie E.164, pusty gdy nieznany. |
| `email` | string |  | E-mail, pusty gdy nieznany. |
| `tags` | string[] |  | Tagi. |
| `sms_optout` | boolean |  | Prawda, gdy SMS-y do tej osoby są zablokowane. |
| `first_source` | string |  | Skąd osoba przyszła po raz pierwszy („Zamówienie w sklepie”, „Ręcznie”, API…). |
| `status_id` | string \| null |  | Identyfikator statusu wewnętrznego (GET /contact-statuses), null gdy brak. Nigdy nie trafia do Mety. |
| `owner_id` | string \| null |  | Identyfikator użytkownika — opiekuna osoby. |
| `last_activity_at` | string (date-time) \| null |  | Czas ostatniej aktywności. |
| `created_at` | string (date-time) |  | Czas utworzenia. |
| `updated_at` | string (date-time) |  | Ostatnia zmiana; używaj z `updated_since`. |
| `orders` | object |  | Co osoba kupiła w sklepie. |
| `orders.count` | integer |  | Kupione, nieanulowane zamówienia ze sklepu. |
| `orders.value` | number |  | Ich wartość w `currency`. |
| `orders.currency` | string |  | Waluta większości zamówień. |
| `orders.first_at` | string (date-time) \| null |  | Czas pierwszego zamówienia. |
| `orders.last_at` | string (date-time) \| null |  | Czas ostatniego zamówienia. |

```bash
curl -X PATCH "https://app.basketly.io/api/v1/contacts/abc123" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Anna Nowak",
    "phone": "+48600100200",
    "email": "anna@example.com",
    "tags": [
      "vip"
    ]
  }'
```

#### `POST /contacts/{id}/notes` — Dodaj notatkę

Dodaje notatkę, telefon lub spotkanie do historii osoby.

Uprawnienie: `contacts:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |
| `Idempotency-Key` | header |  | Unikalny klucz (1–255 widocznych znaków ASCII). Powtórzenie POST z tym samym kluczem i treścią w ciągu 24 godzin zwraca zapisaną odpowiedź z `Idempotent-Replayed: true`. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `kind` | string |  | Rodzaj wpisu. One of: `note`, `call`, `meeting`. Default `note`. |
| `text` | string | yes | Treść, 1–5000 znaków. Max 5000 characters. |
| `time` | string (date-time) |  | Kiedy to było (domyślnie teraz); nie w przyszłości. |

Odpowiedź 201:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string |  | Identyfikator wpisu historii. |
| `contact_id` | string |  | Identyfikator osoby. |
| `kind` | string |  | Rodzaj wpisu. |
| `text` | string |  | Treść. |
| `time` | string (date-time) |  | Czas wpisu. |

```bash
curl -X POST "https://app.basketly.io/api/v1/contacts/abc123/notes" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 5f0c7a2e-8d41-4b6a-9e3f-1c2d3e4f5a6b" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "call",
    "text": "Asked when order 1001 will ship."
  }'
```

#### `POST /contacts/{id}/tags` — Dodaj lub usuń tagi

Dodaje i usuwa tagi bez wysyłania całej listy.

Uprawnienie: `contacts:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |
| `Idempotency-Key` | header |  | Unikalny klucz (1–255 widocznych znaków ASCII). Powtórzenie POST z tym samym kluczem i treścią w ciągu 24 godzin zwraca zapisaną odpowiedź z `Idempotent-Replayed: true`. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `add` | string[] |  | Tagi do dodania. |
| `remove` | string[] |  | Tagi do usunięcia. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string |  | Identyfikator osoby. |
| `name` | string |  | Imię i nazwisko. |
| `phone` | string |  | Telefon w formacie E.164, pusty gdy nieznany. |
| `email` | string |  | E-mail, pusty gdy nieznany. |
| `tags` | string[] |  | Tagi. |
| `sms_optout` | boolean |  | Prawda, gdy SMS-y do tej osoby są zablokowane. |
| `first_source` | string |  | Skąd osoba przyszła po raz pierwszy („Zamówienie w sklepie”, „Ręcznie”, API…). |
| `status_id` | string \| null |  | Identyfikator statusu wewnętrznego (GET /contact-statuses), null gdy brak. Nigdy nie trafia do Mety. |
| `owner_id` | string \| null |  | Identyfikator użytkownika — opiekuna osoby. |
| `last_activity_at` | string (date-time) \| null |  | Czas ostatniej aktywności. |
| `created_at` | string (date-time) |  | Czas utworzenia. |
| `updated_at` | string (date-time) |  | Ostatnia zmiana; używaj z `updated_since`. |
| `orders` | object |  | Co osoba kupiła w sklepie. |
| `orders.count` | integer |  | Kupione, nieanulowane zamówienia ze sklepu. |
| `orders.value` | number |  | Ich wartość w `currency`. |
| `orders.currency` | string |  | Waluta większości zamówień. |
| `orders.first_at` | string (date-time) \| null |  | Czas pierwszego zamówienia. |
| `orders.last_at` | string (date-time) \| null |  | Czas ostatniego zamówienia. |

```bash
curl -X POST "https://app.basketly.io/api/v1/contacts/abc123/tags" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 5f0c7a2e-8d41-4b6a-9e3f-1c2d3e4f5a6b" \
  -H "Content-Type: application/json" \
  -d '{
    "add": [
      "vip"
    ],
    "remove": [
      "cold"
    ]
  }'
```

#### `POST /contacts/{id}/sms-optout` — Zablokuj lub dopuść SMS-y

Utrzymuje listę „nie wysyłaj” i wszystkie osoby z tym samym telefonem w zgodzie.

Uprawnienie: `contacts:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |
| `Idempotency-Key` | header |  | Unikalny klucz (1–255 widocznych znaków ASCII). Powtórzenie POST z tym samym kluczem i treścią w ciągu 24 godzin zwraca zapisaną odpowiedź z `Idempotent-Replayed: true`. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `sms_optout` | boolean | yes | true blokuje SMS-y na telefon osoby (dla wszystkich osób z tym numerem), false ponownie je dopuszcza. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string |  | Identyfikator osoby. |
| `name` | string |  | Imię i nazwisko. |
| `phone` | string |  | Telefon w formacie E.164, pusty gdy nieznany. |
| `email` | string |  | E-mail, pusty gdy nieznany. |
| `tags` | string[] |  | Tagi. |
| `sms_optout` | boolean |  | Prawda, gdy SMS-y do tej osoby są zablokowane. |
| `first_source` | string |  | Skąd osoba przyszła po raz pierwszy („Zamówienie w sklepie”, „Ręcznie”, API…). |
| `status_id` | string \| null |  | Identyfikator statusu wewnętrznego (GET /contact-statuses), null gdy brak. Nigdy nie trafia do Mety. |
| `owner_id` | string \| null |  | Identyfikator użytkownika — opiekuna osoby. |
| `last_activity_at` | string (date-time) \| null |  | Czas ostatniej aktywności. |
| `created_at` | string (date-time) |  | Czas utworzenia. |
| `updated_at` | string (date-time) |  | Ostatnia zmiana; używaj z `updated_since`. |
| `orders` | object |  | Co osoba kupiła w sklepie. |
| `orders.count` | integer |  | Kupione, nieanulowane zamówienia ze sklepu. |
| `orders.value` | number |  | Ich wartość w `currency`. |
| `orders.currency` | string |  | Waluta większości zamówień. |
| `orders.first_at` | string (date-time) \| null |  | Czas pierwszego zamówienia. |
| `orders.last_at` | string (date-time) \| null |  | Czas ostatniego zamówienia. |

```bash
curl -X POST "https://app.basketly.io/api/v1/contacts/abc123/sms-optout" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 5f0c7a2e-8d41-4b6a-9e3f-1c2d3e4f5a6b" \
  -H "Content-Type: application/json" \
  -d '{
    "sms_optout": true
  }'
```

#### `GET /contacts/search` — Szukaj osób z filtrami panelu

Lista Klienci z jej filtrami: wartości jednego filtra to alternatywy (lista po przecinku), filtry łączą się. Każda zła wartość to 400. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `contacts:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `q` | query |  | Imię, e-mail albo telefon. |
| `page` | query |  | Strona, 1 = pierwsza. |
| `per_page` | query |  | Osób na stronie: 10, 25 (domyślnie), 50 albo 100. |
| `owner` | query |  | me, any, none albo identyfikatory członków. |
| `status` | query |  | Identyfikatory statusów albo none. |
| `tag` | query |  | Tag. |
| `source` | query |  | Nazwa pierwszego źródła. |
| `buyer` | query |  | yes = ma opłacone zamówienie, no = brak. |
| `orders_min` | query |  | Co najmniej tyle opłaconych zamówień. |
| `orders_max` | query |  | Najwyżej tyle opłaconych zamówień. |
| `spent_min` | query |  | Wydał co najmniej. |
| `spent_max` | query |  | Wydał najwyżej. |
| `last_order` | query |  | Ostatni zakup w okresie. |
| `inactive_buyer` | query |  | Kupował, ale nie w ostatnich N dniach. |
| `first_order` | query |  | Pierwszy zakup w okresie. |
| `product` | query |  | Kupił produkt o tym id (można powtórzyć). |
| `created` | query |  | today, 7d, 30d albo 90d (albo dni created_from / created_to). |
| `activity` | query |  | Ostatnia aktywność: today, 7d, 30d, 90d (albo activity_from / activity_to). |
| `inactive_days` | query |  | Bez aktywności co najmniej tyle dni. |
| `tasks` | query |  | open, overdue albo none. |
| `phone` | query |  | yes albo no. |
| `email` | query |  | yes albo no. |
| `optout` | query |  | yes albo no. |
| `duplicate` | query |  | yes albo no. |
| `sort` | query |  | activity, activity_asc, created, created_asc, spent, spent_asc, orders, orders_asc, aov (średnie zamówienie), aov_asc, last_order, last_order_asc, first_order albo first_order_asc. Rosnące sortowania zakupów stawiają osoby bez zamówień na końcu. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | object[] |  | Osoby: id, imię, status, opiekun, tagi, ostatnia aktywność, dodano (created), zamówienia (liczba, wartość, pierwsze i ostatnie), shops (rodzaje źródeł opłaconych zamówień), duplicate (podejrzenie duplikatu), open_tasks i overdue_tasks (dla członków widzących zadania). |
| `per_page` | integer |  | Osób na stronie. |
| `total` | integer |  | Wszystkie pasujące osoby. |
| `page` | integer |  | Numer strony. |

```bash
curl "https://app.basketly.io/api/v1/contacts/search" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /contact-statuses` — Lista statusów

Wewnętrzne statusy organizacji. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `contacts:read`

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `statuses` | object[] |  | Statusy: id, name, archived, sort, people. |

```bash
curl "https://app.basketly.io/api/v1/contact-statuses" \
  -H "Authorization: Bearer $API_KEY"
```

#### `POST /contacts/{id}/status` — Ustaw status

Ustawia status wewnętrzny (wpis w historii). Nigdy nie trafia do Mety; uruchamiają się automatyzacje statusu. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `contacts:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |
| `Idempotency-Key` | header |  | Unikalny klucz (1–255 widocznych znaków ASCII). Powtórzenie POST z tym samym kluczem i treścią w ciągu 24 godzin zwraca zapisaną odpowiedź z `Idempotent-Replayed: true`. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `status` | string | yes | Identyfikator statusu albo "", by wyczyścić. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `status` | string |  | Identyfikator statusu ("" = brak). |
| `owner` | string |  | Identyfikator opiekuna. |

```bash
curl -X POST "https://app.basketly.io/api/v1/contacts/abc123/status" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 5f0c7a2e-8d41-4b6a-9e3f-1c2d3e4f5a6b" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "purchase"
  }'
```

#### `GET /contacts/{id}/history` — Historia osoby

Zamówienia, telefony, notatki, statusy, zadania i SMS-y osoby. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `contacts:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |
| `page` | query |  | Strona, 1 = najnowsze. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | object[] |  | Wpisy: kind, time, actor, summary, details. |
| `page` | integer |  | Numer strony (`?page=`). |
| `more` | boolean |  | Istnieje starsza strona. |

```bash
curl "https://app.basketly.io/api/v1/contacts/abc123/history" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /segments` — Lista segmentów

Zapisane segmenty klientów (filtry Klientów z nazwą) i gotowe segmenty sklepu, każdy z liczbą osób widocznych dla członka i — dla zapisanych — stanem grupy odbiorców Meta i listy Klaviyo. `filters` segmentu to parametry /contacts/search. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `contacts:read`

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/segments" \
  -H "Authorization: Bearer $API_KEY"
```

### Konwersje

Konwersje wysyłane do Meta i ich wysyłki.

#### `GET /events` — Lista zdarzeń

Konwersje z wysyłkami; treść zdarzeń nigdy nie jest zwracana.

Uprawnienie: `events:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `limit` | query |  | Rozmiar strony, 1–100 (domyślnie 25). |
| `cursor` | query |  | `next_cursor` z poprzedniej strony. |
| `order` | query |  | Sortowanie po `updated_at` (potem id): `desc` od najnowszych (domyślnie) lub `asc`. |
| `updated_since` | query |  | Tylko rekordy zmienione w tym czasie ISO 8601 lub później. |
| `source` | query |  | Identyfikator źródła. |
| `status` | query |  | Twój status. |
| `external_id` | query |  | Twój identyfikator rekordu. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | Event[] | yes |  |
| `data[].id` | string |  | Identyfikator zdarzenia. |
| `data[].source_id` | string |  | Identyfikator źródła. |
| `data[].external_id` | string |  | Twój identyfikator rekordu. |
| `data[].status` | string |  | Twój status. |
| `data[].meta_event_id` | string |  | Identyfikator deduplikacji Meta. |
| `data[].event_time` | string (date-time) \| null |  | Czas zdarzenia. |
| `data[].value` | number \| null |  | Wartość. |
| `data[].currency` | string |  | Waluta. |
| `data[].deliveries` | Delivery[] |  | Wysyłki tego zdarzenia. |
| `data[].created_at` | string (date-time) |  | Czas utworzenia. |
| `data[].updated_at` | string (date-time) |  | Ostatnia zmiana. |
| `next_cursor` | string \| null | yes | Przekaż jako `cursor`, by pobrać następną stronę; null na ostatniej stronie. |
| `has_more` | boolean | yes | Prawda, gdy istnieje kolejna strona. |

```bash
curl "https://app.basketly.io/api/v1/events" \
  -H "Authorization: Bearer $API_KEY"
```

#### `POST /events` — Wyślij konwersję

Przyjmowanie konwersji serwer–serwer. Aktywne przepływy źródła mapują `status` na zdarzenia Meta i dodają jedną wysyłkę na dataset; zasady deduplikacji bez zmian. Odpowiedź 202: w kolejce, jeszcze nie wysłane.

Uprawnienie: `events:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `Idempotency-Key` | header |  | Unikalny klucz (1–255 widocznych znaków ASCII). Powtórzenie POST z tym samym kluczem i treścią w ciągu 24 godzin zwraca zapisaną odpowiedź z `Idempotent-Replayed: true`. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `source` | string | yes | Identyfikator źródła Arkusz Google lub sklepu Twojej organizacji (Połączenia → szczegóły źródła). |
| `external_id` | string | yes | Twój identyfikator rekordu (numer zamówienia…), do 128 znaków. Max 128 characters. |
| `status` | string | yes | Twój status zamapowany na zdarzenie Meta przez przepływ źródła, np. QUALIFIED albo purchase. Max 80 characters. |
| `consent` | boolean | yes | Musi być true: potwierdzasz podstawę prawną przesłania danych. |
| `event_time` | integer \| string (date-time) |  | Sekundy Unix lub ISO 8601; najwyżej 7 dni wstecz. |
| `event_id` | string |  | Identyfikator deduplikacji Meta; bez niego powstaje z source + external_id + status. |
| `email` | string |  | E-mail klienta (haszowany SHA-256 przed zapisem). |
| `phone` | string |  | Telefon klienta w E.164 (haszowany). |
| `lead_id` | string |  | Identyfikator leada Meta klienta, jeśli go masz; trafia do Mety jako dane klienta. |
| `user_id` | string |  | Twój identyfikator klienta (haszowany jako external_id). |
| `fbp` | string |  | Ciasteczko _fbp. |
| `fbc` | string |  | Ciasteczko _fbc. |
| `event_source_url` | string |  | Adres strony konwersji. |
| `tracking_id` | string |  | Identyfikator śledzenia kolektora (źródła sklepów). |
| `client_ip_address` | string |  | IP klienta. |
| `client_user_agent` | string |  | User agent klienta. |
| `value` | number |  | Wartość konwersji (wymagana dla Purchase). |
| `currency` | string |  | Waluta ISO 4217 (wymagana z wartością). |
| `content_ids` | string[] |  | Identyfikatory produktów. |

Odpowiedź 202:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `event_id` | string |  | Identyfikator zdarzenia w Basketly. |
| `deliveries_created` | integer |  | Nowe wysyłki dodane do kolejki dla datasetów Meta. |
| `duplicates` | integer |  | Wysyłki pominięte jako duplikaty. |
| `sending_enabled` | boolean |  | false, gdy serwer nie wysyła do Meta (tylko kolejka). |

```bash
curl -X POST "https://app.basketly.io/api/v1/events" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 5f0c7a2e-8d41-4b6a-9e3f-1c2d3e4f5a6b" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "src_abc123",
    "external_id": "order-1001",
    "status": "purchase",
    "consent": true,
    "event_time": "2026-10-08T10:00:00Z",
    "email": "anna@example.com",
    "phone": "+48600100200",
    "value": 1200,
    "currency": "PLN"
  }'
```

#### `GET /events/{id}` — Pobierz zdarzenie

Jedna konwersja ze statusem każdej wysyłki.

Uprawnienie: `events:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string |  | Identyfikator zdarzenia. |
| `source_id` | string |  | Identyfikator źródła. |
| `external_id` | string |  | Twój identyfikator rekordu. |
| `status` | string |  | Twój status. |
| `meta_event_id` | string |  | Identyfikator deduplikacji Meta. |
| `event_time` | string (date-time) \| null |  | Czas zdarzenia. |
| `value` | number \| null |  | Wartość. |
| `currency` | string |  | Waluta. |
| `deliveries` | Delivery[] |  | Wysyłki tego zdarzenia. |
| `deliveries[].id` | string |  | Identyfikator wysyłki. |
| `deliveries[].event_id` | string |  | Identyfikator zdarzenia w Basketly. |
| `deliveries[].target_id` | string |  | Identyfikator odbiorcy Meta (połączenia z datasetem). |
| `deliveries[].dataset_id` | string |  | Identyfikator datasetu (piksela) Meta. |
| `deliveries[].event_name` | string |  | Nazwa zdarzenia Meta, np. Purchase, AddToCart. |
| `deliveries[].meta_event_id` | string |  | event_id wysłane do Meta. |
| `deliveries[].mode` | string |  | `test` albo `live`. |
| `deliveries[].status` | string |  | Status wysyłki. One of: `pending`, `sending`, `retry`, `accepted`, `failed`, `cancelled`. |
| `deliveries[].attempts` | integer |  | Dotychczasowe próby. |
| `deliveries[].last_error` | string |  | Ostatni błąd, pusty gdy brak. |
| `deliveries[].accepted_at` | string (date-time) \| null |  | Kiedy Meta przyjęła wysyłkę (odbiór, nie dowód atrybucji). |
| `deliveries[].next_attempt_at` | string (date-time) \| null |  | Następna próba. |
| `deliveries[].created_at` | string (date-time) |  | Czas utworzenia. |
| `deliveries[].updated_at` | string (date-time) |  | Ostatnia zmiana. |
| `created_at` | string (date-time) |  | Czas utworzenia. |
| `updated_at` | string (date-time) |  | Ostatnia zmiana. |

```bash
curl "https://app.basketly.io/api/v1/events/abc123" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /deliveries` — Lista wysyłek

Status wysyłek do Meta, np. `status=failed&updated_since=…`, by śledzić błędy.

Uprawnienie: `events:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `limit` | query |  | Rozmiar strony, 1–100 (domyślnie 25). |
| `cursor` | query |  | `next_cursor` z poprzedniej strony. |
| `order` | query |  | Sortowanie po `updated_at` (potem id): `desc` od najnowszych (domyślnie) lub `asc`. |
| `updated_since` | query |  | Tylko rekordy zmienione w tym czasie ISO 8601 lub później. |
| `status` | query |  | Status wysyłki. |
| `event` | query |  | Identyfikator zdarzenia. |
| `target` | query |  | Identyfikator odbiorcy. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | Delivery[] | yes |  |
| `data[].id` | string |  | Identyfikator wysyłki. |
| `data[].event_id` | string |  | Identyfikator zdarzenia w Basketly. |
| `data[].target_id` | string |  | Identyfikator odbiorcy Meta (połączenia z datasetem). |
| `data[].dataset_id` | string |  | Identyfikator datasetu (piksela) Meta. |
| `data[].event_name` | string |  | Nazwa zdarzenia Meta, np. Purchase, AddToCart. |
| `data[].meta_event_id` | string |  | event_id wysłane do Meta. |
| `data[].mode` | string |  | `test` albo `live`. |
| `data[].status` | string |  | Status wysyłki. One of: `pending`, `sending`, `retry`, `accepted`, `failed`, `cancelled`. |
| `data[].attempts` | integer |  | Dotychczasowe próby. |
| `data[].last_error` | string |  | Ostatni błąd, pusty gdy brak. |
| `data[].accepted_at` | string (date-time) \| null |  | Kiedy Meta przyjęła wysyłkę (odbiór, nie dowód atrybucji). |
| `data[].next_attempt_at` | string (date-time) \| null |  | Następna próba. |
| `data[].created_at` | string (date-time) |  | Czas utworzenia. |
| `data[].updated_at` | string (date-time) |  | Ostatnia zmiana. |
| `next_cursor` | string \| null | yes | Przekaż jako `cursor`, by pobrać następną stronę; null na ostatniej stronie. |
| `has_more` | boolean | yes | Prawda, gdy istnieje kolejna strona. |

```bash
curl "https://app.basketly.io/api/v1/deliveries" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /conversions/balance` — Bilans zakupów

Dzień po dniu: opłacone zamówienia sklepu, zakupy przyjęte przez Basketly i na każde miejsce docelowe (odbiorca Meta, GA4, TikTok, Google Ads, Microsoft Ads, Pinterest, Klaviyo — do Klaviyo tylko zakupy z e-mailem) zakupy przyjęte, z błędem i czekające, z najczęstszymi powodami błędów. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `events:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `days` | query |  | Okres: 7, 14, 30 (domyślnie) albo 90 dni. |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/conversions/balance" \
  -H "Authorization: Bearer $API_KEY"
```

### Zadania

Zadania.

#### `GET /tasks` — Lista zadań

Otwarte zadania wg terminu. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `tasks:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `scope` | query |  | Czyja praca: mine, user (z user) albo all. |
| `user` | query |  | Identyfikator członka dla scope=user. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | object[] |  | Zadania wg terminu. |
| `counts` | object |  | Liczby na sekcję (po terminie, dziś, później, bez terminu). |
| `truncated` | boolean |  | Ponad 500 otwartych zadań. |

```bash
curl "https://app.basketly.io/api/v1/tasks" \
  -H "Authorization: Bearer $API_KEY"
```

#### `POST /contacts/{id}/tasks` — Dodaj zadanie

Dodaje zadanie osobie, jak „Dodaj zadanie” na karcie. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `tasks:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |
| `Idempotency-Key` | header |  | Unikalny klucz (1–255 widocznych znaków ASCII). Powtórzenie POST z tym samym kluczem i treścią w ciągu 24 godzin zwraca zapisaną odpowiedź z `Idempotent-Replayed: true`. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `title` | string | yes | Tytuł zadania. Max 200 characters. |
| `note` | string |  | Notatka. |
| `due_date` | string |  | Dzień terminu RRRR-MM-DD (strefa organizacji); bez niego bez terminu. |
| `due_time` | string |  | Godzina HH:MM; bez niej koniec dnia. |
| `assignee` | string |  | Identyfikator członka (domyślnie opiekun osoby albo osoba klucza). |

Odpowiedź 201:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Identyfikator nowego rekordu. |

```bash
curl -X POST "https://app.basketly.io/api/v1/contacts/abc123/tasks" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 5f0c7a2e-8d41-4b6a-9e3f-1c2d3e4f5a6b" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "example"
  }'
```

#### `POST /tasks/{id}/done` — Oznacz zadanie jako zrobione

Zamyka zadanie, opcjonalnie z wpisem w historii. Wyślij `{}`, jeśli bez wpisu. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `tasks:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |
| `Idempotency-Key` | header |  | Unikalny klucz (1–255 widocznych znaków ASCII). Powtórzenie POST z tym samym kluczem i treścią w ciągu 24 godzin zwraca zapisaną odpowiedź z `Idempotent-Replayed: true`. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `entry` | object |  | Opcjonalny wpis w historii zapisany razem z zadaniem (np. rozmowa, która je zamknęła). |
| `entry.kind` | string | yes | Rodzaj wpisu. One of: `note`, `call`, `meeting`. |
| `entry.text` | string | yes | Treść. |

Odpowiedź 204.

```bash
curl -X POST "https://app.basketly.io/api/v1/tasks/abc123/done" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 5f0c7a2e-8d41-4b6a-9e3f-1c2d3e4f5a6b" \
  -H "Content-Type: application/json" \
  -d '{
    "entry": {
      "kind": "call",
      "text": "Asked when order 1001 will ship."
    }
  }'
```

#### `POST /tasks/{id}/postpone` — Przełóż zadanie

Przesuwa termin o dni albo na datę. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `tasks:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |
| `Idempotency-Key` | header |  | Unikalny klucz (1–255 widocznych znaków ASCII). Powtórzenie POST z tym samym kluczem i treścią w ciągu 24 godzin zwraca zapisaną odpowiedź z `Idempotent-Replayed: true`. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `days` | integer |  | Przesuń o 1–30 dni. Range 1–30. |
| `due_date` | string |  | Albo nowy dzień RRRR-MM-DD. |
| `due_time` | string |  | Z due_date: godzina HH:MM. |

Odpowiedź 204.

```bash
curl -X POST "https://app.basketly.io/api/v1/tasks/abc123/postpone" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 5f0c7a2e-8d41-4b6a-9e3f-1c2d3e4f5a6b" \
  -H "Content-Type: application/json" \
  -d '{
    "days": 1,
    "due_date": "example",
    "due_time": "example"
  }'
```

### Integracje

Gdzie sklep sprzedaje, reklamuje się i wysyła katalog.

#### `GET /integrations` — Lista integracji

Wszystkie miejsca, z którymi łączy się sklep, według kategorii, ze stanem; łączenie zostaje w panelu. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `integrations:read`

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/integrations" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /getting-started` — Pierwsze kroki

Lista pierwszych kroków organizacji: podłączony sklep, odbiorca Meta, działający przepływ do Meta, feed produktowy, odczytane konto reklamowe Meta, inne platformy (opcjonalnie) — każdy ze stanem liczonym z danych organizacji i adresem w panelu, w którym się go wykonuje. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `integrations:read`

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/getting-started" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /map` — Mapa połączeń

Cała konfiguracja sklepu tylko do odczytu: skąd przychodzą dane (źródła sklepu, feedy, logowania), co robi z nimi Basketly (zamówienia, produkty, konwersje, segmenty, automatyzacje, szablony) i dokąd trafiają (odbiorcy Meta, GA4/TikTok/Pinterest/Klaviyo, grupy odbiorców Meta i listy Klaviyo, formaty feedów, API, agenci), ze stanem każdego elementu i połączenia. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `integrations:read`

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/map" \
  -H "Authorization: Bearer $API_KEY"
```

### Zamówienia

Zamówienia ze sklepu z pozycjami i kupującymi.

#### `GET /orders` — Lista zamówień

Zamówienia ze sklepu z okresu (dni w strefie organizacji, domyślnie ostatnie 30), domyślnie kupione, z filtrami i sortowaniem listy Zamówienia. Kilka wartości jednego filtra = dowolna z nich, filtry łączą się; błędna wartość to 400. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `orders:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `from` | query |  | Pierwszy dzień RRRR-MM-DD. |
| `to` | query |  | Ostatni dzień RRRR-MM-DD (zakres do 366 dni). |
| `status` | query |  | Które zamówienia: bought (domyślnie), unpaid, cancelled albo all; lista po przecinku = dowolny z nich. |
| `contact` | query |  | Zamówienia tej osoby (wszystkie daty, jeśli nie podano from/to). |
| `q` | query |  | Fragment numeru zamówienia albo pełny e-mail lub telefon kupującego (dopasowanie po skrócie). |
| `source` | query |  | Lista identyfikatorów źródeł sklepu po przecinku (facets.sources). |
| `shop_status` | query |  | Nazwa statusu w sklepie; powtórz dla kilku (facets.shop_statuses). |
| `status_type` | query |  | Lista po przecinku: new, open, closed, cancelled. |
| `paid` | query |  | Znacznik opłacenia ze sklepu. |
| `total_min` | query |  | Wartość zamówienia co najmniej (w walucie zamówienia). |
| `total_max` | query |  | Wartość zamówienia najwyżej. |
| `platform` | query |  | Lista platform źródła reklamy po przecinku: meta, google_ads, microsoft_ads, tiktok, pinterest, openai_ads, email, comparison, other, direct, unknown (unknown obejmuje zamówienia bez źródła). |
| `campaign` | query |  | Klucz kampanii (identyfikator kampanii albo utm:<utm_campaign> — klucze z ad_facets); powtórz dla kilku. |
| `ad_source` | query |  | yes = znana platforma reklamy, no = wejście bezpośrednie albo nieznane. |
| `meta_send` | query |  | Lista po przecinku: accepted, failed, waiting, none — co stało się z zakupem wysłanym do Mety. |
| `product` | query |  | Pozycja, której nazwa zawiera ten tekst albo której kod, EAN lub identyfikator z katalogu jest mu równy. |
| `country` | query |  | Lista kodów kraju dostawy po przecinku. |
| `sort` | query |  | Kolejność listy. |
| `per_page` | query |  | Zamówień na stronę. |
| `page` | query |  | Strona, 1 = pierwsza. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `orders` | object[] |  | Zamówienia: id, number, placed_at, status, status_type (new, open, closed, cancelled), paid, bought, cancelled, currency, total, shipping, items, city, buyer {id, name}, source_name, source_kind (shoper, idosell, baselinker, woocommerce, shopify…), lines (liczba pozycji), first_product {name, variant, content_id, quantity, product (id rekordu albo puste), image}, sends ([{destination meta \| ga4 \| tiktok \| pinterest \| klaviyo \| openai_ads \| google_ads \| microsoft_ads, status accepted \| failed \| waiting \| skipped}] — najgorszy wynik na miejsce docelowe), ad_source. `ad_source` = odczytanie Basketly (ostatnie kliknięcie reklamy w wizytach kupującego), nigdy atrybucja platformy: platform (meta \| google_ads \| microsoft_ads \| tiktok \| pinterest \| openai_ads \| email \| comparison \| other \| direct \| unknown), method (click_id \| utm \| ids \| feed \| referrer \| none), link (server = tracking_id w zamówieniu, browser = linkOrder na stronie podziękowania, shop = dane sklepu o wejściu, none), deduced, campaign / adset / ad {id, name}, resolved (ids \| names \| empty), utm {source, medium, campaign, content, term, id}, click_at, clicks (identyfikatory kliknięć z wizyty: fbc, gclid, gbraid, wbraid, msclkid, ttclid, ttp, epik, oppref, obref), referrer (host), landing (ścieżka), first_touch, feed {id, name, channel, product} (feed i produkt katalogu z otagowanego linku, z którego przyszła wizyta). |
| `ad_facets` | object |  | Zamówienia wyboru (przed filtrami platformy / kampanii) na platformę reklamy (`platforms`: platform, orders) i na kampanię (`campaigns`: platform, key, name, orders; najwyżej 100). |
| `facets` | object |  | Zamówienia okresu (wszystkie stany, przed pozostałymi filtrami) na sklep (`sources`: id, name, kind, orders), status sklepu (`shop_statuses`: status, status_type, orders) i kraj dostawy (`countries`: country, orders); najwyżej 100 każdego. |
| `page_size` | integer |  | Zamówień na stronę (per_page). |
| `total` | integer |  | Wszystkie pasujące zamówienia. |
| `summary` | object |  | Kupione zamówienia, nieopłacone, anulowane, sztuki, przychód na walutę, średni koszyk, kupujący, nowi i powracający. |

```bash
curl "https://app.basketly.io/api/v1/orders" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /orders/{id}` — Pobierz zamówienie

Jedno zamówienie z pozycjami, kupującym, źródłem reklamy i miejscami, do których poszedł zakup. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `orders:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/orders/abc123" \
  -H "Authorization: Bearer $API_KEY"
```

### Produkty

Produkty z diagnozą i ręcznymi poprawkami.

#### `GET /products` — Lista produktów

Aktywne produkty organizacji albo jednego feedu, po 50 na stronę (albo per_page), z diagnozą, pochodzeniem, kategorią Google, poprawkami, wynikami okresu i trendem sprzedaży. Wartości jednego filtra łączy LUB, filtry — I. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `products:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `feed` | query |  | Tylko produkty tego feedu. |
| `q` | query |  | Tytuł, SKU, dokładny EAN albo dokładny identyfikator katalogowy. |
| `origin` | query |  | Lista (po przecinku) identyfikatorów źródeł sklepów albo feedów, z których pochodzą produkty (facets.origins). |
| `category` | query |  | Kategoria sklepu, dokładnie; powtórz dla kilku (facets.categories). |
| `brand` | query |  | Marka, dokładnie; powtórz dla kilku (facets.brands). |
| `availability` | query |  | Lista (po przecinku): in_stock, out_of_stock, preorder, backorder, unknown. |
| `price_min` | query |  | Najniższa cena dla kupującego (cena promocyjna, jeśli jest). |
| `price_max` | query |  | Najwyższa cena dla kupującego. |
| `score_min` | query |  | Najniższa ocena jakości 0–100. |
| `score_max` | query |  | Najwyższa ocena jakości 0–100. |
| `issue` | query |  | Lista kodów problemów (po przecinku); pasuje dowolny. |
| `status` | query |  | Lista najgorszych problemów (po przecinku): error, warning, clean. |
| `fixes` | query |  | Lista (po przecinku): auto (automatyczne poprawki Basketly naprawiają problem), manual (ręczna poprawka), none. |
| `google` | query |  | Lista (po przecinku): set (wychodzi z feedów z kategorią Google: wybraną przez człowieka, własną albo pewną automatyczną), review (automatyczna, pewność poniżej 60), none (Basketly dobiera ją po nazwie przy budowie feedu). |
| `exposure` | query |  | Lista (po przecinku): none = bez emisji w reklamach katalogowych w okresie, low = poniżej 100 wyświetleń (razem z bez emisji), shown = pokazywane. |
| `sales` | query |  | yes = kupowany w okresie, no = niekupowany. |
| `sort` | query |  | title (domyślnie), title_desc, score (najgorsze najpierw), score_desc, price, stock, issues, synced albo z okresu impressions, clicks, ctr, units, revenue, views — od największych; `_asc` = od najmniejszych. |
| `days` | query |  | Okres statystyk: 7, 14, 30 (domyślnie) albo 90 dni. |
| `per_page` | query |  | Produktów na stronę: 10, 25, 50 (domyślnie) albo 100. |
| `facets` | query |  | 1 dodaje `facets`: pochodzenia, kategorie, marki, dostępność, google i poprawki z liczbą produktów. |
| `page` | query |  | Strona, 1 = pierwsza. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `products` | object[] |  | Produkty: id, external_id, tytuł, cena, cena promocyjna, dostępność, stan, ocena 0–100, najgorszy problem, problemy, errors, warnings, origin_name, origin_kind, google {id, path, by person\|own\|auto\|title\|none, confidence, review — wybór automatyczny albo po nazwie poniżej 60 do sprawdzenia}, fixes {auto, manual}, statystyki okresu, trend (kupione sztuki dziennie, tygodniowo powyżej 30 dni). |
| `total` | integer |  | Wszystkie pasujące produkty. |
| `summary` | object |  | Liczby wyboru: błędy, ostrzeżenia, średnia ocena, problemy wg kodu. |
| `per_page` | integer |  | Produktów na stronę. |
| `facets` | object |  | Przy facets=1: pochodzenia (id, name, kind, products), kategorie i marki (name, products; 500 najliczniejszych) oraz liczby dostępności, google (set, review, none) i poprawek (auto, manual) całego wyboru. |

```bash
curl "https://app.basketly.io/api/v1/products" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /products/performance` — Wyniki katalogu

Produkty bez emisji, ze słabą emisją i pokazywane w reklamach katalogowych, najlepiej sprzedające się i najczęściej pokazywane bez sprzedaży. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `products:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `days` | query |  | Okres statystyk: 7, 14, 30 (domyślnie) albo 90 dni. |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/products/performance" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /products/{product}/stats` — Wyniki produktu

Jeden produkt w reklamach (rozbicie Mety na produkty), w sklepie (skrypt Basketly) i w sprzedaży (zamówienia), dzień po dniu. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `products:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `product` | path | yes | Identyfikator produktu. |
| `days` | query |  | Okres statystyk: 7, 14, 30 (domyślnie) albo 90 dni. |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/products/abc123/stats" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /products/{product}` — Pobierz produkt

Kartoteka produktu z panelu. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `products:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `product` | path | yes | Identyfikator produktu. |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/products/abc123" \
  -H "Authorization: Bearer $API_KEY"
```

#### `PUT /products/{product}/overrides` — Ustaw ręczne poprawki

Wartości wpisane ręcznie wygrywają z poprawkami, regułami i przepływami w każdym feedzie. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `products:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `product` | path | yes | Identyfikator produktu. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `overrides` | object | yes | Atrybut → wartość wpisana ręcznie; wygrywa w każdym feedzie i zostaje po kolejnym pobraniu. "" usuwa atrybut. Zastępuje wszystkie ręczne poprawki (wyślij te, które mają zostać). `id` nie można zmienić. |

Odpowiedź 200:


```bash
curl -X PUT "https://app.basketly.io/api/v1/products/abc123/overrides" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "overrides": {}
  }'
```

#### `GET /guidelines` — Lista wytycznych

Zasady dostawców dotyczące danych produktów ze źródłami i liczbą produktów, które łamią każdą. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `products:read`

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/guidelines" \
  -H "Authorization: Bearer $API_KEY"
```

#### `POST /products/{product}/title-suggestions` — Zaproponuj tytuły

Prosi AI (MiniMax) o 1–3 lepsze tytuły zgodne z wytycznymi tytułów; wysyłane są tylko atrybuty produktu, każdą propozycję sprawdzają ponownie sprawdzenia tytułu Basketly. Nic nie jest zapisywane. Zużywa dzienny budżet AI organizacji (429 po jego wyczerpaniu, także ponad 30 wywołań na godzinę). Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `products:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `product` | path | yes | Identyfikator produktu. |

Odpowiedź 200:


```bash
curl -X POST "https://app.basketly.io/api/v1/products/abc123/title-suggestions" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /meta-catalog/status` — Status produktów w katalogu

Dla najwyżej 500 identyfikatorów produktów: status weryfikacji, dostępność, widoczność i przynależność do zestawów produktów w wybranych katalogach Meta, z powodem z katalogu, dla którego produkt nie ma emisji. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `products:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `content_ids` | query | yes | Lista identyfikatorów produktów (retailer_id w katalogu) po przecinku, najwyżej 500. |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/meta-catalog/status" \
  -H "Authorization: Bearer $API_KEY"
```

### Feedy

Feedy produktowe, ich reguły, przepływy i kategorie Google.

#### `POST /products/unseen-flow` — Feed z produktów bez emisji

Tworzy przepływ feedu z produktów, które reklamy katalogowe pokazały mniej niż `below` razy w ostatnich `days` dniach. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `feeds:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `Idempotency-Key` | header |  | Unikalny klucz (1–255 widocznych znaków ASCII). Powtórzenie POST z tym samym kluczem i treścią w ciągu 24 godzin zwraca zapisaną odpowiedź z `Idempotent-Replayed: true`. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `days` | integer | yes | Okres: 7, 14, 30 albo 90 dni. One of: `7`, `14`, `30`, `90`. |
| `below` | number | yes | Liczba wyświetleń, poniżej której produkt ma słabą emisję (≥ 1). |

Odpowiedź 200:


```bash
curl -X POST "https://app.basketly.io/api/v1/products/unseen-flow" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 5f0c7a2e-8d41-4b6a-9e3f-1c2d3e4f5a6b" \
  -H "Content-Type: application/json" \
  -d '{
    "days": 7,
    "below": 1
  }'
```

#### `GET /feeds` — Lista feedów

Wszystkie feedy ze stanem i adresem wyjścia. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `feeds:read`

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `feeds` | object[] |  | Feedy jak w GET /feeds/{id}, bez listy pól, z `category_coverage` {covered, products}: produkty, które wychodzą z feedu z kategorią Google (jak w zakładce Kategorie Google), null, gdy nie da się policzyć. |

```bash
curl "https://app.basketly.io/api/v1/feeds" \
  -H "Authorization: Bearer $API_KEY"
```

#### `POST /feeds` — Dodaj feed

Jak „Dodaj feed”: z adresu (pobieranie w kolejce), ze sklepu, z innych feedów albo jako przepływ (startuje z wybranymi feedami połączonymi w wyjściu albo z całym płótnem). Wgrywanie pliku zostaje w panelu. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `feeds:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `Idempotency-Key` | header |  | Unikalny klucz (1–255 widocznych znaków ASCII). Powtórzenie POST z tym samym kluczem i treścią w ciągu 24 godzin zwraca zapisaną odpowiedź z `Idempotent-Replayed: true`. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | yes | 2–120 znaków. |
| `input` | string | yes | Skąd brać produkty. One of: `url`, `source`, `feeds`, `flow`. |
| `url` | string |  | input=url: adres feedu (http/https). |
| `source` | string |  | input=source: identyfikator podłączonego sklepu. |
| `refresh_hours` | integer |  | input=url: pobieranie co 0 (nigdy), 1, 3, 6, 12 albo 24 godziny. One of: `0`, `1`, `3`, `6`, `12`, `24`. |
| `parents` | string[] |  | input=feeds albo flow: 1–10 feedów z własnymi produktami, w kolejności ważności. |
| `graph` | object |  | input=flow: całe płótno (jak w PUT /feeds/{id}/flow) zamiast parents. |
| `output` | string |  | Format wyjścia: "" Google/Meta XML, ceneo, csv, openai (CSV dla ChatGPT Ads). One of: ``, `ceneo`, `csv`, `openai`. |

Odpowiedź 200:


```bash
curl -X POST "https://app.basketly.io/api/v1/feeds" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 5f0c7a2e-8d41-4b6a-9e3f-1c2d3e4f5a6b" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Anna Nowak",
    "input": "url",
    "url": "https://example.com/webhooks"
  }'
```

#### `GET /feeds/{id}` — Pobierz feed

Feed z atrybutami; przepływ z płótnem. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `feeds:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/feeds/abc123" \
  -H "Authorization: Bearer $API_KEY"
```

#### `PATCH /feeds/{id}` — Zmień feed

Nazwa, adres, częstotliwość pobierania, automatyczne poprawki, źródła, format wyjścia, tagowanie linków (pominięte pola link_tagging zostają). Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `feeds:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string |  | 2–120 znaków. |
| `url` | string |  | Tylko input=url. |
| `refresh_hours` | integer |  | Tylko input=url. One of: `0`, `1`, `3`, `6`, `12`, `24`. |
| `autofix` | boolean |  | Automatyczne poprawki włączone lub wyłączone (feedy z regułami). |
| `parents` | string[] |  | Tylko input=feeds. |
| `output` | string |  | Format wyjścia (bloczek wyjścia przepływu się dostosuje). One of: ``, `ceneo`, `csv`, `openai`. |
| `link_tagging` | LinkTagging |  |  |
| `link_tagging.enabled` | boolean |  | Tagowanie linków produktów w feedzie wyjściowym (ostatni krok przed podaniem). Nowe feedy startują włączone; feedy sprzed tego ustawienia zostają wyłączone, dopóki ktoś nie włączy. |
| `link_tagging.preset` | string |  | marker = tylko znacznik Basketly bk_f/bk_pid; meta, google, ceneo = parametry kanału; custom = własne. Zestaw bez `params` bierze swoje parametry. One of: `marker`, `meta`, `google`, `ceneo`, `custom`. |
| `link_tagging.params` | string |  | Szablon parametrów dopisywanych do linków, np. utm_source=facebook&utm_content={id}. Znaczniki {id}, {item_group_id}, {brand}, {product_type}, {feed} (nazwa feedu jako slug) wypełnia produkt (zakodowane w URL); każde inne {…} albo {{…}} to makro kanału (ValueTrack Google, Meta) podawane bez zmian. bk_f/bk_pid są zarezerwowane. Parametrów, które link już ma, nigdy nie zastępujemy. |
| `link_tagging.ads_redirect` | boolean |  | Google: parametry trafiają tylko do ads_redirect (kliknięcia reklam produktowych; z linku, gdy produkt go nie ma), link i mobile_link dostają tylko znacznik. Pomijane dla Ceneo. |

Odpowiedź 200:


```bash
curl -X PATCH "https://app.basketly.io/api/v1/feeds/abc123" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Anna Nowak",
    "url": "https://example.com/webhooks"
  }'
```

#### `POST /feeds/{id}/link-tagging/preview` — Podgląd tagowania linków

Taguje linki jednego produktu z feedu szkicem ustawienia (pominięte pola = zapisane). Nic nie jest zapisywane. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `feeds:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `enabled` | boolean |  | Tagowanie linków produktów w feedzie wyjściowym (ostatni krok przed podaniem). Nowe feedy startują włączone; feedy sprzed tego ustawienia zostają wyłączone, dopóki ktoś nie włączy. |
| `preset` | string |  | marker = tylko znacznik Basketly bk_f/bk_pid; meta, google, ceneo = parametry kanału; custom = własne. Zestaw bez `params` bierze swoje parametry. One of: `marker`, `meta`, `google`, `ceneo`, `custom`. |
| `params` | string |  | Szablon parametrów dopisywanych do linków, np. utm_source=facebook&utm_content={id}. Znaczniki {id}, {item_group_id}, {brand}, {product_type}, {feed} (nazwa feedu jako slug) wypełnia produkt (zakodowane w URL); każde inne {…} albo {{…}} to makro kanału (ValueTrack Google, Meta) podawane bez zmian. bk_f/bk_pid są zarezerwowane. Parametrów, które link już ma, nigdy nie zastępujemy. |
| `ads_redirect` | boolean |  | Google: parametry trafiają tylko do ads_redirect (kliknięcia reklam produktowych; z linku, gdy produkt go nie ma), link i mobile_link dostają tylko znacznik. Pomijane dla Ceneo. |

Odpowiedź 200:


```bash
curl -X POST "https://app.basketly.io/api/v1/feeds/abc123/link-tagging/preview" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "preset": "marker",
    "params": "example",
    "ads_redirect": true
  }'
```

#### `PUT /feeds/{id}/rules` — Zapisz reguły feedu

Zastępuje reguły feedu (nie przepływu); feed wyjściowy używa ich od razu. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `feeds:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `rules` | object[] | yes | Reguły w kolejności: id, name, off, match (""/all/any), when [{field, op, value, to}], then i else [{type, field, value, with, from}]. Zastępuje wszystkie reguły. Odrzucane dla przepływów. |

Odpowiedź 200:


```bash
curl -X PUT "https://app.basketly.io/api/v1/feeds/abc123/rules" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "rules": [
      {}
    ]
  }'
```

#### `PUT /feeds/{id}/flow` — Zapisz przepływ feedu

Zastępuje płótno przepływu; feed wyjściowy i jego format zmieniają się od razu. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `feeds:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `graph` | object | yes | Płótno {nodes: [{id, type, label, config, x, y}], edges: [{from, port, to}]}. Typy bloczków: input {feed, processed}, filter i branch {match, when}, change {actions}, autofix, categories, output {format}. Wyjścia: out; branch yes/no. Dokładnie jedno wyjście; każdy bloczek na drodze od wejścia do wyjścia; bez pętli; każdy feed raz. Odrzucone płótno zwraca 400 z `node_id`. |

Odpowiedź 200:


```bash
curl -X PUT "https://app.basketly.io/api/v1/feeds/abc123/flow" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "graph": {}
  }'
```

#### `POST /feeds/{id}/preview` — Podgląd feedu

Uruchamia niezapisane reguły albo niezapisane płótno na wszystkich produktach i zwraca, co by się zmieniło. Nic nie jest zapisywane. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `feeds:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `rules` | object[] |  | Reguły do sprawdzenia (feedy z regułami). |
| `graph` | object |  | Płótno do sprawdzenia (przepływy). |

Odpowiedź 200:


```bash
curl -X POST "https://app.basketly.io/api/v1/feeds/abc123/preview" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "rules": [
      {}
    ],
    "graph": {}
  }'
```

#### `POST /feeds/{id}/fetch` — Pobierz teraz

Dodaje pobranie feedu do kolejki (feedy z feedów i przepływy: ich wejść). Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `feeds:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |
| `Idempotency-Key` | header |  | Unikalny klucz (1–255 widocznych znaków ASCII). Powtórzenie POST z tym samym kluczem i treścią w ciągu 24 godzin zwraca zapisaną odpowiedź z `Idempotent-Replayed: true`. |

Odpowiedź 204.

```bash
curl -X POST "https://app.basketly.io/api/v1/feeds/abc123/fetch" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 5f0c7a2e-8d41-4b6a-9e3f-1c2d3e4f5a6b"
```

#### `GET /feeds/{id}/categories` — Kategorie feedu

Kategorie sklepu z kategorią Google, którą dostaje każda — wybraną przez człowieka, automatycznie z pewnością albo po nazwie każdego produktu — i podpowiedziami. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `feeds:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/feeds/abc123/categories" \
  -H "Authorization: Bearer $API_KEY"
```

#### `PUT /feeds/{id}/categories` — Zapisz kategorie feedu

Zapisuje kategorię Google i własną nazwę dla każdej kategorii sklepu; pozycje bez auto są wyborem człowieka i wygrywają z automatycznym. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `feeds:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `categories` | object[] | yes | [{name, google, own, auto, confidence, broad}] — kategoria sklepu, numer kategorii Google (0 = brak), własna nazwa ("" = bez zmian); bez `auto` kategoria Google jest wyborem człowieka i nigdy nie zmienia się sama; z `auto: true` Basketly może ją przeliczyć. Zastępuje całą listę. |

Odpowiedź 200:


```bash
curl -X PUT "https://app.basketly.io/api/v1/feeds/abc123/categories" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "categories": [
      {}
    ]
  }'
```

#### `PATCH /feeds/{id}/categories` — Zmień wybrane kategorie feedu

Zmienia tylko podane wiersze: kategoria Google człowieka, zatwierdzenie automatycznej, powrót do automatycznej, własna nazwa. Odpowiada zmienionymi wierszami i podsumowaniem wszystkich. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `feeds:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `changes` | object[] | yes | 1–500 wierszy do zmiany: [{name, google?, confirm?, restore?, own?}] — `name` = kategoria sklepu jak w GET; jedno z: `google` (numer kategorii Google: wybór człowieka, od teraz wygrywa), `confirm: true` (bieżący wybór automatyczny staje się wyborem człowieka) albo `restore: true` (powrót do automatycznego wyboru Basketly, wybranego od nowa), i/lub `own` (własna nazwa, "" usuwa). Pozostałe wiersze zostają bez zmian. |

Odpowiedź 200:


```bash
curl -X PATCH "https://app.basketly.io/api/v1/feeds/abc123/categories" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "changes": [
      {}
    ]
  }'
```

#### `GET /google-categories` — Szukaj kategorii Google

Znajduje kategorie taksonomii produktów Google po słowach polskich lub angielskich albo po numerze (do pola `google` kategorii feedu). Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `feeds:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `q` | query |  | Słowa albo numer kategorii, do 100 znaków. |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/google-categories" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /google-taxonomy` — Wersja taksonomii Google

Której wersji taksonomii produktów Google używa Basketly, ile ma kategorii i kiedy ostatnio sprawdzono u Google (co tydzień samo). Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `feeds:read`

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/google-taxonomy" \
  -H "Authorization: Bearer $API_KEY"
```

#### `POST /google-taxonomy/refresh` — Sprawdź taksonomię Google teraz

Dodaje zadanie w tle, które pobiera taksonomię Google (polską i angielską) i włącza poprawną nowszą wersję. Tylko właściciel albo administrator organizacji. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `feeds:write`

Odpowiedź 202:


```bash
curl -X POST "https://app.basketly.io/api/v1/google-taxonomy/refresh" \
  -H "Authorization: Bearer $API_KEY"
```

#### `POST /feeds/{id}/categories/auto` — Przelicz kategorie automatycznie

Wybiera od nowa kategorię Google każdej kategorii sklepu, której nie wybrał człowiek (Basketly robi to sam po każdym odczycie produktów; to przelicza także istniejące wybory). Wybory ludzi zostają. Odpowiada jak GET. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `feeds:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |

Odpowiedź 200:


```bash
curl -X POST "https://app.basketly.io/api/v1/feeds/abc123/categories/auto" \
  -H "Authorization: Bearer $API_KEY"
```

### Szablony grafik

Szablony grafik do reklam katalogowych.

#### `GET /templates` — Lista szablonów grafik

Szablony, którymi reguła feedu albo bloczek przepływu rysuje grafiki produktów. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `templates:read`

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `templates` | object[] |  | Szablony: id, name, format, updated. |

```bash
curl "https://app.basketly.io/api/v1/templates" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /templates/performance` — Wyniki szablonów

Wyniki w reklamach produktów z feedów, które używają każdego szablonu. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `templates:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `from` | query |  | Pierwszy dzień RRRR-MM-DD (domyślnie 30 dni temu). |
| `to` | query |  | Ostatni dzień RRRR-MM-DD. |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/templates/performance" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /templates/{id}` — Pobierz szablon grafiki

Jeden szablon z warstwami. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `templates:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/templates/abc123" \
  -H "Authorization: Bearer $API_KEY"
```

### Raporty

Liczby kampanii i kreacji.

#### `GET /campaigns/report` — Raport kampanii

Wydatki, zakupy, wartość zakupów, koszt zakupu i ROAS na kampanię, zestaw lub reklamę — liczby ze strony Kampanie.

Uprawnienie: `campaigns:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `from` | query |  | Pierwszy dzień RRRR-MM-DD (domyślnie 30 dni temu). |
| `to` | query |  | Ostatni dzień RRRR-MM-DD (zakres do 366 dni). |
| `level` | query |  | Poziom wierszy. |
| `campaign` | query |  | Tylko ta kampania (dla poziomu adset/ad). |
| `adset` | query |  | Tylko ten zestaw (poziom ad). |
| `ad_account` | query |  | Tylko to konto reklamowe Mety (act_… albo sam numer); sumy sklepu pojawiają się wtedy tylko, gdy to jedyne konto organizacji. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `from` | string |  | Pierwszy dzień (strefa czasowa organizacji). |
| `to` | string |  | Ostatni dzień. |
| `level` | string |  | Poziom wierszy. One of: `campaign`, `adset`, `ad`. |
| `totals` | object[] |  | Sumy w każdej walucie: `current` i poprzedni okres tej samej długości (`previous`). Bez filtra kampanii/zestawu zawierają też opłacone zamówienia sklepu z okresu (`shop_orders`, `shop_revenue`) i `mer` = przychód sklepu / wydatki. Sumy i wiersze mają też `baskety_orders`, `baskety_revenue`, `baskety_currency`: opłacone zamówienia, których źródłem reklamy (odczytanie Basketly z wizyt kupującego) jest kampania / zestaw / reklama Mety z wiersza — inna liczba niż zakupy Mety, nigdy do nich nie dodawana. |
| `rows` | array Each row also names its Meta ad account: `ad_account_id` and `ad_account_name` (empty until read). |  | Wiersz na kampanię / zestaw / reklamę ze `status` (stan w Mecie), `optimization` (na co optymalizują jego zestawy: `label` po polsku — standardowe zdarzenia Mety po polsku, np. „Zdarzenie: Zakup (zbiór …)” — `event` i przy celu na zdarzenie `event_name` = techniczna nazwa w Mecie, np. Purchase, `short` = cel słowami bez zbioru na plakietkę, np. „Zakup”) i `metrics`: spend, purchases, purchase_value, cost_per_purchase, roas (zakupy i ich wartość według raportu Mety); dostarczanie zsumowane z dni okresu — impressions, clicks (wszystkie kliknięcia), cpm (wydatki / wyświetlenia × 1000), link_clicks, ctr (kliknięcia linku / wyświetlenia, ułamek), cpc (wydatki / kliknięcie linku; liczby linków tylko z dni odczytanych z kliknięciami linku, wcześniej null), wideo plays_3s, thruplays, p25, p50, p75, p100, hook_rate (odtworzenia 3 s / wyświetlenia) i hold_rate (ThruPlay / odtworzenia 3 s; null dla obrazów); koszty i wskaźniki są null przy zerowym dzielniku. Zasięgu i częstotliwości nie ma (dziennego zasięgu nie da się zsumować). Do tego `baskety_orders` / `baskety_revenue` ze źródła zamówień. Sumy mają te same pola dostarczania. |
| `syncs` | object[] |  | Stan synchronizacji wydatków na konto reklamowe: `ad_account_id` (act_…), `ad_account_name` (nazwa konta w Mecie; pusta, dopóki następny odczyt wydatków jej nie zapisze), `currency`, `last_success`, `last_error` oraz `job`, gdy konto czeka albo jest czytane. |
| `comparison` | object |  | Jak sumy porównują się z poprzednim okresem: {mode whole\|same_hour, until, previous_until, spend_without_today, spend_to, previous_spend_to}. Okres kończący się dziś porównuje liczby Mety i MER po pełnych dniach (bez dzisiejszego w obu okresach), a zamówienia sklepu i Basketly do tej samej godziny; `compare` każdej sumy to bieżące liczby do zestawienia z `previous` (`current` nadal obejmuje dzisiejszy dzień). |
| `shops` | string[] |  | Rodzaje podłączonych sklepów (shoper, idosell, baselinker…), z których zamówień pochodzą liczby sklepu. |

```bash
curl "https://app.basketly.io/api/v1/campaigns/report" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /creatives/report` — Raport kreacji

Metryki na grupę kreacji z trendem tygodniowym i regułą zmęczenia — liczby ze strony Kreacje.

Uprawnienie: `creatives:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `from` | query |  | Pierwszy dzień RRRR-MM-DD. |
| `to` | query |  | Ostatni dzień RRRR-MM-DD. |
| `format` | query |  | Format kreacji. |
| `campaign` | query |  | Identyfikator kampanii Meta. |
| `ad_account` | query |  | Identyfikator konta reklamowego. |
| `tag` | query |  | Filtr tagu `wymiar:wartość` (hook, creator, offer). |
| `weeks` | query |  | Liczba tygodni trendu, 4–8. |
| `channel` | query |  | meta (domyślnie) = grupy kreacji Mety; google_ads, microsoft_ads, tiktok, pinterest albo openai_ads = reklamy tej platformy (i zasoby Google) z liczbami platformy — odpowiedź to wtedy obiekt ChannelCreatives. |
| `active` | query |  | 1 = tylko aktywne reklamy. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `from` | string |  | Pierwszy dzień. |
| `to` | string |  | Ostatni dzień. |
| `weeks` | array[] |  | Zakresy tygodni użyte do trendu. |
| `rows` | array `ad_accounts` lists the Meta ad account ids of the group and `ad_account_names` their names in the same order (empty until read). |  | Wiersz na grupę kreacji (ten sam film, obraz lub kreacja) z metrykami, trendem tygodniowym, oceną zmęczenia i tagami. `baskety_orders` / `baskety_revenue` (w `baskety_currency`): opłacone zamówienia sklepu z okresu, których źródłem reklamowym (odczyt Basketly z ostatniego kliknięcia w wizytach kupującego) jest jedna z reklam kreacji — inna liczba niż zakupy Mety, nigdy do nich nie dodawana. |
| `totals` | object[] |  | Sumy w każdej walucie. |
| `compare_totals` | object[] |  | Bieżące sumy do zestawienia z `previous_totals`: gdy okres kończy się dziś, te same kreacje po pełnych dniach bez dzisiejszego (Meta podaje dane za dzień); w przeciwnym razie równe `totals`. Puste dla okresu tylko z dzisiejszego dnia (nie ma czego porównać). |
| `comparison` | object |  | Jak sumy porównują się z poprzednim okresem: {mode whole\|same_hour, until, previous_until, spend_without_today, spend_to, previous_spend_to}; previous_totals pomijają wtedy odpowiadający dzień na końcu. |
| `previous_totals` | object[] |  | Sumy w każdej walucie tych samych (przefiltrowanych) kreacji z okresu tej samej długości tuż przed `from` (porównanie pod podsumowaniem Kreacji; gdy okres kończy się dziś, bez jego ostatniego dnia — zob. `comparison`). |
| `syncs` | object[] |  | Stan synchronizacji wydatków na konto reklamowe: `ad_account_id` (act_…), `ad_account_name` (nazwa konta w Mecie; pusta, dopóki następny odczyt wydatków jej nie zapisze), `currency`, `last_success`, `last_error` oraz `job`, gdy konto czeka albo jest czytane. |

```bash
curl "https://app.basketly.io/api/v1/creatives/report" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /reports/sales` — Raport sprzedaży

Sprzedaż sklepu w okresie obok poprzedniego: zamówienia, przychód w głównej walucie, średnie zamówienie, kupujący nowi/powracający, wydatki w Mecie, zakupy, ROAS, MER, wydatki na zamówienie, zakupy przyjęte do wysyłki, wydatki w Google Ads / Microsoft Ads według ich raportów, dzień po dniu, 10 najlepszych produktów i `verdict` — jedno zdanie o okresie (od niego zacznij czytanie Raportów). Źródła nigdy nie są łączone. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `from` | query |  | Pierwszy dzień RRRR-MM-DD (domyślnie 30 dni temu). |
| `to` | query |  | Ostatni dzień RRRR-MM-DD (do 366 dni). |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/reports/sales" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /reports/channels` — Kanały i sklep

Każdy kanał w okresie obok sklepu: łączne wydatki, MER i koszt zamówienia ze wszystkich kanałów, liczby każdego kanału według jego platformy (nigdy nie sumowane między platformami), zamówienia Basketly z ostatniego kliknięcia na źródło (udziały sumują się do 100%), suma własnych konwersji platform obok zamówień sklepu, dzień po dniu przychód i wydatki na kanał — Raporty → Sprzedaż. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `from` | query |  | Pierwszy dzień RRRR-MM-DD (domyślnie 30 dni temu). |
| `to` | query |  | Ostatni dzień RRRR-MM-DD (do 366 dni). |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/reports/channels" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /reports/product-channels` — Kanały × produkty

Każdy produkt wobec każdego kanału, który go pokazuje — Raporty → Kanały × produkty: na kanał wyświetlenia, kliknięcia, wydatki, CTR i własne konwersje platformy na produkt tam, gdzie je podaje (Meta nie: null), zamówienia Basketly z ostatniego kliknięcia zawierające produkt, sztuki na 100 kliknięć i udział w wyświetleniach kanału; sztuki, przychód, odsłony, koszyki, stan i dni zapasu sklepu; najlepszy kanał, podział wydatków i źródeł. Nigdy nie sumuje konwersji platform. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `from` | query |  | Pierwszy dzień RRRR-MM-DD (domyślnie 30 dni temu). |
| `to` | query |  | Ostatni dzień RRRR-MM-DD (do 366 dni). |
| `channel` | query |  | Tylko produkty z liczbami w tym kanale; sortowanie po wydatkach i zamówieniach czyta ten kanał. |
| `sort` | query |  | Kolejność (końcówka _asc = rosnąco). |
| `page` | query |  | Strona od 1. |
| `per_page` | query |  | Wierszy na stronę: 10, 25, 50 albo 100. |
| `content_id` | query |  | Jeden produkt po identyfikatorze z katalogu. |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/reports/product-channels" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /channels/report` — Raport kanału

Bez `channel` sumy każdego kanału (Kampanie → Wszystkie kanały); z `channel` kampanie tego kanału (grupy reklam, reklamy, gdzie odczytane) z liczbami platformy i zamówieniami Basketly z ostatniego kliknięcia — jeden słownik dla Mety, Google Ads, Microsoft Ads, TikToka i Pinteresta. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `from` | query |  | Pierwszy dzień RRRR-MM-DD (domyślnie 30 dni temu). |
| `to` | query |  | Ostatni dzień RRRR-MM-DD (do 366 dni). |
| `channel` | query |  | Jeden kanał płatny; puste = sumy wszystkich kanałów. |
| `level` | query |  | Poziom wierszy. |
| `parent` | query |  | Kampania grup reklam / grupa reklam reklam. |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/channels/report" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /manual-spend` — Koszty spoza platform reklamowych

Koszty wpisane przez zespół (porównywarki, narzędzie e-mail, agencja, inne) na miesiąc. Kwota miesiąca rozkłada się równo na jego dni i wchodzi do łącznych wydatków, MER i kosztu zamówienia (GET /reports/channels: wiersze z `manual`), nigdy do liczb platformy. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:read`

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/manual-spend" \
  -H "Authorization: Bearer $API_KEY"
```

#### `POST /manual-spend` — Dodaj koszt spoza platform reklamowych

Dodaje koszt jednego miesiąca. Nic nie jest wysyłane; zmieniają się tylko łączne wydatki i MER. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `Idempotency-Key` | header |  | Unikalny klucz (1–255 widocznych znaków ASCII). Powtórzenie POST z tym samym kluczem i treścią w ciągu 24 godzin zwraca zapisaną odpowiedź z `Idempotent-Replayed: true`. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `channel` | string |  | comparison, email, agency albo other. One of: `comparison`, `email`, `agency`, `other`. |
| `name` | string |  | Czego dotyczy koszt, do 80 znaków (wymagane dla other). |
| `month` | string |  | RRRR-MM. |
| `amount` | number |  | Więcej niż 0, najwyżej 1 000 000 000. |
| `currency` | string |  | Kod ISO (domyślnie główna waluta sklepu). |
| `note` | string |  | Do 500 znaków. |

Odpowiedź 201:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string |  | Identyfikator wpisu. |
| `channel` | string |  | Rodzaj kosztu: comparison (porównywarki), email (narzędzie e-mail), agency (agencja), other (inne). One of: `comparison`, `email`, `agency`, `other`. |
| `name` | string |  | Czego dotyczy koszt (wymagane dla other). |
| `month` | string |  | Miesiąc RRRR-MM; kwota rozkłada się równo na jego dni. |
| `amount` | number |  | Kwota za miesiąc. |
| `currency` | string |  | Kod waluty ISO. |
| `note` | string |  | Notatka. |
| `created` | string (date-time) |  | Utworzono. |
| `updated` | string (date-time) |  | Zmieniono. |

```bash
curl -X POST "https://app.basketly.io/api/v1/manual-spend" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 5f0c7a2e-8d41-4b6a-9e3f-1c2d3e4f5a6b" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Anna Nowak",
    "currency": "PLN"
  }'
```

#### `PATCH /manual-spend/{id}` — Zmień koszt spoza platform reklamowych

Zmienia dowolne pole jednego wpisu. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `channel` | string |  | comparison, email, agency albo other. One of: `comparison`, `email`, `agency`, `other`. |
| `name` | string |  | Czego dotyczy koszt. |
| `month` | string |  | RRRR-MM. |
| `amount` | number |  | Kwota za miesiąc. |
| `currency` | string |  | Kod ISO. |
| `note` | string |  | Notatka. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string |  | Identyfikator wpisu. |
| `channel` | string |  | Rodzaj kosztu: comparison (porównywarki), email (narzędzie e-mail), agency (agencja), other (inne). One of: `comparison`, `email`, `agency`, `other`. |
| `name` | string |  | Czego dotyczy koszt (wymagane dla other). |
| `month` | string |  | Miesiąc RRRR-MM; kwota rozkłada się równo na jego dni. |
| `amount` | number |  | Kwota za miesiąc. |
| `currency` | string |  | Kod waluty ISO. |
| `note` | string |  | Notatka. |
| `created` | string (date-time) |  | Utworzono. |
| `updated` | string (date-time) |  | Zmieniono. |

```bash
curl -X PATCH "https://app.basketly.io/api/v1/manual-spend/abc123" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Anna Nowak",
    "currency": "PLN"
  }'
```

#### `DELETE /manual-spend/{id}` — Usuń koszt spoza platform reklamowych

Usuwa jeden wpis na stałe. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `deleted` | string | yes | Identyfikator usuniętego wpisu. |

```bash
curl -X DELETE "https://app.basketly.io/api/v1/manual-spend/abc123" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /reports/insights` — Wnioski z raportów

Wszystkie wnioski okresu z reguł (sprzedaż, trajektorie produktów, produkty-magnesy, zatowarowanie, otagowane feedy) z liczbami i proponowanym działaniem, najważniejsze pierwsze. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `from` | query |  | Pierwszy dzień RRRR-MM-DD (domyślnie 30 dni temu). |
| `to` | query |  | Ostatni dzień RRRR-MM-DD (do 366 dni). |
| `channel` | query |  | Tylko wnioski o tym kanale (liczniki `channels` nadal obejmują wszystkie wnioski). |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/reports/insights" \
  -H "Authorization: Bearer $API_KEY"
```

#### `PUT /reports/insights/{key}` — Oznacz wniosek

Oznacza jeden wniosek tylko dla twórcy klucza, jak menu ⋯ w Raportach: zrobione, odłożone do dnia, nie dotyczy albo znów otwarte. Inni członkowie nadal go widzą; powtórzenie tego samego stanu niczego nie zmienia. Nie zmienia danych organizacji, więc wystarcza uprawnienie do odczytu raportów. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `key` | path | yes | Klucz wniosku (`key`) z GET /reports/insights. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `state` | string | yes | done, snoozed, dismissed albo open, żeby przywrócić wniosek. One of: `open`, `done`, `snoozed`, `dismissed`. |
| `until` | string (date) |  | Tylko snoozed: dzień powrotu wniosku, RRRR-MM-DD (od jutra do roku; domyślnie 7 dni od dziś). |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `key` | string |  | Klucz wniosku. |
| `state` | string |  | Stan po zmianie. One of: `open`, `done`, `snoozed`, `dismissed`. |
| `until` | string |  | Dzień końca odłożenia albo pusty. |

```bash
curl -X PUT "https://app.basketly.io/api/v1/reports/insights/abc123" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "state": "open"
  }'
```

#### `GET /reports/products` — Trajektorie produktów

Wyświetlenia, kliknięcia i wydatki produktu w reklamach katalogowych Mety obok sprzedanych sztuk i przychodu z zamówień sklepu, dzień po dniu (powyżej 31 dni tydzień po tygodniu), z trendami, zmianą tydzień do tygodnia, wzorcem i wnioskami — każdy produkt z wyświetleniami, sprzedażą albo odsłonami w okresie (do 5000), szukany, filtrowany, sortowany i stronicowany na serwerze (`total`, `listed`, `matched`, `capped`). Kliknięcia i sprzedaż stoją obok siebie, bez atrybucji. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `from` | query |  | Pierwszy dzień RRRR-MM-DD (domyślnie 30 dni temu). |
| `to` | query |  | Ostatni dzień RRRR-MM-DD (do 366 dni). |
| `bucket` | query |  | day albo week (domyślnie day do 31 dni, powyżej week). |
| `q` | query |  | Tekst w nazwie albo identyfikatorze produktu; szuka we wszystkich produktach okresu, nie tylko na pierwszej stronie. |
| `pattern` | query |  | Tylko te wzorce (powtórz albo rozdziel przecinkami); `patterns` w odpowiedzi nadal liczy wszystkie produkty. |
| `sort` | query |  | Kolejność (końcówka _asc = rosnąco). |
| `page` | query |  | Strona od 1. |
| `per_page` | query |  | Produktów na stronę, 1–500. |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/reports/products" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /reports/products/detail` — Trajektoria produktu

Trajektoria jednego produktu, każdy dzień okresu, z jego wnioskami. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | query | yes | Identyfikator produktu z katalogu (content_id). |
| `from` | query |  | Pierwszy dzień RRRR-MM-DD (domyślnie 30 dni temu). |
| `to` | query |  | Ostatni dzień RRRR-MM-DD (do 366 dni). |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/reports/products/detail" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /reports/magnets` — Produkty-magnesy

Produkty, które ludzie oglądają albo klikają, ale rzadko kupują, a ci sami ludzie kupują inne produkty — z powiązanych wizyt skryptu sklepu i z zamówień z reklam katalogowych. Współwystępowanie, nie dowód przyczyny. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `from` | query |  | Pierwszy dzień RRRR-MM-DD (domyślnie 30 dni temu). |
| `to` | query |  | Ostatni dzień RRRR-MM-DD (do 366 dni). |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/reports/magnets" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /reports/unseen` — Produkty niewyświetlane

Produkty w katalogu (feed Basketly czytany przez Metę albo, bez niego, każdy aktywny produkt), dostępne i bez błędów Mety, których reklamy katalogowe nie pokazały w okresie — z przyczyną, dniami w feedzie, popytem w sklepie i wartością zapasu. Jedna strona listy; podsumowanie i wnioski obejmują cały katalog. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `from` | query |  | Pierwszy dzień RRRR-MM-DD (domyślnie 30 dni temu). |
| `to` | query |  | Ostatni dzień RRRR-MM-DD (do 366 dni). |
| `reason` | query |  | Lista po przecinku: not_in_feed, excluded_by_rule, out_of_stock, meta_error_check, meta_rejected, meta_pending, pending_sync, not_in_active_set, not_picked. |
| `exposure` | query |  | Lista po przecinku: none, low, shown (domyślnie none,low). |
| `eligible` | query |  | yes albo no. |
| `feed` | query |  | Id feedu: produkty przez niego serwowane albo pominięte. |
| `category` | query |  | Kategoria sklepu, dokładnie (można powtórzyć). |
| `brand` | query |  | Marka, dokładnie (można powtórzyć). |
| `availability` | query |  | Lista po przecinku: in_stock, out_of_stock, preorder, backorder, unknown. |
| `sales` | query |  | yes albo no: sprzedane sztuki w okresie. |
| `views` | query |  | yes albo no: odsłony w sklepie w okresie. |
| `q` | query |  | Tytuł, SKU albo id z katalogu. |
| `sort` | query |  | stock_value (domyślnie), units, views, revenue, impressions, days_in_feed, last_impression, title; _asc odwraca. |
| `page` | query |  | Strona, od 1. |
| `per_page` | query |  | 10, 25 (domyślnie), 50 albo 100. |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/reports/unseen" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /reports/stock-ads` — Wyprzedane z emisją

Produkty z wyświetleniami i wydatkami w dniach, gdy sklep podawał brak towaru (ze zmian dostępności z nocnych odczytów produktów), opóźnienie katalogu oraz produkty, które wróciły na stan co najmniej 3 dni temu i od tego czasu nie miały wyświetleń. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `from` | query |  | Pierwszy dzień RRRR-MM-DD (domyślnie 30 dni temu). |
| `to` | query |  | Ostatni dzień RRRR-MM-DD (do 366 dni). |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/reports/stock-ads" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /reports/concentration` — Koncentracja emisji

Jak nierówno Meta rozkłada wyświetlenia reklam katalogowych na produkty zakresu (wszystko, konto reklamowe, kampania, zestaw albo reklama): ranking, udziały czołówki, Gini, HHI i efektywna liczba produktów, seria tygodniowa, okresy przed i po wyprzedaniu produktów z czołówki oraz wnioski. Zakupy z Mety tylko dla całego zakresu, nigdy na produkt. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `scope` | query |  | all (domyślnie), account, campaign, adset albo ad. |
| `id` | query |  | Identyfikator konta reklamowego (act_…), kampanii, zestawu albo reklamy; wymagany poza scope=all. Lista w `options` odpowiedzi. |
| `from` | query |  | Pierwszy dzień RRRR-MM-DD (domyślnie 30 dni temu). |
| `to` | query |  | Ostatni dzień RRRR-MM-DD (do 366 dni). |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/reports/concentration" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /reports/stock` — Sygnały zatowarowania

Które produkty skończą się przed dostawą: tempo sprzedaży z 7/14/28 dni, dni zapasu, data braku towaru, punkt zamówienia, sugerowana ilość, poziom względem czasu dostawy i popyt z reklam katalogowych i skryptu sklepu. Stan to liczba ze sklepu z ostatniego odczytu plus podpisany szacunek Basketly; prognoza jest liniowa. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `from` | query |  | Pierwszy dzień RRRR-MM-DD (domyślnie 30 dni temu). |
| `to` | query |  | Ostatni dzień RRRR-MM-DD (do 366 dni). |
| `level` | query |  | Lista po przecinku: sold_out, critical, warning, watch, ok, overstock, thin. |
| `rising` | query |  | 1 = tylko produkty z rosnącymi kliknięciami albo koszykami. |
| `in_ads` | query |  | 1 = tylko produkty z wyświetleniami w ostatnich 7 dniach. |
| `brand` | query |  | Tylko ta marka. |
| `category` | query |  | Kategoria zawiera ten tekst. |
| `source` | query |  | Rodzaj sklepu albo feed. |
| `lead_time` | query |  | Czas dostawy do symulacji w dniach (1–180); domyślnie ustawienie organizacji. |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/reports/stock" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /reports/feed-orders` — Zamówienia z otagowanych feedów

Zamówienia, których wizyta trafiła przez otagowany link katalogu, na feed i na produkt wejścia, z zamówieniami na 100 kliknięć produktów feedu. Własny odczyt Basketly ze skryptu sklepu (wymaga oznaczania linków i skryptu), nie zakupy według Mety. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `from` | query |  | Pierwszy dzień RRRR-MM-DD (domyślnie 30 dni temu). |
| `to` | query |  | Ostatni dzień RRRR-MM-DD (do 366 dni). |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/reports/feed-orders" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /reports/product-sets` — Zdrowie zestawów produktów

Dla każdego zestawu produktów wybranych katalogów Meta: liczba produktów, udział dostępnych, zatwierdzonych, odrzuconych i oczekujących, wydatki zestawów reklam, które go promują, i ostrzeżenia; do tego diagnostyka katalogu Mety. Wymaga opcjonalnego połączenia katalogu (catalog_management); bez niego `connected` jest false, a listy są puste. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `from` | query |  | Pierwszy dzień RRRR-MM-DD (domyślnie 30 dni temu). |
| `to` | query |  | Ostatni dzień RRRR-MM-DD (do 366 dni). |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/reports/product-sets" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /meta-catalog` — Połączenie katalogu Meta

Czy organizacja połączyła katalog Meta (opcjonalnie), które katalogi wybrała i jak poszedł ich ostatni odczyt. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:read`

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/meta-catalog" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /campaigns/products` — Produkty w reklamach katalogowych

Wyniki na produkt z kampanii, grupy reklam albo reklamy według raportu platformy na produkt (domyślnie Meta; Google Ads, Microsoft Ads, TikTok przez `platform`), ze sprzedażą produktu w sklepie. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `campaigns:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `from` | query |  | Pierwszy dzień RRRR-MM-DD (domyślnie 30 dni temu). |
| `to` | query |  | Ostatni dzień RRRR-MM-DD. |
| `platform` | query |  | meta (domyślnie), google_ads, microsoft_ads albo tiktok. |
| `campaign` | query |  | Tylko ta kampania. |
| `adgroup` | query |  | Tylko ta grupa reklam (Google Ads, Microsoft Ads). |
| `ad` | query |  | Tylko ta reklama. |

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/campaigns/products" \
  -H "Authorization: Bearer $API_KEY"
```

### Automatyzacje

Automatyzacje organizacji i ich stan.

#### `GET /automations` — Lista automatyzacji

Automatyzacje organizacji: nazwa, włączona/wyłączona, rodzaj wyzwalacza, wersja, autor, ostatnie uruchomienie, błędne kroki z ostatnich 24 godzin, uruchomienia, wysyłki i błędne kroki z ostatnich 30 dni, uruchomienia w toku i co każda może wysłać. Edycja i włączanie zostają w panelu. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `automations:read`

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/automations" \
  -H "Authorization: Bearer $API_KEY"
```

### Autodiagnostyka

Autodiagnostyka organizacji.

#### `GET /diagnostics` — Ostatnia autodiagnostyka

Ostatnie uruchomienie autodiagnostyki ze wszystkimi sprawdzeniami. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `diagnostics:read`

Odpowiedź 200:


```bash
curl "https://app.basketly.io/api/v1/diagnostics" \
  -H "Authorization: Bearer $API_KEY"
```

#### `POST /diagnostics/run` — Uruchom autodiagnostykę

Jak „Sprawdź teraz”: dodaje uruchomienie do kolejki (albo od razu startuje czekające). Wynik odczytasz przez GET /diagnostics. Działa jako osoba, która utworzyła klucz: obowiązuje jej rola, `scope=mine` oznacza jej własną pracę.

Uprawnienie: `diagnostics:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `Idempotency-Key` | header |  | Unikalny klucz (1–255 widocznych znaków ASCII). Powtórzenie POST z tym samym kluczem i treścią w ciągu 24 godzin zwraca zapisaną odpowiedź z `Idempotent-Replayed: true`. |

Odpowiedź 202:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `job` | object |  | Zadanie (numer, status). |

```bash
curl -X POST "https://app.basketly.io/api/v1/diagnostics/run" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 5f0c7a2e-8d41-4b6a-9e3f-1c2d3e4f5a6b"
```

### Zadania w tle

Prace w tle.

#### `GET /jobs` — Lista zadań w tle

Odczyty zamówień, synchronizacje wydatków, arkuszy i inne prace w tle organizacji.

Uprawnienie: `jobs:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `limit` | query |  | Rozmiar strony, 1–100 (domyślnie 25). |
| `cursor` | query |  | `next_cursor` z poprzedniej strony. |
| `order` | query |  | Sortowanie po `updated_at` (potem id): `desc` od najnowszych (domyślnie) lub `asc`. |
| `updated_since` | query |  | Tylko rekordy zmienione w tym czasie ISO 8601 lub później. |
| `status` | query |  | Status zadania. |
| `kind` | query |  | Rodzaje zadań po przecinku (pole `kind`, np. `shoper_orders,feed_fetch`). |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | Job[] | yes |  |
| `data[].number` | integer |  | Numer zadania w organizacji. |
| `data[].kind` | string |  | Rodzaj zadania, np. ad_spend, shoper_orders, sheets_sync. |
| `data[].label` | string |  | Opis. |
| `data[].status` | string |  | Status. One of: `queued`, `running`, `done`, `failed`, `cancelled`. |
| `data[].priority` | string |  | `user` albo `background`. |
| `data[].trigger` | string |  | Co je uruchomiło. |
| `data[].progress` | object |  | Licznik `done` i opis `text`. |
| `data[].attempts` | integer |  | Próby. |
| `data[].error` | string |  | Błąd nieudanego zadania. |
| `data[].summary` | string |  | Podsumowanie wyniku. |
| `data[].run_after` | string (date-time) \| null |  | Nie wcześniej niż. |
| `data[].started_at` | string (date-time) \| null |  | Start. |
| `data[].finished_at` | string (date-time) \| null |  | Koniec. |
| `data[].created` | string (date-time) |  | Czas utworzenia. |
| `data[].updated_at` | string (date-time) |  | Ostatnia zmiana. |
| `next_cursor` | string \| null | yes | Przekaż jako `cursor`, by pobrać następną stronę; null na ostatniej stronie. |
| `has_more` | boolean | yes | Prawda, gdy istnieje kolejna strona. |

```bash
curl "https://app.basketly.io/api/v1/jobs" \
  -H "Authorization: Bearer $API_KEY"
```

#### `GET /jobs/{number}` — Pobierz zadanie

Jedno zadanie po numerze.

Uprawnienie: `jobs:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `number` | path | yes | Numer zadania. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `number` | integer |  | Numer zadania w organizacji. |
| `kind` | string |  | Rodzaj zadania, np. ad_spend, shoper_orders, sheets_sync. |
| `label` | string |  | Opis. |
| `status` | string |  | Status. One of: `queued`, `running`, `done`, `failed`, `cancelled`. |
| `priority` | string |  | `user` albo `background`. |
| `trigger` | string |  | Co je uruchomiło. |
| `progress` | object |  | Licznik `done` i opis `text`. |
| `attempts` | integer |  | Próby. |
| `error` | string |  | Błąd nieudanego zadania. |
| `summary` | string |  | Podsumowanie wyniku. |
| `run_after` | string (date-time) \| null |  | Nie wcześniej niż. |
| `started_at` | string (date-time) \| null |  | Start. |
| `finished_at` | string (date-time) \| null |  | Koniec. |
| `created` | string (date-time) |  | Czas utworzenia. |
| `updated_at` | string (date-time) |  | Ostatnia zmiana. |

```bash
curl "https://app.basketly.io/api/v1/jobs/42" \
  -H "Authorization: Bearer $API_KEY"
```

### Webhooki

Zdarzenia wysyłane na Twój serwer.

#### `GET /webhooks` — Lista webhooków

Subskrypcje webhooków organizacji (do 10).

Uprawnienie: `webhooks:read`

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | Webhook[] | yes |  |
| `data[].id` | string |  | Identyfikator webhooka. |
| `data[].url` | string |  | Adres HTTPS, który odbiera POST-y. |
| `data[].description` | string |  | Twój opis. |
| `data[].events` | string[] |  | Subskrybowane zdarzenia. One of: `contact.created`, `delivery.failed`. |
| `data[].active` | boolean |  | false wstrzymuje wysyłki. |
| `data[].last_status` | integer |  | Status HTTP ostatniej próby (0 = brak odpowiedzi). |
| `data[].last_error` | string |  | Ostatni błąd. |
| `data[].last_delivery_at` | string (date-time) \| null |  | Ostatnia próba. |
| `data[].consecutive_failures` | integer |  | Nieudane próby z rzędu. |
| `data[].created_at` | string (date-time) |  | Czas utworzenia. |
| `data[].updated_at` | string (date-time) |  | Ostatnia zmiana. |
| `next_cursor` | string \| null | yes | Przekaż jako `cursor`, by pobrać następną stronę; null na ostatniej stronie. |
| `has_more` | boolean | yes | Prawda, gdy istnieje kolejna strona. |

```bash
curl "https://app.basketly.io/api/v1/webhooks" \
  -H "Authorization: Bearer $API_KEY"
```

#### `POST /webhooks` — Dodaj webhook

Subskrybuje adres HTTPS na zdarzenia. Sekret podpisu `secret` jest zwracany tylko tutaj.

Uprawnienie: `webhooks:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `Idempotency-Key` | header |  | Unikalny klucz (1–255 widocznych znaków ASCII). Powtórzenie POST z tym samym kluczem i treścią w ciągu 24 godzin zwraca zapisaną odpowiedź z `Idempotent-Replayed: true`. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `url` | string | yes | Adres HTTPS (bez loginu i hasła, bez adresów prywatnych i lokalnych). Max 2000 characters. |
| `events` | string[] | yes | Zdarzenia do odbierania. One of: `contact.created`, `delivery.failed`. |
| `description` | string |  | Twój opis. Max 200 characters. |

Odpowiedź 201:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string |  | Identyfikator webhooka. |
| `url` | string |  | Adres HTTPS, który odbiera POST-y. |
| `description` | string |  | Twój opis. |
| `events` | string[] |  | Subskrybowane zdarzenia. One of: `contact.created`, `delivery.failed`. |
| `active` | boolean |  | false wstrzymuje wysyłki. |
| `last_status` | integer |  | Status HTTP ostatniej próby (0 = brak odpowiedzi). |
| `last_error` | string |  | Ostatni błąd. |
| `last_delivery_at` | string (date-time) \| null |  | Ostatnia próba. |
| `consecutive_failures` | integer |  | Nieudane próby z rzędu. |
| `created_at` | string (date-time) |  | Czas utworzenia. |
| `updated_at` | string (date-time) |  | Ostatnia zmiana. |
| `secret` | string | yes | Sekret podpisu (`whsec_…`). Widoczny tylko w tej odpowiedzi — zapisz go. |

```bash
curl -X POST "https://app.basketly.io/api/v1/webhooks" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 5f0c7a2e-8d41-4b6a-9e3f-1c2d3e4f5a6b" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks",
    "events": [
      "contact.created",
      "delivery.failed"
    ]
  }'
```

#### `GET /webhooks/{id}` — Pobierz webhook

Jedna subskrypcja z ostatnim wynikiem.

Uprawnienie: `webhooks:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string |  | Identyfikator webhooka. |
| `url` | string |  | Adres HTTPS, który odbiera POST-y. |
| `description` | string |  | Twój opis. |
| `events` | string[] |  | Subskrybowane zdarzenia. One of: `contact.created`, `delivery.failed`. |
| `active` | boolean |  | false wstrzymuje wysyłki. |
| `last_status` | integer |  | Status HTTP ostatniej próby (0 = brak odpowiedzi). |
| `last_error` | string |  | Ostatni błąd. |
| `last_delivery_at` | string (date-time) \| null |  | Ostatnia próba. |
| `consecutive_failures` | integer |  | Nieudane próby z rzędu. |
| `created_at` | string (date-time) |  | Czas utworzenia. |
| `updated_at` | string (date-time) |  | Ostatnia zmiana. |

```bash
curl "https://app.basketly.io/api/v1/webhooks/abc123" \
  -H "Authorization: Bearer $API_KEY"
```

#### `PATCH /webhooks/{id}` — Zmień webhook

Zmienia adres, zdarzenia, opis lub wstrzymuje; wznowienie zeruje licznik błędów.

Uprawnienie: `webhooks:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |

Treść żądania:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `url` | string |  | Nowy adres. |
| `events` | string[] |  | Nowa lista zdarzeń. One of: `contact.created`, `delivery.failed`. |
| `description` | string |  | Twój opis. |
| `active` | boolean |  | Wstrzymaj lub wznów. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string |  | Identyfikator webhooka. |
| `url` | string |  | Adres HTTPS, który odbiera POST-y. |
| `description` | string |  | Twój opis. |
| `events` | string[] |  | Subskrybowane zdarzenia. One of: `contact.created`, `delivery.failed`. |
| `active` | boolean |  | false wstrzymuje wysyłki. |
| `last_status` | integer |  | Status HTTP ostatniej próby (0 = brak odpowiedzi). |
| `last_error` | string |  | Ostatni błąd. |
| `last_delivery_at` | string (date-time) \| null |  | Ostatnia próba. |
| `consecutive_failures` | integer |  | Nieudane próby z rzędu. |
| `created_at` | string (date-time) |  | Czas utworzenia. |
| `updated_at` | string (date-time) |  | Ostatnia zmiana. |

```bash
curl -X PATCH "https://app.basketly.io/api/v1/webhooks/abc123" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks",
    "events": [
      "contact.created",
      "delivery.failed"
    ],
    "active": false
  }'
```

#### `DELETE /webhooks/{id}` — Usuń webhook

Usuwa subskrypcję i jej wysyłki w kolejce.

Uprawnienie: `webhooks:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |

Odpowiedź 204.

```bash
curl -X DELETE "https://app.basketly.io/api/v1/webhooks/abc123" \
  -H "Authorization: Bearer $API_KEY"
```

#### `POST /webhooks/{id}/test` — Wyślij próbny ping

Dodaje do kolejki podpisane zdarzenie `ping`, by sprawdzić odbiornik i weryfikację podpisu.

Uprawnienie: `webhooks:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |
| `Idempotency-Key` | header |  | Unikalny klucz (1–255 widocznych znaków ASCII). Powtórzenie POST z tym samym kluczem i treścią w ciągu 24 godzin zwraca zapisaną odpowiedź z `Idempotent-Replayed: true`. |

Odpowiedź 202:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `delivery_id` | string |  | Wysyłka zdarzenia `ping` dodana do kolejki. |

```bash
curl -X POST "https://app.basketly.io/api/v1/webhooks/abc123/test" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 5f0c7a2e-8d41-4b6a-9e3f-1c2d3e4f5a6b"
```

#### `POST /webhooks/{id}/rotate-secret` — Wymień sekret podpisu

Od razu wymienia sekret podpisu; nowy `secret` jest zwracany tylko tutaj. Wysyłki czekające w kolejce zostaną podpisane nowym sekretem.

Uprawnienie: `webhooks:write`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |
| `Idempotency-Key` | header |  | Unikalny klucz (1–255 widocznych znaków ASCII). Powtórzenie POST z tym samym kluczem i treścią w ciągu 24 godzin zwraca zapisaną odpowiedź z `Idempotent-Replayed: true`. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string |  | Identyfikator webhooka. |
| `url` | string |  | Adres HTTPS, który odbiera POST-y. |
| `description` | string |  | Twój opis. |
| `events` | string[] |  | Subskrybowane zdarzenia. One of: `contact.created`, `delivery.failed`. |
| `active` | boolean |  | false wstrzymuje wysyłki. |
| `last_status` | integer |  | Status HTTP ostatniej próby (0 = brak odpowiedzi). |
| `last_error` | string |  | Ostatni błąd. |
| `last_delivery_at` | string (date-time) \| null |  | Ostatnia próba. |
| `consecutive_failures` | integer |  | Nieudane próby z rzędu. |
| `created_at` | string (date-time) |  | Czas utworzenia. |
| `updated_at` | string (date-time) |  | Ostatnia zmiana. |
| `secret` | string | yes | Sekret podpisu (`whsec_…`). Widoczny tylko w tej odpowiedzi — zapisz go. |

```bash
curl -X POST "https://app.basketly.io/api/v1/webhooks/abc123/rotate-secret" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: 5f0c7a2e-8d41-4b6a-9e3f-1c2d3e4f5a6b"
```

#### `GET /webhooks/{id}/deliveries` — Lista wysyłek webhooka

Ostatnie próby (przechowywane 30 dni), od najnowszych.

Uprawnienie: `webhooks:read`

Parametry:

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | yes | Identyfikator rekordu. |
| `limit` | query |  | Rozmiar strony, 1–100 (domyślnie 25). |
| `cursor` | query |  | `next_cursor` z poprzedniej strony. |
| `order` | query |  | Sortowanie po `updated_at` (potem id): `desc` od najnowszych (domyślnie) lub `asc`. |
| `updated_since` | query |  | Tylko rekordy zmienione w tym czasie ISO 8601 lub później. |

Odpowiedź 200:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | WebhookDelivery[] | yes |  |
| `data[].id` | string |  | Identyfikator wysyłki (nagłówek X-Baskety-Delivery). |
| `data[].webhook_id` | string |  | Identyfikator webhooka. |
| `data[].event` | string |  | Typ zdarzenia. |
| `data[].event_id` | string |  | Identyfikator zdarzenia (`evt_…`, ten sam dla wszystkich webhooków jednego zdarzenia). |
| `data[].status` | string |  | Status. One of: `pending`, `sending`, `retry`, `delivered`, `failed`, `cancelled`. |
| `data[].attempts` | integer |  | Próby. |
| `data[].response_status` | integer |  | Status HTTP ostatniej próby. |
| `data[].last_error` | string |  | Ostatni błąd. |
| `data[].next_attempt_at` | string (date-time) \| null |  | Następna próba. |
| `data[].delivered_at` | string (date-time) \| null |  | Dostarczono. |
| `data[].created_at` | string (date-time) |  | Czas utworzenia. |
| `data[].updated_at` | string (date-time) |  | Ostatnia zmiana. |
| `next_cursor` | string \| null | yes | Przekaż jako `cursor`, by pobrać następną stronę; null na ostatniej stronie. |
| `has_more` | boolean | yes | Prawda, gdy istnieje kolejna strona. |

```bash
curl "https://app.basketly.io/api/v1/webhooks/abc123/deliveries" \
  -H "Authorization: Bearer $API_KEY"
```

## Webhooki

Webhook wysyła `POST` z JSON-em na Twój adres HTTPS, gdy coś zmieni się w organizacji. Organizacja może mieć do 10 webhooków. Sekret podpisu (`whsec_…`) dostajesz tylko w odpowiedzi na utworzenie webhooka. Te same webhooki dodasz, przetestujesz, wstrzymasz i usuniesz też w panelu, na stronie API w sekcji Webhooki, z historią wysyłek każdego.

Dostarczamy co najmniej raz: to samo zdarzenie może przyjść kilka razy, więc pomijaj `id`, które już obsłużyłeś. Treść zawiera tylko identyfikatory i stany, nigdy imion, telefonów ani e-maili; szczegóły pobierz przez `GET`.

### `contact.created`

Dodano osobę (zamówienie w sklepie, ręcznie, przez API, automatyzacją).

```json
{
  "contact_id": "abc123",
  "first_source": "Zamówienie w sklepie"
}
```

### `delivery.failed`

Wysyłka konwersji do Meta ostatecznie się nie powiodła.

```json
{
  "delivery_id": "d123",
  "event_id": "e123",
  "target_id": "t123",
  "dataset_id": "123456789",
  "event_name": "Purchase",
  "mode": "live",
  "error": "Połącz konto Meta ponownie lub sprawdź token."
}
```

- Odpowiedz dowolnym kodem 2xx w ciągu 15 sekund. Najpierw zapisz zdarzenie, a dłuższą pracę wykonaj później.
- Inna odpowiedź albo brak odpowiedzi to ponowienie: łącznie do 8 prób, kolejne po 1 min, 5 min, 30 min, 2 h, 6 h, 12 h i 24 h.
