Skip to content

Guides

Organizations & projects

Every request runs inside one organization and one project. Learn what they are, how a request chooses them, who can reach which project, and how to structure workspaces for your product.

The ownership model

Mahvash organizes everything you create into a two-level hierarchy:

  • An organization is the tenant boundary. It has members with roles, one wallet that pays for all its work, usage limits and budgets. Organizations are completely invisible to each other.
  • A project is a workspace inside an organization. Responses, conversations, files, assets, vector stores, prompts, data sources, webhooks and API keys all belong to exactly one project.

A typical company layout looks like this:

Organization "Acme"                    tenant, members, wallet, limits
├─ Members   Sara (ADMIN), Ali (MEMBER), Nadia (VIEWER)
├─ Teams     Support, Marketing
├─ Project "default"      open to every member         requests with no selector land here
├─ Project "support"      open to every member         support copilot: keys, files, prompts
└─ Project "campaigns"    restricted: Marketing team   only granted teams can reach it

Every account is created with a personal organization and a default project, so a single developer can ignore this page entirely: requests without any selector land in that default project. The structure becomes important when you add teammates, serve several customers, or want to separate environments.

Note:Ownership is fixed. A project never moves to another organization, and a resource never moves to another project. To reorganize, create the new resource in its new home.

What a project gives you

Projects are the main tool for isolation inside an organization. Everything in one project shares:

  • Data — a file, store or response from one project is not visible from another. A foreign ID answers 404, exactly like an ID that never existed.
  • Credentials — an API key reaches exactly one project.
  • Configuration — default instructions, prompts, retrieval data sources, managed tool settings and default retrieval placement.
  • Limits and budgets — request rate, concurrent runs and a monthly spend ceiling can be set per project.

Common ways to split work into projects:

  • one project per product feature (a support copilot and an internal search tool have different data and prompts);
  • one project per environment (staging and production never share keys or documents);
  • one project per customer when you build a multi-tenant SaaS — see Multi-customer SaaS integration.

How a request chooses its organization and project

Mahvash resolves the organization first and the project second, before it looks anything up or starts any work. Two optional headers steer the choice:

SelectorWith a user JWTWith a project API key
X-Mahvash-OrganizationChooses one of your active memberships, by UUID or slugAlready pinned; a different value is refused
X-Mahvash-ProjectChooses a project you can reach inside that organization, by UUID or slugAlready pinned; a different value is refused
No selectorYour personal organization and its default projectThe key's pinned organization and project

Resolution happens in a fixed order:

OrderOrganization comes fromProject comes from
1The organization pinned to the API keyThe project pinned to the API key
2X-Mahvash-Organization, among your active membershipsX-Mahvash-Project, among projects you can reach
3Your personal organizationA project field in the body, where the endpoint accepts one (must agree with the header)
4—The organization's default project

Some important consequences:

  • X-Mahvash-Organization only chooses among organizations you are an active member of. It can never reach an organization you do not belong to.
  • X-Mahvash-Project narrows a request; it can never widen what the caller may reach.
  • An unknown organization or project selector answers 404 rather than silently falling back. A client that thinks it is working for one company must never quietly work in another.
  • A project named in the request body (where an endpoint accepts project) must agree with the header.
  • Continuing a conversation with previous_response_id or chat_id stays in the project that owns that conversation.
# A person working in the "support" project of the "acme" organization
curl https://api.mahvashai.ir/v1/responses \
  -H "Authorization: Bearer $MAHVASH_USER_JWT" \
  -H "X-Mahvash-Organization: acme" \
  -H "X-Mahvash-Project: support" \
  -H "Content-Type: application/json" \
  -d '{"model": "auto", "input": "Summarize today\u0027s open tickets."}'

# A project API key already knows its organization and project
curl https://api.mahvashai.ir/v1/responses \
  -H "Authorization: Bearer $MAHVASH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "auto", "input": "Summarize today\u0027s open tickets."}'

Roles and project access

Every member has one organization role. Roles are ordered: VIEWER < MEMBER < ADMIN < OWNER.

