Integra Sellio por HTTP o conecta tu agente de IA
Una API REST simple y un servidor MCP nativo, con la misma clave, el mismo alcance de tenant y el mismo rate limit. Descubre los campos de cualquier objeto y empieza a leer y escribir registros en minutos.
Empieza rápido
De cero a tu primera llamada en tres pasos:
- En el CRM, abre Configuración → API y desarrolladores y genera una API key. Guárdala en un lugar seguro; no se vuelve a mostrar.
- Usa la clave en el encabezado Authorization: Bearer. Identifica al tenant propietario, así que todo queda acotado a él.
- Haz tu primera llamada y lista tus objetos.
curl -H "Authorization: Bearer sk_live_..." \
https://www.selliocrm.com/api/v1/objectsAutenticación
Cada solicitud lleva una API key en el encabezado Authorization: Bearer. Esa clave identifica al tenant propietario, así que todo queda acotado a él automáticamente, con RLS y RBAC. Nunca expongas la clave en el navegador; úsala solo del lado del servidor. Generas y revocas claves en Configuración → API y desarrolladores.
Alcance de las claves
Al generar una clave eliges su alcance y le das a cada integración solo el acceso que necesita. Los límites valen igual en REST y en MCP, porque las dos superficies usan la misma clave y el mismo control. Una clave sin alcance definido (el valor por defecto) tiene acceso total de lectura y escritura a todos los objetos, como antes.
- Solo lectura: la clave lee datos, pero no crea, edita ni elimina. Bloquea POST, PATCH y DELETE en REST y las herramientas create_record, update_record y delete_record en MCP, con 403 y { "error": "errors.apiKeyReadOnly" }.
- Limitada a objetos: elige los objetos a los que la clave puede acceder. Cualquier objeto fuera de la lista responde 403 con { "error": "errors.apiKeyObjectDenied" }. La lista de objetos ya viene filtrada al alcance, y las actividades cuentan como el objeto activity.
- Permitir eliminación: por defecto una clave NUNCA elimina (ni con escritura habilitada). Activa esta opción al generar la clave para habilitar DELETE /{object}/{id} y la herramienta delete_record. Sin ella, la eliminación responde 403 con { "error": "errors.apiKeyDeleteDenied" }. La eliminación es suave: los registros van a la papelera y pueden restaurarse.
API REST v1
Registros de cualquier objeto, enviando y recibiendo JSON. El {object} es el apiName (por ejemplo contact, lead, opportunity) y el {id} es el uuid del registro. Los objetos personalizados usan los mismos endpoints genéricos.
Parámetros de la lista (GET /{object}): limit (de 1 a 500, por defecto 50), offset (desplazamiento de paginación, por defecto 0; el total viene en el campo total), search (búsqueda por nombre, ignora acentos y mayúsculas), order (campo con :asc o :desc) y filter. Filtros ricos: repite filter=campo:operador:valor para combinar varios (ej.: filter=amount:gte:1000&filter=stage:eq:won). Operadores: eq, neq, contains, gt, lt, gte, lte, empty, not_empty (empty y not_empty no llevan valor). Los filtros se combinan con Y lógico (no hay OR) y valen hasta 12 por llamada. La forma antigua filter=campo:valor (sin operador) sigue siendo igualdad exacta.
# 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: repite filter=campo:operador:valor (Y logico entre ellos)
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"
# Un registro
curl -H "Authorization: Bearer sk_live_..." \
https://www.selliocrm.com/api/v1/contact/8f3c...
# Crear
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
# Crear en lote (hasta 500; respuesta con exito 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 }
# Actualizar (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...
# Eliminar (suave, va a la papelera; requiere el alcance de eliminacion en la clave)
curl -X DELETE -H "Authorization: Bearer sk_live_..." \
https://www.selliocrm.com/api/v1/contact/8f3c...Descubrimiento de objetos y campos
Antes de crear o actualizar un registro, descubre los campos del objeto: qué existe, qué es obligatorio y qué valores acepta un select. Sin adivinar y sin inspeccionar un registro existente. GET /objects/{apiName} devuelve el objeto y sus campos, con required, options (del select), targetObject (del lookup), unique y readOnly (campos de fórmula o rollup, que no son escribibles).
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" }
# ]
# }En MCP, la herramienta describe_object devuelve los mismos campos al agente.
Datos para BI y automatización
GET /data/{object} devuelve los registros aplanados en filas tabulares estables (columnas fijas id, created_at, updated_at, owner_id, más una por campo), ordenadas por updated_at desc. Acepta paginación por page y pageSize (o limit y offset), los filtros incrementales updated_since y created_since (ISO 8601) y filter=campo:valor. Pensado para Power BI, Tableau, Looker y para el polling de Zapier, Make y 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
Sellio expone un servidor MCP (Model Context Protocol) nativo para que tu agente de IA lea y escriba en el CRM de forma segura. Es la misma API key, el mismo rate limit, las mismas validaciones y el mismo RBAC. La URL del servidor es https://www.selliocrm.com/api/mcp. Herramientas disponibles:
list_objects: Lista los objetos del tenant.describe_object: Campos de un objeto (obligatorios, tipos, opciones).list_records: Lista y busca registros.get_record: Un registro por id.create_record: Crea un registro.update_record: Actualiza campos.delete_record: Elimina (suave → papelera). Requiere el alcance de eliminación en la clave.
Los clientes que soportan MCP remoto por HTTP usan la URL con el encabezado Authorization: Bearer. En clientes sin HTTP remoto nativo, usa el puente mcp-remote:
{
"mcpServers": {
"sellio-crm": {
"command": "npx",
"args": [
"-y", "mcp-remote",
"https://www.selliocrm.com/api/mcp",
"--header", "Authorization: Bearer sk_live_..."
]
}
}
}OAuth y apps conectadas
Para integrar EN NOMBRE DEL USUARIO (sin que el cliente pegue una clave de admin), usa el flujo OAuth 2.0 Authorization Code con PKCE. El usuario ve una pantalla de consentimiento con los alcances pedidos, aprueba, y tu app recibe un access token que solo puede hacer lo consentido. Las apps son curadas: registra y publica la app en el marketplace para obtener client_id y client_secret. El flujo en cuatro pasos:
- Redirige al usuario a /api/oauth/authorize con client_id, redirect_uri (coincidencia exacta con la allowlist de la app), scope, state y PKCE (code_challenge con code_challenge_method=S256). El PKCE es obligatorio.
- El usuario conectado ve la pantalla de consentimiento y aprueba. El CRM redirige de vuelta a tu redirect_uri con code y el mismo state.
- En tu servidor, intercambia el code en POST /api/oauth/token (con code_verifier, client_id, client_secret y redirect_uri) por un access token (Bearer, cerca de 1 hora) y un refresh token.
- Llama a la API REST v1 y al servidor MCP con Authorization: Bearer at_..., acotado al consentimiento. Renueva con grant_type=refresh_token; el refresh se rota (el anterior muere al usarse) y su reuso revoca la familia de tokens.
# 1) Envía al usuario al consentimiento (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 # coincidencia EXACTA de la allowlist
&scope=records:read%20records:write:opportunity%20activities:read
&state=<aleatorio>
&code_challenge=<base64url(sha256(code_verifier))>
&code_challenge_method=S256
# → el usuario aprueba → 302 redirect_uri?code=<authcode>&state=<...>
# 2) Intercambia el code por tokens (server-side; envía el 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) Llama a la API v1 / MCP con el access token (limitado al consentimiento)
curl -H "Authorization: Bearer at_..." https://www.selliocrm.com/api/v1/contact
# 4) Renueva con el refresh token (rotado: el rt_ anterior muere al usarse)
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_...Los alcances OAuth mapean a la misma imposición que las claves (lectura, escritura y por objeto). Pide el mínimo necesario; el usuario concede un subconjunto al instalar y el token opera solo dentro de él.
- records:read y records:write: leer y escribir registros de todos los objetos (coarse).
- records:read:contact y records:write:opportunity: refinamiento por objeto (apiName), cuando la app solo necesita algunos objetos.
- activities:read y activities:write: leer y escribir actividades.
- objects:read: descubrir la estructura de objetos y campos.
Widgets embebidos
Tu app puede embeber su propia pantalla (un widget) dentro del CRM: en el detalle de un registro o en el panel. El widget corre en un iframe sandboxed, servido desde un origen tuyo (en la allowlist de la app). El host envía al widget solo contexto de UI por postMessage: recordId, objectApiName, locale y tema. Ningún dato de negocio, token o secreto viaja en ese mensaje. Para leer o escribir datos, tu widget usa la API OAuth con su propio token (los permisos que el tenant concedió al instalar).
- Declara el widget en tu app (clave, título, ubicación record o dashboard y el embedUrl) y lista el origen del embedUrl en la allowlist de orígenes de la app.
- En tu widget, escucha los mensajes del host y acepta solo los del origen del host (event.origin). El host envía sellio:widget:context (version 1) con { recordId, objectApiName, locale, theme, installId }.
- Tu widget solo puede responder sellio:widget:ready (para recibir el contexto de nuevo) y sellio:widget:resize con { height } (para ajustar su altura, con tope). Cualquier otro mensaje se ignora.
- Para datos, llama a la API OAuth (Bearer) con el token de la app. El host nunca entrega token ni datos por postMessage.
// Dentro de TU widget (la página en https://widgets.tuapp.com que corre en el iframe).
// El host envía SOLO contexto de UI. Para DATOS, usa la API OAuth con TU token.
const HOST = 'https://www.selliocrm.com'; // valida SIEMPRE el origen del host
window.addEventListener('message', (event) => {
if (event.origin !== HOST) return; // acepta solo el origen del 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);
// Obtén los datos con TU token OAuth (nunca llegan por postMessage):
// fetch(HOST + '/api/v1/' + objectApiName + '/' + recordId,
// { headers: { Authorization: 'Bearer ' + accessToken } })
}
});
// Pide el contexto al cargar y ajusta la altura a tu contenido:
parent.postMessage({ type: 'sellio:widget:ready', version: 1 }, HOST);
parent.postMessage({ type: 'sellio:widget:resize', version: 1,
payload: { height: document.body.scrollHeight } }, HOST);Seguridad: el iframe es sandboxed y cross-origin, así que el widget no accede al DOM ni a las cookies del host. El host valida el origen exacto en cada postMessage (entrada y salida) y el embedUrl siempre se verifica contra la allowlist de la app. Pide el permiso widgets:embed para poder ubicar widgets.
Webhooks de salida
Recibe eventos del CRM en tiempo real. Registra una URL de destino en Configuración y elige los eventos que suscribes. Cada entrega es un POST con el cuerpo en JSON, el encabezado x-sellio-event con el nombre del evento y el encabezado x-sellio-signature con la firma HMAC-SHA256 del cuerpo. El secreto (whsec_...) se muestra al crear el webhook.
Catálogo de eventos
Ejemplo 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"
}Los eventos de campaña y flujo (flow.*) traen campos adicionales del 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"
}Cómo verificar la firma
Calcula el HMAC-SHA256 del cuerpo CRUDO (tal como se recibió, sin reserializar) con tu secreto whsec_..., en el formato sha256=<hex>, y compáralo con el encabezado x-sellio-signature en tiempo constante. Rechaza cuando no coincida.
import { createHmac, timingSafeEqual } from 'node:crypto';
// rawBody = el cuerpo EXACTO recibido (string), sin 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: usa express.raw({ type: 'application/json' }) para obtener el cuerpo crudo.
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');
// ... procesa el evento
res.status(200).end();
});Rate limit
Cada clave tiene un límite por minuto (por defecto 120, configurable). Las respuestas incluyen los encabezados X-RateLimit-Limit, X-RateLimit-Remaining y X-RateLimit-Reset. Al excederlo, la API responde 429 con el encabezado Retry-After.
Formato de error
Los errores devuelven JSON en el formato { "error": "mensaje" } con el estado HTTP correspondiente:
{ "error": "required field: source" } # HTTP 400- 200 y 201: éxito (201 al crear).
- 400: validación o regla de negocio.
- 401: clave ausente o inválida.
- 403: sin permiso (RBAC) para el objeto, o bloqueado por el alcance de la clave (solo lectura, objeto fuera del alcance, o eliminación no habilitada).
- 404: objeto o registro no encontrado (incluye DELETE/PATCH de un id inexistente).
- 429: rate limit excedido (mira el encabezado Retry-After).
Limitaciones conocidas
- Los filtros se combinan con Y lógico (todos verdaderos a la vez); no hay OR entre filtros.
- La actualización es parcial (PATCH): envías solo los campos a cambiar. No hay PUT de reemplazo total del registro.
Especificación y colección
Importa la especificación OpenAPI 3.1 en Swagger, Insomnia o Postman, o descarga la colección Postman lista con todos los endpoints y las variables apiKey y baseUrl.