API Reference 📚

Panoramica

L'API di Skuno fornisce endpoints RESTful per l'arricchimento e l'ottimizzazione dei dati dei prodotti. Tutte le richieste devono essere autenticate utilizzando una API Key.

Autenticazione

Il sistema utilizza l'autenticazione tramite API Key.

Header richiesti:

apikey: your_api_key_here
X-Tenant-ID: your_tenant_id  # Opzionale

Nota: l'header X-Tenant-ID è opzionale. Se non fornito, verrà utilizzato automaticamente il tenant associato alla tua API Key.


Endpoints

Batch Processing di Prodotti

POST /functions/v1/products-batch

Nuovo endpoint principale per l'arricchimento di più prodotti contemporaneamente con sistema di callback.

Request Headers

Content-Type: application/json
apikey: your_api_key_here

Request Body

{
  "callback_url": "https://your-domain.com/webhook/skuno",
  "products": [
    {
      "barcode": "8001234567890",
      "brand": "Nike",
      "current_description": "Scarpe da running Air Max",
      "schema_override": {
        "type": "object",
        "properties": {
          "language": "it",
          "product_name": {"type": "string"},
          "description": {"type": "string"},
          "brand": {"type": "string"}
        },
        "required": ["product_name", "brand"]
      }
    },
    {
      "barcode": "8001234567891",
      "brand": "Adidas",
      "current_description": "Maglietta sportiva Climacool",
      "schema_override": {
        "type": "object",
        "properties": {
          "language": "it",
          "product_name": {"type": "string"},
          "description": {"type": "string"},
          "brand": {"type": "string"}
        },
        "required": ["product_name", "brand"]
      }
    }
  ]
}

Response

{
  "success": true,
  "batch_id": "b324448f-e99b-44c5-a9c6-566f880e1aa1",
  "total_products": 2,
  "created": 2,
  "failed": 0,
  "errors": [],
  "message": "Batch processing completed. Created: 2, Failed: 0"
}

Importante: Riceverai una callback separata per ogni prodotto processato al callback_url specificato.

Callback Response

Quando l'arricchimento è completato, viene inviata una richiesta POST al callback_url specificato:

{
  "status": "completed",
  "batch_id": "batch_123",
  "tenant_id": "tenant_456",
  "processed_at": "2024-01-15T10:30:00Z",
  "results": [
    {
      "barcode": "1234567890123",
      "status": "success",
      "enriched_data": {
        "name": "Nome Prodotto Arricchito",
        "description": "Descrizione dettagliata...",
        "features": ["Feature 1", "Feature 2"],
        "category": "Categoria",
        "brand": "Brand"
      },
      "images": [
        {
          "image_url": "https://example.com/image1.jpg",
          "thumbnail_url": "https://example.com/thumb1.jpg",
          "title": "Titolo immagine",
          "source_url": "https://source.com/page",
          "width": 800,
          "height": 600,
          "is_primary": true,
          "relevance_score": 8.5,
          "quality_score": 9.0
        }
      ],
      "image_status": "completed",
      "images_found": 3
    }
  ]
}
Campi Immagini nel Callback
  • images: array delle immagini trovate per il prodotto
  • image_status: stato della ricerca immagini (completed, failed, no_images_found)
  • images_found: numero totale di immagini trovate

Struttura oggetto immagine:

  • image_url: URL dell'immagine originale
  • thumbnail_url: URL della miniatura (se disponibile)
  • title: Titolo/descrizione dell'immagine
  • source_url: URL della pagina sorgente
  • width/height: Dimensioni in pixel
  • is_primary: indica se è l'immagine principale
  • relevance_score: Punteggio di rilevanza (0-10)
  • quality_score: Punteggio di qualità (0-10)

Campo Language Obbligatorio

A partire da questa versione, il campo language è obbligatorio nello schema_override di ogni prodotto. Questo campo specifica la lingua in cui l'AI deve arricchire il prodotto.

