Skip to content

Matching System

What this module does

Matching is the business-critical process of connecting intended parents (IPs) with egg donors, gestational carriers, or sperm donors. The matching system handles everything from preference-based filtering to formal match proposals, including the IP’s review process and match decisions.

Business value

Matching is the core service a fertility agency provides. Getting it right — presenting the right candidates to the right IPs at the right time — is what determines an agency’s success. The matching system must balance the agency’s curation (which profiles to show) with the IP’s autonomy (what they can filter and choose).

Key files

  • Directoryorchid/
    • Directorymodels/
      • matching.py Favorite, MatchBreakDetails, MatchingPreferences, MatchingQueue
      • matchsheet.py MatchSheet, MatchSheetTemplate, MatchSheetHeader, MatchSheetQuestion, CaseMatchSheetAnswer
      • agency.py MatchingFilterPreferences (per-agency config)
    • Directoryapi/
      • matching.py 84 KB — matching operations API
      • match_sheet.py 29 KB — match sheet API
    • Directoryviews/
      • admin_matching.py 22 KB — admin matching UI
      • match_sheet.py 8 KB — match sheet views

How matching works — the four sub-flows

There are four separate matching flows, each with its own logic:

flowchart TD
Agency["Agency staff\nmanages matching queue"]
Agency --> ED["ED Matching\n(egg donor to IP)"]
Agency --> GC["GC Matching\n(carrier to IP)"]
Agency --> SD["SD Matching\n(sperm donor to IP)"]
Agency --> SDManual["Pre-matching\n(manual ED/SD selection)"]
ED --> Queue["MatchingQueue\n(ordered candidates)"]
GC --> Queue
SD --> Queue
Queue --> Present["Profile shown to IP"]
Present --> Decision{"IP Decision"}
Decision --> |Interested| Favorite["Favorite added"]
Decision --> |Declined| Next["Next candidate shown"]
Favorite --> Proposal["Formal match proposal"]
Proposal --> Accept["Match accepted\n→ CaseParty created"]
Proposal --> Decline["Match declined\n→ MatchBreakDetails"]

Data model

erDiagram
MatchingQueue {
int case_id
int candidate_user_id
string candidate_type
int position
string status
}
Favorite {
int case_id
int candidate_user_id
string candidate_type
datetime created_at
}
MatchBreakDetails {
int case_id
int candidate_user_id
string reason_code
text notes
}
MatchingPreferences {
int case_id
json preferences
}
MatchSheetTemplate {
int agency_id
string name
string case_type
}
MatchSheetQuestion {
int template_id
string question_text
string question_type
}
CaseMatchSheetAnswer {
int case_id
int question_id
text answer
}
MatchingQueue }|--|| Case : "for"
Favorite }|--|| Case : "for"
MatchBreakDetails }|--|| Case : "for"
MatchSheetTemplate ||--o{ MatchSheetQuestion : "has"
CaseMatchSheetAnswer }|--|| Case : "for"

The matching queue

The MatchingQueue model tracks which candidates are “in queue” to be shown to a specific IP. The agency controls this queue — adding candidates, ordering them, and removing them.

Key concepts:

  • Position — the order in which candidates are presented to the IP
  • Status — current state of the candidate in this IP’s queue (pending, viewed, declined, favorited, matched)
  • The agency can see the full queue; the IP sees only the current candidate being presented
# Get all candidates in an IP's queue
queue = models.MatchingQueue.query.filter_by(
case_id=case.id
).order_by(models.MatchingQueue.position).all()
# Check if a candidate is favorited
fav = models.Favorite.query.filter_by(
case_id=case.id,
candidate_user_id=candidate_user_id
).first()

Match sheets

A match sheet is a structured document comparing both parties in a proposed match. It contains answered questions from both the IP and the donor/carrier, displayed side by side for easy comparison.

Match sheets use a separate template system (MatchSheetTemplate, MatchSheetQuestion) that is different from the main Forms module. Agencies build match sheet templates in the admin settings, and answers are stored as CaseMatchSheetAnswer records.

Agency-level matching configuration

MatchingFilterPreferences (in models/agency.py) controls the matching UI per agency:

  • Which filter options appear (age range, ethnicity, education level, etc.)
  • Which filters are visible to IPs vs. admin-only
  • Default filter values

Match break tracking

When a match proposal is declined or falls apart, a MatchBreakDetails record captures why:

  • Reason code (e.g., medical incompatibility, IP changed their mind, candidate withdrew)
  • Free-text notes from the admin

This data feeds into agency reporting — understanding why matches fail helps agencies improve their processes.

Gotchas