Webhooks
Receive real-time events from Presscart when articles change status or when comments are created, updated, or archived.
Webhooks let your application react to changes in Presscart in real time, without polling the API. You configure an endpoint URL, select the events you want to receive, and Presscart will POST a JSON payload to your endpoint whenever a matching event fires.
Available topics
| Topic | When it fires |
|---|---|
article.status_changed | An article transitions to a new content status, such as draft submitted, revision requested, pending publishing, or published. |
comment.created | An article comment is created in Presscart or through the Comments API. |
comment.updated | An article comment is updated. |
comment.archived | An article comment is archived. |
You can also subscribe to all events when creating a webhook; new topics added in the future will be delivered automatically. Internal comments are not delivered.
Create a webhook in the dashboard
- Open the Presscart app and switch to the workspace and profile you want to subscribe.
- Open Tools in the sidebar and select Webhooks. The table lists each endpoint with its events, status, and creation date.
- Click New webhook.
- Fill in the dialog:
- Endpoint URL — your server URL that will receive webhook events from Presscart. It must be a valid
httporhttpsURL and reachable from the internet. Usehttpsfor anything beyond local testing. - Events to send — pick one or more topics, or choose All events.
- Endpoint URL — your server URL that will receive webhook events from Presscart. It must be a valid
- Click Add (it stays disabled until the URL is valid and at least one event is chosen). You're taken to the webhook detail page.
- On the detail page, reveal and copy the Signing secret. Store it next to your endpoint configuration — you'll need it to verify deliveries (see Verifying signatures below).
Manage a webhook
Open a webhook from the Webhooks table to see its detail page. The Webhook actions menu (the three-dot button) offers:
- Edit endpoint — change the URL and the events.
- Disable endpoint / Enable endpoint — pause or resume deliveries without deleting the webhook.
- Rotate signing secret — generate a new signing secret. Update your receiver right after, because the old secret is replaced.
- Delete webhook — remove the webhook permanently.
The Overview tab shows delivery metrics for the last hour, 24 hours, 7 days, or 30 days. The Event Deliveries tab lists each delivery attempt with its status, response code, topic, event data, and your endpoint's response. For a failed attempt you can retry the delivery from there.
Presscart sends webhook events from the production environment only. Events fired in staging or local environments are not delivered.
Manage webhooks with the API
Webhooks are stored as event destinations. The same endpoints that power the dashboard are available in the API under the Event Destinations tag, with your team slug in the path (for example team-a-bb4478bd2e2e, shown in the app URL):
| Action | Request |
|---|---|
| List topics you can subscribe to | GET /teams/{team_slug}/event-destinations/topics |
| List destinations | GET /teams/{team_slug}/event-destinations |
| Create a destination | POST /teams/{team_slug}/event-destinations |
| Get one destination | GET /teams/{team_slug}/event-destinations/{destination_id} |
| Update a destination | PATCH /teams/{team_slug}/event-destinations/{destination_id} |
| Disable / enable | PUT /teams/{team_slug}/event-destinations/{destination_id}/disable and .../enable |
| Delete a destination | DELETE /teams/{team_slug}/event-destinations/{destination_id} |
| List delivery attempts | GET /teams/{team_slug}/event-destinations/{destination_id}/attempts |
| Retry a delivery | POST /teams/{team_slug}/event-destinations/{destination_id}/retry with { "event_id": "..." } (returns 202) |
Note: These endpoints require a signed-in Presscart user session. They do not accept pc_ API tokens, so you cannot register a webhook from a server using only an API token. Use the dashboard steps above for a server-to-server setup.
To create a webhook, send type: "webhook", the topics, and the endpoint URL in config.url. Use "topics": "*" for all events.
{
"type": "webhook",
"topics": ["article.status_changed", "comment.created"],
"config": { "url": "https://example.com/presscart/webhooks" }
}The create and get responses include a signing_secret for the webhook. List responses never include it. To rotate the secret, send PATCH with { "credentials": { "rotate_secret": true } }, which is what the dashboard does.
Event payload
Every webhook delivery is a JSON POST to your endpoint with the same envelope:
{
"id": "evt_01HF8...",
"topic": "comment.created",
"time": "2026-03-20T10:00:00.000Z",
"metadata": {
"source": "presscart.comments"
},
"data": {
"id": "ReDLZTmGOG2A",
"content": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{ "type": "text", "text": "Can you clarify the requested change?" }
]
}
]
},
"author": { "name": "Jane Smith" },
"created_at": "2026-03-20T10:00:00.000Z",
"updated_at": "2026-03-20T10:00:00.000Z",
"parent_comment_id": null
}
}The shape of data depends on the topic.
article.status_changed
{
"article_id": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee",
"campaign_id": "66666666-6666-6666-6666-666666666666",
"profile_id": "22222222-2222-2222-2222-222222222222",
"status": {
"id": "44444444-4444-4444-4444-444444444444",
"prefix": "pending-content-brief"
},
"effective_at": "2026-03-20T10:00:00.000Z",
"changed_at": "2026-03-20T10:00:00.000Z"
}Use status.prefix (stable string identifier) rather than status.id when branching on status in your code.
comment.created, comment.updated, comment.archived
{
"id": "ReDLZTmGOG2A",
"content": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [{ "type": "text", "text": "Updated comment text." }]
}
]
},
"author": { "name": "Jane Smith" },
"created_at": "2026-03-20T10:00:00.000Z",
"updated_at": "2026-03-20T10:05:00.000Z",
"parent_comment_id": null
}id is the comment reference — the same value returned by the Comments API. For replies, parent_comment_id is the parent's reference. Comment author data includes the display name only; email addresses are not included in webhook payloads.
Verifying deliveries
Each webhook has a signing secret, shown on the webhook detail page (hidden until you click the eye icon, with a copy button). Store it server-side and use it to check that a request came from Presscart.
Presscart delivers webhooks through Hookdeck Outpost, and Outpost produces the signature. The signature header name, algorithm, encoding, and signed content are set in the Outpost deployment configuration, which is not part of the Presscart codebase, so this page cannot state them with certainty. In Outpost's default webhook mode the delivery carries an x-outpost-signature header whose value is v0= followed by the hex-encoded HMAC-SHA256 of the raw request body, keyed with the signing secret. While a secret is being rotated, the header holds one signature per valid secret, separated by commas. See the Outpost webhook documentation for the exact formats and the Standard Webhooks variant.
Before you rely on signature checks in production, send a test delivery to your endpoint (see Local development), log the request headers, and confirm which signature header and format your webhook uses.
Whatever the exact header, verify the raw request body before parsing JSON, compare signatures with a timing-safe comparison, and reject requests whose signature is missing or invalid.
Responding to deliveries
- Return a
2xxstatus code to acknowledge receipt. Anything else is treated as a failure. - Respond quickly (under a few seconds). Do heavy work asynchronously after acknowledging.
- Treat deliveries as at-least-once. The same event may arrive more than once on retries — deduplicate on the envelope
idor, for comment events, ondata.id. - Presscart publishes events as retry-eligible, and Outpost redelivers failed deliveries automatically. The retry schedule is part of the Outpost configuration, not the Presscart code, so it is not documented here. You can also retry a failed attempt manually from the Event Deliveries tab or with the retry endpoint above.
Local development
Webhooks need a reachable endpoint URL. For quick local testing, use Hookdeck Console as a temporary request inspector:
- Open Hookdeck Console and create a temporary webhook URL.
- Create or edit your Presscart webhook and paste the Hookdeck Console URL as the endpoint.
- Trigger an event in Presscart, such as creating a comment or changing an article status.
- Inspect the request body, headers, response, and retries in Hookdeck Console.
If you want Presscart to reach a local server running on your machine, expose that local server with the Hookdeck CLI or a tunnel and use the generated public URL as the Presscart endpoint. You can keep separate webhooks for local, staging, and production.