Skip to content

Endpoint reference

Every read API endpoint, with request shape, response shape, required scopes, and example payloads.

Common conventions

Field naming

A few naming conventions you'll see across endpoints - call out in case any catch you off guard:

  • Application review fields on participants are prefixed last_ because the participant object reflects the most recent review action: last_reviewed_at, last_reviewer_id, last_review_action (approved / rejected / revision_requested). Webhooks for individual review actions use the action-specific verb (application.approved carries approved_at, application.rejected carries rejected_at).
  • Status field naming: status on the resource itself (e.g. application_status for applications, event.publish_status, activity.status).
  • Time fields: always ISO 8601 in UTC. Field name ends in _at (submitted_at, connected_at).
  • Enums starting with a digit are prefixed: min_age values are age_all, age_18_plus - never bare 18_plus (which would be invalid as an enum identifier in many languages).

Base URL

Environment Base
All usage https://api.revento.app/api/v1

Use this base URL for all API requests. To test your integration, connect it to an event you control through the standard OAuth flow; the base URL stays the same.

URLs in this reference are written relative to the base.

Authentication

Every request requires a Bearer header with an installation token (event-scoped) or user token (participant-scoped). Token type matters per endpoint - see the table on each endpoint page.

Authorization: Bearer rev_install_8h3k2m...
Accept: application/vnd.revento.v1+json

Request IDs

Send a UUID on every request:

X-Revento-Request-Id: 0f8ad6e2-7e1a-4e7e-bc3c-8f5b1c4d2e3f

We echo it back in the response and log it on our side.

Pagination

List endpoints return:

{
  "data":        [ /* array of resources */ ],
  "next_cursor": "opaque-string-or-null",
  "has_more":    true
}

To fetch the next page, pass next_cursor as the cursor query parameter. The cursor is opaque - don't parse it. has_more is the canonical "more pages exist" signal. limit defaults to 25, max 50 - kept conservative because list responses can carry custom_form_fields per row, which may approach the 2MB per-response cap on events with rich custom forms. If you receive 413 payload_too_large, reduce limit and retry.

Errors

Every error has the same envelope:

{
  "error":      "string_code",
  "message":    "Human-readable explanation",
  "request_id": "UUID matching X-Revento-Request-Id"
}

Full catalog in Errors.

Endpoint summary

Four groups, four auth styles:

Group Auth Endpoints
Event data (installation token) Authorization: Bearer rev_install_... GET /events/{id}, /participants, /participants/{participant_id}, /program, /activities, /locations, /threads, /registration-waves, /roles
User data (user token) Authorization: Bearer rev_user_... GET /me/profile
OAuth management HTTP Basic client_id:client_secret POST /oauth/revoke
Publisher tooling Authenticated publisher session POST /developer/integrations/{id}/rotate-client-secret, POST /developer/integrations/{id}/rotate-webhook-secret, POST /developer/integrations/{id}/send-test-webhook

Mixing installation and user token types returns 403 user_token_required or 403 installation_token_required. Sending an integration bearer token to publisher-tooling endpoints returns 401 invalid_token.


Event endpoints (installation token)

GET /events/{event_id}

Read event metadata.

Scope: event.read

Request

GET /api/v1/events/evt_abc123 HTTP/1.1
Host: api.revento.app
Authorization: Bearer rev_install_...
Accept: application/vnd.revento.v1+json

Response

{
  "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",
  "created_at":       "2026-04-01T10:30:00Z",
  "updated_at":       "2026-05-09T14:22:00Z"
}

Field reference

Field Type Notes
id string Stable, opaque identifier
title string Display title
description string | null Free-text Markdown
start_date, end_date ISO 8601 (UTC) Always UTC
min_age enum age_all | age_18_plus
access_mode enum closed | open_approval | open_auto
publish_status enum draft | published
visibility enum private | discoverable
organization_id string The organization that owns this event
location_summary string | null Free-text "headline" location. The full venue graph (with buildings, floors, rooms) is at /locations
image_url URL | null Cover image

GET /events/{event_id}/participants

