Guida per sviluppatori

Pubblicare campagne e inviare eventi

Integra le campagne sponsorizzate nella tua app in due passaggi: recupera le campagne che corrispondono al tuo contesto, poi invia un VIEW quando ne viene mostrata una e un CLICK quando viene toccata. Questa guida illustra l'intero ciclo con esempi pronti da copiare.

Salvata solo in questo browser (localStorage) e inviata con ogni richiesta “Prova” qui sotto. Non condividerla e non inserirla nel codice lato client.

Panoramica

L'integrazione è un ciclo breve. Chiedi al nostro server quali campagne mostrare per una determinata posizione e contesto di menu, le mostri e restituisci gli eventi così i budget vengono addebitati e i report restano accurati.

Passaggio 1

Recupera campagne

Cerca le campagne che corrispondono alle tue categorie di ristoranti, alla posizione e ai prodotti.

Passaggio 2

Invia un VIEW

Quando una campagna viene mostrata a un utente, invia un evento VIEW.

Passaggio 3

Invia un CLICK

Quando l'utente la tocca, invia un evento CLICK.

Ogni richiesta qui sotto è reale. Incolla la tua chiave API sopra, modifica il payload e premi Invia per chiamare l'API vera e vedere la risposta direttamente qui.

Cerca le categorie di ristoranti

La ricerca delle campagne è mirata ai ristoranti per categoria, quindi si parte da qui. Chiama GET /api/v1/restaurant-categories per sfogliare il catalogo, filtrando facoltativamente con search e traducendo i nomi con language. Conserva l<code>id</code> di ogni categoria che vuoi targetizzare: lo passerai come <code>restaurantCategoryIds</code> quando recuperi le campagne.

Questo endpoint è pubblico: puoi chiamarlo senza chiave API. Tutti gli altri endpoint qui sotto richiedono la tua chiave nell’header x-api-key.
const params = new URLSearchParams({
  search: "pizza",
  language: "en",
  limit: "20",
})

// Public endpoint — no API key needed.
const res = await fetch(
  `https://adv.api.waithero.com/api/v1/restaurant-categories?${params}`,
)

const { data } = await res.json()
// data: [{ id, name, image }] — keep the ids you want to target.

Parametri della query

CampoTipoObbligatorioDescrizione
searchstringNoCorrispondenza parziale e non sensibile alle maiuscole sul nome della categoria. Ometti per elencare tutto.
languagestringNoCodice lingua (es. "en", "it") usato per tradurre il nome di ogni categoria. Ometti per il nome predefinito.
pageintegerNoNumero di pagina (a partire da 1). Predefinito: 1.
limitintegerNoElementi per pagina, da 1 a 50. Predefinito: 20.

La risposta è un elenco paginato. Leggi data per le categorie di questa pagina e total per sapere quante ne esistono in totale.

Risposta

CampoTipoDescrizione
data[]Le categorie della pagina corrente.
totalintegerNumero totale di categorie che corrispondono alla query, su tutte le pagine.
pageintegerIl numero di pagina restituito.
limitintegerLa dimensione della pagina applicata.

RestaurantCategory — ogni elemento in data[]

CampoTipoDescrizione
idintegerID della categoria. Passalo in restaurantCategoryIds quando recuperi le campagne.
namestringNome della categoria, tradotto quando passi language.
imagestring | nullURL dell'immagine della categoria, oppure null se non presente.

Recupera campagne

Chiama POST /api/v1/client/campaigns/search con il contesto per cui vuoi gli annunci: la posizione, più le categorie di ristoranti e i prodotti attualmente visualizzati. Solo la posizione è obbligatoria — il resto restringe l'abbinamento. La risposta contiene le campagne da mostrare — conserva l<code>id</code> di ogni campagna, ti servirà per riportare gli eventi.

const res = await fetch(
  "https://adv.api.waithero.com/api/v1/client/campaigns/search",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": process.env.CAMPAIGNS_API_KEY,
    },
    body: JSON.stringify({
      restaurantCategoryIds: [12, 34],
      areaLat: 45.4642,
      areaLon: 9.19,
      products: [
        { referenceId: "sku-123", productName: "Margherita", categoryName: "Pizza" },
      ],
    }),
  },
)

