API-first backend for a multi-tenant task management platform. Tenant isolation is enforced at the request, query, and authorization layers.
Header-based tenant resolution using X-Organization-Id.
Why header-based?
- Works cleanly for API-first clients without DNS/subdomain constraints.
- Keeps tenant selection explicit per request.
- Compatible with users belonging to multiple organizations.
The tenant middleware validates membership and stores the tenant context. All tenant-owned models use a global scope that applies organization_id automatically.
- Register: creates a user and a first organization, returns a token.
- Login: issues a personal access token.
- Authenticated requests:
Authorization: Bearer {token}.
{
"message": "Validation failed.",
"errors": { "field": ["Error message"] },
"code": "validation_error"
}
Auth:
POST /api/v1/auth/registerPOST /api/v1/auth/loginPOST /api/v1/auth/logoutGET /api/v1/auth/mePOST /api/v1/invites/accept
Organizations:
GET /api/v1/organizationsPOST /api/v1/organizationsPOST /api/v1/orgs
Tenant-scoped (requires X-Organization-Id):
GET /api/v1/membersPATCH /api/v1/members/{userId}/rolePOST /api/v1/orgs/{org}/transfer-ownershipPOST /api/v1/orgs/{org}/invitesDELETE /api/v1/orgs/{org}/members/{userId}POST /api/v1/orgs/{org}/leaveGET /api/v1/projectsPOST /api/v1/projectsGET /api/v1/projects/{project}PATCH /api/v1/projects/{project}POST /api/v1/projects/{project}/archiveGET /api/v1/tasksPOST /api/v1/tasksGET /api/v1/tasks/{task}PATCH /api/v1/tasks/{task}DELETE /api/v1/tasks/{task}POST /api/v1/tasks/{task}/statusGET /api/v1/audit-logs
erDiagram
USERS ||--o{ ORGANIZATION_USER : member_of
ORGANIZATIONS ||--o{ ORGANIZATION_USER : has_members
ORGANIZATIONS ||--o{ PROJECTS : owns
ORGANIZATIONS ||--o{ TASKS : owns
ORGANIZATIONS ||--o{ AUDIT_LOGS : logs
PROJECTS ||--o{ TASKS : contains
USERS ||--o{ TASKS : assigned
USERS {
bigint id
string name
string email
}
ORGANIZATIONS {
bigint id
string name
string slug
}
ORGANIZATION_USER {
bigint id
bigint organization_id
bigint user_id
string role
}
PROJECTS {
bigint id
bigint organization_id
string name
datetime archived_at
}
TASKS {
bigint id
bigint organization_id
bigint project_id
string title
string status
bigint assigned_to
datetime due_at
datetime deleted_at
}
AUDIT_LOGS {
bigint id
bigint organization_id
bigint actor_id
string action
string entity_type
bigint entity_id
}
- Tenant context lives in middleware and a request-scoped container binding.
- Tenant scoping enforced by a global scope on tenant-owned models.
- RBAC via policies and organization roles (owner/manager/member).
- Membership lifecycle with active/removed status and ownership transfer rules.
- Service layer encapsulates project/task domain actions and audit logging.
- Soft deletes enabled for tasks.
composer installcp .env.example .env- Configure database credentials in
.env php artisan key:generatephp artisan migrate
- Ensure
X-Organization-Idis supplied for tenant-scoped endpoints.
- Each organization has exactly one owner.
- Owner cannot be removed or leave the organization unless ownership is transferred.
- Managers can invite/remove members only.
- Members can only leave the organization themselves.
- Membership removals are soft (status marked removed), and tasks are unassigned.
- Register without an org:
POST /api/v1/auth/registerwithname,email,password. - Register with org: include
organization_nameto create an org and become owner. - Accept invite:
POST /api/v1/invites/acceptwithtoken(andname,passwordif user does not exist). - Auth
/mereturns active organizations and optional active role whenX-Organization-Idis provided. - Audit logs capture project and task changes plus role assignments.