API, webhook e MCP
Collegare Hakubase ad altri sistemi e agli assistenti AI, con le stesse regole di chi lo usa dall'interfaccia.
Hakubase ha tre modi per collegare i sistemi esterni: l’API REST v1, per leggere e scrivere da un ERP o da un’integrazione scritta da voi; i webhook, per sapere quando qualcosa cambia senza chiederlo di continuo; e il server MCP, per gli assistenti AI. API e MCP vedono gli stessi dati con gli stessi permessi: una risorsa aggiunta all’API compare anche nel server MCP, senza una riga in più.
Autenticazione
Le chiavi si creano in Impostazioni → Sviluppatori. Ogni chiave appartiene a un workspace, porta dei permessi e una portata, e va nell’intestazione:
GET /api/v1/companies HTTP/1.1
Host: <workspace>.hakubase.com
Authorization: Bearer hb_…
Una rotta chiede il permesso del modulo che governa la risorsa, non un generico permesso “di integrazione”: una chiave che legge le fatture non per questo vede i progetti.
La promessa di v1
Dentro una versione un campo si aggiunge, mai si toglie e mai cambia significato. Togliere o cambiare significa v2. Il vostro codice scritto oggi contro v1 funziona domani.
/api/v1 è un indirizzo diverso da /api, che serve l’interfaccia e cambia insieme alle schermate: non usatela.
Le risorse
| Risorsa | Lettura | Scrittura |
|---|---|---|
/companies | registry.read | registry.write — crea, aggiorna |
/people | registry.read | registry.write — crea, aggiorna |
/leads | leads.read | leads.write — crea |
/deals | deals.read | — |
/offers | offers.read | — |
/contracts | contracts.read | — |
/projects | projects.read | — |
/tickets | tickets.read | — |
/invoices | billing.read | — |
/credit_notes | billing.read | — |
/expenses | billing.read | — |
/catalog_items | catalog.read | — |
/price_lists | catalog.read | — |
Su /projects il budget (budget_amount) arriva solo a una chiave che ha anche projects.economics; senza, il campo è null.
La reference completa si trova nell’app. In Impostazioni → Sviluppatori → API REST trovate lo schema OpenAPI generato dalle rotte, con la prova delle chiamate contro il vostro workspace. È sempre allineato alla versione in produzione: Hakubase è un servizio, non c’è una versione vecchia da documentare.
Webhook
Un webhook è un evento che esce: quando in Hakubase succede qualcosa, il vostro indirizzo riceve una chiamata. Le iscrizioni si creano in Impostazioni → Sviluppatori → Webhook, con un nome, l’indirizzo (solo https) e gli eventi che volete ricevere.
Gli eventi sono quelli che il prodotto emette, area per area: lead.created, deal.stage_changed, invoice.issued, ticket.closed, project.delivered e gli altri. Sono di più di quelli su cui scattano le automazioni, perché un’integrazione vuole sapere anche quello che nessuna regola guarda. L’elenco completo è nella schermata dell’iscrizione.
Un webhook non ha una portata. Manda gli eventi di tutto il workspace, chiunque li abbia causati: non filtra per i permessi di chi l’ha creato. La schermata lo dice.
La consegna
Ogni evento arriva come una POST in JSON:
POST /hooks/hakubase HTTP/1.1
Host: example.com
Content-Type: application/json
User-Agent: Hakubase-Webhooks/1
X-Hakubase-Event: invoice.issued
X-Hakubase-Delivery: 01a1599c-5e2b-7c41-9d3a-6b0f2e8c7a14
X-Hakubase-Timestamp: 1791561612
X-Hakubase-Signature: sha256=9f2c41d7…
{
"id": "01a1599c-5d90-7e02-8b61-3c7a9f04d2e8",
"type": "invoice.issued",
"occurred_at": "2026-10-09T15:40:12+00:00",
"subject": { "type": "invoice", "id": "01a1599c-5d1f-…" },
"data": {
"public_number": "FT-2026-041",
"total": "30256.00",
"customer": "Fornace Nord S.r.l."
}
}
| Campo | Cosa contiene |
|---|---|
id | L’identificativo dell’evento. |
type | Il tipo, lo stesso di X-Hakubase-Event. |
occurred_at | Quando è successo, in UTC. |
subject | Il record di cui parla l’evento: il tipo e l’identificativo, da leggere con l’API se vi serve tutto. |
data | Quello che l’evento porta con sé, diverso per ogni tipo. |
Verificare la firma
Alla creazione dell’iscrizione nasce un segreto whsec_…, mostrato una volta sola. Ogni consegna è firmata con quello: X-Hakubase-Signature è sha256= seguito dall’HMAC-SHA256, in esadecimale, di {X-Hakubase-Timestamp}.{corpo}. La chiave è il segreto intero, prefisso whsec_ compreso, e il corpo è quello grezzo, prima di qualunque parsing.
Il momento entra nella firma, così una consegna registrata oggi non si può riusare domani: controllate anche che il timestamp sia recente.
import crypto from 'node:crypto';
function verify(rawBody, headers, secret) {
const ts = headers['x-hakubase-timestamp'];
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(`${ts}.`)
.update(rawBody)
.digest('hex');
const got = headers['x-hakubase-signature'] ?? '';
const same = got.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected));
const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
return same && fresh;
}
import hashlib, hmac, time
def verify(raw_body: bytes, headers, secret: str) -> bool:
ts = headers["X-Hakubase-Timestamp"]
expected = "sha256=" + hmac.new(
secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256
).hexdigest()
fresh = abs(time.time() - int(ts)) < 300
return fresh and hmac.compare_digest(expected, headers["X-Hakubase-Signature"])
Ruotare il segreto non fa perdere consegne: per 24 ore il vecchio resta valido, e ogni consegna porta anche X-Hakubase-Signature-Previous, firmata con quello. Avete un giorno per cambiare il segreto dalla vostra parte.
Rispondere, e i nuovi tentativi
Rispondete con un codice 2xx entro 10 secondi: qualunque altra risposta, o il silenzio, vale come consegna non riuscita.
Una consegna non riuscita si riprova dopo 1, 5, 15, 60, 180, 360, 720, 1440 e 1440 minuti — circa settanta ore in tutto — con un piccolo scarto a caso, perché le consegne accumulate mentre il vostro indirizzo era giù non tornino tutte nello stesso istante. Finiti i tentativi, la consegna resta tra quelle non riuscite e si riprova a mano.
X-Hakubase-Delivery non cambia fra un tentativo e l’altro: è così che riconoscete un doppione e lo scartate. Fate in modo che ricevere due volte la stessa consegna non faccia danni.
Un’iscrizione che non riesce a consegnare niente per sette giorni si sospende da sola, e lo scrive nello storico. Si riattiva dalla sua scheda, e da quel momento riceve gli eventi nuovi: quelli di mentre era sospesa non tornano.
Provare
Dalla scheda dell’iscrizione, Invia un evento di prova manda una consegna vera, firmata con il segreto vero, del primo tipo di evento a cui siete iscritti. Il corpo, però, è d’esempio: {"test": true, "by": "…"}. Ogni tentativo — quando, che risposta, quanto ci ha messo, il prossimo — si legge nella scheda Esecuzioni accanto.
Server MCP
POST https://<workspace>.hakubase.com/api/mcp
Per ogni risorsa ci sono gli strumenti hakubase_list_… e hakubase_get_…, e dove la risorsa si scrive anche hakubase_create_… e hakubase_update_…: trentuno in tutto, generati dalle stesse definizioni dell’API.
Il permesso apre lo strumento, e senza non si vede: un assistente non riceve nemmeno l’elenco degli strumenti che poi gli verrebbero rifiutati.
Collegarsi
Con OAuth — la strada giusta per una persona. Il client MCP si collega da solo: scopre il server di autorizzazione, si registra, apre il consenso nel browser e riceve il suo token. Nessun segreto da copiare, e l’app collegata compare nel profilo della persona, da dove si stacca in un clic.
- Due scope,
readewrite. Conreadgli strumenti che scrivono spariscono, anche per chi avrebbe il permesso. - Il token porta il ruolo, non i permessi: un permesso tolto in Hakubase sparisce subito anche per l’assistente.
- Il token vale un’ora; il rinnovo trenta giorni, e cambia a ogni uso.
Con la chiave, per un client che non sa fare OAuth: la stessa Authorization: Bearer hb_… dell’API.