← DocumentaciónAPI

API de Atentina

Cómo usar la plataforma desde tu sistema: los agentes, las llamadas, las campañas y las derivaciones, y los motores de LLM, transcripción y voz con acento argentino, por una API compatible con la de OpenAI.

Las dos APIs

Cómo funciona la plataforma (agentes, canales, conversaciones y motores) está en la introducción.

APIPara quéKey
InferenciaUsar los motores en tu sistema: chat, transcripción y síntesis, compatible con el SDK de OpenAI.llm, stt, tts
PlataformaLo mismo que el panel: agentes, llamadas, contactos, campañas, derivaciones, eventos y reportes.calls

Si preferís usar la plataforma desde un agente de IA, sin programar, mirá MCP para agentes de IA.

Empezar

  • URL base: https://app.atentina.com.ar/api/v1/inference
  • Autenticación: Authorization: Bearer vaas_..., con una API key que tenga el alcance del motor.
  • Compatible con el SDK de OpenAI: cambiás la base_url y la API key. El campo model es obligatorio en los SDK pero se ignora: el modelo lo fija la plataforma.
  1. Creá tu cuenta con el plan Free (20 minutos por mes, llamadas web, panel y API).
  2. En el panel, entrá a API > Nueva API key, elegí el alcance y copiala: se muestra una sola vez.
  3. Hacé el primer pedido:
Python
from openai import OpenAI

client = OpenAI(base_url="https://app.atentina.com.ar/api/v1/inference", api_key="vaas_...")

chat = client.chat.completions.create(
    model="atentina",  # obligatorio en el SDK, pero se ignora
    messages=[{"role": "user", "content": "Hola"}],
)
print(chat.choices[0].message.content)

API keys y alcances

Cada key hace solo lo que le diste. Una key puede tener varios alcances; conviene una por sistema y con el mínimo necesario, porque se revoca en el acto si se filtra.

AlcanceSirve para
llmPOST /chat/completions
sttPOST /audio/transcriptions
ttsPOST /audio/speech y GET /voices
callsLa API de llamadas, agentes y reportes (no usa los motores)
mcpEl servidor MCP, para conectar un agente de IA (ver /desarrolladores/docs/mcp); también la API de llamadas

Las dos APIs no se cruzan: una key llm no administra agentes ni llamadas, y una key calls no usa los motores.

Endpoints

EndpointAlcanceQué hace
POST /chat/completionsllmChat con el LLM, con o sin streaming
POST /audio/transcriptionssttTranscribe un archivo de audio
POST /audio/speechttsSintetiza texto con una de las voces
GET /voicesttsVoces disponibles
GET /modelscualquieraModelos que sirve la plataforma
GET /usagecualquieraTu consumo del mes contra el plan

LLM

Acepta messages, max_tokens, temperature, top_p, stop, seed, response_format, tools, tool_choice y stream. La respuesta trae usage con los tokens de entrada y salida, que es lo que se descuenta.

curl
curl https://app.atentina.com.ar/api/v1/inference/chat/completions \
  -H "Authorization: Bearer $ATENTINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"messages":[{"role":"user","content":"Hola, ¿qué horarios tienen?"}],"max_tokens":200}'

Transcripción

Subís el archivo (wav, mp3, flac, ogg…; hasta 25 MB) y, si querés, language. Se descuentan los segundos de audio que informa usage.

curl
curl https://app.atentina.com.ar/api/v1/inference/audio/transcriptions \
  -H "Authorization: Bearer $ATENTINA_KEY" \
  -F [email protected] -F language=es

Síntesis

voice es obligatoria y tiene que ser una de GET /voices (41 voces con nombres argentinos, como sofia o martin). input admite hasta 1.500 caracteres. Se descuentan los segundos de audio generado.

curl
curl https://app.atentina.com.ar/api/v1/inference/audio/speech \
  -H "Authorization: Bearer $ATENTINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"voice":"sofia","input":"Hola, ¿en qué te puedo ayudar?"}' \
  --output respuesta.wav

Streaming

El LLM y la síntesis se pueden consumir sin esperar a que termine la respuesta.

