API de dungo (v1)

Download OpenAPI specification:

La API de dungo permite que tus propias aplicaciones —tu ERP, tu sitio, un script interno— lean y escriban los datos de tu cuenta: contactos, etiquetas y tratos.

Todo lo que hagas por la API se comporta igual que si lo hicieras desde la aplicación web: etiquetar un contacto o mover un trato dispara los mismos algoritmos, y cada cambio queda registrado en la auditoría de tu cuenta a nombre de la integración que lo hizo.


Autenticación

Cada llamada debe incluir tu API key en el header X-Api-Key:

X-Api-Key: dgk_7Fq2mZK9xR4tLbNc8vWe1A

La key identifica a tu cuenta: no necesitas enviar ningún otro identificador. Cada key solo ve y modifica los datos de la cuenta para la que fue creada.

Cómo obtener una API key

En la aplicación web de dungo, con un usuario SUPER: Ajustes → API → Crear API key. Ahí eliges el nombre, los permisos y la caducidad.

La key completa se muestra una sola vez, al crearla. Guárdala en un lugar seguro (un gestor de secretos o las variables de entorno de tu servidor). Si la pierdes, revócala y crea una nueva. Después de crearla, en el listado solo se ven sus primeros caracteres.

La API está pensada para llamarse desde tu servidor, nunca desde el navegador ni desde una app móvil: cualquiera que vea el código podría leer la key.

Caducidad y revocación

Al crear la key eliges si caduca en días, meses, años, o si no caduca. Una key expirada deja de autenticar por sí sola. Puedes revocar cualquier key desde la misma pantalla, y el corte es inmediato.


Permisos (scopes)

Cada key lleva la lista de permisos que le asignaste. read habilita las lecturas (GET) y write las escrituras (POST, PUT, DELETE) del recurso.

Scope Habilita
contactos:read Listar y consultar contactos, y ver sus etiquetas
contactos:write Crear, editar y eliminar contactos; etiquetar y desetiquetar
etiquetas:read Listar el catálogo de etiquetas
etiquetas:write Crear, editar y eliminar etiquetas
tratos:read Leer tableros, etapas, tratos y su historial
tratos:write Crear, mover y eliminar tratos

Si llamas a un endpoint sin el scope necesario, la respuesta es 403 con el código SCOPE_INSUFICIENTE. Da a cada integración solo los permisos que necesita.


Paginación

Los listados aceptan page (base 0) y size (20 por omisión) y responden con esta forma:

{
  "data": [ ... ],
  "page": 0,
  "size": 20,
  "total": 137,
  "totalPages": 7
}

Errores

Todos los errores comparten el mismo cuerpo:

{
  "code": "NOT_FOUND",
  "message": "Recurso no encontrado",
  "details": "Contacto no encontrado: 0198f1c2-...",
  "timestamp": "2026-08-03T14:22:31.482"
}
HTTP Código Qué pasó
400 VALIDATION_ERROR Faltan campos o un valor es inválido
401 API_KEY_AUSENTE No enviaste el header X-Api-Key
401 API_KEY_INVALIDA La key no existe o está mal formada
401 API_KEY_EXPIRADA La key pasó su fecha de caducidad
401 API_KEY_REVOCADA La key fue revocada desde la aplicación web
403 SCOPE_INSUFICIENTE La key no tiene el permiso para esa operación
404 NOT_FOUND El recurso no existe en tu cuenta
409 CONFLICT Choca con el estado actual: por ejemplo, un teléfono que ya tiene otro contacto (el detalle trae su contactoId)
429 TOO_MANY_REQUESTS Superaste el límite de solicitudes
500 INTERNAL_ERROR Error inesperado del servidor

Límite de uso

Hasta 120 solicitudes por minuto por API key. Al superarlo recibes 429; reintenta después de unos segundos. Si necesitas un volumen mayor, escríbenos.


Primeros pasos

Listar tus contactos:

curl -H "X-Api-Key: $DUNGO_API_KEY" \
  "https://dungo.ai/api/v1/contactos?size=5"

Crear un contacto:

curl -X POST "https://dungo.ai/api/v1/contactos" \
  -H "X-Api-Key: $DUNGO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"nombre":"Ana","apellido":"Perez","telefono":"56912345678","email":"ana@ejemplo.cl"}'

Etiquetar ese contacto:

curl -X POST "https://dungo.ai/api/v1/contactos/$CONTACTO_ID/etiquetas/$ETIQUETA_ID" \
  -H "X-Api-Key: $DUNGO_API_KEY"

Mover un trato de etapa (el etapaId sale de GET /api/v1/tableros/{id}, que devuelve el tablero con sus etapas en orden):

curl -X PUT "https://dungo.ai/api/v1/tratos/$TRATO_ID/etapa" \
  -H "X-Api-Key: $DUNGO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"etapaId":"0198f1c2-4a7b-7c3d-9e21-5f6a7b8c9d01"}'

Mover un trato a una etapa de tipo ganado o perdido es un movimiento más: se usa este mismo endpoint.


Receta: sincronizar un lead desde otro sistema

El caso más común: cuando tu sistema registra un lead, quieres encontrarlo en dungo por su teléfono y etiquetarlo; si no existe, crearlo y etiquetarlo igual.

Eso es una sola llamada: POST /api/v1/contactos/sincronizar. Necesitas una key con contactos:write y, para leer el catálogo de etiquetas la primera vez, etiquetas:read.

Una sola vez: obtén el etiquetaIdDun de la etiqueta que vas a aplicar con GET /api/v1/etiquetas y guárdalo en la configuración de tu integración.

Por cada lead:

curl -X POST "https://dungo.ai/api/v1/contactos/sincronizar" \
  -H "X-Api-Key: $DUNGO_API_KEY" -H "Content-Type: application/json" \
  -d '{
        "telefono": "+56 9 1234 5678",
        "nombre": "Ana",
        "apellido": "Perez",
        "email": "ana@ejemplo.cl",
        "empresa": "Ejemplo SpA",
        "etiquetaIds": ["0198f1c2-4a7b-7c3d-9e21-5f6a7b8c9d01"]
      }'

Respuesta:

{
  "contacto": { "contactoIdDun": "0198f...", "nombre": "Ana", "telefono": "+56 9 1234 5678",},
  "creado": true,
  "datosActualizados": false,
  "etiquetasAgregadas": ["0198f1c2-4a7b-7c3d-9e21-5f6a7b8c9d01"],
  "etiquetasYaPresentes": []
}

Qué te garantiza:

  • El formato del teléfono no importa. +56 9 1234 5678, 56912345678 y (56) 9 1234-5678 son el mismo contacto. Un teléfono identifica a un solo contacto dentro de tu cuenta.
  • Es idempotente. Repetir la llamada con el mismo lead no duplica nada: creado pasa a false y las etiquetas que ya tenía vuelven en etiquetasYaPresentes, sin error.
  • No hay duplicados por concurrencia. Todo ocurre en una transacción, así que dos ejecuciones simultáneas del mismo lead no pueden crear dos contactos. No necesitas serializar nada en tu lado.
  • No se pisan los datos que ya están en dungo. Si el contacto existe, sus datos quedan como están; envía "actualizarDatos": true si prefieres que los tuyos manden.

El tipo de contacto

tipoContactoId acepta CLIENTE, PROSPECTO, PROVEEDOR, COLABORADOR, EX_CLIENTE o DEFAULT, y se aplica solo al crear: los contactos nuevos nacen con el tipo que envíes (o DEFAULT si lo omites) y los que ya existen conservan el suyo, aunque mandes otro.

Es a propósito: si un CLIENTE vuelve a pasar por tu formulario de leads, no queremos degradarlo a PROSPECTO ni disparar los algoritmos de ese cambio sin que nadie lo pida. actualizarDatos: true tampoco toca el tipo. Para cambiarlo deliberadamente está PUT /api/v1/contactos/{id}/tipo/{tipoContactoId}.

nombre solo es obligatorio cuando el contacto hay que crearlo. El código de respuesta es 201 si nació en esa llamada y 200 si ya existía.

Etiquetar dispara el trigger ETIQUETA_AGREGADA del motor de algoritmos: puedes colgar de esa etiqueta una automatización en dungo —por ejemplo, que un agente de IA salude al lead— sin escribir nada más en tu integración.

Buscar por teléfono sin crear nada

Si solo quieres consultar, telefonoExacto compara ignorando el formato:

curl -H "X-Api-Key: $DUNGO_API_KEY" \
  "https://dungo.ai/api/v1/contactos?telefonoExacto=56912345678"

(El parámetro telefono, en cambio, busca por coincidencia parcial: 1234 también devuelve 991234567.)

Contactos

Alta, consulta y mantenimiento de contactos, y su etiquetado.

Obtener un contacto

Requiere el scope contactos:read.

Authorizations:
ApiKeyAuth
path Parameters
id
required
string <uuid>

Responses

Editar un contacto

Actualiza los campos enviados. Requiere el scope contactos:write.

Authorizations:
ApiKeyAuth
path Parameters
id
required
string <uuid>
Request Body schema: application/json
required
nombre
string
apellido
string
telefono
string
email
string
empresa
string
fechaNacimiento
string <date>
genero
string
tipoContactoId
string

Responses

Request samples

Content type
application/json
{
  • "nombre": "string",
  • "apellido": "string",
  • "telefono": "string",
  • "email": "string",
  • "empresa": "string",
  • "fechaNacimiento": "2019-08-24",
  • "genero": "string",
  • "tipoContactoId": "string"
}

Eliminar un contacto

Requiere el scope contactos:write.

Authorizations:
ApiKeyAuth
path Parameters
id
required
string <uuid>

Responses

Cambiar el tipo de un contacto

Punto unico de cambio de tipo: dispara los algoritmos asociados al cambio igual que la aplicacion web. Requiere el scope contactos:write.

Authorizations:
ApiKeyAuth
path Parameters
id
required
string <uuid>
tipoContactoId
required
string

Responses

Listar contactos

Devuelve los contactos del tenant paginados. Los filtros son opcionales y se combinan entre si (AND). etiquetaIds acepta varios valores repitiendo el parametro. nombre, telefono y email buscan por coincidencia PARCIAL (contiene). Para encontrar a alguien por su numero usa telefonoExacto, que compara ignorando el formato: +56 9 1234 5678 y 56912345678 son el mismo telefono. Requiere el scope contactos:read.

Authorizations:
ApiKeyAuth
query Parameters
nombre
string
telefono
string
telefonoExacto
string
email
string
etiquetaIds
Array of strings <uuid> [ items <uuid > ]
page
integer <int32>
Default: 0
size
integer <int32>
Default: 20

Responses

Crear un contacto

El unico campo obligatorio es nombre. El telefono es opcional, pero si viene debe tener entre 8 y 15 digitos y no puede estar tomado: un telefono identifica a un solo contacto dentro de tu cuenta, sin importar como se escriba, y un duplicado responde 409 con el id del contacto que ya lo tiene. Si estas sincronizando leads desde otro sistema, usa POST /api/v1/contactos/sincronizar, que resuelve buscar-o-crear en una sola llamada. Requiere el scope contactos:write.

Authorizations:
ApiKeyAuth
Request Body schema: application/json
required
nombre
string
apellido
string
email
string
telefono
string
empresa
string
fechaNacimiento
string <date>
genero
string
tipoContactoId
string

Responses

Request samples

Content type
application/json
{
  • "nombre": "string",
  • "apellido": "string",
  • "email": "string",
  • "telefono": "string",
  • "empresa": "string",
  • "fechaNacimiento": "2019-08-24",
  • "genero": "string",
  • "tipoContactoId": "string"
}

Etiquetar un contacto

Vincula una etiqueta existente al contacto y dispara el trigger ETIQUETA_AGREGADA de los algoritmos. NO es idempotente: si el contacto ya tiene esa etiqueta responde 400 VALIDATION_ERROR; tratalo como exito en tu integracion. Requiere el scope contactos:write.

Authorizations:
ApiKeyAuth
path Parameters
id
required
string <uuid>
etiquetaId
required
string <uuid>

Responses

Quitar una etiqueta de un contacto

Dispara el trigger ETIQUETA_QUITADA. Requiere el scope contactos:write.

Authorizations:
ApiKeyAuth
path Parameters
id
required
string <uuid>
etiquetaId
required
string <uuid>

Responses

Sincronizar un contacto (buscar o crear + etiquetar)

Pensado para sincronizar leads desde otro sistema en UNA sola llamada: busca el contacto por telefono —comparando sin importar el formato—, lo crea si no existe, y le aplica las etiquetaIds indicadas.

Es idempotente: repetir la misma llamada no duplica nada ni falla. Las etiquetas que el contacto ya tenia se devuelven en etiquetasYaPresentes en vez de dar error, y creado dice si el contacto se dio de alta ahora.

Todo ocurre en una transaccion, asi que dos llamadas simultaneas para el mismo lead no pueden crear dos contactos. nombre solo es obligatorio cuando hay que crearlo. Por omision NO se pisan los datos de un contacto que ya existe: para eso hay que enviar actualizarDatos: true.

tipoContactoId se aplica SOLO al crear. Un contacto que ya existe conserva su tipo aunque aqui se mande otro, para que un CLIENTE que vuelve a pasar por un formulario de leads no quede degradado a PROSPECTO. Para cambiarlo a proposito esta PUT /api/v1/contactos/{id}/tipo/{tipoContactoId}.

Requiere el scope contactos:write.

Authorizations:
ApiKeyAuth
Request Body schema: application/json
required
telefono
required
string

Teléfono del contacto; es la clave de la sincronización. Se compara ignorando el formato: +56 9 1234 5678 y 56912345678 son el mismo.

nombre
string

Obligatorio solo si el contacto no existe todavía.

apellido
string
email
string
empresa
string
tipoContactoId
string

Tipo de contacto del catálogo (CLIENTE, PROSPECTO, PROVEEDOR, COLABORADOR, EX_CLIENTE, DEFAULT). Se aplica SOLO al crear: un contacto que ya existe conserva el tipo que tenía aunque aquí se mande otro. Si se omite, los contactos nuevos quedan con DEFAULT.

etiquetaIds
Array of strings <uuid> [ items <uuid > ]

Etiquetas a aplicar. Las que el contacto ya tenga se informan aparte y no producen error.

actualizarDatos
boolean
Default: "false"

Si el contacto ya existe, sobrescribir nombre, apellido, email y empresa con los enviados. NO afecta al tipo de contacto, que nunca se pisa. Por omisión false: no se toca lo que ya está cargado en dungo.

Responses

Request samples

Content type
application/json
{
  • "telefono": 56912345678,
  • "nombre": "Ana",
  • "apellido": "string",
  • "email": "string",
  • "empresa": "string",
  • "tipoContactoId": "PROSPECTO",
  • "etiquetaIds": [
    ],
  • "actualizarDatos": "false"
}

Listar las etiquetas de un contacto

Requiere el scope contactos:read.

Authorizations:
ApiKeyAuth
path Parameters
id
required
string <uuid>

Responses

Tableros

Lectura de tableros, sus etapas y los tratos que contienen.

Listar tableros

Devuelve los tableros del tenant, paginados. Requiere el scope tratos:read.

Authorizations:
ApiKeyAuth
query Parameters
page
integer <int32>
Default: 0
size
integer <int32>
Default: 20

Responses

Obtener un tablero con sus etapas

Devuelve el tablero junto a sus etapas en orden. De aqui se obtiene el etapaId que se necesita para mover un trato. Requiere el scope tratos:read.

Authorizations:
ApiKeyAuth
path Parameters
id
required
string <uuid>

Responses

Listar los tratos de un tablero

Requiere el scope tratos:read.

Authorizations:
ApiKeyAuth
path Parameters
id
required
string <uuid>

Responses

Etiquetas

Catalogo de etiquetas con las que se clasifica a los contactos.

Editar una etiqueta

Requiere el scope etiquetas:write.

Authorizations:
ApiKeyAuth
path Parameters
id
required
string <uuid>
Request Body schema: application/json
required
nombre
string
color
string

Responses

Request samples

Content type
application/json
{
  • "nombre": "string",
  • "color": "string"
}

Eliminar una etiqueta

Elimina la etiqueta y sus vinculos con los contactos. Requiere el scope etiquetas:write.

Authorizations:
ApiKeyAuth
path Parameters
id
required
string <uuid>

Responses

Listar etiquetas

Devuelve el catalogo de etiquetas del tenant, paginado. Requiere el scope etiquetas:read.

Authorizations:
ApiKeyAuth
query Parameters
page
integer <int32>
Default: 0
size
integer <int32>
Default: 20

Responses

Crear una etiqueta

Requiere el scope etiquetas:write.

Authorizations:
ApiKeyAuth
Request Body schema: application/json
required
nombre
string
color
string

Responses

Request samples

Content type
application/json
{
  • "nombre": "string",
  • "color": "string"
}

Tratos

Creacion, movimiento entre etapas, eliminacion e historial de tratos.

Mover un trato de etapa

Mueve el trato a etapaId, que debe pertenecer a su mismo tablero. Registra el movimiento en el historial y dispara el trigger TRATO_MOVIDO. Si el trato ya esta en esa etapa, no hace nada y responde 200. Requiere el scope tratos:write.

Authorizations:
ApiKeyAuth
path Parameters
id
required
string <uuid>
Request Body schema: application/json
required
etapaId
string <uuid>

Responses

Request samples

Content type
application/json
{
  • "etapaId": "e91a9f4e-55dd-4516-a0cd-ab987d07fbbc"
}

Crear un trato

Crea el trato en la etapa indicada y dispara el trigger TRATO_CREADO. Requiere el scope tratos:write.

Authorizations:
ApiKeyAuth
Request Body schema: application/json
required
tableroId
string <uuid>
contactoId
string <uuid>
etapaId
string <uuid>
titulo
string

Responses

Request samples

Content type
application/json
{
  • "tableroId": "26f52223-d41c-4f15-ac8a-c33c393983b6",
  • "contactoId": "e5cf4809-41f1-443c-947e-5cb346946b8d",
  • "etapaId": "e91a9f4e-55dd-4516-a0cd-ab987d07fbbc",
  • "titulo": "string"
}

Historial de un trato

Devuelve los eventos CREADO / MOVIDO / ELIMINADO del trato en orden. Requiere el scope tratos:read.

Authorizations:
ApiKeyAuth
path Parameters
id
required
string <uuid>

Responses

Eliminar un trato

Dispara el trigger TRATO_ELIMINADO. Requiere el scope tratos:write.

Authorizations:
ApiKeyAuth
path Parameters
id
required
string <uuid>

Responses