Skip to main content
WEBHOOK

Authorizations

Authorization
string
header
required

API key prefixed with jcr_. To obtain a key programmatically (no dashboard required), see POST /v1/authorize.

Body

application/json

The JSON body POSTed to your endpoint.

Test deliveries from POST /webhooks/{id}/test use this same envelope, but their data is a fixed {test: true, …} object rather than real event data, and their event is whichever event you subscribed to first. Branch on data.test before treating data as an event payload.

Deliveries are at-least-once. Dedup on id, which is stable across every attempt and every replay of an event. Do not use the X-Juicer-Delivery-Id header for this — it is regenerated on every attempt, so a receiver that processed a delivery but answered too slowly will see the retry under a different value. The same id also arrives at every webhook you have subscribed to that event, once per endpoint.

Failed deliveries are retried automatically. A non-2xx response, a timeout, or a connection error fails that attempt, and the next one is scheduled 30s, then 2m, 8m, 32m and 2h later — six attempts in all, spread over roughly 2h45m. Answer within 10 seconds — 30 for post.created, whose body carries a whole sync batch — and do your processing after responding. Every attempt is its own row in GET /webhooks/{webhook_id}/deliveries, carrying its own http_status and error_message, and attempt_number counts attempts within one delivery: a new pending row means the next attempt is queued, and failed on attempt 6 means Juicer has given up.

Once the retries are exhausted, replay the event yourself with POST /webhooks/{webhook_id}/deliveries/{id}/redeliver. That queues a fresh delivery of the same event under the same id, with its own full run of automatic retries.

Only a delivery that exhausts its retries counts against the webhook's health, so a single flaky endpoint burns one failure, not six. After 10 consecutive failed deliveries the webhook's status flips to disabled and deliveries stop — including redeliveries, which are accepted but never sent — until you re-enable it with PATCH /webhooks/{id} (status: active). Re-enabling resets consecutive_failures to 0, so a recovered endpoint starts over with the full budget. A successful delivery resets it too.

Verify authenticity by computing HMAC-SHA256 over the raw request body with your webhook's secret and comparing to X-Juicer-Signature (formatted sha256=<hex>). Parsing and re-serializing the JSON before hashing will not match.

id
integer
required

The webhook event's id, and your idempotency key. Retrievable via GET /webhook_events/{id}.

event
enum<string>
required

Also sent in the X-Juicer-Event header.

Available options:
post.created,
social_account.expiring,
social_account.expired
occurred_at
string<date-time>
required

When the event was recorded, in UTC.

data
object
required

Sent for post.created. One event covers a whole sync, so posts holds every post that sync newly created — a source's first sync commonly carries tens of posts. Posts the sync merely updated (refreshed engagement counts, edited captions) are not included.

Posts held for moderation are included; check moderation_status if you only want ones visible in the feed.

moderation_status is the state at ingestion, not a settled verdict. A post queued for AI moderation is delivered as moderated and is commonly approved a few seconds later; a human can approve or reject at any time after that. Neither sends another event — post.created is the only post event Juicer emits. A subscriber that keeps only public posts from this payload therefore drops posts that did become public, so re-read them through GET /feeds/{feed_id}/posts for the settled status.

Response

200

Return any 2xx to acknowledge. Any other response, a timeout, or a connection error fails the attempt and is retried up to 5 times with growing backoff; see WebhookDeliveryBody for the full contract.