Lingue supportate:

  • en - Inglese
  • fr - Francese
  • it - Italiano
  • es - Spagnolo

Esempio di utilizzo:

{
  "schema_override": {
    "type": "object",
    "properties": {
      "language": "fr",
      "product_name": {"type": "string"},
      "description": {"type": "string"},
      "brand": {"type": "string"}
    },
    "required": ["language", "product_name", "brand"]
  }
}

Nota: Se il campo language non viene specificato o contiene un valore non supportato, la richiesta verrà rifiutata con un errore di validazione.


Schema Override

Il campo schema_override permette di personalizzare lo schema di arricchimento per prodotto specifico:

{
  "language": "it",
  "require_images": true,
  "fields": {
    "description": {
      "max_length": 500,
      "style": "marketing"
    },
    "features": {
      "format": "bullet_points",
      "max_items": 5
    }
  }
}

Campi disponibili:

  • language (obbligatorio): Lingua per l'arricchimento (it, en, fr, de, es)
  • require_images (opzionale): richiede ricerca automatica di immagini per il prodotto
  • fields: configurazione specifica per ogni campo
  • style: stile di scrittura (professional, marketing, technical)
  • max_length: lunghezza massima del testo generato
  • format: formato di output (paragraph, bullet_points, list)

Gestione Immagini

La ricerca di immagini viene attivata automaticamente quando:

  • Esplicitamente richiesto: require_images: true
  • Logica predefinita: il prodotto ha brand E descrizione
  • Mai richiesto: require_images: false

Le immagini trovate vengono salvate automaticamente e incluse nella risposta o nel callback.

Formato Oggetto Immagine

Ogni immagine restituita segue questa struttura:

| Campo | Tipo | Descrizione | |-------|------|-------------| | image_url | string | URL dell'immagine a risoluzione originale | | thumbnail_url | string | URL della miniatura (se disponibile) | | title | string | Titolo o descrizione dell'immagine | | is_primary | boolean | true se l'immagine è considerata la principale | | relevance_score | number | Punteggio (0-10) di attinenza al prodotto |

{
  "image_url": "https://storage.skuno.xyz/images/prod_123.jpg",
  "thumbnail_url": "https://storage.skuno.xyz/thumbs/prod_123.jpg",
  "title": "Immagine Prodotto",
  "is_primary": true,
  "relevance_score": 9.5
}

Arricchimento Prodotto Singolo (Legacy)

POST /functions/v1/enrich-products

Request Body

{
  "product": {
    "id": "string",
    "name": "string",
    "description": "string",
    "category": "string",
    "attributes": {
      "additional": "properties"
    }
  }
}

Response

{
  "enriched_product": {
    "id": "string",
    "name": "string",
    "enhanced_description": "string",
    "tags": ["string"],
    "marketing_suggestions": {
      "title_suggestions": ["string"],
      "description_suggestions": ["string"],
      "keywords": ["string"]
    },
    "metadata": {
      "confidence_score": 0.95,
      "processing_time": "string"
    }
  }
}

Gestione Prodotti

Recupera Prodotto

GET /rest/v1/products?id=eq.{product_id}

Response

{
  "id": "string",
  "name": "string",
  "description": "string",
  "enriched_data": {
    "enhanced_description": "string",
    "tags": ["string"],
    "marketing_suggestions": {}
  },
  "created_at": "timestamp",
  "updated_at": "timestamp"
}

Lista Prodotti

GET /rest/v1/products?select=*

Parametri di Query:

  • limit: numero massimo di risultati
  • offset: offset per la paginazione
  • order: campo e direzione dell'ordinamento

Gestione Tag

Genera Tag

POST /functions/v1/generate-tags

Request Body

{
  "description": "string"
}

Response

{
  "tags": ["string"],
  "confidence_scores": {
    "tag1": 0.95,
    "tag2": 0.85
  }
}

Gestione Errori

Codici di Stato

  • 200: Successo
  • 400: Richiesta non valida
  • 401: Non autorizzato
  • 403: Accesso negato
  • 404: Risorsa non trovata
  • 429: Troppe richieste
  • 500: Errore interno del server

