Integra Sellio via HTTP o collega il tuo agente IA
Una semplice REST API e un server MCP nativo, con la stessa chiave, lo stesso ambito di tenant e lo stesso rate limit. Scopri i campi di qualsiasi oggetto e inizia a leggere e scrivere record in pochi minuti.
Avvio rapido
Da zero alla tua prima chiamata in tre passaggi:
- Nel CRM, apri Impostazioni → API e sviluppatori e genera una API key. Conservala in modo sicuro; non verrà mostrata di nuovo.
- Usa la chiave nell’header Authorization: Bearer. Identifica il tenant proprietario, quindi tutto è già limitato al suo ambito.
- Effettua la tua prima chiamata ed elenca i tuoi oggetti.
curl -H "Authorization: Bearer sk_live_..." \
https://www.selliocrm.com/api/v1/objectsAutenticazione
Ogni richiesta porta una API key nell’header Authorization: Bearer. Quella chiave identifica il tenant proprietario, quindi tutto è automaticamente limitato al suo ambito, con RLS e RBAC. Non esporre mai la chiave nel browser; usala solo lato server. Generi e revochi le chiavi in Impostazioni → API e sviluppatori.
Ambiti della chiave
Quando generi una chiave ne scegli l’ambito e dai a ogni integrazione solo l’accesso di cui ha bisogno. I limiti si applicano allo stesso modo in REST e MCP, perché entrambe le interfacce usano la stessa chiave e lo stesso controllo. Una chiave senza ambito impostato (l’impostazione predefinita) ha pieno accesso in lettura e scrittura a tutti gli oggetti, come prima.
- Sola lettura: la chiave legge i dati ma non può creare, modificare o eliminare. Blocca POST, PATCH e DELETE in REST e i tool create_record, update_record e delete_record in MCP, con 403 e { "error": "errors.apiKeyReadOnly" }.
- Limitata agli oggetti: scegli gli oggetti a cui la chiave può accedere. Qualsiasi oggetto fuori dall’elenco restituisce 403 con { "error": "errors.apiKeyObjectDenied" }. L’elenco degli oggetti arriva già filtrato in base all’ambito e le attività contano come l’oggetto activity.
- Consenti l’eliminazione: per impostazione predefinita una chiave non elimina MAI (nemmeno con la scrittura abilitata). Attivala quando generi la chiave per abilitare DELETE /{object}/{id} e il tool delete_record. Senza di essa, l’eliminazione restituisce 403 con { "error": "errors.apiKeyDeleteDenied" }. L’eliminazione è soft: i record vanno nel cestino e possono essere ripristinati.
REST API v1
Record di qualsiasi oggetto, inviando e ricevendo JSON. {object} è l’apiName (per esempio contact, lead, opportunity) e {id} è l’uuid del record. Gli oggetti personalizzati usano gli stessi endpoint generici.
Parametri di elenco (GET /{object}): limit (da 1 a 500, default 50), offset (offset di paginazione, default 0; il totale è nel campo total), search (ricerca per nome, senza distinzione di accenti e maiuscole/minuscole), order (campo con :asc o :desc) e filter. Filtri avanzati: ripeti filter=field:operator:value per combinarne diversi (es. filter=amount:gte:1000&filter=stage:eq:won). Operatori: eq, neq, contains, gt, lt, gte, lte, empty, not_empty (empty e not_empty non prendono valore). I filtri si combinano con AND logico (non esiste OR) e fino a 12 per richiesta. La vecchia forma filter=field:value (senza operatore) significa ancora uguaglianza esatta.
# Elenco (limit, offset, search, filter, order)
curl -H "Authorization: Bearer sk_live_..." \
"https://www.selliocrm.com/api/v1/opportunity?limit=20&order=updated_at:desc&filter=stage:won"
# Filtri avanzati: ripeti filter=field:operator:value (in AND tra loro)
curl -H "Authorization: Bearer sk_live_..." \
"https://www.selliocrm.com/api/v1/opportunity?filter=amount:gte:1000&filter=stage:eq:won&filter=name:contains:acme"
# Un record
curl -H "Authorization: Bearer sk_live_..." \
https://www.selliocrm.com/api/v1/contact/8f3c...
# Crea
curl -X POST -H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"name":"Ana Souza","email":"ana@acme.com","source":"site"}' \
https://www.selliocrm.com/api/v1/contact
# Creazione in blocco (fino a 500; risposta con successo parziale per elemento)
curl -X POST -H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"records":[{"name":"Ana"},{"name":"Bruno"}]}' \
https://www.selliocrm.com/api/v1/contact/bulk
# → { "results": [ { "index": 0, "ok": true, "id": "..." },
# { "index": 1, "ok": false, "error": "..." } ],
# "created": 1, "failed": 1 }
# Aggiorna (parziale)
curl -X PATCH -H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"source":"referral"}' \
https://www.selliocrm.com/api/v1/contact/8f3c...
# Elimina (soft, va nel cestino; richiede l’ambito di eliminazione sulla chiave)
curl -X DELETE -H "Authorization: Bearer sk_live_..." \
https://www.selliocrm.com/api/v1/contact/8f3c...Discovery di oggetti e campi
Prima di creare o aggiornare un record, scopri i campi dell’oggetto: cosa esiste, cosa è obbligatorio e quali valori accetta un campo select. Niente supposizioni e nessuna necessità di ispezionare un record esistente. GET /objects/{apiName} restituisce l’oggetto e i suoi campi, con required, options (per select), targetObject (per lookup), unique e readOnly (campi formula o rollup, non scrivibili).
GET https://www.selliocrm.com/api/v1/objects/contact
# → {
# "apiName": "contact",
# "label": "Contact",
# "fields": [
# { "apiName": "name", "label": "Name", "type": "text", "required": true },
# { "apiName": "email", "label": "Email", "type": "email", "required": false },
# { "apiName": "source", "label": "Source", "type": "select", "required": true,
# "options": [ { "value": "site", "label": "Website" },
# { "value": "referral", "label": "Referral" } ] },
# { "apiName": "company", "label": "Company", "type": "lookup",
# "required": false, "targetObject": "company" }
# ]
# }In MCP, il tool describe_object restituisce gli stessi campi all’agente.
Dati per BI e automazione
GET /data/{object} restituisce i record appiattiti in righe tabellari stabili (colonne fisse id, created_at, updated_at, owner_id, piĂą una per campo), ordinati per updated_at desc. Accetta la paginazione tramite page e pageSize (o limit e offset), i filtri incrementali updated_since e created_since (ISO 8601) e filter=field:value. Pensato per Power BI, Tableau, Looker e per il polling da Zapier, Make e n8n.
curl -H "Authorization: Bearer sk_live_..." \
"https://www.selliocrm.com/api/v1/data/opportunity?pageSize=100&updated_since=2026-01-01T00:00:00Z"
# → { "object": "opportunity",
# "columns": [ { "name": "id", "type": "id" }, ... ],
# "page": 1, "pageSize": 100, "total": 42,
# "rows": [ { "id": "...", "created_at": "...", "amount": 1200, ... } ] }Server MCP per agenti IA
Sellio exposes a native MCP (Model Context Protocol) server so your AI agent can read and write the CRM safely. It is the same API key, the same rate limit, the same validations and the same RBAC. The server URL is https://www.selliocrm.com/api/mcp. Available tools:
list_objects: Elenca gli oggetti del tenant.describe_object: Campi di un oggetto (obbligatori, tipi, opzioni).list_records: Elenca e cerca record.get_record: Un record per id.create_record: Crea un record.update_record: Aggiorna campi.delete_record: Elimina (soft → cestino). Richiede l’ambito di eliminazione sulla chiave.
I client che supportano MCP remoto su HTTP usano l’URL con l’header Authorization: Bearer. Sui client senza HTTP remoto nativo, usa il bridge mcp-remote:
{
"mcpServers": {
"sellio-crm": {
"command": "npx",
"args": [
"-y", "mcp-remote",
"https://www.selliocrm.com/api/mcp",
"--header", "Authorization: Bearer sk_live_..."
]
}
}
}OAuth e app connesse
Per integrare PER CONTO DELL’UTENTE (senza che il cliente incolli una chiave admin), usa il flusso OAuth 2.0 Authorization Code con PKCE. L’utente vede una schermata di consenso con gli scope richiesti, approva e la tua app riceve un access token che può fare solo ciò che è stato consentito. Le app sono selezionate: registra e pubblica l’app nel marketplace per ottenere un client_id e un client_secret. Il flusso in quattro passaggi:
- Reindirizza l’utente a /api/oauth/authorize con client_id, redirect_uri (corrispondenza esatta con l’allowlist dell’app), scope, state e PKCE (code_challenge con code_challenge_method=S256). PKCE è obbligatorio.
- L’utente autenticato vede la schermata di consenso e approva. Il CRM reindirizza al tuo redirect_uri con code e lo stesso state.
- Sul tuo server, scambia il code su POST /api/oauth/token (con code_verifier, client_id, client_secret e redirect_uri) per un access token (Bearer, circa 1 ora) e un refresh token.
- Chiama la REST v1 API e il server MCP con Authorization: Bearer at_..., limitati al consenso. Rinnova con grant_type=refresh_token; il refresh viene ruotato (il vecchio scade all’uso) e il riutilizzo revoca l’intera famiglia di token.
# 1) Invia l’utente al consenso (PKCE S256 + state)
GET https://www.selliocrm.com/api/oauth/authorize
?response_type=code
&client_id=app_...
&redirect_uri=https://your-app.example.com/callback # corrispondenza ESATTA dall’allowlist
&scope=records:read%20records:write:opportunity%20activities:read
&state=<random>
&code_challenge=<base64url(sha256(code_verifier))>
&code_challenge_method=S256
# → l’utente approva → 302 redirect_uri?code=<authcode>&state=<...>
# 2) Scambia il code per i token (lato server; invia il client_secret)
curl -X POST https://www.selliocrm.com/api/oauth/token \
-d grant_type=authorization_code \
-d code=<authcode> \
-d code_verifier=<code_verifier> \
-d client_id=app_... \
-d client_secret=secret_... \
-d redirect_uri=https://your-app.example.com/callback
# → { "access_token": "at_...", "token_type": "Bearer",
# "expires_in": 3600, "refresh_token": "rt_...",
# "scope": "records:read records:write:opportunity activities:read" }
# 3) Chiama la v1 / MCP API con l’access token (limitato al consenso)
curl -H "Authorization: Bearer at_..." https://www.selliocrm.com/api/v1/contact
# 4) Rinnova con il refresh token (ruotato: il vecchio rt_ scade all’uso)
curl -X POST https://www.selliocrm.com/api/oauth/token \
-d grant_type=refresh_token -d refresh_token=rt_... \
-d client_id=app_... -d client_secret=secret_...Gli scope OAuth corrispondono allo stesso enforcement delle chiavi (lettura, scrittura e per oggetto). Richiedi il minimo necessario; l’utente concede un sottoinsieme in fase di installazione e il token opera solo entro di esso.
- records:read e records:write: leggere e scrivere record di tutti gli oggetti (a grana grossa).
- records:read:contact e records:write:opportunity: raffinamento per oggetto (apiName), quando l’app ha bisogno solo di alcuni oggetti.
- activities:read e activities:write: leggere e scrivere attivitĂ .
- objects:read: scoprire la struttura di oggetti e campi.
Widget incorporati
La tua app può incorporare la propria schermata (un widget) all’interno del CRM: nel dettaglio di un record o nella dashboard. Il widget gira in un iframe sandboxed, servito dalla tua origine (nell’allowlist dell’app). L’host invia al widget solo il contesto UI tramite postMessage: recordId, objectApiName, locale e theme. Nessun dato di business, token o segreto viaggia in quel messaggio. Per leggere o scrivere dati, il tuo widget usa la OAuth API con il proprio token (gli scope che il tenant ha concesso in fase di installazione).
- Dichiara il widget nella tua app (key, title, location record o dashboard e l’embedUrl) ed elenca l’origine dell’embedUrl nell’allowlist delle origini dell’app.
- Nel tuo widget, ascolta i messaggi dell’host e accetta solo quelli provenienti dall’origine dell’host (event.origin). L’host invia sellio:widget:context (versione 1) con { recordId, objectApiName, locale, theme, installId }.
- Il tuo widget può rispondere solo con sellio:widget:ready (per ricevere di nuovo il contesto) e sellio:widget:resize con { height } (per adattare la propria altezza, con un limite massimo). Qualsiasi altro messaggio viene ignorato.
- Per i dati, chiama la OAuth API (Bearer) con il token dell’app. L’host non passa mai un token o dati tramite postMessage.
// All’interno del TUO widget (la pagina su https://widgets.yourapp.com in esecuzione nell’iframe).
// L’host invia SOLO il contesto UI. Per i DATI, usa la OAuth API con il TUO token.
const HOST = 'https://www.selliocrm.com'; // Convalida SEMPRE l’origine dell’host
window.addEventListener('message', (event) => {
if (event.origin !== HOST) return; // accetta solo l’origine dell’host
const msg = event.data;
if (!msg || msg.version !== 1) return;
if (msg.type === 'sellio:widget:context') {
const { recordId, objectApiName, locale, theme, installId } = msg.payload;
render(recordId, objectApiName, locale, theme);
// Recupera i dati con il TUO token OAuth (non arriva mai tramite postMessage):
// fetch(HOST + '/api/v1/' + objectApiName + '/' + recordId,
// { headers: { Authorization: 'Bearer ' + accessToken } })
}
});
// Richiedi il contesto al caricamento e adatta l’altezza al tuo contenuto:
parent.postMessage({ type: 'sellio:widget:ready', version: 1 }, HOST);
parent.postMessage({ type: 'sellio:widget:resize', version: 1,
payload: { height: document.body.scrollHeight } }, HOST);Sicurezza: l’iframe è sandboxed e cross-origin, quindi il widget non può raggiungere il DOM o i cookie dell’host. L’host convalida l’origine esatta a ogni postMessage (in entrata e in uscita) e l’embedUrl viene sempre verificato rispetto all’allowlist dell’app. Richiedi lo scope widgets:embed per collocare i widget.
Webhook in uscita
Ricevi gli eventi del CRM in tempo reale. Registra un URL di destinazione nelle Impostazioni e scegli gli eventi a cui iscriverti. Ogni consegna è un POST con un body JSON, l’header x-sellio-event con il nome dell’evento e l’header x-sellio-signature con la firma HMAC-SHA256 del body. Il segreto (whsec_...) viene mostrato al momento della creazione del webhook.
Catalogo degli eventi
Esempio di payload
POST https://your-server.example.com/webhook
x-sellio-event: record.created
x-sellio-signature: sha256=<hmac-hex>
Content-Type: application/json
{
"event": "record.created",
"tenantId": "…",
"objectApiName": "lead",
"recordId": "8f3c…",
"data": { "name": "Ana Souza", "email": "ana@acme.com" },
"timestamp": "2026-07-23T12:00:00.000Z"
}Gli eventi di campagna e flow (flow.*) portano campi di iscrizione aggiuntivi:
{
"event": "flow.replied",
"tenantId": "…",
"flowId": "…",
"flowName": "Outbound Q3",
"recordId": "8f3c…",
"enrollmentId": "…",
"objectApiName": "lead",
"data": { "name": "Ana Souza", "email": "ana@acme.com" },
"meta": {},
"timestamp": "2026-07-23T12:00:00.000Z"
}Come verificare la firma
Calcola l’HMAC-SHA256 del body GREZZO (esattamente come ricevuto, senza riserializzarlo) usando il tuo segreto whsec_..., nella forma sha256=<hex>, e confrontalo con l’header x-sellio-signature in tempo costante. Rifiuta quando non corrisponde.
import { createHmac, timingSafeEqual } from 'node:crypto';
// rawBody = il body ricevuto ESATTO (stringa), senza riserializzarlo.
function isValid(rawBody, signatureHeader, secret) {
const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex');
const a = Buffer.from(signatureHeader || '');
const b = Buffer.from(expected);
return a.length === b.length && timingSafeEqual(a, b);
}
// Express: usa express.raw({ type: 'application/json' }) per ottenere il body grezzo.
app.post('/webhook', (req, res) => {
const ok = isValid(req.body.toString('utf8'), req.header('x-sellio-signature'), process.env.SELLIO_WHSEC);
if (!ok) return res.status(401).end();
const event = req.header('x-sellio-event');
// ... elabora l’evento
res.status(200).end();
});Rate limit
Ogni chiave ha un limite al minuto (default 120, configurabile). Le risposte includono gli header X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset. In caso di superamento, l’API restituisce 429 con l’header Retry-After.
Formato degli errori
Gli errori restituiscono JSON nella forma { "error": "message" } con lo stato HTTP corrispondente:
{ "error": "required field: source" } # HTTP 400- 200 e 201: successo (201 in creazione).
- 400: validazione o regola di business.
- 401: chiave mancante o non valida.
- 403: nessun permesso (RBAC) per l’oggetto, oppure bloccato dall’ambito della chiave (sola lettura, oggetto fuori dall’ambito o eliminazione non abilitata).
- 404: oggetto o record non trovato (incluso DELETE/PATCH di un id inesistente).
- 429: rate limit superato (vedi l’header Retry-After).
Limitazioni note
- I filtri si combinano con AND logico (tutti veri contemporaneamente); non esiste OR tra i filtri.
- Gli aggiornamenti sono parziali (PATCH): invii solo i campi da modificare. Non esiste PUT per la sostituzione completa del record.
Specifica e collection
Importa la specifica OpenAPI 3.1 in Swagger, Insomnia o Postman, oppure scarica la collection Postman pronta con ogni endpoint e le variabili apiKey e baseUrl.