Social guides
Guides

Webhooks.

Keep your backend in step with your communities without polling: every event is recorded the moment it happens, sent to your endpoint, signed, and retried for about 32 hours if your server is down.

You'll need a workspace with Social enabled and a workspace key (pcft_live_…) whose policy allows social.read and social.update. Delivery is at-least-once: dedupe on the event id (also sent as webhook-id).


1

One endpoint for the workspace, or one per community

The routes are identical; only the path decides which events an endpoint receives.

  • /v1/workspaces/:workspace_id/webhooks/…: the endpoint receives the events of every community in the workspace, including communities you create later. Use this when you run many communities and want one receiver.
  • /v1/communities/:community_id/webhooks/…: the endpoint receives that community's events only. The same endpoints are also at /v1/workspaces/:workspace_id/communities/:community_id/webhooks/…, which the console uses.

An event goes to each subscribed endpoint once, whichever scope it is in. Every body carries community_id and workspace_id, so a workspace endpoint can tell the communities apart. Up to 10 endpoints per workspace and 10 per community.


2

Add an endpoint

From the console (Social → Webhooks, or a community's Webhooks tab) or the API. Endpoints, signatures, tests, retries and replay work the same in every ProductCraft product.

POST /v1/workspaces/:workspace_id/webhooks/endpoints
curl -X POST "https://api.social.productcraft.co/v1/workspaces/<workspace_id>/webhooks/endpoints" \
  -H "Authorization: Bearer pcft_live_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: social-hook-1" \
  -d '{
    "url": "https://hooks.acme.com/webhooks/social",
    "event_types": ["post.created", "post.deleted", "comment.created"]
  }'

The answer carries the whsec_… signing secret exactly once, and its scope_type / scope_id say which scope it is in. Unknown event types are refused with a 400, so a typo cannot register silently. Verifying the signature, Send test, automatic retries, auto-disable, the event log, replay and secret rotation are in the Webhooks guide, with Node code you can paste.


3

What a Social event looks like

Signed with Standard Webhooks like every ProductCraft event; community_id and workspace_id are on every body.

post.created
{
  "id":           "evt_83cfc794-78e9-4102-96de-3a9a6f5bc31e",
  "type":         "post.created",
  "created_at":   "2026-10-17T10:00:00.000Z",
  "community_id": "6f1d2c3b-4a5e-4f60-8b7c-9d0e1f2a3b4c",
  "workspace_id": "24fab978-7cd7-48ae-bce1-aba876b11957",
  "data": {
    "community_id": "6f1d2c3b-4a5e-4f60-8b7c-9d0e1f2a3b4c",
    "post_id":      "2d3e4f50-6172-4c8d-ae9f-0a1b2c3d4e5f",
    "actor_id":     "0b1c2d3e-4f50-4a6b-8c7d-8e9f0a1b2c3d",
    "body":         "Shipping the new onboarding today.",
    "status":       "published",
    ...
  }
}

4

Event catalog

Twenty-one event types (live list with descriptions and examples: GET …/webhooks/event-types). New types never reach an endpoint that lists its events until you add them; an endpoint with an empty list gets them automatically.

Event typeFires when
post.createdA post (text / link / image / quote / story) was created. Includes drafts and scheduled posts — check data.status.
post.updatedA post body / title / attributes was edited.
post.deletedA post was soft-deleted — including reposts deleted via either delete route. Use this to evict mirrored posts.
post.reaction.createdA reaction landed on a post.
repost.createdAn actor reposted a post (data.source_post_id points at the original).
comment.createdA comment was created (data.parent_id null for top-level).
comment.updatedA comment body was edited.
comment.deletedA comment was removed.
comment.reaction.createdA reaction landed on a comment.
edge.follow.createdAn actor followed another actor.
edge.follow.deletedAn actor unfollowed another actor.
edge.follow.requestedAn actor asked to follow a private actor — awaiting that actor’s approval.
edge.follow.request_approvedA private actor approved a pending follow request (the edge itself arrives separately as edge.follow.created).
flag.createdContent was reported to the moderation queue (data.auto_hidden tells you if it tripped the auto-hide threshold).
flag.updatedA flag was resolved (actioned / dismissed).
moderation.action.appliedA moderator applied an action (hide / remove / restore / warn / ban / dismiss) — audit-row granularity.
actor.updatedAn actor profile changed (update, or upsert on an existing actor).
dm.message.createdA direct message was sent — the minimal DM push primitive (data.recipient_actor_ids lists everyone to notify).
tag.createdAn actor was tagged in a post (data.tagged_actor_id). The tag lands pending until that actor approves it.
asset.readyAn uploaded asset finished processing and is attachable to a post.
asset.failedAn asset failed processing or storage (data.failure_reason).

5

Set up before 17 October 2026?

Webhooks made through the older routes keep working until 15 January 2027.

Every webhook you registered with POST /v1/communities/:community_id/webhooks is now a community endpoint with the same id, URL, events and secret: you see it under …/webhooks/endpoints, and it receives the Standard Webhooks headers. Until 15 January 2027 it also still gets the old x-pcft-signature header; the Webhooks guide covers that header and moving your verifier. Secrets issued since already start with whsec_. The body is unchanged apart from the new workspace_id.

The old routes (…/webhooks, …/webhooks/:id with /test, /deliveries, /redeliver and /rotate-secret, and GET /v1/webhook-event-types) keep working until the same date and answer with Deprecation and Sunset headers. Two differences: a test now sends the example of the first event the webhook subscribes to (there is no ping type any more) and both test and redeliver report the outcome in their answer instead of running later.