Skip to main content

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 users row.
  • Intended clinic slug is globally unique.

Happy path

Step by step

  1. In the Filament panel → Tenants → New (or via API).
  2. Fill:
    • name (display name)
    • slug (URL identifier, unique)
    • email (primary clinic email)
    • phone, address (optional)
    • subscription_plan — defaults to trial
    • Clinic admin credentials: owner_name, owner_email, password
  3. Submit → StoreTenantRequest validates, TenantController::store runs the three inserts inside a single request.
  4. API returns { tenant, user }.
  5. Vetty shares the login details with the clinic.
  6. Clinic admin logs in at /auth/login. Their current_tenant is populated automatically.
  7. Clinic admin sees the SPA pre-filled with an empty dashboard — no patients, no appointments yet.

What's created in the DB

TableRow
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_user pivot 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 collisionStoreTenantRequest rejects with 422.
  • Email collision — either tenants.email or users.email unique 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:

  1. System Admin opens the tenant in Filament.
  2. Sets status = 'suspended'.
  3. ResolveTenantMembership immediately starts returning 403 for all /tenant/{id}/… requests — the clinic is functionally paused without any data loss.

To cancel (terminal):

  1. Set status = 'cancelled'.
  2. After a retention window, a soft-delete cascade could be added (not yet implemented).