Overview

The Events app stores event data in two HubSpot custom CRM objects: events and event_registrations. The worksby.design API provides endpoints for listing events, managing registrations, and checking attendees in. Events themselves are created and edited directly in HubSpot CRM (not via this API) — see Managing events below.

Base URL: https://api.worksby.design/apps/events/

All responses are JSON. Successful responses include "success": true. Errors include "error": "<message>" and an appropriate HTTP status code.

For AI agents: the most common workflow is: call fetch-events to list what's available, call fetch-event-details to get attendee counts and details, then call create-registration to register someone. Each call requires portalId — always include it.

Authentication

All endpoints except GET /license-status require the portal to have the Events app installed and a valid active license. Authentication is handled automatically — no API key is passed by the caller.

Every request must include portalId — the numeric HubSpot portal ID. For GET requests pass it as a query parameter. For POST requests include it in the JSON body.

The server validates that the portal has completed the OAuth install flow and holds a stored access token. If not, the endpoint returns 403 with { "error": "Portal <id> not authorised for events" }. If the license has expired it returns 403 with { "code": "LICENSE_INACTIVE" }.

Custom Object — events

Created automatically on first install. The name property is unique and acts as the primary identifier across all API calls and navigation links. Object type name: events. The numeric objectTypeId is portal-specific and resolved by the backend at runtime — callers never need it.

events — all properties

PropertyTypeDescription
namestring · uniqueEvent name. Must be unique per portal. Used as the primary event identifier for URL routing and in fetch-event-details name lookups. Cannot be duplicated.
url_slugstringLegacy URL identifier. Also accepted by fetch-event-details as a lookup key (tried before name). By convention equals a slugified version of name.
descriptionstring · textareaFull event description. Shown on the event detail page.
meta_descriptionstringSEO meta description. Keep under 160 characters. Maps to the HubSpot dynamic page meta description field.
highlightsstring · textareaShort bullet-style summary of event highlights. Shown as a callout on event detail pages.
start_datetimedatetimeEvent start date and time. ISO 8601 format (2026-06-15T09:00:00.000Z). Used as the sort key in fetch-events and the upcoming-only filter.
end_datetimedatetimeEvent end date and time. ISO 8601 format.
event_typestringFree-text event category. Common values: conference, webinar, workshop, meetup.
event_status enumeration Controls visibility. Only published events are returned by fetch-events.
draft published cancelled
event_tagsstringFree-text tags for filtering. Semicolon-separated by convention (e.g. training;certification;online).
image_featuredstring · URLFull URL to the hero/featured image. Also maps to the HubSpot dynamic page featured image field.
image_thumbnailstring · URLFull URL to the event card thumbnail image.
video_featuredstring · URLURL to a featured video (embed URL or direct link). Optional.
is_online boolean Whether the event is online-only.
truefalse
location_namestringVenue name or platform name for online events (e.g. Convention Center Amsterdam, Online via Zoom).
street_addressstringStreet address of the venue. Leave blank for online events.
citystringCity. Leave blank for online events.
postal_codestringPostal/ZIP code.
countrystringCountry name in full (e.g. Netherlands, Belgium).
location_map_typestringMap provider hint (e.g. google). Optional.
location_map_urlstring · URLEmbed URL for an interactive map.
location_image_mapstring · URLURL to a static map image.
price_amountnumberTicket price. Use 0 for free events.
price_currencystringISO 4217 currency code (e.g. EUR, USD).
payment_linkstring · URLExternal payment or booking URL (e.g. Stripe, Eventbrite). Optional.
ticket_typesstring · textareaAvailable ticket type names, one per line or semicolon-separated (e.g. standard;vip;student). Used to populate the ticket type selector on the registration form. The ticket_type field on a registration should match one of these values.
capacity_totalnumberMaximum number of attendees. Set to 0 for unlimited. create-registration blocks new registrations when capacity_current >= capacity_total (and capacity_total > 0).
capacity_currentnumberCurrent registration count. Incremented automatically by create-registration. Starts at 0.
featuresstringSemicolon-separated list of feature/amenity codes shown as icons on event pages. Common values: wifi, parking, food_drinks, networking, certificate, recording, accessible.
contact_emailstringOrganiser contact email address. Shown on the event detail page.
contact_phonestringOrganiser contact phone number.
external_urlstring · URLLink to an external event page or additional information. Optional.
registration_modestringHow registrations are collected. Common value: form (inline registration form). Optional — used by the front-end to decide which registration UI to show.
requires_membership boolean Whether the event is restricted to members only.
truefalse
meeting_platformstringOnline meeting platform name (e.g. Zoom, Microsoft Teams). Shown when is_online is true.
online_meeting_urlstring · URLDirect join URL for the online meeting. Typically shared only after registration.
associated_projectsstringInternal field — links the event to a HubSpot Projects record by numeric ID. Events with a value here are hidden when hideProjectEvents: true is passed to fetch-events.
training_typestringOptional classification for training events (e.g. certification, induction). Used by portal-specific filtering logic.