LLM: con "stream": true responde SSE como OpenAI: líneas data: {chunk}, un último chunk con usage y data: [DONE].

curl
curl -N https://app.atentina.com.ar/api/v1/inference/chat/completions \
  -H "Authorization: Bearer $ATENTINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"stream": true, "max_tokens": 200, "messages": [{"role": "user", "content": "Contame algo breve"}]}'

Síntesis: tres formas de recibir el audio.

PedidoQué llegaPara qué
response_format: "wav" (default)El archivo WAV completo, al terminarGuardar o reproducir un audio ya hecho
response_format: "pcm"Bytes PCM crudos (24 kHz, mono, 16 bits) a medida que se sintetizanReproducir en vivo o enviarlo a otro sistema
response_format: "pcm" + stream_format: "sse"Eventos SSE con el audio en base64Navegadores y clientes que ya consumen SSE
curl + ffplay
# Escuchar mientras se sintetiza (ffplay viene con ffmpeg)
curl -sN https://app.atentina.com.ar/api/v1/inference/audio/speech \
  -H "Authorization: Bearer $ATENTINA_KEY" -H "Content-Type: application/json" \
  -d '{"voice":"sofia","input":"Hola, ¿en qué te puedo ayudar?","response_format":"pcm"}' \
  | ffplay -f s16le -ar 24000 -ac 1 -nodisp -autoexit -

En una medición del 9 de octubre de 2026, el primer audio en pcm llegó a los 53 ms, contra 2,1 s del WAV completo; y el primer token del LLM con stream, a los 32 ms.

Un stream cuenta contra el cupo igual que un pedido normal. Si cortás antes de que termine, se cobra solo lo ya entregado.

Límites y consumo

Los fija tu plan y valen para todas las keys de tu cuenta juntas. Los cupos son por mes calendario (se renuevan el día 1 a las 00:00, hora de Argentina) y están separados por motor: agotar uno no afecta a los otros.

LímiteQué mide
api_llm_input_tokensTokens del prompt, por mes
api_llm_output_tokensTokens generados, por mes
api_tts_minutesAudio sintetizado, en minutos por mes
api_stt_minutesAudio transcripto, en minutos por mes
api_rate_limitPedidos por minuto, entre todas las keys
  • Antes de cada pedido se verifica la key, su alcance, el tope por minuto y el cupo. Si no pasa, el pedido no llega al motor ni se cobra.
  • Un pedido que falla en el motor no se cobra.
  • En el chat, max_tokens se achica a lo que te queda de tokens de salida del mes.
  • La plataforma guarda cuánto consumiste, por key y por día, pero no los prompts, los audios ni los textos.

Para ver tu consumo del mes contra el plan (no gasta cupo):

curl
curl https://app.atentina.com.ar/api/v1/inference/usage -H "Authorization: Bearer $ATENTINA_KEY"

Errores

El cuerpo siempre tiene la misma forma:

{"detail": "texto para mostrar", "code": "codigo_estable", "errors": []}
HTTPcodeCuándo
401Sin API key, inválida o revocada
403scope_missingLa key no tiene el alcance de ese motor
403not_in_planEl plan no incluye ese motor
429rate_limitedTe pasaste de los pedidos por minuto; trae Retry-After
429api_*Se agotó el cupo del mes de ese motor
400upstream_rejectedEl motor rechazó el pedido (prompt más largo que el contexto, audio ilegible)
400payload_too_largeAudio de más de 25 MB: partilo
422invalid_request / invalid_voicePedido mal armado, o voz inexistente
502upstream_errorEl motor no responde

Un 429 por cupo agotado no se arregla reintentando: hay que esperar al día 1 o ampliar el plan. Uno por rate_limited sí, después de los segundos de Retry-After.

Qué incluye cada plan

La API de llamadas es la misma en todos los planes; lo que cambia es qué funciones están habilitadas y con qué tope. Si el plan no incluye algo, el pedido no hace nada y responde 429 con un code que dice qué falta.

FunciónFreeMostradorSucursalRed
Llamadas salientes (POST /calls)NoSíSíSí
WhatsAppNoSíSíSí
Contactos activosNo5005.000Sin tope
Campañas de WhatsAppNoNoSíSí
Campañas de llamadasNoNoSí, de a 1 llamadaSí, hasta 3 a la vez
Equipo para derivar llamadasNo1 persona2 personasSin tope

