Skip to main content

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!