API di importazione: importare dati tramite POST e PUT

POST · PUT · Importa · Upsert

API di importazione

Importa i dati nel negozio. A partire dalla versione 2.1.0 con separazione dei metodi HTTP RESTful: POST per i nuovi record, PUT per gli aggiornamenti.

POST /Import/JSON/{entity}  # INSERT
PUT  /Import/JSON/{entity}  # UPDATE

Metodi HTTP (v2.1.0)

Semantica RESTful per una chiara definizione degli intenti:

POST = INSERT

Creare nuovi record

  • Crea solo nuovi record
  • ID = 0 o nessun ID → assegnazione automatica dell'ID
  • ID > 0 già esistente → 409 Conflict
  • Esempio: creare una nuova categoria
POST /Import/JSON/categories
{"data": [{"categories_id": 0, ...}]}

PUT = UPDATE

Aggiornare i record esistenti

  • Aggiorna solo i record esistenti
  • L'ID deve esistere, altrimenti → 404 Not Found
  • Non è possibile creare nuovi record
  • Esempio: modificare la descrizione della categoria
PUT /Import/JSON/categories
{"data": [{"categories_id": 142, ...}]}
Migrazione dalla v2.0.x: il precedente comportamento POST (Upsert) non è più supportato. Suddividete le vostre importazioni in INSERT (POST) e UPDATE (PUT).

Endpoint disponibili

Entità POST (INSERT) PUT (UPDATE) Ambito
Prodotti POST /Import/JSON/products PUT /Import/JSON/products products:write
Categorie POST /Import/JSON/categories PUT /Import/JSON/categories categories:write
Clienti POST /Import/JSON/customers PUT /Import/JSON/customers customers:write
Produttori POST /Import/JSON/manufacturers PUT /Import/JSON/manufacturers manufacturers:write
Articoli di attualità POST /Import/JSON/newsdesks PUT /Import/JSON/newsdesks news:write
Categorie di notizie POST /Import/JSON/newsdeskcats PUT /Import/JSON/newsdeskcats newscategories:write
Domande frequenti POST /Import/JSON/faq PUT /Import/JSON/faq faq:write
Categorie delle FAQ POST /Import/JSON/faqcats PUT /Import/JSON/faqcats faqcategories:write
Cronologia SEO POST /Import/JSON/seohistory PUT /Import/JSON/seohistory seohistory:write
Contratti POST /Import/JSON/contracts PUT /Import/JSON/contracts contracts:write
Abbonati alla newsletter POST /Import/JSON/newsletter_subscribers PUT /Import/JSON/newsletter_subscribers newsletter:write
Campagne newsletter POST /Import/JSON/newsletters PUT /Import/JSON/newsletters newsletters:write
Ticket v2.60.0 POST /Import/JSON/tickets PUT /Import/JSON/tickets tickets:write
Ordini NUOVO v2.20.0 POST /Import/JSON/orders PUT /Import/JSON/orders orders:write
Media NOVITÀ v2.16.0POST /Import/JSON/mediaPUT /Import/JSON/media/{id}media:write
Slider NOVITÀ v2.22.0 POST /Import/JSON/slider PUT /Import/JSON/slider slider:write
Progetti (xoCRM) NOVITÀ v2.35.0 POST /Import/JSON/projects PUT /Import/JSON/projects projects:write
Attività di progetto NOVITÀ v2.35.0 POST /Import/JSON/project_tasks PUT /Import/JSON/project_tasks project_tasks:write
Appuntamenti (xoCRM) NOVITÀ v2.44.0 POST /Import/JSON/appointments PUT /Import/JSON/appointments appointments:write
Caratteristiche e valori delle opzioni NOVITÀ v2.96.0 POST /Import/JSON/products_options PUT /Import/JSON/products_options products:write
Profili tempi di consegna NOVITÀ v2.94.0 POST /Import/JSON/shipping_profiles PUT /Import/JSON/shipping_profiles products:write
Reparti ticket NOVITÀ v2.103.0 POST /Import/JSON/ticket_departments PUT /Import/JSON/ticket_departments tickets:write
Impostazioni NUOVO v2.108.0 – PUT /Import/JSON/configuration configuration:write
Importazione CSV/XLS NOVITÀ v2.109.0 – PUT /Import/JSON/porters porters:write
Richieste relative ai ticket NUOVO v2.118.0 – PUT /Import/JSON/ticket_claims tickets:write
Modificato nella v2.122.13: nuovo avviso CONTRACT_STATUS_UNKNOWN nell’importazione dei contratti (POST/PUT /Import/JSON/contracts): se status.status_id o contracts_status_id non è uno stato contrattuale del negozio o non è un numero intero, oppure se nell’oggetto status manca status_id, l’importazione salva il record senza lo stato. Viene conteggiato come inserted o updated; l’avviso si trova in warnings[] e nella voce in records[], con field e contracts_id. Anche PUT /Import/JSON/contracts/{id}/status ora verifica lo stato del contratto: un ID sconosciuto o non intero genera un errore HTTP 400 INVALID_STATUS, mentre error.valid_status_ids elenca gli ID validi. Fino alla versione v2.122.12 entrambi i metodi verificavano lo stato dell’ordine; l’importazione ignorava silenziosamente uno stato rifiutato e contracts_status_id veniva scritto senza verifica. Dettagli alla voce Contracts.
Modificato nella v2.122.11:
  • POST/PUT /Import/JSON/customers il blocco della newsletter viene scritto su customers_nl_lock (0/1, true/false, "0"/"1"). 1 cancella ogni abbonamento dell’account, sia nel negozio che presso il fornitore della newsletter; anche un’esportazione restituita con 1 comporta la cancellazione. Finora il campo era UNKNOWN_FIELD. Dettagli alla voce Customers.
  • Caricamento dei media: un campo con un solo valore in formato elenco genera un errore HTTP 400 INVALID_FIELD_VALUE; gli errori di caricamento come multipart/form-data si verificano anche in caso di UTF-8 non valido con tronco (sezione «Caricamento dei media» più in basso).