Custom Object — event_registrations

One record per attendee per event. Created by create-registration. Each registration is associated to its event and (optionally) to a HubSpot contact.

event_registrations — all properties

PropertyTypeDescription
attendee_namestringFull name (firstName + ' ' + lastName). Set automatically by create-registration. Can be edited via update-registration.
first_namestringGiven name.
last_namestringFamily name.
emailstringEmail address. Used to match or create a HubSpot contact during registration. Editable via update-registration.
phonestringPhone number. Editable via update-registration.
companystringCompany name. Editable via update-registration.
job_titlestringJob title. Editable via update-registration.
registration_datedatetimeISO 8601 timestamp of when the registration was created. Set automatically.
registration_status enumeration Current status of the registration. checked_in is written programmatically by check-in-by-qr — it is not a declared enum option in the schema but works in HubSpot because the property is a text field under the hood.
confirmed waitlisted cancelled checked_in
ticket_typestringTicket type chosen at registration. Defaults to standard if not specified. Should match a value from the event's ticket_types property.
qr_codestringUnique QR check-in code generated at registration time. Format: EVT-XXXXXXXXXXXX (16 chars, uppercase alphanumeric). Used by check-in-by-qr.
check_in_datetimedatetimeISO 8601 timestamp of when the attendee was checked in. Set by check-in-by-qr when action: 'checkin'.
payment_status enumeration Payment state. check-in-by-qr grants access when status is free, paid, or complimentary. free and complimentary are non-schema values used programmatically.
pending paid refunded free complimentary
amount_paidnumberAmount paid in the event's currency. Optional — use for paid registrations.
special_requirementsstring · textareaDietary, accessibility, or other special requirements. Shown on the event attendee list.

Associations

Three association labels are created automatically on install. Label names are stable contracts — the backend resolves portal-specific numeric type IDs at runtime by matching on the label name. Do not rename these labels in HubSpot.

FromToLabelInverse label
eventsevent_registrationsEvent RegistrationsEvent
contactsevent_registrationsRegistered AttendeeEvent Registration
contactseventsRegisteredRegistered Contact
create-registration creates all three associations automatically. You do not need to create them manually when using this API.

Endpoints

GET /apps/events/license-status

Check whether a portal has an active Events license. Does not require OAuth install — safe to call before the install flow.

Query parameters

ParameterTypeDescription
portalIdrequiredstringHubSpot portal ID.

Response

{ "licensed": true }
POST /apps/events/fetch-events

Return a list of published events, sorted by start_datetime ascending. By default returns only upcoming events (start date in the future).

Request body

FieldTypeDescription
portalIdrequiredstringHubSpot portal ID.
upcomingOnlybooleanFilter to events whose start_datetime is in the future. Default: true. Pass false to include past events.
hideProjectEventsbooleanIf true, exclude events that have a value in associated_projects (used to hide internal project-linked events from public listings). Default: false.

Response

{
  "success": true,
  "total": 3,
  "events": [
    {
      "id": "12345678",
      "name": "Monthly Member Webinar",
      "url_slug": "monthly-member-webinar",
      "start_datetime": "2026-06-15T14:00:00.000Z",
      "end_datetime": "2026-06-15T16:00:00.000Z",
      "event_type": "webinar",
      "event_status": "published",
      "event_tags": "online;members",
      "is_online": "true",
      "location_name": "Online via Microsoft Teams",
      "city": null,
      "country": null,
      "price_amount": "0",
      "price_currency": "EUR",
      "capacity_total": "500",
      "capacity_current": "12",
      "image_featured": "https://images.unsplash.com/...",
      "image_thumbnail": "https://images.unsplash.com/..."
    }
  ]
}
Each event in the array contains all events object properties. Use id when you need to reference this event in other HubSpot CRM API calls; use name when calling fetch-event-details.
POST /apps/events/fetch-event-details

