Documentazione

Il manuale della sua bacheca

Una pagina, tre corsie: cominci qui, passi la seconda a chi gestisce i casi e mandi la terza a chi scrive il codice.

COMINCI QUI

Per chi amministra

Attivi il piano, inviti il suo team, fissi il suo prefisso dei casi e controlli la fatturazione.

Guida introduttiva →
IL SUO SITO WEB

Per chi pubblica il sito

Tre widget incorporabili con una riga di script: contatto, apertura dei casi e bacheca del cliente finale.

Widget →
INTEGRAZIONE

Per chi integra

API REST con credenziali dedicate, webhook firmati e sincronizzazione incrementale.

Riferimento dell'API →

Cos'è la Bacheca

Una bacheca di supporto multi-brand: lei assiste i SUOI clienti, con il SUO brand, sulla nostra infrastruttura.

La Bacheca di Supporto è una coda unica di casi per la sua operazione: i suoi clienti aprono casi dal suo sito (widget), via API o via email, e il suo team li gestisce da un pannello dedicato su dash.elportaldelcliente.com. Ogni caso entra, avanza e scala senza che nessuno sia sveglio — ma non si chiude senza un nome dietro.

La sua bacheca è un tenant isolato: i suoi casi, i suoi clienti finali e i suoi agenti non si mescolano con quelli di nessun altro. L'isolamento fa parte del design del sistema, non è una configurazione che si possa dimenticare. Se il nostro staff tocca un suo caso come supporto di ultimo livello, l'accesso viene registrato e lei può consultarlo.

I suoi clienti non hanno bisogno di un account con noi. Parlano con il suo brand: il widget, il suo sito e la sua email. Chi ha l'account (e il controllo) è lei.

Guida introduttiva

