Quickstart
Create a destination, subscribe it to events, verify the endpoint is wired
correctly, and receive your first delivery. Every request below is runnable
against /events/*.
Prerequisites
- A Helix API key. See Authentication.
- An HTTPS endpoint you control that can receive a
POSTrequest.
Base URL: https://api.feeds.onhelix.ai
Step 1: Create a destination
curl -X POST https://api.feeds.onhelix.ai/events/destinations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Newsroom CMS",
"transport": "webhook",
"config": { "url": "https://cms.acme.com/hx" }
}'
The response includes your signing secret in full — store it now. It is not a show-once secret: the secrets endpoint returns every still-valid secret in full at any time.
The response also includes a validation block from a pre-flight reachability
probe. An unreachable URL still saves; only a non-HTTPS or unsafe URL is
rejected. See
API Reference.
{
"success": true,
"data": {
"id": "8f14e45f-ceea-467e-adde-3fb5c25adfcd",
"status": "enabled",
"secrets": [{ "secret": "whsec_…", "expiresAt": null }],
"validation": {
"reachable": true,
"statusCode": 200,
"responseTimeMs": 142
}
}
}
Save the returned id — you'll need it for the next step. You can also set
your own externalId in the request body to reference this destination by
an id from your own system instead of the server-assigned one — see {ref}
resolution.
Step 2: Subscribe to events
Declare which events this destination should receive. This example scopes
to every news.* event across the whole organization:
curl -X POST https://api.feeds.onhelix.ai/events/destinations/{id}/subscriptions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"eventTypes": ["news.*"],
"scopeKind": "all"
}'
To scope to specific feeds instead, use scopeKind: "feeds" with a
feeds array — see Concepts.
See Event Types for
the full catalog of eventTypes values, or browse the per-type pages
starting with news.item.added.
Step 3: Verify signature handling
Implement signature verification now, then prove it with a test delivery (below) before the first real event fires:
Respond within 30 seconds. Any 2xx status is success; everything else
is a failure. The response body is never read — acknowledge first, then do
the real work asynchronously, as the handler below does. See
Reliability: The contract
for the full rules.
const express = require('express');
const { Webhook } = require('standardwebhooks');
const secret = 'whsec_...'; // the whsec_… value from Step 1
const wh = new Webhook(secret);
const app = express();
app.post(
'/hx',
express.raw({ type: 'application/cloudevents+json' }),
(req, res) => {
try {
wh.verify(req.body, req.headers);
} catch (err) {
return res.status(401).send('Invalid signature');
}
res.status(200).send('OK');
// process asynchronously below
}
);
See Signature Verification for the full explanation and other language libraries.
Now send yourself a signed test delivery. It goes through exactly the path a real event takes — same envelope, same signed headers, same custom headers — and tells you what your endpoint answered:
curl -X POST https://api.feeds.onhelix.ai/events/destinations/{ref}/test \
-H "Authorization: Bearer YOUR_API_KEY"
{
"success": true,
"data": {
"delivered": true,
"statusCode": 200,
"responseTimeMs": 142,
"error": null,
"webhookId": "…",
"event": { "type": "delivery.test", "id": "…", "time": "…" }
}
}
delivered: false with a statusCode of 401 means your endpoint rejected
the signature; null with an error means it never answered. The event type
is delivery.test — accept it (verify, return 2xx) and otherwise ignore it.
See Reliability: Test deliveries.
Step 4: Receive a delivery
This step needs real feed activity to trigger it. If you don't already have a feed producing content, set one up first: see News Feeds Quickstart or Event Feeds Quickstart, then add or update an item in a feed your subscription covers.
Once a matching event occurs (a new article lands in a subscribed feed, for
example), your endpoint receives a POST with a CloudEvents envelope. Give
it a minute or two: delivery is
debounced per subject (120 seconds
by default), so a freshly created destination seeing nothing for the first
couple of minutes is expected, not broken.
{
"specversion": "1.0",
"type": "news.item.added",
"source": "/feeds/news/{feedId}",
"subject": "123e4567-e89b-12d3-a456-426614174000",
"id": "456e7890-a12b-34c5-d678-901234567890",
"time": "2024-01-15T10:30:00.000Z",
"datacontenttype": "application/json",
"data": {
"feedId": "123e4567-e89b-12d3-a456-426614174000",
"id": "123e4567-e89b-12d3-a456-426614174000",
"newsPageId": "123e4567-e89b-12d3-a456-426614174000"
}
}
Use the envelope id as your idempotency key — it is stable across retry
attempts of the same delivery. A replay
reuses that same id, though, so a naive "skip if I've already seen this
id" store will also silently skip a replay you actually wanted
reprocessed — see Reliability: Idempotency
for how to key your dedupe so a replay still gets through. The data
payload is intentionally minimal; fetch full content from the corresponding
feed API. See Concepts for the envelope and
payload philosophy.
Next steps
- Concepts — envelope, type taxonomy, wildcards, feed scoping, debounce.
- Reliability — retries, auto-disable, replay, backfill.
- API Reference — the complete
/events/*surface.