Documentation

Webhook Event Reference

Organization webhook event types and their data, the delivery envelope, headers, signatures, retries, and the reward.generate request.

Implementing a receiving endpoint is covered in Receiving Webhooks.

Event types

EventFires when
activity.deletedAn activity is deleted
membership.joinedA user's membership in the organization starts
membership.leftA holder leaves, is removed or revoked, or the organization is deleted
membership.renewedA membership renews
membership.tier_changedA membership moves to another tier
reward.redeemedA reward lands in a wallet that belongs to a Davi user
webhook.testA test delivery is sent from the dashboard
reward.generateDavi asks for a reward's content. Sent only to the webhook a reward names; see reward.generate

No event fires when a membership's term runs out, whether it was not renewed or it served out a cancellation. Access stops at valid_until.

membership.tier_changed fires for each membership moved, whether it moved on its own, as part of moving every membership off a tier, or back to its previous tier because the payment for a change was reversed.

reward.redeemed fires once the ledger transfer confirms, and only when the receiving wallet is connected to a Davi user. A reward issued to a card or link with no account behind it does not produce one.

Delivery envelope

Every delivery is a JSON body with the same outer shape.

FieldMeaning
eventThe event type
timestampWhen this attempt was sent, ISO 8601. A retry carries a later one
organization_uuidThe organization that owns the webhook
user_uuidAlways null for an organization webhook. The user an event concerns is in data
dataThe event's fields, below
{
  "event": "membership.joined",
  "timestamp": "2026-01-15T18:42:07.512000+00:00",
  "organization_uuid": "8f3c…",
  "user_uuid": null,
  "data": {
    "origin": "user_app",
    "schema_version": 1,
    "user_uuid": "b21a…",
    "organization_uuid": "8f3c…",
    "tier_slug": "gold"
  }
}

Event data

Every data object carries two common fields:

FieldMeaning
originuser_app, or admin when Davi staff made the change
schema_version1

The rest depends on the event. A field marked nullable can be null.

activity.deleted

FieldMeaning
activity_uuidThe deleted activity
organization_uuidIts organization
activity_nameIts name at deletion
deleted_by_user_uuidWho deleted it. Nullable: null when no user did

membership.joined

FieldMeaning
user_uuidThe holder
organization_uuidThe organization
tier_slugThe tier joined

membership.left

FieldMeaning
user_uuidThe former holder
organization_uuidThe organization
ended_byleft (the holder's decision), removed (the organization's), or closed (the organization was deleted)

membership.renewed

FieldMeaning
user_uuidThe holder
organization_uuidThe organization

membership.tier_changed

FieldMeaning
user_uuidThe holder
organization_uuidThe organization
tier_slugThe tier the membership is on now. Empty when a reversal returned it to a tier that has since been deleted

reward.redeemed

FieldMeaning
wallet_addressThe receiving wallet
user_idThe recipient's user UUID
usernameNullable
first_name, last_nameNullable
account_typeThe recipient's account type
reward_template_idThe reward's UUID
transaction_idThe ledger transaction that issued it
organization_uuidThe reward's organization
pointsWhat the recipient was credited. Differs from the reward's points when their membership tier multiplies rewards. Nullable

webhook.test

FieldMeaning
testtrue
messageA fixed description of the test
timestampWhen the test was sent

webhook.test has no origin or schema_version.

Headers

HeaderValue
Content-Typeapplication/json
X-Webhook-EventThe event type
X-Webhook-Signaturesha256=<hex>, an HMAC-SHA256 of the raw body. The key is the SHA-256 hex digest of the signing secret, not the secret itself
X-Webhook-TimestampWhen this attempt was sent, Unix seconds
X-Webhook-DeliveryUnique delivery id, the same on every retry of that delivery

Delivery states and retries

PropertyValue
SuccessAny 2xx response
Statespending, success, failed, retrying
Timeout30 seconds per attempt
Retry scheduleIncreasing backoff, approximately 0s, 1s, 5s, 30s, 2m and 10m after the initial attempt
Not retriedA 4xx other than 429. The delivery fails at once
RedirectsNot followed. A 3xx counts as a failed attempt
HistoryStatus, response code, timings and error for each delivery, shown in the dashboard

Endpoint requirements

RequirementDetail
URLMust be publicly reachable. Private, loopback and .local addresses are rejected at creation
Signing secretReturned once, when the webhook is created. Rotating it invalidates the previous secret

reward.generate

A request for content, not a notification. When a reward whose content storage is external is redeemed, Davi posts reward.generate to the webhook the reward names in external_webhook_uuid, and nowhere else. Subscribing another webhook to it delivers nothing. The named webhook must be enabled and belong to the reward's organization.

The envelope, headers, signature, timeout and retries are the ones above. The redemption answers the caller with a delivery_uuid straight away; poll GET /api/v1/rewards/deliveries/{delivery_uuid} (scope reward:read) until its status is completed or failed.

Request data

FieldMeaning
reward_template_idThe reward's UUID
reward_template_nameIts name
external_idThe reward's content_config.external_id. Nullable
user_idThe recipient's user UUID
usernameNullable: null for a wallet with no user behind it
wallet_addressThe receiving wallet
transaction_idAlways "". The ledger transaction is created only after you answer

The redemption's metadata is not forwarded. data carries no origin or schema_version.

Response

Answer 2xx with a JSON body in one of two forms. A body in neither form, a 4xx, or running out of retries fails the redemption and returns its reserved supply.

Inline content. Davi stores the content at issuance and serves it from then on. It never changes afterwards.

{
  "content": {
    "content_version": "1.0",
    "generated_at": "2026-07-22T10:30:00Z",
    "data": {},
    "items": [
      {
        "type": "ticket",
        "title": "Main Stage Pass",
        "ticket_number": "TICKET-2026-000123",
        "starts_at": "2026-09-15T09:00:00Z",
        "barcode_value": "TCKT-8F3A-19C2-77BE",
        "barcode_format": "qrcode"
      }
    ]
  }
}

A hosted manifest. Davi fetches the content from you when the holder opens the reward.

{
  "manifest_url": "https://partner.example.com/davi/manifests/abc123",
  "access_key": "a-bearer-token-for-this-manifest"
}
RuleDetail
FetchGET manifest_url with Authorization: Bearer <access_key>, answered with the same object as content above
manifest_urlAn absolute http(s) URL on a public host
Timeout10 seconds per fetch
CachingDavi caches the manifest and fetches it again when the cache expires, so it must keep serving the same content for the reward's lifetime

Content fields

FieldMeaning
content_versionRequired. A string you choose, such as "1.0"
dataRequired. A free-form object
itemsThe reward's items. Each has a type: attachment, certificate, badge, coupon, voucher, ticket or asset
generated_atISO 8601. When the content was produced

Each item type's fields are listed in the API Reference with the reward schemas. A ticket requires type, title and ticket_number.