Skip to main content

Event RSVP

Handle event attendance through RSVP responses, track attendees, and manage user participation in scheduled events.

Overview

The RSVP system allows users to respond to event invitations with two status types:
  • Going: Confirmed attendance
  • Not Going: Declined invitation
Each event tracks RSVP counts and provides APIs to manage responses and query attendees.

RSVP Response Types

StatusDescriptionImpact
GoingUser will attendCounted in rsvpCount, included in attendee list
Not GoingUser won’t attendRecorded but not counted in attendance

Creating an RSVP

RSVP as Going

Task { @MainActor in
    do {
        let event = try await AmityEventRepository.shared.getEvent(id: "event-123")
        let rsvp = try await event.createRSVP(status: .going)
        print("RSVP created: \(rsvp.status)")
    } catch {
        print("Failed to RSVP: \(error)")
    }
}

RSVP as Not Going

let event = try await AmityEventRepository.shared.getEvent(id: "event-123")
let rsvp = try await event.createRSVP(status: .notGoing)

Updating an RSVP

Change existing RSVP response:
Task { @MainActor in
    do {
        let event = try await AmityEventRepository.shared.getEvent(id: "event-123")
        let updatedRSVP = try await event.updateRSVP(status: .notGoing)
        print("RSVP updated to: \(updatedRSVP.status)")
    } catch {
        print("Failed to update RSVP: \(error)")
    }
}

Getting My RSVP

Check current user’s RSVP status for an event:
Task { @MainActor in
    do {
        let event = try await AmityEventRepository.shared.getEvent(id: "event-123")
        let myRSVP = try await event.getMyRSVP()
        print("My RSVP status: \(myRSVP.status)")
        print("Responded at: \(myRSVP.respondedAt)")
    } catch {
        print("Failed to get RSVP: \(error)")
    }
}

Querying Event Attendees

Get All Going Attendees

let event = try await AmityEventRepository.shared.getEvent(id: "event-123")
let attendees = event.getRSVPs(status: .going)

attendees.observe { responses in
    responses.forEach { rsvp in
        print("Attendee: \(rsvp.user.displayName)")
        print("Responded at: \(rsvp.respondedAt)")
    }
    print("Total going: \(responses.count)")
}

Get Not Going Users

let event = try await AmityEventRepository.shared.getEvent(id: "event-123")
let notGoing = event.getRSVPs(status: .notGoing)

notGoing.observe { responses in
    print("Not going count: \(responses.count)")
}

RSVP Data Model

The AmityEventResponse object contains:
interface AmityEventResponse {
  status: AmityEventResponseStatus; // going | not_going
  eventId: string;
  event?: AmityEvent; // Linked event object
  userId: string; // User who RSVP'd
  user?: AmityUser; // Linked user object
  createdAt: string; // ISO 8601 timestamp
  respondedAt: string; // ISO 8601 timestamp
}

Common Use Cases

Display RSVP Status Button

import { EventRepository, AmityEventResponseStatus } from '@amityco/ts-sdk';

// Get current user's RSVP and display appropriate button
const unsubscribe = EventRepository.getEvent(
    eventId,
    async ({ data: event, loading, error }) => {
        if (event && !loading) {
            const myRSVP = await event.getMyRSVP();

            if (!myRSVP) {
                // Show RSVP options
                displayRSVPOptions();
            } else if (myRSVP.status === AmityEventResponseStatus.Going) {
                // Show "Going" button, allow change
                displayGoingButton(myRSVP);
            } else {
                // Show "Not Going" button, allow change
                displayNotGoingButton(myRSVP);
            }
        }
    }
);

Attendee List with Real-Time Updates

import { EventRepository, AmityEventResponseStatus } from '@amityco/ts-sdk';

let rsvpUnsubscribe: (() => void) | undefined;

