Talkyria API

Confirma pedidos, dispara llamadas y gestiona agentes de voz desde tu propio sistema. Todo lo que ves aquí sale del contrato OpenAPI, así que refleja la API real.

https://api.talkyria.com/api/v1https://app.talkyria.com/api/v1

Tu primera llamada

Tres pasos, dos minutos.

1 · Consigue tu clave

En el dashboard: Integraciones → API → Crear clave. Se muestra una sola vez, así que guárdala. Empieza con una de sandbox (tk_test_…) si vas a probar.

2 · Comprueba que funciona

Pídele a la API quién eres. Si responde, ya estás dentro.
cURL
curl 'https://api.talkyria.com/api/v1/me' \
  -H 'Authorization: Bearer tk_live_TU_CLAVE'

3 · Dispara tu primera llamada

Un pedido con su teléfono, y el agente llama.
cURL
curl -X POST 'https://api.talkyria.com/api/v1/calls/trigger' \
  -H 'Authorization: Bearer tk_live_TU_CLAVE' \
  -H 'Content-Type: application/json' \
  -d '{
    "externalId": "MI-PEDIDO-001",
    "customerName": "María",
    "customerPhone": "+573001234567",
    "totalPrice": 89900
  }'

Antes de integrar

Cinco cosas que evitan el 90 % de los tropiezos.

La API vive en dos hosts. No son intercambiables.

api.talkyria.com

Pedidos, llamadas, voces, saldo, webhooks salientes, ofertas y leads.

app.talkyria.com

Agentes, analytics, teléfonos y números verificados.

La misma clave sirve en los dos. Cada operación de abajo lleva su host en un distintivo junto al método, así que no hace falta memorizarlo. Y si te equivocas, la respuesta te lo dice: code: "wrong_host" con un campo correctUrl que trae la URL exacta a la que reenviar la petición.

Autenticación

Cada petición lleva tu clave en Authorization: Bearer tk_live_…. Nunca la pongas en el navegador: los endpoints de datos no habilitan CORS a propósito, porque la clave da acceso a los datos de tus clientes. Llama a la API desde tu servidor.

Sandbox

Las claves tk_test_… simulan telefonía y mutaciones: no hacen llamadas reales ni generan cobros, y responden { "test": true }. La gestión de agentes sí opera sobre agentes reales, porque es configuración.

Si tienes varias tiendas

Con una sola tienda no tienes que hacer nada. Si tu workspace tiene varias, manda X-Talkyria-Shop: mi-tienda.myshopify.com (dominio o id) para decir sobre cuál operar. Sin esa cabecera y con varias tiendas, la respuesta es 400 shop_ambiguous.

Errores y reintentos

Todo error trae code (estable, para ramificar) y message (para leer). El campo error es legado y su contenido cambia según el host — no lo uses para decidir. Los 429 traen Retry-After con los segundos exactos a esperar.

Límites

Por ventana de 60 segundos y por clave: 1200 lecturas, 600 escrituras y 120 en sandbox. Además un tope por tienda de 2000 por minuto, así que varias claves de una misma tienda no multiplican el límite. Al excederlo llega un 429 con las cabeceras X-RateLimit-*.

Permisos de las claves

Cada clave lleva los permisos que le des. Si le falta uno, la respuesta es 403 insufficient_scope e incluye required_scope con el que hay que agregar.
  • orders:read

    Leer pedidos: cliente, estado, dirección, totales e historial de llamadas.

  • orders:write

    Actualizar el estado de un pedido (logístico en api., del pedido en app.).

  • calls:trigger

    Disparar llamadas e ingresar pedidos para llamar.

  • calls:read

    Leer el resultado de las llamadas: duración, outcome y resumen.

  • calls:read:content

    Leer transcripción y grabación. Es contenido sensible, por eso va aparte.

  • agents:read

    Leer agentes y su configuración, incluido el prompt cuando es personalizado.

  • agents:write

    Crear, editar y borrar agentes, su flujo conversacional y su voz.

  • voices:read

    Leer el catálogo de voces disponibles.

  • webhooks:read

    Leer la configuración de los webhooks salientes y su historial de entregas.

  • webhooks:write

    Crear, editar y borrar webhooks salientes.

  • leads:write

    Ingresar leads de ventas para que el agente los llame.

  • leads:read

    Leer los leads de ventas de un agente y su estado en la cadencia.

  • offers:read

    Leer resultados de upsell: producto ofrecido, si lo aceptaron y cuánto sumó.

  • offers:write

    Crear, editar y borrar ofertas (upsell, cantidad, combo) en Shopify y Dropi.

  • analytics:read

    Leer métricas agregadas: tasas, outcomes, embudo y desglose por agente.

  • billing:read

    Leer el saldo de la cuenta, la tarifa por minuto y los minutos incluidos.

  • phones:read

    Leer los números dedicados de la cuenta y su asignación.

  • phones:write

    Comprar, asignar y liberar números dedicados. Comprar descuenta saldo.

  • verified_caller_ids:read

    Leer los números propios verificados y su estado de verificación.

  • verified_caller_ids:write

    Solicitar la verificación de un número propio, confirmarla y borrarlo.

  • human_calls:dispatch

    Pedir que una PERSONA de tu equipo llame, desde su softphone. No es el motor de voz: sólo entra a quien tenga el teléfono conectado. Consume saldo igual que una llamada de IA.

  • human_calls:read

    Ver qué asesores pueden atender en este momento y por qué los demás no.

Orders

Pedidos: consulta, historial de llamadas y estado logístico.

los dos hostsorders:read
api.talkyria.comorders:read
api.talkyria.comcalls:read
api.talkyria.comcalls:trigger
api.talkyria.comorders:write
app.talkyria.comorders:read
los dos hostsorders:write
app.talkyria.comorders:write

Calls

Disparar llamadas y leer su metadata/outcome.

api.talkyria.comcalls:read
api.talkyria.comcalls:read
los dos hostscalls:trigger
api.talkyria.comcalls:read
api.talkyria.comcalls:trigger
api.talkyria.comcalls:read
api.talkyria.comcalls:trigger
api.talkyria.comcalls:trigger

Agents

Crear, editar, listar y borrar agentes de voz.

