Skip to content

Forms & Intake

What this module does

Orchid has a full dynamic form engine used for:

  • Intake applications — when a donor or carrier applies to an agency
  • Medical release forms — authorization forms for sharing medical records
  • Closing forms — end-of-journey documentation
  • Custom agency forms — arbitrary questionnaires an agency assigns to clients
  • Public submissions — unauthenticated forms for external applicants

Forms are composed of typed questions, support different question types (text, multiple choice, date, file upload), and store all submissions per user/case.

Business value

Every piece of structured information Orchid collects from clients comes through forms. A donor’s medical history, an IP’s preferences, a carrier’s previous pregnancy information — all of it flows through the form engine. Without forms, the matching and screening modules have nothing to work with.

Key files

  • Directoryorchid/
    • Directorymodels/
      • form.py 41 KB — Form, FormQuestion, UserForm, UserFormAnswer, IntakeFormSubmission, GeneralFormSubmission, MedicalReleaseForm
    • Directoryviews/
      • forms.py 29 KB — form rendering views
      • form_intake.py 51 KB — intake flow views
      • circle_intake.py 3 KB — Circle partner agency intake
    • Directoryapi/
      • forms.py 45 KB — form operations API
      • external_form.py 32 KB — public/unauthenticated form submissions
    • Directoryinfo/
      • prebuilt_forms.py 98 KB — pre-built question bank definitions
  • Directorytemplates/
    • Directoryforms/ — form rendering templates
    • macros_form_questions.html — question type rendering macros

Data model

erDiagram
Form {
int id
int agency_id
string name
string form_type
bool is_active
}
FormQuestion {
int form_id
int position
string question_type
string question_text
bool is_required
json options
}
UserForm {
int user_id
int case_id
int form_id
string status
datetime submitted_at
}
UserFormAnswer {
int user_form_id
int question_id
text answer
string s3_key
}
IntakeFormSubmission {
int user_id
int agency_id
string submission_type
}
GeneralFormSubmission {
int form_id
string external_email
json answers
}
Form ||--o{ FormQuestion : "has"
UserForm }|--|| Form : "is instance of"
UserForm ||--o{ UserFormAnswer : "has"
Form ||--o{ IntakeFormSubmission : "has"
Form ||--o{ GeneralFormSubmission : "has"

Form types

form_typeUsed forWho fills it out
'intake'Donor/carrier applicationAuthenticated user
'medical_release'Medical record authorizationAuthenticated user
'closing'End-of-case documentationAuthenticated user
'general'Custom agency questionnairesAuthenticated user
'external'Public inquiry/applicationUnauthenticated visitor

Question types

FormQuestion.question_type determines how the question is rendered and what kind of answer is stored:

TypeRendered asAnswer stored as
'text'Single-line text inputString
'textarea'Multi-line text areaString
'multiple_choice'Radio buttonsString (selected option)
'checkboxes'CheckboxesJSON array
'date'Date pickerDate string
'file'File uploaderS3 key
'yes_no'Yes/No radio'yes' or 'no'

Form submission lifecycle

1. Agency admin creates Form + FormQuestion records
(either custom-built or from prebuilt_forms.py question bank)
2. Form is assigned to a user via a workflow task (CaseTask)
→ UserForm record created with status='pending'
3. User logs into /user portal → sees their assigned forms
→ navigates to /forms/form/<id>
4. User fills out the form → saves in progress
→ UserFormAnswer records created/updated
5. User submits the form → UserForm.status = 'submitted'
→ Workflow task marked complete
→ Admin notified
6. Admin reviews submission in admin portal

Intake forms — the special case

Intake forms (form_type = 'intake') are the application forms that unauthenticated prospective donors/carriers fill out before they have a login. The intake flow at /forms/ (handled by form_intake.py, 51 KB) handles:

  1. Public landing page for the agency’s application
  2. reCAPTCHA verification
  3. Form rendering for unauthenticated visitors
  4. On submission: creating a User account and IntakeFormSubmission record
  5. Sending a confirmation email via SendGrid

The Circle intake flow

orchid/views/circle_intake.py handles a separate intake path for Circle — a partner agency’s referral system. The Circle intake is accessible at /circle/ and has different branding and routing than the standard intake, but it uses the same underlying form models.

External (public) form submissions

orchid/api/external_form.py handles form submissions from unauthenticated external users — people who fill out a form on an agency’s public website. These create GeneralFormSubmission records without requiring a login.

Key difference from intake forms: external submissions do not create user accounts automatically. They are reviewed by agency staff who then decide whether to create an account for the person.

prebuilt_forms.py — the question bank

orchid/info/prebuilt_forms.py (98 KB) defines a library of reusable question sets as Python dictionaries. When an agency builds a new intake form, they can pull from this library instead of creating questions from scratch.

Example structure:

PREBUILT_QUESTIONS = {
'personal_information': [
{'code': 'first_name', 'type': 'text', 'label': 'First Name', 'required': True},
{'code': 'date_of_birth', 'type': 'date', 'label': 'Date of Birth', 'required': True},
# ...
],
'medical_history': [
# ...
],
}

PDF printing

Forms can be printed to PDF via dedicated view routes:

/forms/<type>/<case_id>/form/<id>/print
/forms/<type>/<case_id>/closing-form/<id>/print
/forms/<type>/<case_id>/release-form/<id>/print
/forms/<type>/custom-profile/<id>/print

These render PDF-optimized templates in templates/pdf/ rather than the standard UI templates.

Gotchas