Skip to main content
Webhooks let your application react to changes in a user’s Bkmark account the moment they happen — no polling required. When an event occurs (a bookmark is created, a tag is renamed, a group is deleted), Bkmark sends an HTTP POST request containing a JSON payload to the URL you registered. You verify the request came from Bkmark using an HMAC-SHA256 signature, then process the event however your application needs.

Creating a Subscription

Send a POST request to /api/v1/webhooks with the endpoint URL you want to receive events and the list of events you want to subscribe to.
The secret field is returned only once — at creation time. Store it immediately in a secure secrets manager (such as AWS Secrets Manager, HashiCorp Vault, or an environment variable). You cannot retrieve it again. If you lose it, delete the subscription and create a new one.
Response 201 Created:
string
Unique identifier for this webhook subscription. Use it to update, delete, or query deliveries.
string
The HTTPS endpoint Bkmark will POST events to.
string
HMAC-SHA256 signing secret. Shown once only. Use this to verify the X-Bkmark-Signature header on every incoming request.
array
The event types this subscription will receive.
boolean
Whether the subscription is currently enabled.

Request Body Parameters

string
required
The HTTPS endpoint Bkmark will deliver events to. Must be publicly accessible.
array of strings
required
One or more event type strings. Subscribe to all events with ["*"], or list individual events. See Available Events below.

Available Events

Bkmark emits events across three resource types: bookmarks, groups, and tags.
Transition events (bookmark.favorited, bookmark.unfavorited, bookmark.archived, bookmark.unarchived) fire only when the boolean state actually changes. For example, PATCHing isArchived: true on a bookmark that is already archived does not fire bookmark.archived. No-op updates to tags and groups also do not fire tag.updated or group.updated.

Payload Format

Every webhook delivery is a POST request with Content-Type: application/json. The top-level structure is consistent across all events:
string
The event type string, e.g. bookmark.created.
string (ISO 8601)
UTC timestamp of when the event occurred. Use this along with the entity id for idempotency checks.
object
The event payload. Shape varies by event type — see the examples below.

Payload Examples


Signature Verification

Every delivery includes an X-Bkmark-Signature header containing the HMAC-SHA256 hex digest of the raw request body, computed using your webhook secret.
Always verify the signature before processing any webhook. Skip this step and an attacker can send forged events to your endpoint. Use a timing-safe comparison function — standard string equality is vulnerable to timing attacks.
The verification algorithm is:
  1. Read the raw request body as bytes (do not parse JSON first).
  2. Compute HMAC-SHA256(secret, rawBody) and encode as a hex string.
  3. Compare the result with the value in X-Bkmark-Signature using a timing-safe equality function.
  4. Reject any request where the values do not match.

Node.js

Python


Managing Subscriptions

Use the following endpoints to list, update, delete, and test your webhook subscriptions. Example — disable a subscription without deleting it:

Delivery and Retries

Bkmark dispatches webhook requests asynchronously from a background job queue. The following guarantees and constraints apply:
  • Your endpoint must return a 2xx status code within 10 seconds. Any non-2xx response or a timeout is treated as a failure.
  • Failed deliveries are retried 3 times using exponential backoff.
  • You can inspect every attempt — including the HTTP response code your server returned — via GET /api/v1/webhooks/:id/deliveries.
Respond with 200 OK immediately and offload any heavy processing to a background worker or task queue. This prevents timeouts caused by slow database writes or third-party API calls inside your handler.
Because deliveries can be retried, your handler should be idempotent. Use the timestamp field and the entity id inside data together as a deduplication key — if you’ve already processed an event with that combination, skip it.

Polling Helpers

If you need to backfill events or reconcile state without webhooks, you can poll the bookmarks list endpoint with time-based filters. Both filters are strictly greater-than — a bookmark whose timestamp exactly matches the boundary is excluded. Combine with sortField=updatedAt&sortDir=desc for efficient incremental sync.