Skip to content

Create a webhook endpoint

POST /webhooks
Scopewebhooks:manage
Customer session tokenNot accepted: call from your server with an API key
Rate limit classwrite (limits)
Idempotency keyRequired (idempotency)
MCP toolcharm_create_webhook

Subscribes a URL to loyalty events. The response includes the endpoint’s signing secret exactly once; every delivery carries an X-Charm-Hmac-Sha256 header (base64 HMAC-SHA256 of the raw body, keyed with that secret) to verify against. Deliveries are at-least-once — dedupe on the event id — and a 2xx within 5 seconds acknowledges; anything else is retried with backoff for about a day, after which the delivery is dropped. An endpoint that keeps failing is disabled automatically.

FieldTypeDescription
urlstringPublic https URL that receives the POSTed events.
topicsarray of string: points.earned, points.adjusted, reward.redeemed, tier.changed, tier.approaching, store_credit.issued, referral.completedEvent topics to subscribe to.

Returns WebhookEndpointCreated.

FieldTypeDescription
idstring
urlstring
topicsarray of string
statusstring
created_atstring or null(nullable)
consecutive_failuresnumber
last_success_atstring or null(nullable)
last_failure_atstring or null(nullable)
disabled_reasonstring or null(nullable)
api_versionstringThe dated API version this endpoint’s deliveries are rendered in (Charm-Version).
secretstringThe signing secret, shown exactly once.
{
"id": "id",
"url": "https://example.com/charm-hook",
"topics": [
"topics"
],
"status": "active",
"created_at": "2026-10-01T09:30:00.000Z",
"consecutive_failures": 1,
"last_success_at": "2026-10-01T09:30:00.000Z",
"last_failure_at": "2026-10-01T09:30:00.000Z",
"disabled_reason": null,
"api_version": "api version",
"secret": "whsec_3f9…"
}
Terminal window
curl -X POST "https://charm.appfleece.app/api/v1/webhooks" \
-H "Authorization: Bearer chrm_live_..." \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/charm-hook","topics":["points.earned"]}'

unauthorized · invalid_token · insufficient_scope · access_required · validation_error · rate_limited · idempotency_conflict

Every error body carries code, message, request_id and a doc_url pointing at the matching entry in the error catalog.