uidudevelopers
Domains

Events

Fetch event listings, event details, and submit RSVPs (attendance) against the uidu Events API.

  • Use it for: Public website · Server integration
  • Reads: public token — works in the browser
  • Writes: Bearer (apiKey) — server-side only
  • CLI: uidu events, uidu attendances
  • Auth: what each token can do and where it may run — Authentication

Events are what your workspace runs in time — a workshop, a gala, a weekly class. You list them and show their details with the public token, and record RSVPs (attendances) from your server.

Mental model

  • Event — the metadata: name, description, body, cover, published, publicPath / publicUrl, isPaidEvent, hasSeats, currentCapacity, a primaryAddress (formattedAddress, name, city, postalCode, countryCode, latitude, longitude), and ready-made calendar links: toGoogleCalendarUrl and toIcsUrl (an .ics download).
  • EventInstance — one occurrence in time: id, beginsAt, finishesAt (ISO timestamps). An event has one or more. event.instance is the next or current one — what most pages show — and event.instances is all of them, for a series view or a date picker. Only getEvent returns instances (and instance.endDate / endTime); listEvents summaries carry just instance.
  • Attendance — a contact's RSVP, attached to an instance, never to the event: its attendableId is an EventInstance.id. An event with several dates has several instances, each attended independently. An attendance comes back with id, checkedIn and contact.id.

Shapes and gotchas:

  • Passing the event id as attendableId is the classic mistake — always take it from event.instance.id or one of event.instances[].id.
  • beginsAt / finishesAt are typed loosely (ISO8601 scalars come through as any); parse them with new Date(...) before comparing or formatting.
  • listEvents has no date filter: it returns the workspace's events (first/after page through them, see Pagination). Filter and sort on instance.beginsAt yourself.
  • createAttendance and createEvent return the payload as-is — { errors, attendance } and { errors, event }, or null; validation failures come back in errors, not as a throw. Both need the apiKey client, so they run on your server or from the CLI (uidu attendances create, uidu events create), never with the public token in the browser. See Writes and Authentication.

Use cases

Upcoming events page

Goal: a page listing the events that haven't started yet, soonest first.

// app/events/page.tsx — a Server Component
import Link from 'next/link';
import { listEvents } from '@uidu/client';
import { uidu } from '@/lib/uidu';

export default async function EventsPage() {
  const events = await listEvents(uidu, { first: 50 });
  const now = Date.now();

  const upcoming = events
    .filter(
      (e) =>
        e.instance?.beginsAt && new Date(e.instance.beginsAt).getTime() > now,
    )
    .sort(
      (a, b) =>
        new Date(a.instance!.beginsAt).getTime() -
        new Date(b.instance!.beginsAt).getTime(),
    );

  return (
    <ul>
      {upcoming.map((event) => (
        <li key={event.id}>
          <Link href={`/event/${encodeURIComponent(event.id)}`}>
            {event.name}
          </Link>
          <time>{new Date(event.instance!.beginsAt).toLocaleDateString()}</time>
          {event.primaryAddress?.city && (
            <span> · {event.primaryAddress.city}</span>
          )}
        </li>
      ))}
    </ul>
  );
}
  • Each summary has id, name, description, cover, published, publicPath, isPaidEvent, primaryAddress (formattedAddress, city) and instance (id, beginsAt, finishesAt). An event with no upcoming occurrence has no instance to show.
  • Put the detail route under a path segment (/event/[id]), not a root /[id] that would also catch /favicon.ico.
  • Need every event, for a sitemap or generateStaticParams? Use paginate with ListEventsDocument — Pagination & field values.

Event page with RSVP

Goal: show one event with its dates and calendar links, and let a visitor RSVP for a date.

// app/event/[id]/page.tsx
import { createAttendance, getEvent } from '@uidu/client';
import { notFound } from 'next/navigation';
import { uidu } from '@/lib/uidu'; // createClient({ workspace, publicToken, apiKey })