Formato Errori

{
  "error": {
    "code": "string",
    "message": "string",
    "details": {}
  }
}

Rate Limiting

Le richieste sono limitate a:

  • 100 richieste/minuto per utente autenticato
  • 10 richieste/minuto per utente anonimo

Headers di risposta:

  • X-RateLimit-Limit: 100
  • X-RateLimit-Remaining: 95
  • X-RateLimit-Reset: 1620000000

Endpoint di Stato

Controllo Stato Prodotto

GET /functions/v1/product-status/{product_id}

Recupera lo stato di arricchimento di un prodotto specifico.

Headers

Authorization: Bearer <JWT_TOKEN>
Content-Type: application/json

Response

{
  "product_id": "uuid",
  "enrichment_status": "completed",
  "created_at": "2023-01-01T12:00:00Z",
  "updated_at": "2023-01-01T12:30:00Z",
  "enriched_fields": {
    "description": "Descrizione arricchita",
    "tags": ["tag1", "tag2"]
  },
  "progress": {
    "total_fields": 10,
    "enriched_fields": 8,
    "percentage": 80
  }
}

Lista Prodotti con Stato

GET /functions/v1/product-status

Recupera la lista dei prodotti con filtri di stato.

Parametri Query:

  • status: Filtra per stato di arricchimento
  • limit: Numero massimo di risultati (default: 50)
  • offset: Offset per paginazione (default: 0)

Response

{
  "products": [
    {
      "id": "uuid",
      "name": "Nome Prodotto",
      "enrichment_status": "completed",
      "created_at": "2023-01-01T12:00:00Z",
      "updated_at": "2023-01-01T12:30:00Z"
    }
  ],
  "pagination": {
    "total": 100,
    "limit": 50,
    "offset": 0,
    "has_more": true
  }
}

Sistema di Notifiche

Invio Notifica

POST /functions/v1/notifications

Invia una notifica immediata o programmata.

Payload

{
  "type": "email",
  "recipient": "user@example.com",
  "subject": "Arricchimento Completato",
  "message": "Il tuo prodotto è stato arricchito con successo",
  "tenant_id": "uuid",
  "priority": "normal",
  "scheduled_at": "2023-01-01T15:00:00Z",
  "data": {
    "product_id": "uuid",
    "enrichment_details": {}
  }
}

Tipi di Notifica

  • email: notifica via email
  • webhook: chiamata webhook

Recupero Notifiche

GET /functions/v1/notifications

Recupera le notifiche inviate.

Parametri Query:

  • tenant_id: ID del tenant (obbligatorio)
  • status: Filtra per stato (sent, failed, pending)
  • type: Filtra per tipo di notifica
  • limit: Numero massimo di risultati
  • offset: Offset per paginazione

Processamento Notifiche

  • POST /functions/v1/process-notifications
    • Processa le notifiche in coda (per uso interno/cron)
  • GET /functions/v1/process-notifications/stats
    • Recupera statistiche delle notifiche delle ultime 24 ore

Webhooks

Eventi Disponibili

  • product.enriched: quando un prodotto è stato arricchito (evento principale)
  • product.failed: quando l'arricchimento di un prodotto fallisce
  • error.processing: quando si verifica un errore durante l'elaborazione

Nota: al momento non esiste un evento batch.completed. Ogni prodotto in un batch genera una callback product.enriched separata.

Formato Payload Webhook

