Saltar al contenido principal
Al enviar tareas de generación asíncrona como video / imagen / audio, puedes incluir una URL de devolución de llamada. Una vez que la tarea finaliza (con éxito o error), enviaremos activamente el resultado por POST a tu URL, para que no tengas que estar consultando constantemente.

Inicio rápido

Al enviar una tarea, añade un campo webhook al cuerpo de la solicitud:
curl -X POST https://tu-dominio-de-acceso/v1/images/generations \
  -H "Authorization: Bearer TU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-image-2",
    "prompt": "a red apple on a table",
    "size": "1024x1024",
    "webhook": "https://your-server.com"
  }'
Cuando la tarea termine, enviaremos una solicitud POST a tu URL + /callback.
Otros endpoints de tareas asíncronas (video, audio, etc.) funcionan igual: solo añade el campo webhook al cuerpo de la solicitud.

Reglas de la URL

El webhook que proporcionas es la URL base, a la que añadimos automáticamente /callback:
Tu webhookA dónde enviamos realmente el POST
https://your-server.comhttps://your-server.com/callback
https://your-server.com/apihttps://your-server.com/api/callback
https://your-server.com/api/https://your-server.com/api/callback
Por lo tanto, tu servidor necesita un endpoint que acepte POST .../callback.

Qué vas a recibir

El contenido enviado es exactamente igual al que devuelve el endpoint «Obtener estado de la tarea»: puedes procesarlo con la misma lógica de análisis.
{
  "id": "task_01KV7FXR8BEYS1BWHJCT3JMCJ5",
  "status": "completed",
  "progress": 100,
  "created": 1781589029,
  "completed": 1781589058,
  "actual_time": 29,
  "cost": 0.006,
  "credits_cost": 0.06,
  "result": {
    "images": [{ "url": ["https://.../result.png"], "expires_at": 1781675458 }]
  }
}
Para tareas de video el resultado está en result.videos, y para audio en result.audios.
Solo enviamos cuando una tarea alcanza un estado final (completed / failed); no enviamos mientras está en procesamiento.

Reintentos y deduplicación (importante)

  • Reintentos: Si tu servidor no devuelve 2xx en unos 10 segundos, o devuelve 5xx, reintentaremos automáticamente, hasta 3 veces, con intervalos de aproximadamente 10 s, 30 s y 60 s. Si las 3 fallan, desistimos (en unos 2 minutos).
  • Sin reintento: Si tu endpoint devuelve 4xx (se considera URL / solicitud incorrecta), desistimos de inmediato sin reintentar.
  • Deduplicación: Normalmente una tarea se envía solo una vez. Pero en casos extremos (p. ej., un reinicio de nuestro lado tras el envío pero antes de la confirmación) podrías recibir envíos duplicados. Asegúrate de deduplicar de forma idempotente por id (task_id) para evitar el procesamiento doble.
Recomendaciones para tu endpoint receptor:
1

Devuelve 2xx lo antes posible

Recibe y encola primero, luego procesa de forma asíncrona; no nos hagas esperar a que termines de procesar.
2

Deduplica por id

Usa id (task_id) como clave de idempotencia para evitar el procesamiento doble.
3

Configura y verifica la firma

En producción, verifica el origen de las solicitudes de devolución de llamada y rechaza las falsificadas.

Requisitos de la URL de devolución de llamada

Por seguridad, la URL de devolución de llamada debe cumplir:
RequisitoDescripción
Accesible públicamenteNo puede ser una dirección interna / local (p. ej. 127.0.0.1, 10.x, 192.168.x serán rechazadas)
Protocolohttp o https (https recomendado)
PuertoUsa puertos estándar (80 / 443); los puertos no estándar pueden bloquearse
DominioNo puede apuntar a nuestro propio dominio de servicio
Las URL que no cumplan estos requisitos se descartan (sin envío ni reintento).

Preguntas frecuentes

Revisa punto por punto:
  1. ¿La tarea realmente finalizó? Consulta los detalles de la tarea: ¿status es completed / failed (no se envía durante el procesamiento)?
  2. ¿Tu URL es accesible públicamente? ¿Podemos alcanzar tu /callback?
  3. ¿El puerto es estándar (80 / 443)? Los puertos no estándar pueden ser bloqueados por las políticas de seguridad.
  4. ¿Tu /callback devolvió 2xx a tiempo? Si devuelve 4xx, desistimos de inmediato.
  5. ¿Usas https? ¿El certificado es válido?
Algunos modelos producen varias imágenes a la vez, por lo que images[].url puede ser un array; simplemente trátalo como un array.
Si result incluye expires_at (marca de tiempo Unix), indica la hora de caducidad del enlace; transfiérelo/guárdalo a tiempo.
No. Solo enviamos una vez, cuando la tarea finalmente tiene éxito o falla.

Ejemplo mínimo de receptor

Python
from http.server import BaseHTTPRequestHandler, HTTPServer
import json

class H(BaseHTTPRequestHandler):
    def do_POST(self):
        n = int(self.headers.get("Content-Length") or 0)
        body = self.rfile.read(n)
        data = json.loads(body)
        print("Devolución de llamada de tarea recibida:", data["id"], data["status"])
        # TODO: deduplicar por id, verificar la firma y luego procesar
        self.send_response(200); self.end_headers()
        self.wfile.write(b'{"ok":true}')

HTTPServer(("0.0.0.0", 443), H).serve_forever()
Devuelve 200 lo antes posible y ejecuta tu lógica de procesamiento de forma asíncrona en segundo plano.