api.talkyria.comagents:read
app.talkyria.comagents:write
api.talkyria.comagents:read
app.talkyria.comagents:write
app.talkyria.comagents:write
app.talkyria.comagents:read
app.talkyria.comagents:write

Workflow

Flujo conversacional completo del agente (Nivel B).

app.talkyria.comagents:read
app.talkyria.comagents:write

Voices

Catálogo de voces.

api.talkyria.comvoices:read

Leads

Leads de ventas (speed-to-lead).

api.talkyria.comleads:read
api.talkyria.comleads:write

Offers

Resultados de upsell / smart-offers (revenue conversation intelligence).

api.talkyria.comoffers:read
api.talkyria.comoffers:read
api.talkyria.comoffers:write
api.talkyria.comoffers:read
api.talkyria.comoffers:write
api.talkyria.comoffers:write

Analytics

Métricas agregadas (tasas, outcomes, funnel, por-agente).

app.talkyria.comanalytics:read

Webhooks

Webhooks salientes: recibe el resultado final de cada llamada en tiempo real (sin polling).

api.talkyria.comwebhooks:read
api.talkyria.comwebhooks:write
api.talkyria.comwebhooks:read
api.talkyria.comwebhooks:write
api.talkyria.comwebhooks:write
api.talkyria.comwebhooks:read

Billing

Cuenta: saldo, tarifa, minutos gratis y contexto de la clave (solo lectura).

api.talkyria.combilling:read
api.talkyria.combilling:read

Phones

Números dedicados: comprar, listar, asignar a un agente y liberar.

app.talkyria.comphones:read
app.talkyria.comphones:write
app.talkyria.comphones:read
app.talkyria.comphones:write
app.talkyria.comphones:write

Verified Caller IDs

Números propios verificados: solicitar el código, confirmarlo y usarlos como identificador de llamada.

app.talkyria.comverified_caller_ids:read
app.talkyria.comverified_caller_ids:write
app.talkyria.comverified_caller_ids:write
app.talkyria.comverified_caller_ids:write

Llamadas

api.talkyria.com
api.talkyria.com
api.talkyria.com

Esquemas

Las formas de datos que referencian las operaciones.

Error
Ejemplo
{
  "code": "not_found",
  "message": "string",
  "error": "Order not found",
  "correctUrl": "https://…",
  "correctHost": "api.talkyria.com",
  "docs": "string"
}
  • codereq"bad_request" | "unauthorized" | "payment_required" | "forbidden" | "not_found" | "conflict" | "unprocessable_entity" | "rate_limited" | "rate_limit" | "internal_error" | "error" | "missing_auth" | "invalid_api_key" | "key_expired" | "insufficient_scope" | "validation_error" | "invalid_json" | "wrong_host" | "method_not_allowed"

    Código estable legible por máquina. **Es el único campo por el que debes ramificar.** Los códigos de la lista son los transversales; muchos endpoints devuelven además uno más específico de su dominio (`order_not_found`, `billing_inactive`, `shop_ambiguous`, `idempotency_conflict`, `no_voice_engine`, `wrong_engine`…). Trata un código desconocido como un fallo de su familia de estado HTTP.

  • messagereqstring

    Texto humano del error. Siempre presente.

  • errorreqstring

    LEGADO — no ramifiques por este campo. En `api.talkyria.com` lleva el mensaje humano; en `app.talkyria.com` lleva el código. Se conserva para no romper a quien ya lo lee, pero su significado depende del host. Usa `code` (máquina) y `message` (humano).

  • correctUrlstring

    Sólo con `code: "wrong_host"`. URL exacta a la que reenviar la petición.

  • correctHoststring

    Sólo con `code: "wrong_host"`. Host que sí sirve la operación.

  • docsstring

    Enlace a la documentación de la operación.

OrderLogistics

Logística del pedido (aditivo, 2026-09-15). Reúne lo que antes venía plano más la última novedad conocida.

Ejemplo
{
  "carrier": "Servientrega",
  "trackingNumber": "ABC123456789",
  "logisticStatus": "string",
  "noveltyText": "string",
  "lastIncidenceAt": "2026-09-09T14:32:00.000Z"
}
  • carrierstring
  • trackingNumberstring
  • logisticStatusstring

    Texto libre del canal — ver la nota en `Order.logisticStatus`.

  • noveltyTextstring

    Texto de la última novedad reportada por la transportadora, si la hubo.

  • lastIncidenceAtstring (date-time)
OrderInsights

Huella digital del comprador y datos de la dirección según Dropi (aditivo, 2026-09-15). `null` cuando el pedido no tiene datos de Dropi. Nunca incluye datos financieros del comercio.

Ejemplo
{
  "riskLevel": "alto",
  "riskReasons": [
    "string"
  ],
  "addressValidated": false,
  "merchantTags": [
    "string"
  ],
  "origin": "string",
  "buyerHistory": {
    "total": 0,
    "delivered": 0,
    "returned": 0,
    "deliveryRate": 0
  },
  "coverageCod": false,
  "carrierName": "Servientrega",
  "cityName": "Bogotá"
}
  • riskLevel"alto" | "medio" | "bajo" | "null"

    Riesgo del comprador según su historial de entregas y devoluciones en Dropi.

  • riskReasonsstring[]
  • addressValidatedboolean

    true si la transportadora validó la dirección.

  • merchantTagsstring[]

    Etiquetas que el comercio puso al pedido en Dropi.

  • originstring

    De dónde nació el pedido en Dropi (shopify, chateapro, manual…).

  • buyerHistoryobject

    Huella digital del comprador en Dropi: pedidos hechos, recibidos y devueltos, y el porcentaje recibido sobre los terminados.

  • └totalinteger
  • └deliveredinteger
  • └returnedinteger
  • └deliveryRateinteger

    0-100: % de pedidos terminados que el comprador recibió. null sin pedidos terminados.

  • coverageCodboolean

    true si la ciudad tiene cobertura de pago contraentrega con esa transportadora.

  • carrierNamestring
  • cityNamestring