Fetch full details for a single event including all registered attendees. Looks up the event by url_slug, then name (exact match), then name (partial match) — the first match wins.

Request body

FieldTypeDescription
portalIdrequiredstringHubSpot portal ID.
eventIdrequiredstringEvent identifier — can be the url_slug, the exact name, or a partial name. The backend tries each lookup in order until a match is found.
isNameLookupbooleanPass true when eventId is a decoded event name from a URL path segment. This skips the url_slug lookup and saves one HubSpot API call. Default: false.
contactIdstringOptional HubSpot contact ID. Reserved for future use — currently unused by the backend but can be passed without error.

Response

{
  "success": true,
  "event": {
    "id": "12345678",
    "name": "Monthly Member Webinar",
    "description": "Our regular online gathering...",
    "start_datetime": "2026-06-15T14:00:00.000Z",
    "capacity_total": "500",
    "capacity_current": "12",
    ...all event properties...
  },
  "registrations": [
    {
      "id": "98765432",
      "attendee_name": "Sarah Johnson",
      "email": "sarah.johnson@example.com",
      "registration_status": "confirmed",
      "payment_status": "paid",
      "qr_code": "EVT-ABC123DEF456",
      "check_in_datetime": null,
      ...all registration properties...
    }
  ]
}
POST /apps/events/fetch-user-registrations

Find all event IDs that a specific contact is registered for. Matches by contact email address via the event_registrations object.

Request body

FieldTypeDescription
portalIdrequiredstringHubSpot portal ID.
contactIdrequiredstringHubSpot contact ID. The backend fetches the contact's email and uses it to search registrations.

Response

{
  "success": true,
  "eventIds": ["12345678", "87654321"]
}
Returns an array of event IDs (not names or slugs). Cross-reference these with the id field returned by fetch-events to determine which events a contact has already registered for.
POST /apps/events/create-registration

Register an attendee for an event. Creates the event_registrations record, creates or updates the HubSpot contact, and creates all three association links (event → registration, contact → registration, contact → event). Increments capacity_current on the event.

Request body

FieldTypeDescription
portalIdrequiredstringHubSpot portal ID.
eventIdrequiredstringEvent identifier — numeric ID from fetch-events, or a url_slug string. Non-numeric values trigger a slug lookup first.
formDatarequiredobjectAttendee details. See sub-fields below.
contactIdstringOptional HubSpot contact ID. If provided, the contact is updated with the form data rather than searched by email. If omitted, the backend searches by email and creates the contact if not found.

formData fields

FieldTypeDescription
firstNamerequiredstringAttendee first name.
lastNamerequiredstringAttendee last name.
emailrequiredstringAttendee email address. Used to find or create the HubSpot contact.
phonestringPhone number.
companystringCompany name.
jobTitlestringJob title.
ticketTypestringTicket type. Defaults to standard if not provided. Should match a value from the event's ticket_types field.
specialRequirementsstringDietary, accessibility, or other special requirements.

Response

{
  "success": true,
  "registration": {
    "id": "98765432",
    "qr_code": "EVT-ABC123DEF456",
    "email": "sarah.johnson@example.com"
  }
}
Capacity check: if capacity_total > 0 and capacity_current >= capacity_total, the registration is rejected with 400 { "error": "Event is at full capacity" }. Check capacity before attempting to register.
POST /apps/events/update-registration

Update a single editable property on an existing registration. Used for inline attendee detail corrections.

Request body

FieldTypeDescription
portalIdrequiredstringHubSpot portal ID.
registrationIdrequiredstringNumeric ID of the event_registrations record.
propertyNamerequiredstring Property to update. Only these five values are accepted:
attendee_name job_title company email phone
propertyValuestringNew value. Pass an empty string to clear the field.

Response

