# Feed e report: recupero e parsing dei file

> **Knowledge base interna** — non è traduzione della doc Amazon. Consolida il
> comportamento reale di feed e report così come **implementato nella libreria** e
> **usato in produzione** dai consumer interni (`data-layer`, `gclid-mapper`).
> Le operazioni feed/report **non sono nella doc ufficiale** Amazon (che copre il
> catalogo): questa pagina è la fonte principale.
>
> Indice: [../../README.md](../../README.md) · Auth: [autenticazione-per-versione.md](autenticazione-per-versione.md)

## In una riga

Oltre al catalogo (GetItems ecc.), la Creators API espone due famiglie di dati **a file**:

- **Feed** — export bulk del catalogo/offerte, sul path **`/catalog/v1`**.
- **Report** — dati di performance/affiliazione (click, ordini, revenue, earnings), sul path **`/reports/v1`**.

Entrambi seguono lo **stesso pattern in due passi**: prima si **elenca** (`list*`), poi si **ottiene un URL di download presigned** (`get*`) e lo si scarica separatamente; il file è tipicamente un **`.tsv.gz`** (TSV compresso gzip) da decomprimere e parsare.

```
list*  ──▶  { feeds|reports: [ { filename/feedName, size, md5, lastUpdated/lastModified } ] }
get*   ──▶  { url }                       (presigned, breve durata)
GET url ─▶  <corpo .tsv.gz>               (richiesta separata, senza Authorization)
           gzdecode ─▶ TSV ─▶ righe normalizzate
```

## Path e permessi: tre piani distinti

| Piano | Path base | Operazioni |
| :-- | :-- | :-- |
| Catalogo | `/catalog/v1` | `getItems`, `searchItems`, `getVariations`, `getBrowseNodes` |
| Feed | `/catalog/v1` | `listFeeds`, `getFeed` |
| Report | `/reports/v1` | `listReports`, `getReport` |

