Integre o Sellio por HTTP ou conecte o seu agente de IA
Uma API REST simples e um servidor MCP nativo, com a mesma chave, o mesmo escopo de tenant e o mesmo rate limit. Descubra os campos de qualquer objeto e comece a ler e escrever registros em minutos.
Comece rápido
Do zero à primeira chamada em três passos:
- No CRM, abra Configurações → API e desenvolvedores e gere uma API key. Guarde a chave em local seguro; ela não é exibida de novo.
- Use a chave no cabeçalho Authorization: Bearer. Ela identifica o tenant dono, então tudo já fica escopado a ele.
- Faça a primeira chamada e liste os seus objetos.
curl -H "Authorization: Bearer sk_live_..." \
https://www.selliocrm.com/api/v1/objectsAutenticação
Toda requisição leva uma API key no cabeçalho Authorization: Bearer. É ela que identifica o tenant dono, então tudo já fica escopado a ele automaticamente, com RLS e RBAC. Nunca exponha a chave no navegador; use-a somente no servidor. Você gera e revoga chaves em Configurações → API e desenvolvedores.
Escopo das chaves
Ao gerar uma chave você escolhe o escopo dela e dá a cada integração só o acesso de que precisa. Os limites valem igual no REST e no MCP, porque as duas superfícies usam a mesma chave e o mesmo controle. Uma chave sem escopo definido (o padrão) tem acesso total de leitura e escrita a todos os objetos, como antes.
- Somente leitura: a chave lê dados, mas não cria, edita nem exclui. Bloqueia POST, PATCH e DELETE no REST e as ferramentas create_record, update_record e delete_record no MCP, com 403 e { "error": "errors.apiKeyReadOnly" }.
- Limitada a objetos: escolha os objetos que a chave pode acessar. Qualquer objeto fora da lista responde 403 com { "error": "errors.apiKeyObjectDenied" }. A lista de objetos já vem filtrada ao escopo, e as atividades contam como o objeto activity.
- Permitir exclusão: por padrão uma chave NUNCA exclui (nem com escrita liberada). Ligue esta opção ao gerar a chave para habilitar DELETE /{objeto}/{id} e a ferramenta delete_record. Sem ela, a exclusão responde 403 com { "error": "errors.apiKeyDeleteDenied" }. A exclusão é suave: os registros vão para a lixeira e podem ser restaurados.
API REST v1
Registros de qualquer objeto, enviando e recebendo JSON. O {object} é o apiName (por exemplo contact, lead, opportunity) e o {id} é o uuid do registro. Objetos personalizados usam os mesmos endpoints genéricos.
Parâmetros da listagem (GET /{object}): limit (de 1 a 500, padrão 50), offset (deslocamento de paginação, padrão 0; o total vem no campo total), search (busca por nome, ignora acento e caixa), order (campo com :asc ou :desc) e filter. Filtros ricos: repita filter=campo:operador:valor para combinar vários (ex.: filter=amount:gte:1000&filter=stage:eq:won). Operadores: eq, neq, contains, gt, lt, gte, lte, empty, not_empty (empty e not_empty não levam valor). Os filtros combinam com E lógico (não há OU) e valem no máximo 12 por chamada. A forma antiga filter=campo:valor (sem operador) continua sendo igualdade exata.
# Listar (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"
# Filtros ricos: repita filter=campo:operador:valor (E logico entre eles)
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"
# Um registro
curl -H "Authorization: Bearer sk_live_..." \
https://www.selliocrm.com/api/v1/contact/8f3c...
# Criar
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
# Criar em lote (ate 500; resposta com sucesso parcial por item)
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 }
# Atualizar (parcial)
curl -X PATCH -H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"source":"referral"}' \
https://www.selliocrm.com/api/v1/contact/8f3c...
# Excluir (suave, vai para a lixeira; exige o escopo de exclusao na chave)
curl -X DELETE -H "Authorization: Bearer sk_live_..." \
https://www.selliocrm.com/api/v1/contact/8f3c...Descoberta de objetos e campos
Antes de criar ou atualizar um registro, descubra os campos do objeto: o que existe, o que é obrigatório e quais valores um select aceita. Você não precisa adivinhar nem inspecionar um registro que já existe. GET /objects/{apiName} devolve o objeto e seus campos, com required, options (do select), targetObject (do lookup), unique e readOnly (campos de fórmula ou rollup, que não são graváveis).
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" }
# ]
# }No MCP, a ferramenta describe_object devolve os mesmos campos para o agente.
Dados para BI e automação
GET /data/{object} devolve os registros achatados em linhas tabulares estáveis (colunas fixas id, created_at, updated_at, owner_id, mais uma por campo), ordenadas por updated_at desc. Aceita paginação por page e pageSize (ou limit e offset), o filtro incremental updated_since e created_since (ISO 8601) e filter=campo:valor. Feito para Power BI, Tableau, Looker e para o polling de 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, ... } ] }Servidor MCP para agentes de IA
O Sellio expõe um servidor MCP (Model Context Protocol) nativo para que o seu agente de IA leia e escreva no CRM com segurança. É a mesma API key, o mesmo rate limit, as mesmas validações e o mesmo RBAC. A URL do servidor é https://www.selliocrm.com/api/mcp. Ferramentas disponíveis:
list_objects: Lista os objetos do tenant.describe_object: Campos de um objeto (obrigatórios, tipos, opções).list_records: Lista e pesquisa registros.get_record: Um registro pelo id.create_record: Cria um registro.update_record: Atualiza campos.delete_record: Exclui (suave → lixeira). Exige o escopo de exclusão na chave.
Clientes que suportam MCP remoto por HTTP usam a URL com o cabeçalho Authorization: Bearer. Em clientes sem HTTP remoto nativo, use a ponte mcp-remote:
{
"mcpServers": {
"sellio-crm": {
"command": "npx",
"args": [
"-y", "mcp-remote",
"https://www.selliocrm.com/api/mcp",
"--header", "Authorization: Bearer sk_live_..."
]
}
}
}OAuth e apps conectados
Para integrar EM NOME DO USUÁRIO (sem que o cliente cole uma chave de admin), use o fluxo OAuth 2.0 Authorization Code com PKCE. O usuário vê uma tela de consentimento com os escopos pedidos, aprova, e o seu app recebe um access token que só pode fazer o que foi consentido. Os apps são curados: registre e publique o app no marketplace para obter client_id e client_secret. O fluxo em quatro passos:
- Redirecione o usuário para /api/oauth/authorize com client_id, redirect_uri (match exato da allowlist do app), scope, state e o PKCE (code_challenge com code_challenge_method=S256). O PKCE é obrigatório.
- O usuário logado vê a tela de consentimento e aprova. O CRM redireciona de volta para a sua redirect_uri com code e o mesmo state.
- No seu servidor, troque o code em POST /api/oauth/token (com code_verifier, client_id, client_secret e redirect_uri) por um access token (Bearer, cerca de 1 hora) e um refresh token.
- Chame a API REST v1 e o servidor MCP com Authorization: Bearer at_..., escopado ao consentimento. Renove com grant_type=refresh_token; o refresh é rotacionado (o antigo morre ao usar) e reuso revoga a família de tokens.
# 1) Envie o usuário ao consentimento (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 # match EXATO da allowlist
&scope=records:read%20records:write:opportunity%20activities:read
&state=<aleatorio>
&code_challenge=<base64url(sha256(code_verifier))>
&code_challenge_method=S256
# → o usuário aprova → 302 redirect_uri?code=<authcode>&state=<...>
# 2) Troque o code por tokens (server-side; envie o 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) Chame a API v1 / MCP com o access token (escopado ao consentimento)
curl -H "Authorization: Bearer at_..." https://www.selliocrm.com/api/v1/contact
# 4) Renove com o refresh token (rotacionado: o rt_ antigo morre ao usar)
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_...Os escopos OAuth mapeiam para a mesma imposição das chaves (leitura, escrita e por objeto). Peça o mínimo necessário; o usuário concede um subconjunto na instalação e o token opera só dentro dele.
- records:read e records:write: ler e escrever registros de todos os objetos (coarse).
- records:read:contact e records:write:opportunity: refino por objeto (apiName), quando o app precisa só de alguns objetos.
- activities:read e activities:write: ler e escrever atividades.
- objects:read: descobrir a estrutura de objetos e campos.
Widgets embutidos
Seu app pode embutir uma tela própria (widget) dentro do CRM: no detalhe de um registro ou no painel. O widget roda num iframe sandboxed, servido a partir de uma origem sua (na allowlist do app). O host manda ao widget apenas contexto de UI por postMessage: recordId, objectApiName, locale e tema. Nenhum dado de negócio, token ou segredo trafega por essa mensagem. Para ler ou escrever dados, seu widget usa a API OAuth com o próprio token (os escopos que o tenant concedeu na instalação).
- Declare o widget no seu app (chave, título, local record ou dashboard e o embedUrl) e liste a origem do embedUrl na allowlist de origens do app.
- No seu widget, ouça as mensagens do host e aceite somente as da origem do host (event.origin). O host envia sellio:widget:context (version 1) com { recordId, objectApiName, locale, theme, installId }.
- Seu widget pode enviar de volta apenas sellio:widget:ready (para receber o contexto de novo) e sellio:widget:resize com { height } (para ajustar a altura, com teto). Qualquer outra mensagem é ignorada.
- Para dados, chame a API OAuth (Bearer) com o token do app. O host nunca entrega token nem dado pelo postMessage.
// Dentro do SEU widget (a página em https://widgets.seuapp.com que roda no iframe).
// O host manda SÓ contexto de UI. Para DADOS, use a API OAuth com o SEU token.
const HOST = 'https://www.selliocrm.com'; // valide SEMPRE a origem do host
window.addEventListener('message', (event) => {
if (event.origin !== HOST) return; // aceite só a origem do 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);
// Busque os dados com o SEU token OAuth (nunca vêm pelo postMessage):
// fetch(HOST + '/api/v1/' + objectApiName + '/' + recordId,
// { headers: { Authorization: 'Bearer ' + accessToken } })
}
});
// Peça o contexto assim que carregar e ajuste a altura ao seu conteúdo:
parent.postMessage({ type: 'sellio:widget:ready', version: 1 }, HOST);
parent.postMessage({ type: 'sellio:widget:resize', version: 1,
payload: { height: document.body.scrollHeight } }, HOST);Segurança: o iframe é sandboxed e cross-origin, então o widget não acessa o DOM nem os cookies do host. O host valida a origem exata em cada postMessage (entrada e saída) e o embedUrl é sempre checado contra a allowlist do app. Peça o escopo widgets:embed para poder posicionar widgets.
Webhooks de saída
Receba eventos do CRM em tempo real. Cadastre uma URL de destino em Configurações e escolha os eventos que assina. Cada entrega é um POST com o corpo em JSON, o cabeçalho x-sellio-event com o nome do evento e o cabeçalho x-sellio-signature com a assinatura HMAC-SHA256 do corpo. O segredo (whsec_...) é mostrado ao criar o webhook.
Catálogo de eventos
Exemplo de 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"
}Os eventos de campanha e fluxo (flow.*) trazem campos adicionais do enrollment:
{
"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"
}Como verificar a assinatura
Calcule o HMAC-SHA256 do corpo CRU (exatamente como recebido, sem reserializar) usando o seu segredo whsec_..., no formato sha256=<hex>, e compare com o cabeçalho x-sellio-signature em tempo constante. Rejeite quando não bater.
import { createHmac, timingSafeEqual } from 'node:crypto';
// rawBody = corpo EXATO recebido (string), sem reserializar.
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: use express.raw({ type: 'application/json' }) para ter o corpo cru.
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');
// ... processe o evento
res.status(200).end();
});Rate limit
Cada chave tem um limite por minuto (padrão 120, configurável). As respostas trazem os cabeçalhos X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset. Ao exceder, a API responde 429 com o cabeçalho Retry-After.
Formato de erro
Erros retornam JSON no formato { "error": "mensagem" } com o status HTTP correspondente:
{ "error": "required field: source" } # HTTP 400- 200 e 201: sucesso (201 na criação).
- 400: validação ou regra de negócio.
- 401: chave ausente ou inválida.
- 403: sem permissão (RBAC) para o objeto, ou bloqueado pelo escopo da chave (somente leitura, objeto fora do escopo, ou exclusão não habilitada).
- 404: objeto ou registro não encontrado (inclui DELETE/PATCH de um id inexistente).
- 429: rate limit excedido (veja o cabeçalho Retry-After).
Limitações conhecidas
- Os filtros combinam com E lógico (todos verdadeiros ao mesmo tempo); não há OU entre filtros.
- A atualização é parcial (PATCH): você envia só os campos a alterar. Não há PUT de substituição total do registro.
Especificação e coleção
Importe a especificação OpenAPI 3.1 no Swagger, Insomnia ou Postman, ou baixe a coleção Postman pronta com todos os endpoints e as variáveis apiKey e baseUrl.