List participants of the event. Returns the event-scoped profile (not the global Revento user profile) - including custom form responses and PII fields where present.

Scope: participants.read

Request

GET /api/v1/events/evt_abc123/participants?limit=50 HTTP/1.1
Authorization: Bearer rev_install_...

Response

{
  "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"
      },
      "last_reviewed_at":    "2026-05-02T09:14:00Z",
      "last_reviewer_id":    "usr_org_123",
      "last_review_action":  "approved",
      "registered_at":       "2026-05-02T09:14:00Z",
      "created_at":          "2026-04-29T14:00:00Z",
      "updated_at":          "2026-05-02T09:14:00Z"
    }
  ],
  "next_cursor": "eyJjIjogIjIw...",
  "has_more":    true
}

Roles

role is event-scoped - every event defines its own roles (typically Attendee, Organizer, Staff, NPC, but custom roles are possible). The full role list is at GET /events/{id}/roles.

Application statuses

pending | approved | rejected | revision_requested

custom_form_fields

A JSON object whose keys depend on the event's custom application form. The schema is defined per-event by the organizer. Treat it as an opaque map - never assume specific keys exist.


GET /events/{event_id}/participants/{participant_id}

Read one participant by id.

Scope: participants.read

Request

GET /api/v1/events/evt_abc123/participants/ptp_001 HTTP/1.1
Authorization: Bearer rev_install_...
Accept: application/vnd.revento.v1+json

Response

{
  "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"
  },
  "last_reviewed_at":    "2026-05-02T09:14:00Z",
  "last_reviewer_id":    "usr_org_123",
  "last_review_action":  "approved",
  "registered_at":       "2026-05-02T09:14:00Z",
  "created_at":          "2026-04-29T14:00:00Z",
  "updated_at":          "2026-05-02T09:14:00Z"
}

The object shape matches one row from GET /events/{event_id}/participants.


GET /events/{event_id}/program

Read the event's program - threads with nested activities. Activities have start_time and end_time (UTC) - group them by date on your side if you need a day-level view.

Scope: event.read

Response

{
  "threads": [
    {
      "id":          "thr_main",
      "name":        "Main Hall",
      "description": "All headline activities",
      "icon_name":   "auditorium",
      "color_hex":   "#2ED75A",
      "sort_order":  0,
      "activities": [
        { /* full activity object - see GET /events/{event_id}/activities */ }
      ]
    }
  ]
}

For paginated flat access to activities, use /activities.


GET /events/{event_id}/activities

List activities, paginated. Use this when you don't need the program tree structure or when there are many activities.

Scope: event.read

Response

{
  "data": [
    {
      "id":                        "act_001",
      "title":                     "Opening Ceremony",
      "description":               "Welcome and overview of the weekend",
      "activity_type":             "rpg_session",
      "activity_details":          { /* typed object per activity_type */ },
      "start_time":                "2026-08-15T16:00:00Z",
      "end_time":                  "2026-08-15T17:00:00Z",
      "duration_minutes":          60,
      "status":                    "scheduled",
      "thread": {
        "id":        "thr_main",
        "name":      "Main Hall",
        "color_hex": "#2ED75A"
      },
      "location": { /* full location with building/floor/venue summary */ },
      "host_id":                   "ptp_001",
      "host_display_name":         "Sample Host",
      "co_host_ids":               ["ptp_002", "ptp_003"],
      "co_host_display_names":     "Co-host A, Co-host B",
      "markers": [
        { "id": "mkr_kids", "name": "Kids-friendly", "color_hex": "#DCA041" }
      ],
      "capacity":                  8,
      "sign_ups_enabled":          true,
      "committed_signups_count":   5,
      "signed_up_participant_ids": ["ptp_010", "ptp_011", "ptp_012", "ptp_013", "ptp_014"],
      "registration_waves":        ["wave_a"],
      "created_at":                "2026-04-20T10:00:00Z",
      "updated_at":                "2026-08-15T14:30:00Z"
    }
  ],
  "next_cursor": "...",
  "has_more": false
}

