REST APIPOST

Webhooks

Receive signed HTTP notifications when your posts are published, scheduled or fail, and when an image finishes generating, then verify each payload signature.

POSThttps://post.adaptlypost.com/post/api/v1/webhooks

Register a webhook endpoint. Every webhook receives all events, signed with its secret.

API Key (Bearer token)
Permission:webhooks.manageRoles and permissions

Creating, updating, deleting and testing a webhook need webhooks.manage (Admin and Editor). Listing and reading webhooks need webhooks.read, which Contributor keys do not hold.

Body Parameters

ParameterTypeDescription
urlREQUIREDstringPublicly reachable http(s) endpoint that will receive event deliveries. Private hosts are rejected. Max 10 webhooks per workspace.

Events

Every registered webhook receives all of the following events. The event name is sent in the payload and in the x-adaptly-event header, so you can filter on your side.

EventDescription
post.publishedAll platforms of the post published successfully
post.partially_failedSome platforms published, others failed
post.failedPublishing failed on all platforms
post.scheduledA post was scheduled for a future date
account.unauthorizedA platform rejected an account's token; the account is unauthorized until it is reconnected
image.completedAn image started with POST /ai/images finished; data.image.imageUrl is ready to use
image.failedAn image started with POST /ai/images failed; data.image.error says why

Event payload

Every delivery is a JSON body with id, event, createdAt and data. data carries post, the full post in the same shape as GET /api/v1/social-posts/:id, for post.* events; account (id, platform, displayName, pageId for Facebook, status and the platform's reason) for account.unauthorized; and image (jobId, sessionId, prompt, then imageUrl and imageId, or error) for image.* events. A test delivery sends the event webhook.test with the webhook id and the list of events.

Verifying Signatures

Every delivery is signed with your webhook secret (whsec_...). Compute an HMAC-SHA256 over "{timestamp}.{rawBody}" and compare it to the x-adaptly-signature header before trusting the payload.

The Secret Is Shown Only Once

The signing secret (whsec_...) is returned only in the response to the create request, never in list, get, or update responses. Store it securely. If you lose it, delete the webhook and create a new one.
Verify a Delivery (Node.js)
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'];

Delivery and Retries

Your endpoint must respond with a 2xx status within 10 seconds. Failed deliveries are retried up to 5 times with exponential backoff. Deliveries can arrive more than once, make your handler idempotent.

Auto-Disable

After 20 consecutive failed deliveries the webhook is automatically disabled. Re-enable it via PATCH { "active": true } or from the dashboard.

Managing Webhooks

Webhooks are managed with the following endpoints, using the same API key authentication:

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 event

You can also manage webhooks visually from the dashboard, in the API Tokens page.

OpenAPI Specification

A machine-readable OpenAPI 3.0 spec of the entire REST API: including webhooks and the event payload schema, is publicly available. Import it into Make, n8n, or any OpenAPI-compatible tool to scaffold an integration.

curl https://post.adaptlypost.com/post/api/v1/openapi.json
Register a Webhook
curl --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"
}'
Send a Test Event
curl --request POST \
  --url https://post.adaptlypost.com/post/api/v1/webhooks/<webhook-id>/test \
  --header 'Authorization: Bearer <api-key>'
Example Event Delivery
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"
    }
  }
}
201
{
  "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
}