Las llamadas salientes, las campañas de llamadas y las derivaciones usan los minutos salientes del plan y necesitan un número asignado a tu cuenta. Para saber qué tiene tu cuenta, con el motivo de lo que no:

curl
curl https://app.atentina.com.ar/api/v1/plan/features -H "Authorization: Bearer $ATENTINA_KEY"
Respuesta
{
  "client_id": "…", "tier_name": "Sucursal", "free": false,
  "features": {
    "outbound_calls": {"enabled": true, "reason": null},
    "campaigns_voice": {"enabled": true, "reason": null},
    "campaigns_whatsapp": {"enabled": true, "reason": null},
    "whatsapp": {"enabled": true, "reason": null},
    "contacts": {"enabled": true, "reason": null},
    "team": {"enabled": true, "reason": null},
    "call_links": {"enabled": true, "reason": null}
  }
}
429 codeCuándo
outbound_minutesSe acabaron los minutos salientes del mes (o el plan no tiene)
concurrency_limitYa tenés todas las llamadas a la vez que permite el plan
whatsapp_not_in_planEl plan no incluye WhatsApp
contacts_limitLa agenda está llena: archivá contactos para desarchivar otros. En una carga, los que no entran vuelven en invalid con el motivo
campaign_channel_not_in_planEl plan no incluye campañas por ese canal
campaign_concurrencyconcurrency pasa el tope del plan
campaign_contactsLa campaña tiene más contactos de los que permite el plan
campaigns_per_monthYa iniciaste todas las campañas del mes
campaign_followup_not_in_planEl plan no incluye el seguimiento por WhatsApp
team_members_limitEl equipo está completo, o el plan no incluye derivación

Agentes y llamadas

Con una key calls administrás agentes, lanzás y cortás llamadas, cargás contactos, armás campañas, configurás las derivaciones a tu equipo, leés los eventos y descargás reportes, con la URL base https://app.atentina.com.ar/api/v1. También se puede hacer desde tu agente de programación, con las skills para Claude Code, Cursor y Codex. La referencia completa de cada pedido y respuesta está en el panel, en /api/v1/docs.

Una llamada

El agente llama al número y la conversación queda en el panel. El teléfono va en formato internacional; from_number_id elige con qué número tuyo sale (sin él, el primero) y voice cambia la voz.

curl
curl https://app.atentina.com.ar/api/v1/calls \
  -H "Authorization: Bearer $ATENTINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"agent_id": "<id del agente>", "phone": "+5493515551234"}'

La respuesta trae el conversation_id. Con GET /calls/{conversation_id} ves el estado (pendiente, sonando, en_curso, finalizada, fallida), la duración, los datos que tomó el agente y el resultado. Para muchos números, usá una campaña.

Tu propio LLM

Por defecto, el agente piensa con nuestro modelo de lenguaje. Si querés otro, cargás la API key de tu proveedor y elegís desde qué turno de la conversación lo usa: por ejemplo, el nuestro para saludar y tomar los datos, y el tuyo desde el cuarto mensaje.

Proveedores

  • Compatible con OpenAI: OpenAI, Groq, Together, DeepSeek o cualquier API con el formato de OpenAI, con su URL (https y pública).
  • OpenRouter y Anthropic, con su URL fija.

Cómo se configura

  1. La conexión. En el panel, Configuración > Modelos > Nueva conexión: el proveedor, tu API key y el modelo. Al guardarla se prueba sola: autentica y mide cuánto tarda en responder. La key queda cifrada y no se vuelve a mostrar; se ven sus últimos 4 caracteres.
  2. El agente. En su editor, sección Modelo de lenguaje, o por API en su definición (PUT /agents/{id}/definition, que guarda una versión nueva): hasta 3 etapas, cada una desde un turno. La etapa sin connection es nuestro modelo. Un turno es cada mensaje de la persona; el saludo del agente no cuenta.