RoleTypical capabilities
VIEWERRead projects, conversations, files, stores and prompts it can reach
MEMBEREverything a viewer can, plus create responses, files, vector stores, batches and prompts
ADMINEverything a member can, plus manage projects, API keys, grants, webhooks, members, teams, audit and usage reports
OWNERFull control of the organization; the last owner cannot be removed or demoted

Nobody can grant a role above their own, and an organization always keeps at least one owner.

Organization-wide and restricted projects

By default a project is open to every active member of the organization, at their organization role. Where your organization uses restricted projects, a restricted project is reachable only by owners, administrators, and the teams or members named in an access grant:

# Create a team, then give it access to one restricted project
curl -X POST https://api.mahvashai.ir/api/v1/tenancy/teams \
  -H "Authorization: Bearer $MAHVASH_USER_JWT" \
  -H "X-Mahvash-Organization: acme" \
  -H "Content-Type: application/json" \
  -d '{"name": "Marketing"}'

curl -X POST https://api.mahvashai.ir/api/v1/tenancy/projects/<project-uuid>/grants \
  -H "Authorization: Bearer $MAHVASH_USER_JWT" \
  -H "X-Mahvash-Organization: acme" \
  -H "Content-Type: application/json" \
  -d '{"team": "<team-uuid>", "role": "MEMBER"}'

Two rules are easy to get wrong:

  • A grant widens where you can go, never what you can do there. The effective role is the lower of the organization role and the grant role, so a VIEWER holding a MEMBER grant can still only read.
  • A project you cannot reach looks like it does not exist. It is reported as 404, not 403, so lists and IDs never reveal what other teams have.

Limits and budgets

An organization and each of its projects (and each API key) can carry a request rate (requests_per_minute), a cap on concurrently running work (max_active_runs), and — for organizations and projects — a monthly budget with an alert threshold:

curl -X PATCH https://api.mahvashai.ir/api/v1/tenancy/organizations/settings \
  -H "Authorization: Bearer $MAHVASH_USER_JWT" \
  -H "X-Mahvash-Organization: acme" \
  -H "Content-Type: application/json" \
  -d '{"requests_per_minute": 120, "max_active_runs": 4, "monthly_budget_usd": "500.00", "budget_alert_percent": 80}'
  • The tightest limit wins. A project cannot raise its organization's limit and a key cannot raise its project's. Setting a small limit on a key you hand to a partner is the simplest way to contain it.
  • null means "inherit", not "unlimited".
  • Over the request rate you receive 429 with Retry-After; over the concurrency cap the new run is refused at admission.
  • monthly_budget_usd is a hard ceiling measured per calendar month in UTC: work that would cross it is refused. budget_alert_percent only reports, it never blocks.

Managing a workspace through the API

Workspace management uses the /api/v1/tenancy endpoints with a user JWT — never a project key. Send X-Mahvash-Organization to choose which organization you are managing. A typical provisioning script:

  1. 1List the organizations you belong to with GET /api/v1/tenancy/organizations, and confirm what a request resolves to with GET /api/v1/tenancy/organization.
  2. 2Create a project with POST /api/v1/tenancy/projects.
  3. 3Optionally configure it: prompts, retrieval data sources, managed tool settings.
  4. 4Issue a project API key and store it in your secret manager.
  5. 5Hand the key to the service that will make AI calls.
curl https://api.mahvashai.ir/api/v1/tenancy/organizations \
  -H "Authorization: Bearer $MAHVASH_USER_JWT"

# Which organization, project and role did this request resolve to?
curl https://api.mahvashai.ir/api/v1/tenancy/organization \
  -H "Authorization: Bearer $MAHVASH_USER_JWT" \
  -H "X-Mahvash-Organization: acme"
curl -X POST https://api.mahvashai.ir/api/v1/tenancy/projects \
  -H "Authorization: Bearer $MAHVASH_USER_JWT" \
  -H "X-Mahvash-Organization: acme" \
  -H "Content-Type: application/json" \
  -d '{"name": "Support assistant", "slug": "support", "description": "Ticket drafting"}'

Creating projects, issuing keys, managing grants and webhooks all require the ADMIN role. The default project cannot be archived or deleted, because it is where requests without a selector land. Deleting any other project archives it and keeps its history for accounting.

Related references: Organizations, Projects, API credentials, Members, Teams and Access grants.