Saltar al contenido
Odysı
Veamos si encajamos
← Odysi Suite
Guía Desarrolladores · Beta

Odysi Suite para desarrolladores: REST, MCP, OAuth y webhooks

Odysi CRM es un CRM multiempresa en el que cada valor de cada atributo es un intervalo de tiempo. Odysi guarda y muestra los datos; el trabajo lo hacen tu propio código y tu propia IA (Claude, ChatGPT o cualquier cliente MCP), a través de una API REST y un servidor MCP. Aquí tienes lo que necesitas para leer y escribir esos datos, conectar tus herramientas y tus agentes, y reaccionar cuando algo cambia. Todo lo que describimos ya funciona y está en beta; lo que aún no existe tiene su propia sección.

T
Actualizado el 17 de octubre de 2026
Tema Odysi Suite
Lectura 15 min
N.º 01

Dos entradas, un solo espacio de trabajo

REST, en https://crm.odysi.studio/v2, es para código: scripts, servicios, importaciones e integraciones. MCP, en https://crm.odysi.studio/mcp, es para agentes de IA: Claude, ChatGPT, Claude Code o un agente hecho por ti. Las dos aceptan las mismas credenciales, una clave de API o un token de OAuth, las dos están ligadas a un espacio de trabajo y las dos funcionan con los permisos de la persona que hay detrás de la credencial. Los webhooks van en sentido contrario: Odysi avisa a tu código de lo que ha cambiado.

Odysi CRM no hace por su cuenta ninguna llamada a IA generativa. Lo que se escribe y se decide lo hace tu código, o tu IA a través del conector; el CRM usa modelos de IA solo para proponer cómo se asignan las columnas de un CSV y señalar posibles duplicados. Odysi Scribe usa modelos de IA para transcribir llamadas y pulir dictados. Odysi Suite es gratis durante la beta y es para uso profesional. Un aviso: la interfaz de Odysi CRM está solo en inglés.

Fig. 1 · De tu código o tu IA al espacio de trabajo, y de vuelta
Tu código y tu IA

Un script, un servicio, Claude, ChatGPT o cualquier cliente MCP.

REST y MCP

Una clave de API o un token de OAuth, ligados a un espacio de trabajo, con los permisos de la persona que hay detrás.

Tu espacio de trabajo

Registros, listas, notas, reuniones y llamadas. Los webhooks (en discontinuo) avisan a tu código de lo que ha cambiado.

N.º 02

El modelo: objetos, registros y valores que guardan su historia

Un espacio de trabajo contiene objetos (tipos de registro). Cada objeto tiene atributos con tipo, y los registros son sus filas. Todo espacio de trabajo nuevo trae los objetos estándar companies, people, deals, users y workspaces, con los mismos slugs en todos, así que una integración puede darlos por fijos. Los objetos y atributos personalizados son datos, no código: crear uno no ejecuta ninguna migración. Cada consulta queda limitada a tu espacio de trabajo por construcción.

  • Identificadores. El id de un registro empieza por su espacio de trabajo: { workspace_id, object_id, record_id }. Cualquier ruta acepta un UUID o el api_slug, y un slug renombrado sigue funcionando 90 días.
  • Los valores son intervalos. Cada valor lleva active_from, active_until (null mientras está vigente) y created_by_actor. Al actualizarlo se cierra el intervalo anterior y se abre otro; no se sobrescribe nada. La historia se lee con ?show_historic=true en /v2/objects/{object}/records/{id}/attributes/{attribute}/values, y el valor que tenía en cualquier momento se consulta con $as_of.
  • Cada cambio tiene autor. Un actor es un workspace-member, un api-token (con el nombre de la clave y el miembro por el que actúa) o system, así que la historia muestra el cambio de un agente como hecho en nombre de la persona.
  • Listas. Una lista es un proceso, como un embudo de ventas o una campaña. Un registro puede estar en muchas listas, y cada entrada tiene sus propios atributos de lista. Las entradas tienen los mismos endpoints de consulta, upsert e historia que los registros.
  • La escritura sigue al verbo. En un atributo de selección múltiple, PUT sobre un registro sustituye los valores vigentes y PATCH los amplía.
  • Nada desaparece por accidente. Archivar se puede deshacer. Borrar es un borrado lógico, y un administrador puede restaurar durante 30 días. Una fusión da al registro un id nuevo; un GET sobre un id antiguo devuelve el registro fusionado con redirected_from, y la fusión se puede deshacer durante 30 días.
  • Lo demás también es de primera. Notas, tareas, comentarios, reuniones, grabaciones de llamadas, archivos, un registro de correos e informes, por REST, por MCP o por los dos. Los atributos de interacción (primera, última y próxima) se calculan a partir de reuniones y notas y son de solo lectura; los atributos de fórmula se calculan al escribir.

