Conchi Developers
Documentacion publica para integradores externos. El contrato OpenAPI se puede descargar desde esta misma pagina.

Conchi Agent API V1

Resumen

La Conchi Agent API permite que un cliente externo envie mensajes de una conversacion a Conchi y reciba la respuesta final del agente mediante un webhook configurado para ese cliente externo.

El flujo de integracion es asincrono:

Cliente externo -> POST /api/v1/external-agent/turns -> Conchi Agent -> callback al webhook del cliente externo

V1 soporta:

V1 no soporta consulta de pedidos, cancelacion de pedidos, pagos ni callbacks estructurados obligatorios para UI. Conchi puede anadir campos opcionales de forma compatible dentro de V1.

Conceptos

Concepto Descripcion
externalClientId Identifica al cliente externo autenticado por la API key. No se envia en el body. Conchi lo deriva de la credencial bearer.
externalAccountId Identifica una cuenta, farmacia, centro, workspace o scope dentro del cliente externo. Es opcional si el cliente no tiene subcuentas.
externalEndCustomerId Identifica al usuario final que mantiene la conversacion dentro del canal del cliente externo.
sessionId Identificador de la sesion 24h creada o reutilizada por Conchi.
turnId Identificador del mensaje aceptado por Conchi.
correlationId Identificador opcional enviado por el cliente externo y devuelto en el callback. Sirve para correlacionar request y respuesta.
requestId Identificador opcional de la peticion del cliente externo. Si no se envia, Conchi usa correlationId o genera uno.

Sesiones 24h

Una sesion facturable de External Agent se reutiliza durante 24 horas cuando coinciden:

externalClientId + externalAccountId + externalEndCustomerId

Si el cliente externo no tiene subcuentas y omite externalAccountId, Conchi lo normaliza internamente a default. Si la integracion esta configurada con accountScopeRequired = true, externalAccountId pasa a ser obligatorio.

La conversacion del agente se aisla por:

externalClientId + externalAccountId + externalEndCustomerId + sessionId

Entornos

Usar siempre el host de API, no el host de la aplicacion/admin.

Entorno Base URL
Staging https://staging-api.conchi.ai
Produccion https://api.conchi.ai

La forma de URL es la misma en staging y produccion:

POST {baseUrl}/api/v1/external-agent/turns

Antes de pasar a produccion, el cliente externo debe validar en staging:

Autenticacion de entrada

La llamada a Conchi usa bearer auth:

Authorization: Bearer <external_client_api_key>

La API key identifica al externalClientId. El cliente externo no debe enviar externalClientId en el body. Conchi rechaza campos legacy o alias que intenten sustituir la identidad autenticada.

Las claves internas de Conchi no sirven para autenticar llamadas de clientes externos.

Versionado

La version mayor esta en la URL:

/api/v1/...

Opcionalmente, el cliente externo puede enviar la fecha de contrato V1:

Conchi-API-Version: 2026-05-01

Politica V1:

Enviar un mensaje

POST /api/v1/external-agent/turns HTTP/1.1
Authorization: Bearer <external_client_api_key>
Conchi-API-Version: 2026-05-01
Content-Type: application/json

Ejemplo con cuenta externa

Usar externalAccountId cuando el cliente externo gestiona varias farmacias, centros, cuentas o workspaces y cada uno tiene sus propios usuarios finales.

{
  "externalAccountId": "pharmacy-001",
  "externalEndCustomerId": "customer-789",
  "message": {
    "type": "text",
    "text": "Busco ibuprofeno 400"
  },
  "correlationId": "turn-2026-05-01-0001",
  "requestId": "turn-2026-05-01-0001",
  "capabilityHint": "product_search",
  "metadata": {
    "source": "web-chat"
  }
}

Ejemplo sin subcuentas

Omitir externalAccountId solo si la integracion no separa farmacias, centros, workspaces o cuentas hijas.

{
  "externalEndCustomerId": "app-user-789",
  "message": {
    "type": "text",
    "text": "Busco ibuprofeno 400"
  },
  "correlationId": "turn-2026-05-01-0002",
  "capabilityHint": "product_search"
}

Ejemplo de reserva

Para reservas, enviar idempotencyKey estable por intento logico de reserva. Esto evita duplicados si el cliente externo repite la misma operacion por timeout o reintento propio.

{
  "externalAccountId": "pharmacy-001",
  "externalEndCustomerId": "customer-789",
  "message": {
    "type": "text",
    "text": "Si, reservalo"
  },
  "correlationId": "turn-2026-05-01-0003",
  "requestId": "turn-2026-05-01-0003",
  "capabilityHint": "reservation_create",
  "idempotencyKey": "reservation-customer-789-2026-05-01-0001"
}