Order
Ejemplo
{
  "id": "ord_a1b2c3",
  "shopifyOrderId": "5891234567890",
  "externalId": "MI-PEDIDO-001",
  "externalSource": "string",
  "orderNumber": "1042",
  "status": "confirmed",
  "customerName": "María González",
  "customerPhone": "+573001234567",
  "customerCity": "Bogotá",
  "totalPrice": 0,
  "itemsSummary": "1x Faja Reductora Talla M",
  "logisticStatus": "PENDIENTE CONFIRMACION",
  "trackingNumber": "ABC123456789",
  "carrier": "Servientrega",
  "callAttempts": 0,
  "cityFinal": "Bogotá",
  "provinceFinal": "Cundinamarca",
  "confirmationChannel": "string",
  "attentionReason": "string",
  "confirmedAt": "2026-09-09T14:32:00.000Z",
  "createdAt": "2026-09-09T14:32:00.000Z",
  "logistics": {
    "carrier": "Servientrega",
    "trackingNumber": "ABC123456789",
    "logisticStatus": "string",
    "noveltyText": "string",
    "lastIncidenceAt": "2026-09-09T14:32:00.000Z"
  },
  "insights": {
    "riskLevel": "alto",
    "riskReasons": [
      "string"
    ],
    "addressValidated": false,
    "merchantTags": [
      "string"
    ],
    "origin": "string",
    "buyerHistory": {
      "total": 0,
      "delivered": 0,
      "returned": 0,
      "deliveryRate": 0
    },
    "coverageCod": false,
    "carrierName": "Servientrega",
    "cityName": "Bogotá"
  }
}
  • idstring
  • shopifyOrderIdstring
  • externalIdstring
  • externalSourcestring
  • orderNumberstring
  • statusstring
  • customerNamestring
  • customerPhonestring
  • customerCitystring
  • totalPricenumber
  • itemsSummarystring
  • logisticStatusstring

    Estado logístico TAL CUAL lo reporta el canal del comercio — es texto libre, NO un enumerado cerrado. Cada integración usa su propio vocabulario: ChateaPro manda mayúsculas en español (`PENDIENTE CONFIRMACION`, `NOVEDAD`, `ENTREGADO`, `EN REPARTO`, `RECLAME EN OFICINA`, `GUIA_GENERADA`, `DEVOLUCION`, `CANCELADO`…) y Dropi manda las cadenas de su transportadora. Medido en producción: 41 valores distintos en ChateaPro, 57 en Dropi y 82 en Shopify. NO compares por igualdad contra una lista fija: normaliza (mayúsculas, sin tildes) y compara por palabra clave, o usa `status` (el estado del pedido, que sí es acotado).

  • trackingNumberstring
  • carrierstring
  • callAttemptsinteger
  • cityFinalstring
  • provinceFinalstring
  • confirmationChannelstring

    Quién confirmó: call | whatsapp | cross_integration.

  • attentionReasonstring
  • confirmedAtstring (date-time)
  • createdAtstring (date-time)
  • logisticsOrderLogisticsver esquema
  • insightsOrderInsightsver esquema
OrderDetail

Detalle de un pedido. La forma DIFIERE de la del listado: aquí el cliente y la dirección vienen anidados, y los productos se llaman `products` (no `itemsSummary`).

Ejemplo
{
  "id": "ord_a1b2c3",
  "shopifyOrderId": "5891234567890",
  "externalId": "MI-PEDIDO-001",
  "externalSource": "string",
  "orderNumber": "1042",
  "status": "confirmed",
  "customer": {
    "name": "María González",
    "phone": "+573001234567",
    "city": "Bogotá"
  },
  "products": "1x Faja Reductora Talla M",
  "totalPrice": 0,
  "currency": "COP",
  "address": {
    "address1": "Calle 40 # 18-08",
    "address2": "Calle 40 # 18-08",
    "city": "Bogotá",
    "province": "Cundinamarca"
  },
  "addressFinal": "Calle 40 # 18-08",
  "cityFinal": "Bogotá",
  "provinceFinal": "Cundinamarca",
  "logisticStatus": "string",
  "trackingNumber": "ABC123456789",
  "carrier": "Servientrega",
  "logistics": {
    "carrier": "Servientrega",
    "trackingNumber": "ABC123456789",
    "logisticStatus": "string",
    "noveltyText": "string",
    "lastIncidenceAt": "2026-09-09T14:32:00.000Z"
  },
  "insights": {
    "riskLevel": "alto",
    "riskReasons": [
      "string"
    ],
    "addressValidated": false,
    "merchantTags": [
      "string"
    ],
    "origin": "string",
    "buyerHistory": {
      "total": 0,
      "delivered": 0,
      "returned": 0,
      "deliveryRate": 0
    },
    "coverageCod": false,
    "carrierName": "Servientrega",
    "cityName": "Bogotá"
  },
  "callAttempts": 0,
  "confirmationChannel": "string",
  "attentionReason": "string",
  "confirmedAt": "2026-09-09T14:32:00.000Z",
  "createdAt": "2026-09-09T14:32:00.000Z",
  "metadata": {},
  "calls": [
    {
      "id": "ord_a1b2c3",
      "callType": "string",
      "status": "confirmed",
      "outcome": "confirmed",
      "providerCallId": "id_3f9b02",
      "durationSeconds": 0,
      "summary": "El cliente confirmó el pedido y la dirección.",
      "notesForHuman": "string",
      "cancelReason": "string",
      "resolutionInstruction": "string",
      "channel": "ai",
      "human": {
        "advisor": {
          "id": "ord_a1b2c3",
          "name": "María González"
        },
        "dialedPhone": "+573001234567",
        "label": "string"
      },
      "analysis": {
        "sentiment": "string",
        "wrongNumber": false,
        "addressChanged": false,
        "productConfirmed": false,
        "priceConfirmed": false
      },
      "offer": {
        "shown": false,
        "accepted": false,
        "productTitle": "string"
      },
      "disconnectionReason": "string",
      "fromNumber": "string",
      "createdAt": "2026-09-09T14:32:00.000Z",
      "transcript": "string",
      "recordingUrl": "https://…"
    }
  ]
}
  • idstring
  • shopifyOrderIdstring
  • externalIdstring
  • externalSourcestring
  • orderNumberstring
  • statusstring
  • customerobject

    Cliente. En el LISTADO estos mismos datos vienen planos (`customerName`, `customerPhone`, `customerCity`).

  • └namestring
  • └phonestring
  • └citystring
  • productsstring

    Resumen de productos. En el listado se llama `itemsSummary`.

  • totalPricenumber
  • currencystring

    Moneda del país de la tienda (COP, MXN, PEN…).

  • addressobject
  • └address1string
  • └address2string
  • └citystring
  • └provincestring
  • addressFinalstring

    Dirección corregida durante la llamada, si el cliente la cambió.

  • cityFinalstring
  • provinceFinalstring
  • logisticStatusstring

    Texto libre del canal — ver la nota en `Order.logisticStatus`.

  • trackingNumberstring
  • carrierstring
  • logisticsOrderLogisticsver esquema
  • insightsOrderInsightsver esquema
  • callAttemptsinteger
  • confirmationChannelstring

    Quién confirmó: call | whatsapp | cross_integration.

  • attentionReasonstring
  • confirmedAtstring (date-time)
  • createdAtstring (date-time)
  • metadataobject

    Sólo campos propios del comercio (`customVars`, `productDetails`, `productDescription`, `callType`); nunca el blob interno.

  • callsCall[]ver esquema
