Documentazione API

Integra le funzionalità di Shippidu direttamente nel tuo sistema.

Introduzione

Le API REST di Shippidu permettono di gestire ordini, spedizioni, prodotti e corrieri in modo programmatico. Tutte le risposte sono nel formato standard JSON.

Base URL: https://admin.shippidu.com/api/v1
Encoding: UTF-8
Format: application/json

Autenticazione

L'autenticazione avviene tramite Bearer Token nell'header della richiesta (in alternativa l'header x-api-key). Le chiavi si generano da Impostazioni → API registrando una App: gli scope sono definiti a livello di App e condivisi da tutte le sue chiavi.

Authorization: Bearer shpd_sk_xxxxxxxxxxxxxxxx

Convenzioni

  • Le risposte di successo hanno corpo { "data": ... }; le liste aggiungono meta (paginazione). Gli errori hanno corpo { "error": "messaggio" } con eventuale details sugli errori di validazione.
  • Gli stati (ordini, pagamenti, spedizioni, pubblicazioni) sono enum in maiuscolo inglese (es. TO_SHIP, PAID). Sono i valori da inviare e che riceverai nelle risposte; la traduzione in italiano avviene solo nell'interfaccia.
  • I prezzi sono numeri in richiesta (14.90) e stringhe decimali in risposta ("14.90").
  • Le date sono in formato ISO 8601.
  • Ogni mutazione viene registrata nell'audit log dell'App.

Oggetto Order

Forma dell'oggetto restituito dagli endpoint ordine. L'indirizzo di spedizione è incluso in shippingAddressDetails.

{
  "id": 1042,
  "externalId": "ORD-12345",
  "marketplace": "api",
  "integrationId": null,
  "status": "TO_SHIP",
  "paymentStatus": "PAID",
  "buyerName": "Mario Rossi",
  "buyerEmail": "[email protected]",
  "externalUsername": "mario_rossi_99",
  "total": "49.99",
  "currency": "EUR",
  "itemsCount": 2,
  "carrierIntegrationId": 3,
  "orderDate": "2026-03-09T10:00:00.000Z",
  "shipByDate": "2026-03-11T10:00:00.000Z",
  "createdAt": "2026-03-09T10:00:05.000Z",
  "shippingAddressDetails": {
    "fullName": "Mario Rossi",
    "addressLine1": "Via Roma 1",
    "addressLine2": "Interno 3",
    "city": "Milano",
    "state": "MI",
    "postalCode": "20121",
    "country": "Italia",
    "countryCode": "IT",
    "phoneNumber": "+39 02 1234567",
    "email": "[email protected]"
  }
}

status: TO_SHIP, IN_TRANSIT, SHIPPED, CANCELLED, RETURNED. marketplace indica il canale di origine (api per gli ordini creati via API). externalUsername è il nickname dell'acquirente sul canale (la risposta di creazione lo espone anche come buyerUsername).

POST/api/v1/orders

Crea un nuovo ordine manuale (canale api, stato iniziale TO_SHIP). Il corriere viene assegnato automaticamente se sono configurate regole di spedizione.

Scope richiesto: orders:write

Request

POST https://admin.shippidu.com/api/v1/orders
Authorization: Bearer shpd_sk_your_key
Content-Type: application/json

Body di esempio:

{
  "externalId": "ORD-12345",
  "buyerName": "Mario Rossi",
  "buyerEmail": "[email protected]",
  "buyerUsername": "mario_rossi_99",
  "paymentStatus": "PAID",
  "total": 49.99,
  "currency": "EUR",
  "orderDate": "2026-03-09T10:00:00Z",
  "shipByDate": "2026-03-11T10:00:00Z",
  "items": [
    {
      "title": "Prodotto A",
      "sku": "SKU-001",
      "quantity": 2,
      "unitPrice": 24.99,
      "imageUrl": "https://cdn.esempio.it/prodotto-a.jpg",
      "shipByDate": "2026-03-11T10:00:00Z"
    }
  ],
  "shippingAddress": {
    "recipientName": "Mario Rossi",
    "address1": "Via Roma 1",
    "address2": "Interno 3",
    "city": "Milano",
    "province": "MI",
    "postalCode": "20121",
    "countryCode": "IT",
    "phone": "+39 02 1234567",
    "email": "[email protected]"
  }
}

Campi — radice

CampoTipoRichiestoDescrizione
buyerNamestringOBBLIGATORIONome e cognome dell'acquirente
totalnumberOBBLIGATORIOImporto totale (positivo)
itemsarrayOBBLIGATORIOMinimo 1 articolo
shippingAddressobjectOBBLIGATORIOIndirizzo di spedizione
externalIdstringopzionaleID ordine nel tuo sistema. Se omesso viene generato automaticamente
buyerEmailstringopzionaleEmail dell'acquirente
buyerUsernamestringopzionaleUsername/nickname dell'acquirente sul canale originale
paymentStatusstring (enum)opzionaleStato pagamento. Valori: PENDING, PAID, FAILED, REFUNDED. Default: PAID
orderDatestring ISO8601opzionaleData ordine. Default: ora corrente
shipByDatestring ISO8601opzionaleData entro cui spedire l'intero ordine
currencystring 3 charopzionaleCodice valuta ISO 4217. Default: EUR

Campi — items[]

CampoTipoRichiestoDescrizione
titlestringOBBLIGATORIONome del prodotto
quantityintegerOBBLIGATORIOQuantità (minimo 1)
unitPricenumberOBBLIGATORIOPrezzo unitario (>= 0)
skustringopzionaleCodice SKU del prodotto
imageUrlstring URLopzionaleURL immagine del prodotto (deve usare https://)
shipByDatestring ISO8601opzionaleData spedizione prevista per questo articolo

Campi — shippingAddress

CampoTipoRichiestoDescrizione
recipientNamestringOBBLIGATORIONome del destinatario
address1stringOBBLIGATORIOIndirizzo principale
citystringOBBLIGATORIOCittà
postalCodestringOBBLIGATORIOCAP
countryCodestring 2 charOBBLIGATORIOCodice paese ISO 3166-1 alpha-2 (es. IT)
address2stringopzionaleIndirizzo secondario (interno, scala, ecc.)
provincestringopzionaleProvincia o stato
phonestringopzionaleTelefono del destinatario
emailstringopzionaleEmail del destinatario, usata dai corrieri per le notifiche

Response 201 Created

{
  "data": {
    "id": 1042,
    "externalId": "ORD-12345",
    "marketplace": "api",
    "status": "TO_SHIP",
    "paymentStatus": "PAID",
    "buyerName": "Mario Rossi",
    "buyerEmail": "[email protected]",
    "buyerUsername": "mario_rossi_99",
    "total": "49.99",
    "currency": "EUR",
    "orderDate": "2026-03-09T10:00:00.000Z",
    "shipByDate": "2026-03-11T10:00:00.000Z"
  }
}

buyerUsername — corrisponde al campo externalUsername nel sistema interno.

paymentStatus — se omesso, il valore predefinito è PAID.

GET/api/v1/orders

Recupera una lista paginata di tutti gli ordini del merchant, di qualsiasi canale (eBay, Amazon, Shopify, API, ...), esclusi quelli di integrazioni eliminate.

Scope richiesto: orders:read

Query Parameters

  • page (int) — Numero di pagina (default 1)
  • limit (int) — Elementi per pagina (default 20, max 100)
  • status (string) — Filtra per stato: TO_SHIP, IN_TRANSIT, SHIPPED, CANCELLED, RETURNED
  • from (string ISO8601) — Data ordine minima
  • to (string ISO8601) — Data ordine massima
curl -G https://admin.shippidu.com/api/v1/orders \
  -H "Authorization: Bearer shpd_sk_your_key" \
  -d "limit=10&status=TO_SHIP"

Response 200 OK

{
  "data": [ { "id": 1042, "status": "TO_SHIP", "...": "..." } ],
  "meta": { "page": 1, "limit": 20, "total": 42, "totalPages": 3 }
}

GET/api/v1/orders/:id

Recupera i dettagli completi di un singolo ordine. :id è l'ID numerico interno dell'ordine.

Scope richiesto: orders:read

curl https://admin.shippidu.com/api/v1/orders/1042 \
  -H "Authorization: Bearer shpd_sk_your_key"

PATCH/api/v1/orders/:id

Aggiorna lo stato di un ordine esistente.

Scope richiesto: orders:write

curl -X PATCH https://admin.shippidu.com/api/v1/orders/1042 \
  -H "Authorization: Bearer shpd_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{ "status": "CANCELLED" }'

Valori ammessi: TO_SHIP, IN_TRANSIT, SHIPPED, CANCELLED, RETURNED.

Oggetto Shipment

Forma dell'oggetto restituito dagli endpoint spedizione.

{
  "id": 88,
  "status": "PENDING",
  "trackingNumber": null,
  "carrierIntegrationId": 3,
  "recipientName": "Mario Rossi",
  "addressLine1": "Via Roma 1",
  "addressLine2": null,
  "city": "Milano",
  "state": "MI",
  "postalCode": "20121",
  "country": "Italia",
  "countryCode": "IT",
  "trackingRegistered": false,
  "trackingRegisteredAt": null,
  "trackingRegistrationError": null,
  "shippedAt": null,
  "errorMessage": null,
  "createdAt": "2026-06-12T14:51:49.007Z",
  "updatedAt": "2026-06-12T14:51:49.007Z"
}

status: PENDING, PROCESSING, LABEL_CREATED, ERROR.

POST/api/v1/shipments

Registra una nuova spedizione dai dati del destinatario e dal corriere scelto. Alla creazione è in stato PENDING, senza etichetta: questa viene generata presso il corriere in un secondo passaggio con POST /shipments/:id/process (o in blocco con POST /shipments/bulk-process).

Scope richiesto: shipments:write

{
  "carrierIntegrationId": 3,
  "recipientName": "Mario Rossi",
  "addressLine1": "Via Roma 1",
  "addressLine2": "Interno 3",
  "city": "Milano",
  "state": "MI",
  "postalCode": "20121",
  "country": "Italia",
  "countryCode": "IT",
  "phoneNumber": "+39 02 1234567",
  "recipientEmail": "[email protected]",
  "trackingNumber": null,
  "orderId": 1042
}

Campi

CampoTipoRichiestoDescrizione
carrierIntegrationIdintegerOBBLIGATORIOID corriere collegato (vedi GET /carrier-integrations). Deve appartenere al merchant
recipientNamestringOBBLIGATORIONome destinatario
addressLine1stringOBBLIGATORIOIndirizzo
citystringOBBLIGATORIOCittà
postalCodestringOBBLIGATORIOCAP
countryCodestring 2 charOBBLIGATORIOCodice paese ISO (es. IT)
addressLine2stringopzionaleIndirizzo secondario
statestringopzionaleProvincia/stato
countrystringopzionaleNome paese esteso
phoneNumberstringopzionaleTelefono destinatario
recipientEmailstringopzionaleEmail destinatario
trackingNumberstringopzionalePer corrieri a tracking esterno
orderIdintegeropzionaleCollega un ordine del merchant

Response 201 Created

Restituisce direttamente l'oggetto Shipment (non incapsulato in data).

POST/api/v1/shipments/:id/process

Genera l'etichetta presso il corriere (o registra il tracking). La spedizione passa a LABEL_CREATED o ERROR. Consentito solo da PENDING/ERROR.

Scope richiesto: shipments:write

curl -X POST https://admin.shippidu.com/api/v1/shipments/88/process \
  -H "Authorization: Bearer shpd_sk_your_key"

Restituisce l'oggetto Shipment aggiornato. 409 se già processata, 422 se il corriere restituisce errore.

POST/api/v1/shipments/bulk-process

Accoda fino a 500 spedizioni per il processamento asincrono.

Scope richiesto: shipments:write

{ "shipmentIds": [88, 89, 90] }

Response 202 Accepted

{ "data": { "queued": 3, "skipped": 0, "jobIds": ["..."] } }

GET/api/v1/shipments

Lista paginata delle spedizioni (ordinate dalla più recente).

Scope richiesto: shipments:read

Query Parameters

  • status (string) — Filtra per stato (PENDING, PROCESSING, LABEL_CREATED, ERROR)
  • limit (int) — Elementi (default 50, max 200)
  • offset (int) — Scostamento per la paginazione (default 0)

Response 200 OK

{
  "data": [ { "id": 88, "status": "PENDING", "...": "..." } ],
  "total": 124,
  "limit": 50,
  "offset": 0
}

GET/api/v1/shipments/:id

Dettaglio di una spedizione. Restituisce l'oggetto Shipment.

Scope richiesto: shipments:read

GET/api/v1/shipments/:id/label

Scarica l'etichetta in formato PDF (Content-Type: application/pdf). 404 se l'etichetta non è ancora disponibile.

Scope richiesto: shipments:read

curl https://admin.shippidu.com/api/v1/shipments/88/label \
  -H "Authorization: Bearer shpd_sk_your_key" \
  --output label-88.pdf

GET/api/v1/shipments/:id/tracking-events

Elenco degli eventi di tracking registrati per la spedizione.

Scope richiesto: shipments:read

{ "data": [ { "status": "...", "description": "...", "timestamp": "..." } ], "total": 4 }

PATCH/api/v1/shipments/:id

Modifica destinatario, indirizzo e/o corriere. Consentito solo prima della generazione etichetta (stato PENDING/ERROR), altrimenti 409. Almeno un campo deve essere presente.

Scope richiesto: shipments:write

{ "recipientName": "Luigi Verdi", "carrierIntegrationId": 5 }

DELETE/api/v1/shipments/:id

Annulla la spedizione (cancellazione presso il corriere quando supportata). Se la spedizione è PENDING senza tracking viene eliminata fisicamente.

Scope richiesto: shipments:delete

{ "data": { "shipmentId": 88, "deleted": false, "status": "CANCELLED" } }

POST/api/v1/shipments/:id/close-workday

Chiusura di giornata / manifest per i corrieri che la richiedono (es. GLS, BRT).

Scope richiesto: shipments:write

{ "data": { "shipmentId": 88, "closed": true } }

Oggetto Product

La vista lista è leggera; il dettaglio (GET /products/:id e le risposte di scrittura) include in più options, variants e listings.

{
  "id": 583,
  "sku": "RSDSPIH12PM",
  "handle": "display-lcd-iphone-12-pro-max",
  "name": "DISPLAY LCD PER APPLE IPHONE 12 PRO MAX",
  "description": "<p>HTML sanificato</p>",
  "price": "24.98",
  "brand": "Apple",
  "mpn": null,
  "ean": "8051234567890",
  "conditionId": "new",
  "categoryId": "el-4-8-4-2",
  "tags": ["ricambi"],
  "itemSpecifics": { "Marca": "Apple" },
  "images": [ { "url": "https://...", "altText": null, "displayOrder": 0, "isPrimary": true } ],
  "inventory": [ { "locationId": 8, "quantity": 10 } ],
  "hasVariants": false,
  "options": [ { "name": "Colore", "values": ["Nero", "Bianco"] } ],
  "variants": [ { "id": 519, "sku": "RSDSPIH12PM-1", "price": "24.98", "ean": null, "mpn": null, "optionValues": { "Colore": "Nero" }, "inventory": [ { "locationId": 8, "quantity": 6 } ] } ],
  "listings": [ { "id": 12, "provider": "ebay", "status": "PUBLISHED", "externalItemId": "256...", "quantity": 6, "quantitySold": 2 } ],
  "category": { "id": "el-4-8-4-2", "name": "Custodie cellulari", "fullPath": "Elettronica > ... > Custodie cellulari" },
  "createdAt": "2026-06-12T14:51:49.007Z",
  "updatedAt": "2026-06-12T14:51:49.007Z"
}

inventory è lo stock fisico per sede. I listings sono read-only: status vale DRAFT, PUBLISHED, SYNCING o ERROR, quantity è la quantità esposta sul canale e quantitySold il venduto rilevato dalla sync.

POST/api/v1/products

Crea un prodotto (conteggiato nei limiti del piano).

Scope richiesto: products:write

{
  "sku": "RSDSPIH12PM",
  "name": "Display LCD iPhone 12 Pro Max",
  "price": 24.98,
  "description": "<p>Descrizione HTML</p>",
  "brand": "Apple",
  "ean": "8051234567890",
  "tags": ["ricambi"],
  "itemSpecifics": { "Marca": "Apple" },
  "images": [ { "url": "https://cdn.esempio.it/foto.jpg", "isExternal": false, "isPrimary": true } ],
  "inventory": [ { "locationId": 8, "quantity": 10 } ]
}

Campi principali

CampoTipoRichiestoDescrizione
skustringOBBLIGATORIOUnico per merchant (409 se duplicato)
namestringOBBLIGATORIOMax 500 caratteri
pricenumberOBBLIGATORIO>= 0
handlestringopzionaleGenerato dal nome se assente
descriptionstringopzionaleHTML, sanificato server-side, max 50000
brand / mpn / conditionIdstringopzionaleCampi dedicati
eanstringopzionale8/12/13/14 cifre
categoryIdstringopzionaleID (slug) categoria prodotto - vedi GET /categories
tagsstring[]opzionaleMax 50
itemSpecificsobjectopzionaleValori string o string[]
imagesarrayopzionaleMax 20. { url (https), isExternal?, altText?, isPrimary? }
inventoryarrayopzionaleMax 50. { locationId, quantity } stock iniziale per sede (solo prodotto semplice)
optionsarrayopzionaleMax 5. Assi variante { name, values[] }. Da fornire insieme a variants
variantsarrayopzionaleMax 100. Combinazioni { optionValues, sku?, price?, ean?, mpn?, inventory? }. Richiede options

Le immagini con isExternal: true vengono referenziate per URL; altrimenti scaricate e caricate su storage Shippidu. Risposta: 201 con il dettaglio completo.

Creazione con varianti (stile Shopify productSet)

Fornisci options e variants insieme per creare il prodotto con le sue combinazioni in un'unica chiamata. Vengono create esattamente le combinazioni elencate; lo stock va indicato per variante e l'inventory a livello di prodotto non è ammesso.

{
  "sku": "TSHIRT", "name": "T-Shirt", "price": 19.90,
  "options": [
    { "name": "Colore", "values": ["Nero", "Bianco"] },
    { "name": "Taglia", "values": ["S", "M"] }
  ],
  "variants": [
    { "optionValues": { "Colore": "Nero", "Taglia": "S" },
      "sku": "TSHIRT-NERO-S", "price": 19.90,
      "inventory": [ { "locationId": 8, "quantity": 12 } ] },
    { "optionValues": { "Colore": "Bianco", "Taglia": "M" },
      "sku": "TSHIRT-BIANCO-M",
      "inventory": [ { "locationId": 8, "quantity": 5 } ] }
  ]
}

Regole: entrambi o nessuno dei due campi; ogni optionValues deve valorizzare tutti gli assi; combinazioni duplicate o stock di prodotto insieme alle varianti → 400. Per generare automaticamente tutte le combinazioni (prodotto cartesiano) usa invece PUT /products/:id/options.

GET/api/v1/products

Lista paginata (vista leggera, senza options/variants/listings).

Scope richiesto: products:read

Query Parameters

  • page (int) — Numero di pagina (default 1)
  • limit (int) — Elementi per pagina (default 20, max 100)
  • search (string) — Ricerca su nome e SKU
{ "data": [ ...Product ], "meta": { "page": 1, "limit": 20, "total": 42, "totalPages": 3 } }

GET/api/v1/products/:id

Dettaglio completo con options, variants e listings.

Scope richiesto: products:read

PATCH/api/v1/products/:id

Aggiornamento parziale, stessi campi del create (i campi testuali accettano null per svuotare).

Scope richiesto: products:write

  • images: sostituzione completa della galleria (le immagini non riusate vengono eliminate).
  • inventory: imposta lo stock per sede del prodotto semplice. Se il prodotto ha varianti → 422 (usa l'endpoint variante).

DELETE/api/v1/products/:id

Elimina il prodotto con varianti, stock, pubblicazioni e media non condivisi. Risposta 204.

Scope richiesto: products:delete

PUT/api/v1/products/:id/options

Definisce gli assi del prodotto e rigenera le combinazioni come prodotto cartesiano (max 5 assi, max 100 valori per asse).

Scope richiesto: products:write

{ "options": [ { "name": "Colore", "values": ["Nero", "Bianco"] }, { "name": "Taglia", "values": ["S", "M"] } ] }
  • Le combinazioni esistenti conservano SKU, prezzo e stock; le nuove ereditano il prezzo master e ricevono SKU suffissato (SKU-1, SKU-2...).
  • Conversione semplice → varianti: lo stock del prodotto passa alla prima combinazione.
  • { "options": null } collassa a prodotto semplice: lo stock confluisce sulla variante default.

POST/api/v1/products/:id/variants

Aggiunge una singola combinazione. optionValues deve essere completo e coerente con gli assi.

Scope richiesto: products:write

{ "optionValues": { "Colore": "Rosso", "Taglia": "M" }, "sku": "ABC-ROSSO-M", "price": 19.90 }

Risposta 201 con l'oggetto Variant.

PATCH/api/v1/products/:id/variants/:variantId

Aggiorna i campi variante e lo stock fisico per sede.

Scope richiesto: products:write

{
  "sku": "ABC-ROSSO-M",
  "price": 21.50,
  "ean": null,
  "mpn": null,
  "inventory": [ { "locationId": 8, "quantity": 12 } ]
}

inventory è un upsert per sede (le sedi non incluse restano invariate). Se la variante è esposta su canali live, la quantità non può scendere sotto quanto è esposto → 422.

DELETE/api/v1/products/:id/variants/:variantId

Elimina la combinazione (il suo stock viene rimosso). La variante default non è eliminabile (rimuovi gli assi con PUT /options). Risposta 204.

Scope richiesto: products:write

GET/api/v1/locations

Elenco read-only dei magazzini: serve a scoprire i locationId richiesti da tutte le scritture di stock.

Scope richiesto: locations:read

{ "data": [ { "id": 8, "name": "Magazzino principale", "isDefault": true, "active": true, "city": "Roma", "postalCode": "00100", "country": "IT" } ] }

GET/api/v1/carrier-integrations

Elenco read-only dei corrieri collegati: serve a scoprire il carrierIntegrationId richiesto da POST /shipments. Credenziali e configurazioni non vengono mai esposte.

Scope richiesto: carriers:read

{ "data": [ { "id": 3, "provider": "gls", "connectionName": "GLS Milano", "status": "ACTIVE", "connectedAt": "2026-05-02T09:00:00.000Z" } ] }

GET/api/v1/categories

Lookup read-only sulla tassonomia prodotto interna (Shopify Standard Product Taxonomy, IT): serve a scoprire il categoryId da assegnare a un prodotto. L'id è uno slug testuale (non numerico), ed è il valore da passare in categoryId.

Scope richiesto: products:read

Query Parameters

  • search (string) — Ricerca sul percorso completo (foglie prima), max 50
  • parentId (string) — Figli diretti di un nodo (navigazione albero)

Senza parametri restituisce i nodi radice.

{ "data": [ {
  "id": "el-4-8-4-2",
  "name": "Custodie cellulari",
  "fullPath": "Elettronica > ... > Custodie cellulari",
  "parentId": "el-4-8-4", "level": 4, "isLeaf": false
} ] }

Errori

L'API usa i codici di stato HTTP convenzionali per indicare il successo o il fallimento di una richiesta.

CodiceSignificato
400Bad Request – Dati o parametri non validi
401Unauthorized – Chiave API mancante, invalida o scaduta
403Forbidden – Scope insufficiente
404Not Found – Risorsa inesistente o di altro merchant
409Conflict – Conflitto di stato (es. SKU duplicato, spedizione già processata)
422Unprocessable Entity – Operazione non valida per lo stato della risorsa
429Too Many Requests – Rate limit superato (header Retry-After)
500Server Error – Errore interno

Rate Limits

Per garantire stabilità al servizio si applicano due limiti: 300 richieste / 15 minuti per IP e 1.000 richieste / ora per chiave API. Al superamento l'API risponde con 429 e l'header Retry-After (secondi).

Scopes

Quando registri un'App puoi limitarne l'accesso associando specifici scope, condivisi da tutte le sue chiavi. Scope disponibili:

  • orders:read — Lettura ordini
  • orders:write — Creazione e modifica ordini
  • shipments:read — Lettura spedizioni ed eventi tracking
  • shipments:write — Creazione, processamento e gestione spedizioni
  • shipments:delete — Annullamento ed eliminazione spedizioni
  • products:read — Lettura catalogo prodotti
  • products:write — Creazione e modifica prodotti
  • products:delete — Eliminazione prodotti
  • locations:read — Lettura sedi/magazzini
  • carriers:read — Lettura corrieri collegati