Campos del request

Campo Requerido Limite Descripcion
externalAccountId Condicional 1-160 chars Cuenta/farmacia/workspace del cliente externo. Obligatorio si la integracion tiene accountScopeRequired = true; si no, se normaliza a default cuando se omite.
externalEndCustomerId Si 1-160 chars Usuario final de la conversacion. Participa en la sesion 24h.
message.type Si text V1 solo acepta mensajes de texto.
message.text Si 1-8000 chars Texto del usuario final.
correlationId No Max 160 chars Id de correlacion del cliente externo. Conchi lo devuelve en el callback si se envio.
requestId No Max 160 chars Id de request del cliente externo. Si se omite, Conchi usa correlationId o genera uno.
idempotencyKey Condicional Max 180 chars Recomendado y requerido operativamente para creacion de reservas.
capabilityHint No Enum Sugerencia de intencion: product_search, reservation_create o human_escalation. No habilita capabilities desactivadas.
metadata No Objeto JSON Metadatos pequenos de integracion. No enviar PII innecesaria.

Campos no soportados como externalClientId, externalClientAccountId, externalClientEndCustomerId, externalClientCustomerId o customerId no forman parte del contrato y pueden ser rechazados.

Respuesta de aceptacion

Si el mensaje se acepta para procesamiento asincrono, Conchi responde 202 Accepted:

{
  "sessionId": "00000000-0000-0000-0000-000000000001",
  "turnId": "00000000-0000-0000-0000-000000000002",
  "requestId": "turn-2026-05-01-0001",
  "status": "queued"
}

Esta respuesta no es la respuesta final del agente. La respuesta final llega por callback.

Callback de respuesta final

Conchi envia el callback al webhook configurado para el cliente externo o para el externalAccountId si existe override.

Metodo:

POST <external_client_webhook_url>
Content-Type: application/json

Payload V1:

{
  "apiVersion": "v1",
  "eventVersion": "2026-05-01",
  "type": "conchi.agent.final_response",
  "deliveryTarget": "external_agent_callback",
  "createdAt": "2026-05-01T10:00:00.000Z",
  "externalClientId": "00000000-0000-0000-0000-000000000010",
  "externalAccountId": "pharmacy-001",
  "externalEndCustomerId": "customer-789",
  "sessionId": "00000000-0000-0000-0000-000000000001",
  "turnId": "00000000-0000-0000-0000-000000000002",
  "correlationId": "turn-2026-05-01-0001",
  "status": "answered",
  "message": "He encontrado estas opciones:\n\n1) Ibuprofeno 400 mg\n3.50 EUR\nDisponible"
}

Campos del callback

Campo Requerido Descripcion
apiVersion Si Version mayor del contrato. En V1 siempre v1.
eventVersion Si Version del evento webhook configurada para el cliente externo. Valor inicial: 2026-05-01.
type Si Tipo de evento. Para respuesta final: conchi.agent.final_response.
deliveryTarget Si Siempre external_agent_callback para esta integracion.
createdAt Si Fecha ISO-8601 en la que Conchi creo el evento.
externalClientId Si Cliente externo autenticado que origino la conversacion.
externalAccountId Si Cuenta/farmacia/workspace normalizado. Puede ser default.
externalEndCustomerId Si Usuario final de la conversacion.
sessionId Si Sesion 24h usada por el turn.
turnId Si Turn aceptado por Conchi.
correlationId No Devuelto cuando el request original lo incluyo.
status Si Estado funcional de la respuesta: answered, escalated, failed o needs_more_info.
message Si Texto final que debe mostrar el cliente externo al usuario final.
structured No Datos estructurados opcionales. No son obligatorios en V1 y deben tratarse como additive-only.

El cliente externo debe renderizar message en su propia UI/canal. Los callbacks de External Agent no usan WhatsApp, Meta ni sender de Conchi.

Autenticacion del callback

El modo de autenticacion se configura en Admin por cliente externo o por override de externalAccountId.

Modo none

Conchi envia headers de evento, pero no envia secreto:

Content-Type: application/json
X-Conchi-Event-Id: <outbox_event_id>
X-Conchi-Timestamp: <unix_timestamp_seconds>

Modo bearer

Conchi envia el secreto configurado como bearer:

Content-Type: application/json
X-Conchi-Event-Id: <outbox_event_id>
X-Conchi-Timestamp: <unix_timestamp_seconds>
Authorization: Bearer <external_agent_callback_secret>

Modo hmac_sha256

Conchi firma el body exacto del callback:

Content-Type: application/json
X-Conchi-Event-Id: <outbox_event_id>
X-Conchi-Timestamp: <unix_timestamp_seconds>
X-Conchi-Signature-256: sha256=<hex_digest>

Entrada de firma:

{event_id}.{timestamp}.{raw_body}

Requisitos recomendados para el receptor:

Ejemplo de verificacion HMAC en Node.js:

import crypto from "node:crypto";

function verifyConchiCallback({ secret, eventId, timestamp, rawBody, signature }) {
  const expectedHex = crypto
    .createHmac("sha256", secret)
    .update(`${eventId}.${timestamp}.${rawBody}`)
    .digest("hex");

  const receivedHex = signature.replace(/^sha256=/, "");
  const expected = Buffer.from(expectedHex, "hex");
  const received = Buffer.from(receivedHex, "hex");

  if (expected.length !== received.length) return false;
  return crypto.timingSafeEqual(expected, received);
}

Reintentos y timeouts de callback

Los callbacks de chat final response se entregan en tiempo real desde el outbox de Conchi.

Politica V1:

Intento Cuando se ejecuta
1 Inmediato tras crear el evento de callback.
2 1 segundo despues del fallo del intento 1.
3 3 segundos despues del fallo del intento 2.

Detalles:

Errores de entrada

Formato comun:

{
  "error": "INVALID_PAYLOAD",
  "message": "INVALID_PAYLOAD",
  "details": [
    {
      "path": "message.text",
      "message": "String must contain at least 1 character(s)"
    }
  ]
}

Errores habituales:

HTTP Codigo Motivo
400 INVALID_PAYLOAD El body no cumple el schema.
400 INVALID_EXTERNAL_IDENTIFIER Un identificador externo esta vacio o supera 160 caracteres.
400 EXTERNAL_ACCOUNT_ID_REQUIRED La integracion requiere externalAccountId y no se envio.
401 EXTERNAL_AGENT_AUTH_REQUIRED Falta Authorization: Bearer ....
401 EXTERNAL_CLIENT_AUTH_INVALID API key inexistente o invalida.
401 EXTERNAL_CLIENT_CREDENTIAL_INACTIVE Credencial desactivada.
401 EXTERNAL_CLIENT_CREDENTIAL_EXPIRED Credencial expirada.
403 EXTERNAL_CLIENT_INACTIVE Cliente externo inactivo o deshabilitado.
403 EXTERNAL_ACCOUNT_INACTIVE Cuenta externa bloqueada o inactiva por override.
500 INTERNAL_ERROR Error inesperado.

Ejemplos curl

Buscar producto

curl -i -X POST "${CONCHI_BASE_URL}/api/v1/external-agent/turns" \
  -H "Authorization: Bearer ${CONCHI_EXTERNAL_AGENT_API_KEY}" \
  -H "Conchi-API-Version: 2026-05-01" \
  -H "Content-Type: application/json" \
  -d '{
    "externalAccountId": "pharmacy-001",
    "externalEndCustomerId": "customer-789",
    "message": { "type": "text", "text": "Busco ibuprofeno 400" },
    "correlationId": "turn-search-001",
    "requestId": "turn-search-001",
    "capabilityHint": "product_search"
  }'

Crear reserva

curl -i -X POST "${CONCHI_BASE_URL}/api/v1/external-agent/turns" \
  -H "Authorization: Bearer ${CONCHI_EXTERNAL_AGENT_API_KEY}" \
  -H "Conchi-API-Version: 2026-05-01" \
  -H "Content-Type: application/json" \
  -d '{
    "externalAccountId": "pharmacy-001",
    "externalEndCustomerId": "customer-789",
    "message": { "type": "text", "text": "Si, reservalo" },
    "correlationId": "turn-reservation-001",
    "requestId": "turn-reservation-001",
    "capabilityHint": "reservation_create",
    "idempotencyKey": "reservation-customer-789-001"
  }'

Escalar a humano

curl -i -X POST "${CONCHI_BASE_URL}/api/v1/external-agent/turns" \
  -H "Authorization: Bearer ${CONCHI_EXTERNAL_AGENT_API_KEY}" \
  -H "Conchi-API-Version: 2026-05-01" \
  -H "Content-Type: application/json" \
  -d '{
    "externalAccountId": "pharmacy-001",
    "externalEndCustomerId": "customer-789",
    "message": { "type": "text", "text": "Quiero hablar con una persona" },
    "correlationId": "turn-human-001",
    "requestId": "turn-human-001",
    "capabilityHint": "human_escalation"
  }'

Checklist de integracion

Antes de produccion:

OpenAPI

El contrato publico V1 importable en herramientas como Swagger o Postman esta en docs/openapi/conchi-agent-api-v1.yaml.