LogisticStatusUpdate

Cuerpo del estado LOGÍSTICO — `PATCH https://api.talkyria.com/api/v1/orders/{id}/status`. Al menos un campo.

Ejemplo
{
  "logisticStatus": "EN REPARTO",
  "trackingNumber": "ABC123456789",
  "carrier": "Servientrega"
}
  • logisticStatusstring

    Texto libre, el vocabulario de tu canal. Ver la nota en `Order.logisticStatus`.

  • trackingNumberstring
  • carrierstring
OrderStatusUpdate

Cuerpo del estado del PEDIDO — `PATCH https://app.talkyria.com/api/v1/orders/{id}/status`. Confirmar o cancelar además detiene los reintentos de llamada.

Ejemplo
{
  "status": "confirmed",
  "notes": "string"
}
  • statusreq"confirmed" | "cancelled" | "needs_attention"
  • notesstring

    Nota libre que queda en el pedido.

Call
Ejemplo
{
  "id": "ord_a1b2c3",
  "callType": "string",
  "status": "confirmed",
  "outcome": "confirmed",
  "providerCallId": "id_3f9b02",
  "durationSeconds": 0,
  "summary": "El cliente confirmó el pedido y la dirección.",
  "notesForHuman": "string",
  "cancelReason": "string",
  "resolutionInstruction": "string",
  "channel": "ai",
  "human": {
    "advisor": {
      "id": "ord_a1b2c3",
      "name": "María González"
    },
    "dialedPhone": "+573001234567",
    "label": "string"
  },
  "analysis": {
    "sentiment": "string",
    "wrongNumber": false,
    "addressChanged": false,
    "productConfirmed": false,
    "priceConfirmed": false
  },
  "offer": {
    "shown": false,
    "accepted": false,
    "productTitle": "string"
  },
  "disconnectionReason": "string",
  "fromNumber": "string",
  "createdAt": "2026-09-09T14:32:00.000Z",
  "transcript": "string",
  "recordingUrl": "https://…"
}
  • idstring
  • callTypestring
  • statusstring
  • outcomestring
  • providerCallIdstring

    Id de la llamada en el motor de voz (útil para cruzar con soporte). `null` cuando no hubo llamada en el motor. El mismo campo llega en el webhook saliente (`call.providerCallId`). Añadido 2026-10-02.

  • durationSecondsinteger
  • summarystring
  • notesForHumanstring
  • cancelReasonstring
  • resolutionInstructionstring

    Instrucción operativa generada por IA (novedades/logística).

  • channel"ai" | "human"

    Quién marcó: `ai` = el motor de voz · `human` = una persona del equipo desde el softphone. Ambas conviven en la misma lista; este campo es el que las separa.

  • humanobject

    Presente sólo cuando `channel` = `human` (en `ai` viene `null`). Identifica quién marcó y, si fue una marcación libre, a qué número.

  • └advisorobject

    El asesor que marcó. `null` = la marcó el dueño de la cuenta.

  • └dialedPhonestring

    Marcación libre: número marcado a mano, sin pedido detrás. `null` cuando la llamada sí tiene pedido.

  • └labelstring

    Etiqueta que el asesor puso a la marcación libre.

  • analysisobject

    Análisis post-llamada tipado (allowlisted, no-PII). Lo produce la IA: en llamadas con `channel` = `human` todos sus campos vienen en `null` (no hay análisis de IA en una llamada de asesor).

  • └sentimentstring
  • └wrongNumberboolean
  • └addressChangedboolean
  • └productConfirmedboolean
  • └priceConfirmedboolean
  • offerobject

    Resultado de upsell — solo con scope `offers:read`.

  • └shownboolean
  • └acceptedboolean
  • └productTitlestring
  • disconnectionReasonstring
  • fromNumberstring
  • createdAtstring (date-time)
  • transcriptstring

    Solo con scope `calls:read:content`.

  • recordingUrlstring

    Enlace estable `/rec/<token>`. Solo con scope `calls:read:content`.

OfferResult
Ejemplo
{
  "id": "ord_a1b2c3",
  "callId": "call_9f2e10",
  "type": "string",
  "name": "María González",
  "shown": false,
  "accepted": false,
  "productTitle": "string",
  "discountedPrice": 0,
  "quantityAccepted": 0,
  "chosenVariantTitle": "string",
  "revenueUsd": 0,
  "lineItemAddedToShopify": false,
  "shopifyError": "string",
  "createdAt": "2026-09-09T14:32:00.000Z"
}
  • idstring
  • callIdstring
  • typestring

    upsell | quantity | bundle

  • namestring
  • shownboolean
  • acceptedboolean
  • productTitlestring
  • discountedPricenumber
  • quantityAcceptedinteger
  • chosenVariantTitlestring
  • revenueUsdnumber

    Revenue del upsell (GMV del merchant).

  • lineItemAddedToShopifyboolean
  • shopifyErrorstring
  • createdAtstring (date-time)
