Attendee Questions API Guide
OverviewWhat Is It?
TheA Attendeecustomizable Questionsquestionnaire system allowsthat event organizers attach to createtheir customevents questionnairesto collect information from attendees during registration. Think of it like Google Forms, but built specifically for attendees.events Itand integratesintegrated into the ticket purchase flow.
The Two Sides
Organizer Side (Building the Form)
When an organizer creates an event, they can optionally enable "Attendee Questions." The system automatically creates an empty form with one page. The organizer then builds their questionnaire by:
-
Adding pages - to organize questions into logical sections (e.g., "Personal Info", "Dietary Needs", "Travel Details")
-
Adding fields to pages - choosing from various field types like text, email, phone, dropdowns, checkboxes, ratings, file uploads, etc.
-
Adding options - for choice-based fields (dropdown, radio, checkbox), the
FormorganizerBuilderaddsmodulethe available choices -
Configuring settings - deciding when to show the form (before or after checkout), whether it's required, and
provides:if walk-in attendees should also complete it
The organizer can reorder pages, fields, and options. They can edit labels, add descriptions, set validation rules (minimum length, date ranges, file size limits), and mark fields as required or optional.
Attendee Side (Filling the Form)
When someone registers for a published event, they see the questionnaire. The system works page-by-page:
-
View a page - attendee sees one page of questions at a time with navigation showing progress (Page 1 of 3)
-
Fill and save - as the attendee completes a page and moves to the next, their answers are validated and saved automatically. If any answer is invalid (wrong format, too short, invalid selection), the page won't save and they see specific error messages for each problematic field.
-
Submit - when ready, the attendee clicks submit. The system checks ALL pages to ensure every required field is answered and all answers are valid. If everything passes, the submission is finalized. If not, they see a summary showing which pages have problems.
The key principle: validate before saving, never store invalid data. This keeps the database clean and gives immediate feedback.
The Analytics Side (Viewing Results)
Once attendees start submitting, the organizer can view comprehensive analytics:
-
OrganizerSummaryendpointsstatistics:Create/manage-questionnairestotal(draftsubmissions,events)completion rates, average time to complete, submission trends over time, and which pages have the highest drop-off rates -
AttendeeField-by-fieldendpointsbreakdown:View- for each question, see response rates andsubmitdetailed analysis. For choice fields, see how many people selected each option with percentages. For numeric fields, see averages, ranges, and distributions. For text fields, see common words and length statistics. -
Spreadsheet view - all responses
(publishedinevents)a table format, like Google Forms' response spreadsheet, with each row being one submission and each column being one question -
AnalyticsExportendpoints:-Viewdownloadsubmissionallstatisticsdata(publishedasevents)CSV or Excel for external analysis
How Is It Useful?
For Event Organizers
Logistics planning:
- Collect t-shirt sizes to order the right quantities
- Gather dietary requirements for catering
- Know arrival times for transportation planning
- Get emergency contact information for safety
Attendee profiling:
- Ask about interests for personalized experiences
- Collect company/role info for networking features
- Understand how attendees heard about the event for marketing
Compliance and safety:
- Collect required documents (ID uploads)
- Gather health declarations
- Get consent for photography/recording
- Verify age requirements
Engagement:
- Ask what sessions attendees plan to attend
- Collect questions for Q&A panels
- Gather song requests for performers
- Get feedback on previous events
For Attendees
Smoother experience:
- Complete registration in one flow instead of separate forms
- Save progress and return later (draft mode)
- Clear validation messages prevent submission errors
- Know exactly what's required vs optional
Personalization:
- Their preferences are captured for a tailored experience
- Dietary needs are communicated without awkward conversations
- Special requirements are noted in advance
Real-World Example
Music Festival Scenario:
An organizer creates a 3-day festival. They enable attendee questions with two pages:
Page 1: Essential Info
- Emergency contact name (required, text)
- Emergency contact phone (required, phone validation)
- T-shirt size (required, dropdown: S/M/L/XL)
- Camping or day pass? (required, radio)
Page 2: Preferences
- Dietary restrictions (optional, checkbox: vegetarian, vegan, gluten-free, halal, kosher)
- Other dietary notes (optional, textarea)
- Which headliner are you most excited for? (optional, dropdown with artist names)
When 500 people register, the organizer sees:
- 65% chose Medium or Large t-shirts
- 23% have dietary restrictions (42% of those are vegetarian)
- Artist X is the most anticipated headliner with 45% of votes
- Page 2 has 15% drop-off (people skipping optional questions)
They export the data to:
- Order t-shirts: 50 S, 180 M, 170 L, 100 XL
- Tell catering: 115 vegetarian meals, 45 vegan, 60 gluten-free
- Adjust sound check priority based on headliner popularity
Why This Design?
Page-by-page saves prevent data loss. If someone fills 3 pages then loses internet, they don't lose everything.
Strict validation before saving means the database only contains valid, usable data. No garbage entries to clean up later.
Empty-body submit means the final submission is just a validation check, not a data transfer. Everything is already saved from auto-saves.
Parallel page validation on submit makes the final check fast even with many pages.
Separation of draft and published means organizers can freely edit their form while building the event, but once published and people start responding, the structure is stable.
Summary
It's a form builder tailored for events. Organizers build custom questionnaires, attendees fill them page-by-page with auto-save and validation, and organizers get rich analytics to plan better events. The technical design prioritizes data integrity, user experience, and actionable insights.
System Flow
┌─────────────────────────────────────────────────────────────────────────────┐
│ ORGANIZER FLOW │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. Create Event Draft │
│ POST /api/v1/e-events/drafts │
│ ↓ │
│ 2. Enable Attendee Questions │
│ PUT /api/v1/e-events/drafts/{draftId}/attendee-questions │
│ ↓ │
│ 3. Add Pages (optional - default page created automatically) │
│ POST /api/v1/e-events/drafts/{draftId}/attendee-questions/pages │
│ ↓ │
│ 4. Add Fields to Pages │
│ POST /api/v1/e-events/drafts/{draftId}/attendee-questions/pages/{pageId}/fields │
│ ↓ │
│ 5. Add Options to Choice Fields (dropdown/radio/checkbox) │
│ POST /api/v1/e-events/drafts/{draftId}/attendee-questions/fields/{fieldId}/options │
│ ↓ │
│ 6. Publish Event │
│ POST /api/v1/e-events/drafts/{draftId}/publish │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ ATTENDEE FLOW │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. View Questionnaire (check if registration open) │
│ GET /api/v1/e-events/{eventId}/attendee-questions │
│ ↓ │
│ 2. Get Each Page │
│ GET /api/v1/e-events/{eventId}/attendee-questions/pages/{pageNumber} │
│ ↓ │
│ 3. Save Page Answers (auto-save per page) │
│ PATCH /api/v1/e-events/{eventId}/attendee-questions/responses/pages/{n}│
│ ↓ │
│ 4. Submit Final Response │
│ POST /api/v1/e-events/{eventId}/attendee-questions/responses/submit │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ ANALYTICS FLOW (Organizer Only) │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ GET /api/v1/e-events/{eventId}/attendee-questions/analytics/summary │
│ GET /api/v1/e-events/{eventId}/attendee-questions/analytics/fields │
│ GET /api/v1/e-events/{eventId}/attendee-questions/analytics/fields/{id} │
│ GET /api/v1/e-events/{eventId}/attendee-questions/analytics/responses │
│ GET /api/v1/e-events/{eventId}/attendee-questions/analytics/export/csv │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Authentication
All endpoints require JWT authentication except public event viewing.
Header: Authorization: Bearer {jwt_token}
PART 1: ORGANIZER ENDPOINTS
Base URL
/api/v1/e-events/drafts/{draftId}/attendee-questions
1.1 Enable Attendee Questions
Creates a new questionnaire for the event draft. Automatically creates an empty form with one default page.
Endpoint:
PUT /api/v1/e-events/drafts/{draftId}/attendee-questions
Request:
{
"displayTime": "BEFORE_CHECKOUT",
"isRequiredOnline": true,
"applyToAtDoor": false
}
Request Fields:
| Field | Type | Required | Description |
|---|---|---|---|
displayTime |
enum | No | When to show form: BEFORE_CHECKOUT or AFTER_CHECKOUT. Default: BEFORE_CHECKOUT |
isRequiredOnline |
boolean | No | Must complete before checkout? Default: false |
applyToAtDoor |
boolean | No | Show to walk-in attendees? Default: false |
Response (201 Created):
{
"success": true,
"httpStatus": "CREATED",
"message": "Attendee questions enabled",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"eventId": "660e8400-e29b-41d4-a716-446655440001",
"formId": "770e8400-e29b-41d4-a716-446655440002",
"displayTime": "BEFORE_CHECKOUT",
"isRequiredOnline": true,
"applyToAtDoor": false,
"createdAt": "2025-01-23T10:30:00Z",
"updatedAt": "2025-01-23T10:30:00Z"
}
}
Error Responses:
| Status | Message |
|---|---|
| 404 | Draft not found |
| 403 | You don't have access to this draft |
| 403 | Attendee questions already enabled. Use update instead. |
1.2 Update Attendee Questions Settings
Endpoint:
PATCH /api/v1/e-events/drafts/{draftId}/attendee-questions
Request:
{
"displayTime": "AFTER_CHECKOUT",
"isRequiredOnline": false
}
All fields optional - only updates provided fields.
Response (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Settings updated",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"eventId": "660e8400-e29b-41d4-a716-446655440001",
"formId": "770e8400-e29b-41d4-a716-446655440002",
"displayTime": "AFTER_CHECKOUT",
"isRequiredOnline": false,
"applyToAtDoor": false,
"createdAt": "2025-01-23T10:30:00Z",
"updatedAt": "2025-01-23T10:35:00Z"
}
}
1.3 Get Attendee Questions (with full form)
Endpoint:
GET /api/v1/e-events/drafts/{draftId}/attendee-questions
Response (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Attendee questions retrieved",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"eventId": "660e8400-e29b-41d4-a716-446655440001",
"formId": "770e8400-e29b-41d4-a716-446655440002",
"displayTime": "BEFORE_CHECKOUT",
"isRequiredOnline": true,
"applyToAtDoor": false,
"createdAt": "2025-01-23T10:30:00Z",
"updatedAt": "2025-01-23T10:30:00Z",
"form": {
"formId": "770e8400-e29b-41d4-a716-446655440002",
"title": "Attendee Questions - Music Festival 2025",
"description": "Custom questions for event attendees",
"pages": [
{
"pageId": "880e8400-e29b-41d4-a716-446655440003",
"title": "Personal Information",
"description": "Basic attendee details",
"displayOrder": 1,
"fields": [
{
"fieldId": "990e8400-e29b-41d4-a716-446655440004",
"type": "TEXT",
"label": "Emergency Contact Name",
"description": "Who should we contact in case of emergency?",
"placeholder": "Full name",
"displayOrder": 1,
"required": true,
"validation": {
"minLength": 2,
"maxLength": 100
},
"options": []
},
{
"fieldId": "aa0e8400-e29b-41d4-a716-446655440005",
"type": "PHONE",
"label": "Emergency Contact Phone",
"displayOrder": 2,
"required": true,
"options": []
},
{
"fieldId": "bb0e8400-e29b-41d4-a716-446655440006",
"type": "DROPDOWN",
"label": "T-Shirt Size",
"displayOrder": 3,
"required": true,
"options": [
{"optionId": "opt1", "label": "Small", "displayOrder": 1},
{"optionId": "opt2", "label": "Medium", "displayOrder": 2},
{"optionId": "opt3", "label": "Large", "displayOrder": 3},
{"optionId": "opt4", "label": "X-Large", "displayOrder": 4}
]
}
]
},
{
"pageId": "cc0e8400-e29b-41d4-a716-446655440007",
"title": "Dietary Requirements",
"displayOrder": 2,
"fields": [
{
"fieldId": "dd0e8400-e29b-41d4-a716-446655440008",
"type": "CHECKBOX",
"label": "Dietary Restrictions",
"description": "Select all that apply",
"displayOrder": 1,
"required": false,
"options": [
{"optionId": "opt5", "label": "Vegetarian", "displayOrder": 1},
{"optionId": "opt6", "label": "Vegan", "displayOrder": 2},
{"optionId": "opt7", "label": "Gluten-free", "displayOrder": 3},
{"optionId": "opt8", "label": "Halal", "displayOrder": 4},
{"optionId": "opt9", "label": "Kosher", "displayOrder": 5}
]
},
{
"fieldId": "ee0e8400-e29b-41d4-a716-446655440009",
"type": "TEXTAREA",
"label": "Other Dietary Notes",
"placeholder": "Any allergies or special requirements?",
"displayOrder": 2,
"required": false
}
]
}
]
}
}
}
Response when not configured (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Attendee questions not configured",
"data": null
}
1.4 Disable Attendee Questions
Permanently deletes the form and all configuration.
Endpoint:
DELETE /api/v1/e-events/drafts/{draftId}/attendee-questions
Response (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Attendee questions disabled"
}
Pages Management
1.5 Add Page
Endpoint:
POST /api/v1/e-events/drafts/{draftId}/attendee-questions/pages
Request:
{
"title": "Travel Information",
"description": "Help us plan your arrival"
}
Response (201 Created):
{
"success": true,
"httpStatus": "CREATED",
"message": "Page added",
"data": {
"pageId": "ff0e8400-e29b-41d4-a716-446655440010",
"title": "Travel Information",
"description": "Help us plan your arrival",
"displayOrder": 3
}
}
1.6 Update Page
Endpoint:
PATCH /api/v1/e-events/drafts/{draftId}/attendee-questions/pages/{pageId}
Request:
{
"title": "Travel & Accommodation",
"description": "Updated description"
}
Response (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Page updated",
"data": {
"pageId": "ff0e8400-e29b-41d4-a716-446655440010",
"title": "Travel & Accommodation",
"description": "Updated description",
"displayOrder": 3
}
}
1.7 Delete Page
Endpoint:
DELETE /api/v1/e-events/drafts/{draftId}/attendee-questions/pages/{pageId}
Response (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Page deleted"
}
1.8 Reorder Pages
Endpoint:
POST /api/v1/e-events/drafts/{draftId}/attendee-questions/pages/reorder
Request:
{
"orderedIds": [
"cc0e8400-e29b-41d4-a716-446655440007",
"880e8400-e29b-41d4-a716-446655440003",
"ff0e8400-e29b-41d4-a716-446655440010"
]
}
Response (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Pages reordered"
}
Fields Management
1.9 Add Field
Endpoint:
POST /api/v1/e-events/drafts/{draftId}/attendee-questions/pages/{pageId}/fields
Request (Text Field):
{
"type": "TEXT",
"label": "Company Name",
"description": "Your current employer",
"placeholder": "Enter company name",
"required": false,
"validation": {
"minLength": 2,
"maxLength": 100
}
}
Request (Dropdown Field):
{
"type": "DROPDOWN",
"label": "How did you hear about us?",
"required": true
}
Request (Rating Field):
{
"type": "RATING",
"label": "How excited are you for this event?",
"required": true,
"validation": {
"min": 1,
"max": 5
}
}
Request (File Upload Field):
{
"type": "FILE",
"label": "Upload ID Document",
"description": "Government-issued ID for verification",
"required": true,
"validation": {
"maxSizeMb": 5,
"accept": "image/*,.pdf"
}
}
Available Field Types:
| Type | Description | Has Options |
|---|---|---|
TEXT |
Single line text | No |
TEXTAREA |
Multi-line text | No |
EMAIL |
Email with validation | No |
PHONE |
Phone number | No |
NUMBER |
Numeric input | No |
DATE |
Date picker | No |
TIME |
Time picker | No |
DATETIME |
Date and time | No |
DROPDOWN |
Single select dropdown | Yes |
RADIO |
Single select radio buttons | Yes |
CHECKBOX |
Multi-select checkboxes | Yes |
RATING |
Star rating | No |
SCALE |
Linear scale (1-10) | No |
SLIDER |
Slider input | No |
FILE |
File upload | No |
URL |
URL with validation | No |
CURRENCY |
Money input | No |
SECTION_HEADER |
Display only - section title | No |
PARAGRAPH |
Display only - text block | No |
Response (201 Created):
{
"success": true,
"httpStatus": "CREATED",
"message": "Field added",
"data": {
"fieldId": "110e8400-e29b-41d4-a716-446655440011",
"type": "DROPDOWN",
"label": "How did you hear about us?",
"displayOrder": 1,
"required": true,
"options": []
}
}
1.10 Update Field
Endpoint:
PATCH /api/v1/e-events/drafts/{draftId}/attendee-questions/fields/{fieldId}
Request:
{
"label": "How did you discover this event?",
"required": false,
"description": "Help us improve our marketing"
}
Response (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Field updated",
"data": {
"fieldId": "110e8400-e29b-41d4-a716-446655440011",
"type": "DROPDOWN",
"label": "How did you discover this event?",
"description": "Help us improve our marketing",
"displayOrder": 1,
"required": false,
"options": []
}
}
1.11 Delete Field
Endpoint:
DELETE /api/v1/e-events/drafts/{draftId}/attendee-questions/fields/{fieldId}
Response (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Field deleted"
}
1.12 Reorder Fields
Endpoint:
POST /api/v1/e-events/drafts/{draftId}/attendee-questions/pages/{pageId}/fields/reorder
Request:
{
"orderedIds": [
"bb0e8400-e29b-41d4-a716-446655440006",
"990e8400-e29b-41d4-a716-446655440004",
"aa0e8400-e29b-41d4-a716-446655440005"
]
}
Response (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Fields reordered"
}
Options Management (for DROPDOWN, RADIO, CHECKBOX)
1.13 Add Option
Endpoint:
POST /api/v1/e-events/drafts/{draftId}/attendee-questions/fields/{fieldId}/options
Request:
{
"label": "Social Media"
}
Response (201 Created):
{
"success": true,
"httpStatus": "CREATED",
"message": "Option added",
"data": {
"optionId": "220e8400-e29b-41d4-a716-446655440012",
"label": "Social Media",
"displayOrder": 1
}
}
1.14 Update Option
Endpoint:
PATCH /api/v1/e-events/drafts/{draftId}/attendee-questions/options/{optionId}
Request:
{
"label": "Social Media (Facebook, Instagram, etc.)"
}
Response (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Option updated",
"data": {
"optionId": "220e8400-e29b-41d4-a716-446655440012",
"label": "Social Media (Facebook, Instagram, etc.)",
"displayOrder": 1
}
}
1.15 Delete Option
Endpoint:
DELETE /api/v1/e-events/drafts/{draftId}/attendee-questions/options/{optionId}
Response (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Option deleted"
}
1.16 Reorder Options
Endpoint:
POST /api/v1/e-events/drafts/{draftId}/attendee-questions/fields/{fieldId}/options/reorder
Request:
{
"orderedIds": [
"220e8400-e29b-41d4-a716-446655440012",
"330e8400-e29b-41d4-a716-446655440013",
"440e8400-e29b-41d4-a716-446655440014"
]
}
Response (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Options reordered"
}
PART 2: ATTENDEE ENDPOINTS (Public)
Base URL
/api/v1/e-events/{eventId}/attendee-questions
Note: These endpoints require the event to be PUBLISHED.
2.1 Get Questionnaire (First Page)
Endpoint:
GET /api/v1/e-events/{eventId}/attendee-questions
Response - No questionnaire configured:
{
"success": true,
"httpStatus": "OK",
"message": "No questionnaire for this event",
"data": {
"eventId": "660e8400-e29b-41d4-a716-446655440001",
"eventTitle": "Music Festival 2025",
"hasForm": false,
"registrationOpen": false,
"message": "No questionnaire for this event"
}
}
Response - Registration not yet open:
{
"success": true,
"httpStatus": "OK",
"message": "Registration opens on 2025-02-01",
"data": {
"eventId": "660e8400-e29b-41d4-a716-446655440001",
"eventTitle": "Music Festival 2025",
"hasForm": true,
"registrationOpen": false,
"message": "Registration opens on 2025-02-01",
"registrationOpensAt": "2025-02-01T09:00:00Z",
"registrationClosesAt": "2025-02-15T18:00:00Z",
"displayTime": "BEFORE_CHECKOUT",
"isRequired": true
}
}
Response - Registration closed:
{
"success": true,
"httpStatus": "OK",
"message": "Registration closed on 2025-02-15",
"data": {
"eventId": "660e8400-e29b-41d4-a716-446655440001",
"eventTitle": "Music Festival 2025",
"hasForm": true,
"registrationOpen": false,
"message": "Registration closed on 2025-02-15",
"registrationOpensAt": "2025-02-01T09:00:00Z",
"registrationClosesAt": "2025-02-15T18:00:00Z"
}
}
Response - Registration open (returns page 1):
{
"success": true,
"httpStatus": "OK",
"message": "Questionnaire retrieved",
"data": {
"eventId": "660e8400-e29b-41d4-a716-446655440001",
"eventTitle": "Music Festival 2025",
"hasForm": true,
"registrationOpen": true,
"registrationOpensAt": "2025-02-01T09:00:00Z",
"registrationClosesAt": "2025-02-15T18:00:00Z",
"displayTime": "BEFORE_CHECKOUT",
"isRequired": true,
"currentPage": 1,
"totalPages": 2,
"formTitle": "Attendee Questions - Music Festival 2025",
"formDescription": "Please complete this questionnaire",
"page": {
"pageId": "880e8400-e29b-41d4-a716-446655440003",
"title": "Personal Information",
"description": "Basic attendee details",
"displayOrder": 1,
"fields": [
{
"fieldId": "990e8400-e29b-41d4-a716-446655440004",
"type": "TEXT",
"label": "Emergency Contact Name",
"description": "Who should we contact in case of emergency?",
"placeholder": "Full name",
"displayOrder": 1,
"required": true,
"validation": {
"minLength": 2,
"maxLength": 100
},
"options": []
},
{
"fieldId": "aa0e8400-e29b-41d4-a716-446655440005",
"type": "PHONE",
"label": "Emergency Contact Phone",
"displayOrder": 2,
"required": true,
"options": []
},
{
"fieldId": "bb0e8400-e29b-41d4-a716-446655440006",
"type": "DROPDOWN",
"label": "T-Shirt Size",
"displayOrder": 3,
"required": true,
"options": [
{"optionId": "opt1", "label": "Small", "displayOrder": 1},
{"optionId": "opt2", "label": "Medium", "displayOrder": 2},
{"optionId": "opt3", "label": "Large", "displayOrder": 3},
{"optionId": "opt4", "label": "X-Large", "displayOrder": 4}
]
}
]
}
}
}
2.2 Get Specific Page
Endpoint:
GET /api/v1/e-events/{eventId}/attendee-questions/pages/{pageNumber}
Example:
GET /api/v1/e-events/660e8400-e29b-41d4-a716-446655440001/attendee-questions/pages/2
Response (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Page 2 of 2",
"data": {
"eventId": "660e8400-e29b-41d4-a716-446655440001",
"eventTitle": "Music Festival 2025",
"hasForm": true,
"registrationOpen": true,
"currentPage": 2,
"totalPages": 2,
"page": {
"pageId": "cc0e8400-e29b-41d4-a716-446655440007",
"title": "Dietary Requirements",
"displayOrder": 2,
"fields": [
{
"fieldId": "dd0e8400-e29b-41d4-a716-446655440008",
"type": "CHECKBOX",
"label": "Dietary Restrictions",
"description": "Select all that apply",
"displayOrder": 1,
"required": false,
"options": [
{"optionId": "opt5", "label": "Vegetarian", "displayOrder": 1},
{"optionId": "opt6", "label": "Vegan", "displayOrder": 2},
{"optionId": "opt7", "label": "Gluten-free", "displayOrder": 3},
{"optionId": "opt8", "label": "Halal", "displayOrder": 4},
{"optionId": "opt9", "label": "Kosher", "displayOrder": 5}
]
},
{
"fieldId": "ee0e8400-e29b-41d4-a716-446655440009",
"type": "TEXTAREA",
"label": "Other Dietary Notes",
"placeholder": "Any allergies or special requirements?",
"displayOrder": 2,
"required": false
}
]
}
}
}
Error - Page not found:
{
"success": false,
"httpStatus": "NOT_FOUND",
"message": "Page not found. Valid pages: 1 to 2"
}
2.3 Save Page Answers (Auto-Save)
Saves answers for a single page. Validates ALL fields on the page before saving. If any field is invalid, nothing is saved.
Endpoint:
PATCH /api/v1/e-events/{eventId}/attendee-questions/responses/pages/{pageNumber}
Request:
{
"answers": {
"990e8400-e29b-41d4-a716-446655440004": {
"fieldId": "990e8400-e29b-41d4-a716-446655440004",
"value": "John Doe"
},
"aa0e8400-e29b-41d4-a716-446655440005": {
"fieldId": "aa0e8400-e29b-41d4-a716-446655440005",
"value": "+255712345678"
},
"bb0e8400-e29b-41d4-a716-446655440006": {
"fieldId": "bb0e8400-e29b-41d4-a716-446655440006",
"value": "Large"
}
}
}
Answer Format by Field Type:
| Field Type | Value Format | Example |
|---|---|---|
| TEXT, TEXTAREA, EMAIL, PHONE, URL | string | "John Doe" |
| NUMBER, CURRENCY, RATING, SCALE, SLIDER | number | 42 or 4.5 |
| DATE | string (YYYY-MM-DD) | "2025-02-15" |
| TIME | string (HH:MM) | "14:30" |
| DATETIME | string (ISO) | "2025-02-15T14:30:00" |
| DROPDOWN, RADIO | string (selected label) | "Large" |
| CHECKBOX | array of strings | ["Vegetarian", "Gluten-free"] |
| FILE | object | See below |
File Upload Answer:
{
"fieldId": "file-field-id",
"value": null,
"fileUrl": "https://storage.example.com/uploads/id-doc.pdf",
"fileName": "id-doc.pdf",
"fileSize": 1048576,
"fileType": "application/pdf"
}
Response - Success (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Page 1 saved",
"data": {
"eventId": "660e8400-e29b-41d4-a716-446655440001",
"pageNumber": 1,
"saved": true,
"savedAt": "2025-01-23T10:45:00Z",
"isValid": true,
"errors": [],
"totalFieldsOnPage": 3,
"answeredFieldsOnPage": 3,
"overallProgress": {
"totalPages": 2,
"completedPages": 1,
"totalFields": 5,
"answeredFields": 3,
"completionPercentage": 60
}
}
}
Response - Validation Failed (400 Bad Request):
{
"success": false,
"httpStatus": "BAD_REQUEST",
"message": "Validation failed - page not saved",
"data": {
"eventId": "660e8400-e29b-41d4-a716-446655440001",
"pageNumber": 1,
"saved": false,
"isValid": false,
"errors": [
{
"fieldId": "aa0e8400-e29b-41d4-a716-446655440005",
"fieldLabel": "Emergency Contact Phone",
"errorCode": "INVALID_PHONE",
"message": "Please enter a valid phone number"
},
{
"fieldId": "bb0e8400-e29b-41d4-a716-446655440006",
"fieldLabel": "T-Shirt Size",
"errorCode": "INVALID_OPTION",
"message": "Selected option is not valid"
}
],
"totalFieldsOnPage": 3,
"answeredFieldsOnPage": 3,
"overallProgress": {
"totalPages": 2,
"completedPages": 0,
"totalFields": 5,
"answeredFields": 0,
"completionPercentage": 0
}
}
}
Validation Error Codes:
| Code | Description |
|---|---|
REQUIRED |
Required field is missing |
INVALID_EMAIL |
Invalid email format |
INVALID_PHONE |
Invalid phone number |
INVALID_NUMBER |
Not a valid number |
INVALID_DATE |
Invalid date format |
INVALID_TIME |
Invalid time format |
INVALID_DATETIME |
Invalid datetime format |
INVALID_URL |
Invalid URL format |
INVALID_OPTION |
Selected option not in list |
INVALID_SELECTION |
Invalid selection format |
TOO_SHORT |
Text too short (minLength) |
TOO_LONG |
Text too long (maxLength) |
BELOW_MIN |
Number below minimum |
ABOVE_MAX |
Number above maximum |
DATE_TOO_EARLY |
Date before minDate |
DATE_TOO_LATE |
Date after maxDate |
TOO_FEW_SELECTIONS |
Not enough checkboxes |
TOO_MANY_SELECTIONS |
Too many checkboxes |
PATTERN_MISMATCH |
Doesn't match regex |
FILE_TOO_LARGE |
File exceeds maxSizeMb |
INVALID_FILE_TYPE |
File type not accepted |
2.4 Submit Final Response
Submits the form. No request body needed - validates all saved pages and submits if all valid.
Endpoint:
POST /api/v1/e-events/{eventId}/attendee-questions/responses/submit
Response - Success (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Submission successful",
"data": {
"responseId": "550e8400-e29b-41d4-a716-446655440099",
"eventId": "660e8400-e29b-41d4-a716-446655440001",
"success": true,
"status": "SUBMITTED",
"submittedAt": "2025-01-23T10:50:00Z",
"pageValidations": [
{
"pageNumber": 1,
"pageTitle": "Personal Information",
"isValid": true,
"hasAnswers": true,
"totalFields": 3,
"answeredFields": 3,
"requiredFields": 3,
"requiredAnswered": 3,
"errors": []
},
{
"pageNumber": 2,
"pageTitle": "Dietary Requirements",
"isValid": true,
"hasAnswers": true,
"totalFields": 2,
"answeredFields": 1,
"requiredFields": 0,
"requiredAnswered": 0,
"errors": []
}
],
"totalPages": 2,
"validPages": 2,
"invalidPages": 0,
"allPagesValid": true
}
}
Response - Validation Failed (400 Bad Request):
{
"success": false,
"httpStatus": "BAD_REQUEST",
"message": "Validation failed - 1 page(s) have errors",
"data": {
"eventId": "660e8400-e29b-41d4-a716-446655440001",
"success": false,
"status": "DRAFT",
"pageValidations": [
{
"pageNumber": 1,
"pageTitle": "Personal Information",
"isValid": false,
"hasAnswers": true,
"totalFields": 3,
"answeredFields": 2,
"requiredFields": 3,
"requiredAnswered": 2,
"errors": [
{
"fieldId": "bb0e8400-e29b-41d4-a716-446655440006",
"fieldLabel": "T-Shirt Size",
"errorCode": "REQUIRED",
"message": "T-Shirt Size is required"
}
]
},
{
"pageNumber": 2,
"pageTitle": "Dietary Requirements",
"isValid": true,
"hasAnswers": false,
"totalFields": 2,
"answeredFields": 0,
"requiredFields": 0,
"requiredAnswered": 0,
"errors": []
}
],
"totalPages": 2,
"validPages": 1,
"invalidPages": 1,
"allPagesValid": false
}
}
Error - No draft found:
{
"success": false,
"httpStatus": "NOT_FOUND",
"message": "No draft found. Please save your answers first."
}
2.5 Get Submission Status
Endpoint:
GET /api/v1/e-events/{eventId}/attendee-questions/responses/status
Response - No submission started:
{
"success": true,
"httpStatus": "OK",
"message": "No submission started",
"data": {
"eventId": "660e8400-e29b-41d4-a716-446655440001",
"success": false,
"status": null,
"totalPages": 2,
"validPages": 0,
"invalidPages": 0,
"allPagesValid": false
}
}
Response - Draft in progress:
{
"success": true,
"httpStatus": "OK",
"message": "Status: DRAFT",
"data": {
"responseId": "550e8400-e29b-41d4-a716-446655440099",
"eventId": "660e8400-e29b-41d4-a716-446655440001",
"success": false,
"status": "DRAFT",
"submittedAt": null,
"pageValidations": [
{
"pageNumber": 1,
"pageTitle": "Personal Information",
"isValid": true,
"hasAnswers": true,
"totalFields": 3,
"answeredFields": 3,
"requiredFields": 3,
"requiredAnswered": 3,
"errors": []
},
{
"pageNumber": 2,
"pageTitle": "Dietary Requirements",
"isValid": true,
"hasAnswers": false,
"totalFields": 2,
"answeredFields": 0,
"requiredFields": 0,
"requiredAnswered": 0,
"errors": []
}
],
"totalPages": 2,
"validPages": 2,
"invalidPages": 0,
"allPagesValid": true
}
}
Response - Already submitted:
{
"success": true,
"httpStatus": "OK",
"message": "Status: SUBMITTED",
"data": {
"responseId": "550e8400-e29b-41d4-a716-446655440099",
"eventId": "660e8400-e29b-41d4-a716-446655440001",
"success": true,
"status": "SUBMITTED",
"submittedAt": "2025-01-23T10:50:00Z",
"totalPages": 2,
"validPages": 2,
"invalidPages": 0,
"allPagesValid": true
}
}
2.6 Withdraw Submission
Endpoint:
DELETE /api/v1/e-events/{eventId}/attendee-questions/responses
Response (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Submission withdrawn"
}
PART 3: ANALYTICS ENDPOINTS (Organizer Only)
Base URL
/api/v1/e-events/{eventId}/attendee-questions/analytics
Note: Only the event organizer can access analytics.
3.1 Get Summary Statistics
Endpoint:
GET /api/v1/e-events/{eventId}/attendee-questions/analytics/summary
Response (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Summary retrieved",
"data": {
"eventId": "660e8400-e29b-41d4-a716-446655440001",
"eventTitle": "Music Festival 2025",
"totalStarted": 250,
"totalDrafts": 45,
"totalSubmitted": 180,
"totalWithdrawn": 25,
"completionRate": 72.0,
"dropOffRate": 28.0,
"averageCompletionTimeSeconds": 185.5,
"fastestCompletionSeconds": 45,
"slowestCompletionSeconds": 890,
"submissionTrend": [
{"date": "2025-01-01", "count": 0},
{"date": "2025-01-02", "count": 5},
{"date": "2025-01-03", "count": 12},
{"date": "2025-01-04", "count": 8},
{"date": "2025-01-05", "count": 15},
{"date": "2025-01-06", "count": 22},
{"date": "2025-01-07", "count": 18}
],
"pageDropOff": [
{
"pageNumber": 1,
"pageTitle": "Personal Information",
"started": 250,
"completed": 220,
"dropOffRate": 12.0
},
{
"pageNumber": 2,
"pageTitle": "Dietary Requirements",
"started": 220,
"completed": 180,
"dropOffRate": 18.2
}
],
"firstSubmissionAt": "2025-01-02T09:15:00",
"lastSubmissionAt": "2025-01-23T10:50:00",
"generatedAt": "2025-01-23T11:00:00"
}
}
3.2 Get All Fields Analytics
Endpoint:
GET /api/v1/e-events/{eventId}/attendee-questions/analytics/fields
Response (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Fields analytics retrieved",
"data": {
"eventId": "660e8400-e29b-41d4-a716-446655440001",
"totalResponses": 180,
"fields": [
{
"fieldId": "990e8400-e29b-41d4-a716-446655440004",
"label": "Emergency Contact Name",
"type": "TEXT",
"pageNumber": 1,
"required": true,
"responseCount": 180,
"skippedCount": 0,
"responseRate": 100.0,
"textStats": {
"averageLength": 15,
"minLength": 5,
"maxLength": 45,
"commonWords": ["john", "jane", "mary", "james", "david"]
}
},
{
"fieldId": "bb0e8400-e29b-41d4-a716-446655440006",
"label": "T-Shirt Size",
"type": "DROPDOWN",
"pageNumber": 1,
"required": true,
"responseCount": 180,
"skippedCount": 0,
"responseRate": 100.0,
"optionBreakdown": [
{"value": "Medium", "count": 65, "percentage": 36.1},
{"value": "Large", "count": 58, "percentage": 32.2},
{"value": "Small", "count": 35, "percentage": 19.4},
{"value": "X-Large", "count": 22, "percentage": 12.2}
]
},
{
"fieldId": "dd0e8400-e29b-41d4-a716-446655440008",
"label": "Dietary Restrictions",
"type": "CHECKBOX",
"pageNumber": 2,
"required": false,
"responseCount": 95,
"skippedCount": 85,
"responseRate": 52.8,
"optionBreakdown": [
{"value": "Vegetarian", "count": 42, "percentage": 44.2},
{"value": "Gluten-free", "count": 28, "percentage": 29.5},
{"value": "Vegan", "count": 18, "percentage": 18.9},
{"value": "Halal", "count": 12, "percentage": 12.6},
{"value": "Kosher", "count": 5, "percentage": 5.3}
]
}
]
}
}
3.3 Get Single Field Analytics
Endpoint:
GET /api/v1/e-events/{eventId}/attendee-questions/analytics/fields/{fieldId}?page=0&size=20
Response (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Field analytics retrieved",
"data": {
"eventId": "660e8400-e29b-41d4-a716-446655440001",
"fieldId": "bb0e8400-e29b-41d4-a716-446655440006",
"label": "T-Shirt Size",
"type": "DROPDOWN",
"required": true,
"totalResponses": 180,
"responseCount": 180,
"skippedCount": 0,
"responseRate": 100.0,
"optionBreakdown": [
{"value": "Medium", "count": 65, "percentage": 36.1},
{"value": "Large", "count": 58, "percentage": 32.2},
{"value": "Small", "count": 35, "percentage": 19.4},
{"value": "X-Large", "count": 22, "percentage": 12.2}
],
"responses": [
{
"responseId": "resp-001",
"value": "Large",
"submittedAt": "2025-01-23T10:50:00"
},
{
"responseId": "resp-002",
"value": "Medium",
"submittedAt": "2025-01-23T10:48:00"
}
],
"totalResponseCount": 180,
"page": 0,
"pageSize": 20
}
}
3.4 Get All Responses (Spreadsheet View)
Endpoint:
GET /api/v1/e-events/{eventId}/attendee-questions/analytics/responses?page=0&size=20
Response (200 OK):
{
"success": true,
"httpStatus": "OK",
"message": "Responses retrieved",
"data": {
"eventId": "660e8400-e29b-41d4-a716-446655440001",
"totalResponses": 180,
"page": 0,
"pageSize": 20,
"totalPages": 9,
"columns": [
{"fieldId": "990e8400-e29b-41d4-a716-446655440004", "label": "Emergency Contact Name", "type": "TEXT", "pageNumber": 1},
{"fieldId": "aa0e8400-e29b-41d4-a716-446655440005", "label": "Emergency Contact Phone", "type": "PHONE", "pageNumber": 1},
{"fieldId": "bb0e8400-e29b-41d4-a716-446655440006", "label": "T-Shirt Size", "type": "DROPDOWN", "pageNumber": 1},
{"fieldId": "dd0e8400-e29b-41d4-a716-446655440008", "label": "Dietary Restrictions", "type": "CHECKBOX", "pageNumber": 2},
{"fieldId": "ee0e8400-e29b-41d4-a716-446655440009", "label": "Other Dietary Notes", "type": "TEXTAREA", "pageNumber": 2}
],
"responses": [
{
"responseId": "resp-001",
"submittedById": "user-001",
"submittedByName": "kibuti",
"submittedByEmail": "kibuti@example.com",
"status": "SUBMITTED",
"startedAt": "2025-01-23T10:40:00",
"submittedAt": "2025-01-23T10:50:00",
"completionTimeSeconds": 600,
"answers": {
"990e8400-e29b-41d4-a716-446655440004": "John Doe",
"aa0e8400-e29b-41d4-a716-446655440005": "+255712345678",
"bb0e8400-e29b-41d4-a716-446655440006": "Large",
"dd0e8400-e29b-41d4-a716-446655440008": ["Vegetarian", "Gluten-free"],
"ee0e8400-e29b-41d4-a716-446655440009": "No nuts please"
}
},
{
"responseId": "resp-002",
"submittedById": "user-002",
"submittedByName": "john_doe",
"submittedByEmail": "john@example.com",
"status": "SUBMITTED",
"startedAt": "2025-01-23T10:30:00",
"submittedAt": "2025-01-23T10:45:00",
"completionTimeSeconds": 900,
"answers": {
"990e8400-e29b-41d4-a716-446655440004": "Jane Smith",
"aa0e8400-e29b-41d4-a716-446655440005": "+255798765432",
"bb0e8400-e29b-41d4-a716-446655440006": "Medium",
"dd0e8400-e29b-41d4-a716-446655440008": null,
"ee0e8400-e29b-41d4-a716-446655440009": null
}
}
]
}
}
3.5 Export to CSV
Endpoint:
GET /api/v1/e-events/{eventId}/attendee-questions/analytics/export/csv
Response: Downloads a CSV file
CSV Content:
Response ID,Submitted By,Email,Submitted At,Completion Time (seconds),Emergency Contact Name,Emergency Contact Phone,T-Shirt Size,Dietary Restrictions,Other Dietary Notes
resp-001,kibuti,kibuti@example.com,2025-01-23T10:50:00,600,John Doe,+255712345678,Large,Vegetarian; Gluten-free,No nuts please
resp-002,john_doe,john@example.com,2025-01-23T10:45:00,900,Jane Smith,+255798765432,Medium,,
3.6 Export to Excel
Endpoint:
GET /api/v1/e-events/{eventId}/attendee-questions/analytics/export/excel
Response: Downloads an Excel (.xlsx) file
PART 4: COMPLETE ENDPOINTS REFERENCE
Organizer Endpoints (Draft Events)
| Method | Endpoint | Description |
|---|---|---|
| PUT | /drafts/{draftId}/attendee-questions |
Enable questionnaire |
| PATCH | /drafts/{draftId}/attendee-questions |
Update settings |
| GET | /drafts/{draftId}/attendee-questions |
Get with full form |
| DELETE | /drafts/{draftId}/attendee-questions |
Disable (delete) |
| POST | /drafts/{draftId}/attendee-questions/pages |
Add page |
| PATCH | /drafts/{draftId}/attendee-questions/pages/{pageId} |
Update page |
| DELETE | /drafts/{draftId}/attendee-questions/pages/{pageId} |
Delete page |
| POST | /drafts/{draftId}/attendee-questions/pages/reorder |
Reorder pages |
| POST | /drafts/{draftId}/attendee-questions/pages/{pageId}/fields |
Add field |
| PATCH | /drafts/{draftId}/attendee-questions/fields/{fieldId} |
Update field |
| DELETE | /drafts/{draftId}/attendee-questions/fields/{fieldId} |
Delete field |
| POST | /drafts/{draftId}/attendee-questions/pages/{pageId}/fields/reorder |
Reorder fields |
| POST | /drafts/{draftId}/attendee-questions/fields/{fieldId}/options |
Add option |
| PATCH | /drafts/{draftId}/attendee-questions/options/{optionId} |
Update option |
| DELETE | /drafts/{draftId}/attendee-questions/options/{optionId} |
Delete option |
| POST | /drafts/{draftId}/attendee-questions/fields/{fieldId}/options/reorder |
Reorder options |
Attendee Endpoints (Published Events)
| Method | Endpoint | Description |
|---|---|---|
| GET | /{eventId}/attendee-questions |
Get form (page 1) |
| GET | /{eventId}/attendee-questions/pages/{n} |
Get specific page |
| PATCH | /{eventId}/attendee-questions/responses/pages/{n} |
Save page (auto-save) |
| POST | /{eventId}/attendee-questions/responses/submit |
Submit (no body) |
| GET | /{eventId}/attendee-questions/responses/status |
Get my status |
| DELETE | /{eventId}/attendee-questions/responses |
Withdraw |
Analytics Endpoints (Organizer Only)
| Method | Endpoint | Description |
|---|---|---|
| GET | /{eventId}/attendee-questions/analytics/summary |
Overall stats |
| GET | /{eventId}/attendee-questions/analytics/fields |
All fields breakdown |
| GET | /{eventId}/attendee-questions/analytics/fields/{fieldId} |
Single field detail |
| GET | /{eventId}/attendee-questions/analytics/responses |
Spreadsheet view |
| GET | /{eventId}/attendee-questions/analytics/export/csv |
Download CSV |
| GET | /{eventId}/attendee-questions/analytics/export/excel |
Download Excel |
PART 5: INSOMNIA TESTING GUIDE
Setup Environment Variables
base_url: http://localhost:8080/api/v1
jwt_token: <your_token>
draft_id: <your_draft_id>
event_id: <your_published_event_id>
page_id: <page_uuid>
field_id: <field_uuid>
option_id: <option_uuid>
Testing Flow
Step 1: Create Event Draft
POST {{base_url}}/e-events/drafts
Authorization: Bearer {{jwt_token}}
Content-Type: application/json
{
"title": "Test Music Festival 2025",
"categoryId": "{{category_id}}",
"eventFormat": "IN_PERSON",
"description": "Test event for questionnaire"
}
Step 2: Enable Attendee Questions
PUT {{base_url}}/e-events/drafts/{{draft_id}}/attendee-questions
Authorization: Bearer {{jwt_token}}
Content-Type: application/json
{
"displayTime": "BEFORE_CHECKOUT",
"isRequiredOnline": true,
"applyToAtDoor": false
}
Step 3: Get Questionnaire (see default page)
GET {{base_url}}/e-events/drafts/{{draft_id}}/attendee-questions
Authorization: Bearer {{jwt_token}}
Step 4: Add Text Field
POST {{base_url}}/e-events/drafts/{{draft_id}}/attendee-questions/pages/{{page_id}}/fields
Authorization: Bearer {{jwt_token}}
Content-Type: application/json
{
"type": "TEXT",
"label": "Emergency Contact Name",
"placeholder": "Full name",
"required": true,
"validation": {
"minLength": 2,
"maxLength": 100
}
}
Step 5: Add Dropdown Field
POST {{base_url}}/e-events/drafts/{{draft_id}}/attendee-questions/pages/{{page_id}}/fields
Authorization: Bearer {{jwt_token}}
Content-Type: application/json
{
"type": "DROPDOWN",
"label": "T-Shirt Size",
"required": true
}
Step 6: Add Options to Dropdown
POST {{base_url}}/e-events/drafts/{{draft_id}}/attendee-questions/fields/{{field_id}}/options
Authorization: Bearer {{jwt_token}}
Content-Type: application/json
{"label": "Small"}
Repeat for: Medium, Large, X-Large
Step 7: Publish Event
POST {{base_url}}/e-events/drafts/{{draft_id}}/publish
Authorization: Bearer {{jwt_token}}
Step 8: (As Attendee) Get Questionnaire
GET {{base_url}}/e-events/{{event_id}}/attendee-questions
Authorization: Bearer {{attendee_token}}
Step 9: (As Attendee) Save Page 1
PATCH {{base_url}}/e-events/{{event_id}}/attendee-questions/responses/pages/1
Authorization: Bearer {{attendee_token}}
Content-Type: application/json
{
"answers": {
"{{text_field_id}}": {
"fieldId": "{{text_field_id}}",
"value": "John Doe"
},
"{{dropdown_field_id}}": {
"fieldId": "{{dropdown_field_id}}",
"value": "Large"
}
}
}
Step 10: (As Attendee) Submit
POST {{base_url}}/e-events/{{event_id}}/attendee-questions/responses/submit
Authorization: Bearer {{attendee_token}}
Step 11: (As Organizer) View Analytics
GET {{base_url}}/e-events/{{event_id}}/attendee-questions/analytics/summary
Authorization: Bearer {{jwt_token}}
Step 12: (As Organizer) Export CSV
GET {{base_url}}/e-events/{{event_id}}/attendee-questions/analytics/export/csv
Authorization: Bearer {{jwt_token}}
This guide covers the complete Attendee Questions API. Ready to implement and test!