Multi-tenancy
First-class Filament tenant scoping is on the roadmap. This page describes what works today and how to scope approvals to a tenant in the meantime.
How flows are matched today
submitForApproval() resolves a flow by model type only:
ApprovalFlow::activeFor($record::class);
There is no built-in tenant column on flows yet, so in a multi-tenant app you have two solid options.
Option A — pass the flow explicitly (recommended)
Skip auto-resolution and hand the run the tenant's flow. This keeps you in full control and needs no plugin changes:
$flow = $tenant->approvalFlows() // your own relation / lookup
->where('model_type', PurchaseOrder::class)
->where('is_active', true)
->firstOrFail();
$record->submitForApproval(flow: $flow);
Store the tenant key on the flow yourself (e.g. a team_id column added in your own migration) and query it however your app models tenancy.
Option B — tenant-aware approver resolution
Even with a shared flow, keep approvers tenant-correct by writing a custom resolver that filters users to the current tenant:
public function resolve(ApprovalStep $step): Collection
{
return User::role($step->approver_role)
->where('team_id', Filament::getTenant()?->getKey())
->get();
}
Because the Inbox uses its own query, override it too so users only see approvals for their tenant — filter on the approvable's tenant relation.
Scoping what users see
The Approval Inbox lists everything the user can act on across the app. In a tenant panel, constrain it by extending the inbox page and scoping the base query to the active tenant (e.g. whereHasMorph('approvable', ..., fn ($q) => $q->where('team_id', $tenant->id))).
What's coming
Planned first-class support will add an optional tenant column to flows, tenant-scoped activeFor(), and a tenant-aware Inbox out of the box, matching Filament's native tenancy. Track progress on the roadmap. Until then, Options A and B cover production multi-tenant use.
Next: Upgrade (v4 ↔ v5).