Attendee Questions API Guide
Overview
The Attendee Questions system allows event organizers to create custom questionnaires for attendees. It integrates with the Form Builder module and provides:
- Organizer endpoints: Create/manage questionnaires (draft events)
- Attendee endpoints: View and submit responses (published events)
- Analytics endpoints: View submission statistics (published events)
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!