Skip to main content
Workspace webhooks POST a signed JSON payload to a URL you control whenever a subscribed post event fires. Use them to sync a CRM, trigger an internal job, or keep another system in step with TryPost. They are outgoing event subscriptions for the workspace — not inbound Telegram/Stripe webhooks.

Who can manage webhooks

Only the account owner and workspace admins can create, edit, test, rotate, replay, or delete webhooks. Members and viewers do not see Webhooks in the sidebar. See Team & roles.

Create a webhook

1

Open Webhooks

In the sidebar, click Webhooks.
2

Create webhook

Click Create webhook.
3

Set the endpoint

Enter a public http:// or https:// URL (max 255 characters). Private, loopback, and link-local addresses are rejected.
4

Choose events

Select at least one event. There is no wildcard — you must list the events you want.
5

Copy the signing secret

TryPost opens the webhook detail page. Copy the signing secret. You can reveal it again later, or rotate it.
Creating a webhook does not send a request. Use Send test event when you want to ping the URL. New webhooks start enabled — you cannot create them disabled. There is no cap on how many webhooks a workspace can have. Two rows can share the same URL. Webhooks have no name — identify them by URL and events. You can also manage webhooks through the REST API and MCP tools.

Events

post.created does not fire again when you edit a draft. Status events fire on the transition, not on every save. There is no event for publishing. A post moving into that state is silent. post.unscheduled fires only when a scheduled post returns to draft — moving a failed post back to draft does not send it. A new post is always created as draft, even if you pass scheduled_at. Scheduling is a later status change, so create-then-schedule produces post.created and then post.scheduled. Publish now (dashboard Post now, REST PUT with status=publishing, or publish-post-tool without scheduled_at) goes draftpublishing (silent) → published / partially_published / failed. You do not get post.scheduled. A scheduled post that later becomes due follows the same silent publishing step, then the outcome event. Edits that do not change status send nothing: content, media, labels, platforms, or a new scheduled_at on a post that is already scheduled. Duplicate is a new post, so it fires post.created. A failed or published post cannot be republished in place — duplicate it and you will get a fresh lifecycle. There are no events for comments, mentions, social-account disconnects, or team changes — those stay in notifications. AI Create has no dedicated post.ready event; when the draft is saved you get post.created (created_via is web). Deleting a workspace removes the webhooks with it and does not send post.deleted for each post.

Receive a delivery

Each delivery is POST with Content-Type: application/json. TryPost does not follow redirects — 3xx counts as a failure. Respond with 2xx (including 204) as soon as you have accepted the payload. Dedupe on envelope id: retries of the same attempt reuse it, but they refresh created_at and therefore the signature. Do not treat the full body as byte-identical across retries.

Headers

Self-hosting? Override the User-Agent with TRYPOST_USER_AGENT. Private endpoints stay rejected unless you set TRYPOST_ALLOW_PRIVATE_NETWORK=true — see Configuration.

Envelope

Every event — including the test ping — uses the same envelope:

Verify the signature

Compute HMAC-SHA256 of the raw body bytes with the signing secret. Compare the hex digest to X-Webhook-Signature with a constant-time comparison. Do not re-serialize the JSON — key order and escaping must match the bytes TryPost signed.
Frameworks that parse JSON before you see the request often discard the raw body. Read the unparsed body (for example Express express.raw({ type: 'application/json' })) and only then JSON.parse.
The secret is stored encrypted. It looks like whsec_ plus 32 random characters. You can read it from the dashboard, GET /webhooks/{id}, or get-webhook-tool.

Post payload

For every post event except post.deleted, data is the post at the moment of the event: post.deleted is only { "id": "…", "workspace_id": "…" } — the post is already gone.

Media

Each media[] item:

Platforms

One entry per post_platform row — including accounts that are not enabled for this publish (enabled: false). Create/sync writes a row for every connected account; filter on enabled if you only care about the ones that will go out.

Test event

On the webhook detail page, open the actions menu and click Send test event. That POSTs a signed webhook.test ping with an empty data object. A 2xx response is required. Timeouts, connection errors, and non-2xx statuses fail the test. The test is synchronous: the dashboard, API, and MCP wait for the HTTP response (5 second timeout). It does not write a delivery log, so you cannot replay it. It does not change status, consecutive_failures, or last_sent_at. The ping runs even if the webhook is paused or disabled — status only gates real post events.