Hay 18 tipos de atributo:

  • text
  • number
  • checkbox
  • currency
  • date
  • timestamp
  • select
  • status
  • record-reference
  • actor-reference
  • domain
  • email-address
  • phone-number
  • personal-name
  • location
  • rating
  • interaction
  • formula

Al escribir se acepta un valor suelto, una lista de valores u objetos explícitos: "funding_raised": 1200000.5 se lee después como un valor de moneda "1200000.50" en USD, y "foundation_date": "2020-06" como "2020-06-01". Las marcas de tiempo viajan en ISO 8601, en UTC y con precisión de nanosegundos; las fechas, como YYYY-MM-DD.

Fig. 2 · Un atributo, tres valores a lo largo del tiempo
Intervalos cerrados

Cada valor antiguo conserva su active_from y su active_until: esa es la historia.

$as_of

Una consulta lee el valor tal como estaba en un instante (la línea turquesa).

El valor vigente

Su active_until es null hasta que el siguiente cambio lo cierra.

N.º 03

Autenticación: claves de API y OAuth 2.1

Cada petición lleva Authorization: Bearer <token>. Los tokens se distinguen a simple vista: las claves de API empiezan por odysi_sk_, los tokens de acceso de OAuth por odysi_at_ y los de refresco por odysi_rt_. GET /v2/self te dice quién es un token: active, scope, client_id, exp (null en una clave de API), iat, authorized_by_workspace_member_id y el id, el nombre y el slug del espacio de trabajo.

Claves de API, para tu propio código

  • Las crean los administradores en Settings › Connected apps › Advanced › API keys, o con POST /v2/api_keys. La clave se muestra una sola vez; Odysi guarda solo una huella.
  • Una clave actúa como el administrador que la creó, con sus permisos y nunca con más ámbitos (scopes) que los suyos; por la API puedes acotarla aún más. Deja de funcionar si ese miembro sale del espacio de trabajo o queda suspendido.
  • Crear o revocar una clave queda en el registro de auditoría.

OAuth 2.1, para apps y agentes que actúan en nombre de una persona

  • Descubrimiento. Los metadatos del servidor de autorización (RFC 8414) están en https://crm.odysi.studio/.well-known/oauth-authorization-server, y los del recurso protegido (RFC 9728) en /.well-known/oauth-protected-resource/mcp y /.well-known/oauth-protected-resource/v2. Endpoints: /oauth/authorize, /oauth/token, /oauth/revoke, /oauth/introspect y /oauth/register.
  • Código de autorización con PKCE. S256 es obligatorio para clientes públicos y plain no se acepta nunca. La respuesta de autorización incluye iss.
  • Tres formas de identificar tu cliente. Una URL de Client ID Metadata Document (solo https, en caché una hora), el registro dinámico de clientes (RFC 7591, POST /oauth/register) o una app que registra un administrador con POST /v2/oauth/clients, que puede ser confidencial (client_secret_post o client_secret_basic).
  • Las URI de redirección tienen que ser https, salvo las de loopback (localhost, 127.0.0.1, [::1]), que valen con cualquier puerto (RFC 8252), así que funcionan las herramientas de escritorio y de línea de comandos.
  • Duración de los tokens. Los de acceso duran una hora. Los de refresco (refresh tokens) rotan en cada uso y duran 365 días, con detección de reutilización y 60 segundos de margen si se hacen dos refrescos a la vez. offline_access se acepta y se ignora: siempre recibes un token de refresco.
  • Un token, un recurso (RFC 8707). Un token pedido con resource https://crm.odysi.studio/mcp o https://crm.odysi.studio/v2 queda ligado a él; si lo mandas al otro, se rechaza con un mensaje que dice cuál es el bueno. Las respuestas 401 y 403 llevan desafíos WWW-Authenticate con resource_metadata.
  • Cada persona decide. Cada miembro ve y desconecta sus conexiones en Settings › Connected apps; los administradores ven las de todos.