Da zero all'operatività, in cinque passi.

  1. Crei il suo account su dash.elportaldelcliente.com.
  2. Attivi un piano. Si paga con il saldo del suo account (lo stesso portafoglio di tutto l'ecosistema); se non basta, la schermata le propone di ricaricare esattamente quanto manca.
  3. Inviti i suoi agenti nella scheda «Agenti»: email e ruolo (agente o amministratore). Riceverà un link di accesso da consegnare loro.
  4. Accenda un widget in «Widget e API»: registri il dominio del suo sito come origine autorizzata, copi lo script e lo incolli nel suo sito.
  5. Integra via codice? Emetta la sua credenziale tab_live_ nella stessa scheda e segua il riferimento dell'API in questa pagina.
Il suo prefisso dei casi: in «Widget e API» può fissare 4 lettere proprie (per esempio ACME) e i suoi casi verranno numerati ACME-1044. I casi già emessi non vengono rinumerati.

Il pannello della bacheca

Il pannello del tenant su dash.elportaldelcliente.com, scheda per scheda.

SchedaCosa fa
RiepilogoStato del suo abbonamento, scadenza, rinnovo e rinnovo automatico. Da qui si può anche cancellare.
AgentiInvitare via email con ruolo di agente o amministratore; cambiare i ruoli; revocare l'accesso. Il titolare resta protetto.
Widget e APIPrefisso dei casi, origini autorizzate (CORS), i tre widget con il loro script e le credenziali dell'API.
Accessi del fornitoreL'audit: ogni accesso del nostro staff ai suoi casi, con chi, cosa e quando.

Gli agenti che lei invita vedono e gestiscono i casi della sua bacheca; gli amministratori gestiscono inoltre l'abbonamento, il team e le integrazioni.

Widget per il suo sito

Tre widget, una riga di script ciascuno. Senza iframe di terze parti, senza cookie di tracciamento.

Nella scheda «Widget e API» del suo pannello: accenda il widget, registri il dominio del suo sito come origine autorizzata e copi lo script generato. Il parametro data-color accetta il colore del suo brand.

<script src="https://api.elportaldelcliente.com/api/tablero/widget/tablero.js"
        data-tenant="su-tablero" data-widget="contacto" data-color="#a177ff"></script>
data-widgetCosa faRichiede
contactoModulo di contatto: nome, email e messaggio. Crea un caso nella sua bacheca.Widget acceso + origine autorizzata
abrir-casoApertura di casi con oggetto: restituisce al visitatore il numero del caso.Widget acceso + origine autorizzata
tableroBacheca del cliente finale: vede i propri casi, li apre e risponde dal suo sito.Quanto sopra + token del cliente (sotto)

Protezioni del widget

I widget anonimi accettano 10 tentativi per visitatore (IP) ogni 10 minuti per bacheca. Se il widget è spento, l'origine non è autorizzata o l'abbonamento è scaduto, il widget risponde 404 widget no disponible — volutamente indistinguibile, per non rivelare la configurazione della sua bacheca.

Il widget «bacheca»: identificare il suo cliente

Il widget bacheca mostra a ogni cliente finale solo i propri casi. A questo scopo, il suo server scambia un token effimero per cliente: chiami POST /v1/clientes/:ref/token-widget con la sua credenziale API (server-side, mai dal browser) e consegni il token al widget.

curl -X POST https://api.elportaldelcliente.com/api/tablero/v1/clientes/cli-842/token-widget \
  -H "Authorization: Bearer $TABLERO_KEY"

HTTP/1.1 201
{"success":true,"data":{"token":"tabw_…","expires_at":"2026-08-18 21:15:00"}}
window.TableroPortal.setToken(token);
Il token tabw_ vive 15 minuti e non si rinnova. Alla scadenza, il widget riceverà 401 token_vencido: ne scambi un altro. Non esponga mai la sua credenziale tab_live_ nel browser — il token effimero esiste esattamente per questo.

Credenziali dell'API

Una credenziale Bearer per integrazione, con ambito limitato e revocabile.

Le credenziali si emettono dal suo pannello («Widget e API» → «Credenziali dell'API»). Hanno la forma tab_live_… e vengono mostrate una sola volta: la conservi nel suo gestore di segreti. Noi ne archiviamo soltanto l'hash.

curl -H "Authorization: Bearer tab_live_…" \
  https://api.elportaldelcliente.com/api/tablero/v1/

Al momento dell'emissione può limitarne gli scope. Una credenziale senza scope espliciti ha accesso a tutta la sua bacheca:

ScopeConsente
casos.readLeggere i casi e le loro risposte
casos.writeCreare casi, rispondere e cambiare stato/priorità
clientes.readLeggere i suoi clienti finali
clientes.writeCreare/disattivare clienti finali ed emettere token per i widget
departamentos.manageLeggere e amministrare i reparti (incluse le caselle IMAP)
webhooks.manageLeggere e amministrare i webhook
accesos.readLeggere l'audit degli accessi del fornitore
  • Massimo 5 credenziali attive per bacheca. Revochi quelle che non usa più.
  • La credenziale identifica la sua bacheca: non invii mai tenant_id nel corpo — l'API lo rifiuta con 400.
  • Con l'abbonamento scaduto la credenziale continua a leggere (GET); ogni scrittura risponde 402 suscripcion_vencida.
  • Una credenziale revocata risponde 401 immediatamente su tutte le rotte.
Esclusivamente server-side. La credenziale tab_live_ non va mai nel browser, in un'app mobile né in un repository. Per il browser esiste il token effimero tabw_.

Riferimento dell'API

REST su HTTPS, JSON in entrambe le direzioni, risposte nella forma {success, data}.

Convenzioni: i corpi viaggiano in Content-Type: application/json; le date sono UTC YYYY-MM-DD HH:MM:SS; gli elenchi paginati restituiscono total, page e limit. Ciò che non è suo risponde 404, mai 403: l'API non conferma l'esistenza di risorse altrui.

https://api.elportaldelcliente.com/api/tablero/v1

Radice: identità, piano e utilizzo

GET / è il contratto che non può mentire: restituisce chi è lei, quali scope ha questa credenziale, il suo piano con i limiti, lo stato del suo abbonamento e quanto ha già utilizzato.

curl -H "Authorization: Bearer $TABLERO_KEY" \
  https://api.elportaldelcliente.com/api/tablero/v1/

{"success":true,"data":{
  "tenant":{"slug":"su-tablero","nombre":"Su Marca"},
  "scopes_de_esta_credencial":["casos.read","casos.write"],
  "plan":{"id":"tablero-base","nombre":"BASE","limites":{"max_agentes":5,"max_departamentos":5,"max_articulos_kb":100}},
  "suscripcion":{"estado":"active","current_period_end":"2027-08-18"},
  "uso":{"agentes":3,"departamentos":2},
  "recursos":["/casos","/clientes","/departamentos","/accesos","/webhooks"],
  "version":"v1"}}

Clienti finali

Un cliente finale è un cliente suo (non un account del nostro ecosistema). Si identifica tramite external_ref: l'ID che quel cliente ha nel SUO sistema.

MetodoRottaScopeCosa fa
GET/clientesclientes.readElencare i clienti (fino a 500, i più recenti prima)
POST/clientesclientes.writeCreare o aggiornare per external_ref (upsert idempotente)
GET/clientes/:refclientes.readLeggere un cliente tramite il suo external_ref
DELETE/clientes/:refclientes.writeDisattivare (non cancella: conserva lo storico dei casi)
POST/clientes/:ref/token-widgetclientes.writeEmettere un token effimero per il widget «bacheca» (15 min)
curl -X POST https://api.elportaldelcliente.com/api/tablero/v1/clientes \
  -H "Authorization: Bearer $TABLERO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"external_ref":"cli-842","email":"[email protected]","name":"Maria Rojas"}'
Retry sicuri: POST /clientes è un upsert per external_ref — ripetere la chiamata non duplica i clienti. Nella creazione di casi, invece, un retry di rete crea un caso nuovo: riprovi solo quando non ha ricevuto risposta.

Casi

La risorsa centrale. Un caso ha uno stato, una priorità, un cliente finale facoltativo e una conversazione di risposte. Le note interne degli agenti non vengono mai esposte dall'API.

MetodoRottaScopeCosa fa
GET/casoscasos.readElencare con filtri: stato, cliente, updated_since, limit (1-100), page
POST/casoscasos.writeCreare un caso (subject obbligatorio; cliente_ref, priority, department_id, body/body_html facoltativi)
GET/casos/:idcasos.readDettaglio con la conversazione completa (risposte in ordine). Accetta l'ID o il numero del caso (es. SOLC-1051) indifferentemente
POST/casos/:id/respuestascasos.writeRispondere come il suo team, o come il cliente con en_nombre_de
PATCH/casos/:idcasos.writeCambiare status e/o priority

Creare un caso collegato a un cliente finale:

curl -X POST https://api.elportaldelcliente.com/api/tablero/v1/casos \
  -H "Authorization: Bearer $TABLERO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"subject":"No puedo entrar al panel","cliente_ref":"cli-842","priority":"high",
       "body":"Al iniciar sesion la pagina queda en blanco."}'

HTTP/1.1 201
{"success":true,"data":{"id":"…","numero":"SOLC-1051"}}

Elencare con filtri, e sincronizzazione incrementale con updated_since (ISO 8601): in quella modalità l'ordine è updated_at ascendente, pensato per avanzare pagina per pagina senza perdere modifiche. Salvi l'updated_at più alto che ha elaborato e lo usi come cursore della passata successiva.

curl -H "Authorization: Bearer $TABLERO_KEY" \
  "https://api.elportaldelcliente.com/api/tablero/v1/casos?estado=open&cliente=cli-842&limit=50&page=1"

# sincronizzazione incrementale (ordine: updated_at ascendente)
curl -H "Authorization: Bearer $TABLERO_KEY" \
  "https://api.elportaldelcliente.com/api/tablero/v1/casos?updated_since=2026-08-18T00:00:00Z&limit=100"
Un filtro che non filtra è peggio di un errore: uno estado fuori dalla lista valida (o qualsiasi parametro non riconosciuto) risponde 400 con il dettaglio — non viene mai ignorato in silenzio né restituisce l'elenco completo senza avvisare.

Rispondere a un caso. Senza en_nombre_de la risposta firma come il suo team (author_type: agent); con en_nombre_de firma come quel cliente finale (author_type: client):

curl -X POST https://api.elportaldelcliente.com/api/tablero/v1/casos/$CASO_ID/respuestas \
  -H "Authorization: Bearer $TABLERO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"body":"Ya lo revisamos: intente de nuevo.","autor_nombre":"Soporte Acme"}'

# ...oppure a nome del cliente finale:
  -d '{"body":"Sigue igual desde mi lado.","en_nombre_de":"cli-842"}'
CampoValori
statusopen · pending · resolved · closed
prioritylow · medium · high · urgent
author_typeclient · agent
Isolamento verificabile: ogni interrogazione dei casi passa dall'ambito del suo tenant sul server. L'ID di un caso di un'altra bacheca risponde 404 — l'API si comporta come se non esistesse, perché per lei non esiste.

Reparti

Code interne della sua bacheca (vendite, fatturazione, tecnico…). Ognuna può avere la propria casella IMAP: la bacheca ne esegue il polling e converte le email in arrivo in casi.

MetodoRottaScopeCosa fa
GET/departamentosdepartamentos.manageElencare i reparti (include la configurazione email, senza la password)
POST/departamentosdepartamentos.manageCreare (name obbligatorio; casella IMAP facoltativa; soggetto al limite del piano)
PUT/departamentos/:iddepartamentos.manageModificare qualsiasi campo; email_password vuoto elimina la credenziale
DELETE/departamentos/:iddepartamentos.manageArchiviare (i casi esistenti non vengono toccati)

La casella parla sempre IMAP (email_protocol: imap); la password viene cifrata a riposo e non viene mai restituita. Con email_polling_enabled: 1 il polling è automatico.

Audit degli accessi

Ogni accesso del nostro staff ai casi della sua bacheca viene registrato. È lo stesso audit della scheda «Accessi del fornitore», leggibile via API:

curl -H "Authorization: Bearer $TABLERO_KEY" \
  https://api.elportaldelcliente.com/api/tablero/v1/accesos

Webhook

Il suo sistema lo sa all'istante: ogni evento arriva firmato al suo URL HTTPS.

EventoSi attiva quando
caso.creadoNasce un caso nella sua bacheca — via API, via widget o via email in arrivo
caso.respondidoQualcuno risponde: il suo team (dal pannello o dall'API), o il suo cliente finale (widget, API o email)
caso.estado_cambiadoCambia lo stato di un caso (per mano del suo team, dell'API o del nostro sistema)

Registri quanti webhook le servono con POST /webhooks: URL https, gli eventi che vuole e un secret di almeno 16 caratteri con cui firmeremo ogni consegna.

curl -X POST https://api.elportaldelcliente.com/api/tablero/v1/webhooks \
  -H "Authorization: Bearer $TABLERO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://su-sistema.com/hooks/tablero",
       "eventos":["caso.creado","caso.respondido","caso.estado_cambiado"],
       "secret":"un-secreto-de-al-menos-16-caracteres"}'

Ogni consegna è un POST con l'evento, il momento di emissione e i dati minimi per reagire. La firma copre timestamp.corpo:

POST /hooks/tablero
Content-Type: application/json
X-Tablero-Evento: caso.respondido
X-Tablero-Timestamp: 1765432100
X-Tablero-Firma: 3f1a…  # HMAC-SHA256(secret, timestamp + "." + corpo)

{"evento":"caso.respondido","emitido_at":"2026-08-18T20:15:00.000Z",
 "data":{"caso_id":"…","numero":"SOLC-1051","respuesta_id":"…","autor_tipo":"agent"}}

Verificare la firma

Rifiuti ogni consegna la cui firma non verifichi o il cui timestamp disti più di 5 minuti dall'orologio. Confronti in tempo costante.

// Node.js
const crypto = require('node:crypto');

function verificarFirma(rawBody, headers, secret) {
  const ts  = headers['x-tablero-timestamp'];
  const sig = headers['x-tablero-firma'];
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
  const esperado = crypto.createHmac('sha256', secret)
    .update(ts + '.' + rawBody).digest('hex');
  return sig.length === esperado.length &&
    crypto.timingSafeEqual(Buffer.from(esperado), Buffer.from(sig));
}
# Python
import hashlib, hmac, time

def verificar_firma(raw_body: bytes, headers: dict, secret: str) -> bool:
    ts  = headers.get("x-tablero-timestamp", "")
    sig = headers.get("x-tablero-firma", "")
    if abs(time.time() - float(ts or 0)) > 300:
        return False
    esperado = hmac.new(secret.encode(), f"{ts}.".encode() + raw_body,
                        hashlib.sha256).hexdigest()
    return hmac.compare_digest(esperado, sig)

Semantica di consegna (la legga prima di fidarsi)

  • Un tentativo per evento, senza retry. Se il suo endpoint non risponde 2xx entro 10 secondi, quella consegna va persa — lo stato reale è sempre rileggibile via API.
  • La sua rete di sicurezza è la sincronizzazione incrementale: una passata periodica di GET /casos?updated_since=… recupera qualsiasi evento perso.
  • GET /webhooks mostra fail_count, last_ok_at e last_error_at di ogni webhook: li controlli in fase di debug.
  • Gli eventi vengono emessi a prescindere dalla via: API, widget, il pannello del suo team, il nostro staff o l'email in arrivo del suo cliente finale.

Errori

Errori JSON con success:false; comanda il codice HTTP.

{"success":false,"error":"…"}
HTTPSignificato
400Richiesta non valida: manca un campo obbligatorio o un valore non supera la validazione
401Credenziale assente, non valida o revocata
402Abbonamento scaduto: sola lettura fino al rinnovo (suscripcion_vencida)
403La credenziale non ha lo scope richiesto dall'endpoint
404Non esiste per lei: risorse altrui e rotte inesistenti rispondono allo stesso modo
409Conflitto: limite del piano raggiunto o slug già in uso
429Troppi tentativi (widget anonimi: 10 per IP ogni 10 minuti)
500Errore nostro; se persiste, ci scriva con l'ora esatta

I messaggi di error sono testo leggibile per esseri umani e possono cambiare. Ramifichi il suo codice sul codice HTTP e su questi slug stabili:

Slug stabileSignificato
suscripcion_vencida402 sulle scritture con l'abbonamento scaduto
token_vencido401 del widget «bacheca» quando il token tabw_ è scaduto: ne scambi un altro
saldo_insuficiente402 del pannello all'attivazione/rinnovo senza saldo sufficiente

Piani

Cinque piani annuali, la stessa bacheca. La differenza è quante persone e quanta struttura ci stanno.

I prezzi in vigore sono pubblicati nella pagina principale e via API pubblica (GET /api/public/tablero/plans). Tutti i piani includono casi illimitati, widget, API, webhook e audit degli accessi.

PianoAgentiRepartiArticoli KB
ZERO2225
BASE55100
PLUS1010250
PRO2015500
MAX50251000
Nessuna prova gratuita, di proposito. Il piano ZERO esiste per cominciare in piccolo con un impegno reale. L'attivazione addebita l'anno intero dal saldo del suo account.

Alla scadenza dell'abbonamento c'è un periodo di tolleranza; poi la bacheca passa in sola lettura (i GET continuano a rispondere, le scritture restituiscono 402). I suoi dati non vengono cancellati.

Limiti operativi

  • Casi: illimitati in tutti i piani. Gli elenchi accettano limit da 1 a 100 per pagina.
  • Clienti finali e audit: gli elenchi restituiscono fino a 500 righe (i più recenti prima). Usi GET /clientes/:ref per letture puntuali.
  • Credenziali: fino a 5 attive per bacheca.
  • Widget anonimi: 10 tentativi per visitatore (IP) ogni 10 minuti.
  • Allegati: l'API non accetta ancora file; gli allegati delle email in arrivo vengono conservati con il caso.

La dorsale non impone oggi una quota rigida di chiamate; operi con un ritmo ragionevole (pause tra le pagine, retry con attesa esponenziale). Se un'integrazione ha bisogno di volume garantito, parliamone prima.