Documentação

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.

COMECE AQUI

Para quem administra

Ative o plano, convide sua equipe, defina seu prefixo de casos e controle o faturamento.

Guia de início →
SEU SITE

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 →
INTEGRAÇÃO

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.

Seus clientes não precisam de conta conosco. Eles falam com a sua marca: o widget, seu site e seu e-mail. Quem tem a conta (e o controle) é você.

Guia de início

Do zero à operação, em cinco passos.

  1. Crie sua conta em dash.elportaldelcliente.com.
  2. 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.
  3. Convide seus agentes na aba «Agentes»: e-mail e papel (agente ou administrador). Você receberá um link de acesso para entregar a eles.
  4. 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.
  5. Vai integrar por código? Emita sua credencial tab_live_ na mesma aba e siga a referência da API desta página.
Seu prefixo de casos: em «Widgets e API» você pode definir 4 letras próprias (por exemplo 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.

AbaO que faz
ResumoStatus da sua assinatura, vencimento, renovação e renovação automática. É aqui também que se cancela.
AgentesConvidar por e-mail com papel de agente ou administrador; alterar papéis; remover acesso. O titular fica protegido.
Widgets e APIPrefixo de casos, origens autorizadas (CORS), os três widgets com seu script e as credenciais da API.
Acessos do provedorA 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-widgetO que fazRequer
contactoFormulário de contato: nome, e-mail e mensagem. Cria um caso no seu quadro.Widget ligado + origem autorizada
abrir-casoAbertura de casos com assunto: devolve o número do caso ao visitante.Widget ligado + origem autorizada
tableroQuadro 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);
O 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:

ScopePermite
casos.readLer casos e suas respostas
casos.writeCriar casos, responder e mudar status/prioridade
clientes.readLer seus clientes finais
clientes.writeCriar/desativar clientes finais e emitir tokens de widget
departamentos.manageLer e administrar departamentos (inclui caixas IMAP)
webhooks.manageLer e administrar webhooks
accesos.readLer 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_id no 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.
Server-side somente. A credencial 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étodoRotaScopeO que faz
GET/clientesclientes.readListar clientes (até 500, os mais recentes primeiro)
POST/clientesclientes.writeCriar ou atualizar por external_ref (upsert idempotente)
GET/clientes/:refclientes.readLer um cliente pelo seu external_ref
DELETE/clientes/:refclientes.writeDesativar (não apaga: preserva o histórico de casos)
POST/clientes/:ref/token-widgetclientes.writeEmitir 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"}'
Retentativas seguras: 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étodoRotaScopeO que faz
GET/casoscasos.readListar com filtros: status, cliente, updated_since, limit (1-100), page
POST/casoscasos.writeCriar um caso (subject obrigatório; cliente_ref, priority, department_id, body/body_html opcionais)
GET/casos/:idcasos.readDetalhe com a conversa completa (respostas ordenadas). Aceita o ID ou o número do caso (ex.: SOLC-1051) indistintamente
POST/casos/:id/respuestascasos.writeResponder como sua equipe, ou como o cliente com en_nombre_de
PATCH/casos/:idcasos.writeMudar 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"
Um filtro que não filtra é pior que um erro: um 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"}'
CampoValores
statusopen · pending · resolved · closed
prioritylow · medium · high · urgent
author_typeclient · agent
Isolamento verificável: toda consulta de casos passa pelo escopo do seu inquilino no servidor. Um ID de caso de outro quadro responde 404 — a API se comporta como se ele não existisse, porque para você não existe.

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étodoRotaScopeO que faz
GET/departamentosdepartamentos.manageListar departamentos (inclui a configuração de e-mail, sem a senha)
POST/departamentosdepartamentos.manageCriar (name obrigatório; caixa IMAP opcional; sujeito ao limite do plano)
PUT/departamentos/:iddepartamentos.manageEditar qualquer campo; email_password vazio apaga a credencial
DELETE/departamentos/:iddepartamentos.manageArquivar (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.

EventoDispara quando
caso.creadoNasce um caso no seu quadro — por API, por widget ou por e-mail recebido
caso.respondidoAlguém responde: sua equipe (pelo painel ou pela API), ou seu cliente final (widget, API ou e-mail)
caso.estado_cambiadoMuda 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 /webhooks mostra fail_count, last_ok_at e last_error_at de 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":"…"}
HTTPSignificado
400Requisição inválida: falta um campo obrigatório ou um valor não passa na validação
401Credencial ausente, inválida ou revogada
402Assinatura vencida: somente leitura até renovar (suscripcion_vencida)
403A credencial não tem o scope que o endpoint exige
404Não existe para você: recursos alheios e rotas inexistentes respondem igual
409Conflito: limite do plano atingido ou slug já em uso
429Tentativas demais (widgets anônimos: 10 por IP a cada 10 minutos)
500Erro 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ávelSignificado
suscripcion_vencida402 em escritas com a assinatura vencida
token_vencido401 do widget «quadro» quando o token tabw_ expirou: troque por outro
saldo_insuficiente402 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.

PlanoAgentesDepartamentosArtigos de KB
ZERO2225
BASE55100
PLUS1010250
PRO2015500
MAX50251000
Sem teste grátis, de propósito. O plano ZERO existe para começar pequeno com compromisso real. A ativação debita o ano completo do saldo da sua conta.

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 limit de 1 a 100 por página.
  • Clientes finais e auditoria: as listagens devolvem até 500 linhas (os mais recentes primeiro). Use GET /clientes/:ref para 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.