Introduzione
A chi servono le API
Le API di Sigillo sono per chi vuole far parlare i preventivi con altri strumenti: il form del proprio sito, un CRM, un foglio di calcolo, Zapier o Make, il gestionale del commercialista. Se fai tutto dall'app non ti servono.
Leggi e crei preventivi e clienti, pubblichi il link da mandare al cliente, scarichi PDF, fatture e XML per lo SDI, leggi le richieste arrivate dalla vetrina. Con i webhook Sigillo ti avvisa quando succede qualcosa, senza che tu debba controllare di continuo.
Autenticazione
Una chiave per workspace
- Apri l'app, vai in Impostazioni → API e webhook e premi Nuova chiave.
- Dai un nome alla chiave (es. “Sito web”) e scegli il permesso.
- Copia la chiave: inizia con
sgl_live_e la vedi una volta sola. Sigillo ne conserva solo un'impronta, quindi se la perdi ne crei una nuova e revochi la vecchia.
Mandala in ogni richiesta nell'header Authorization:
Authorization: Bearer sgl_live_…| Permesso | Cosa può fare |
|---|---|
| Lettura | Tutti gli endpoint GET: preventivi, rate, PDF, clienti, fatture, XML, richieste. |
| Lettura e scrittura | Tutto quello della lettura, più POST e PATCH: creare e modificare clienti, creare e pubblicare preventivi. |
Una chiave di sola lettura su un endpoint di scrittura riceve 403 forbidden. Una chiave revocata, sbagliata o mancante riceve 401 unauthorized. Ogni chiave vale per un solo workspace.
Ogni workspace può avere al massimo 5 chiavi attive: una per integrazione (il form del sito, Zapier, il gestionale) e una di scorta per cambiare chiave senza interruzioni. Crea la nuova, aggiorna l'integrazione, poi revoca la vecchia. Le chiavi revocate non contano.
Base URL e formato
JSON su HTTPS
https://app.mysigillo.com/api/v1- Solo HTTPS. Le richieste con body usano
Content-Type: application/json. - I campi sono in
snake_case. Gli importi sono in euro, come numeri con i decimali (1628.7), non in centesimi. - Le date civili sono
AAAA-MM-GG(2026-09-15); i momenti sono ISO 8601 in UTC (2026-09-15T09:12:44.000Z). - I campi senza valore valgono
null, non spariscono dalla risposta.
Limiti
60 richieste al minuto
Ogni chiave può fare fino a 60 richieste al minuto. Il limite è per chiave, non per workspace. Ogni risposta ti dice a che punto sei:
| Header | Significato |
|---|---|
X-RateLimit-Limit | Richieste consentite nella finestra (60). |
X-RateLimit-Remaining | Richieste che puoi ancora fare con questa chiave nel minuto corrente. |
Oltre il limite ricevi 429 rate_limited: aspetta qualche secondo e riprova, meglio con un'attesa che cresce a ogni tentativo. Per sincronizzare molti dati usa la paginazione con limit=100 invece di tante richieste piccole, e i webhook invece di interrogare l'API a intervalli.
Errori
Sempre lo stesso formato
Le risposte con errore hanno uno status HTTP 4xx o 5xx e questo body. code è stabile e fatto per il tuo codice; message è in italiano e fatto per le persone.
{
"error": {
"code": "invalid_request",
"message": "items deve contenere almeno una riga."
}
}| HTTP | code | Quando |
|---|---|---|
| 400 | invalid_request | Parametri o body non validi (il messaggio dice quale campo), campi sconosciuti, oppure metodo HTTP non previsto su un percorso esistente. |
| 401 | unauthorized | Chiave mancante, sbagliata o revocata. |
| 402 | plan_limit | Il workspace non è più Premium, o hai raggiunto un limite del piano. |
| 403 | forbidden | La chiave non ha il permesso per questa operazione (es. scrittura con chiave di lettura). |
| 404 | not_found | L’oggetto non esiste o appartiene a un altro workspace. |
| 409 | conflict | L’operazione non è possibile nello stato attuale dell’oggetto. |
| 429 | rate_limited | Troppe richieste: vedi i limiti. |
| 500 | internal | Errore nostro. Riprova tra poco; se continua scrivici. |
Un metodo non previsto su un percorso che esiste (per esempio DELETE /clients/{id}) riceve 400 invalid_request, non 405, con l'header Allow che elenca i metodi accettati:
HTTP/1.1 400 Bad Request
Allow: GET, PATCH
Content-Type: application/json
{
"error": {
"code": "invalid_request",
"message": "Metodo DELETE non previsto su questo percorso."
}
}Paginazione
Liste a cursore
Gli endpoint che restituiscono liste (GET /quotes, /clients, /invoices, /requests) partono dagli elementi più recenti e accettano:
| Parametro | Descrizione |
|---|---|
limit | Quanti elementi per pagina, da 1 a 100. Predefinito 20. |
starting_after | L’id dell’ultimo elemento della pagina precedente. |
La risposta ha has_more (ci sono altre pagine?) e next_cursor, da passare come starting_after alla richiesta successiva. Quando has_more è false, next_cursor è null.
curl "https://app.mysigillo.com/api/v1/clients?limit=2&starting_after=cl_8Hq2Lm4Tz9" \
-H "Authorization: Bearer sgl_live_…"{
"object": "list",
"data": [
{
"id": "cl_2Wn7Rt5Kx3",
"object": "client",
"…": "…"
},
{
"id": "cl_6Jd1Pq8Vy4",
"object": "client",
"…": "…"
}
],
"has_more": true,
"next_cursor": "cl_6Jd1Pq8Vy4"
}Endpoint
Riferimento
Tutti i percorsi sono relativi a https://app.mysigillo.com/api/v1.
| Metodo | Percorso | Cosa fa |
|---|---|---|
| GET | /me | Workspace e chiave in uso |
| GET | /quotes | Elenco preventivi |
| GET | /quotes/{id} | Un preventivo |
| POST | /quotes | Crea un preventivo in bozza |
| POST | /quotes/{id}/publish | Pubblica il link per il cliente |
| GET | /quotes/{id}/pdf | PDF del preventivo |
| GET | /quotes/{id}/payments | Rate e pagamenti |
| GET · POST | /clients | Elenco e creazione clienti |
| GET · PATCH | /clients/{id} | Leggi o modifica un cliente |
| GET | /invoices | Elenco fatture e note di credito |
| GET | /invoices/{id} | Una fattura |
| GET | /invoices/{id}/xml | XML FatturaPA per lo SDI |
| GET | /requests | Richieste dalla vetrina |
Account
/mechiave letturaVerifica la chiave
Il primo test da fare: se risponde 200, la chiave funziona. Restituisce il workspace a cui appartiene la chiave e il suo permesso.
curl https://app.mysigillo.com/api/v1/me \
-H "Authorization: Bearer sgl_live_…"{
"workspace": {
"id": "ws_Ab12Cd34",
"name": "Studio Rossi Design",
"plan": "premium"
},
"key": {
"id": "ak_Q8wE2rT6yU",
"name": "Sito web",
"scope": "write"
}
}Preventivi
/quoteschiave letturaElenco dei preventivi
Dal più recente. Accetta limit e starting_after. Gli stati possibili sono draft, sent, accepted, rejected, expired.
curl "https://app.mysigillo.com/api/v1/quotes?limit=20" \
-H "Authorization: Bearer sgl_live_…"{
"object": "list",
"data": [
{
"id": "qt_3Kf9Xw2Pa7",
"object": "quote",
"number": "2026-014",
"status": "sent",
"client": {
"id": "cl_8Hq2Lm4Tz9",
"name": "Marta Bianchi",
"company": "Studio Bianchi Srl",
"email": "marta@studiobianchi.it",
"vat_number": "IT01234567890",
"fiscal_code": null,
"address": "Via Roma 12"
},
"items": [
{
"description": "Logo e identità visiva",
"quantity": 1,
"unit_price": 1200,
"vat_rate": 22,
"discount": 0
},
{
"description": "Biglietti da visita (500 pz)",
"quantity": 1,
"unit_price": 150,
"vat_rate": 22,
"discount": 10
}
],
"currency": "EUR",
"totals": {
"subtotal": 1335,
"vat": 293.7,
"cassa": 0,
"total": 1628.7
},
"issue_date": "2026-09-15",
"valid_until": "2026-10-15",
"public_url": "https://app.mysigillo.com/q/Tk7wQ2…",
"signed_at": null,
"signer_name": null,
"created_at": "2026-09-15T09:12:44.000Z",
"updated_at": "2026-09-15T09:20:02.000Z",
"created_by_name": "Alessio Bertolini"
}
],
"has_more": true,
"next_cursor": "qt_3Kf9Xw2Pa7"
}/quotes/{id}chiave letturaUn preventivo
curl https://app.mysigillo.com/api/v1/quotes/qt_3Kf9Xw2Pa7 \
-H "Authorization: Bearer sgl_live_…"{
"id": "qt_3Kf9Xw2Pa7",
"object": "quote",
"number": "2026-014",
"status": "sent",
"client": {
"id": "cl_8Hq2Lm4Tz9",
"name": "Marta Bianchi",
"company": "Studio Bianchi Srl",
"email": "marta@studiobianchi.it",
"vat_number": "IT01234567890",
"fiscal_code": null,
"address": "Via Roma 12"
},
"items": [
{
"description": "Logo e identità visiva",
"quantity": 1,
"unit_price": 1200,
"vat_rate": 22,
"discount": 0
},
{
"description": "Biglietti da visita (500 pz)",
"quantity": 1,
"unit_price": 150,
"vat_rate": 22,
"discount": 10
}
],
"currency": "EUR",
"totals": {
"subtotal": 1335,
"vat": 293.7,
"cassa": 0,
"total": 1628.7
},
"issue_date": "2026-09-15",
"valid_until": "2026-10-15",
"public_url": "https://app.mysigillo.com/q/Tk7wQ2…",
"signed_at": null,
"signer_name": null,
"created_at": "2026-09-15T09:12:44.000Z",
"updated_at": "2026-09-15T09:20:02.000Z",
"created_by_name": "Alessio Bertolini"
}/quoteschiave scritturaCrea un preventivo in bozza
Il preventivo nasce in bozza: lo trovi nell'app, pronto da rivedere o da pubblicare. La risposta è 201 con l'oggetto quote completo.
| Campo | Descrizione |
|---|---|
client_id | Id di un cliente in rubrica. In alternativa a client. |
client | Dati del cliente scritti nel preventivo: stessi campi dell'oggetto client (name obbligatorio, notes escluso). Vengono copiati solo nel preventivo: non creano un cliente in rubrica (per quello usa POST /clients). |
items | Almeno una riga: description, quantity, unit_price (euro), vat_rate (percentuale, es. 22), discount (percentuale, facoltativo). |
issue_date | Data del preventivo, AAAA-MM-GG. Facoltativa. |
valid_until | Scadenza, AAAA-MM-GG. Facoltativa. |
notes | Note visibili al cliente. Facoltative. |
terms | Termini e condizioni. Facoltativi. |
Ritenuta, cassa previdenziale e marca da bollo seguono le impostazioni fiscali del workspace, come quando crei il preventivo dall'app.
Serve esattamente uno tra client_id e client: se li mandi entrambi, o nessuno dei due, ricevi 400 invalid_request.
curl https://app.mysigillo.com/api/v1/quotes \
-H "Authorization: Bearer sgl_live_…" \
-H "Content-Type: application/json" \
-d '{
"client": {
"name": "Luca Verdi",
"email": "luca@verdi.it",
"company": "Trattoria Verdi"
},
"items": [
{
"description": "Sito vetrina 5 pagine",
"quantity": 1,
"unit_price": 1800,
"vat_rate": 22,
"discount": 0
},
{
"description": "Modulo prenotazioni",
"quantity": 1,
"unit_price": 400,
"vat_rate": 22,
"discount": 0
}
],
"issue_date": "2026-09-17",
"valid_until": "2026-10-17",
"notes": "Consegna in 4 settimane dalla firma.",
"terms": "Acconto 30% alla firma, saldo alla consegna."
}'{
"id": "qt_7Lp2Qs9Dm4",
"object": "quote",
"number": "2026-015",
"status": "draft",
"client": {
"id": "cl_8Hq2Lm4Tz9",
"name": "Marta Bianchi",
"company": "Studio Bianchi Srl",
"email": "marta@studiobianchi.it",
"vat_number": "IT01234567890",
"fiscal_code": null,
"address": "Via Roma 12"
},
"items": [
{
"description": "Logo e identità visiva",
"quantity": 1,
"unit_price": 1200,
"vat_rate": 22,
"discount": 0
},
{
"description": "Biglietti da visita (500 pz)",
"quantity": 1,
"unit_price": 150,
"vat_rate": 22,
"discount": 10
}
],
"currency": "EUR",
"totals": {
"subtotal": 1335,
"vat": 293.7,
"cassa": 0,
"total": 1628.7
},
"issue_date": "2026-09-15",
"valid_until": "2026-10-15",
"public_url": null,
"signed_at": null,
"signer_name": null,
"created_at": "2026-09-15T09:12:44.000Z",
"updated_at": "2026-09-15T09:20:02.000Z",
"created_by_name": "Alessio Bertolini"
}/quotes/{id}/publishchiave scritturaPubblica il link per il cliente
Crea il link pubblico, come il bottone “Pubblica” nell'app. Il cliente lo apre, legge, firma e paga. pin è il codice che il cliente inserisce per aprire il link; null se il link non è protetto. Mandali con canali diversi, per esempio link via email e PIN via WhatsApp.
curl -X POST https://app.mysigillo.com/api/v1/quotes/qt_7Lp2Qs9Dm4/publish \
-H "Authorization: Bearer sgl_live_…"{
"public_url": "https://app.mysigillo.com/q/Tk7wQ2…",
"pin": "4821"
}/quotes/{id}/pdfchiave letturaScarica il PDF
Risponde con il file (Content-Type: application/pdf), lo stesso PDF che scarichi dall'app.
curl https://app.mysigillo.com/api/v1/quotes/qt_3Kf9Xw2Pa7/pdf \
-H "Authorization: Bearer sgl_live_…" \
-o preventivo-2026-014.pdf/quotes/{id}/paymentschiave letturaRate e pagamenti
Il piano dei pagamenti del preventivo. payment_url è il link di pagamento Stripe della rata, se l'hai creato. È una lista standard, sempre completa in una pagina: has_more vale false.
curl https://app.mysigillo.com/api/v1/quotes/qt_3Kf9Xw2Pa7/payments \
-H "Authorization: Bearer sgl_live_…"{
"object": "list",
"data": [
{
"id": "pay_1",
"label": "Acconto 30%",
"amount": 488.61,
"due_date": "2026-09-20",
"paid": true,
"paid_at": "2026-09-18",
"method": "stripe",
"payment_url": "https://buy.stripe.com/…"
},
{
"id": "pay_2",
"label": "Saldo",
"amount": 1140.09,
"due_date": "2026-10-30",
"paid": false,
"paid_at": null,
"method": null,
"payment_url": "https://buy.stripe.com/…"
}
],
"has_more": false,
"next_cursor": null
}Clienti
/clientschiave letturaElenco dei clienti
curl "https://app.mysigillo.com/api/v1/clients?limit=50" \
-H "Authorization: Bearer sgl_live_…"{
"object": "list",
"data": [
{
"id": "cl_8Hq2Lm4Tz9",
"object": "client",
"name": "Marta Bianchi",
"company": "Studio Bianchi Srl",
"email": "marta@studiobianchi.it",
"phone": "+39 347 123 4567",
"vat_number": "IT01234567890",
"fiscal_code": null,
"sdi_code": "M5UXCR1",
"pec": null,
"address": "Via Roma 12",
"city": "Milano",
"zip": "20121",
"province": "MI",
"country": "IT",
"notes": null,
"created_at": "2026-09-02T08:14:03.000Z",
"updated_at": "2026-09-10T16:40:21.000Z"
}
],
"has_more": false,
"next_cursor": null
}/clientschiave scritturaCrea un cliente
Campi dell'oggetto client, tutti stringhe. name è obbligatorio. Un campo che non esiste nell'oggetto client riceve 400 invalid_request.
curl https://app.mysigillo.com/api/v1/clients \
-H "Authorization: Bearer sgl_live_…" \
-H "Content-Type: application/json" \
-d '{
"name": "Marta Bianchi",
"company": "Studio Bianchi Srl",
"email": "marta@studiobianchi.it",
"vat_number": "IT01234567890",
"sdi_code": "M5UXCR1"
}'{
"id": "cl_8Hq2Lm4Tz9",
"object": "client",
"name": "Marta Bianchi",
"company": "Studio Bianchi Srl",
"email": "marta@studiobianchi.it",
"phone": "+39 347 123 4567",
"vat_number": "IT01234567890",
"fiscal_code": null,
"sdi_code": "M5UXCR1",
"pec": null,
"address": "Via Roma 12",
"city": "Milano",
"zip": "20121",
"province": "MI",
"country": "IT",
"notes": null,
"created_at": "2026-09-02T08:14:03.000Z",
"updated_at": "2026-09-10T16:40:21.000Z"
}/clients/{id}chiave letturaUn cliente
curl https://app.mysigillo.com/api/v1/clients/cl_8Hq2Lm4Tz9 \
-H "Authorization: Bearer sgl_live_…"{
"id": "cl_8Hq2Lm4Tz9",
"object": "client",
"name": "Marta Bianchi",
"company": "Studio Bianchi Srl",
"email": "marta@studiobianchi.it",
"phone": "+39 347 123 4567",
"vat_number": "IT01234567890",
"fiscal_code": null,
"sdi_code": "M5UXCR1",
"pec": null,
"address": "Via Roma 12",
"city": "Milano",
"zip": "20121",
"province": "MI",
"country": "IT",
"notes": null,
"created_at": "2026-09-02T08:14:03.000Z",
"updated_at": "2026-09-10T16:40:21.000Z"
}/clients/{id}chiave scritturaModifica un cliente
Mandi solo i campi da cambiare; quelli che non mandi restano com'erano. Un valore null rimuove il campo (non vale per name, che è obbligatorio). Un campo sconosciuto riceve 400 invalid_request. I preventivi già fatti non cambiano: tengono i dati del cliente di quel momento.
curl -X PATCH https://app.mysigillo.com/api/v1/clients/cl_8Hq2Lm4Tz9 \
-H "Authorization: Bearer sgl_live_…" \
-H "Content-Type: application/json" \
-d '{
"pec": "studiobianchi@pec.it",
"phone": null
}'{
"id": "cl_8Hq2Lm4Tz9",
"object": "client",
"name": "Marta Bianchi",
"company": "Studio Bianchi Srl",
"email": "marta@studiobianchi.it",
"phone": null,
"vat_number": "IT01234567890",
"fiscal_code": null,
"sdi_code": "M5UXCR1",
"pec": "studiobianchi@pec.it",
"address": "Via Roma 12",
"city": "Milano",
"zip": "20121",
"province": "MI",
"country": "IT",
"notes": null,
"created_at": "2026-09-02T08:14:03.000Z",
"updated_at": "2026-09-10T16:40:21.000Z"
}Fatture
/invoiceschiave letturaElenco di fatture e note di credito
from e to (AAAA-MM-GG, inclusi) filtrano per data di emissione; entrambi facoltativi. document_type è il tipo SDI: TD01 fattura, TD04 nota di credito.
curl "https://app.mysigillo.com/api/v1/invoices?from=2026-09-01&to=2026-09-30" \
-H "Authorization: Bearer sgl_live_…"{
"object": "list",
"data": [
{
"id": "inv_5Rt8Yc1Mn2",
"object": "invoice",
"number": "2026/0004",
"document_type": "TD01",
"status": "issued",
"issue_date": "2026-09-16",
"client": {
"name": "Studio Bianchi Srl",
"vat_number": "IT01234567890"
},
"currency": "EUR",
"totals": {
"subtotal": 1335,
"vat": 293.7,
"total": 1628.7
},
"quote_id": "qt_3Kf9Xw2Pa7",
"created_at": "2026-09-16T10:02:11.000Z"
}
],
"has_more": false,
"next_cursor": null
}/invoices/{id}chiave letturaUna fattura
curl https://app.mysigillo.com/api/v1/invoices/inv_5Rt8Yc1Mn2 \
-H "Authorization: Bearer sgl_live_…"{
"id": "inv_5Rt8Yc1Mn2",
"object": "invoice",
"number": "2026/0004",
"document_type": "TD01",
"status": "issued",
"issue_date": "2026-09-16",
"client": {
"name": "Studio Bianchi Srl",
"vat_number": "IT01234567890"
},
"currency": "EUR",
"totals": {
"subtotal": 1335,
"vat": 293.7,
"total": 1628.7
},
"quote_id": "qt_3Kf9Xw2Pa7",
"created_at": "2026-09-16T10:02:11.000Z"
}/invoices/{id}/xmlchiave letturaXML FatturaPA
Risponde con il file XML (application/xml) nel formato FatturaPA 1.2, lo stesso che scarichi dall'app da caricare sul portale SDI o da girare al commercialista.
curl https://app.mysigillo.com/api/v1/invoices/inv_5Rt8Yc1Mn2/xml \
-H "Authorization: Bearer sgl_live_…" \
-o IT18522971003_00004.xmlRichieste
/requestschiave letturaRichieste dalla vetrina
Le richieste di preventivo arrivate dalla tua vetrina pubblica. status è facoltativo: new, viewed, converted, archived, spam.
curl "https://app.mysigillo.com/api/v1/requests?status=new" \
-H "Authorization: Bearer sgl_live_…"{
"object": "list",
"data": [
{
"id": "rq_9Pz4Kd7Wb1",
"object": "request",
"status": "new",
"name": "Luca Verdi",
"email": "luca@verdi.it",
"phone": null,
"service": "Sito web",
"budget": "1_5k",
"message": "Mi serve un sito vetrina per il mio ristorante, 5 pagine e prenotazioni.",
"created_at": "2026-09-17T07:45:30.000Z"
}
],
"has_more": false,
"next_cursor": null
}Oggetti
Cosa restituisce l’API
Ogni oggetto ha id e object (il tipo). Gli oggetti non espongono mai dati interni come la firma grafica, gli indirizzi IP o i token dei link.
quote
| Campo | Tipo | Descrizione |
|---|---|---|
number | string | Numero del preventivo. |
status | string | draft, sent, accepted, rejected, expired. |
client | object | id, name, company, email, vat_number, fiscal_code, address: i dati del cliente scritti nel preventivo. |
items | array | Righe: description, quantity, unit_price, vat_rate, discount. |
currency | string | Sempre EUR. |
totals | object | subtotal, vat, cassa, total in euro. |
issue_date · valid_until | date | Data del preventivo e scadenza. |
public_url | string | null | Link per il cliente, se pubblicato. |
signed_at · signer_name | datetime · string | null | Quando e chi ha firmato. |
created_at · updated_at | datetime | Creazione e ultima modifica. |
created_by_name | string | null | Chi l’ha creato. |
{
"id": "qt_3Kf9Xw2Pa7",
"object": "quote",
"number": "2026-014",
"status": "sent",
"client": {
"id": "cl_8Hq2Lm4Tz9",
"name": "Marta Bianchi",
"company": "Studio Bianchi Srl",
"email": "marta@studiobianchi.it",
"vat_number": "IT01234567890",
"fiscal_code": null,
"address": "Via Roma 12"
},
"items": [
{
"description": "Logo e identità visiva",
"quantity": 1,
"unit_price": 1200,
"vat_rate": 22,
"discount": 0
},
{
"description": "Biglietti da visita (500 pz)",
"quantity": 1,
"unit_price": 150,
"vat_rate": 22,
"discount": 10
}
],
"currency": "EUR",
"totals": {
"subtotal": 1335,
"vat": 293.7,
"cassa": 0,
"total": 1628.7
},
"issue_date": "2026-09-15",
"valid_until": "2026-10-15",
"public_url": "https://app.mysigillo.com/q/Tk7wQ2…",
"signed_at": null,
"signer_name": null,
"created_at": "2026-09-15T09:12:44.000Z",
"updated_at": "2026-09-15T09:20:02.000Z",
"created_by_name": "Alessio Bertolini"
}payment
| Campo | Tipo | Descrizione |
|---|---|---|
label | string | Nome della rata (es. Acconto 30%). |
amount | number | Importo in euro. |
due_date | date | null | Scadenza. |
paid · paid_at | boolean · date | null | Se è pagata e quando. |
method | string | null | bonifico, stripe, paypal, contanti, assegno, altro. |
payment_url | string | null | Link di pagamento Stripe della rata. |
{
"id": "pay_1",
"label": "Acconto 30%",
"amount": 488.61,
"due_date": "2026-09-20",
"paid": true,
"paid_at": "2026-09-18",
"method": "stripe",
"payment_url": "https://buy.stripe.com/…"
}client
| Campo | Tipo | Descrizione |
|---|---|---|
name | string | Nome e cognome o nome di riferimento. Obbligatorio. |
company | string | null | Ragione sociale. |
email · phone | string | null | Contatti. |
vat_number · fiscal_code | string | null | Partita IVA e codice fiscale. |
sdi_code · pec | string | null | Codice destinatario SDI (7 caratteri) e PEC per la fattura elettronica. |
address · city · zip · province · country | string | null | Indirizzo. province è la sigla (MI). |
notes | string | null | Note interne, non visibili al cliente. |
created_at · updated_at | datetime | Creazione e ultima modifica. |
invoice
| Campo | Tipo | Descrizione |
|---|---|---|
number | string | Numero della fattura (es. 2026/0004). |
document_type | string | Tipo documento SDI: TD01 fattura, TD04 nota di credito. |
status | string | draft, issued, paid, cancelled. |
issue_date | date | Data di emissione. |
client | object | name e vat_number del cliente. |
totals | object | subtotal, vat, total in euro. |
quote_id | string | null | Il preventivo da cui è nata. |
created_at | datetime | Creazione. |
{
"id": "inv_5Rt8Yc1Mn2",
"object": "invoice",
"number": "2026/0004",
"document_type": "TD01",
"status": "issued",
"issue_date": "2026-09-16",
"client": {
"name": "Studio Bianchi Srl",
"vat_number": "IT01234567890"
},
"currency": "EUR",
"totals": {
"subtotal": 1335,
"vat": 293.7,
"total": 1628.7
},
"quote_id": "qt_3Kf9Xw2Pa7",
"created_at": "2026-09-16T10:02:11.000Z"
}request
| Campo | Tipo | Descrizione |
|---|---|---|
status | string | new, viewed, converted, archived, spam. |
name · email · phone | string | Chi ha scritto. |
service | string | null | Il servizio scelto nel form. |
budget | string | null | Fascia: lt_1k, 1_5k, 5_15k, 15k_plus, unknown. |
message | string | Descrizione del progetto. |
created_at | datetime | Quando è arrivata. |
{
"id": "rq_9Pz4Kd7Wb1",
"object": "request",
"status": "new",
"name": "Luca Verdi",
"email": "luca@verdi.it",
"phone": null,
"service": "Sito web",
"budget": "1_5k",
"message": "Mi serve un sito vetrina per il mio ristorante, 5 pagine e prenotazioni.",
"created_at": "2026-09-17T07:45:30.000Z"
}Webhook
Ti avvisiamo noi
Un webhook è un URL tuo che Sigillo chiama con una richiesta POST quando succede qualcosa. Lo crei in Impostazioni → API e webhook → Nuovo webhook: inserisci l'URL (solo https://), scegli gli eventi e copia il segreto di firma (whsec_…), che vedi una volta sola. Da lì puoi mandare un evento di prova, vedere le ultime consegne, disattivarlo o ruotare il segreto. Puoi configurare fino a 10 webhook per workspace.
Eventi
| Evento | Quando | data |
|---|---|---|
quote.published | Hai pubblicato il link di un preventivo. | object: quote |
quote.viewed | Il cliente ha aperto il link (al massimo un evento ogni 10 minuti per preventivo). | object: quote |
quote.signed | Il cliente ha accettato e firmato. | object: quote |
quote.rejected | Il cliente ha rifiutato. | object: quote |
payment.received | Una rata risulta pagata. | object: payment, quote_id |
invoice.created | È stata emessa una fattura. | object: invoice |
creditnote.created | È stata emessa una nota di credito (document_type TD04). | object: invoice |
request.received | È arrivata una richiesta dalla vetrina. | object: request |
ping | Evento di prova inviato con “Invia test” in Impostazioni, anche se il webhook è disattivato. Non conta per le statistiche né per la disattivazione automatica. | — |
Cosa ricevi
Il body è JSON. id identifica l'evento: se ricevi due volte lo stesso id (può succedere con i nuovi tentativi), elaboralo una volta sola.
POST /webhook/sigillo HTTP/1.1
Content-Type: application/json
Sigillo-Event: quote.signed
Sigillo-Signature: t=1789640400,v1=5f2b0c…e91a
{
"id": "evt_4Hs8Nq2Lw6",
"type": "quote.signed",
"created": 1789640400,
"workspace_id": "ws_Ab12Cd34",
"data": {
"object": {
"id": "qt_3Kf9Xw2Pa7",
"object": "quote",
"number": "2026-014",
"status": "accepted",
"client": {
"id": "cl_8Hq2Lm4Tz9",
"name": "Marta Bianchi",
"company": "Studio Bianchi Srl",
"email": "marta@studiobianchi.it",
"vat_number": "IT01234567890",
"fiscal_code": null,
"address": "Via Roma 12"
},
"items": [
{
"description": "Logo e identità visiva",
"quantity": 1,
"unit_price": 1200,
"vat_rate": 22,
"discount": 0
},
{
"description": "Biglietti da visita (500 pz)",
"quantity": 1,
"unit_price": 150,
"vat_rate": 22,
"discount": 10
}
],
"currency": "EUR",
"totals": {
"subtotal": 1335,
"vat": 293.7,
"cassa": 0,
"total": 1628.7
},
"issue_date": "2026-09-15",
"valid_until": "2026-10-15",
"public_url": "https://app.mysigillo.com/q/Tk7wQ2…",
"signed_at": "2026-09-17T10:20:00.000Z",
"signer_name": "Marta Bianchi",
"created_at": "2026-09-15T09:12:44.000Z",
"updated_at": "2026-09-15T09:20:02.000Z",
"created_by_name": "Alessio Bertolini"
}
}
}Per payment.received, data.object è la rata (oggetto payment) e data.quote_id è il preventivo a cui appartiene:
{
"id": "evt_7Qm3Vx9Tc2",
"type": "payment.received",
"created": 1789726800,
"workspace_id": "ws_Ab12Cd34",
"data": {
"object": {
"id": "pay_1",
"label": "Acconto 30%",
"amount": 488.61,
"due_date": "2026-09-20",
"paid": true,
"paid_at": "2026-09-18",
"method": "stripe",
"payment_url": "https://buy.stripe.com/…"
},
"quote_id": "qt_3Kf9Xw2Pa7"
}
}Gli eventi di prova hanno type: "ping": rispondi 2xx e ignorali. Servono solo a controllare che URL e verifica della firma funzionino.
| Header | Contenuto |
|---|---|
Sigillo-Event | Il tipo di evento, uguale a type nel body. |
Sigillo-Signature | t= momento dell'invio in secondi Unix, v1= HMAC SHA-256 in esadecimale di <t>.<body> calcolato con il segreto del webhook. |
Verifica la firma
Prima di fidarti di un evento, ricalcola la firma sul body grezzo (non su un JSON riformattato), confrontala a tempo costante e scarta i timestamp più vecchi di 5 minuti.
import crypto from "node:crypto";
import express from "express";
const app = express();
const SECRET = process.env.SIGILLO_WEBHOOK_SECRET; // whsec_…
// Serve il body grezzo: la firma è calcolata sui byte esatti.
app.post("/webhook/sigillo", express.raw({ type: "application/json" }), (req, res) => {
const header = req.get("Sigillo-Signature") || "";
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const t = Number(parts.t);
const body = req.body.toString("utf8");
const expected = crypto
.createHmac("sha256", SECRET)
.update(`${t}.${body}`)
.digest("hex");
const fresh = Math.abs(Date.now() / 1000 - t) < 300; // 5 minuti
const valid =
parts.v1?.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
if (!fresh || !valid) return res.status(400).send("firma non valida");
const event = JSON.parse(body);
// Rispondi subito, lavora dopo.
res.sendStatus(200);
if (event.type === "quote.signed") {
console.log("Firmato:", event.data.object.number);
}
});
app.listen(3000);import hashlib, hmac, json, os, time
from flask import Flask, request, abort
app = Flask(__name__)
SECRET = os.environ["SIGILLO_WEBHOOK_SECRET"].encode() # whsec_…
@app.post("/webhook/sigillo")
def sigillo_webhook():
header = request.headers.get("Sigillo-Signature", "")
parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
body = request.get_data(as_text=True) # body grezzo
try:
t = int(parts["t"])
except (KeyError, ValueError):
abort(400)
expected = hmac.new(SECRET, f"{t}.{body}".encode(), hashlib.sha256).hexdigest()
if abs(time.time() - t) > 300 or not hmac.compare_digest(expected, parts.get("v1", "")):
abort(400)
event = json.loads(body)
if event["type"] == "payment.received":
print("Pagamento:", event["data"]["object"]["amount"], "preventivo", event["data"]["quote_id"])
return "", 200Consegna e nuovi tentativi
- Rispondi con uno status 2xx entro 10 secondi. Se hai lavoro lungo da fare, rispondi subito e fallo dopo.
- Qualsiasi altra risposta, un timeout o un errore di rete contano come fallimento. Sigillo riprova dopo 1 minuto, 5 minuti, 30 minuti, 2 ore e 6 ore.
- Dopo 20 consegne fallite di fila il webhook si disattiva da solo e in Impostazioni vedi il motivo. Sistemato il tuo server, lo riattivi con un click.
- Gli eventi possono arrivare in ordine diverso da quello in cui sono successi: se ti serve lo stato aggiornato, rileggi l'oggetto con l'API.
Casi d’uso
Cosa ci puoi fare
Il form del tuo sito crea il preventivo
Il visitatore compila il form sul tuo sito, il tuo server chiama POST /quotes con i suoi dati e le righe del servizio scelto. Trovi la bozza in Sigillo, la controlli e la pubblichi. La chiave resta sul server, mai nella pagina.
Zapier e Make con i webhook
Crea uno Zap o uno scenario con il trigger “Webhook” (Catch Hook in Zapier, Custom webhook in Make), incolla l'URL in Sigillo e scegli gli eventi. Esempi: messaggio su Slack quando arriva quote.signed, riga su Google Sheets per ogni payment.received, contatto nella newsletter per ogni request.received.
Export mensile delle fatture
Il primo del mese uno script legge le fatture del mese prima con GET /invoices e scarica l'XML di ognuna, pronte per il gestionale o per il commercialista.
FROM=2026-08-01; TO=2026-08-31
curl -s "https://app.mysigillo.com/api/v1/invoices?from=$FROM&to=$TO&limit=100" \
-H "Authorization: Bearer $SIGILLO_API_KEY" \
| jq -r '.data[].id' \
| while read id; do
curl -s "https://app.mysigillo.com/api/v1/invoices/$id/xml" \
-H "Authorization: Bearer $SIGILLO_API_KEY" -o "$id.xml"
doneSincronizza il CRM
Tieni allineati i clienti tra Sigillo e il CRM con GET /clients, POST /clients e PATCH /clients/{id}, e aggiorna la trattativa quando arrivano quote.signed o quote.rejected.
Ti manca un endpoint o un evento? Scrivici a info@mysigillo.com.