> [!IMPORTANT]
> **I permessi non coincidono.** L'abilitazione al catalogo non implica quella ai
> feed, né ai report. In particolare, come documentato nella pagina auth, le
> **credenziali v3 spesso non sono abilitate ai feed** → `listFeeds`/`getFeed`
> rispondono `HTTP 404`. Per i feed in EU/IT usare una credenziale **2.2**.
> Vedi [autenticazione-per-versione.md](autenticazione-per-versione.md#caveat-operativi-verificati-sul-campo-non-nella-doc-ufficiale).

## Feed (`/catalog/v1`)

Nella doc ufficiale Amazon **non esiste** una pagina per i feed: quanto segue è
ricostruito dall'implementazione.

### listFeeds

`POST /catalog/v1/listFeeds` — nessun body, solo header comuni (`Authorization`, `x-marketplace`).

Risposta (forma osservata):

```json
{ "feeds": [ { "feedName": "…", "size": 12345, "md5": "…", "lastUpdated": "2026-07-30T03:00:00Z", "contentType": "…" } ] }
```

### getFeed

`POST /catalog/v1/getFeed` con body `{ "feedName": "<nome dalla listFeeds>" }` → `{ "url": "<presigned>" }`.

> [!NOTE]
> La chiave dell'URL nella risposta **varia**: i consumer accettano difensivamente
> `url` / `downloadUrl` / `download_url` / `s3Url` / `s3_url` / `location`. Vedi
> [Varianza dei nomi campo](#varianza-dei-nomi-campo).

## Report (`/reports/v1`)

Gli schemi e le **famiglie** di report sono già documentati (in inglese) e non
vengono duplicati qui:

- [ListReports](../../en/api-reference/operations/list-reports.md) — forma della risposta e metadati.
- [GetReport](../../en/api-reference/operations/get-report.md) — URL presigned e download.
- [Report families](../../en/api-reference/operations/report-families.md) — famiglie note (`tracking_id`, `category`, `earnings`, `orders`, `subtag`, `linked_product`, `bounty`, …) e schemi TSV colonna-per-colonna.

Punti essenziali:

- `POST /reports/v1/listReports` (no body) → `{ reports: [ { filename, md5, size, lastModified } ] }`.
- `POST /reports/v1/getReport` con `{ "filename": "…" }` → `{ url }` presigned.
- Il **nome file** ha forma `<account>-<family>-<YYYYMMDD>.tsv.gz`: la **famiglia** e la **data** si ricavano dal nome (la libreria lo fa in `ReportMetadata::family()` / `reportDate()`).
- Le famiglie hanno **marker che si sovrappongono**: confrontare prima la più specifica (`linked_product_tracking` prima di `linked_product`, `tracking_id` prima di `tracking`, `bounty_earnings` prima di `bounty`).

> [!NOTE]
> Una famiglia assente dalla risposta **non** significa "non supportata": può solo
> essere non disponibile per quell'account/data. Non dedurre l'inesistenza.

## Il file: download e parsing

1. **Download**: l'`url` di `get*` è **presigned** (breve durata) — si scarica con una **GET separata**, **senza** header `Authorization` (l'auth è già nella firma dell'URL). Usare un **timeout lungo** (i consumer usano 600 s): i file possono essere grossi.
2. **Decompressione**: il corpo è `.tsv.gz` → `gzdecode()`. Il parser dei consumer è **difensivo**: decomprime solo se riconosce il magic gzip, altrimenti tratta il contenuto come già in chiaro (`maybeGunzip`).
3. **Struttura TSV Amazon**: **riga 1 = titolo**, **riga 2 = intestazioni di colonna**; le righe che iniziano con `Parameters:` o le righe-titolo vanno **saltate**. Va rimosso l'eventuale **BOM**.
4. **Delimitatore**: tipicamente TAB, ma il parser rileva il delimitatore (gestisce anche CSV).
5. **Formato alternativo**: alcuni contenuti possono essere **JSON** (iniziano con `[` o `{`) invece di TSV — gestirlo.

Esempio minimale (report):

```php
$url = $client->getReport('acme-tracking_id-report-20260730.tsv.gz')['url'];
$tsv = gzdecode(file_get_contents($url));
// poi: salta riga 1 (titolo), usa riga 2 come header, split su TAB.
```

## Varianza dei nomi campo

Le risposte reali dell'API **non sono consistenti** nel casing/naming delle chiavi.
I consumer normalizzano accettando più alias per lo stesso campo — replicare questo
approccio difensivo:

| Concetto | Alias osservati |
| :-- | :-- |
| Nome feed | `feedName`, `feed_name`, `name` |
| Nome report | `filename`, `fileName`, `file_name`, `name` |
| Ultimo aggiornamento | `lastUpdated`, `last_updated`, `lastModified`, `last_modified`, `updatedAt`, `updated_at`, `createdAt`, `created_at` |
| URL di download | `url`, `downloadUrl`, `download_url`, `s3Url`, `s3_url`, `location` |

## Dove è implementato

- **Libreria** (fonte di verità): [`src/CreatorsApiClient.php`](../../../src/CreatorsApiClient.php) — `listFeeds()`, `getFeed()`, `listReports()`, `getReport()` (ritornano la decodifica JSON grezza); DTO [`src/Model/ReportMetadata.php`](../../../src/Model/ReportMetadata.php) — `family()`, `reportDate()`, `fromListResponse()`.
  - **Nota roadmap**: gli helper di *download + gunzip* non sono ancora nella libreria (`getReport` ritorna l'URL, non il file). Vedi [CLAUDE.md](../../../CLAUDE.md) → *On-demand*.
- **Consumer interni** (da migrare sulla libreria):
  - `gclid-mapper` → `src/Report/Amazon/CreatorsApiClient.php` (`downloadFeed`, `downloadReport`, matching per pattern + `lastUpdated`) e `ReportParser.php` (`maybeGunzip`, `stripBom`, `detectDelimiter`, skip righe titolo).
  - `data-layer` → SDK Creators esteso a mano (report + auth v3.x).

## Riferimenti

- Knowledge report EN: [list-reports](../../en/api-reference/operations/list-reports.md) · [get-report](../../en/api-reference/operations/get-report.md) · [report-families](../../en/api-reference/operations/report-families.md)
- [Autenticazione per credential version](autenticazione-per-versione.md)
- [Header e parametri comuni di richiesta](../concepts/common-request-headers-and-parameters.md)