Novità nella v2.57.0: POST/PUT /Import/JSON/customers ora gestisce gli sconti per i clienti tramite il campo discounts[] (sconto percentuale per categoria; category_id: 0 insieme alle sottocategorie = assortimento completo; flag opzionali subcategories, qpb, option, special). Semantica «replace-on-present»: se il campo è presente, l’intero elenco degli sconti del cliente viene sostituito; un array vuoto [] rimuove tutti gli sconti; se il campo manca, gli sconti rimangono invariati. Inoltre, customers_id_extern (numero cliente WaWi) e customers_group_id (vengono convalidati rispetto ai gruppi di clienti esistenti) sono modificabili, e PUT può indirizzare il cliente anche senza e-mail tramite customers_id o customers_id_extern (pura gestione degli sconti/dati anagrafici) – il metodo POST richiede comunque e-mail + indirizzo.
Novità nella v2.57.0: POST/PUT /Import/JSON/products scrive prezzi personalizzati per cliente tramite il nuovo campo customers_prices[]: cliente tramite customers_id o customers_id_extern (numero cliente WaWi), Partial Upsert (vengono scritte solo le colonne inviate, ad es. customers_price, prezzi scalari products_price1..12 + products_price1..12_qty, products_qty_blocks, products_min_qty, products_max_qty), cancellazione tramite "_action": "delete", sincronizzazione completa tramite customers_prices_replace: true (insieme a customers_prices: [] vuoto, vengono rimossi tutti i prezzi del prodotto per i clienti). L’esportazione dei prodotti fornisce customers_prices compatibile con il round-trip.
Novità nella v2.24.0: PUT /Import/JSON/tickets ora può inviare non solo commenti interni, ma anche risposte pubbliche ai clienti. Due flag booleani opzionali per ogni articolo: is_public_reply: true rende pubblica la voce della cronologia (visibile nel frontend), notify_customer: true invia inoltre un’e-mail al cliente tramite il template $mail_ticket_update_with_content(analogo al percorso di risposta nel backend). Entrambi i flag devono essere rigorosamente booleani true — tutti gli altri valori vengono ignorati. A partire dalla v2.25.0: in caso di is_public_reply: true viene automaticamente allegata la firma dell’amministratore memorizzata nel backend (voce della cronologia + e-mail) — Disattivazione tramite append_signature: false. In ticket_comments non inviare quindi più una firma propria. admin_id > 0 è obbligatorio per le risposte pubbliche. Il comportamento predefinito (senza flag) rimane interno + senza e-mail — retrocompatibile con tutti i client v2.11.0+. A partire dalla v2.60.0: POST/PUT /Import/JSON/tickets accetta anche attachments[] (name + content_base64) — Gli allegati dei ticket non sono quindi più solo leggibili, ma anche modificabili. I file vengono allegati alla voce della cronologia: senza ticket_comments nella stessa richiesta, vengono rifiutati con un messaggio di errore.
Novità nella v2.20.0: POST/PUT /Import/JSON/orders consente di creare e aggiornare gli ordini. L’ordine viene generato secondo le stesse regole delle importazioni dal marketplace; le giacenze di magazzino vengono registrate al momento della creazione (Best-Effort).
Novità nella v2.17.0: PUT /Import/JSON/products consente di gestire le offerte speciali relative ai prodotti tramite specials[]: più gruppi di clienti, prezzo speciale netto o discount_percent, periodo di validità, stato, denominazione dell’offerta e cancellazione per ciascun gruppo.