export default async function EventPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  const event = await getEvent(uidu, { id: decodeURIComponent(id) });
  if (!event) notFound();

  async function rsvp(formData: FormData) {
    'use server';
    const result = await createAttendance(uidu, {
      input: {
        attributes: {
          attendableId: String(formData.get('instanceId')), // an INSTANCE id, not event.id
          contactAttributes: {
            email: String(formData.get('email')),
            contactableAttributes: {
              kind: 'person',
              firstName: String(formData.get('firstName')),
            },
          },
        },
      },
    });
    if (result?.errors?.length) {
      // validation failure: return result.errors to the form
      return;
    }
    // result?.attendance?.id, result?.attendance?.contact?.id
  }

  return (
    <article>
      <h1>{event.name}</h1>
      <p>{event.primaryAddress?.formattedAddress}</p>
      <a href={event.toGoogleCalendarUrl ?? undefined}>
        Add to Google Calendar
      </a>
      <a href={event.toIcsUrl ?? undefined}>Download .ics</a>

      <form action={rsvp}>
        <select name="instanceId" defaultValue={event.instance?.id}>
          {(event.instances ?? []).map((i) => (
            <option key={i.id} value={i.id}>
              {new Date(i.beginsAt).toLocaleString()}
            </option>
          ))}
        </select>
        <input name="firstName" required />
        <input name="email" type="email" required />
        <button>RSVP</button>
      </form>
    </article>
  );
}
  • For a single-date event, skip the picker and send event.instance.id.
  • AttendanceAttributes beyond the instance: contactAttributes creates the attendee in the same mutation, or contactId attaches an existing contact instead; formResponseAttributes carries extra answers (dietary needs…) in the shape described in Forms — every content is { value }; rsvpStatus is pending, accepted, declined or tentative.
  • isPaidEvent, hasSeats and currentCapacity tell you whether a plain RSVP fits the event; paid tickets go through uidu's checkout, which this domain doesn't wrap.
  • The demo's version, with error handling, is submitRsvp in apps/demo/src/lib/actions.ts.

Publish an event from your server

Goal: create an event with its first date from a script or an admin tool.

import { createEvent, updateEvent } from '@uidu/client';
import { uidu } from '@/lib/uidu'; // must carry apiKey

const created = await createEvent(uidu, {
  input: {
    attributes: {
      name: 'Launch party',
      eventInstancesAttributes: [
        {
          startDate: '2026-11-20',
          startTime: '19:00',
          endDate: '2026-11-20',
          endTime: '23:00',
        },
      ],
    },
  },
});
if (created?.errors?.length)
  throw new Error(created.errors.map((e) => e.name).join(', '));

const id = created?.event?.id;
if (id)
  await updateEvent(uidu, { input: { id, attributes: { hasSeats: true } } });
  • Dates are written per instance as startDate / endDate (YYYY-MM-DD) and startTime / endTime (HH:MM); an instance without endTime lasts one hour. Reads give you the combined beginsAt / finishesAt instead.
  • EventAttributes also takes body (rich text), cover, addressesAttributes, eventTagList, preferences — the full list is in Schema. The payload returns the new event's id and name.
  • The same from a terminal: uidu events create, uidu events update — CLI. Events can't be deleted through the SDK or the CLI.

API at a glance

Every @uidu/client function in Events, generated from the source — the full reference has the rest of the SDK.

FunctionKindAuthCLIDescription
createAttendancewriteBearerattendances createRSVP a contact to an event: attendableId is an event instance id, never the event id.
createEventwriteBearerevents createCreate an event; returns the payload as-is, { errors, event }, validation failures included.
getEventreadpublic tokenevents getOne event with its primary address, its next or current instance and all its instances, or null.
listEventsreadpublic tokenevents listThe workspace's events as a flat array of summaries, each with its next or current instance; page with first/after.
updateEventwriteBearerevents updateUpdate an event: { input: { id, attributes } } (create it with createEvent). Returns the payload (with errors) or null.

Types: Event

CLI:

Entitylistgetcreateupdatedelete
events✓✓✓✓—
attendances——✓——

On this page