Los tres ámbitos de la pantalla de consentimiento
ÁmbitoQué cubre
crm:readTodos los ámbitos finos :read.
crm:writecrm:read más lectura y escritura en registros, entradas de lista, notas, tareas, comentarios, reuniones, el registro de correos, informes, archivos y la configuración de objetos y listas. Claude se conecta con crm:read crm:write.
crm:admincrm:write más user_management:read-write, webhook:read-write y workspace_settings:read-write: miembros, claves de API, webhooks y ajustes del espacio de trabajo, hasta donde lo permita el rol de la persona.

Los ámbitos finos son resource:permission (por ejemplo, note:read o meeting:read-write), y :read-write incluye :read. El ámbito de un token es un techo, no un permiso: el acceso propio del miembro sigue mandando.

N.º 04

REST /v2: formato, errores, límites y reintentos

  • Un solo formato. Las respuestas van en { "data": … }; las colecciones pueden añadir pagination y las consultas, meta. Los cuerpos de escritura van envueltos igual, { "data": { … } }; los de consulta, no.
  • Una sola forma de error en todas partes: { "status_code", "type", "code", "message" }, con los tipos invalid_request_error, authentication_error, permission_error, conflict_error, rate_limit_error y api_error. El mensaje nombra el campo que falla.
  • Límites. 100 lecturas y 25 escrituras por segundo y por token. Las consultas y agregaciones cuentan como lecturas y pagan de un presupuesto de coste de 5.000 puntos por espacio de trabajo cada 10 segundos móviles; una llamada masiva paga una escritura por id. Si te pasas, recibes un 429 con Retry-After en segundos.
  • Reintentos seguros. Manda una Idempotency-Key en cualquier petición que no sea GET: la respuesta (con cualquier código por debajo de 500) se guarda 24 horas para ese espacio de trabajo, ese token y esa clave, y un reintento la recibe de nuevo con Idempotency-Replayed: true. Los upserts son idempotentes por naturaleza.
  • Detalles que ayudan. Cuerpos de hasta 16 MB. Cada respuesta lleva un x-request-id, y se respeta el X-Request-Id que mandes si parece un id (de 8 a 128 letras, cifras, . _ : -). Las actualizaciones y borrados masivos admiten hasta 500 ids, y el archivado, hasta 500 registros por llamada.
Crear una empresa
curl -s https://crm.odysi.studio/v2/objects/companies/records \
  -H "Authorization: Bearer $ODYSI_KEY" -H 'content-type: application/json' \
  -d '{"data":{"values":{"name":"Acme","domains":["acme.com"],"categories":["Software"]}}}'
Respuesta (resumida)
{ "data": {
  "id": { "workspace_id": "…", "object_id": "…", "record_id": "…" },
  "values": {
    "name": [ { "value": "Acme", "active_from": "…", "active_until": null,
                "attribute_type": "text", "created_by_actor": { … } } ],
    "categories": [ { "option": { "title": "Software" }, … } ],
    "created_by": [ { "referenced_actor_type": "api-token", … } ]
  } } }
Upsert de una persona por su email, seguro de reintentar
curl -s -X PUT "https://crm.odysi.studio/v2/objects/people/records?matching_attribute=email_addresses" \
  -H "Authorization: Bearer $ODYSI_KEY" -H 'content-type: application/json' \
  -H 'Idempotency-Key: import-2026-10-17-row-42' \
  -d '{"data":{"values":{"email_addresses":["john.smith@acme.com"],"job_title":"CTO"}}}'
Dos errores, tal como llegan
# 400: un filtro sobre un atributo que no existe
{ "status_code": 400, "type": "invalid_request_error", "code": "filter_error",
  "message": "Unknown attribute 'stagee' on 'deals'." }

# 429, con la cabecera Retry-After: 2
{ "status_code": 429, "type": "rate_limit_error", "code": "rate_limit_exceeded",
  "message": "Retry after 2s." }

Un upsert necesita matching_attribute (un atributo único); sin él, la respuesta es un 400 missing_matching_attribute. Las demás familias siguen el mismo patrón: entradas de lista, fusiones y duplicados, reuniones y grabaciones, archivos (enlaces firmados de subida de un solo uso y de descarga de 15 minutos) y un feed de actividad (GET /v2/activity, lo más reciente primero, solo lo que quien llama puede ver, con cursor).

N.º 05

El lenguaje de consulta