const { campaigns } = await res.json()
// Render campaigns, and remember each campaign.id for event reporting.
Corpo della richiesta (JSON)
Inserisci la tua chiave API in cima alla pagina per provare questa richiesta.

Corpo della richiesta

CampoTipoObbligatorioDescrizione
restaurantCategoryIdsinteger[]NoID delle categorie di ristoranti da abbinare. Facoltativo — invia restaurantCategoryNames al suo posto, oppure nessuno dei due per cercare in tutte le categorie.
restaurantCategoryNamesstring[]NoNomi delle categorie di ristoranti da abbinare (es. "Pizza"). Facoltativo — invia restaurantCategoryIds al suo posto, oppure nessuno dei due per cercare in tutte le categorie.
addressstringNoIndirizzo in testo libero dell'area per cui pubblichi gli annunci. Facoltativo se invii invece areaLat e areaLon. Se fornito, il server lo converte in coordinate e le restituisce nella risposta.
areaLatnumberNoLatitudine dell'area per cui pubblichi gli annunci. Facoltativa se invii invece address; forniscila con areaLon.
areaLonnumberNoLongitudine dell'area per cui pubblichi gli annunci. Facoltativa se invii invece address; forniscila con areaLat.
products[]NoI prodotti attualmente visualizzati — una voce per prodotto (struttura sotto). Facoltativo: senza prodotti possono corrispondere solo le campagne HOMEPAGE.
Punta ai ristoranti per categoria con restaurantCategoryIds, restaurantCategoryNames o entrambi. Gli ID corrispondono esattamente, quindi preferiscili quando li hai; i nomi vengono confrontati con il nostro catalogo. Non inviare nessuno dei due e la ricerca non viene filtrata per categoria.
Imposta la posizione di ricerca con un address in testo libero oppure con le coordinate areaLat + areaLon — invia almeno uno. Se invii un address, il server lo converte in coordinate e restituisce areaLat, areaLon e il tuo address risolti nel campo area della risposta.
products è facoltativo, ma è ciò su cui vengono abbinate le campagne: le campagne SUGGESTION e HIGHLIGHT promuovono ciascuna un prodotto, quindi una ricerca che non invia prodotti può restituire solo campagne HOMEPAGE.

products[] — ogni prodotto

CampoTipoObbligatorioDescrizione
referenceIdstringNoFacoltativo. Il tuo ID opaco del prodotto (es. uno SKU). Se impostato, viene restituito in matchedProductReferenceIds così sai quale campagna corrisponde a quale prodotto.
productNamestringNome visualizzato del prodotto.
categoryNamestringNome della categoria del prodotto.

Ogni campagna nella risposta include il suo id, il type (HOMEPAGE, SUGGESTION o HIGHLIGHT), l<code>advisor</code>, leventuale promotedProduct e matchedProductReferenceIds — i referenceId che hai inviato e a cui questa campagna corrisponde, così sai esattamente dove posizionarla.

Risposta

CampoTipoDescrizione
areaLa posizione su cui è stata eseguita la ricerca — un Area (campi sotto). Riporta le coordinate usate dal server, ricavate dal tuo address quando ne hai inviato uno.
campaigns[]Le campagne da mostrare — ciascuna una Campaign (campi sotto).

Area — la posizione di ricerca risolta

CampoTipoDescrizione
areaLatnumberLatitudine su cui è stata eseguita la ricerca. Ricavata da address se ne hai inviato uno, altrimenti l'areaLat che hai passato.
areaLonnumberLongitudine su cui è stata eseguita la ricerca. Ricavata da address se ne hai inviato uno, altrimenti l'areaLon che hai passato.
addressstring | nullL'address che hai inviato, restituito, oppure null se hai cercato per coordinate.

Risposta — ogni elemento di campaigns[]