{ "success": true }
POST /apps/events/check-in-by-qr

Look up a registration by QR code and optionally check the attendee in. Returns an access decision that drives the check-in UI: grant entry, deny, flag as already checked in, or report an error.

Request body

FieldTypeDescription
portalIdrequiredstringHubSpot portal ID.
qrCoderequiredstringQR code value from the registration. Format: EVT-XXXXXXXXXXXX.
actionstringPass "checkin" to update the registration to checked_in and record the timestamp. Omit (or any other value) to perform a read-only verification without mutating the record.

Access decisions

DecisionMeaningWhen
GRANTEntry permittedregistration_status is confirmed AND payment_status is free, paid, or complimentary
DENYEntry refusedregistration_status is confirmed but payment not cleared, or registration_status is waitlisted or cancelled
ALREADY_INAlready checked inregistration_status is checked_in
ERRORQR code not foundNo registration matches the provided QR code (404 response)

Response

{
  "success": true,
  "accessDecision": "GRANT",
  "checkedIn": true,
  "registration": {
    "id": "98765432",
    "attendeeName": "Sarah Johnson",
    "firstName": "Sarah",
    "lastName": "Johnson",
    "email": "sarah.johnson@example.com",
    "company": "Acme Corp",
    "jobTitle": "Product Manager",
    "ticketType": "standard",
    "registrationStatus": "checked_in",
    "paymentStatus": "paid",
    "checkInDatetime": "2026-06-15T09:32:00.000Z",
    "specialRequirements": null
  },
  "event": {
    "name": "Annual Member Conference",
    "startDatetime": "2026-06-15T09:00:00.000Z"
  }
}
Note: checkedIn is true only when action: "checkin" was passed and the decision was GRANT. On DENY or ALREADY_IN, the record is not modified regardless of the action.

Creating and Managing Events

Events are created and edited directly via the HubSpot CRM API — this worksby.design API does not expose a create/update event endpoint. Use the standard HubSpot /crm/v3/objects/{objectTypeId} endpoints with the events custom object.

Resolving the objectTypeId

HubSpot assigns a different numeric objectTypeId to the events custom object on each portal. To resolve it, fetch all schemas and find the one whose name field equals "events":

GET https://api.hubapi.com/crm/v3/schemas
Authorization: Bearer {access_token}

// In the response, find:
results.find(s => s.name === 'events').objectTypeId
// → e.g. "2-12345678"

Creating an event

Once you have the objectTypeId, create an event with a POST to /crm/v3/objects/{objectTypeId}:

POST https://api.hubapi.com/crm/v3/objects/{objectTypeId}
Authorization: Bearer {access_token}
Content-Type: application/json

{
  "properties": {
    "name": "Monthly Member Webinar",
    "url_slug": "monthly-member-webinar",
    "description": "Our regular online gathering for members.",
    "meta_description": "Free monthly online webinar — live panel and open Q&A.",
    "highlights": "Live panel, member updates, exclusive content.",
    "event_type": "webinar",
    "event_status": "published",
    "start_datetime": "2026-07-15T14:00:00.000Z",
    "end_datetime": "2026-07-15T16:00:00.000Z",
    "is_online": "true",
    "location_name": "Online via Microsoft Teams",
    "price_amount": "0",
    "price_currency": "EUR",
    "capacity_total": "500",
    "capacity_current": "0",
    "features": "recording",
    "registration_mode": "form",
    "image_featured": "https://images.unsplash.com/photo-...",
    "image_thumbnail": "https://images.unsplash.com/photo-..."
  }
}

Required properties for a visible event

At minimum, set these properties for an event to appear in fetch-events results:

  • name — must be unique; this is the event identifier
  • event_status: "published" — draft and cancelled events are excluded
  • start_datetime — must be in the future (unless upcomingOnly: false)

Updating an event

PATCH https://api.hubapi.com/crm/v3/objects/{objectTypeId}/{eventId}
Authorization: Bearer {access_token}
Content-Type: application/json

{
  "properties": {
    "event_status": "cancelled"
  }
}
Name uniqueness: the name property has a unique constraint. Attempting to create two events with the same name on the same portal will fail with a HubSpot 409 conflict error. If you need test events, use distinct names.