POST /v2/objects/{object}/records/query recibe filter, sorts, limit (hasta 500) y offset. Las entradas de lista usan el mismo formato en /v2/lists/{list}/entries/query, y si un slug existe en los dos sitios, gana el atributo de la lista.

  • Operadores: $eq, $gt, $gte, $lt, $lte, $contains, $starts_with, $ends_with, $in y $not_empty, combinados con $and, $or y $not. No hay $neq: usa $not, que significa «ningún valor vigente coincide», así que «sin la etiqueta EMEA» también funciona con selección múltiple.
  • Forma corta o completa. { "name": "Acme" } y la forma con subcampos se aceptan las dos y se leen igual.
  • Valores dinámicos: { "$current_user": true } y fechas relativas como { "$relative": "-90d" }.
  • Tiempo: filtra por active_from («ha cambiado en los últimos 7 días») o añade $as_of para comparar el valor tal como estaba en un instante.
  • Rutas: sigue referencias entre registros con path: [[object_or_list, attribute], …] y constraints, hasta 4 saltos; parent_record salta de una entrada de lista a su registro.
  • Orden: los nulos al final, los estados en el orden de sus etapas y record_id como último desempate, así que las páginas son estables.
  • Coste, en cada respuesta: meta.cost_score, su desglose en meta.cost, el meta.plan y meta.elapsed_ms; añade include_count: true para obtener meta.total. La puntuación es una base más 2 por condición de filtro, 5 por orden, 15 por salto de ruta, 10 por predicado sin índice, 25 por historia y 1 por cada 100 filas; una consulta puede costar hasta 500.
Empresas de software sin interacción en 90 días
curl -s https://crm.odysi.studio/v2/objects/companies/records/query \
  -H "Authorization: Bearer $ODYSI_KEY" -H 'content-type: application/json' \
  -d '{
    "filter": { "$and": [
      { "categories": "Software" },
      { "last_interaction": { "$lt": { "$relative": "-90d" } } }
    ] },
    "sorts": [ { "attribute": "name", "direction": "asc" } ],
    "limit": 50
  }'

# Respuesta: las filas y lo que ha costado la consulta
{ "data": [ … ],
  "meta": { "cost_score": …, "cost": { "filter_conditions": 2, "sort_specs": 1, … },
            "plan": "…", "limit": 50, "offset": 0, "elapsed_ms": … } }
Dos filtros que solo permiten los intervalos
# oportunidades cuya etapa ha cambiado desde el 10 de octubre
{ "stage": { "active_from": { "$gte": "2026-10-10T00:00:00Z" } } }

# oportunidades que estaban en "In Progress" a final de junio
{ "stage": { "$as_of": "2026-06-30T23:59:59Z", "$eq": "In Progress" } }
N.º 06

El servidor MCP

  • Dónde y cómo. https://crm.odysi.studio/mcp, Streamable HTTP, sin estado, solo POST. Servidor «Odysi CRM», con tools.listChanged.
  • Con qué permisos. Te conectas con OAuth (Client ID Metadata Document, registro dinámico o una app registrada) o con una clave de API, ligados a un espacio de trabajo. Cada llamada se ejecuta con los permisos del miembro que conectó. Una herramienta fuera del ámbito de un token de OAuth responde 403 insufficient_scope con un desafío para ampliar el permiso; con una clave de API, es un error de la herramienta.
  • Pensado para agentes. Cada herramienta tiene título e indicaciones de solo lectura, destructiva e idempotente. Los errores llegan como error <code>: <message> con isError: true, redactados para que el agente pueda corregir la llamada. Los archivos, las importaciones del Playbook y el audio de las llamadas nunca viajan en los argumentos: usan enlaces firmados de corta duración por HTTP normal (archivos de hasta 50 MB).
  • Clientes probados. Claude, un conector de ChatGPT y Claude Code mediante redirecciones de loopback.

Operaciones nuevas sin reconectar

Claude mantiene la lista de herramientas de una conversación durante toda la conversación, así que Odysi está hecho para no pedir nunca una reconexión. Tres herramientas no cambian jamás de nombre, entrada ni anotaciones: list_operations devuelve todas las operaciones que puede ejecutar la conexión, incluidas las que se añadieron después de empezar la conversación, y con name devuelve el esquema de entrada completo de una; run_read_operation y run_write_operation ejecutan cualquier operación por su nombre. Una capacidad nueva llega como una operación nueva, no necesita ámbitos nuevos, y las herramientas existentes solo ganan campos opcionales. Hoy tools/list devuelve 137 herramientas y hay unas 140 operaciones.

