Webhook event catalog¶
Every webhook event type, with payload schema, when it fires, and deduplication semantics.
Common envelope¶
Every webhook delivery has the same shape:
POST {your webhook URL} HTTP/1.1
Content-Type: application/json
X-Revento-Event: {event type, e.g. "application.approved"}
X-Revento-Delivery-Id: {UUID v4 — unique per delivery, retries reuse this}
X-Revento-Timestamp: {unix epoch seconds}
X-Revento-Signature: sha256={HMAC-SHA256 of timestamp + "." + body}
Body shape:
{
"id": "evt_delivery_xyz",
"type": "application.approved",
"occurred_at": "2026-05-09T14:22:31Z",
"event_id": "evt_abc123",
"organization_id": "org_xyz789",
"data": { /* event-specific resource snapshot */ }
}
The data object carries the full resource snapshot at the moment the event occurred. You can act on it directly without making an API call to fetch the resource. If you need related data not included in the snapshot (e.g. you got application.approved and want the event metadata too), call the API with your installation token.
All envelope fields are always present. For events fired at the integration level rather than per-event (e.g. integration.suspended), event_id and organization_id are null rather than absent.
Deduplication¶
Use X-Revento-Delivery-Id as the dedup key. Retries reuse the same delivery id — an integration that processes a delivery, fails to acknowledge in time, and receives the retry should detect it and ack idempotently rather than processing twice.
For your own state-machine dedup, the resource id is documented under each event type below.
Snapshot freshness¶
The data snapshot is the source of truth for the event itself. application.approved carries the application as approved — that's not going to change retroactively. Process the snapshot directly.
If your handler delays processing for hours and you specifically need the current state of the resource at processing time (rather than the state at the event), you can call the API with the resource id. This is uncommon — most handlers should use the snapshot.
Event types¶
event.published¶
The event transitioned from draft to published.
Fires when: an organizer publishes a previously-draft event for the first time.
Payload
{
"type": "event.published",
"event_id": "evt_abc123",
"data": {
"id": "evt_abc123",
"title": "Sample Convention 2026",
"description": "Annual convention...",
"start_date": "2026-08-15T16:00:00Z",
"end_date": "2026-08-17T16:00:00Z",
"min_age": "age_all",
"access_mode": "open_approval",
"publish_status": "published",
"visibility": "discoverable",
"organization_id": "org_xyz789",
"location_summary": "Main Venue, Sample City",
"image_url": "https://example.com/image.jpg",
"published_at": "2026-04-15T08:00:00Z",
"created_at": "2026-04-01T10:30:00Z",
"updated_at": "2026-04-15T08:00:00Z"
}
}
Dedup key: id.
event.unpublished¶
The event transitioned from published back to draft.
Fires when: an organizer unpublishes a previously-published event.
Payload: same data shape as event.published, with publish_status: "draft" and an unpublished_at timestamp.
Dedup key: (id, unpublished_at).
event.visibility_changed¶
The event's visibility field changed (between private and discoverable).
Payload
{
"type": "event.visibility_changed",
"event_id": "evt_abc123",
"data": {
/* full event resource — see event.published */
"id": "evt_abc123",
"visibility": "discoverable",
"previous_visibility": "private",
"changed_at": "2026-04-20T12:00:00Z"
/* ... rest of event fields */
}
}
Dedup key: (id, changed_at).
event.access_mode_changed¶
The event's access_mode field changed (between closed, open_approval, and open_auto).
Payload: identical shape to event.visibility_changed with access_mode and previous_access_mode fields.
Dedup key: (id, changed_at).
application.submitted¶
A participant submitted an application to the event.
Fires when: the participant clicks "Submit" on the application form. Re-submission after revision_requested fires application.submitted again with the same id and a new submitted_at.
Payload
{
"type": "application.submitted",
"event_id": "evt_abc123",
"data": {
"id": "app_xyz",
"participant_id": "ptp_001",
"user_id": "usr_def456",
"status": "pending",
"submitted_at": "2026-04-29T14:00:00Z",
"is_resubmission": false,
"custom_form_fields": {
"motivation": "Sample motivation...",
"favorite_track": "RPG",
"arrival_day": "Friday"
},
"attachments": []
}
}
Dedup key: (id, submitted_at) — re-submissions have a different timestamp.
application.approved¶
An organizer approved the application.
Payload
{
"type": "application.approved",
"event_id": "evt_abc123",
"data": {
"id": "app_xyz",
"participant_id": "ptp_001",
"user_id": "usr_def456",
"status": "approved",
"submitted_at": "2026-04-29T14:00:00Z",
"approved_at": "2026-05-02T09:14:00Z",
"approver_id": "usr_org_123",
"last_review_action": "approved",
"review_message": null,
"custom_form_fields": { /* same shape as application.submitted */ },
"attachments": [],
"status_history": [
{ "status": "pending", "at": "2026-04-29T14:00:00Z" },
{ "status": "approved", "at": "2026-05-02T09:14:00Z" }
]
}
}
The data block matches GET /events/{id}/participants per-row shape with the application fields plus the action-specific approved_at / approver_id.
Dedup key: id.
application.rejected¶
An organizer rejected the application.
Payload: same shape as application.approved with status: "rejected", rejected_at, rejecter_id, last_review_action: "rejected". review_message carries the optional rejection reason if the organizer recorded one.
Dedup key: id.
application.revision_requested¶
An organizer asked the applicant to revise their application before reviewing again.
Payload
{
"type": "application.revision_requested",
"event_id": "evt_abc123",
"data": {
"id": "app_xyz",
"participant_id": "ptp_001",
"user_id": "usr_def456",
"status": "revision_requested",
"submitted_at": "2026-04-29T14:00:00Z",
"requested_at": "2026-05-01T11:00:00Z",
"requester_id": "usr_org_123",
"last_review_action": "revision_requested",
"review_message": "Could you provide more detail on...",
"custom_form_fields": { /* the responses being asked to revise */ },
"attachments": [],
"status_history": [
{ "status": "pending", "at": "2026-04-29T14:00:00Z" },
{ "status": "revision_requested", "at": "2026-05-01T11:00:00Z" }
]
}
}
Dedup key: (id, requested_at) — multiple revision requests are possible.
participant.registered¶
An applicant has been confirmed as a participant on the event.
Fires when: the participant transitions to registered status (typically after their application is approved, or immediately on submission for events with access_mode: open_auto).
Payload
{
"type": "participant.registered",
"event_id": "evt_abc123",
"data": {
"id": "ptp_001",
"user_id": "usr_def456",
"display_name": "Sample Name",
"nickname": "Sample",
"organisation": "Sample Org",
"privacy_mode": false,
"role": { "id": "role_attendee", "name": "Attendee" },
"application_status": "approved",
"real_name": "Sample Real Name",
"email": "sample@example.com",
"admin_notes": null,
"custom_form_fields": {
"favorite_track": "RPG",
"dietary_notes": "Vegetarian",
"arrival_day": "Friday"
},
"registered_at": "2026-05-03T10:25:00Z",
"created_at": "2026-04-29T14:00:00Z",
"updated_at": "2026-05-03T10:25:00Z"
}
}
The data block matches GET /events/{id}/participants per-row shape — the full event-scoped participant profile, including PII (real_name, email) which is admissible because the framework gates on formal-organization status at the connection layer.
Dedup key: id.
participant.profile_updated¶
A participant's event-scoped profile changed — either the participant edited their own profile, or an organizer edited it on their behalf.
Fires when: any PATCH /events/{id}/participants/{user_id} request succeeds. Coalesced if multiple fields change in the same request.
Payload
{
"type": "participant.profile_updated",
"event_id": "evt_abc123",
"data": {
"id": "ptp_001",
"user_id": "usr_def456",
"actor": "self",
"actor_id": "usr_def456",
"updated_at": "2026-05-09T14:22:00Z",
"profile": {
/* full event-scoped participant snapshot — same shape as participant.registered */
}
}
}
actor values:
- self — the participant edited their own profile
- organizer — an organizer / staff member edited it; actor_id identifies which one
Dedup key: (id, updated_at).
participant.removed¶
A participant was removed from the event.
Fires when: an organizer issues DELETE /events/{id}/participants/{user_id}. The participant's event-scoped profile, application, and any signups are cleared.
Payload
{
"type": "participant.removed",
"event_id": "evt_abc123",
"data": {
"id": "ptp_001",
"user_id": "usr_def456",
"removed_at": "2026-05-09T14:22:00Z",
"removed_by": "usr_org_123"
}
}
Dedup key: (id, removed_at).
activity.created¶
A new activity was added to the event's program.
Payload
{
"type": "activity.created",
"event_id": "evt_abc123",
"data": {
/* full activity snapshot — see GET /events/{id}/activities/{aid} */
"id": "act_001",
"title": "Opening Ceremony",
"description": "Welcome and overview",
"activity_type": "rpg_session",
"activity_details": { /* typed object per activity_type */ },
"start_time": null,
"end_time": null,
"duration_minutes": 60,
"status": "new",
"thread": { /* full thread object */ },
"location": null,
"host_id": null,
"host_display_name": null,
"co_host_ids": [],
"co_host_display_names": "",
"markers": [],
"capacity": 8,
"sign_ups_enabled": false,
"committed_signups_count": 0,
"signed_up_participant_ids": [],
"registration_waves": [],
"created_at": "2026-04-20T10:00:00Z",
"updated_at": "2026-04-20T10:00:00Z"
}
}
Dedup key: id.
activity.updated¶
An existing activity changed. This covers all field-level edits — title, description, capacity, sign_ups_enabled, host assignment, location, scheduling fields (start_time/end_time/thread), markers, etc.
Payload: full activity snapshot with the post-update state.
Dedup key: (id, updated_at). Note: rapid successive edits produce multiple deliveries; downstream consumers should idempotently apply the latest snapshot by updated_at.
activity.deleted¶
An activity was removed from the program.
Payload
{
"type": "activity.deleted",
"event_id": "evt_abc123",
"data": {
"id": "act_001",
"deleted_at": "2026-04-25T16:00:00Z"
}
}
Dedup key: id — an activity can only be deleted once.
activity.capacity_state_changed¶
The signup state for an activity changed (a participant signed up, signed out, or capacity changed). Coalesced and debounced — multiple sign-ups within a short window produce a single event with the current snapshot.
Fires when: committed_signups_count for an activity changes; debounced on a ~10-second window per activity to avoid flooding integrations at peak wave-open moments.
Payload
{
"type": "activity.capacity_state_changed",
"event_id": "evt_abc123",
"data": {
"activity_id": "act_001",
"capacity": 8,
"committed_signups_count": 5,
"sign_ups_enabled": true,
"signed_up_participant_ids": ["ptp_010", "ptp_011", "ptp_012", "ptp_013", "ptp_014"],
"snapshot_at": "2026-08-15T14:30:10Z"
}
}
The payload carries the full current signup snapshot — your integration can diff against its local state to detect individual sign-ups and sign-outs.
For real-time per-signup deltas at scale, raise the use case with your account contact.
Dedup key: (activity_id, snapshot_at).
thread.created / thread.updated / thread.deleted¶
A program thread (a parallel track within the event — e.g. "RPG Sessions", "Lectures") changed.
Payload for create/update: full thread snapshot:
{
"type": "thread.created",
"event_id": "evt_abc123",
"data": {
"id": "thr_main",
"name": "Main Hall",
"description": "All headline activities",
"icon_name": "auditorium",
"color_hex": "#2ED75A",
"sort_order": 0,
"created_at": "2026-04-10T09:00:00Z",
"updated_at": "2026-04-10T09:00:00Z"
}
}
For thread.deleted: { "id": "thr_main", "deleted_at": "..." }.
Dedup key: id for delete, (id, updated_at) for update.
location.created / location.updated / location.deleted¶
A venue location changed. Activities reference locations directly; building/floor/venue changes are reflected as location updates (the related entities are denormalized into the location response).
Payload for create/update: full location snapshot with parent venue + building + floor names denormalized:
{
"type": "location.created",
"event_id": "evt_abc123",
"data": {
"id": "loc_main_hall",
"name": "Main Hall",
"code": "MH-1",
"description": "...",
"scope": "indoor",
"type": "room",
"is_schedulable": true,
"capacity": 150,
"amenities": ["projector", "wifi"],
"position_x": 50.0,
"position_y": 50.0,
"icon_override": null,
"color_hex": null,
"operating_hours": "08:00-22:00",
"sort_order": 0,
"building": {
"id": "bld_main",
"name": "Main Building"
},
"floor": {
"id": "flr_0",
"number": 0,
"name": "Ground Floor"
},
"venue": {
"id": "ven_default",
"name": "Main Venue",
"address": "1 Sample Street, 12-345 Sample City"
},
"created_at": "2026-04-10T09:00:00Z",
"updated_at": "2026-04-10T09:00:00Z"
}
}
For location.deleted: { "id": "loc_main_hall", "deleted_at": "..." }.
Dedup key: id for delete, (id, updated_at) for update.
registration_wave.started¶
A registration wave transitioned from scheduled to started — sign-ups for activities gated by this wave are now open.
Fires when: the wave's scheduled starts_at timestamp is reached (cron-driven).
Payload
{
"type": "registration_wave.started",
"event_id": "evt_abc123",
"data": {
"id": "wave_a",
"name": "Friday morning",
"starts_at": "2026-05-10T08:00:00Z",
"ends_at": "2026-05-10T12:00:00Z",
"status": "started",
"activity_ids": ["act_001", "act_002", "act_003"],
"started_at": "2026-05-10T08:00:00Z"
}
}
Dedup key: (id, started_at).
registration_wave.finished¶
A registration wave transitioned from started to finished — sign-ups for activities gated by this wave are now closed.
Payload: same shape as registration_wave.started with status: "finished" and finished_at field.
Dedup key: (id, finished_at).
data.deletion_required¶
The integration's connection has been revoked. The integration is contractually expected to delete its cached copy of this event's data within 30 days.
Note: this event's payload is minimal — there's nothing to act on, only to delete.
Fires when: - An organizer revokes the integration from the event - The organization loses formal status - The integration publisher unpublishes - Revento suspends the integration
Payload
{
"type": "data.deletion_required",
"event_id": "evt_abc123",
"data": {
"event_id": "evt_abc123",
"organization_id": "org_xyz789",
"granted_scopes": ["event.read", "participants.read", "program.read"],
"deadline": "2026-06-08T14:30:00Z",
"reason": "organizer_revoked"
}
}
Reasons: organizer_revoked | org_unverified | integration_unpublished | revento_suspended.
Dedup key: (event_id, organization_id, deadline).
Contract note: there is no acknowledgment endpoint. The dispatch audit entry records that you were informed. The 30-day deadline is contractual.
user.revoked_access¶
A specific participant has revoked their "Login with Revento" consent for your integration.
Fires when: the participant uses "Settings → Connected apps → Revoke access" on your integration row.
Payload
{
"type": "user.revoked_access",
"event_id": "evt_abc123",
"data": {
"user_id": "usr_def456",
"event_id": "evt_abc123",
"integration_id": "int_yourapp",
"granted_scopes": ["profile.read", "event.attendance"],
"revoked_at": "2026-05-09T16:00:00Z"
}
}
Dedup key: (user_id, event_id, revoked_at).
After receiving this, your integration should delete any per-user data for user_id in the context of event_id. Other events the same user is signed in to via Login with Revento are unaffected — each (user, event) pair is independent.
integration.suspended¶
Revento has suspended your integration platform-wide. All your tokens are invalidated.
Payload
{
"type": "integration.suspended",
"event_id": null,
"data": {
"integration_id": "int_yourapp",
"suspended_at": "2026-05-09T18:00:00Z",
"reason_code": "security_review",
"reason_text": "Suspending pending review of reported credential leak."
}
}
Reason codes: security_review | terms_violation | data_handling_concern | voluntary_pause.
This is a notification — there's no action you can take via the API to resume. Contact your account owner.
Dedup key: (integration_id, suspended_at).
integration.unpublished¶
You — the integration publisher — have unpublished the integration. Per (event, organization) tuple, one of these fires alongside data.deletion_required.
Payload
{
"type": "integration.unpublished",
"event_id": "evt_abc123",
"data": {
"integration_id": "int_yourapp",
"unpublished_at": "2026-05-09T19:00:00Z",
"affected_event_id": "evt_abc123",
"affected_organization_id": "org_xyz789"
}
}
Dedup key: (integration_id, affected_event_id, affected_organization_id, unpublished_at).