Skip to main content

Overview

The Attendees API allows you to manage attendees for your events, including creating, updating, and deleting attendee records. Each attendee is assigned a unique 8-character ID for check-in purposes.

The Attendee Object

id
string
required
Unique UUID identifier for the attendee
event_id
string
required
UUID of the event this attendee belongs to
name
string
required
Attendee’s full name (max 200 characters)
email
string
required
Attendee’s email address
unique_id
string
required
Unique 8-character alphanumeric ID for check-in (e.g., “A3F7K9M2”)
checked_in
boolean
default:"false"
Whether the attendee has checked in
checked_in_at
string | null
Timestamp when the attendee checked in (ISO 8601 format)
checkin_method
string | null
Method used for check-in: manual, qr_scan, or self_service
portal_active
boolean
default:"false"
Whether the attendee portal is active for this attendee
confirmation_email_sent
boolean
default:"false"
Whether a confirmation email has been sent
custom_fields
json | null
Object containing custom field values (e.g., {"Department": "Engineering", "Seat": "A12"})
created_at
string
Timestamp when the attendee was created
updated_at
string
Timestamp when the attendee was last updated

List Attendees

Retrieve all attendees for an event:
const { data: attendees, error } = await supabase
  .from('attendees')
  .select('*')
  .eq('event_id', eventId)
  .order('created_at', { ascending: false })

if (error) {
  console.error('Error fetching attendees:', error)
} else {
  console.log('Attendees:', attendees)
}

Response

[
  {
    "id": "456e7890-e89b-12d3-a456-426614174001",
    "event_id": "123e4567-e89b-12d3-a456-426614174000",
    "name": "Jane Smith",
    "email": "[email protected]",
    "unique_id": "K7M9P2Q5",
    "checked_in": false,
    "checked_in_at": null,
    "checkin_method": null,
    "portal_active": true,
    "confirmation_email_sent": true,
    "custom_fields": {
      "Department": "Engineering",
      "Seat": "A12"
    },
    "created_at": "2026-03-01T10:30:00Z",
    "updated_at": "2026-03-01T10:30:00Z"
  }
]

Get Attendee

Retrieve a single attendee by ID:
const { data: attendee, error } = await supabase
  .from('attendees')
  .select('*')
  .eq('id', attendeeId)
  .single()

if (error) {
  console.error('Error fetching attendee:', error)
} else {
  console.log('Attendee:', attendee)
}

Create Attendee

Add a new attendee to an event (requires 1 attendee token):
// Generate unique ID
function generateUniqueId() {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
  let result = ''
  for (let i = 0; i < 8; i++) {
    result += chars[Math.floor(Math.random() * chars.length)]
  }
  return result
}

// Deduct token
const { data: deducted, error: tokenError } = await supabase.rpc(
  'deduct_attendee_tokens',
  {
    _organization_id: organizationId,
    _count: 1
  }
)

if (tokenError || !deducted) {
  console.error('Insufficient attendee tokens')
  return
}

// Create attendee
const { data: attendee, error } = await supabase
  .from('attendees')
  .insert({
    event_id: eventId,
    name: 'John Doe',
    email: '[email protected]',
    unique_id: generateUniqueId(),
    custom_fields: {
      Department: 'Sales',
      Seat: 'B5'
    }
  })
  .select()
  .single()

if (error) {
  console.error('Error creating attendee:', error)
} else {
  console.log('Attendee created:', attendee)
}

Parameters

Update Attendee

Update attendee information:
const { data: attendee, error } = await supabase
  .from('attendees')
  .update({
    name: 'Jane Smith-Doe',
    email: '[email protected]',
    custom_fields: {
      Department: 'Engineering',
      Seat: 'A15'
    }
  })
  .eq('id', attendeeId)
  .select()
  .single()

if (error) {
  console.error('Error updating attendee:', error)
} else {
  console.log('Attendee updated:', attendee)
}

Delete Attendee