Descubrir y luego ejecutar por nombre (JSON-RPC por POST /mcp)
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call",
  "params": { "name": "list_operations", "arguments": { "group": "Tasks" } } }

{ "jsonrpc": "2.0", "id": 2, "method": "tools/call",
  "params": { "name": "run_read_operation",
              "arguments": { "operation": "list_tasks",
                             "arguments": { "open_only": true, "limit": 20 } } } }
Familias de herramientas
FamiliaPor ejemplo
Identidad y esquemawhoami, describe_schema (objetos, atributos, opciones, estados y listas en una llamada), set_stage_requirements
Registrossearch_records, query_records, count_records, aggregate_records, get_records, upsert_record, merge_records, list_duplicates
Listas y vistaslist_entries, add_to_list, update_entry, list_views, create_view
Informesrun_report, create_report_doc (una página de bloques que Odysi calcula en directo), create_report_share_link
Notas y tareassearch_notes, create_note, create_note_from_template, list_tasks, update_tasks
Reuniones y correoslist_meetings, create_meeting, log_email (hasta 100 por llamada, idempotente por message_id)
Archivoscreate_file_upload, list_files, link_file, restore_file
Transcripcioneslist_transcripts, get_transcript, attach_transcript, create_meeting_notes (la nota la escribe Odysi Scribe)
Esquemacreate_attribute, add_statuses, create_object, create_list
Accesosget_access, set_access, create_team, set_member_role
Playbookget_playbook, get_playbook_doc, search_playbook, update_playbook_doc, list_playbook_history, restore_playbook_version
Operaciones estableslist_operations, run_read_operation, run_write_operation

Por qué cada conversación lee primero el Playbook

El Playbook son las instrucciones de trabajo del espacio de trabajo para los agentes: los documentos del equipo, con los de cada persona encima, con historial y restauración. Las instrucciones del servidor piden a cada conversación, también a las ejecuciones programadas, que llame a get_playbook antes que a nada, y que guarde las preferencias que exprese la persona con update_playbook_doc y scope=me. Así, todos los asistentes conectados al espacio de trabajo siguen las mismas reglas y las mismas preferencias de cada persona, en lugar de llevar cada uno sus propios archivos de instrucciones. Si el Playbook cambia en mitad de una conversación, cada resultado de herramienta lleva una nota para volver a leerlo.

N.º 07

Webhooks: firmados, filtrados y con reintentos

Los webhooks se gestionan en Settings o con GET|POST /v2/webhooks, GET|PATCH|DELETE /v2/webhooks/{id}, el registro de envíos en /v2/webhooks/{id}/deliveries (últimos 7 días), POST …/deliveries/{delivery_id}/redeliver y POST /v2/webhooks/{id}/test (ámbito webhook:read-write). Un webhook creado con una app de OAuth pertenece a esa app, y uno creado con una clave del espacio de trabajo pertenece al espacio de trabajo; ninguno ve los del otro.

Suscribirse solo a los cambios de etapa
curl -s https://crm.odysi.studio/v2/webhooks \
  -H "Authorization: Bearer $ODYSI_KEY" -H 'content-type: application/json' \
  -d '{"data":{"target_url":"https://example.com/odysi",
       "subscriptions":[{"event_type":"record.updated",
         "filter":{"$and":[{"field":"stage","operator":"changed"}]}}]}}'

# La respuesta incluye "secret" (64 caracteres hexadecimales). Solo se muestra una vez: guárdalo.
  • Los filtros se aplican antes de enviar: { "$and": [ { "field", "operator", "value" } ] } o null, con equals, not_equals, in, not_in y changed. field es una ruta con puntos dentro del payload, como id.object_id; changed coincide cuando ese atributo está en updated_attributes. Cada combinación de destino, tipo de evento y filtro es única (si no, 409 uniqueness_conflict).
  • El payload. El evento más event_id, occurred_at, actor y sequence, que ordena los eventos del espacio de trabajo. record.updated lleva updated_attributes y changes ({ from, to }, o { added, removed } en selección múltiple); record.created lleva attributes.
  • El envío. Un POST por HTTPS a un destino que sea https y no apunte a una dirección privada; las redirecciones no se siguen. Espera 5 segundos, cualquier 2xx cuenta como entregado y el ritmo máximo es de unas 25 peticiones por segundo a cada URL. Responde rápido y haz el trabajo después.
  • Cabeceras: odysi-signature, odysi-signature-timestamp, odysi-event-type, odysi-webhook-id, idempotency-key (el id del evento, igual en cada reintento) y user-agent: Odysi-Webhooks/1.0.
  • Reintentos. 10 intentos en total: el primero y luego reintentos a los 30 s, 2 min, 10 min, 30 min, 1 h, 2 h, 4 h, 8 h y 16 h de cada fallo, unas 32 horas en total. Tras el último, el envío queda en failed y el webhook en degraded; el siguiente envío correcto lo devuelve a active.
  • Reenvío. Cualquier envío se puede volver a poner en cola desde el registro, que guarda su estado, los intentos, el último código, los primeros 1.000 caracteres de tu respuesta y el último error. La entrega es al menos una vez, así que descarta duplicados por idempotency-key. Una suscripción solo ve los eventos posteriores a su creación.