BulkBatch
Ejemplo
{
  "id": "ord_a1b2c3",
  "status": "confirmed",
  "total": 0,
  "processed": 0,
  "succeeded": 0,
  "failed": 0,
  "customMotif": "string",
  "agentId": "agt_7f3c1d",
  "createdAt": "2026-09-09T14:32:00.000Z",
  "updatedAt": "2026-09-09T14:32:00.000Z"
}
  • idstring
  • statusstring

    pending | in_progress | paused | cancelled | completed

  • totalinteger
  • processedinteger
  • succeededinteger
  • failedinteger
  • customMotifstring
  • agentIdstring

    Agente elegido para todo el lote (null = cada pedido con su agente).

  • createdAtstring (date-time)
  • updatedAtstring (date-time)
Agent
Ejemplo
{
  "id": "ord_a1b2c3",
  "integration": "shopify",
  "type": "string",
  "name": "María González",
  "triggerStatuses": [
    "string"
  ],
  "isActive": false,
  "voice": {
    "id": "ord_a1b2c3",
    "name": "María González",
    "language": "string"
  },
  "voiceReady": false,
  "hasCallerNumber": false,
  "createdAt": "2026-09-09T14:32:00.000Z"
}
  • idstring
  • integrationstring
  • typestring
  • namestring
  • triggerStatusesstring[]
  • isActiveboolean
  • voiceobject
  • └idstring
  • └namestring
  • └languagestring
  • voiceReadyboolean
  • hasCallerNumberboolean
  • createdAtstring (date-time)
AgentDetail
Ejemplo
{
  "id": "ord_a1b2c3",
  "integration": "shopify",
  "type": "string",
  "name": "María González",
  "triggerStatuses": [
    "string"
  ],
  "isActive": false,
  "voice": {
    "id": "ord_a1b2c3",
    "name": "María González",
    "language": "string"
  },
  "voiceReady": false,
  "hasCallerNumber": false,
  "createdAt": "2026-09-09T14:32:00.000Z",
  "retry": {
    "maxRetries": 0,
    "delayMinutes": 0
  },
  "maxCallDuration": 0,
  "updatedAt": "2026-09-09T14:32:00.000Z",
  "callMode": "AUTO",
  "hybridAmountThreshold": 0,
  "duplicateCallWindowHours": 0,
  "timezone": "string",
  "callDelaySeconds": 0
}
  • idstring
  • integrationstring
  • typestring
  • namestring
  • triggerStatusesstring[]
  • isActiveboolean
  • voiceobject
  • └idstring
  • └namestring
  • └languagestring
  • voiceReadyboolean
  • hasCallerNumberboolean
  • createdAtstring (date-time)
  • retryobject
  • └maxRetriesinteger
  • └delayMinutesinteger
  • maxCallDurationinteger
  • updatedAtstring (date-time)
  • callMode"AUTO" | "MANUAL" | "HYBRID"
  • hybridAmountThresholdnumber
  • duplicateCallWindowHoursinteger
  • timezonestring
  • callDelaySecondsinteger
Voice
Ejemplo
{
  "voiceId": "9Godp7dNohUvXk6qp0gS",
  "name": "María González",
  "language": "string",
  "accent": "string",
  "gender": "string",
  "tier": "string",
  "isMultilingual": false,
  "previewUrl": "https://…"
}
  • voiceIdstring
  • namestring
  • languagestring
  • accentstring
  • genderstring
  • tierstring
  • isMultilingualboolean
  • previewUrlstring
Phone

Número dedicado (merchant-safe — nunca expone el carrier interno, Regla 119).

Ejemplo
{
  "id": "ord_a1b2c3",
  "phoneNumber": "+16015551234",
  "nickname": "string",
  "country": "US",
  "monthlyPriceUsd": 2,
  "isActive": false,
  "purchasedAt": "2026-09-09T14:32:00.000Z",
  "nextBillingAt": "2026-09-09T14:32:00.000Z"
}
  • idstring
  • phoneNumberstring
  • nicknamestring
  • countrystring
  • monthlyPriceUsdnumber
  • isActiveboolean
  • purchasedAtstring (date-time)
  • nextBillingAtstring (date-time)
VerifiedCallerId

Número propio del merchant verificado (Camino 1). Nunca expone el detalle de fallo del proveedor de verificación.

Ejemplo
{
  "id": "ord_a1b2c3",
  "phoneNumber": "+573001234567",
  "country": "CO",
  "status": "pending",
  "verificationMethod": "call",
  "verifiedAt": "2026-09-09T14:32:00.000Z",
  "lastTestCallAt": "2026-09-09T14:32:00.000Z",
  "createdAt": "2026-09-09T14:32:00.000Z"
}
  • idstring
  • phoneNumberstring
  • countrystring

    ISO 3166-1 alfa-2 derivado del número (p. ej. "CO", "ES").

  • status"pending" | "verified" | "failed"
  • verificationMethod"call" | "sms"
  • verifiedAtstring (date-time)
  • lastTestCallAtstring (date-time)
  • createdAtstring (date-time)
TriggerRequest

Dispara una llamada. Shape CANÓNICO = PLANO (los campos de orden en la raíz). Por compatibilidad, el endpoint TAMBIÉN acepta el shape ANIDADO legacy (`order:{ externalId, products, totalPrice, currency }` + `address:{}`) — ambos producen el mismo resultado. Sólo `externalId` + `customer.phone` son obligatorios (agente-first): para un agente contraentrega manda producto/precio/dirección; para un agente API genérico manda tu propio contexto en `customVariables`.