Formato della richiesta

L’importazione richiede un array data contenente gli oggetti da importare:

{
  "type": "categories",
  "data": [
    {
      "categories_id": 142,
      "parent_id": 110,
      "status": 1,
      "languages": {
        "de": {
          "categories_name": "Produktattribute",
          "categories_description": "<p>Beschreibung als HTML-String...</p>"
        }
      }
    }
  ]
}

Parametri di query v2.122.10

Questi parametri di richiesta sono validi per ogni endpoint di importazione (POST e PUT /Import/JSON/…); l'importazione non prevede filtri specifici per ciascun endpoint.

Parametri Valori Effetto
_strict true oppure false Con true, un campo sconosciuto interrompe l’intera richiesta con un codice HTTP 400 UNKNOWN_FIELD prima che venga scritto alcunché. Senza il parametro o con false, l’importazione segnala i campi sconosciuti come avviso e scrive gli altri.
language un singolo valore (solo la regola dell’elenco) Imposta la lingua di sessione dell’importazione, ad esempio per la ricerca dei nomi delle caratteristiche. Il negozio accetta solo il codice esatto di una lingua del backend e, in caso contrario, ricade silenziosamente sulla sua lingua predefinita, solitamente la stessa che era stata indicata. Si verifica quindi solo che non venga fornito alcun valore dell’elenco.
  • La modalità rigorosa viene attivata solo da true; a partire dalla v2.122.12 gli spazi vuoti ai margini non vengono conteggiati (?_strict=%20true viene considerato come true). Nei negozi precedenti alla versione 4.9.173, inviare il valore senza spazi ai margini. Altri valori di _strict (ad esempio 1 o yes) e qualsiasi valore dell’elenco (?language[]=de, ?_strict[]=true) generano un errore HTTP 400 INVALID_FILTER_VALUE con il messaggio details[] (“… Nothing was written”). La verifica avviene prima che l’importazione legga o scriva qualcosa.
  • Un valore vuoto significa «non impostato»; a partire dalla versione v2.122.12, in _strict è sufficiente anche uno spazio vuoto: l’importazione funziona quindi come se il parametro non fosse presente. L’importazione continua a ignorare silenziosamente i nomi di parametri sconosciuti.
Modificato nella v2.122.10: Fino alla v2.122.9, ?_strict=1 ometteva silenziosamente la modalità rigorosa: il chiamante si aspettava un «tutto o niente», ma l’importazione scriveva in parte nonostante i campi sconosciuti. ?language[]=de terminava con un errore HTTP 500. Ora entrambi generano un errore HTTP 400 e non viene scritto nulla.
Modificato nella v2.122.12: fino alla v2.122.11, lo shop verificava _strict senza spazi ai margini, ma l’importazione leggeva comunque il valore non troncato. ?_strict=%20true o ?_strict=true%0A superavano la verifica e disattivavano comunque silenziosamente la modalità rigorosa: l’importazione scriveva parzialmente nonostante i campi sconosciuti. Ora viene applicato il valore verificato e la modalità rigorosa è attiva.

Formato della risposta (v2.1.0)

La risposta contiene un array records con lo stato di ogni record:

{
  "success": true,
  "api_version": "2.1.0",
  "type": "categories",
  "stats": {
    "total": 2,
    "inserted": 1,
    "updated": 1,
    "failed": 0
  },
  "errors": [],
  "timestamp": "2026-01-27 14:00:00",
  "records": [
    {"index": 1, "categories_id": 144, "status": "inserted"},
    {"index": 2, "categories_id": 142, "status": "updated"}
  ]
}

Risposte di errore

409 Conflitto (POST)

L'ID esiste già:

{"success":false,"error":{"code":"ALREADY_EXISTS"}}
404 Pagina non trovata (PUT)

L'ID non esiste:

{"success":false,"error":{"code":"NOT_FOUND"}}

Campi aggiuntivi del prodotto (extra_fields)

