Skip to content

Webhooks

Webhooks let your systems react in real time when resources change in PlusPlus — no polling required.

Preview

Webhooks are designed but not yet implemented. This page describes the planned interface so you can design integrations against it. The API surface may change before launch.

How webhooks work

  1. You register a subscription with a target URL, an HMAC secret, and a list of event types.
  2. When a matching event fires, PlusPlus POSTs a JSON payload to your URL with an X-Plusplus-Signature header.
  3. Your endpoint verifies the signature, processes the event, and returns 2xx.
  4. PlusPlus retries failures with exponential backoff for ~24 hours, then marks the delivery dead.

Event taxonomy

Naming follows {resource}.{action} (Stripe/GitHub-style).

Generic content events

Fired for every content type. Payload uses the shared ContentItemSummary schema.

Event When
content_item.created Any content item is created.
content_item.updated Any content item's shared metadata changes.
content_item.deleted Any content item is hard-deleted.
content_item.archived Any content item is archived (soft-delete).
content_item.unarchived Any content item is restored from archive.

Type-specific events

Fired alongside the generic event, with a richer, type-specific payload.

Type Event prefix Actions
Article article.* created, updated, deleted, archived, unarchived
Video video.* created, updated, deleted, archived, unarchived
Course course.* created, updated, deleted, archived, unarchived
Guide guide.* created, updated, deleted, archived, unarchived
Event event.* created, updated, deleted, archived, unarchived
Event Type event_type.* created, updated, deleted, archived, unarchived
Link link.* created, updated, deleted, archived, unarchived
Track track.* created, updated, deleted, archived, unarchived
Scheduled Track scheduled_track.* created, updated, deleted, archived, unarchived
Collection collection.* created, updated, deleted, archived, unarchived

When an article is created, both content_item.created and article.created fire. Subscribe to whichever fits your needs (or both — events are deduplicated by id).

Assignment events

Event When
assignment.created A user is assigned content.
assignment.updated Assignment metadata changes.
assignment.completed A user marks an assignment complete.
assignment.dropped A user drops an assignment.
assignment.deleted An assignment is removed.

Wildcard subscriptions

You can subscribe with fnmatch-style globs:

  • * — all events
  • content_item.* — all generic content events
  • article.* — all article-specific events
  • assignment.* — all assignment events

Payload format

{
  "id": "evt_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "type": "article.created",
  "api_version": "2.0.0",
  "created_at": "2026-04-06T12:00:00Z",
  "data": {
    "object": { /* same shape as GET response for that resource */ }
  }
}

data.object is identical to the GET response for that resource — no special parsing required.

Signature verification

Each delivery includes:

X-Plusplus-Signature: t=1714473600,v1=<hex_hmac>
X-Plusplus-Event: article.created
X-Plusplus-Event-Id: evt_a1b2c3d4-...

Verify the signature by computing HMAC-SHA256(secret, "{t}.{raw_body}") and comparing with v1 using a constant-time comparator. Reject deliveries where t is more than five minutes old to prevent replay.

Delivery semantics

  • At-least-once. Use id to deduplicate.
  • Retries. Exponential backoff up to ~24 hours total.
  • Order. Deliveries are not strictly ordered. Use created_at if order matters.
  • Timeout. Your endpoint must return 2xx within 10 seconds.

Security

  • Target URLs must be https://.
  • Private/internal IP ranges (10.x, 172.16.x, 192.168.x, 127.x, localhost) are rejected at registration.
  • The HMAC secret is shown once at creation and rotation. Store it in a secret manager.

Subscription API

Method Path Description
GET /webhooks List subscriptions.
POST /webhooks Create a subscription (returns secret once).
GET /webhooks/{public_id} Get a subscription.
PATCH /webhooks/{public_id} Update url, events, description, or is_active.
DELETE /webhooks/{public_id} Delete a subscription.
POST /webhooks/{public_id}/test Send a ping event to verify your endpoint.
POST /webhooks/{public_id}/rotate-secret Generate a new secret (returns once).
GET /webhooks/{public_id}/deliveries List recent delivery attempts.

See the API reference for full request/response schemas once these endpoints ship.