Skip to content

Repository files navigation

Multi-Tenant SaaS Task Management API (Laravel)

API-first backend for a multi-tenant task management platform. Tenant isolation is enforced at the request, query, and authorization layers.

Tenant Isolation Strategy

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.

Auth Flow (Sanctum)

  • Register: creates a user and a first organization, returns a token.
  • Login: issues a personal access token.
  • Authenticated requests: Authorization: Bearer {token}.

API Error Format

{
  "message": "Validation failed.",
  "errors": { "field": ["Error message"] },
  "code": "validation_error"
}

Core Endpoints (v1)

Auth:

  • POST /api/v1/auth/register
  • POST /api/v1/auth/login
  • POST /api/v1/auth/logout
  • GET /api/v1/auth/me
  • POST /api/v1/invites/accept

Organizations:

  • GET /api/v1/organizations
  • POST /api/v1/organizations
  • POST /api/v1/orgs

Tenant-scoped (requires X-Organization-Id):

  • GET /api/v1/members
  • PATCH /api/v1/members/{userId}/role
  • POST /api/v1/orgs/{org}/transfer-ownership
  • POST /api/v1/orgs/{org}/invites
  • DELETE /api/v1/orgs/{org}/members/{userId}
  • POST /api/v1/orgs/{org}/leave
  • GET /api/v1/projects
  • POST /api/v1/projects
  • GET /api/v1/projects/{project}
  • PATCH /api/v1/projects/{project}
  • POST /api/v1/projects/{project}/archive
  • GET /api/v1/tasks
  • POST /api/v1/tasks
  • GET /api/v1/tasks/{task}
  • PATCH /api/v1/tasks/{task}
  • DELETE /api/v1/tasks/{task}
  • POST /api/v1/tasks/{task}/status
  • GET /api/v1/audit-logs

ER Diagram

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
  }
Loading

Key Architectural Decisions

  • 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.

Setup

  1. composer install
  2. cp .env.example .env
  3. Configure database credentials in .env
  4. php artisan key:generate
  5. php artisan migrate

Notes

  • Ensure X-Organization-Id is supplied for tenant-scoped endpoints.

Ownership, Invites, and Membership Rules

  • 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.

Onboarding Paths

  • Register without an org: POST /api/v1/auth/register with name,email,password.
  • Register with org: include organization_name to create an org and become owner.
  • Accept invite: POST /api/v1/invites/accept with token (and name,password if user does not exist).
  • Auth /me returns active organizations and optional active role when X-Organization-Id is provided.
  • Audit logs capture project and task changes plus role assignments.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages