Tutorial
¿No recibes los callbacks de HiAPI? Guía de diagnóstico y reparación de problemas de entrega de Webhooks
Al enviar una tarea a POST https://api.hiapi.ai/v1/tasks incluiste callback.url para que HiAPI te notifique mediante un POST cuando la tarea termine; sin embargo, aunque en GET /v1/tasks/{taskId} la tarea ya aparece como success, tu endpoint de callback no ha recibido ni una sola notificación. En el 80% de los casos el problema no está del lado de HiAPI, sino en si la dirección de callback es accesible, si el receptor devuelve 2xx según lo acordado, y en si el polling y el callback compiten entre sí. A continuación revisamos punto por punto según el contrato real de callbacks y damos pasos de reparación que puedes aplicar de inmediato.
Si tu tarea tarda mucho en alcanzar el estado terminal (con
data.statusestancado durante mucho tiempo enqueued/handling/archiving), ese es otro tipo de problema; consulta Guía de diagnóstico y reintento para tareas de HiAPI bloqueadas o con timeout. Este artículo solo trata el escenario en el que la tarea ya terminó pero no se recibe la notificación de callback.
Síntomas del problema
El patrón típico es que del lado de la tarea todo funciona normalmente: GET /v1/tasks/{taskId} devuelve data.status como success (o fail), pero en los logs de tu endpoint de callback desplegado en la red pública no hay ningún registro de POST proveniente de HiAPI; o bien lo recibes de forma ocasional y se pierde con frecuencia; o bien el mismo taskId se entrega dos veces y tu lógica de deduplicación lo trata como anomalía.
Primero establezcamos un hecho: el callback es solo una forma de obtener el resultado, no el único canal. Las tareas asíncronas de HiAPI siempre se pueden consultar de forma activa con GET /v1/tasks/{taskId} para obtener el estado terminal; el callback (Webhook) solo te ahorra el polling. Por lo tanto, "no recibo el callback" no equivale a "no puedo obtener el resultado"; primero confirma con polling que la tarea en sí está bien, y luego localiza el problema de entrega.
El contrato real del callback (primero alinéate, luego depura)
Al registrar el callback, callback es un campo de nivel superior de POST /v1/tasks, al mismo nivel que model e input, y no debe meterse dentro de input:
{
"model": "gpt-image-2",
"input": { "prompt": "a calm blue data center" },
"callback": {
"url": "https://your-domain.com/hiapi/callback",
"when": "final"
}
}
Puntos que debes recordar:
callback.urldebe ser una URL http(s) accesible desde la red pública; HiAPI inicia un POST hacia ella desde la red pública.callback.whenactualmente solo aceptafinal: se entrega una vez cuando la tarea alcanza el estado terminal, y tantosuccesscomofaillo activan. No hay callbacks por progreso o por etapas.- El método de entrega es
POST+Content-Type: application/json, y el cuerpo de la petición es idéntico al campodatadeGET /v1/tasks/{taskId}, es decir, el objeto de detalle de la tarea, que incluyestatus,output,error,taskId,created,completed, etc. - Cuando la entrega falla (no 2xx / timeout / error de red), se reintenta según inmediato → +1 minuto → +5 minutos → +20 minutos, hasta 4 veces; si los 4 intentos fallan, no se reintenta más y no afecta el estado de la tarea en sí.
- El callback de estado terminal tiene semántica at least once, no exactly once. El mismo
taskIdpuede entregarse varias veces, y el receptor debe deduplicar de forma idempotente portaskId.
Los detalles del contrato se rigen por la documentación oficial: Creación de tareas y campo callback, Descripción general de la API asíncrona (reintentos de callback e idempotencia).
Causas comunes (ordenadas por probabilidad de ocurrencia)
1. La dirección de callback no es accesible desde la red pública (la más común)
Se escribió como http://localhost:3000/..., una IP interna, o un dominio que solo resuelve en el DNS interno. HiAPI no puede alcanzar tu máquina local desde la red pública, por lo que naturalmente no puede entregar. Esto es lo más fácil de encontrar en desarrollo local.
2. El receptor no devuelve 2xx
El handler lanza una excepción, devuelve una redirección 3xx, es bloqueado por un middleware de autenticación con 401/403, o devuelve 5xx. HiAPI solo considera exitosa la entrega con HTTP 2xx; todo lo demás entra en reintento, y cuando la ventana de reintentos se agota, se manifiesta como "no recibido".
3. Timeout de procesamiento
El handler primero realiza tareas pesadas (descargar el producto, transferir a almacenamiento de objetos, escribir en la base de datos, enviar mensajes) y luego devuelve 200; tarda demasiado y se considera timeout, lo que activa reintentos, y con acumulación de veces parece que se están perdiendo paquetes.
4. Colocar callback dentro de input
Es un campo de nivel superior; si se coloca dentro de input no se reconoce como registro de callback; además, el input de cada modelo tiene un esquema estricto, y agregar claves desconocidas normalmente provoca un error síncrono al enviar: invalid input: <root>: additional properties 'callback' not allowed, por lo que la tarea ni siquiera se envía correctamente.
5. when tiene un valor distinto de final
Actualmente solo se admite final; cualquier otro valor se rechaza.
6. Competencia entre polling y callback
Se habilitó tanto el polling como la espera del callback; el polling obtuvo primero el estado terminal y cerró el registro local de la tarea, y cuando llega el callback no encuentra el registro correspondiente y se interpreta como "no recibido".
7. Bloqueo por certificado o WAF
La dirección de callback usa un certificado autofirmado o caducado, o el WAF bloquea el POST anónimo que no lleva un UA convencional ni tus cabeceras de autenticación propias, tratándolo como tráfico sospechoso.
Pasos de reparación
- Primero confirma que la tarea en sí está bien. Usa
GET /v1/tasks/{taskId}para consultardata.status; si essuccess/fail, la tarea ya está en estado terminal y el problema está en la entrega; si sigue en un estado no terminal, es otro tipo de problema (ver el diagnóstico de bloqueo al inicio). - Cambia la dirección de callback por un dominio https público. Desde una máquina externa ejecuta
curl -i -X POST https://your-domain.com/hiapi/callback -d '{}'para confirmar que se conecta y devuelve 2xx. Para depurar en localhost, usa un túnel público para mapear el puerto local hacia la red pública. - Primero registra el callback de forma fiable y luego devuelve 2xx rápidamente. Al recibir la petición, primero escribe el contenido del callback en la base de datos o en una cola persistente (este paso debe ser rápido); después de escribirlo correctamente, devuelve 2xx; las tareas pesadas como descargas y transferencias déjalas para hacerlas de forma asíncrona en segundo plano. No devuelvas 200 antes de persistir: si el proceso se cae después, este callback se pierde, y tras recibir 2xx HiAPI no volverá a entregarlo. Al mismo tiempo, exime esa ruta del middleware de autenticación, porque los callbacks de HiAPI no llevan tus cabeceras de autenticación propias.
- Verifica la ubicación y los valores de
callback. Confirma que está en el nivel superior del cuerpo de la petición (al mismo nivel quemodeleinput), queurles http(s) público y quewhenesfinal. - Realiza deduplicación idempotente por
taskId. Al recibir el callback, primero consulta localmente si ya se procesó estetaskId; si no se ha procesado, entonces persiste / activa las acciones posteriores; bajo la semántica at-least-once, las llegadas duplicadas son normales, no descartes duplicados como errores. - Añade un polling de respaldo. Para los
taskIdque superen el tiempo esperado sin haber recibido callback, consulta activamenteGET /v1/tasks/{taskId}para obtener el estado terminal. El callback es una optimización, el polling es la línea base; ambos deben cerrarse con la misma lógica de idempotencia. - Transfiere los productos lo antes posible. El
output[].urldel cuerpo del callback es un enlace temporal (storageestemp,expireAtes una marca de tiempo Unix, y caduca en aproximadamente 7 días); en cuanto lo obtengas, descárgalo o transfiérelo inmediatamente a tu propio almacenamiento; no guardes directamente el hotlink.
Ejemplo mínimo de verificación
Primero, sin tocar el callback, demuestra con "envío + polling" que tu key y la integración en sí funcionan (usa el id de modelo sin formato):
# 1. Enviar tarea
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-image-2","input":{"prompt":"a calm blue data center"}}'
# → {"code":200,"message":"success","data":{"taskId":"tk-hiapi-..."}}
# 2. Usar el taskId devuelto para consultar el estado terminal
curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-xxxx \
-H "Authorization: Bearer $HIAPI_API_KEY"
# → data.status pasa de queued/handling/archiving a success (con output) o fail (con error)
Si al enviar devuelve HTTP 401 + {"error":{"code":"permission_denied","type":"hiapi_error","request_id":"..."}}, significa que la API key es incorrecta; verifícala en la consola y vuelve a intentarlo. Después de confirmar que la integración no tiene problemas, añade el callback de nivel superior al cuerpo del POST y verifica el receptor punto por punto según los pasos de reparación anteriores.
FAQ
No dejo de recibir el callback, pero la tarea en realidad se completó con éxito, ¿aún puedo obtener el resultado?
Sí. En cualquier momento puedes usar GET /v1/tasks/{taskId} para leer data.status y data.output; la pérdida del callback no afecta tu capacidad de obtener el resultado activamente.
¿Cuántas veces se reintenta el callback y con qué intervalo?
En caso de no 2xx / timeout / error de red, se reintenta según inmediato → +1 minuto → +5 minutos → +20 minutos, hasta 4 veces. Si los 4 intentos fallan, no se reintenta más y no afecta el estado success/fail de la tarea en sí.
¿Es normal recibir dos callbacks del mismo taskId?
Sí. El callback de estado terminal tiene semántica "at least once"; basta con deduplicar de forma idempotente por taskId, no trates la entrega duplicada como error.
¿Cómo recibo callbacks en desarrollo local?
HiAPI no puede alcanzar localhost ni direcciones de red interna. O usas un túnel público para mapear el puerto local hacia la red pública, o en la etapa local cambias directamente a polling y pasas a callback al poner en producción.
¿Qué campos hay en el cuerpo de la petición de callback?
Es idéntico a data de GET /v1/tasks/{taskId}, e incluye taskId, status, output (array de productos cuando tiene éxito), error (con code y message cuando falla), created, completed, etc. Para más detalles, consulta la documentación de detalle de tarea.
¿Se cobra de más si no recibo el callback? No. El fallo de la tarea en sí no genera cobro, y la cuota reservada se devuelve; el éxito o fracaso de la entrega del callback tampoco cambia el resultado de facturación de la tarea; la facturación solo se calcula según si la tarea finalmente se genera con éxito.
Artículos relacionados
Ver todos los artículos →-
Tutorial
Cómo llamar a hailuo-2.3-fast/image-to-video a través de la API de HiAPI: práctica con curl y Python
La primera solicitud ejecutable del nivel rápido y de bajo coste de imagen a vídeo: del ID del modelo a la descarga del MP4
-
Tutorial
Cómo llamar a hailuo-2.3/text-to-video mediante la API de HiAPI: curl, Python y ejemplos de solicitud ejecutables
-
Tutorial
Uso de Kandinsky 6.0 Pro texto a video en HiAPI: tutorial con curl y Python
Demostración práctica centrada en la solicitud: construir la llamada de prompt a clip de video, enviar la solicitud, sondear el estado de finalización y descargar el resultado