JSON
"llm": {
  "stages": [
    {"from_turn": 1},
    {"from_turn": 4, "connection": "<id de la conexión>", "model": "<modelo del proveedor>"}
  ],
  "fallback_local": true
}
  • Respaldo: con fallback_local, si tu proveedor falla o tarda más de 20 segundos, ese turno lo contesta nuestro modelo y la conversación sigue. Sin respaldo, el turno falla.
  • Costos: los tokens los pagás a tu proveedor. Los minutos del plan se cuentan igual.
  • Latencia: se suma lo que tarda tu proveedor en empezar a responder. En el detalle de cada conversación se ve qué proveedor y modelo contestó cada turno.
  • Privacidad: desde el turno en que lo usa, el texto de la conversación y las instrucciones del agente van a tu proveedor, bajo sus condiciones. El audio no sale: la voz y la transcripción siguen en nuestros servidores. Ver la política de privacidad.

La API de inferencia sigue siendo solo nuestra: tu proveedor lo usa el agente, no esa API.

Cortar y bloquear

Para terminar una llamada en curso desde tu sistema: cuelga a la otra punta y la llamada queda con ended_reason hangup_by_user. Si la llamada ya terminó, responde 409 call_not_active.

curl
curl -X POST https://app.atentina.com.ar/api/v1/calls/<conversation_id>/hangup -H "Authorization: Bearer $ATENTINA_KEY"
# {"status": "finalizada", "ended_reason": "hangup_by_user"}

Para no volver a atender un número, bloquealo: sus llamadas entrantes, por tu línea o por WhatsApp, se cortan sin atender y quedan como rechazada con ended_reason blocked_number. GET /blocked-numbers los lista y DELETE /blocked-numbers/{phone} desbloquea uno. Si ya estaba bloqueado, se actualiza la nota.

curl
curl https://app.atentina.com.ar/api/v1/blocked-numbers \
  -H "Authorization: Bearer $ATENTINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone": "+5493515551234", "note": "Llama para insultar"}'

Contactos

Cada cuenta tiene su agenda. El teléfono es lo único obligatorio y no se repite: cargar uno que ya está lo actualiza (los datos se suman, no se borran). El resto es opcional: name, email, external_id (el id en tu sistema, para cruzar el reporte), attributes (datos libres) y tags (para armar campañas por grupo).

curl
curl https://app.atentina.com.ar/api/v1/contacts \
  -H "Authorization: Bearer $ATENTINA_KEY" \
  -H "Content-Type: application/json" \
  -d '[{"phone": "351 555-1234", "name": "Ana Pérez", "external_id": "C-1032",
        "attributes": {"deuda": 15000, "vence": "10 de noviembre"}, "tags": ["mora_30"]},
       {"phone": "+54 9 11 5555-0000"}]'

Por CSV, con POST /contacts/import: una columna de teléfono (telefono, celular, phone…) y, si querés, nombre, email, id y etiquetas; el resto de las columnas pasan a attributes. La respuesta dice cuántos se crearon y actualizaron, y cuáles no entraron, con la fila y el motivo.

CSV
telefono,nombre,deuda,vence
351 555-1234,Ana Pérez,15000,10 de noviembre
11 5555-0000,,8200,15 de noviembre
  • No se borran: se archivan (POST /contacts/{id}/archive o en lote por ids o etiquetas). Un archivado no entra en campañas nuevas y sale de las que están en curso. GET /contacts?archived=true los lista. Si lo volvés a cargar, se actualiza pero sigue archivado hasta que lo desarchives.
  • No contactar: /contacts/optouts guarda los teléfonos que pidieron no ser contactados, por canal (voice, whatsapp o all). Ninguna campaña los llama ni les escribe. También se cargan solos: cuando alguien responde "baja" por WhatsApp, o lo pide en la llamada si el agente tiene un resultado marcado como "no contactar".

Campañas

Una campaña llama ("channel": "voice") o escribe por WhatsApp ("channel": "whatsapp", con una plantilla aprobada) a una lista de contactos, sola, dentro del horario que le pongas. Este ejemplo llama a los contactos con la etiqueta mora_30, de a tres a la vez, de lunes a viernes de 9 a 20, desde el 3 de noviembre, con hasta tres intentos cada dos horas:

curl
curl https://app.atentina.com.ar/api/v1/campaigns \
  -H "Authorization: Bearer $ATENTINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "voice",
    "name": "Mora 30 días - noviembre",
    "agent_id": "<id del agente>",
    "purpose": "cobranza",
    "tags": ["mora_30"],
    "concurrency": 3,
    "window_start": 9, "window_end": 20, "weekdays": [0, 1, 2, 3, 4],
    "starts_at": "2026-11-03T12:00:00Z",
    "max_attempts": 3, "retry_minutes": 120,
    "callback_url": "https://tu-sistema.com.ar/atentina/webhook",
    "start": true
  }'
  • De a uno o en paralelo: concurrency en 1 llama de a un contacto; con N, hasta N a la vez (el tope lo pone tu plan). rate_per_minute limita cuántas llamadas empiezan por minuto.
  • Programada: starts_at y ends_at (UTC), y el horario en la hora de Argentina con window_start, window_end y weekdays (0 es lunes).
  • Reintentos: max_attempts, retry_minutes y retry_on, los resultados que se reintentan (por defecto no atendió, ocupado, contestador y falla).
  • Datos del contacto: el saludo del agente puede usar {{nombre}} o cualquier atributo ({{deuda}}). Un atributo con el mismo nombre que un dato que el agente pide ya no se pregunta, y el resto lo recibe como contexto.
  • Finalidad: purpose es comercial, cobranza, turnos o aviso. Una comercial se inicia con "no_call_registry_checked": true: confirmás que filtraste la lista contra el Registro Nacional No Llame (Ley 26.951).
  • Seguimiento por WhatsApp: con followup (apagado por defecto), a quien no atendió después del último intento le llega una plantilla de WhatsApp; si responde, sigue la conversación con el agente.
EndpointQué hace
POST /campaignsCrea la campaña (en draft) con sus contactos; con "start": true la inicia en el mismo pedido
POST /campaigns/{id}/contactsSuma contactos: contact_ids, tags, contacts (lista) o csv
POST /campaigns/{id}/startLa inicia (o la programa, si tiene starts_at)
POST /campaigns/{id}/pause · /resume · /cancelLas llamadas en curso terminan normalmente
PATCH /campaigns/{id}Cambia horario, ritmo, reintentos o webhook de una campaña que no terminó
GET /campaignsLista, con filtros channel y status
GET /campaigns/{id}Estado y avance: lo que se consulta en el polling
GET /campaigns/{id}/contactsContactos paginados, con filtros status y result, y su conversation_id
GET /campaigns/{id}/statsIndicadores: tasa de contacto y de objetivo, duración media, atendidas por hora
GET /campaigns/{id}/report.csvReporte con una fila por contacto
GET /campaigns/planQué permite tu plan: canales, campañas por mes, contactos por campaña, llamadas a la vez

Estado y avance

Consultá GET /campaigns/{id} cada pocos segundos (el panel lo hace cada 5). progress cuenta los contactos por estado y results, por resultado de la llamada; goal son los que llegaron al objetivo del agente (por ejemplo, una promesa de pago), y eta_minutes estima cuánto falta al ritmo de los últimos 15 minutos. En WhatsApp, results trae enviados, entregados, leídos y respondidos.

curl
curl https://app.atentina.com.ar/api/v1/campaigns/<id> -H "Authorization: Bearer $ATENTINA_KEY"
Respuesta
{
  "id": "…", "channel": "voice", "status": "running", "status_reason": null, "in_window": true,
  "total": 500, "percent": 33.0, "eta_minutes": 95,
  "progress": {"pending": 310, "in_progress": 3, "retry": 22, "done": 140, "failed": 25, "skipped": 0},
  "results": {"answered": 120, "no_answer": 30, "busy": 8, "voicemail": 10,
              "invalid": 6, "rejected": 1, "failed": 0},
  "goal": 64, "minutes_used": 212.5
}
EstadoQué significa
draftCreada, sin iniciar. Podés sumar contactos y cambiar la configuración.
scheduledIniciada con fecha de inicio futura: arranca sola en starts_at.
runningLlamando o enviando, dentro del horario. Fuera de horario sigue running con in_window en false.
pausedPausada por vos, o sola: se acabaron los minutos del plan o fallaron muchas llamadas seguidas (status_reason dice por qué).
doneTerminó: no queda nadie por contactar, o pasó ends_at.
cancelledCancelada. Los contactos pendientes quedan en skipped.
ResultadoQué pasóSe reintenta
answeredAtendió y habló con el agenteNo
no_answerSonó y no atendióSí
busyOcupadoSí
voicemailAtendió un contestadorSí
invalidEl número no existeNo
rejectedRechazó la llamadaNo
failedFalló la llamada por otro motivoSí