Ejemplo
{
  "externalId": "MI-PEDIDO-001",
  "callType": "confirmation",
  "estado": "string",
  "immediate": false,
  "scheduledAt": "2026-09-09T14:32:00.000Z",
  "timezone": "string",
  "language": "string",
  "source": "string",
  "agentId": "agt_7f3c1d",
  "merchantExternalId": "id_3f9b02",
  "customer": {
    "name": "María González",
    "phone": "+573001234567",
    "email": "cliente@ejemplo.com",
    "ns": "string"
  },
  "products": [
    {
      "name": "María González",
      "quantity": 1,
      "price": "string"
    }
  ],
  "totalPrice": "string",
  "currency": "COP",
  "shippingAddress": {
    "address1": "Calle 40 # 18-08",
    "address2": "Calle 40 # 18-08",
    "city": "Bogotá",
    "province": "Cundinamarca",
    "countryCode": "CO"
  },
  "metadata": {},
  "customVariables": {
    "clave": "string"
  }
}
  • externalIdreqstring

    ID del pedido en tu sistema (clave de idempotencia).

  • callType"confirmation" | "novelty" | "office" | "delivery" | "dispatched" | "delivered" | "devolucion"
  • estadostring

    Opcional. Estado del pedido EN TU PLATAFORMA ("listo", "empacado", lo que sea). Sin `agentId`, el canal (`source`) + este estado eligen el agente según los disparadores configurados en el Studio. Un estado sin disparador guarda el pedido sin llamada (`not_called`, `no_trigger_configured`).

  • immediateboolean

    «Llamar ya»: omite el retardo de primera llamada del agente Y su horario de atención (la llamada sale en ~2 s). NO omite el modo de llamada del agente. Excluyente con `scheduledAt`.

  • scheduledAtstring (date-time)

    Llamar en este instante (ISO 8601, futuro, máx. 7 días). Reemplaza el retardo del agente y no se reprograma por su horario; si cae fuera de él, la respuesta lo avisa en `warnings` (`scheduled_outside_agent_hours`). Excluyente con `immediate`. Sin `immediate` ni `scheduledAt`, la llamada hereda el retardo, el horario y la acción fuera de horario del agente.

  • timezonestring

    No es un override: el horario es del agente. Si se envía, se ignora y la respuesta lo indica en `warnings` (`ignored_field:timezone`).

  • languagestring

    No es un override: el idioma es del agente. Si se envía, se ignora y la respuesta lo indica en `warnings` (`ignored_field:language`).

  • sourcestring

    De dónde viene la llamada (ej. tu CRM). Opcional; default `api`. También aceptado como `metadata.source`.

  • agentIdstring

    Enlace explícito de agente ("trigger custom"): marca ESTE agente para esta llamada. REQUERIDO para agentes API-native (type="api", sin ruteo por estado); opcional para agentes de integración — cuando se omite, aplica el ruteo por estado. Debe ser un agente ACTIVO de tu cuenta (si no, responde 400 agent_not_found).

  • merchantExternalIdstring

    Tu ID de merchant (opcional).

  • customerreqobject
  • └namestring
  • └phonereqstring

    E.164, ej. +573001234567.

  • └emailstring
  • └nsstring

    Contact NS de ChateaPro (opcional).

  • productsobject[]

    Opcional. Productos del pedido.

  • └namestring
  • └quantityinteger
  • └pricestring
  • totalPricestring

    Opcional. Valor total (string numérico).

  • currencystring
  • shippingAddressobject
  • └address1string
  • └address2string
  • └citystring
  • └provincestring
  • └countryCodestring
  • metadataobject

    Objeto libre. `metadata.source` es una alternativa a `source`.

  • customVariablesobject

    Variables custom del cliente que el agente puede hablar. La clave DEBE empezar por `cv_` (charset `[a-z0-9_]`, máx 40); máx 25 claves, valor string ≤200 chars (se recorta). Se sanitizan server-side. Para que se hablen, referéncialas en el workflow del agente (Nivel B) como `{{cv_xxx}}`.

TriggerResponse
Ejemplo
{
  "success": true,
  "orderId": "id_3f9b02",
  "callId": "call_9f2e10",
  "status": "queued",
  "notCalledReason": "string",
  "message": "string",
  "schedule": {
    "firstCallAt": "2026-09-09T14:32:00.000Z",
    "source": "request_immediate",
    "delaySeconds": 0,
    "deferredToWindow": false,
    "agentId": "agt_7f3c1d",
    "triggerId": "id_3f9b02"
  },
  "callMode": {
    "agent": "string",
    "applied": false,
    "reason": "string"
  },
  "warnings": [
    "string"
  ]
}
  • successboolean
  • orderIdstring
  • callIdstring
  • statusstring

    `queued` | `not_called` | `paused`.

  • notCalledReasonstring

    Solo con `status: not_called`.

  • messagestring
  • scheduleobject

    Plan de la primera llamada (aditivo).

  • └firstCallAtstring (date-time)
  • └source"request_immediate" | "request_scheduled" | "trigger" | "agent" | "settings" | "off_hours"

    De dónde salió el momento de la llamada. `off_hours` = movida a la próxima franja del agente.

  • └delaySecondsinteger

    Retardo heredado (o pedido) antes de pisos y horario.

  • └deferredToWindowboolean
  • └agentIdstring
  • └triggerIdstring
  • callModeobject

    Modo de llamada del agente aplicado a esta llamada (aditivo).

  • └agentstring
  • └appliedboolean
  • └reasonstring
  • warningsstring[]

    `ignored_field:timezone`, `ignored_field:language`, `scheduled_outside_agent_hours`.

CreateAgentRequest

Crear un agente. `integration` selecciona el tipo; omitirlo lo resuelve el motor del workspace. Campos requeridos adicionales por integración: **universal** → `mission` (define qué atiende el agente y le siembra sus disparadores); **shopify** → `type`; **chateapro/dropi** → `triggerStatuses` (+ `triggerCallType` obligatorio en chateapro); **sales** → `productName`, `productPrice`, `currency`, `paymentType`. Campos no aplicables a la integración se ignoran.

