Workflow: Clinic Onboarding
How a new veterinary clinic is provisioned on Vetty.
Actors
- System Admin (Vetty operator)
- New clinic Admin user
Preconditions
- Clinic admin email is not already used by any
usersrow. - Intended clinic slug is globally unique.
Happy path
Step by step
- In the Filament panel → Tenants → New (or via API).
- Fill:
name(display name)slug(URL identifier, unique)email(primary clinic email)phone,address(optional)subscription_plan— defaults totrial- Clinic admin credentials:
owner_name,owner_email,password
- Submit →
StoreTenantRequestvalidates,TenantController::storeruns the three inserts inside a single request. - API returns
{ tenant, user }. - Vetty shares the login details with the clinic.
- Clinic admin logs in at
/auth/login. Theircurrent_tenantis populated automatically. - Clinic admin sees the SPA pre-filled with an empty dashboard — no patients, no appointments yet.
What's created in the DB
| Table | Row |
|---|---|
tenants | { name, slug, email, phone, address, subscription_plan='trial', status='active' } |
users | { name, email, phone, account_type='staff', password=hashed, current_tenant_id=<tenant.id> } |
tenant_user | { tenant_id, user_id, role='clinic_admin', status='active', is_default=true } |
Next onboarding tasks for the clinic admin
- Add additional staff: create a user + a
tenant_userpivot row per person. (Currently requires Filament or API calls; a self-service "invite staff" screen is a good follow-up.) - Customize the clinic details — address, phone, logo upload (future).
- Import or manually enter existing clients and pets using the Patients module.
Edge cases
- Slug collision —
StoreTenantRequestrejects with 422. - Email collision — either
tenants.emailorusers.emailunique violation. - Partial failure — today the controller doesn't wrap the three inserts in a transaction. A recommended patch:
DB::transaction(function () use ($request, &$tenant, &$user) {$tenant = Tenant::create([...]);$user = User::create([...]);$tenant->users()->attach($user->id, [...]);});
Deactivation / offboarding
To suspend a clinic:
- System Admin opens the tenant in Filament.
- Sets
status = 'suspended'. ResolveTenantMembershipimmediately starts returning403for all/tenant/{id}/…requests — the clinic is functionally paused without any data loss.
To cancel (terminal):
- Set
status = 'cancelled'. - After a retention window, a soft-delete cascade could be added (not yet implemented).