Reporte

Una fila por contacto: external_id, teléfono, nombre, sus datos, estado, resultado, intentos, duración, resultado de la conversación, si llegó al objetivo, los datos que tomó el agente y el link a la conversación. Se abre en Excel con los acentos bien.

curl
curl https://app.atentina.com.ar/api/v1/campaigns/<id>/report.csv -H "Authorization: Bearer $ATENTINA_KEY" -o reporte.csv

Límites

Las campañas son parte del plan: qué canales, cuántas campañas por mes, cuántos contactos por campaña, cuántas llamadas a la vez y el tamaño de la agenda (GET /campaigns/plan). Las llamadas de una campaña usan los minutos salientes del plan; si se acaban, la campaña se pausa sola y la retomás con /resume cuando haya minutos.

Webhook

En vez de consultar el estado, podés recibirlo: con callback_url (https), la campaña hace un POST a tu sistema por cada contacto que termina (contact.finished, con la misma fila del reporte), por cada seguimiento por WhatsApp (contact.followup) y uno al terminar (campaign.finished, con el avance y los indicadores).

  • Firma: cada POST trae X-Atentina-Signature: sha256=<hex>, el HMAC-SHA256 del cuerpo con tu callback_secret. Si no mandás uno al crear la campaña, se genera y se muestra una sola vez en la respuesta.
  • Reintentos: se toma como recibido cualquier 2xx. Si no, se reintenta a los 30 s, 2 min, 10 min, 1 h y 6 h. delivery_id (también en X-Atentina-Delivery) es el mismo en los reintentos: usalo para no procesar dos veces el mismo evento.
Python
import hashlib, hmac

def firma_valida(cuerpo: bytes, header: str, secreto: str) -> bool:
    esperada = "sha256=" + hmac.new(secreto.encode(), cuerpo, hashlib.sha256).hexdigest()
    return hmac.compare_digest(esperada, header)

# header = request.headers["X-Atentina-Signature"]; cuerpo = el body crudo, antes de parsear el JSON

Equipo y derivaciones

El agente puede pasar una llamada a una persona de tu equipo. Mientras el cliente escucha música, llama a la persona, le cuenta en pocas palabras de qué se trata, y si marca 1 los conecta. La conversación con la persona también queda transcripta en el panel. Se arma en tres pasos.

1. Las personas. Cada una con su teléfono de Argentina, al que se la llama. Cuántas podés cargar depende del plan.

curl
curl https://app.atentina.com.ar/api/v1/team \
  -H "Authorization: Bearer $ATENTINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Laura Gómez", "phone": "351 555-4321"}'

2. Las áreas. Un área agrupa a las personas que atienden un tema. description es lo que lee el agente para elegirla. strategy en sequential las llama en el orden de member_ids, y en round_robin va rotando quién arranca. Cada una suena ring_seconds. Con schedule (hora de Argentina, franjas por día y feriados en closed), fuera de horario el agente no deriva y toma el mensaje; sin horario, atiende siempre.

curl
curl https://app.atentina.com.ar/api/v1/handoff-areas \
  -H "Authorization: Bearer $ATENTINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Ventas",
    "description": "Presupuestos, precios y compras",
    "member_ids": ["<id de Laura>", "<id de otra persona>"],
    "strategy": "sequential",
    "ring_seconds": 20,
    "schedule": {"week": {"mon": [["09:00", "13:00"], ["16:00", "20:00"]],
                          "tue": [["09:00", "18:00"]]},
                 "closed": ["2026-12-25"]}
  }'

