Case Lifecycle
A case represents a single surrogacy or egg donation arrangement. It is the central entity in SeedTrust — every payment, document, message, and approval belongs to a case. Understanding how a case moves through its lifecycle is foundational to understanding everything else.
Case Types
There are two case types, and they follow different stage progressions:
- SURROGACY — A gestational surrogacy arrangement. Includes pregnancy tracking, heartbeat confirmation, and delivery milestones.
- EGG — An egg donation arrangement. Shorter lifecycle, no pregnancy tracking.
Code:
Case.case_type,CaseTypes.SURROGACY,CaseTypes.EGG
Surrogacy Case Stages
Surrogacy cases move through ten stages in order:
flowchart TD S1["1. Awaiting Escrow Agreement Execution\nWaiting for IP + Surrogate to sign escrow agreement"] S2["2. Pending Funding\nWaiting for IP to wire initial escrow funds"] S3["3. Awaiting Executed GSA\nWaiting for Gestational Surrogacy Agreement to be signed"] S4["4. Awaiting Heart Beat Confirmation\nWaiting for pregnancy confirmation via ultrasound"] S5["5. Pregnant\nPregnancy confirmed — full compensation plan active"] S6["6. Post-Delivery Monitoring\nBaby delivered — monitoring period before closure"] S7["7. Awaiting Approval to Close\nAll parties confirmed ready to close"] S8["8. Final Accounting\nFinal financial reconciliation"] S9["9. Final Disbursement\nLast payments being processed"] S10["10. Closed and Archived\nCase fully closed"]
S1 --> S2 --> S3 --> S4 --> S5 --> S6 --> S7 --> S8 --> S9 --> S10One automatic transition exists: When a pregnancy date is recorded and the compensation plan has a calendar date set, the system automatically advances from Awaiting Heart Beat Confirmation → Pregnant and sends a funding notification email to Intended Parents.
All other transitions are manual — an Admin updates the stage in the UI.
Egg Donation Case Stages
Egg donation cases follow a shorter eight-stage progression with no pregnancy tracking:
flowchart TD E1["1. Awaiting Escrow Agreement Execution"] E2["2. Pending Funding"] E3["3. Egg Retrieval"] E4["4. Compensation Payment"] E5["5. Awaiting Approval to Close"] E6["6. Final Accounting"] E7["7. Final Disbursement"] E8["8. Closed and Archived"]
E1 --> E2 --> E3 --> E4 --> E5 --> E6 --> E7 --> E8What Actually Controls Stage
Stage vs. Pre-GSA Flag — These Are Different Things
A common source of confusion: the case stage and the pre_gsa boolean are separate fields that do not automatically stay in sync.
Case.stage_id— an integer FK referencingcase_stage.stage_id; the human-readable name (e.g., “Awaiting Executed GSA”) comes from the relatedCaseStagerowCase.pre_gsa— a boolean flag that controls approval rules and payment restrictions
pre_gsa = True means the case is operating under pre-GSA rules (stricter approvals, CM review required). It does not automatically flip to False when the stage advances past “Awaiting Executed GSA” — it must be explicitly updated. Always check both fields when diagnosing payment or approval behavior.
Fields That Gate Behavior
The stage name is mostly informational. The fields that actually gate what can or cannot happen on a case are:
| Field | What It Controls |
|---|---|
Case.pre_gsa | Whether CM review is required before DR approval |
Case.payment_halted | Completely blocks all outgoing payments regardless of approval |
Case.surrogate_access | What financial information the surrogate can see |
Case.surrogate_dr_submit | Whether the surrogate can submit DRs |
Case.dr_auto_approve | Whether DRs bypass human review |
Case.compensation_approved | Whether the compensation plan is active |
Case.ledger_status | Whether the account balance is sufficient (GOOD, LOW, INSUFFICIENT) |
Case Creation
When an Admin creates a case, the initial stage is set automatically based on whether the admin company has the escrow agreement display feature enabled:
- If enabled → Stage 1 (Awaiting Escrow Agreement Execution)
- If disabled → Stage 2 (Pending Funding) — skips the escrow agreement step
For EGG cases, redaction is also enabled by default (is_redacted = True) and surrogate access is set to NONE.
Code:
seedtrust_flask/seedtrust/views/admin/case/case_creation.py
Escrow Agreement Signing
Before a case can advance past Stage 1, the escrow agreement must be signed. Signing is tracked separately per party:
CaseSurrogate.escrow_signed— timestamp when the surrogate signedCaseIP.escrow_signed— timestamp when the IP signed
Egg donation cases: Donors do not sign an escrow agreement. Only the IP signs.
If the escrow agreement template is updated after a party has already signed, the needs_resignature flag is set on that party’s record, and they must sign again before payments can proceed.
The Automatic Pregnant Transition
This is the only stage transition that happens without Admin intervention. The system advances the case from Awaiting Heart Beat Confirmation → Pregnant when all of the following are true:
- Case type is
SURROGACY Case.date_of_pregnancyhas been recorded- The base compensation plan item has a
calendar_dateset Case.close_without_pregnancyis not setCase.actual_delivery_dateis not already set
Side effect: If the case has include_pregnancy_requirements = True and a pregnancy_funding_requirement set, a funding notification email is automatically sent to all Intended Parents on the case.
Code:
advance_case_stage_pregnant()inseedtrust_flask/seedtrust/utils/flaskutils.py
Who Can Change Stages
Stage changes are Admin-only in practice. There is no formal validation in code that prevents an Admin from jumping stages out of order — the stage field is treated as an informational label rather than an enforced state machine.
Case access for non-Admin users is scoped to their assigned cases:
- Case Managers — only cases they are assigned to
- Surrogates — only their own cases
- Intended Parents — only their own cases
- IP Representatives — only their assigned cases
- Agency Owners — all cases from their agency
Code:
case_access_check()in Flask views
Case Closure
A case can be closed in two special ways outside the normal stage progression:
Case.close_without_pregnancy— closes a surrogacy case that never resulted in a pregnancyCase.close_without_funding— closes a case that was never funded
Both are flags set by Admins and affect how the final accounting is handled.
How the API Returns Stage Information
Both the case list and case detail endpoints return the stage as a plain string (the stage name, not the ID):
{ "case_stage": "Pregnant"}The frontend uses this string directly for display. There is no numeric stage ID exposed in the API.
Code:
seedtrustapi/src/seedtrust/modules/case/service.py
Gotchas for Developers
Stage and pre_gsa are independent. A case can be in “Post-Delivery Monitoring” and still have pre_gsa = True if it was never manually flipped. Always check both when writing approval or payment logic.
Stage transitions are not validated in code. There is no state machine enforcement. An Admin can manually set a surrogacy case to “Closed and Archived” from “Awaiting Escrow Agreement Execution”. This means data integrity relies on Admin discipline, not code guards.
The stage is a lookup table, not an enum in the DB. Stage IDs are integers referencing the case_stage table. The enum values in enums.py are string names only — they don’t map directly to stage_id integers. Always join to case_stage when querying; never hardcode a stage integer.
EGG cases start on different stage IDs. Surrogacy stages and egg stages share the same case_stage table but occupy different rows (surrogacy starts at ID 1, egg starts at ID 15). Stage IDs do not carry consistent meaning across case types — “Pending Funding” is ID 2 for surrogacy and ID 16 for egg. Always join to case_stage and filter by case_type; never hardcode a stage ID without knowing the case type.