Ejemplo
{
  "integration": "universal",
  "mission": "confirmation_cod",
  "name": "María González",
  "type": "confirmation",
  "triggerCallType": "confirmation",
  "description": "string",
  "triggerStatuses": [
    "string"
  ],
  "voiceId": "9Godp7dNohUvXk6qp0gS",
  "language": "string",
  "allowUnverifiedVoice": false,
  "maxRetries": 0,
  "retryDelayMinutes": 0,
  "maxCallDuration": 0,
  "callDelaySeconds": 0,
  "phoneNumberId": "+573001234567",
  "enableVoicemailDetection": false,
  "customPrompt": "string",
  "useCustomPrompt": false,
  "workingHours": {
    "respect": false,
    "start": "string",
    "end": "string",
    "days": [
      0
    ],
    "timezone": "string",
    "offHoursAction": "string"
  },
  "callMode": "AUTO",
  "duplicateMode": "WINDOW",
  "discountEnabled": false,
  "discountCode": "string",
  "discountType": "percent",
  "discountValue": 0,
  "productName": "1x Faja Reductora Talla M",
  "productPrice": "string",
  "currency": "COP",
  "paymentType": "string",
  "productDetails": "string",
  "quantityOfferEnabled": false,
  "upsellEnabled": false,
  "objections": "string"
}
  • integration"universal" | "shopify" | "chateapro" | "dropi" | "sales"

    Omitida → la resuelve el MOTOR del workspace (universal si ya migró; shopify si es legado). NUNCA declarar un default estático: un cliente generado que lo inyecte explícito recibiría 409 wrong_engine en workspaces universales (Regla 502).

  • mission"confirmation_cod" | "confirmation_prepaid" | "dispatch" | "novelty" | "office_pickup" | "delivery_upcoming" | "delivery_attempt" | "post_delivery" | "returns_recovery" | "cancelled_winback" | "telemarketing" | "cart_recovery" | "sales" | "custom"

    universal — vocación del agente. De ella salen su flujo canónico y sus disparadores. OBLIGATORIA para `universal`: omitirla responde 400 (un agente sin misión nacería sin disparadores y no recibiría pedidos). Las 14 vocaciones: `confirmation_cod` (confirmar contraentrega), `confirmation_prepaid` (confirmar prepagado), `dispatch` (aviso de despacho), `novelty` (resolver novedades logísticas), `office_pickup` (recoger en oficina), `delivery_upcoming` (entrega próxima), `delivery_attempt` (intento de entrega), `post_delivery` (posventa), `returns_recovery` (recuperar devoluciones), `cancelled_winback` (recuperar cancelados), `telemarketing`, `cart_recovery` (carritos abandonados), `sales` (venta en frío) y `custom` (flujo propio).

  • namereqstring
  • type"confirmation" | "cart_recovery" | "prepaid" | "dispatch_cod" | "dispatch_prepaid" | "custom" | "api"

    Solo shopify. `api` = agente API-native (integration-neutral, sin ruteo por estado): se marca únicamente con el `agentId` explícito de POST /calls/trigger. Ilimitados por cuenta.

  • triggerCallType"confirmation" | "novelty" | "office" | "delivery" | "dispatched" | "delivered" | "devolucion"

    chateapro (obligatorio) / dropi.

  • descriptionstring
  • triggerStatusesstring[]

    shopify / chateapro / dropi.

  • voiceIdstring
  • languagestring
  • allowUnverifiedVoiceboolean

    Con `voiceId`: el servidor rechaza con `422 voice_language_mismatch` una voz que el catálogo PRUEBA ajena al idioma del agente (ni nativa ni verificada para él). `true` la acepta igualmente — la misma libertad que da el panel.

  • maxRetriesinteger
  • retryDelayMinutesinteger
  • maxCallDurationinteger
  • callDelaySecondsinteger
  • phoneNumberIdstring
  • enableVoicemailDetectionboolean
  • customPromptstring
  • useCustomPromptboolean
  • workingHoursobject
  • └respectboolean
  • └startstring
  • └endstring
  • └daysinteger[]
  • └timezonestring
  • └offHoursActionstring
  • callMode"AUTO" | "MANUAL"

    Solo dropi.

  • duplicateMode"WINDOW" | "OFF"

    Solo dropi.

  • discountEnabledboolean
  • discountCodestring
  • discountType"percent" | "fixed"
  • discountValuenumber
  • productNamestring

    Solo sales (requerido).

  • productPricestring

    Solo sales (requerido).

  • currencystring

    Solo sales (requerido).

  • paymentTypestring

    Solo sales (requerido).

  • productDetailsstring
  • quantityOfferEnabledboolean
  • upsellEnabledboolean
  • objectionsstring
UpdateAgentRequest

Todos los campos son opcionales. Solo se aplican los presentes (allowlist per-integración). Además de los listados: **chateapro/dropi** aceptan `triggerCallType`; **dropi** acepta `callMode`/`duplicateMode`/`exclusionGroup`; **sales** acepta sus campos de producto/oferta (`productName`, `productPrice`, `currency`, `paymentType`, `objections`, `upsellEnabled`, `metaPixelId`, `metaCapiToken`, …).

Ejemplo
{
  "integration": "string",
  "triggerCallType": "confirmation",
  "name": "María González",
  "description": "string",
  "triggerStatuses": [
    "string"
  ],
  "voiceId": "9Godp7dNohUvXk6qp0gS",
  "language": "string",
  "allowUnverifiedVoice": false,
  "customPrompt": "string",
  "useCustomPrompt": false,
  "maxRetries": 0,
  "retryDelayMinutes": 0,
  "retryScheduleJson": "string",
  "maxCallDuration": 0,
  "callDelaySeconds": 0,
  "callMode": "string",
  "hybridAmountThreshold": 0,
  "phoneNumberId": "+573001234567",
  "verifiedCallerIdId": "id_3f9b02",
  "isActive": false,
  "enableVoicemailDetection": false,
  "duplicateMode": "string",
  "duplicateCallWindowHours": 0,
  "discountEnabled": false,
  "discountCode": "string",
  "discountType": "string",
  "discountValue": 0,
  "workingHours": {
    "respect": false,
    "start": "string",
    "end": "string",
    "days": [
      0
    ],
    "timezone": "string",
    "offHoursAction": "string"
  },
  "retryHours": {
    "respect": false,
    "start": "string",
    "end": "string",
    "days": "string"
  }
}
  • integrationstring

    Ignorado — la integración se resuelve por el id.

  • triggerCallType"confirmation" | "novelty" | "office" | "delivery" | "dispatched" | "delivered" | "devolucion"

    chateapro/dropi.

  • namestring
  • descriptionstring
  • triggerStatusesstring[]
  • voiceIdstring
  • languagestring
  • allowUnverifiedVoiceboolean

    Con `voiceId`: el servidor rechaza con `422 voice_language_mismatch` una voz que el catálogo PRUEBA ajena al idioma del agente (ni nativa ni verificada para él). `true` la acepta igualmente — la misma libertad que da el panel.

  • customPromptstring
  • useCustomPromptboolean
  • maxRetriesinteger
  • retryDelayMinutesinteger
  • retryScheduleJsonstring
  • maxCallDurationinteger
  • callDelaySecondsinteger
  • callModestring

    **universal**: `AUTO` | `MANUAL` | `HYBRID` — el modo que respetan TODAS las llamadas del agente, también las creadas por API. **dropi**: `AUTO` | `MANUAL`.

  • hybridAmountThresholdnumber

    universal: con `HYBRID`, solo se llama sola a los pedidos por DEBAJO de este monto. Un valor ≤ 0 se guarda como null (se llama todo), igual que en el Studio.

  • phoneNumberIdstring
  • verifiedCallerIdIdstring
  • isActiveboolean
  • enableVoicemailDetectionboolean
  • duplicateModestring
  • duplicateCallWindowHoursinteger
  • discountEnabledboolean
  • discountCodestring
  • discountTypestring
  • discountValuenumber
  • workingHoursobject
  • └respectboolean
  • └startstring
  • └endstring
  • └daysinteger[]
  • └timezonestring
  • └offHoursActionstring
  • retryHoursobject
  • └respectboolean
  • └startstring
  • └endstring
  • └daysstring | integer[]

    CSV o array de días.

