Glanced
Glanced

Webhooks

Get events delivered to any HTTPS endpoint the moment they happen. Works with Zapier, n8n, IFTTT, and Make, or your own code.

Webhooks send events to your endpoint as they happen: new articles, saved and unsaved items, new episodes, and arriving newsletters. Point one at a Zapier or n8n hook, or receive and verify the events in your own code.

Requires a Plus subscription.

Create and manage webhooks under Automation → Webhooks. You can hold up to 10 at a time, each with its own endpoint, signing secret, and event selection.

Events

EventFires when
article.newNew articles appear in a feed you subscribe to
article.starredYou save an item
article.unstarredYou unsave an item
subscription.addedYou subscribe to a feed or newsletter
subscription.removedYou unsubscribe (including OPML rollbacks)
episode.newNew episodes appear in a podcast you subscribe to
newsletter.newA newsletter you've connected arrives

You choose the events each webhook receives. Article feeds fire article.new, podcast feeds fire episode.new.

Envelope

Each delivery is a POST with a JSON body and these headers:

HeaderValue
webhook-idmsg_<delivery id>, the same across retries. Use it to drop duplicates
webhook-timestampUnix seconds
webhook-signaturev1,<base64> HMAC-SHA256 signature. See Verification
webhook-eventThe event type
{
	"event_type": "article.new",
	"data": {
		"feed_id": "00000000-0000-0000-0000-000000000001",
		"feed_title": "Example Feed",
		"count": 1,
		"last_update": "2026-01-15T09:30:00+00:00",
		"articles": [
			{
				"article_id": "00000000-0000-0000-0000-000000000002",
				"title": "Example article",
				"url": "https://example.com/example-article",
				"author": "Example Author",
				"published_at": "2026-01-15T09:30:00+00:00"
			}
		]
	}
}

article.new and episode.new arrive as batches. count is the total number of new entries, and the inline list is capped at 25. When count is larger than the list, fetch the rest with the API.

article.starred / article.unstarred

Both directions share one shape. item_id matches the id used by the items API, while article_id and episode_id identify the underlying article or episode.

{
	"item_id": "00000000-0000-0000-0000-000000000004",
	"article_id": "00000000-0000-0000-0000-000000000002",
	"episode_id": null,
	"item_type": "article",
	"title": "Example article",
	"url": "https://example.com/example-article",
	"author": "Example Author",
	"published_at": "2026-01-15T09:30:00+00:00",
	"feed_title": "Example Feed"
}

For episodes, url is the audio enclosure and episode_id is set instead of article_id. feed_title is null when the article reaches you through multiple subscribed feeds.

episode.new

{
	"feed_id": "00000000-0000-0000-0000-000000000001",
	"feed_title": "Example Podcast",
	"count": 1,
	"last_update": "2026-01-15T09:30:00+00:00",
	"episodes": [
		{
			"episode_id": "00000000-0000-0000-0000-000000000003",
			"title": "Example episode",
			"audio_url": "https://example.com/example-episode.mp3",
			"duration_seconds": 1800,
			"published_at": "2026-01-15T09:30:00+00:00"
		}
	]
}

newsletter.new

One event per received email, listing every article the email contained.

{
	"subscription_id": "00000000-0000-0000-0000-000000000005",
	"feed_id": "00000000-0000-0000-0000-000000000001",
	"feed_title": "Example Newsletter",
	"from_email": "sender@example.com",
	"subject": "Example Newsletter #12",
	"email_id": "00000000-0000-0000-0000-000000000006",
	"count": 1,
	"articles": [
		{
			"article_id": "00000000-0000-0000-0000-000000000002",
			"title": "Example article",
			"url": "https://example.com/example-article",
			"author": "Example Author",
			"published_at": "2026-01-15T09:30:00+00:00"
		}
	]
}

subscription.added / subscription.removed

{
	"subscription_id": "00000000-0000-0000-0000-000000000005",
	"feed_url": "https://example.com/feed.xml",
	"feed_title": "Example Feed",
	"action": "created",
	"folder_id": null
}

For added events, action is created, existing, or moved. For removed events it is unsubscribe or opml_rollback. Importing an OPML file fires one subscription.added per feed, and rolling the import back fires one subscription.removed per feed.

Verification

Signatures follow the Standard Webhooks spec, so any off-the-shelf verifier works. The signature is HMAC-SHA256 over msg_id.timestamp.body, where body is the exact raw bytes you received. It is base64-encoded and keyed with the signing secret shown once, when the webhook is created.

from standardwebhooks import Webhook, WebhookVerificationError

wh = Webhook("whsec_...")  # your signing secret

try:
    wh.verify(
        payload,  # raw request body bytes
        {
            "webhook-id": headers["webhook-id"],
            "webhook-timestamp": headers["webhook-timestamp"],
            "webhook-signature": headers["webhook-signature"],
        },
    )
except WebhookVerificationError:
    ...  # invalid signature

Endpoints must use HTTPS. The secret is shown once at creation. To rotate it, delete and recreate the webhook.

Delivery semantics

  • At-least-once. Retries reuse the same webhook-id, so treat it as your deduplication key.
  • Retries. Timeouts, 5xx, and 429 responses retry up to 3 times, with a 10 second timeout per attempt. Any other 4xx fails immediately.
  • Auto-disable. After 10 consecutive failed deliveries the webhook is disabled and you are emailed. Resuming it resets the failure counter, and so does any successful delivery.
  • Event log. The webhook's detail page keeps a rolling 30 day log of deliveries, with payloads and response codes.
  • Pause. Pausing stops deliveries without deleting the webhook. Test events still work while paused.

Ordering and bursts

Events across different feeds carry no ordering guarantees. Subscribing to many feeds at once, such as an OPML import, fires subscription.added per feed in quick succession. Feeds you pause stop firing article and episode events until resumed.

Test events

Use Send test event on a webhook to deliver a sample to your endpoint right away. The default is a generic test event. You can also send a sample shaped exactly like a real delivery for any event type. Samples carry an extra "test": true field so your handler can tell them apart from real traffic. Test sends share a 10 per minute limit with delivery resends.

How is this guide?

On this page