Los 34 tipos de evento
FamiliaEventos
Registrosrecord.created, record.updated, record.merged, record.deleted, record.restored, records.archived, records.unarchived
Listaslist.created, list.updated, list.deleted, list-attribute.created, list-attribute.updated
Entradas de listalist-entry.created, list-entry.updated, list-entry.deleted
Atributosobject-attribute.created, object-attribute.updated
Notas y tareasnote.created, note.updated, note-content.updated, note.deleted, task.created, task.updated, task.deleted
Comentarioscomment.created, comment.resolved, comment.unresolved, comment.deleted
Reuniones y llamadasmeeting.created, meeting.updated, meeting.deleted, call_recording.created
Miembros y accesosworkspace-member.created, permission.changed

Cómo comprobar la firma

La firma es hex(HMAC-SHA256(secret, "<timestamp>.<raw body>")), con la marca de tiempo en segundos Unix de odysi-signature-timestamp. Usa el secreto tal cual te lo devolvió Odysi. Compruébala sobre el cuerpo en bruto, antes de interpretar el JSON, compara en tiempo constante y rechaza las marcas de tiempo antiguas (cinco minutos es un margen razonable).

Fig. 3 · Cómo se firma y se comprueba un envío
La cadena firmada

La cabecera de la marca de tiempo, un punto y el cuerpo en bruto, byte a byte.

HMAC-SHA256

Con tu secreto del webhook como clave, escrito en hexadecimal.

Comparar

Igual a odysi-signature, en tiempo constante: el envío es auténtico.

Node.js, con el mismo esquema que Odysi
import crypto from "node:crypto";
import express from "express";