CampoTipoDescrizione
idintegerID della campagna. Conservalo — lo invii come idAdCampaign quando registri gli eventi.
type"HOMEPAGE" | "SUGGESTION" | "HIGHLIGHT"Dove mostrare la campagna.
status"ACTIVE"Lo stato del ciclo di vita della campagna. La ricerca restituisce solo campagne attualmente in erogazione, quindi qui è sempre ACTIVE.
titlestringTitolo visualizzato. Presente per le campagne HOMEPAGE; assente per le altre.
dateStartstring | nullInizio della finestra di pubblicazione (ISO 8601), oppure null.
dateEndstring | nullFine della finestra di pubblicazione (ISO 8601), oppure null.
advisorL'inserzionista dietro la campagna — un Advisor (campi sotto).
masterProducts[]I prodotti presentati dalla campagna — ciascuno un MasterProduct (campi sotto).
promotedProduct | nullL'unico prodotto promosso per le campagne SUGGESTION e HIGHLIGHT, oppure null per le altre — un MasterProduct (campi sotto).
matchedProductReferenceIdsstring[]I referenceId che hai inviato e che questa campagna ha abbinato ai master product di Waithero.
homepage | nullIl banner homepage mostrato per le campagne HOMEPAGE, oppure null per le altre — un HomepageContent (campi sotto).

Advisor — l'inserzionista dietro la campagna

CampoTipoDescrizione
idintegerL'ID dell'inserzionista.
namestringIl nome visualizzato dell'inserzionista.
logostring | nullURL del logo dell'inserzionista, oppure null se assente.
linkstringL'URL di destinazione dell'inserzionista (es. il suo negozio).

Cosa sono i master product?

I master product sono il catalogo condiviso di Waithero degli articoli promossi dalle campagne. I products che invii descrivono il tuo menu; il nostro server abbina ciascuno a un master product per nome e categoria, e restituisce una campagna quando i suoi master product coincidono con i prodotti che hai inviato. I master product abbinati tornano in masterProducts e promotedProduct.

MasterProduct — usato da masterProducts[] e promotedProduct

CampoTipoDescrizione
idintegerL'ID del prodotto.
namestringIl nome visualizzato del prodotto.
categoryNamestringIl nome della categoria del prodotto. Assente quando non è determinabile.

HomepageContent — contenuto del banner homepage

CampoTipoDescrizione
linkstringURL di destinazione aperto al tocco del banner.
titlestringTitolo del banner.
descriptionstringTesto di supporto del banner. Assente quando non impostato.
imagestringURL dell'immagine del banner. Assente quando non impostato.

Invia eventi VIEW e CLICK

Invia un evento a POST /api/v1/events con l<code>idAdCampaign</code> della campagna e un <code>eventType</code> pari a <code>VIEW</code> o <code>CLICK</code>. Invia un <code>VIEW</code> nel momento in cui una campagna diventa visibile e un <code>CLICK</code> quando lutente la tocca.

async function reportEvent(idAdCampaign, eventType) {
  await fetch("https://adv.api.waithero.com/api/v1/events", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": process.env.CAMPAIGNS_API_KEY,
    },
    body: JSON.stringify({ idAdCampaign, eventType }),
  })
}

// When the ad is shown:
reportEvent(42, "VIEW")
// When the user taps it:
reportEvent(42, "CLICK")

Corpo della richiesta

CampoTipoObbligatorioDescrizione
idAdCampaignintegerID della campagna a cui si riferisce l'evento (l'id ottenuto dalla ricerca).
eventType"VIEW" | "CLICK"L'evento che stai registrando.
idUserintegerNoID facoltativo dell'utente finale, per l'attribuzione.

Una chiamata riuscita restituisce l'evento registrato con l<code>amount</code> addebitato e il nuovo <code>remainingBudget</code> della campagna.

Risposta

CampoTipoDescrizione
idintegerID dell'evento registrato.
idAdCampaignintegerID della campagna a cui si riferisce l'evento (l'id ottenuto dalla ricerca).
eventType"VIEW" | "CLICK"L'evento che stai registrando.
amountnumberImporto addebitato alla campagna per questo evento.
remainingBudgetnumberBudget residuo della campagna dopo l'addebito.

Inviare più campagne insieme

Se hai mostrato più campagne in una sola vista, inviale insieme con POST /api/v1/events/batch — stesso eventType e un elenco di ID campagna.

