Webhooks
Recibe notificaciones HTTP firmadas cuando tus publicaciones se publiquen, se programen o fallen, y cuando una imagen termine de generarse, y verifica cada firma.
https://post.adaptlypost.com/post/api/v1/webhooksRegistra un endpoint de webhook. Cada webhook recibe todos los eventos, firmados con su secreto.
Crear, actualizar, eliminar y probar un webhook necesita webhooks.manage (Administrador y Editor). Listar y leer webhooks necesita webhooks.read, que las claves de Colaborador no tienen.
Parámetros del cuerpo
| Parámetro | Tipo | Descripción |
|---|---|---|
urlOBLIGATORIO | string | Endpoint http(s) accesible públicamente que recibirá las entregas de eventos. Se rechazan hosts privados. Máx. 10 webhooks por espacio de trabajo. |
Eventos
Cada webhook registrado recibe todos los eventos siguientes. El nombre del evento se envía en el payload y en la cabecera x-adaptly-event, para que puedas filtrar en tu lado.
| Evento | Descripción |
|---|---|
post.published | Todas las plataformas de la publicación publicaron correctamente |
post.partially_failed | Algunas plataformas publicaron, otras fallaron |
post.failed | La publicación falló en todas las plataformas |
post.scheduled | Se programó una publicación para el futuro |
account.unauthorized | Una plataforma rechazó el token de una cuenta; la cuenta queda como unauthorized hasta que se reconecte |
image.completed | Una imagen iniciada con POST /ai/images terminó; data.image.imageUrl está lista para usar |
image.failed | Una imagen iniciada con POST /ai/images falló; data.image.error indica el motivo |
Contenido del evento
Cada entrega es un cuerpo JSON con id, event, createdAt y data. data lleva post, la publicación completa con la misma forma que GET /api/v1/social-posts/:id, en los eventos post.*; account (id, platform, displayName, pageId en Facebook, status y el motivo de la plataforma) en account.unauthorized; e image (jobId, sessionId, prompt y después imageUrl e imageId, o error) en los eventos image.*. Una entrega de prueba envía el evento webhook.test con el id del webhook y la lista de eventos.
Verificación de firmas
Cada entrega está firmada con el secreto de tu webhook (whsec_...). Calcula un HMAC-SHA256 sobre "{timestamp}.{rawBody}" y compáralo con la cabecera x-adaptly-signature antes de confiar en el payload.
El secreto se muestra solo una vez
El secreto de firma (whsec_...) se devuelve solo en la respuesta a la solicitud de creación, nunca en respuestas de lista, obtención o actualización. Guárdalo de forma segura. Si lo pierdes, elimina el webhook y crea uno nuevo.const crypto = require('crypto');
const expected =
'sha256=' +
crypto
.createHmac('sha256', webhookSecret)
.update(`${req.headers['x-adaptly-timestamp']}.${rawBody}`)
.digest('hex');
const isValid = expected === req.headers['x-adaptly-signature'];Entrega y reintentos
Tu endpoint debe responder con un estado 2xx en 10 segundos. Las entregas fallidas se reintentan hasta 5 veces con backoff exponencial. Las entregas pueden llegar más de una vez: haz tu handler idempotente. Una entrega a un host privado o interno se rechaza y queda registrada con un error.
Desactivación automática
Tras 20 entregas fallidas consecutivas, el webhook se desactiva automáticamente. Reactívalo con PATCH { "active": true } o desde el panel.Gestión de webhooks
Los webhooks se gestionan con los siguientes endpoints, con la misma autenticación por clave API:
GET /api/v1/webhooks # list registered webhooks
GET /api/v1/webhooks/:id # get one webhook
PATCH /api/v1/webhooks/:id # update url or active
DELETE /api/v1/webhooks/:id # delete a webhook
POST /api/v1/webhooks/:id/test # send a signed webhook.test eventTambién puedes gestionar los webhooks visualmente desde el panel, en la página API y agentes de IA.
Especificación OpenAPI
Hay disponible públicamente una especificación OpenAPI 3.0 legible por máquina de toda la API REST: incluidos los webhooks y el esquema del payload de eventos. Impórtala en Make, n8n o cualquier herramienta compatible con OpenAPI.
curl https://post.adaptlypost.com/post/api/v1/openapi.jsoncurl --request POST \
--url https://post.adaptlypost.com/post/api/v1/webhooks \
--header 'Authorization: Bearer <api-key>' \
--header 'Content-Type: application/json' \
--data '{
"url": "https://your-server.com/webhook"
}'curl --request POST \
--url https://post.adaptlypost.com/post/api/v1/webhooks/<webhook-id>/test \
--header 'Authorization: Bearer <api-key>'POST <your-url>
x-adaptly-event: post.published
x-adaptly-webhook-id: wh_abc123
x-adaptly-timestamp: 1784643600
x-adaptly-signature: sha256=3f5a...
{
"id": "deliver-webhook-...",
"event": "post.published",
"createdAt": "2026-07-21T14:20:00.000Z",
"data": {
"post": {
"id": "post_xyz789",
"status": "COMPLETED",
"recurringPostId": "rp_abc123",
"occurrenceAt": "2026-07-21T14:19:00.000Z",
"platforms": [
{
"platform": "TWITTER",
"status": "PUBLISHED",
"platformPostId": "1234567890",
"publishedAt": "2026-07-21T14:19:58.000Z"
}
]
}
}
}
POST <your-url>
x-adaptly-event: account.unauthorized
{
"id": "deliver-webhook-...",
"event": "account.unauthorized",
"createdAt": "2026-09-13T09:10:00.000Z",
"data": {
"account": {
"id": "conn_jkl012",
"platform": "FACEBOOK",
"displayName": "Your Page",
"pageId": "123456789012345",
"status": "unauthorized",
"reason": "Error validating access token: Sessions for the user are not allowed because the user is not a confirmed user."
}
}
}
POST <your-url>
x-adaptly-event: image.completed
{
"id": "deliver-webhook-...",
"event": "image.completed",
"createdAt": "2026-09-20T10:02:31.000Z",
"data": {
"image": {
"jobId": "generate-image-8f2a1c40-6d3b-4e91-b7c2-0a5d9e13f846",
"sessionId": "b31c7a44-2f19-4c85-9d60-7e2af4c81b93",
"prompt": "a red bicycle leaning against a yellow wall, soft morning light",
"imageUrl": "https://cdn.adaptlypost.com/ai-images/ai-studio-4c81b93.png",
"imageId": "d7e44a10-93bc-4f27-8ae5-1b06c2f4d938"
}
}
}{
"id": "wh_abc123",
"url": "https://your-server.com/webhook",
"active": true,
"secret": "whsec_c99807...",
"createdAt": "2026-07-21T14:00:00.000Z",
"updatedAt": "2026-07-21T14:00:00.000Z",
"lastSuccessAt": null,
"lastFailureAt": null,
"disabledAt": null
}