{
  "event": "product.enriched",
  "batch_id": "b324448f-e99b-44c5-a9c6-566f880e1aa1",
  "product_id": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2025-01-25T14:30:00.000Z",
  "product": {
    "barcode": "8001234567890",
    "brand": "Nike",
    "original_description": "Scarpe da running Air Max",
    "enriched_description": "Scarpe da running Nike Air Max con tecnologia di ammortizzazione avanzata...",
    "enriched_data": {
      "tags": ["running", "sport", "scarpe", "nike", "air-max"],
      "category": "Calzature Sportive",
      "features": ["Ammortizzazione Air Max", "Tomaia traspirante"],
      "confidence_score": 0.95
    }
  },
  "processing_info": {
    "started_at": "2025-01-25T14:29:45.000Z",
    "completed_at": "2025-01-25T14:30:00.000Z",
    "processing_time_ms": 15000
  },
  "data": {
    "product_id": "string",
    "status": "success",
    "enriched_fields": {
      "description": "Nuova descrizione",
      "tags": ["tag1", "tag2"]
    },
    "details": {}
  }
}

Sicurezza Webhook

Tutti i webhook includono un header X-Skuno-Signature con firma HMAC-SHA256 per verificare l'autenticità.

// Verifica della firma
import crypto from 'crypto'

const signature = request.headers['x-skuno-signature'] as string
const payload = JSON.stringify(request.body)

// Esempio: recupero del secret specifico per connessione
const { data: connection } = await supabase
  .from('ecommerce_connections')
  .select('webhook_secret')
  .eq('id', connectionId)
  .single()

const secret = connection?.webhook_secret

const expectedSignature = crypto
  .createHmac('sha256', secret)
  .update(payload)
  .digest('hex')

const isValid = signature === expectedSignature

Dashboard API

Ottieni Statistiche Dashboard

GET /dashboard

Headers:

Authorization: Bearer <token>

Response

{
  "products": {
    "total": 1000,
    "enriched": 850,
    "pending": 100,
    "failed": 50,
    "enrichment_rate": 85.0
  },
  "notifications": {
    "total_sent": 500,
    "pending": 10,
    "failed": 5,
    "success_rate": 97.0,
    "by_type": {
      "email": 300,
      "webhook": 150
    }
  },
  "queue": {
    "pending_jobs": 25,
    "processing_jobs": 5,
    "failed_jobs": 2,
    "avg_processing_time": 1500
  },
  "webhooks": {
    "total_sent": 200,
    "successful": 190,
    "failed": 10,
    "success_rate": 95.0
  },
  "system": {
    "uptime": "5h 30m",
    "active_connections": 15,
    "last_updated": "2025-01-20T10:00:00Z"
  }
}

Ottieni Log di Sistema

GET /dashboard/logs?limit=50

Headers:

Authorization: Bearer <token>

Parametri Query:

  • limit (opzionale): numero massimo di log da restituire (default: 50)

Response

{
  "logs": [
    {
      "id": "uuid",
      "level": "info",
      "message": "Product enrichment completed",
      "component": "enrichment",
      "context": {
        "product_id": "uuid",
        "processing_time_ms": 1500
      },
      "created_at": "2025-01-20T10:00:00Z"
    }
  ]
}

Ottieni Attività Recenti

GET /dashboard/activity?limit=20

Headers:

Authorization: Bearer <token>

Parametri Query:

  • limit (opzionale): numero massimo di attività da restituire (default: 20)

Response

{
  "activities": [
    {
      "id": "uuid",
      "action": "product_created",
      "resource_type": "product",
      "resource_id": "uuid",
      "details": {
        "product_name": "iPhone 15",
        "category": "Electronics"
      },
      "user_id": "uuid",
      "created_at": "2025-01-20T10:00:00Z"
    }
  ]
}

Health Check

GET /dashboard/health

Response

{
  "status": "healthy",
  "timestamp": "2025-01-20T10:00:00Z",
  "services": {
    "database": "healthy",
    "queue": "healthy",
    "notifications": "healthy"
  }
}

Best Practices

Gestione degli Errori

  • Implementare retry con backoff esponenziale
  • Gestire gracefully i timeout
  • Loggare gli errori per il debugging

Performance

  • Utilizzare la paginazione per grandi dataset
  • Implementare caching lato client
  • Minimizzare il payload delle richieste

Sicurezza

  • Utilizzare sempre HTTPS
  • Validare tutti gli input
  • Implementare timeout appropriati