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 (anAdmin) 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_repin Flask,UserTypes.IP_COMPANYin 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 Level | What the Surrogate Can See |
|---|---|
NONE | No financial details |
PARTIAL | Limited financial visibility |
PART_BAL | Can see partial balance information |
FULL | Full 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:
| Category | What It Covers | Example Permissions |
|---|---|---|
| Case | Case viewing, editing, lifecycle management | CREATE_CASES, EDIT_CASE_STAGE, VIEW_LEDGER, DELETE_TRANSACTION |
| ACH | ACH form access and approval | VIEW_SURRO_ACH_FORMS, APPROVE_ACH_FORMS, VIEW_PROTECTED_INFO |
| Disbursements | DR dashboard, creation, editing | CREATE_DRS, VIEW_DR_DASHBOARD, EDIT_DRS, DR_FAST_REVIEW_ACCESS |
| Payments | Payment processing and approval | MAKE_PAYMENTS, APPROVE_PAYMENTS, AUTO_APPROVER, PULL_SPC |
| Banking | NACHA/ACH batch management | CREATE_NACHA, DOWNLOAD_NACHA_HISTORY, UPLOAD_NACHA_SFTP, VIEW_CIP_SUBMISSIONS |
| Deposits | Deposit creation and management | CREATE_DEPOSITS, PAY_DEPOSITS, VIEW_DEPOSITS |
| Agency | Agency management | CREATE_AGENCY, EDIT_AGENCY, CM_MANAGEMENT |
| Vendor | Vendor management | CREATE_VENDOR, APPROVE_VENDOR |
| Company | Company-level settings | USER_MANAGEMENT, MANAGE_PERMISSIONS, EMAIL_TEMPLATES |
| Reports | Reporting and analytics access | FINANCIAL_STATS, METRICS_ACCESS, GENERATE_1099 |
| Partner Program | Preferred partner points system | VIEW_PARTNER_PROGRAM, ADD_POINTS, REDEEM_POINTS |
Code:
Permissionclass inseedtrust_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 allperm_check— aPermissionenum value (or list) the admin must havecategory_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:
authcheckinutils/auth.py,PermissionExtensioninpermission_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/AdminPermissiontables 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()