Field reference

Field Type Notes
activity_type enum rpg_session | lecture | workshop | competition | discussion | LARP | other | break
activity_details object Typed per activity_type. The schema is defined by the event's activity type definitions. Treat as opaque per-type JSON unless you've coordinated with the organizer on what fields to expect.
start_time, end_time ISO 8601 (UTC) | null null until the activity is scheduled
status enum new | scheduled | canceled
thread object Reference to the parent thread, with name + color for display
location object | null Full location with building/floor/venue. null until a location is assigned
host_id, co_host_ids string, string[] Participant IDs (per /participants). Display names denormalized into host_display_name + co_host_display_names (comma-joined).
signed_up_participant_ids string[] Current participants signed up (only present if sign_ups_enabled)
committed_signups_count int Number that have actually committed; useful when signed_up_participant_ids is paginated or omitted
markers object[] Tag-like badges (e.g. "Kids-friendly", "Advanced") with display info
registration_waves string[] IDs of waves that gate sign-ups for this activity

GET /events/{event_id}/locations

The full venue graph - venues, buildings, floors, and locations (the rooms / spots that activities point at).

Scope: event.read

Response

{
  "venues": [
    {
      "id":             "ven_default",
      "name":           "Main Venue",
      "address":        "1 Sample Street, 12-345 Sample City",
      "google_maps_url": "https://maps.google.com/...",
      "center_lat":     52.0,
      "center_lng":     19.0,
      "sort_order":     0,
      "buildings": [
        {
          "id":           "bld_main",
          "name":         "Main Building",
          "description":  "...",
          "lat":          52.0,
          "lng":          19.0,
          "position_x":   50.0,
          "position_y":   50.0,
          "sort_order":   0,
          "floors": [
            {
              "id":         "flr_0",
              "number":     0,
              "name":       "Ground Floor",
              "sort_order": 0
            }
          ]
        }
      ],
      "locations": [
        {
          "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" }
        }
      ]
    }
  ]
}

Location types: room | stage | expo | food | restroom | entrance | info | parking | rest_area | first_aid | smoking_area | other

Scopes: indoor | outdoor


GET /events/{event_id}/threads

The flat list of program threads, with display metadata. Activities reference threads by id; use this if you need to render thread color/icon outside the program tree.

Scope: program.read

Response

{
  "data": [
    {
      "id":          "thr_main",
      "name":        "Main Hall",
      "description": "All headline activities",
      "icon_name":   "auditorium",
      "color_hex":   "#2ED75A",
      "sort_order":  0
    }
  ]
}

GET /events/{event_id}/registration-waves

List the event's registration waves - the time windows during which activity sign-ups are open.

Scope: program.read

Response

{
  "data": [
    {
      "id":           "wave_a",
      "name":         "Friday morning",
      "starts_at":    "2026-05-10T08:00:00Z",
      "ends_at":      "2026-05-10T12:00:00Z",
      "status":       "scheduled",
      "activity_ids": ["act_001", "act_002", "act_003"]
    }
  ]
}

Statuses: scheduled | started | finished | canceled


GET /events/{event_id}/roles

The event's role catalog. Participants reference roles by id; use this to resolve role names if you cache participants.

Scope: event.read

Response

{
  "data": [
    { "id": "role_attendee",  "name": "Attendee",  "description": "..." },
    { "id": "role_organizer", "name": "Organizer", "description": "..." }
  ]
}

User endpoints (user token)

GET /me/profile

Read the signed-in participant's basic profile.

Scope: profile.read

Response

{
  "user_id":      "usr_def456",
  "display_name": "Sample Name",
  "email":        "sample@example.com",
  "locale":       "pl",
  "event_id":     "evt_abc123"
}

email is included if the participant granted profile.read. The scope is per-participant and may be declined on the consent screen.

Email verification state is not part of the v1 contract. If an email is present, treat it as user-provided profile data, not as proof that the address was verified.

event.attendance does not unlock a user-token endpoint

