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 aggiungonometa(paginazione). Gli errori hanno corpo{ "error": "messaggio" }con eventualedetailssugli 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
| Campo | Tipo | Richiesto | Descrizione |
|---|---|---|---|
| buyerName | string | OBBLIGATORIO | Nome e cognome dell'acquirente |
| total | number | OBBLIGATORIO | Importo totale (positivo) |
| items | array | OBBLIGATORIO | Minimo 1 articolo |
| shippingAddress | object | OBBLIGATORIO | Indirizzo di spedizione |
| externalId | string | opzionale | ID ordine nel tuo sistema. Se omesso viene generato automaticamente |
| buyerEmail | string | opzionale | Email dell'acquirente |
| buyerUsername | string | opzionale | Username/nickname dell'acquirente sul canale originale |
| paymentStatus | string (enum) | opzionale | Stato pagamento. Valori: PENDING, PAID, FAILED, REFUNDED. Default: PAID |
| orderDate | string ISO8601 | opzionale | Data ordine. Default: ora corrente |
| shipByDate | string ISO8601 | opzionale | Data entro cui spedire l'intero ordine |
| currency | string 3 char | opzionale | Codice valuta ISO 4217. Default: EUR |
Campi — items[]
| Campo | Tipo | Richiesto | Descrizione |
|---|---|---|---|
| title | string | OBBLIGATORIO | Nome del prodotto |
| quantity | integer | OBBLIGATORIO | Quantità (minimo 1) |
| unitPrice | number | OBBLIGATORIO | Prezzo unitario (>= 0) |
| sku | string | opzionale | Codice SKU del prodotto |
| imageUrl | string URL | opzionale | URL immagine del prodotto (deve usare https://) |
| shipByDate | string ISO8601 | opzionale | Data spedizione prevista per questo articolo |
Campi — shippingAddress
| Campo | Tipo | Richiesto | Descrizione |
|---|---|---|---|
| recipientName | string | OBBLIGATORIO | Nome del destinatario |
| address1 | string | OBBLIGATORIO | Indirizzo principale |
| city | string | OBBLIGATORIO | Città |
| postalCode | string | OBBLIGATORIO | CAP |
| countryCode | string 2 char | OBBLIGATORIO | Codice paese ISO 3166-1 alpha-2 (es. IT) |
| address2 | string | opzionale | Indirizzo secondario (interno, scala, ecc.) |
| province | string | opzionale | Provincia o stato |
| phone | string | opzionale | Telefono del destinatario |
| string | opzionale | Email 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
| Campo | Tipo | Richiesto | Descrizione |
|---|---|---|---|
| carrierIntegrationId | integer | OBBLIGATORIO | ID corriere collegato (vedi GET /carrier-integrations). Deve appartenere al merchant |
| recipientName | string | OBBLIGATORIO | Nome destinatario |
| addressLine1 | string | OBBLIGATORIO | Indirizzo |
| city | string | OBBLIGATORIO | Città |
| postalCode | string | OBBLIGATORIO | CAP |
| countryCode | string 2 char | OBBLIGATORIO | Codice paese ISO (es. IT) |
| addressLine2 | string | opzionale | Indirizzo secondario |
| state | string | opzionale | Provincia/stato |
| country | string | opzionale | Nome paese esteso |
| phoneNumber | string | opzionale | Telefono destinatario |
| recipientEmail | string | opzionale | Email destinatario |
| trackingNumber | string | opzionale | Per corrieri a tracking esterno |
| orderId | integer | opzionale | Collega 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
| Campo | Tipo | Richiesto | Descrizione |
|---|---|---|---|
| sku | string | OBBLIGATORIO | Unico per merchant (409 se duplicato) |
| name | string | OBBLIGATORIO | Max 500 caratteri |
| price | number | OBBLIGATORIO | >= 0 |
| handle | string | opzionale | Generato dal nome se assente |
| description | string | opzionale | HTML, sanificato server-side, max 50000 |
| brand / mpn / conditionId | string | opzionale | Campi dedicati |
| ean | string | opzionale | 8/12/13/14 cifre |
| categoryId | string | opzionale | ID (slug) categoria prodotto - vedi GET /categories |
| tags | string[] | opzionale | Max 50 |
| itemSpecifics | object | opzionale | Valori string o string[] |
| images | array | opzionale | Max 20. { url (https), isExternal?, altText?, isPrimary? } |
| inventory | array | opzionale | Max 50. { locationId, quantity } stock iniziale per sede (solo prodotto semplice) |
| options | array | opzionale | Max 5. Assi variante { name, values[] }. Da fornire insieme a variants |
| variants | array | opzionale | Max 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.
| Codice | Significato |
|---|---|
| 400 | Bad Request – Dati o parametri non validi |
| 401 | Unauthorized – Chiave API mancante, invalida o scaduta |
| 403 | Forbidden – Scope insufficiente |
| 404 | Not Found – Risorsa inesistente o di altro merchant |
| 409 | Conflict – Conflitto di stato (es. SKU duplicato, spedizione già processata) |
| 422 | Unprocessable Entity – Operazione non valida per lo stato della risorsa |
| 429 | Too Many Requests – Rate limit superato (header Retry-After) |
| 500 | Server 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