Skip to content

User Roles & Permissions

SeedTrust has six distinct user types. Each type sees a different version of the platform, has access to different actions, and has different relationships to cases. Getting this right is critical — the wrong access can expose financial data or allow an approval step to be bypassed.


The Six User Types

Admin

A SeedTrust employee. Admins operate the internal platform and have access to all cases across all agencies. Their exact capabilities are controlled by a granular permission system (see Admin Permissions below).

Every Admin is assigned one of four operational roles, which defines their job function:

  • Escrow Specialist — manages the financial side of cases
  • Payment Manager — processes and approves payment batches
  • Service Manager — handles case operations and client service
  • Sales Manager — manages agency relationships

These roles do not directly grant permissions — they are a label. Actual permissions are controlled by the AdminRole assigned to the admin.

Code: UserTypes.ADMIN, Admin, AdminTypes


Agency Owner

Called Agency Administrator in the UI. Works at a surrogacy or egg donation agency and can see all cases associated with their agency. Depending on how the agency and individual cases are configured, they may be able to:

  • View case status, payment history, and compensation plans
  • Submit or review disbursement requests
  • Approve DRs (if set as the DR approval authority on a case)

Agency Owners do not have the granular Admin permission system — their access is controlled at the case and agency configuration level.

Naming note: “Agency Owner” is the code and database term. “Agency Administrator” is what appears in the UI. Do not confuse with AgencyAdmin, which is a SeedTrust employee (an Admin) assigned to oversee an agency in a specific operational role (Escrow Specialist, Payment Manager, etc.).

Code: UserTypes.AGENCY_OWNER, AgencyOwner


Case Manager (CM)

Assigned to specific cases (not all cases). Oversees the day-to-day of a surrogacy journey. Key responsibilities:

  • Reviews disbursement requests before they go for final approval, particularly pre-GSA
  • Communicates with surrogates and IPs about the case
  • Can be the DR approval authority on a case

Like Agency Owners, CMs do not use the Admin permission system — their access is scoped to their assigned cases.

Code: UserTypes.CASE_MANAGER


Intended Parent (IP)

The individual or couple funding the escrow account. Their platform access is primarily read-only:

  • View case status and current stage
  • View transaction and payment history
  • Sign the escrow agreement
  • In some case configurations, approve disbursement requests

IPs can be set as the DR approval authority on a case, which gives them approve/deny capability.

Code: UserTypes.INTENDED_PARENT, IntendedParent


IP Representative (IP Rep)

An attorney or authorized representative acting on behalf of an Intended Parent. Has the same capabilities as an IP and can:

  • Sign the escrow agreement on behalf of the IP
  • Approve disbursement requests when set as the approval authority

Code: ip_rep in Flask, UserTypes.IP_COMPANY in FastAPI (legacy alias — use IP Rep as the standard term)


Surrogate / Egg Donor

The individual carrying the pregnancy or donating eggs. Their access depends on the surrogate_access level configured on their case:

Access LevelWhat the Surrogate Can See
NONENo financial details
PARTIALLimited financial visibility
PART_BALCan see partial balance information
FULLFull financial visibility

Whether a surrogate can submit disbursement requests is a separate setting (surrogate_dr_submit) configured per case.

Code: UserTypes.SURROGATE, Surrogate, SurrogateAccess


Same Email, Multiple Roles

The same person can have accounts under multiple user types. For example, someone might be both an Intended Parent on one case and a consultant on another. At login, the user selects which role they are logging in as. The user_type field determines which account and which access level is active for the session.

This is why the login form on the mobile app has a role selector, and why the FastAPI login endpoint requires both email_address and user_type.


Admin Permissions

The Admin permission system is the most complex part of access control in SeedTrust. It applies only to Admins — all other user types have their access controlled by case configuration, not this system.

How It Works

Every Admin is assigned an Admin Role (e.g., “Escrow Specialist”, “Admin Master”). Each Admin Role has a set of Permissions assigned to it. When an Admin attempts to perform an action, the system checks whether their role includes the required permission.

Admin → AdminRole → [Permission A, Permission B, Permission C, ...]

Roles and their permission assignments are managed in the database and can be changed without a code deployment.

Permission Categories

There are 122 permissions organized into 11 categories:

CategoryWhat It CoversExample Permissions
CaseCase viewing, editing, lifecycle managementCREATE_CASES, EDIT_CASE_STAGE, VIEW_LEDGER, DELETE_TRANSACTION
ACHACH form access and approvalVIEW_SURRO_ACH_FORMS, APPROVE_ACH_FORMS, VIEW_PROTECTED_INFO
DisbursementsDR dashboard, creation, editingCREATE_DRS, VIEW_DR_DASHBOARD, EDIT_DRS, DR_FAST_REVIEW_ACCESS
PaymentsPayment processing and approvalMAKE_PAYMENTS, APPROVE_PAYMENTS, AUTO_APPROVER, PULL_SPC
BankingNACHA/ACH batch managementCREATE_NACHA, DOWNLOAD_NACHA_HISTORY, UPLOAD_NACHA_SFTP, VIEW_CIP_SUBMISSIONS
DepositsDeposit creation and managementCREATE_DEPOSITS, PAY_DEPOSITS, VIEW_DEPOSITS
AgencyAgency managementCREATE_AGENCY, EDIT_AGENCY, CM_MANAGEMENT
VendorVendor managementCREATE_VENDOR, APPROVE_VENDOR
CompanyCompany-level settingsUSER_MANAGEMENT, MANAGE_PERMISSIONS, EMAIL_TEMPLATES
ReportsReporting and analytics accessFINANCIAL_STATS, METRICS_ACCESS, GENERATE_1099
Partner ProgramPreferred partner points systemVIEW_PARTNER_PROGRAM, ADD_POINTS, REDEEM_POINTS

Code: Permission class in seedtrust_flask/seedtrust/models/admin_permissions.py

Master Admin Role

An “Admin Master” role exists that has every permission assigned. This is the super-admin role used by SeedTrust’s most senior staff. A standard “Admin” role also exists as a baseline.

Code: AdminPermission.init_master_admin_role()


How Permissions Are Checked in Code

Flask (server-rendered pages)

Route-level: A @authcheck() decorator is applied to view functions. It accepts:

  • user_types — which roles are allowed to access this route at all
  • perm_check — a Permission enum value (or list) the admin must have
  • category_check — the admin must have any permission in this category
@authcheck(user_types=["admin"], perm_check=Permission.Disbursements.CREATE_DRS)
def create_disbursement():
...

If the user doesn’t pass the check, they get a 403 or are redirected.

Template-level: A Jinja2 template extension allows conditional rendering based on permissions:

{% perm_check Permission.Case.EDIT_CASE_DETAILS %}
<button>Edit Case</button>
{% endperm_check %}
{% perm_check not Permission.Banking.CREATE_NACHA %}
<p>You don't have access to create batches.</p>
{% endperm_check %}

Code: authcheck in utils/auth.py, PermissionExtension in permission_extension.py

FastAPI (mobile API)

FastAPI does not yet use the granular Admin permission system. It only checks user_type (from the JWT token). An admin who logs in via FastAPI is treated as a generic admin — their specific role permissions are not enforced at the API layer.

Permission checking in FastAPI routes uses dependency injection:

@router.get("/cases")
async def list_cases(current_user: CurrentUser, db: DbSession):
# current_user is the authenticated user; type is available via current_user.user_type
...

Planned improvement: As more Admin functionality moves to FastAPI, granular permission enforcement needs to be added. Until then, any authenticated Admin can reach any Admin endpoint in the FastAPI layer. If you need to enforce a specific permission in a new FastAPI endpoint, manually check against the AdminRole/AdminPermission tables and document that you’ve done so.


Common Gotchas

Admin vs AgencyAdmin are completely different things. An Admin is a SeedTrust employee with the full permission system. An AgencyAdmin is a person at an agency with elevated access within that agency only. The class names and table names differ — do not confuse them.

Permission checks in Flask are inconsistent. Some routes use the @authcheck decorator correctly. Others use inline checks like if g.admin.has_permission(...) or even if g.admin.admin_type == "Escrow Specialist". When adding a new permission check, always use the decorator — do not add inline checks.

FastAPI has no granular permissions. Any admin authenticated via FastAPI can hit any admin endpoint. This is a known gap and should be addressed as FastAPI grows.

Multiple accounts per email. Always include user_type when querying for a user. Querying by email alone can return the wrong record if a person has accounts under multiple roles.

Permission cache. The Admin model caches permissions per request via permission_cache. If you update role permissions in the database during a session, the change won’t be reflected until the user’s next request. Use admin.refresh_permissions() to force a reload if needed.

Code: Admin.permission_cache, Admin.refresh_permissions()