WorkflowDefinition

Grafo del flujo conversacional. El motor valida invariantes al guardar.

Ejemplo
{
  "nodes": [
    {
      "id": "ord_a1b2c3",
      "type": "startCall",
      "position": {},
      "data": {}
    }
  ],
  "edges": [
    {
      "id": "ord_a1b2c3",
      "source": "string",
      "target": "string",
      "data": {
        "label": "string",
        "condition": "string"
      }
    }
  ],
  "global_node_id": "string"
}
  • nodesreqobject[]
  • └idstring
  • └typestring
  • └positionobject
  • └dataobject
  • edgesobject[]
  • └idstring
  • └sourcestring
  • └targetstring
  • └dataobject
  • global_node_idstring
AdvancedConfig

Configuración avanzada del agente. En PATCH todos los campos son opcionales (los omitidos se preservan).

Ejemplo
{
  "ring_duration_ms": 0,
  "max_call_duration_ms": 0,
  "end_call_after_silence_ms": 0,
  "enable_backchannel": false,
  "voicemail": {
    "enabled": false,
    "action": "hangup",
    "speech_cutoff_seconds": 0,
    "system_prompt": "string"
  },
  "dictionary": [
    {
      "phrase": "string",
      "boost": 0,
      "phonetic": "+573001234567"
    }
  ],
  "analysis_fields": [
    {
      "type": "boolean",
      "name": "María González",
      "description": "string",
      "choices": [
        "string"
      ]
    }
  ],
  "handbook": {
    "default_personality": false,
    "natural_filler_words": false,
    "high_empathy": false,
    "echo_verification": false,
    "nato_phonetic_alphabet": false,
    "speech_normalization": false,
    "smart_matching": false,
    "ai_disclosure": false,
    "scope_boundaries": false
  }
}
  • ring_duration_msinteger

    Duración del timbrado (ms).

  • max_call_duration_msinteger

    Duración máxima de la llamada (ms).

  • end_call_after_silence_msinteger

    Colgar tras silencio total (ms).

  • enable_backchannelboolean

    Acks naturales (ajá/mhm) mientras el cliente habla.

  • voicemailobject
  • └enabledboolean

    Detección de buzón de voz.

  • └action"hangup"

    Siempre `hangup` (el motor cuelga al detectar buzón).

  • └speech_cutoff_secondsinteger
  • └system_promptstring
  • dictionaryobject[]

    Palabras clave (boost STT). PATCH reemplaza la lista completa.

  • └phrasereqstring
  • └boostinteger
  • └phoneticstring
  • analysis_fieldsobject[]

    Campos de análisis post-llamada. PATCH reemplaza la lista completa.

  • └typereq"boolean" | "string" | "enum" | "number"
  • └namereqstring
  • └descriptionstring
  • └choicesstring[]

    Solo type=enum.

  • handbookobject

    Toggles de personalidad.

  • └default_personalityboolean
  • └natural_filler_wordsboolean
  • └high_empathyboolean
  • └echo_verificationboolean
  • └nato_phonetic_alphabetboolean
  • └speech_normalizationboolean
  • └smart_matchingboolean
  • └ai_disclosureboolean
  • └scope_boundariesboolean
WebhookEndpoint

Un webhook saliente registrado. El `secret` NUNCA se incluye aquí.

Ejemplo
{
  "id": "ord_a1b2c3",
  "url": "https://…",
  "events": "call.outcome_final",
  "isActive": false,
  "merchantExternalId": "id_3f9b02",
  "createdAt": "2026-09-09T14:32:00.000Z"
}
  • idstring
  • urlstring (uri)
  • eventsstring

    Eventos suscritos, separados por coma (Regla 421). `call.outcome_final` = todos los resultados de llamada; o granulares `call.confirmed`/`call.cancelled`/`call.no_answer`/`call.voicemail`/`call.novelty`/`call.needs_attention`/`call.failed`. `call.novelty_resolved` (novedad resuelta por la IA o por Dropi; payload con `novelty` en vez de `call`) solo llega si se lista explícitamente o con `*`.

  • isActiveboolean
  • merchantExternalIdstring
  • createdAtstring (date-time)
WebhookEndpointWithSecret

Webhook con el `secret` incluido — devuelto SOLO al crearlo o al regenerarlo (una vez).

Ejemplo
{
  "id": "ord_a1b2c3",
  "url": "https://…",
  "events": "call.outcome_final",
  "isActive": false,
  "merchantExternalId": "id_3f9b02",
  "createdAt": "2026-09-09T14:32:00.000Z"
}
  • idstring
  • urlstring (uri)
  • eventsstring

    Eventos suscritos, separados por coma (Regla 421). `call.outcome_final` = todos los resultados de llamada; o granulares `call.confirmed`/`call.cancelled`/`call.no_answer`/`call.voicemail`/`call.novelty`/`call.needs_attention`/`call.failed`. `call.novelty_resolved` (novedad resuelta por la IA o por Dropi; payload con `novelty` en vez de `call`) solo llega si se lista explícitamente o con `*`.

  • isActiveboolean
  • merchantExternalIdstring
  • createdAtstring (date-time)