app.zeens.event

An event: a photowalk, workshop, meetup, exhibition or talk. Written to the organiser's own PDS. Others RSVP via app.zeens.rsvp (ATProto handle) or via an email address tracked off-protocol in PocketBase. The app does NOT collect payment: paid events carry free-text paymentInstructions and any URL/link in that field is rejected at write time. Carries the app.zeens.v marker so firehose + Lua filters recognise it. NOTE: the record id stays 'app.zeens.event' and the app.zeens.* namespace is network plumbing — it is not renamed when the product is. Two orthogonal axes describe an event: `mode` (WHERE it happens — in person, online, or both) and `categories` (WHAT it is — one or more of photowalk/workshop/meetup/exhibition/talk/other). The `hostedBy` field is DEPRECATED (the organiser is the record owner); kept for back-compat with old records.

Repository @zeens.app did:plc:pwpuqwge6g7xrjmfbw73uycw namespace authority
Details · record, key tid
Kindrecord
Record keytid
Authorityzeens.app
Indexed

Record

record · key tid

Properties

app.zeens.v

requiredinteger

Zeens version marker (always 1).

When true, an RSVP is pending until the organiser approves it; only approved RSVPs count toward the cap and the attendee list. Approval state is tracked off-protocol (the organiser cannot mutate another user's rsvp record). Default false.

Behaviour when maxParticipants is reached. 'hard' = block new going RSVPs. 'waitlist' = overflow goes on a waitlist, promoted in RSVP order. Only meaningful when maxParticipants is set. Default 'hard'.

  • knownValues: hard | waitlist

categories

requiredarray

WHAT the event is. One or more (max 3) — an event can be both a photowalk and a meetup. Drives browse/filter, which OR-matches any element. Orthogonal to `mode`. The legacy value 'online_meetup' is tolerated on read and means mode 'online' + category 'meetup'.

  • minLength: 1
  • maxLength: 3
itemsstring
  • knownValues: photowalk | workshop | meetup | exhibition | talk | other

Wide cover image shown on the event header and share previews.

  • maxSize: 4000000
  • accept: image/jpeg, image/png, image/webp

createdAt

requiredstring · datetime

createdBy

string

Display text for the event creator/organiser (the account that owns this record).

  • maxLength: 200

Long-form event description.

  • maxLength: 3000

endsAt

string · datetime

Optional event end instant.

facets

array

Rich-text facets (links, mentions) over the description, reusing the Bluesky richtext lexicon.

itemsref

hostedBy

string

DEPRECATED. Display text for who is hosting. The organiser is the record owner, so this is no longer written; kept so old records still read.

  • maxLength: 200

Ordered agenda for the event: a list of items, each with optional time, descriptive text, and an optional location. Works for any event type — a photowalk's route, a workshop's schedule, an exhibition's programme. Items that carry coordinates are drawn on the event map as numbered pins in order; an event whose items have no coordinates simply renders the list with no map.

  • maxLength: 30
itemsref
Reference

Physical location. Expected for mode 'in_person' and 'hybrid'; absent for 'online'.

Reference

Optional cap on confirmed (going) participants. Absent = unlimited.

  • minimum: 1

mode

requiredstring

WHERE the event happens, and one of exactly three values. 'in_person' needs a location; 'online' needs a virtualUrl and has no location; 'hybrid' needs both. Orthogonal to `categories` — an online workshop is mode 'online' + categories ['workshop']. Records written before this field carried meaning default to 'in_person' on read. The legacy value 'virtual' is tolerated on read and means 'online'.

  • knownValues: in_person | online | hybrid

paid

boolean

Whether the event costs money. The app never processes payment.

Free-text explanation of how the organiser collects payment out of band. MUST NOT contain a URL/link; rejected at write time client- and server-side.

  • maxLength: 1000

Photography genre tags (Street, Landscape, Portrait, ...). Up to 3, optional. Drawn from the shared category list used by posts. Distinct from `categories`, which is the event type.

  • maxLength: 3
itemsstring
  • maxLength: 100

rsvpPolicy

requiredstring

Who may RSVP. 'atmosphere' = any ATProto account. 'photosky' = members only. 'anyone_email' = anyone, including non-ATProto people via an email address. Enforcement is soft (obscurity-level), like collection visibility.

  • knownValues: atmosphere | photosky | anyone_email

startsAt

requiredstring · datetime

Event start, as an absolute instant. Render in `timezone`.

timezone

requiredstring

IANA timezone name (e.g. 'America/New_York'). Stored separately from startsAt so calendar export (.ics DTSTART;TZID) and the displayed local time are correct.

  • maxLength: 100

title

requiredstring

Event title.

  • maxLength: 300
  • maxGraphemes: 150

virtualUrl

string · uri

Join link for events with mode 'online' or 'hybrid'. Required for those modes. Shown only to RSVP'd users in the client.

#itineraryItem

object

Properties

Optional location for this item. Only meaningful for events with mode 'in_person' or 'hybrid'. Items with lat/lng are plotted on the event map.

Reference

text

requiredstring

What happens at this point in the event (e.g. 'Meet at the park entrance', 'Darkroom demo', 'Q&A with the curator').

  • maxLength: 500

time

string

Free-text time label for this item (e.g. '9:00 AM', 'Lunch'). Stored as text, not a datetime, so it renders verbatim without timezone handling.

  • maxLength: 50

#location

object

Properties

address

string

Human-readable street address.

  • maxLength: 500

lat

integer

Latitude in microdegrees (degrees x 1e7). ATProto rejects non-integer numbers, so coordinates are scaled to integers.

lng

integer

Longitude in microdegrees (degrees x 1e7).

name

string

Venue / place name.

  • maxLength: 200

Similar lexicons

semantic match

Lexicons whose schemas describe something close to this one. Check them before publishing a new one that overlaps.