Delete an attendee:
const { error } = await supabase
  .from('attendees')
  .delete()
  .eq('id', attendeeId)

if (error) {
  console.error('Error deleting attendee:', error)
} else {
  console.log('Attendee deleted successfully')
}
Deleting an attendee does NOT automatically return attendee tokens. Tokens are only returned when the entire event is deleted.

Toggle Portal Access

Enable or disable portal access for an attendee:
const { error } = await supabase
  .from('attendees')
  .update({ portal_active: true })
  .eq('id', attendeeId)

if (error) {
  console.error('Error toggling portal:', error)
} else {
  console.log('Portal access updated')
}

Search Attendees

Search attendees by name, email, or unique ID:
const searchTerm = 'john'

const { data: results } = await supabase
  .from('attendees')
  .select('*')
  .eq('event_id', eventId)
  .or(`unique_id.ilike.%${searchTerm}%,email.ilike.%${searchTerm}%,name.ilike.%${searchTerm}%`)
  .limit(20)

if (results) {
  console.log('Search results:', results)
}

Get Attendee Portal Data

Retrieve attendee data for the public portal (no authentication required):
const { data: portalData } = await supabase.rpc(
  'get_attendee_portal_data',
  {
    _event_id: eventId,
    _unique_id: 'K7M9P2Q5'
  }
)

if (portalData && portalData.length > 0) {
  const attendee = portalData[0]
  console.log('Portal data:', attendee)
}

Response

id
string
Attendee UUID
name
string
Attendee name
email
string
Attendee email
unique_id
string
Unique check-in ID
checked_in
boolean
Check-in status
portal_active
boolean
Portal access status
custom_fields
json
Custom field values

Filter Attendees

By Check-in Status

// Get checked-in attendees
const { data: checkedIn } = await supabase
  .from('attendees')
  .select('*')
  .eq('event_id', eventId)
  .eq('checked_in', true)

// Get pending attendees
const { data: pending } = await supabase
  .from('attendees')
  .select('*')
  .eq('event_id', eventId)
  .eq('checked_in', false)

By Email Status

const { data: emailSent } = await supabase
  .from('attendees')
  .select('*')
  .eq('event_id', eventId)
  .eq('confirmation_email_sent', true)

By Portal Access

const { data: activePortals } = await supabase
  .from('attendees')
  .select('*')
  .eq('event_id', eventId)
  .eq('portal_active', true)

Export Attendees

Export attendee data for reporting:
// Get all attendees with check-in data
const { data: attendees } = await supabase
  .from('attendees')
  .select('name, email, unique_id, checked_in, checked_in_at, checkin_method, portal_active, custom_fields')
  .eq('event_id', eventId)
  .order('created_at', { ascending: false })

// Format for export
const exportData = attendees?.map(a => ({
  Name: a.name,
  Email: a.email,
  'Unique ID': a.unique_id,
  'Checked In': a.checked_in ? 'Yes' : 'No',
  'Check-In Time': a.checked_in_at || '',
  'Check-In Method': a.checkin_method || '',
  'Portal Active': a.portal_active ? 'Yes' : 'No',
  ...a.custom_fields
}))

console.log('Export data:', exportData)

Validation

Always validate attendee data before insertion:
  • Name: 1-200 characters, no HTML tags
  • Email: Valid email format, max 255 characters
  • Unique ID: 8 alphanumeric characters, unique per event
  • Custom fields: Max 50 characters per field name, max 500 characters per value
function validateAttendee(data: any): string[] {
  const errors: string[] = []
  
  if (!data.name || data.name.length > 200) {
    errors.push('Invalid name')
  }
  
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
  if (!data.email || !emailRegex.test(data.email)) {
    errors.push('Invalid email')
  }
  
  if (!data.unique_id || data.unique_id.length !== 8) {
    errors.push('Invalid unique ID')
  }
  
  return errors
}

Next Steps

Check-ins API

Check in attendees at your event

Email API

Send confirmation emails to attendees

Build docs developers (and LLMs) love