Desenvolvedores

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:

  1. 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.
  2. Use a chave no cabeçalho Authorization: Bearer. Ela identifica o tenant dono, então tudo já fica escopado a ele.
  3. Faça a primeira chamada e liste os seus objetos.
curl -H "Authorization: Bearer sk_live_..." \
  https://www.selliocrm.com/api/v1/objects

Autenticaçã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.

GET/objectsLista os objetos do tenant.
GET/objects/{apiName}Campos do objeto (descoberta).
GET/{object}Lista e pesquisa registros (filtros ricos, ordenação, paginação).
GET/{object}/{id}Um registro pelo id.
POST/{object}Cria um registro.
POST/{object}/bulkCria em lote (até 500; sucesso parcial por item).
PATCH/{object}/{id}Atualiza campos (parcial).
PATCH/{object}/bulkAtualiza em lote ({ updates: [{ id, ...campos }] }).
DELETE/{object}/{id}Exclui (suave → lixeira). Exige o escopo de exclusão na chave.
GET/data/{object}Registros achatados em linhas (BI/automação).
GET/activitiesLista atividades (use ?recordId para um registro).
POST/activitiesCria uma atividade e vincula a um registro.

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.
GET/api/oauth/authorizeConsentimento do usuário (exige sessão). Emite o code.
POST/api/oauth/tokenTroca code por tokens; e renova (refresh_token).
POST/api/oauth/revokeRevoga um access ou refresh token.
# 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).

  1. 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.
  2. 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 }.
  3. 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.
  4. 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

record.createdrecord.updatedflow.enrolledflow.message_sentflow.repliedflow.step_completedflow.completed

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.

Crie a sua conta e gere a primeira chave

Criar conta grátis
Desenvolvedores · Sellio