// rawBody es el cuerpo tal como llega (un Buffer), antes de interpretar el JSON.
export function verifyOdysiWebhook(rawBody, headers, secret) {
  const signature = headers["odysi-signature"];
  const timestamp = headers["odysi-signature-timestamp"];
  if (typeof signature !== "string" || typeof timestamp !== "string") return false;
  // Rechaza envíos con más (o menos) de cinco minutos de diferencia.
  const ts = Number(timestamp);
  if (!Number.isFinite(ts) || Math.abs(Date.now() / 1000 - ts) > 300) return false;
  const expected = crypto.createHmac("sha256", secret)
    .update(timestamp + ".")
    .update(rawBody)
    .digest("hex");
  const a = Buffer.from(signature);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

const app = express();
app.post("/odysi", express.raw({ type: "application/json" }), (req, res) => {
  if (!verifyOdysiWebhook(req.body, req.headers, process.env.ODYSI_WEBHOOK_SECRET)) {
    return res.sendStatus(401);
  }
  const event = JSON.parse(req.body.toString("utf8"));
  // Descarta duplicados por req.headers["idempotency-key"], encola el trabajo y responde ya.
  res.sendStatus(204);
});
N.º 08

Odysi Scribe por la API

Odysi Scribe transcribe llamadas con el nombre de quien habla y las archiva con la reunión o la oportunidad que toca. Las apps Odysi Recorder para Mac y Windows usan la misma API pública /v2 que tú: inician sesión como clientes públicos de OAuth, con PKCE y una redirección de loopback. Puedes meter grabaciones de dos maneras.

Mandar el audio para que Odysi Scribe lo transcriba

  • POST /v2/scribe/transcripts con { "data": { "file_name", "size" } } (y, si quieres, mime, recorded_at, title, language, context o meeting_id) devuelve la transcripción y adónde subir el archivo. Los bytes van por trozos con PUT /v2/scribe/transcripts/{id}/content?offset=N, y final=1 en el último; una grabación pequeña puede ir en una sola petición multipart con la parte file al final.
  • Límites: trozos de 16 MiB y hasta 2 GB por grabación por defecto. Cada miembro solo puede tener unas pocas subidas en curso a la vez (429 too_many_uploads), y si la transcripción no está configurada para ti, la llamada responde 503 scribe_not_configured. Ámbito record_permission:read-write.
  • Sigue el progreso con GET /v2/scribe/transcripts/{id}: uploading, processing y luego ready o failed. Las subidas hechas con una clave de API o una app conectada se marcan con via: "agent", y solo quien sube ve la transcripción hasta que se vincula a una reunión o un registro. Las transcripciones aún no generan webhooks, así que consulta su estado.
Empezar una subida
curl -s https://crm.odysi.studio/v2/scribe/transcripts \
  -H "Authorization: Bearer $ODYSI_KEY" -H 'content-type: application/json' \
  -d '{"data":{"file_name":"call-2026-10-17.m4a","size":48211234}}'

# Respuesta (resumida): la transcripción y adónde hacer PUT de los trozos
{ "data": { "transcript": { … },
            "upload": { "url": "…", "method": "PUT", "chunk_bytes": 16777216,
                        "max_bytes": 2000000000, "expires_at": "…" } } }

Traer tu propia transcripción

POST /v2/call_recordings recibe meeting_id, media_url, duration_seconds, participants, source y transcript: { segments, raw_transcript? }, donde cada segmento lleva speech, start_time y end_time en segundos, y un speaker opcional. También puedes añadir la transcripción más tarde con POST /v2/call_recordings/{id}/transcript. Ámbito meeting:read-write; una grabación emite call_recording.created cuando se guarda su transcripción.

Una transcripción de tu propia grabadora
curl -s https://crm.odysi.studio/v2/call_recordings \
  -H "Authorization: Bearer $ODYSI_KEY" -H 'content-type: application/json' \
  -d '{"data":{"meeting_id":"…",
       "participants":[{"email_address":"ann@acme.example","name":"Ann"}],
       "transcript":{"segments":[
         {"speech":"Budget is approved.","start_time":0.5,"end_time":3,"speaker":{"name":"Ann"}}]}}}'

{ "data": { "call_recording_id": "…" } }
N.º 09

Odysi Projects: su propio dominio y su propio conector

Odysi Projects, para el trabajo con clientes una vez ganada la oportunidad, funciona en projects.odysi.studio con la misma cuenta y el mismo espacio de trabajo, pero con su propio inicio de sesión, su propio servidor de OAuth y sus documentos de descubrimiento, y su propio conector en https://projects.odysi.studio/mcp. Sus ámbitos son projects:read y projects:read-write, y allí no valen ni las claves de API ni los tokens del CRM.

  • El mismo patrón. tools/list devuelve get_project_playbook, list_operations, run_read_operation, run_write_operation y whoami, con unas 100 operaciones detrás que dependen del rol (equipo, cliente, administrador). Cada operación tiene también su ruta REST en el dominio de Projects.
  • Escrituras con control. Una escritura hecha desde una conexión indica su source; una destructiva necesita approved_by, una persona real del proyecto; y todo lo que vería el cliente espera por defecto en Drafts a que una persona lo publique. La parte del cliente puede estar en inglés o en español, según el proyecto.
  • Bajo petición. Odysi activa Odysi Projects estudio por estudio. Hoy funciona para nuestro propio estudio y nuestros clientes; si lo quieres para el tuyo, háblanos.
N.º 10

Registro de auditoría, exportación e inspector de permisos

  • Registro de auditoría. GET /v2/audit_log?limit= (administradores; 200 por defecto, hasta 5.000, lo más reciente primero), o ?format=csv para un CSV. Recoge los cambios de configuración: claves de API, apps conectadas, webhooks, miembros, roles y permisos, fusiones, exportaciones y supresiones de datos.
  • Exportarlo todo. POST /v2/export (administradores) lanza un trabajo y devuelve un enlace de descarga: un archivo JSON con cada registro y toda su historia, listas, notas, tareas, comentarios y reuniones.
  • Acceso por objeto y por lista. Cuatro niveles (sin acceso, solo lectura, lectura y escritura, acceso completo), para todo el espacio de trabajo, un equipo o un miembro. Gana la regla más generosa y no hay reglas de denegación; ser administrador es un rol de ajustes: no amplía lo que devuelven las consultas y búsquedas (la exportación completa, que solo pueden lanzar los administradores, lo incluye todo). Una ruta hacia un objeto que quien llama no puede leer no coincide con nada, y las referencias a él se ocultan al leer.
  • ¿Por qué Ann ve esto? GET /v2/objects/{object}/permissions/inspect?workspace_member_id=… (y lo mismo bajo /v2/lists/{list}) responde con su nivel y las reglas que se lo dan. Los permisos se leen y se fijan con GET|PUT /v2/objects/{object}/permissions, o con get_access y set_access por MCP.
