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.
Per chi amministra
Attivi il piano, inviti il suo team, fissi il suo prefisso dei casi e controlli la fatturazione.
Guida introduttiva →Per chi pubblica il sito
Tre widget incorporabili con una riga di script: contatto, apertura dei casi e bacheca del cliente finale.
Widget →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.
Guida introduttiva
Da zero all'operatività, in cinque passi.
- Crei il suo account su dash.elportaldelcliente.com.
- 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.
- Inviti i suoi agenti nella scheda «Agenti»: email e ruolo (agente o amministratore). Riceverà un link di accesso da consegnare loro.
- 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.
- Integra via codice? Emetta la sua credenziale
tab_live_nella stessa scheda e segua il riferimento dell'API in questa pagina.
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.
| Scheda | Cosa fa |
|---|---|
Riepilogo | Stato del suo abbonamento, scadenza, rinnovo e rinnovo automatico. Da qui si può anche cancellare. |
Agenti | Invitare via email con ruolo di agente o amministratore; cambiare i ruoli; revocare l'accesso. Il titolare resta protetto. |
Widget e API | Prefisso dei casi, origini autorizzate (CORS), i tre widget con il loro script e le credenziali dell'API. |
Accessi del fornitore | L'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-widget | Cosa fa | Richiede |
|---|---|---|
contacto | Modulo di contatto: nome, email e messaggio. Crea un caso nella sua bacheca. | Widget acceso + origine autorizzata |
abrir-caso | Apertura di casi con oggetto: restituisce al visitatore il numero del caso. | Widget acceso + origine autorizzata |
tablero | Bacheca 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);
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:
| Scope | Consente |
|---|---|
casos.read | Leggere i casi e le loro risposte |
casos.write | Creare casi, rispondere e cambiare stato/priorità |
clientes.read | Leggere i suoi clienti finali |
clientes.write | Creare/disattivare clienti finali ed emettere token per i widget |
departamentos.manage | Leggere e amministrare i reparti (incluse le caselle IMAP) |
webhooks.manage | Leggere e amministrare i webhook |
accesos.read | Leggere 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_idnel 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.
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.
| Metodo | Rotta | Scope | Cosa fa |
|---|---|---|---|
| GET | /clientes | clientes.read | Elencare i clienti (fino a 500, i più recenti prima) |
| POST | /clientes | clientes.write | Creare o aggiornare per external_ref (upsert idempotente) |
| GET | /clientes/:ref | clientes.read | Leggere un cliente tramite il suo external_ref |
| DELETE | /clientes/:ref | clientes.write | Disattivare (non cancella: conserva lo storico dei casi) |
| POST | /clientes/:ref/token-widget | clientes.write | Emettere 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"}'
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.
| Metodo | Rotta | Scope | Cosa fa |
|---|---|---|---|
| GET | /casos | casos.read | Elencare con filtri: stato, cliente, updated_since, limit (1-100), page |
| POST | /casos | casos.write | Creare un caso (subject obbligatorio; cliente_ref, priority, department_id, body/body_html facoltativi) |
| GET | /casos/:id | casos.read | Dettaglio con la conversazione completa (risposte in ordine). Accetta l'ID o il numero del caso (es. SOLC-1051) indifferentemente |
| POST | /casos/:id/respuestas | casos.write | Rispondere come il suo team, o come il cliente con en_nombre_de |
| PATCH | /casos/:id | casos.write | Cambiare 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"
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"}'
| Campo | Valori |
|---|---|
status | open · pending · resolved · closed |
priority | low · medium · high · urgent |
author_type | client · agent |
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.
| Metodo | Rotta | Scope | Cosa fa |
|---|---|---|---|
| GET | /departamentos | departamentos.manage | Elencare i reparti (include la configurazione email, senza la password) |
| POST | /departamentos | departamentos.manage | Creare (name obbligatorio; casella IMAP facoltativa; soggetto al limite del piano) |
| PUT | /departamentos/:id | departamentos.manage | Modificare qualsiasi campo; email_password vuoto elimina la credenziale |
| DELETE | /departamentos/:id | departamentos.manage | Archiviare (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.
| Evento | Si attiva quando |
|---|---|
caso.creado | Nasce un caso nella sua bacheca — via API, via widget o via email in arrivo |
caso.respondido | Qualcuno risponde: il suo team (dal pannello o dall'API), o il suo cliente finale (widget, API o email) |
caso.estado_cambiado | Cambia 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 /webhooksmostrafail_count,last_ok_atelast_error_atdi 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":"…"}
| HTTP | Significato |
|---|---|
400 | Richiesta non valida: manca un campo obbligatorio o un valore non supera la validazione |
401 | Credenziale assente, non valida o revocata |
402 | Abbonamento scaduto: sola lettura fino al rinnovo (suscripcion_vencida) |
403 | La credenziale non ha lo scope richiesto dall'endpoint |
404 | Non esiste per lei: risorse altrui e rotte inesistenti rispondono allo stesso modo |
409 | Conflitto: limite del piano raggiunto o slug già in uso |
429 | Troppi tentativi (widget anonimi: 10 per IP ogni 10 minuti) |
500 | Errore 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 stabile | Significato |
|---|---|
suscripcion_vencida | 402 sulle scritture con l'abbonamento scaduto |
token_vencido | 401 del widget «bacheca» quando il token tabw_ è scaduto: ne scambi un altro |
saldo_insuficiente | 402 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.
| Piano | Agenti | Reparti | Articoli KB |
|---|---|---|---|
| ZERO | 2 | 2 | 25 |
| BASE | 5 | 5 | 100 |
| PLUS | 10 | 10 | 250 |
| PRO | 20 | 15 | 500 |
| MAX | 50 | 25 | 1000 |
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
limitda 1 a 100 per pagina. - Clienti finali e audit: gli elenchi restituiscono fino a 500 righe (i più recenti prima). Usi
GET /clientes/:refper 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.