// Display attendee list that updates in real-time
const unsubscribe = EventRepository.getEvent(
    eventId,
    ({ data: event, loading, error }) => {
        if (event && !loading) {
            rsvpUnsubscribe = event.getRSVPs(
                { status: AmityEventResponseStatus.Going },
                ({ data: responses, loading, error }) => {
                    if (responses) {
                        const attendeeList = responses.map(rsvp => ({
                            userId: rsvp.userId,
                            name: rsvp.user?.displayName,
                            avatar: rsvp.user?.avatar,
                            respondedAt: rsvp.respondedAt
                        }));

                        updateAttendeeUI(attendeeList);
                        updateCountBadge(responses.length);
                    }
                }
            );
        }
    }
);

// Call unsubscribe() and rsvpUnsubscribe() when component unmounts

RSVP Summary

import { EventRepository, AmityEventResponseStatus } from '@amityco/ts-sdk';

let rsvpUnsubscribe: (() => void) | undefined;

// Get comprehensive RSVP summary
const unsubscribe = EventRepository.getEvent(
    eventId,
    ({ data: event, loading, error }) => {
        if (event && !loading) {
            console.log(`RSVP Summary for: ${event.title}`);
            console.log(`Going: ${event.rsvpCount}`);

            // Optionally get detailed attendee list
            rsvpUnsubscribe = event.getRSVPs(
                { status: AmityEventResponseStatus.Going },
                ({ data: responses }) => {
                    if (responses) {
                        console.log("Attendees:", responses.map(r => r.user?.displayName));
                    }
                }
            );
        }
    }
);

Toggle RSVP Status

import { EventRepository, AmityEventResponseStatus } from '@amityco/ts-sdk';

// Toggle between Going and Not Going
function toggleRSVP(eventId: string) {
    const unsubscribe = EventRepository.getEvent(
        eventId,
        async ({ data: event, loading, error }) => {
            if (event && !loading) {
                const myRSVP = await event.getMyRSVP();

                if (!myRSVP || myRSVP.status !== AmityEventResponseStatus.Going) {
                    // Create or update to Going
                    if (!myRSVP) {
                        await event.createRSVP(AmityEventResponseStatus.Going);
                    } else {
                        await event.updateRSVP(AmityEventResponseStatus.Going);
                    }
                    showNotification("You're going to this event!");
                } else {
                    // Update to Not Going
                    await event.updateRSVP(AmityEventResponseStatus.NotGoing);
                    showNotification("RSVP updated");
                }

                // Unsubscribe after operation
                unsubscribe();
            }
        }
    );
}

Best Practices

Maintain UI state based on RSVP status:
// Check RSVP status before showing options
const myRSVP = await event.getMyRSVP();

if (myRSVP) {
    // User has RSVP'd - show update options
    showRSVPUpdateUI(myRSVP.status);
} else {
    // User hasn't RSVP'd - show create options
    showRSVPCreateUI();
}
Subscribe to attendee lists for live updates:
  • Use live collections for attendee lists
  • Update counts automatically
  • Unsubscribe when view is dismissed
  • Handle network disconnections gracefully
Handle RSVP errors appropriately:
try {
    await event.createRSVP(status);
} catch (error) {
    if (error.code === 'ALREADY_EXISTS') {
        // User already RSVP'd, use update instead
        await event.updateRSVP(status);
    } else if (error.code === 'EVENT_ENDED') {
        showError("This event has ended");
    } else {
        showError("Failed to RSVP");
    }
}
Optimize attendee list queries:
  • Implement pagination for large attendee lists
  • Cache frequently viewed lists
  • Load counts separately from full lists
  • Use appropriate query limits

Notifications

RSVP actions may trigger notifications:
ActionNotification Trigger
User RSVPs “Going”Event creator notified of new attendee
User changes to “Not Going”Event creator may be notified
Event updatesAll “Going” attendees notified of changes
Event reminderAll “Going” users notified
Notification Settings: Notification behavior is configurable at the network level through the Admin Console.

Error Handling

do {
    try await event.createRSVP(status: .going)
} catch AmityError.alreadyExists {
    // RSVP already exists, use update instead
    try await event.updateRSVP(status: .going)
} catch AmityError.eventEnded {
    showError("Cannot RSVP to ended event")
} catch AmityError.eventCancelled {
    showError("This event has been cancelled")
} catch {
    showError("Failed to RSVP: \(error.localizedDescription)")
}