Status

You can set enabled or disabled from the actions menu (Enable endpoint / Disable endpoint). You cannot set paused yourself — only TryPost does that after failures. Re-enabling a paused webhook clears consecutive_failures and paused_at. Saving an already-enabled webhook with status: enabled does not reset the counter.

Delivery, retries, and pause

Deliveries run on the webhooks Horizon queue.
  • Timeout: 10 seconds (test ping: 5 seconds)
  • Retries: 3 attempts, 60 seconds apart
  • Success: HTTP 2xx. last_sent_at updates and the failure counter resets
  • Failure: After 3 failed attempts, the consecutive-failure count goes up by one
  • Pause: At 5 consecutive failed deliveries, the webhook is paused and the account owner is emailed
A replay still delivers if the webhook is paused or disabled. Regular events do not. A successful replay on a paused webhook updates last_sent_at and resets the failure counter, but does not re-enable it — you still have to set status back to enabled. Private or local endpoints are rejected at create/update and again at delivery time.
Self-hosting? Horizon must be running. It already includes a webhooks supervisor — you do not add a separate worker. See Production.

Delivery logs

Open a webhook to see logs, newest first: event type, HTTP status, attempts, payload, and response body (first 2,000 characters). New deliveries appear live over WebSockets — no refresh. Self-hosting that live view needs Reverb running. The dashboard loads more rows as you scroll (25 at a time). The REST list paginates at 15. MCP returns the first limit rows (default 50, max 100) with no cursor. Logs older than 7 days are deleted daily. Deleting a webhook deletes its logs immediately. Test pings never appear here.
Self-hosting? The prune job is app:prune-webhook-logs, scheduled daily. Your cron must run php artisan schedule:run every minute — see Scheduled tasks.

Replay a delivery

Replay queues a new signed POST with the original data, a new envelope id, and a new log row. The dashboard flash, API { "replayed": true }, and MCP result mean the job was queued — not that the receiver already answered. Watch the new log row (it appears live) for the outcome. Replay works even when the webhook is paused or disabled. You can replay a successful log as well as a failed one.

Manage from the dashboard

On the webhook detail page, the actions menu also lets you:
  • Edit endpoint — change the URL and subscribed events
  • Enable endpoint / Disable endpoint — toggle delivery without deleting the row
  • Rotate signing secret — issue a new whsec_… value
  • Send test event — ping the URL
  • Copy webhook ID — paste into API or MCP calls
  • Delete — remove the webhook and its logs
The overview card shows the signing secret (masked). Reveal or copy it at any time — unlike API keys, the secret stays readable until you rotate it.

Rotate the signing secret

Rotate signing secret issues a new whsec_… value. The previous secret stops working immediately — including the next retry of an in-flight delivery, which signs with the new secret. Update the receiver before you rotate, or deliveries will fail signature checks.

Delete a webhook

Delete from the list or the detail page. Delivery to that URL stops at once, remaining logs are removed, and queued jobs for that webhook are discarded. This cannot be undone.

Via the API and MCP

List and update responses omit signing_secret. Create, get, and rotate return it. REST delete is 204; MCP delete returns { "deleted": true }.

FAQ

No. Send an explicit events array. * and unknown names are rejected.
No. Only Send test event (or the matching API / MCP call) sends a request.
Five deliveries in a row exhausted their retries. Fix the endpoint, then set status back to enabled.
Seven days. Older rows are pruned daily. Deleting the webhook removes the remaining logs.
Yes. Tests and replays ignore status. Only subscribed post events are skipped while paused or disabled.
No. publishing is silent. Subscribe to post.published, post.partially_published, and post.failed for the outcome.
No. Public http:// URLs are accepted. Private and loopback addresses are not, unless a self-hosted instance sets TRYPOST_ALLOW_PRIVATE_NETWORK=true.
No. It can reset the failure counter, but you still have to set status to enabled before subscribed events fire again.
No. Immediate publish never enters scheduled. Subscribe to post.created plus the outcome events (post.published, post.partially_published, post.failed).
No. post.scheduled only fires when status becomes scheduled. Editing scheduled_at on an already-scheduled post is silent.
No. Workspace webhooks are always POST with Content-Type, User-Agent, and X-Webhook-Signature.
No. Only the seven post lifecycle events. Comments, mentions, and account disconnects stay in notifications.