Regla 1 — La conexión es SIEMPRE por dominio:
coolify.pampl.ingUsa
https://coolify.pampl.ing/api/v1como base. No uses la IP ni el puerto (192.168.1.10:8000). Motivos:
- La IP puede cambiar; el dominio no.
- Desde fuera de la red local la IP no es alcanzable, el dominio sí.
- El acceso por dominio pasa por Traefik con SSL válido.
Guía práctica para el equipo de desarrollo. Documenta lo que puedes hacer con el token de desarrollo, lo que no, y a quién acudir cuando necesites más.
https://coolify.pampl.inghttps://coolify.pampl.ing/api/v1El token del equipo de desarrollo tiene tres permisos: read, write y deploy.
Con eso puedes gestionar el ciclo completo de una aplicación: crearla, configurarla, desplegarla y diagnosticarla. Lo que no incluye es el permiso read:sensitive, que es el que da acceso a secretos y claves privadas.
Caduca en 1 año. Cuando expire, las llamadas empezarán a devolver 401 sin previo aviso. Pídele a José uno nuevo antes de que eso pase.
.md, ni en un comando que quede en el historial. Guárdalo en una variable de entorno (COOLIFY_TOKEN).curl -H "Authorization: Bearer $COOLIFY_TOKEN" \
-H "Accept: application/json" \
https://coolify.pampl.ing/api/v1/version
Comprobar que sigue siendo válido:
curl -s -o /dev/null -w "%{http_code}\n" \
-H "Authorization: Bearer $COOLIFY_TOKEN" \
https://coolify.pampl.ing/api/v1/teams/current
200 = válido · 401 = caducado o revocado (contacta con José).
Estas son las trampas que hacen perder tiempo de verdad. Todas verificadas contra nuestro servidor.
La ruta "REST canónica" no existe y devuelve 404. Coolify invierte el orden:
| Lo que se intenta primero | Lo correcto |
|---|---|
❌ GET /applications/{uuid}/deployments → 404 |
✅ GET /deployments/applications/{uuid} |
Este 404 ha llevado a concluir, erróneamente, que "la API no permite ver los logs de despliegue". Sí los permite.
Ojo también con GET /deployments (a secas): devuelve solo los despliegues en curso, así que casi siempre responde []. No es el histórico.
logs es un JSON dentro de un JSONLa respuesta trae logs como string que a su vez contiene JSON. Hay que parsearlo dos veces. Con jq, mediante fromjson:
# MAL: no devuelve nada útil
... | jq '.logs[]'
# BIEN
... | jq -r '.logs | fromjson | .[] | .output'
Cada entrada tiene: output, type (stdout/stderr), timestamp, command, order.
⚠️ Ese
outputpuede contener la clave SSH privada del host en claro (base64). Ver Aviso de seguridad: logs de deployment de Coolify antes de compartir o pegar cualquier log de deployment fuera del equipo de sistemas.
Esto es lo más contraintuitivo del token de desarrollo:
value y real_value vienen ocultosPara configurar una aplicación nueva no es un problema: escribes los valores que necesitas. Si necesitas consultar un valor ya configurado, míralo en la interfaz web de Coolify, donde sí se ven.
Son tipos de recurso distintos con endpoints distintos. Un UUID de aplicación no funciona en /databases/{uuid} ni al revés.
En nuestra instancia GET /services devuelve []: todo está registrado como application o database. Si buscas algo en /services y no aparece, prueba en /applications.
Los contenedores se llaman {uuid}-{timestamp}. El UUID de la app es la parte anterior al guion, pero en compose multi-contenedor puede no coincidir con el recurso que buscas. Ante la duda, localízalo por nombre en GET /applications.
docker a mano se pierde en el próximo deployLos contenedores se recrean en cada despliegue. Cualquier cambio aplicado directamente con docker (ficheros, límites, renombrados) desaparece. Lo que deba persistir se configura en Coolify (Storages, Environment Variables, Resource Limits), que sí se reaplica en cada deploy.
curl -s -H "Authorization: Bearer $COOLIFY_TOKEN" \
https://coolify.pampl.ing/api/v1/applications \
| jq -r '.[] | "\(.name)\t\(.uuid)\t\(.status)"' | grep -i "NOMBRE"
curl -s -H "Authorization: Bearer $COOLIFY_TOKEN" \
"https://coolify.pampl.ing/api/v1/applications/APP_UUID" \
| jq '{name, status, fqdn, git_repository, git_branch}'
curl -s -H "Authorization: Bearer $COOLIFY_TOKEN" \
"https://coolify.pampl.ing/api/v1/deploy?uuid=APP_UUID"
# Forzando rebuild sin caché (útil si sospechas de la caché de Docker)
curl -s -H "Authorization: Bearer $COOLIFY_TOKEN" \
"https://coolify.pampl.ing/api/v1/deploy?uuid=APP_UUID&force=true"
Devuelve el deployment_uuid con el que puedes seguir el progreso.
curl -s -H "Authorization: Bearer $COOLIFY_TOKEN" \
"https://coolify.pampl.ing/api/v1/deployments/applications/APP_UUID" \
| jq -r '.deployments[] | "\(.status)\t\(.deployment_uuid)\t\(.created_at)"' | head -20
curl -s -H "Authorization: Bearer $COOLIFY_TOKEN" \
"https://coolify.pampl.ing/api/v1/deployments/DEPLOYMENT_UUID" \
| jq -r '.logs | fromjson | .[] | "[\(.type)] \(.output)"'
Recuerda: ese log puede contener la clave SSH root del servidor en claro (ver §2.2). No lo pegues fuera del equipo de sistemas sin filtrarlo.
curl -s -H "Authorization: Bearer $COOLIFY_TOKEN" \
"https://coolify.pampl.ing/api/v1/deployments/DEPLOYMENT_UUID" \
| jq -r '.logs | fromjson | .[] | select(.type=="stderr") | .output'
APP=APP_UUID
DEP=$(curl -s -H "Authorization: Bearer $COOLIFY_TOKEN" \
"https://coolify.pampl.ing/api/v1/deployments/applications/$APP" \
| jq -r '[.deployments[] | select(.status=="failed")][0].deployment_uuid')
echo "Deployment fallido: $DEP"
curl -s -H "Authorization: Bearer $COOLIFY_TOKEN" \
"https://coolify.pampl.ing/api/v1/deployments/$DEP" \
| jq -r '.logs | fromjson | .[] | select(.type=="stderr") | .output'
curl -s -H "Authorization: Bearer $COOLIFY_TOKEN" \
"https://coolify.pampl.ing/api/v1/applications/APP_UUID/logs" | jq -r '.logs'
Si la aplicación no está corriendo, este endpoint devuelve 400 Application is not running. Para logs de un contenedor que ya no existe, ver el archivador de logs del servidor.
curl -s -X POST -H "Authorization: Bearer $COOLIFY_TOKEN" \
"https://coolify.pampl.ing/api/v1/applications/APP_UUID/restart"
curl -s -H "Authorization: Bearer $COOLIFY_TOKEN" \
"https://coolify.pampl.ing/api/v1/applications/APP_UUID/envs" \
| jq -r '.[] | .key'
Recuerda: verás los nombres, no los valores (ver regla 2.3).
curl -s -X POST -H "Authorization: Bearer $COOLIFY_TOKEN" \
-H "Content-Type: application/json" \
"https://coolify.pampl.ing/api/v1/applications/APP_UUID/envs" \
-d '{"key":"MI_VARIABLE","value":"mi-valor","is_preview":false}'
curl -s -X PATCH -H "Authorization: Bearer $COOLIFY_TOKEN" \
-H "Content-Type: application/json" \
"https://coolify.pampl.ing/api/v1/applications/APP_UUID/envs/bulk" \
-d '{"data":[{"key":"VAR_A","value":"valor-a"},{"key":"VAR_B","value":"valor-b"}]}'
curl -s -H "Authorization: Bearer $COOLIFY_TOKEN" \
"https://coolify.pampl.ing/api/v1/applications/APP_UUID/storages" \
| jq -r '.persistent_storages[] | "\(.host_path // .name) → \(.mount_path)"'
read — consultar| Puedes | Endpoint |
|---|---|
| Listar y ver aplicaciones | GET /applications, GET /applications/{uuid} |
| Ver logs de runtime | GET /applications/{uuid}/logs |
| Ver histórico y logs de despliegues | GET /deployments/applications/{uuid}, GET /deployments/{uuid} |
| Listar nombres de variables de entorno | GET /applications/{uuid}/envs |
| Ver volúmenes y tareas programadas | GET /applications/{uuid}/storages, /scheduled-tasks |
| Listar bases de datos | GET /databases, GET /databases/{uuid} |
| Ver proyectos y entornos | GET /projects, GET /projects/{uuid} |
| Ver el equipo | GET /teams/current, GET /teams/current/members |
| Inventario de recursos | GET /resources |
| Versión y salud | GET /version, GET /health |
write — crear y configurar| Puedes | Endpoint |
|---|---|
| Crear aplicaciones | POST /applications/public, /private-deploy-key, /dockerfile, /dockerimage |
| Modificar configuración | PATCH /applications/{uuid} |
| Gestionar variables de entorno | POST/PATCH/DELETE sobre /applications/{uuid}/envs |
| Gestionar volúmenes persistentes | POST/PATCH/DELETE sobre /applications/{uuid}/storages |
| Gestionar tareas programadas | POST/PATCH/DELETE sobre /applications/{uuid}/scheduled-tasks |
| Crear bases de datos | POST /databases/postgresql, /mysql, /redis, /mongodb… |
| Crear proyectos y entornos | POST /projects, POST /projects/{uuid}/environments |
deploy — controlar el ciclo de vida| Puedes | Endpoint |
|---|---|
| Desplegar | GET/POST /deploy?uuid=APP_UUID |
| Arrancar | POST /applications/{uuid}/start |
| Reiniciar | POST /applications/{uuid}/restart |
| Parar | POST /applications/{uuid}/stop |
| Cancelar un despliegue en curso | POST /deployments/{uuid}/cancel |
403 o campos ocultos)| No puedes | Por qué |
|---|---|
Leer claves SSH privadas (/security/keys muestra la lista, pero sin el campo private_key) |
Requiere read:sensitive. Esas claves dan acceso root al servidor |
| Leer valores de variables de entorno | Requiere read:sensitive (son secretos) |
Ver dockerfile / docker_compose de una app |
Requiere read:sensitive |
| Ver los webhook secrets de Bitbucket/GitHub | Requiere read:sensitive |
| Ver contraseñas de basic auth | Requiere read:sensitive |
⚠️ Importante: esta restricción de
read:sensitivees la única barrera real que impide hoy que la clave SSH root del servidor circule libremente, porque los logs de deployment (no las claves listadas arriba) exponen esa misma clave en claro — ver Aviso de seguridad: logs de deployment de Coolify. No pidas ese permiso para tokens de desarrollo pensando que solo amplía lo que ya puedes ver: ampliaría exactamente esa fuga.
El permiso write incluye operaciones irreversibles. El token no distingue entre "tu app" y "la app de otro equipo":
DELETE /applications/{uuid} → elimina una aplicaciónDELETE /databases/{uuid} → elimina una base de datos con sus datosDELETE /projects/{uuid} → elimina un proyecto enteroPOST /applications/{uuid}/stop → para una aplicación en producciónAntes de un DELETE o un stop, verifica el UUID con un GET y comprueba que es lo que crees:
curl -s -H "Authorization: Bearer $COOLIFY_TOKEN" \
"https://coolify.pampl.ing/api/v1/applications/APP_UUID" | jq '{name, fqdn}'
Y no pares aplicaciones en horario laboral sin avisar a quien las use.
Escribe a jose.moya@pampling.com cuando necesites:
403 Missing required permissions en algo que consideres legítimo para tu trabajoMejor preguntar que romper algo en producción.
| Código | Respuesta | Causa |
|---|---|---|
401 |
Unauthenticated |
Token ausente, mal escrito, caducado o revocado |
403 |
Missing required permissions: X |
Tu token no tiene ese permiso → contacta con José |
403 |
You are not allowed to access the API |
Restricción de acceso a la API → contacta con José |
404 |
{"message":"Not found."} |
La ruta no existe. Revisa el orden del path (ver regla 2.1) |
404 |
{"message":"No resources found."} |
La ruta es correcta, pero el UUID no corresponde a ningún recurso |
422 |
{"message":"Validation failed.","errors":{...}} |
Faltan campos obligatorios. El campo errors dice cuáles |
Distinguir los dos 404 es clave: "Not found." = esa URL no existe · "No resources found." = la URL está bien, el recurso no.
En scripts y automatizaciones
curl --max-time 15Si usas Claude Code u otro agente de IA
404, antes de concluir que la función no existe, consulta la regla 2.1 de esta guía: el orden del path de deployments está invertido respecto a lo esperable403, es falta de permisos: no intentes rodearlo, contacta con JoséDELETE, stop o cualquier operación sobre recursos que no sean los tuyoscurl por comando; no encadenes varias operaciones de escritura en una sola líneaNunca