event.attendance discloses only attendance confirmation - no application data, no email, no roles. The existence of an issued user token IS the confirmation. There is no /me/application endpoint in v1. Integrations that need participant application state (status, form responses, review action) read it from the installation-token endpoint GET /events/{id}/participants (scope: participants.read) and correlate by user_id. Combining the two token types is the v1 design.


Developer and OAuth management endpoints

Two auth models exist here:

  • POST /oauth/revoke uses your integration's client_id + client_secret (HTTP Basic auth).
  • The /developer/integrations/... endpoints use your authenticated publisher session in Revento's integration-management tools. They are not integration bearer-token endpoints.

POST /oauth/revoke

Revoke a token (RFC 7009).

Request

POST /oauth/revoke HTTP/1.1
Host: auth.revento.app
Content-Type: application/x-www-form-urlencoded
Authorization: Basic {base64(client_id:client_secret)}

token={the access or refresh token to revoke}
&token_type_hint=access_token

token_type_hint is optional; values are access_token or refresh_token.

Response

Always 200 OK with empty body, including for unknown tokens (prevents information disclosure about token validity).

Revoking an access token invalidates only that token. Revoking a refresh token invalidates the entire token family.

POST /developer/integrations/{integration_id}/rotate-client-secret

Generate a new OAuth client_secret for your integration. The current and previous client secret remain valid at /oauth/token for a 24-hour overlap window so you can roll the new secret through your fleet without downtime.

Auth: authenticated publisher session

Request: empty body.

Response

{
  "client_secret":              "rev_client_secret_new_xyz789...",
  "previous_secret_expires_at": "2026-05-10T14:30:00Z"
}

The new secret is shown once in this response. Store it securely before leaving the page or closing the flow that initiated the rotation.

Operational notes:

  • Rate-limited to 3 rotations per 24 hours per integration.
  • The management flow may require a fresh re-authentication step before completing the rotation.

POST /developer/integrations/{integration_id}/rotate-webhook-secret

Generate a new HMAC signing secret for your integration's webhook deliveries.

Auth: authenticated publisher session

Request: empty body.

Response

{
  "secret":                     "whsec_newSecret_xyz789...",
  "previous_secret_expires_at": "2026-05-10T14:30:00Z"
}

The new secret is shown once in this response. Store it before navigating away.

During the 24-hour overlap, deliveries carry:

X-Revento-Signature:           sha256={signature with new secret}
X-Revento-Signature-Previous:  sha256={signature with previous secret}

Note on the subscription model: your integration has a single webhook URL and a single signing secret, declared in your integration manifest at registration time. There are no per-event-type subscription objects to manage - when your manifest declares an event type, you receive deliveries for it automatically as soon as you have an installation token that's authorized for the matching event.

If you need to change which event types you receive: update your integration manifest. If you need to change your webhook URL: update your manifest. Manifest edits don't trigger re-consent unless they add scopes.

Operational notes:

  • Rate-limited to 3 rotations per 24 hours per integration.

POST /developer/integrations/{integration_id}/send-test-webhook

Send one representative signed webhook to your configured webhook_url without waiting for a real event.

Auth: authenticated publisher session

Request

{
  "event_type": "application.approved"
}

event_type must be one of the event types already declared in your manifest.

Response

{
  "status_code":            200,
  "response_time_ms":       184,
  "response_body_preview":  "{\"ok\":true}",
  "signature_header_value": "sha256=8e1c4b..."
}

Operational notes:

  • Rate-limited to 10 test deliveries per hour per integration.
  • Uses the same signing and transport pipeline as normal webhook delivery.
  • Truncates response_body_preview to 1 KB.
  • Runs out of band from the normal queue: no delivery queue row, no retries, no replay history.

API versioning

Version Status
v1 Current

Use the Accept header to pin a version: Accept: application/vnd.revento.v1+json. Calls without an Accept header default to the current version. Pin the header in production so a version transition can't silently change the response shape.

Any future version is supported alongside the previous one for at least 6 months after announcement, with the deprecation surfaced both in the Changelog and in Revento's integration-management notices.