3. El agente. En su definición (PUT /agents/{id}/definition, que guarda una versión nueva) van las áreas a las que puede derivar y, en when, cuándo hacerlo en palabras (vacío: la descripción del área). Por ahora solo con el motor classic.

JSON
"handoff": {
  "areas": [
    {"area_id": "<id del área Ventas>", "when": "Quiere un presupuesto o pide hablar con un vendedor"}
  ],
  "hold_music": {"enabled": true, "track": "default", "volume": 0.3}
}
  • Si nadie atiende (no atiende, da ocupado, salta un contestador o no marca 1), el agente vuelve con el cliente y le toma el mensaje.
  • Volver al agente: la persona marca return_digits (2 por defecto; nunca con *) y el agente retoma la llamada. Si la persona corta, on_person_hangup elige entre que el agente siga (resume) o se despida y corte (end).
  • Minutos: el tramo con la persona gasta minutos salientes desde que atiende; los intentos sin atender no se cobran. Sin minutos salientes, no se deriva. La llamada transferida ocupa una sola de las llamadas a la vez de tu plan.
  • Dónde no hay derivación: en las campañas y en los mensajes de WhatsApp. Sí en las entrantes por tu línea o por WhatsApp, y en las salientes que lanzás con POST /calls.
EndpointQué hace
GET · POST /teamLista o suma personas: name, phone (para transferirle llamadas) y active
PATCH · DELETE /team/{id}Cambia o saca a una persona
GET · POST /handoff-areasLista o crea áreas; cada una trae open_now y su horario en palabras (schedule_text)
GET · PATCH · DELETE /handoff-areas/{id}Un área. Si un agente la usa no se borra (409 area_in_use)
GET /handoff-areas/defaultsLos textos que dice el agente al derivar, para cambiarlos por área en messages
GET /hold-musicLos temas de espera para hold_music.track

Eventos

Cada llamada deja una línea de tiempo: cuándo sonó, cuándo la atendieron, cada intento de derivación y cómo terminó. GET /events la devuelve en orden de llegada, con filtros conversation_id, type (exacto o prefijo, como handoff.), since y until. Para leer solo lo nuevo, repetí el pedido con after_id igual al último id que recibiste (hasta 1.000 por pedido con limit).

curl
curl "https://app.atentina.com.ar/api/v1/events?type=handoff.&after_id=1520" -H "Authorization: Bearer $ATENTINA_KEY"
Respuesta
[
  {"id": 1521, "at": "2026-11-03T14:02:11Z", "type": "handoff.requested", "actor": "agent",
   "conversation_id": "…", "area_id": "…", "team_member_id": null, "data": {}},
  {"id": 1522, "at": "2026-11-03T14:02:12Z", "type": "handoff.dialing", "actor": "system",
   "conversation_id": "…", "area_id": "…", "team_member_id": "…", "data": {}},
  {"id": 1525, "at": "2026-11-03T14:02:31Z", "type": "handoff.connected", "actor": "person",
   "conversation_id": "…", "area_id": "…", "team_member_id": "…", "data": {}}
]
TipoQué pasó
call.ringing · call.answered · call.endedLa llamada suena, la atienden y termina (call.ended trae el estado final y el motivo)
handoff.requestedEl agente decidió pasar la llamada a un área
handoff.dialing · answered · confirmedSe llama a una persona, atiende y marca 1 para tomar la llamada
handoff.no_answer · busy · failed · machine · not_confirmedEse intento no salió: no atendió, ocupado, falla, contestador o no marcó 1
handoff.connected · endedLa persona y el cliente hablan; ended trae los segundos
handoff.returned_to_ai · person_hung_upLa persona devolvió la llamada al agente o cortó
handoff.unavailableNo se pudo derivar: fuera de horario, sin personas, sin minutos o sin canal (data trae el motivo)
hold.started · hold.endedEmpieza y termina la música de espera
Acceso

¿Algo no está claro? Escribinos

Pedí acceso al plan Free, o contanos qué te falta en la documentación y lo sumamos.

Dejanos un email o un teléfono y te contactamos.

¿Preferís otro medio?

O llamanos al 0800-220-1233: te atiende nuestro agente.

[email protected]