3PLAdmin Implementation Plan

Created Feb 26, 2026 public

Goals

Current Problems

No SetPassword onboarding: 3PLAdmin creation can route users to normal login without having set a password.

Overbroad access: /clients loads all clients and allows 3PLAdmin access, so 3PLAdmins can see global client data.

Key Design Decision

We will show child clients immediately in /clients so the 3PLAdmin can click into a child client and add couriers.

Data Model

erDiagram
    Clients {
        Guid ClientId PK
        Guid ParentThreePlClientId FK "nullable - UI scoping"
        bool IsThreePlOrg "default false"
    }
    ClientCourierLogin {
        Guid Id PK
        Guid ClientId FK
        Guid ManagedByThreePlClientId FK "nullable - source of truth"
    }
    ClientUsers {
        Guid UserId FK
        Guid ClientId FK
    }
    IdentityUser {
        Guid Id PK
        string Role "3PLAdmin or ClaimitAdmin"
    }

    Clients ||--o{ Clients : "ParentThreePlClientId"
    Clients ||--o{ ClientCourierLogin : "has"
    Clients ||--o{ ClientCourierLogin : "ManagedByThreePlClientId"
    Clients ||--o{ ClientUsers : "has"
    IdentityUser ||--o{ ClientUsers : "linked via"

Entity Definitions

Entity Description
3PL org A Clients row with IsThreePlOrg = true
3PLAdmin user Identity user with role 3PLAdmin, linked to the 3PL org client via ClientUsers
Child client A normal Clients row with ParentThreePlClientId = <3PLOrgClientId>
Courier ownership Modeled at CCL level via ClientCourierLogin.ManagedByThreePlClientId = <3PLOrgClientId>

Implementation Steps

Step 1: Ensure 3PLAdmin Uses Invite-Only SetPassword Flow

Update the admin/user creation paths so that 3PLAdmin is treated like an external/client user:

  1. Create IdentityUser + assign 3PLAdmin role
  2. Create ClientUser linking them to the 3PL org/home client
  3. Send onboarding email using existing SDK onboarding flow to /authenticate/user/password/set?pkey=...
  4. Verify the 3PLAdmin can complete SetPassword and then sign in

Files involved:

Step 2: Add BE Fields (Schema)

ClientCourierLogin:

Clients:

Step 3: Scope /clients for 3PLAdmin

flowchart TD
    A[User loads /clients] --> B{User role?}
    B -->|ClaimitAdmin| C[Load all clients globally]
    B -->|3PLAdmin| D[Determine ThreePlHomeClientId from ClientUsers]
    D --> E["Load clients WHERE ParentThreePlClientId = ThreePlHomeClientId"]
    E --> F[Optionally include org/home client in list]
    F --> G[Apply search/pagination to scoped list]

Files involved:

Step 4: Determine the 3PL "Home Client ID" in FE

For a logged-in 3PLAdmin, determine the 3PL org/home client from ClientUsers (the client the 3PLAdmin user is linked to). This is the ThreePlHomeClientId used for scoping and for stamping fields.

Files involved:

Step 5: Self-Serve Child Client Creation for 3PLAdmin

When a 3PLAdmin creates a new child client via the onboarding wizard:

  1. Create the new Client row
  2. Immediately set Clients.ParentThreePlClientId = <ThreePlHomeClientId>
  3. Ensure Clients.IsThreePlOrg remains false
  4. Hide/disable the "Company Type" selector for 3PLAdmins (they should only create child clients)

Files involved:

Step 6: Stamp CCL Ownership When Couriers Are Added

When a 3PLAdmin adds a courier login for a child client:

  1. Create ClientCourierLogin as usual
  2. Set ClientCourierLogin.ManagedByThreePlClientId = <ThreePlHomeClientId>

This ensures the courier/claims chain is correctly associated to the 3PL at the CCL level.

Files involved:

Step 7: Badges/Indicators for ClaimitAdmin (Optional)

On ClaimitAdmin views (clients table, claims table):

Portal UX Flow

sequenceDiagram
    participant CA as ClaimitAdmin
    participant Portal as Portal
    participant DB as Database
    participant TPA as 3PLAdmin
    participant Email as Email

    CA->>Portal: Create 3PL org + initial 3PLAdmin
    Portal->>DB: Create Clients row (IsThreePlOrg=true)
    Portal->>DB: Create IdentityUser + assign 3PLAdmin role
    Portal->>DB: Create ClientUsers row (user → 3PL org)
    Portal->>Email: Send onboarding email with SetPassword link

    Email->>TPA: /authenticate/user/password/set?pkey=...
    TPA->>Portal: Set password + sign in

    TPA->>Portal: View /clients
    Portal->>DB: Load WHERE ParentThreePlClientId = 3PLOrgId
    DB->>Portal: Return scoped child clients

    TPA->>Portal: Create child client
    Portal->>DB: Create Clients row + set ParentThreePlClientId
    Note over Portal,DB: Child appears immediately in /clients

    TPA->>Portal: Add courier for child client
    Portal->>DB: Create CCL row
    Portal->>DB: Set ManagedByThreePlClientId = 3PLOrgId

    Note over DB: Claims flow naturally through child client CCLs

BE Schema Summary

Field Table Type Purpose
ManagedByThreePlClientId ClientCourierLogin Nullable Guid FK Source of truth for 3PL ownership
ParentThreePlClientId Clients Nullable Guid FK UI scoping and performance
IsThreePlOrg Clients Bool Badges and filtering

Verification Checklist