N.º 11

Lo que aún no hay

  • Todavía no hay un archivo OpenAPI público ni un sitio de documentación. Durante la beta compartimos la referencia completa de la API con quien la pida. Hasta entonces, la API se describe sola: GET /v2/self, describe_schema, list_operations y los documentos de descubrimiento.
  • No hay librerías cliente. Usa HTTP sin más o cualquier cliente MCP.
  • REST y MCP aún no cubren exactamente lo mismo. Casi todo lo que vas a necesitar está en los dos, pero no todas las operaciones; para MCP, consulta list_operations.
  • No hay sincronización de correo ni de calendario, a propósito. Tu IA lee Gmail o Calendar con sus propios conectores y escribe en Odysi las reuniones y los correos (create_meeting, log_email).
  • No hay motor de flujos ni agente integrado. La automatización es tu código reaccionando a los webhooks, o tu IA.
  • No hay webhooks para transcripciones de Odysi Scribe, archivos ni el Playbook.
  • No se puede instalar en tus servidores. Odysi funciona como servicio alojado.
N.º 12

Cómo empezar

  1. Consigue un espacio de trabajo

    Regístrate en crm.odysi.studio/signup. Gratis durante la beta, para uso profesional.

  2. Crea una clave de API

    Como administrador, después de confirmar tu email: Settings › Connected apps › Advanced › API keys. Cópiala; solo se muestra una vez.

  3. Pregunta quién eres

    GET /v2/self, y luego lee el esquema con describe_schema o con una primera consulta.

  4. Conecta tu IA

    Añade https://crm.odysi.studio/mcp como conector personalizado, como explica la guía de configuración.

Qué hace Odysi Suite por un equipo lo cuenta la presentación de Odysi Suite; cómo conectar Claude o ChatGPT, Odysi Recorder y el Playbook paso a paso, la guía de configuración. Para la referencia completa de la API, o para activar Odysi Projects, háblanos.

Preguntas frecuentes

Lo que nos preguntan los desarrolladores

¿Hay un archivo OpenAPI o una referencia completa de la API?
En público, todavía no. Durante la beta compartimos la referencia completa con quien la pida. Mientras tanto, la API se describe sola: GET /v2/self dice quién es un token, describe_schema devuelve todos los objetos, atributos y listas, list_operations devuelve cada operación MCP con su esquema de entrada, y los documentos de descubrimiento de OAuth son públicos.
¿Odysi envía mis datos a un modelo de IA?
Odysi CRM y Odysi Projects no hacen por su cuenta ninguna llamada a IA generativa: lo que se escribe y se decide lo hace tu IA, a través del conector, o tu código. El CRM usa modelos de IA solo para proponer cómo se asignan las columnas de un CSV y señalar posibles duplicados. Odysi Scribe usa modelos de IA para transcribir llamadas, poner nombre a quien habla, archivar cada llamada con su reunión u oportunidad, pulir los dictados y, cuando lo pides, escribir las notas de la reunión.
¿Cómo mantengo mi propio sistema sincronizado con Odysi?
Suscríbete por webhook a los cambios que te interesen (34 tipos de evento, firmados y con reintentos durante unas 32 horas), escribe con upserts sobre un atributo único y una Idempotency-Key, y descarta los envíos repetidos por su cabecera idempotency-key. Odysi no sincroniza buzones ni calendarios, a propósito: tu IA los lee con sus propios conectores y escribe en Odysi lo que importa.

Comprobado con el código de Odysi CRM y los documentos de descubrimiento publicados por su conector el 17 de octubre de 2026. Odysi Suite está en beta: los límites y las cifras pueden cambiar, y la referencia completa de la API se comparte bajo petición.

Odysi Suite
Beta · gratis durante la beta

Consigue un espacio de trabajo y una clave de API

Regístrate en Odysi CRM, crea una clave y haz tu primera llamada en unos minutos. Para la referencia completa de la API, o para activar Odysi Projects, háblanos.