O manual do seu quadro
Uma página, três trilhas: comece aqui, passe a segunda para quem atende os casos e mande a terceira para quem escreve o código.
Para quem administra
Ative o plano, convide sua equipe, defina seu prefixo de casos e controle o faturamento.
Guia de início →Para quem publica o site
Três widgets embutíveis com uma linha de script: contato, abertura de casos e quadro do cliente final.
Widgets →Para quem integra
API REST com credenciais próprias, webhooks assinados e sincronização incremental.
Referência da API →O que é o Quadro
Um quadro de suporte multimarca: você atende os SEUS clientes, sob a SUA marca, sobre a nossa infraestrutura.
O Quadro de Suporte é uma fila única de casos para a sua operação: seus clientes abrem casos pelo seu site (widgets), por API ou por e-mail, e sua equipe os atende em um painel próprio em dash.elportaldelcliente.com. Cada caso entra, avança e escala sem que ninguém esteja acordado — mas não é concluído sem um nome por trás.
Seu quadro é um inquilino isolado: seus casos, seus clientes finais e seus agentes não se misturam com os de mais ninguém. O isolamento faz parte do design do sistema, não é uma configuração que se possa esquecer. Se a nossa equipe tocar em um caso seu como suporte de último nível, o acesso fica registrado e você pode consultá-lo.
Guia de início
Do zero à operação, em cinco passos.
- Crie sua conta em dash.elportaldelcliente.com.
- Ative um plano. O pagamento sai do saldo da sua conta (a mesma carteira de todo o ecossistema); se faltar, a tela oferece recarregar exatamente o que falta.
- Convide seus agentes na aba «Agentes»: e-mail e papel (agente ou administrador). Você receberá um link de acesso para entregar a eles.
- Ligue um widget em «Widgets e API»: registre o domínio do seu site como origem autorizada, copie o script e cole no seu site.
- Vai integrar por código? Emita sua credencial
tab_live_na mesma aba e siga a referência da API desta página.
ACME) e seus casos são numerados ACME-1044. Os casos já emitidos não são renumerados.O painel do quadro
O painel do inquilino em dash.elportaldelcliente.com, aba por aba.
| Aba | O que faz |
|---|---|
Resumo | Status da sua assinatura, vencimento, renovação e renovação automática. É aqui também que se cancela. |
Agentes | Convidar por e-mail com papel de agente ou administrador; alterar papéis; remover acesso. O titular fica protegido. |
Widgets e API | Prefixo de casos, origens autorizadas (CORS), os três widgets com seu script e as credenciais da API. |
Acessos do provedor | A auditoria: cada acesso da nossa equipe a casos seus, com quem, o quê e quando. |
Os agentes que você convida veem e atendem os casos do seu quadro; os administradores também gerenciam a assinatura, a equipe e as integrações.
Widgets para seu site
Três widgets, uma linha de script cada um. Sem iframes de terceiros, sem cookies de rastreamento.
Na aba «Widgets e API» do seu painel: ligue o widget, registre o domínio do seu site como origem autorizada e copie o script gerado. O parâmetro data-color aceita a cor da sua marca.
<script src="https://api.elportaldelcliente.com/api/tablero/widget/tablero.js"
data-tenant="su-tablero" data-widget="contacto" data-color="#a177ff"></script>
| data-widget | O que faz | Requer |
|---|---|---|
contacto | Formulário de contato: nome, e-mail e mensagem. Cria um caso no seu quadro. | Widget ligado + origem autorizada |
abrir-caso | Abertura de casos com assunto: devolve o número do caso ao visitante. | Widget ligado + origem autorizada |
tablero | Quadro do cliente final: ele vê os próprios casos, abre e responde pelo seu site. | O anterior + token de cliente (abaixo) |
Proteções do widget
Os widgets anônimos aceitam 10 tentativas por visitante (IP) a cada 10 minutos por quadro. Se o widget estiver desligado, a origem não estiver autorizada ou a assinatura tiver vencido, o widget responde 404 widget no disponible — indistinguível de propósito, para não revelar a configuração do seu quadro.
O widget «quadro»: identificar o seu cliente
O widget de quadro mostra a cada cliente final somente os próprios casos. Para isso, o seu servidor troca um token efêmero por cliente: chame POST /v1/clientes/:ref/token-widget com a sua credencial de API (server-side, nunca a partir do navegador) e entregue o token ao 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 minutos e não se renova. Quando vencer, o widget receberá 401 token_vencido: troque por outro. Nunca exponha sua credencial tab_live_ no navegador — o token efêmero existe exatamente para isso.Credenciais da API
Uma credencial Bearer por integração, com escopo delimitado e revogável.
As credenciais são emitidas no seu painel («Widgets e API» → «Credenciais da API»). Têm a forma tab_live_… e são exibidas uma única vez: guarde-a no seu gerenciador de segredos. Nós armazenamos apenas o hash.
curl -H "Authorization: Bearer tab_live_…" \
https://api.elportaldelcliente.com/api/tablero/v1/
Ao emiti-la, você pode delimitar os seus scopes. Uma credencial sem scopes explícitos tem acesso a todo o seu quadro:
| Scope | Permite |
|---|---|
casos.read | Ler casos e suas respostas |
casos.write | Criar casos, responder e mudar status/prioridade |
clientes.read | Ler seus clientes finais |
clientes.write | Criar/desativar clientes finais e emitir tokens de widget |
departamentos.manage | Ler e administrar departamentos (inclui caixas IMAP) |
webhooks.manage | Ler e administrar webhooks |
accesos.read | Ler a auditoria de acessos do provedor |
- Máximo de 5 credenciais ativas por quadro. Revogue as que você não usa mais.
- A credencial identifica o seu quadro: nunca envie
tenant_idno corpo — a API rejeita com 400. - Com a assinatura vencida, a credencial continua lendo (GET); toda escrita responde
402 suscripcion_vencida. - Uma credencial revogada responde 401 imediatamente em todas as rotas.
tab_live_ jamais vai no navegador, em um app móvel ou em um repositório. Para o navegador existe o token efêmero tabw_.Referência da API
REST sobre HTTPS, JSON nas duas direções, respostas com a forma {success, data}.
Convenções: os corpos vão em Content-Type: application/json; as datas são UTC YYYY-MM-DD HH:MM:SS; as listagens paginadas devolvem total, page e limit. O que não é seu responde 404, nunca 403: a API não confirma a existência de recursos alheios.
https://api.elportaldelcliente.com/api/tablero/v1
Raiz: identidade, plano e uso
GET / é o contrato que não pode mentir: devolve quem é você, quais scopes esta credencial tem, seu plano com limites, o status da sua assinatura e quanto já foi usado.
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"}}
Clientes finais
Um cliente final é um cliente seu (não uma conta do nosso ecossistema). Ele é identificado por external_ref: o ID que esse cliente tem no SEU sistema.
| Método | Rota | Scope | O que faz |
|---|---|---|---|
| GET | /clientes | clientes.read | Listar clientes (até 500, os mais recentes primeiro) |
| POST | /clientes | clientes.write | Criar ou atualizar por external_ref (upsert idempotente) |
| GET | /clientes/:ref | clientes.read | Ler um cliente pelo seu external_ref |
| DELETE | /clientes/:ref | clientes.write | Desativar (não apaga: preserva o histórico de casos) |
| POST | /clientes/:ref/token-widget | clientes.write | Emitir token efêmero para o widget «quadro» (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 é um upsert por external_ref — repetir a chamada não duplica clientes. Ao criar casos, por outro lado, uma retentativa de rede cria um caso novo: repita somente quando não recebeu resposta.Casos
O recurso central. Um caso tem status, prioridade, um cliente final opcional e uma conversa de respostas. As notas internas dos agentes jamais são expostas pela API.
| Método | Rota | Scope | O que faz |
|---|---|---|---|
| GET | /casos | casos.read | Listar com filtros: status, cliente, updated_since, limit (1-100), page |
| POST | /casos | casos.write | Criar um caso (subject obrigatório; cliente_ref, priority, department_id, body/body_html opcionais) |
| GET | /casos/:id | casos.read | Detalhe com a conversa completa (respostas ordenadas). Aceita o ID ou o número do caso (ex.: SOLC-1051) indistintamente |
| POST | /casos/:id/respuestas | casos.write | Responder como sua equipe, ou como o cliente com en_nombre_de |
| PATCH | /casos/:id | casos.write | Mudar status e/ou priority |
Criar um caso vinculado a um cliente final:
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"}}
Listar com filtros, e sincronização incremental com updated_since (ISO 8601): nesse modo a ordem é updated_at ascendente, pensada para avançar página a página sem perder mudanças. Guarde o updated_at mais alto que você processou e use-o como cursor da próxima passada.
curl -H "Authorization: Bearer $TABLERO_KEY" \
"https://api.elportaldelcliente.com/api/tablero/v1/casos?estado=open&cliente=cli-842&limit=50&page=1"
# sincronizacao incremental (ordem: 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 fora da lista válida (ou qualquer parâmetro que não reconhecemos) responde com 400 e o detalhe — nunca é ignorado em silêncio nem devolve a lista completa sem avisar.Responder um caso. Sem en_nombre_de, a resposta assina como a sua equipe (author_type: agent); com en_nombre_de, assina como esse cliente final (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"}'
# ...ou em nome do cliente final:
-d '{"body":"Sigue igual desde mi lado.","en_nombre_de":"cli-842"}'
| Campo | Valores |
|---|---|
status | open · pending · resolved · closed |
priority | low · medium · high · urgent |
author_type | client · agent |
Departamentos
Filas internas do seu quadro (vendas, faturamento, técnico…). Cada uma pode ter a sua própria caixa IMAP: o quadro a sonda e converte os e-mails recebidos em casos.
| Método | Rota | Scope | O que faz |
|---|---|---|---|
| GET | /departamentos | departamentos.manage | Listar departamentos (inclui a configuração de e-mail, sem a senha) |
| POST | /departamentos | departamentos.manage | Criar (name obrigatório; caixa IMAP opcional; sujeito ao limite do plano) |
| PUT | /departamentos/:id | departamentos.manage | Editar qualquer campo; email_password vazio apaga a credencial |
| DELETE | /departamentos/:id | departamentos.manage | Arquivar (os casos existentes não são tocados) |
A caixa fala IMAP sempre (email_protocol: imap); a senha é criptografada em repouso e nunca é devolvida. Com email_polling_enabled: 1 a sondagem roda automaticamente.
Auditoria de acessos
Cada acesso da nossa equipe a casos do seu quadro fica registrado. Esta é a mesma auditoria da aba «Acessos do provedor», legível por API:
curl -H "Authorization: Bearer $TABLERO_KEY" \
https://api.elportaldelcliente.com/api/tablero/v1/accesos
Webhooks
Seu sistema fica sabendo na hora: cada evento vai assinado para a sua URL HTTPS.
| Evento | Dispara quando |
|---|---|
caso.creado | Nasce um caso no seu quadro — por API, por widget ou por e-mail recebido |
caso.respondido | Alguém responde: sua equipe (pelo painel ou pela API), ou seu cliente final (widget, API ou e-mail) |
caso.estado_cambiado | Muda o status de um caso (pela sua equipe, pela API ou pelo nosso sistema) |
Registre quantos precisar com POST /webhooks: URL https, os eventos que você quer e um segredo de pelo menos 16 caracteres com o qual assinaremos cada entrega.
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"}'
Cada entrega é um POST com o evento, o momento de emissão e os dados mínimos para reagir. A assinatura cobre 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"}}
Verificar a assinatura
Rejeite toda entrega cuja assinatura não confira ou cujo timestamp esteja a mais de 5 minutos do relógio. Compare em tempo constante.
// 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)
Semântica de entrega (leia antes de confiar)
- Uma tentativa por evento, sem retentativas. Se o seu endpoint não responder 2xx em 10 segundos, essa entrega se perde — o estado real sempre pode ser relido pela API.
- Sua rede de segurança é a sincronização incremental: uma passada periódica de
GET /casos?updated_since=…recupera qualquer evento perdido. GET /webhooksmostrafail_count,last_ok_atelast_error_atde cada webhook: consulte-os ao depurar.- Os eventos são emitidos seja qual for a via: API, widgets, o painel da sua equipe, nossa equipe ou o e-mail recebido do seu cliente final.
Erros
Erros JSON com success:false; o código HTTP é o que manda.
{"success":false,"error":"…"}
| HTTP | Significado |
|---|---|
400 | Requisição inválida: falta um campo obrigatório ou um valor não passa na validação |
401 | Credencial ausente, inválida ou revogada |
402 | Assinatura vencida: somente leitura até renovar (suscripcion_vencida) |
403 | A credencial não tem o scope que o endpoint exige |
404 | Não existe para você: recursos alheios e rotas inexistentes respondem igual |
409 | Conflito: limite do plano atingido ou slug já em uso |
429 | Tentativas demais (widgets anônimos: 10 por IP a cada 10 minutos) |
500 | Erro nosso; se persistir, escreva para nós com a hora exata |
As mensagens de error são texto legível para humanos e podem mudar. Ramifique o seu código pelo código HTTP e por estes slugs estáveis:
| Slug estável | Significado |
|---|---|
suscripcion_vencida | 402 em escritas com a assinatura vencida |
token_vencido | 401 do widget «quadro» quando o token tabw_ expirou: troque por outro |
saldo_insuficiente | 402 do painel ao ativar/renovar sem saldo suficiente |
Planos
Cinco planos anuais, o mesmo quadro. A diferença é quanta gente e estrutura cabem.
Os preços vigentes são publicados na página principal e pela API pública (GET /api/public/tablero/plans). Todos os planos incluem casos ilimitados, widgets, API, webhooks e auditoria de acessos.
| Plano | Agentes | Departamentos | Artigos de KB |
|---|---|---|---|
| ZERO | 2 | 2 | 25 |
| BASE | 5 | 5 | 100 |
| PLUS | 10 | 10 | 250 |
| PRO | 20 | 15 | 500 |
| MAX | 50 | 25 | 1000 |
Ao vencer a sua assinatura, há um período de carência; depois o quadro passa a somente leitura (os GET continuam respondendo, as escritas devolvem 402). Seus dados não são apagados.
Limites operacionais
- Casos: ilimitados em todos os planos. As listagens aceitam
limitde 1 a 100 por página. - Clientes finais e auditoria: as listagens devolvem até 500 linhas (os mais recentes primeiro). Use
GET /clientes/:refpara leituras pontuais. - Credenciais: até 5 ativas por quadro.
- Widgets anônimos: 10 tentativas por visitante (IP) a cada 10 minutos.
- Anexos: a API ainda não aceita arquivos; os anexos de e-mail recebido são preservados com o caso.
O tronco não impõe hoje uma cota rígida de chamadas; opere em um ritmo razoável (pausas entre páginas, retentativas com espera exponencial). Se uma integração precisar de volume garantido, vamos conversar antes.