Skip to content

Multi-project

A project is the tenancy boundary in z4j. Agents, tasks, events, schedules, memberships - all scoped to a project.

Use one project per:

  • Environment (staging, production).
  • Business unit (marketing-queue, billing-queue).
  • Customer (if you're running z4j for your customers).
  • First project is created automatically at first-boot setup.
  • Subsequent projects: API POST /api/v1/projects (requires org-level admin).
Terminal window
curl -X POST https://z4j.example.com/api/v1/projects \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"slug": "billing-prod", "name": "Billing (production)"}'

slug is required. The bearer token is a personal API key; a cookie session can call the same route but must also send the CSRF header.

When you mint an agent token, you pick the project. The token is bound - an agent authenticated with token_A can only write to project_A.

A user can belong to many projects with different roles per project:

User project: staging project: billing-prod
alice admin admin
bob operator viewer
charlie viewer none

See RBAC.

The dashboard's navigation rail (the left sidebar) shows the active project. Switching reloads the tasks/events views with the new scope.

Audit rows carry project_id. Export is per-project through the project's own route, GET /api/v1/projects/{slug}/audit?format=csv; there is no project_id query parameter.

DELETE /api/v1/projects/{slug} is a soft archive, not a hard delete. It requires a system administrator, CSRF protection, and a fresh MFA check. The brain refuses to archive the last active project.

Archiving sets is_active=false and hides the project from ordinary list views. Agents, tasks, events, schedules, memberships, and audit rows remain in the database. There is no public hard-delete or typed-confirmation workflow.

The project's agents are disconnected as part of the archive: live sockets close with the revoked-agent close code on every replica, new handshakes are refused, and the long-poll routes answer 403 with error: project_inactive. Their tokens stay valid, so reactivating the project needs no re-minting; the agents reconnect on their next retry.

Not supported. Each project is isolated. If you need cross-project metrics, export audit and events via API and aggregate externally.