I campi aggiuntivi di un prodotto vengono inseriti esclusivamente tramite il campo extra_fields – non come campo al livello superiore del prodotto. Una chiave come google_product_category, posizionata direttamente accanto a products_model, viene ignorata (e segnalata nella risposta come UNKNOWN_FIELD ).

A partire dalla versione 2.51.0, sono accettate tutte le seguenti forme di scrittura – a scelta tramite l’ID del campo o il nome del campo (si tollera l’uso di maiuscole/minuscole e spazi):

{
  "type": "products",
  "data": [{
    "products_model": "ABC-123",

    "extra_fields": {
      "Google Produktkategorie (xoPort)": "1234",
      "Google Zustand (xoPort)": "new",
      "Google Verfügbarkeit (xoPort)": "in stock"
    }

    // ... alternativ nach Feld-ID:
    // "extra_fields": {"7": "1234", "8": "new", "9": "in stock"}

    // ... oder als Liste:
    // "extra_fields": [{"products_extra_fields_id": 7, "products_extra_fields_value": "1234"}]
  }]
}

Anche la struttura restituita dall’esportazione JSON (oggetto, indicizzato in base all’ID del campo) può essere reimportata senza modifiche.

Campi di Google Shopping

I campi del feed di Google sono campi aggiuntivi standard con ID fissi:

ID campo Nome campo (predefinito) Feed Google
7 Categoria prodotto Google (xoPort) g:google_product_category
8 Stato Google (xoPort) g:condition
9 Disponibilità Google (xoPort) g:availability
Nota: una stringa vuota "" svuota il campo, mentre null significa «non incluso» (il campo rimane invariato). Un nome di campo che non esiste nel negozio viene segnalato nella risposta come avviso EXTRA_FIELD invece di essere ignorato.

Schede prodotto (tabs)

Le schede del prodotto (ad es. «Descrizione delle prestazioni», «Scheda tecnica», «Istruzioni di montaggio») vengono gestite tramite il campo tabs – un array piatto di oggetti scheda. Un formato con nidificazione di linguaggi come {"de": […]} non è supportato e viene ignorato senza generare alcun messaggio di errore (nessun avviso, nessuna scheda).

{
  "type": "products",
  "data": [{
    "products_model": "ABC-123",

    "tabs": [
      {
        "tab_name": "Leistungsbeschreibung",
        "tab_content": "<h3>Leistungsbeschreibung</h3><p>...</p>",
        "language_id": 2,
        "sort_order": 0
      },
      {
        "tab_name": "Service description",
        "tab_content": "<h3>Service description</h3><p>...</p>",
        "language_id": 1,
        "sort_order": 0
      }
    ]
  }]
}
Campo Obbligatorio Descrizione
tab_name ✓ (in caso di nuova creazione) Titolo della scheda, così come viene visualizzato sul prodotto. Se non si inserisce tab_name, la voce viene saltata.
tab_content – Contenuto HTML della scheda.
language_id – Lingua della voce (1 = inglese, 2 = tedesco). Se non specificata, viene utilizzata la lingua predefinita del negozio.
sort_order – Ordinamento della scheda in base al prodotto (impostazione predefinita 0).
tab_id – Solo per gli aggiornamenti: aggiorna tab_name/tab_content di una scheda esistente nella lingua specificata.
Comportamento in dettaglio
  • Nuova creazione: ogni inserimento senza tab_id crea una nuova scheda con la propria tab_id – anche per ogni lingua. Le schede multilingue vengono quindi inviate come un inserimento per ogni lingua (vedi esempio sopra); il frontend mostra, filtrato per lingua, solo le schede della lingua attiva.
  • tab_id Aggiornamento: con tab_id, la scheda esistente nella lingua specificata viene aggiornata – ma solo se la riga della lingua esiste già. Una versione linguistica mancante di una scheda esistente non può essere creata successivamente tramite API (a tal fine, inviare una nuova scheda senza ).
  • Nessuna eliminazione in caso di assenza: le schede non inviate rimangono invariate. L’eliminazione delle schede avviene nel backend a livello di prodotto.
  • Export-Round-Trip: GET /products fornisce le schede come oggetto con chiave tab_id (inclusi tab_name, tab_content, language_id, sort_order).
Attenzione: il campo si chiama tab_name/tab_content – non tabs_title/tabs_content. Le voci con ortografia errata o con una struttura linguistica mista vengono scartate senza alcun avviso.

Stock per magazzino (storages)