curl -X POST "https://adv.api.waithero.com/api/v1/events/batch" \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{ "idAdCampaigns": [42, 43], "eventType": "VIEW" }'

Corpo della richiesta batch

CampoTipoObbligatorioDescrizione
idAdCampaignsinteger[]ID delle campagne per cui registrare lo stesso evento. Almeno uno.
eventType"VIEW" | "CLICK"L'evento che stai registrando.
idUserintegerNoID facoltativo dell'utente finale, per l'attribuzione.

Risposta batch — ogni elemento di results[]

CampoTipoDescrizione
idAdCampaignintegerLa campagna a cui si riferisce questo risultato. Sempre presente.
status"ok" | "error"ok se l'evento è stato registrato per quella campagna, altrimenti error.
idintegerID dell'evento registrato. Presente solo quando status è ok.
eventType"VIEW" | "CLICK"L'evento registrato. Presente solo quando status è ok.
amountnumberImporto addebitato alla campagna. Presente solo quando status è ok.
remainingBudgetnumberIl budget residuo della campagna dopo l'addebito. Presente solo quando status è ok.
messagestringDettaglio dell'errore quando status è error.
L'endpoint batch restituisce un array results per campagna; ogni voce è ok o error, così una campagna in errore non blocca mai le altre.

Esempio completo

Ecco l'intero ciclo di vita in un unico posto: risolvi le categorie che servi, recupera le campagne corrispondenti, segnala un VIEW quando ciascuna viene mostrata e un CLICK quando viene toccata.

Passo 1

Risolvi le categorie

Elenca le categorie di ristoranti e raccogli gli id che vuoi targetizzare.

Passo 2

Recupera le campagne

Cerca con quegli ID di categoria, la posizione e i prodotti in vista.

Passo 3

Segnala VIEW

Invia un VIEW per ogni campagna nel momento in cui diventa visibile.

Passo 4

Segnala CLICK

Invia un CLICK quando l'utente tocca una campagna.

const BASE = "https://adv.api.waithero.com/api/v1"
const headers = {
  "Content-Type": "application/json",
  "x-api-key": process.env.CAMPAIGNS_API_KEY,
}

// 1. Resolve the category IDs you want to target.
const categories = await fetch(
  `${BASE}/restaurant-categories?search=pizza`,
  { headers },
).then((r) => r.json())
const restaurantCategoryIds = categories.data.map((c) => c.id)

// 2. Ask which campaigns to show for this context.
const { campaigns } = await fetch(`${BASE}/client/campaigns/search`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    restaurantCategoryIds,
    areaLat: 45.4642,
    areaLon: 9.19,
    products: [
      { referenceId: "sku-123", productName: "Margherita", categoryName: "Pizza" },
    ],
  }),
}).then((r) => r.json())

// 3. Render each campaign, then report a VIEW when it becomes visible.
for (const campaign of campaigns) {
  render(campaign) // your UI — place it using campaign.matchedProductReferenceIds
  await fetch(`${BASE}/events`, {
    method: "POST",
    headers,
    body: JSON.stringify({ idAdCampaign: campaign.id, eventType: "VIEW" }),
  })
}

// 4. Report a CLICK when the user taps a campaign.
function onCampaignClick(campaign) {
  return fetch(`${BASE}/events`, {
    method: "POST",
    headers,
    body: JSON.stringify({ idAdCampaign: campaign.id, eventType: "CLICK" }),
  })
}
Usa matchedProductReferenceIds per posizionare ogni campagna accanto al prodotto esatto che ha corrisposto, e conserva ogni campaign.id: è l<code>idAdCampaign</code> con cui segnali gli eventi.

Errori e budget

La registrazione di un evento addebita il budget residuo della campagna (il suo costPerView o costPerClick). Quando il budget di una campagna raggiunge zero, viene terminata automaticamente e smette di comparire nelle ricerche successive. Gestisci queste risposte quando invii gli eventi:

StatoSignificato
400La campagna non è attiva, non è ancora iniziata, è terminata oppure il tipo di evento non è supportato.
401Chiave API mancante o non valida.
402Budget residuo insufficiente — la campagna non può più essere addebitata.
404Campagna non trovata.