Le giacenze, le scorte di sicurezza e le scorte di segnalazione per ciascun magazzino vengono gestite nell’importazione dei prodotti esclusivamente tramite il campo storages – un array piatto di oggetti magazzino, ciascuno con storages_id (alias warehouse_id). A partire dalla v2.45.0 si tratta di un aggiornamento parziale: un payload parziale come {"storages_id": 3, "products_reorder_level": 10} modifica solo il livello di segnalazione di quel magazzino e non imposta le altre colonne del magazzino su 0.

È necessario che il sistema di magazzino avanzato sia attivato (EXTENDED_STORAGE_SYSTEM); in caso contrario, il campo viene ignorato e la risposta contiene l’avviso STORAGE_SYSTEM_DISABLED. Tutti i campi, la semantica e le insidie sono descritti nella sezione «Scorte e livelli di segnalazione per magazzino» della guida di riferimento Products.

Attenzione: products_reorder_level e products_safe_quantity al livello più alto del prodotto riguardano il magazzino principale (la tabella products), non un magazzino da storages – e sono importabili solo a partire dalla v2.63.0; in precedenza venivano scartati come campi sconosciuti. storages_id: 1 è già il primo magazzino aggiuntivo, non il magazzino principale. Una voce di magazzino senza storages_id viene ignorata senza alcun avviso: l’importazione segnala quindi il successo, sebbene non sia stato scritto nulla.

Caricamento dei file multimediali: campi con un solo valore v2.122.11

Per POST /Import/JSON/media e PUT /Import/JSON/media/{id} (ambito media:write), come multipart/form-data e con JSON troncato: un campo che contiene esattamente un valore, sotto forma di elenco (campo del modulo entity_type[]=… o entity_type[key]=…, nel JSON troncato un array o un oggetto, anche vuoto) genera un errore HTTP 400 INVALID_FIELD_VALUE con field. Non viene letto né scritto nulla.

{
  "success": false,
  "api_version": "2.122.11",
  "error": {
    "code": "INVALID_FIELD_VALUE",
    "message": "Field \"entity_id\" must be a single value, not a list (entity_id[]=… or entity_id[key]=…; in a JSON body an array or object). Send the field once.",
    "field": "entity_id"
  }
}
Campi fino alla v2.122.10 come elenco
entity_type, file_name, file_field HTTP 500 INTERNAL_ERROR
entity_id, media_id, sort, options_id letto come 1: entity_id[]=5 ha allegato l'immagine all'articolo 1
overwrite, is_main un elenco non vuoto si chiamava «sì», così come overwrite[]=0
subfolder, così come target_folder, se tutte le cartelle sono condivise il testo «Array»: l’immagine è finita nella cartella array
file_url, file_base64 fuorviante INVALID_URL ovvero INVALID_BASE64
nel campo del file stesso (più file con lo stesso nome, file[]=…) HTTP 413 o 400 UPLOAD_INI_SIZE
in una voce di descriptions: language_code, language_id, title language_code: HTTP 500 solo dopo la scrittura (su POST l’immagine era già presente, su PUT i titoli mancavano in seguito); title: il testo «Array»; language_id: scritto nella lingua 1
  • Più file comportano più richieste; l’endpoint accetta esattamente un file.
  • Un campo in una voce di descriptions viene denominato in field con la sua voce, ad esempio descriptions[0].language_code. La verifica avviene prima della prima scrittura.
  • Invariato: descriptions stesso è un elenco per ogni lingua; i campi sconosciuti vengono ignorati come in precedenza.
  • Gli errori che ripetono un campo del modulo (entity_type in UNSUPPORTED_ENTITY_TYPE, target_folder, subfolder, file_url) venivano restituiti senza corpo in caso di UTF-8 non valido (HTTP 400, 403 o 502). Ora vengono restituiti con il corpo, con caratteri illeggibili come U+FFFD. Come in precedenza, non viene scritto nulla.

Avvia l’importazione

Creare un client OAuth2 con scope ":write" per le risorse desiderate.

Crea un client OAuth2

Esportazione dei dati con l’API xoPort

Gli endpoint di esportazione dell’API xoPort consentono l’accesso in lettura a tutti i dati rilevanti del negozio online. Che si tratti di sincronizzazione con sistemi ERP, creazione di backup o analisi di business intelligence, l’API di esportazione fornisce dati JSON coerenti e strutturati.

Ottimizzazione delle prestazioni

  • Utilizzate le esportazioni singole (/product/{id}) per query mirate
  • Utilizzate i parametri di filtro per ridurre il volume dei dati
  • Implementate la cache lato client
  • Per grandi volumi di dati: sincronizzazione incrementale basata su last_modified