> For the complete documentation index, see [llms.txt](https://docs.otari.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.otari.ai/api-reference/organizations.md).

# Organizations

## Create Organization

> Create an organization with the caller as its owner.\
> \
> Takes a name; the slug is derived from it with a random suffix, so two\
> organizations may share a name and a later rename does not move the slug.\
> A default workspace is provisioned alongside, because an organization\
> without one has nowhere to hold a key, a budget or a usage row.\
> \
> The caller is \*\*not\*\* moved into it. Switching is a separate call\
> (\`\`POST /me/switch\`\`), so creating an organization does not change what the\
> rest of the caller's session is looking at.\
> \
> Where signup is closed, the caller must already be an active member of an\
> organization; a caller with none is refused with a 403.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"OrganizationCreateRequest":{"description":"Create an organization, with the caller as its owner.\n\nName only. The slug is derived server-side and never sent, because it is\nunique where the name is not: two organizations may share a name, and a\nrename deliberately does not move the slug.","properties":{"name":{"maxLength":255,"minLength":1,"title":"Name","type":"string"}},"required":["name"],"title":"OrganizationCreateRequest","type":"object"},"OrganizationPublic":{"properties":{"created_at":{"format":"date-time","title":"Created At","type":"string"},"created_by_user_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"Created By User Id"},"id":{"format":"uuid","title":"Id","type":"string"},"name":{"maxLength":255,"title":"Name","type":"string"},"slug":{"maxLength":255,"title":"Slug","type":"string"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Updated At"}},"required":["name","slug","id","created_at"],"title":"OrganizationPublic","type":"object"},"OrganizationRefusal":{"description":"The body of a request the organizations domain refuses.","properties":{"detail":{"description":"Why the request was refused.","title":"Detail","type":"string"}},"required":["detail"],"title":"OrganizationRefusal","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/organizations":{"post":{"description":"Create an organization with the caller as its owner.\n\nTakes a name; the slug is derived from it with a random suffix, so two\norganizations may share a name and a later rename does not move the slug.\nA default workspace is provisioned alongside, because an organization\nwithout one has nowhere to hold a key, a budget or a usage row.\n\nThe caller is **not** moved into it. Switching is a separate call\n(``POST /me/switch``), so creating an organization does not change what the\nrest of the caller's session is looking at.\n\nWhere signup is closed, the caller must already be an active member of an\norganization; a caller with none is refused with a 403.","operationId":"organizations-create_organization","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationCreateRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationPublic"}}},"description":"Successful Response"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationRefusal"}}},"description":"Signup is closed and the caller is not an active member of any organization."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Create Organization","tags":["organizations"]}}}}
```

## Get Active Organization Context

> Get the caller's active organization and their standing in it.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"OrganizationMembershipContextPublic":{"description":"An organization plus the caller's standing in it.\n\nWhat every tenancy page reads first: which organization it is looking at,\nand what the caller may do there.","properties":{"caller":{"anyOf":[{"$ref":"#/components/schemas/CallerIdentityPublic"},{"type":"null"}]},"deployment_operator":{"default":false,"title":"Deployment Operator","type":"boolean"},"organization":{"$ref":"#/components/schemas/OrganizationPublic"},"organization_member_id":{"format":"uuid","title":"Organization Member Id","type":"string"},"provider_key_encryption_available":{"default":false,"title":"Provider Key Encryption Available","type":"boolean"},"role":{"title":"Role","type":"string"},"status":{"title":"Status","type":"string"},"workspace_memberships":{"items":{"$ref":"#/components/schemas/CallerWorkspaceMembershipPublic"},"title":"Workspace Memberships","type":"array"}},"required":["organization_member_id","role","status","organization"],"title":"OrganizationMembershipContextPublic","type":"object"},"CallerIdentityPublic":{"description":"Who the caller is, as against what they may do.\n\nThe account control at the foot of the dashboard's sidebar draws a person,\nand no authenticated route reported the caller's own name or address, so it\ndrew a role for everybody who could reach it: \"Operator\", which is not a\nname and on a multi-tenant deployment was not even true\n(mozilla-ai/otari#832).\n\nCarried on the membership context for the reason ``deployment_operator`` is:\nthe shell reads that context before it paints, so an identity taken from it\nneeds no request of its own and cannot arrive a beat after the chrome it\nnames. Publishing it costs nothing either, since it is the caller's own\nidentity and they are holding the credential that resolved to it.\n\n``email`` and ``full_name`` are nullable for opposite reasons. A local\noperator identity has no address, because first boot provisions it with a\nname and nothing to sign in with but the master key; a member added by\naddress has no name until they claim the identity and supply one. So a shell\nhas to be ready to draw either one alone.","properties":{"claims_deployment":{"description":"Whether setting this identity's password claims the deployment, which stops the master key signing in to the dashboard. True for the deployment's operator until it holds a password, whether or not it already has an address; false for everybody else.","title":"Claims Deployment","type":"boolean"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"has_password":{"description":"Whether this identity holds a dashboard password. False for one that signs in only through an OAuth provider or a passkey, and for a roster entry nobody has claimed yet. PUT /api/v1/auth/password requires current_password from a cookie-authenticated caller exactly while this is true.","title":"Has Password","type":"boolean"},"user_id":{"format":"uuid","title":"User Id","type":"string"}},"required":["user_id","has_password","claims_deployment"],"title":"CallerIdentityPublic","type":"object"},"OrganizationPublic":{"properties":{"created_at":{"format":"date-time","title":"Created At","type":"string"},"created_by_user_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"Created By User Id"},"id":{"format":"uuid","title":"Id","type":"string"},"name":{"maxLength":255,"title":"Name","type":"string"},"slug":{"maxLength":255,"title":"Slug","type":"string"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Updated At"}},"required":["name","slug","id","created_at"],"title":"OrganizationPublic","type":"object"},"CallerWorkspaceMembershipPublic":{"description":"One workspace the caller belongs to, and their role in it.\n\nCarried on the membership context so the shell can populate its workspace\nswitcher and choose a default from the first authenticated call, rather than\nlisting workspaces and then asking for the caller's role in each. Only the\ncaller's own memberships appear, so this is not a directory of the\norganization's workspaces: an admin sees the ones they joined, and the\nworkspace list endpoint remains the way to see the rest.","properties":{"name":{"title":"Name","type":"string"},"role":{"title":"Role","type":"string"},"workspace_id":{"format":"uuid","title":"Workspace Id","type":"string"}},"required":["workspace_id","name","role"],"title":"CallerWorkspaceMembershipPublic","type":"object"},"OrganizationRefusal":{"description":"The body of a request the organizations domain refuses.","properties":{"detail":{"description":"Why the request was refused.","title":"Detail","type":"string"}},"required":["detail"],"title":"OrganizationRefusal","type":"object"}}},"paths":{"/api/v1/organizations/me":{"get":{"description":"Get the caller's active organization and their standing in it.","operationId":"organizations-get_active_organization_context","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationMembershipContextPublic"}}},"description":"Successful Response"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationRefusal"}}},"description":"The caller is an active member of no organization."}},"summary":"Get Active Organization Context","tags":["organizations"]}}}}
```

## Update Active Organization

> Rename the caller's active organization.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"ActiveOrganizationUpdateRequest":{"properties":{"name":{"maxLength":255,"minLength":1,"title":"Name","type":"string"}},"required":["name"],"title":"ActiveOrganizationUpdateRequest","type":"object"},"OrganizationMembershipContextPublic":{"description":"An organization plus the caller's standing in it.\n\nWhat every tenancy page reads first: which organization it is looking at,\nand what the caller may do there.","properties":{"caller":{"anyOf":[{"$ref":"#/components/schemas/CallerIdentityPublic"},{"type":"null"}]},"deployment_operator":{"default":false,"title":"Deployment Operator","type":"boolean"},"organization":{"$ref":"#/components/schemas/OrganizationPublic"},"organization_member_id":{"format":"uuid","title":"Organization Member Id","type":"string"},"provider_key_encryption_available":{"default":false,"title":"Provider Key Encryption Available","type":"boolean"},"role":{"title":"Role","type":"string"},"status":{"title":"Status","type":"string"},"workspace_memberships":{"items":{"$ref":"#/components/schemas/CallerWorkspaceMembershipPublic"},"title":"Workspace Memberships","type":"array"}},"required":["organization_member_id","role","status","organization"],"title":"OrganizationMembershipContextPublic","type":"object"},"CallerIdentityPublic":{"description":"Who the caller is, as against what they may do.\n\nThe account control at the foot of the dashboard's sidebar draws a person,\nand no authenticated route reported the caller's own name or address, so it\ndrew a role for everybody who could reach it: \"Operator\", which is not a\nname and on a multi-tenant deployment was not even true\n(mozilla-ai/otari#832).\n\nCarried on the membership context for the reason ``deployment_operator`` is:\nthe shell reads that context before it paints, so an identity taken from it\nneeds no request of its own and cannot arrive a beat after the chrome it\nnames. Publishing it costs nothing either, since it is the caller's own\nidentity and they are holding the credential that resolved to it.\n\n``email`` and ``full_name`` are nullable for opposite reasons. A local\noperator identity has no address, because first boot provisions it with a\nname and nothing to sign in with but the master key; a member added by\naddress has no name until they claim the identity and supply one. So a shell\nhas to be ready to draw either one alone.","properties":{"claims_deployment":{"description":"Whether setting this identity's password claims the deployment, which stops the master key signing in to the dashboard. True for the deployment's operator until it holds a password, whether or not it already has an address; false for everybody else.","title":"Claims Deployment","type":"boolean"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"has_password":{"description":"Whether this identity holds a dashboard password. False for one that signs in only through an OAuth provider or a passkey, and for a roster entry nobody has claimed yet. PUT /api/v1/auth/password requires current_password from a cookie-authenticated caller exactly while this is true.","title":"Has Password","type":"boolean"},"user_id":{"format":"uuid","title":"User Id","type":"string"}},"required":["user_id","has_password","claims_deployment"],"title":"CallerIdentityPublic","type":"object"},"OrganizationPublic":{"properties":{"created_at":{"format":"date-time","title":"Created At","type":"string"},"created_by_user_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"Created By User Id"},"id":{"format":"uuid","title":"Id","type":"string"},"name":{"maxLength":255,"title":"Name","type":"string"},"slug":{"maxLength":255,"title":"Slug","type":"string"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Updated At"}},"required":["name","slug","id","created_at"],"title":"OrganizationPublic","type":"object"},"CallerWorkspaceMembershipPublic":{"description":"One workspace the caller belongs to, and their role in it.\n\nCarried on the membership context so the shell can populate its workspace\nswitcher and choose a default from the first authenticated call, rather than\nlisting workspaces and then asking for the caller's role in each. Only the\ncaller's own memberships appear, so this is not a directory of the\norganization's workspaces: an admin sees the ones they joined, and the\nworkspace list endpoint remains the way to see the rest.","properties":{"name":{"title":"Name","type":"string"},"role":{"title":"Role","type":"string"},"workspace_id":{"format":"uuid","title":"Workspace Id","type":"string"}},"required":["workspace_id","name","role"],"title":"CallerWorkspaceMembershipPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/organizations/me":{"patch":{"description":"Rename the caller's active organization.","operationId":"organizations-update_active_organization","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActiveOrganizationUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationMembershipContextPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Update Active Organization","tags":["organizations"]}}}}
```

## Delete Active Organization

> Delete the caller's active organization, if it has no usage, and move the caller out of it.\
> \
> Owners and admins only. Its workspaces, memberships, invitations and\
> organization-level settings go with it. Refused for the deployment's default\
> organization, for the only organization the caller belongs to, and for one\
> whose workspaces still hold usage, API keys, aliases or routing policies, so\
> a deletion never takes spend history with it.\
> \
> Answers with the caller's context in the organization they are moved to,\
> their oldest other one.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"OrganizationMembershipContextPublic":{"description":"An organization plus the caller's standing in it.\n\nWhat every tenancy page reads first: which organization it is looking at,\nand what the caller may do there.","properties":{"caller":{"anyOf":[{"$ref":"#/components/schemas/CallerIdentityPublic"},{"type":"null"}]},"deployment_operator":{"default":false,"title":"Deployment Operator","type":"boolean"},"organization":{"$ref":"#/components/schemas/OrganizationPublic"},"organization_member_id":{"format":"uuid","title":"Organization Member Id","type":"string"},"provider_key_encryption_available":{"default":false,"title":"Provider Key Encryption Available","type":"boolean"},"role":{"title":"Role","type":"string"},"status":{"title":"Status","type":"string"},"workspace_memberships":{"items":{"$ref":"#/components/schemas/CallerWorkspaceMembershipPublic"},"title":"Workspace Memberships","type":"array"}},"required":["organization_member_id","role","status","organization"],"title":"OrganizationMembershipContextPublic","type":"object"},"CallerIdentityPublic":{"description":"Who the caller is, as against what they may do.\n\nThe account control at the foot of the dashboard's sidebar draws a person,\nand no authenticated route reported the caller's own name or address, so it\ndrew a role for everybody who could reach it: \"Operator\", which is not a\nname and on a multi-tenant deployment was not even true\n(mozilla-ai/otari#832).\n\nCarried on the membership context for the reason ``deployment_operator`` is:\nthe shell reads that context before it paints, so an identity taken from it\nneeds no request of its own and cannot arrive a beat after the chrome it\nnames. Publishing it costs nothing either, since it is the caller's own\nidentity and they are holding the credential that resolved to it.\n\n``email`` and ``full_name`` are nullable for opposite reasons. A local\noperator identity has no address, because first boot provisions it with a\nname and nothing to sign in with but the master key; a member added by\naddress has no name until they claim the identity and supply one. So a shell\nhas to be ready to draw either one alone.","properties":{"claims_deployment":{"description":"Whether setting this identity's password claims the deployment, which stops the master key signing in to the dashboard. True for the deployment's operator until it holds a password, whether or not it already has an address; false for everybody else.","title":"Claims Deployment","type":"boolean"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"has_password":{"description":"Whether this identity holds a dashboard password. False for one that signs in only through an OAuth provider or a passkey, and for a roster entry nobody has claimed yet. PUT /api/v1/auth/password requires current_password from a cookie-authenticated caller exactly while this is true.","title":"Has Password","type":"boolean"},"user_id":{"format":"uuid","title":"User Id","type":"string"}},"required":["user_id","has_password","claims_deployment"],"title":"CallerIdentityPublic","type":"object"},"OrganizationPublic":{"properties":{"created_at":{"format":"date-time","title":"Created At","type":"string"},"created_by_user_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"Created By User Id"},"id":{"format":"uuid","title":"Id","type":"string"},"name":{"maxLength":255,"title":"Name","type":"string"},"slug":{"maxLength":255,"title":"Slug","type":"string"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Updated At"}},"required":["name","slug","id","created_at"],"title":"OrganizationPublic","type":"object"},"CallerWorkspaceMembershipPublic":{"description":"One workspace the caller belongs to, and their role in it.\n\nCarried on the membership context so the shell can populate its workspace\nswitcher and choose a default from the first authenticated call, rather than\nlisting workspaces and then asking for the caller's role in each. Only the\ncaller's own memberships appear, so this is not a directory of the\norganization's workspaces: an admin sees the ones they joined, and the\nworkspace list endpoint remains the way to see the rest.","properties":{"name":{"title":"Name","type":"string"},"role":{"title":"Role","type":"string"},"workspace_id":{"format":"uuid","title":"Workspace Id","type":"string"}},"required":["workspace_id","name","role"],"title":"CallerWorkspaceMembershipPublic","type":"object"},"OrganizationRefusal":{"description":"The body of a request the organizations domain refuses.","properties":{"detail":{"description":"Why the request was refused.","title":"Detail","type":"string"}},"required":["detail"],"title":"OrganizationRefusal","type":"object"}}},"paths":{"/api/v1/organizations/me":{"delete":{"description":"Delete the caller's active organization, if it has no usage, and move the caller out of it.\n\nOwners and admins only. Its workspaces, memberships, invitations and\norganization-level settings go with it. Refused for the deployment's default\norganization, for the only organization the caller belongs to, and for one\nwhose workspaces still hold usage, API keys, aliases or routing policies, so\na deletion never takes spend history with it.\n\nAnswers with the caller's context in the organization they are moved to,\ntheir oldest other one.","operationId":"organizations-delete_active_organization","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationMembershipContextPublic"}}},"description":"Successful Response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationRefusal"}}},"description":"It is the deployment's default organization, or the only one the caller belongs to."},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationRefusal"}}},"description":"The caller is not an owner or admin of the organization."},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationRefusal"}}},"description":"The organization still holds usage, API keys, aliases or routing policies."}},"summary":"Delete Active Organization","tags":["organizations"]}}}}
```

## List Active Organization Domains

> List the caller's organization's email-domain claims. Owners and admins only.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"OrganizationDomainsPublic":{"properties":{"count":{"title":"Count","type":"integer"},"data":{"items":{"$ref":"#/components/schemas/OrganizationDomainPublic"},"title":"Data","type":"array"}},"required":["data","count"],"title":"OrganizationDomainsPublic","type":"object"},"OrganizationDomainPublic":{"description":"A claim as its organization's admins see it.\n\nCarries ``verification_record`` (the whole string to publish) rather than\nthe raw token: the admin never has a use for the token on its own, and one\nfield that can be copied verbatim into a DNS panel is harder to get wrong\nthan a prefix they must remember to prepend.","properties":{"created_at":{"format":"date-time","title":"Created At","type":"string"},"default_role":{"default":"member","maxLength":32,"title":"Default Role","type":"string"},"domain":{"maxLength":255,"title":"Domain","type":"string"},"enabled":{"default":true,"title":"Enabled","type":"boolean"},"id":{"format":"uuid","title":"Id","type":"string"},"organization_id":{"format":"uuid","title":"Organization Id","type":"string"},"proof_expires_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Proof Expires At"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Updated At"},"verification_record":{"title":"Verification Record","type":"string"},"verified_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Verified At"}},"required":["organization_id","domain","id","verification_record","created_at"],"title":"OrganizationDomainPublic","type":"object"}}},"paths":{"/api/v1/organizations/me/domains":{"get":{"description":"List the caller's organization's email-domain claims. Owners and admins only.","operationId":"organizations-list_active_organization_domains","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationDomainsPublic"}}},"description":"Successful Response"}},"summary":"List Active Organization Domains","tags":["organizations"]}}}}
```

## Create Active Organization Domain

> Claim an email domain for the caller's organization. Owners and admins only.\
> \
> The claim lands unverified and does nothing until \`\`POST\
> /me/domains/{id}/verify\`\` finds the record in \`\`verification\_record\`\`\
> published at the domain's apex. A public email provider is refused outright,\
> and a domain another organization already claims answers 409 without saying\
> who holds it.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"OrganizationDomainCreateRequest":{"properties":{"default_role":{"default":"member","enum":["member","viewer"],"title":"Default Role","type":"string"},"domain":{"maxLength":255,"minLength":1,"title":"Domain","type":"string"},"enabled":{"default":true,"title":"Enabled","type":"boolean"}},"required":["domain"],"title":"OrganizationDomainCreateRequest","type":"object"},"OrganizationDomainPublic":{"description":"A claim as its organization's admins see it.\n\nCarries ``verification_record`` (the whole string to publish) rather than\nthe raw token: the admin never has a use for the token on its own, and one\nfield that can be copied verbatim into a DNS panel is harder to get wrong\nthan a prefix they must remember to prepend.","properties":{"created_at":{"format":"date-time","title":"Created At","type":"string"},"default_role":{"default":"member","maxLength":32,"title":"Default Role","type":"string"},"domain":{"maxLength":255,"title":"Domain","type":"string"},"enabled":{"default":true,"title":"Enabled","type":"boolean"},"id":{"format":"uuid","title":"Id","type":"string"},"organization_id":{"format":"uuid","title":"Organization Id","type":"string"},"proof_expires_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Proof Expires At"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Updated At"},"verification_record":{"title":"Verification Record","type":"string"},"verified_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Verified At"}},"required":["organization_id","domain","id","verification_record","created_at"],"title":"OrganizationDomainPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/organizations/me/domains":{"post":{"description":"Claim an email domain for the caller's organization. Owners and admins only.\n\nThe claim lands unverified and does nothing until ``POST\n/me/domains/{id}/verify`` finds the record in ``verification_record``\npublished at the domain's apex. A public email provider is refused outright,\nand a domain another organization already claims answers 409 without saying\nwho holds it.","operationId":"organizations-create_active_organization_domain","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationDomainCreateRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationDomainPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Create Active Organization Domain","tags":["organizations"]}}}}
```

## Update Active Organization Domain

> Change a claim's auto-join role or enabled flag. Owners and admins only.\
> \
> The domain itself and its verification state are not editable: a different\
> domain is a different claim and needs its own proof.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"OrganizationDomainUpdateRequest":{"properties":{"default_role":{"anyOf":[{"enum":["member","viewer"],"type":"string"},{"type":"null"}],"title":"Default Role"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled"}},"title":"OrganizationDomainUpdateRequest","type":"object"},"OrganizationDomainPublic":{"description":"A claim as its organization's admins see it.\n\nCarries ``verification_record`` (the whole string to publish) rather than\nthe raw token: the admin never has a use for the token on its own, and one\nfield that can be copied verbatim into a DNS panel is harder to get wrong\nthan a prefix they must remember to prepend.","properties":{"created_at":{"format":"date-time","title":"Created At","type":"string"},"default_role":{"default":"member","maxLength":32,"title":"Default Role","type":"string"},"domain":{"maxLength":255,"title":"Domain","type":"string"},"enabled":{"default":true,"title":"Enabled","type":"boolean"},"id":{"format":"uuid","title":"Id","type":"string"},"organization_id":{"format":"uuid","title":"Organization Id","type":"string"},"proof_expires_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Proof Expires At"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Updated At"},"verification_record":{"title":"Verification Record","type":"string"},"verified_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Verified At"}},"required":["organization_id","domain","id","verification_record","created_at"],"title":"OrganizationDomainPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/organizations/me/domains/{organization_domain_id}":{"patch":{"description":"Change a claim's auto-join role or enabled flag. Owners and admins only.\n\nThe domain itself and its verification state are not editable: a different\ndomain is a different claim and needs its own proof.","operationId":"organizations-update_active_organization_domain","parameters":[{"in":"path","name":"organization_domain_id","required":true,"schema":{"format":"uuid","title":"Organization Domain Id","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationDomainUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationDomainPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Update Active Organization Domain","tags":["organizations"]}}}}
```

## Delete Active Organization Domain

> Drop an email-domain claim. Owners and admins only.\
> \
> Members who already joined through it keep their membership: they are\
> colleagues by then, not an artifact of the claim.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"Message":{"description":"A human-readable acknowledgment for an operation with nothing to return.","properties":{"message":{"description":"What happened.","title":"Message","type":"string"}},"required":["message"],"title":"Message","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/organizations/me/domains/{organization_domain_id}":{"delete":{"description":"Drop an email-domain claim. Owners and admins only.\n\nMembers who already joined through it keep their membership: they are\ncolleagues by then, not an artifact of the claim.","operationId":"organizations-delete_active_organization_domain","parameters":[{"in":"path","name":"organization_domain_id","required":true,"schema":{"format":"uuid","title":"Organization Domain Id","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Delete Active Organization Domain","tags":["organizations"]}}}}
```

## Verify Active Organization Domain

> Prove control of a claimed domain via its DNS TXT record. Owners and admins only.\
> \
> Idempotent, and answers 400 while the record is not visible yet, which is\
> the expected answer straight after publishing one.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"OrganizationDomainPublic":{"description":"A claim as its organization's admins see it.\n\nCarries ``verification_record`` (the whole string to publish) rather than\nthe raw token: the admin never has a use for the token on its own, and one\nfield that can be copied verbatim into a DNS panel is harder to get wrong\nthan a prefix they must remember to prepend.","properties":{"created_at":{"format":"date-time","title":"Created At","type":"string"},"default_role":{"default":"member","maxLength":32,"title":"Default Role","type":"string"},"domain":{"maxLength":255,"title":"Domain","type":"string"},"enabled":{"default":true,"title":"Enabled","type":"boolean"},"id":{"format":"uuid","title":"Id","type":"string"},"organization_id":{"format":"uuid","title":"Organization Id","type":"string"},"proof_expires_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Proof Expires At"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Updated At"},"verification_record":{"title":"Verification Record","type":"string"},"verified_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Verified At"}},"required":["organization_id","domain","id","verification_record","created_at"],"title":"OrganizationDomainPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/organizations/me/domains/{organization_domain_id}/verify":{"post":{"description":"Prove control of a claimed domain via its DNS TXT record. Owners and admins only.\n\nIdempotent, and answers 400 while the record is not visible yet, which is\nthe expected answer straight after publishing one.","operationId":"organizations-verify_active_organization_domain","parameters":[{"in":"path","name":"organization_domain_id","required":true,"schema":{"format":"uuid","title":"Organization Domain Id","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationDomainPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Verify Active Organization Domain","tags":["organizations"]}}}}
```

## Invite Active Organization Member

> Invite an address to the caller's active organization by email.\
> \
> Organization owners and admins only. Unlike \`\`POST /me/members\`\`, the\
> membership lands \`\`invited\`\` rather than \`\`active\`\`: it becomes active\
> once the recipient accepts (\`\`POST /api/v1/invitations/accept\`\`). When the\
> email did not go out (\`\`mail\_sent\`\` is false: mail is not configured, the\
> send failed, or the invitation mail limit was reached), the response\
> carries the accept link so the caller can share it themselves. An address\
> whose invitation is still pending is refused; reissue that invitation to\
> send a new link.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"InviteOrganizationMemberRequest":{"description":"Invite an address to the caller's organization, optionally into workspaces at once.\n\nField-for-field ``ActiveOrganizationMemberCreateRequest``'s twin: the two\nrequests ask for the same thing and differ only in what creating one\nproduces (this lands ``invited`` and emails a link; that lands ``active``\nimmediately).","properties":{"email":{"format":"email","maxLength":255,"title":"Email","type":"string"},"role":{"default":"member","enum":["owner","admin","member","viewer"],"title":"Role","type":"string"},"workspace_assignments":{"anyOf":[{"items":{"$ref":"#/components/schemas/WorkspaceAssignmentRequest"},"maxItems":50,"type":"array"},{"type":"null"}],"title":"Workspace Assignments"}},"required":["email"],"title":"InviteOrganizationMemberRequest","type":"object"},"WorkspaceAssignmentRequest":{"description":"A workspace and the role to grant in it, applied when a member is added.","properties":{"role":{"default":"member","enum":["owner","admin","member","viewer"],"title":"Role","type":"string"},"workspace_id":{"format":"uuid","title":"Workspace Id","type":"string"}},"required":["workspace_id"],"title":"WorkspaceAssignmentRequest","type":"object"},"InviteOrganizationMemberResultPublic":{"description":"What issuing an invitation produces, and whether the email actually went out.","properties":{"accept_link":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The link to share with the invitee yourself, set only when mail_sent is false. A delivered link goes to the invitee's mailbox alone, so the inviter cannot open it as them.","title":"Accept Link"},"created_at":{"format":"date-time","title":"Created At","type":"string"},"email":{"title":"Email","type":"string"},"expires_at":{"format":"date-time","title":"Expires At","type":"string"},"invitation_id":{"format":"uuid","title":"Invitation Id","type":"string"},"mail_limited":{"default":false,"description":"Whether the email was held back because the organization or the address reached its invitation mail limit for the day. Share the link yourself, or reissue the invitation later to email a new one.","title":"Mail Limited","type":"boolean"},"mail_sent":{"description":"Whether the invitation email was actually dispatched. False when mail is not configured, the send itself failed, or mail_limited.","title":"Mail Sent","type":"boolean"},"organization_member_id":{"format":"uuid","title":"Organization Member Id","type":"string"},"role":{"title":"Role","type":"string"},"status":{"const":"invited","default":"invited","title":"Status","type":"string"}},"required":["invitation_id","organization_member_id","email","role","mail_sent","expires_at","created_at"],"title":"InviteOrganizationMemberResultPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/organizations/me/member-invitations":{"post":{"description":"Invite an address to the caller's active organization by email.\n\nOrganization owners and admins only. Unlike ``POST /me/members``, the\nmembership lands ``invited`` rather than ``active``: it becomes active\nonce the recipient accepts (``POST /api/v1/invitations/accept``). When the\nemail did not go out (``mail_sent`` is false: mail is not configured, the\nsend failed, or the invitation mail limit was reached), the response\ncarries the accept link so the caller can share it themselves. An address\nwhose invitation is still pending is refused; reissue that invitation to\nsend a new link.","operationId":"organizations-invite_active_organization_member","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteOrganizationMemberRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteOrganizationMemberResultPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Invite Active Organization Member","tags":["organizations"]}}}}
```

## Bulk Invite Active Organization Members

> Invite several addresses to the caller's active organization at once.\
> \
> Organization owners and admins only. Every address gets the same role and\
> workspace assignments. Each one is checked as \`\`POST /me/member-invitations\`\`\
> would check it, and an address that is refused lands in \`\`failed\`\` with the\
> reason rather than failing the request, so the answer is 200 even when some\
> or all were refused. Each invited entry carries its own \`\`mail\_sent\`\`, and\
> its accept link when that is false.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"BulkInviteOrganizationMembersRequest":{"description":"Invite several addresses at once, all with the same role and workspace assignments.","properties":{"emails":{"items":{"type":"string"},"maxItems":100,"minItems":1,"title":"Emails","type":"array"},"role":{"default":"member","enum":["owner","admin","member","viewer"],"title":"Role","type":"string"},"workspace_assignments":{"anyOf":[{"items":{"$ref":"#/components/schemas/WorkspaceAssignmentRequest"},"maxItems":50,"type":"array"},{"type":"null"}],"title":"Workspace Assignments"}},"required":["emails"],"title":"BulkInviteOrganizationMembersRequest","type":"object"},"WorkspaceAssignmentRequest":{"description":"A workspace and the role to grant in it, applied when a member is added.","properties":{"role":{"default":"member","enum":["owner","admin","member","viewer"],"title":"Role","type":"string"},"workspace_id":{"format":"uuid","title":"Workspace Id","type":"string"}},"required":["workspace_id"],"title":"WorkspaceAssignmentRequest","type":"object"},"BulkInviteOrganizationMembersResultPublic":{"description":"What a bulk invite produced: one entry per submitted address, repeats included, in one of the two lists.","properties":{"failed":{"items":{"$ref":"#/components/schemas/BulkInvitationFailurePublic"},"title":"Failed","type":"array"},"invited":{"items":{"$ref":"#/components/schemas/InviteOrganizationMemberResultPublic"},"title":"Invited","type":"array"}},"required":["invited","failed"],"title":"BulkInviteOrganizationMembersResultPublic","type":"object"},"BulkInvitationFailurePublic":{"description":"An address the bulk invite could not invite, and why.","properties":{"detail":{"title":"Detail","type":"string"},"email":{"title":"Email","type":"string"}},"required":["email","detail"],"title":"BulkInvitationFailurePublic","type":"object"},"InviteOrganizationMemberResultPublic":{"description":"What issuing an invitation produces, and whether the email actually went out.","properties":{"accept_link":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The link to share with the invitee yourself, set only when mail_sent is false. A delivered link goes to the invitee's mailbox alone, so the inviter cannot open it as them.","title":"Accept Link"},"created_at":{"format":"date-time","title":"Created At","type":"string"},"email":{"title":"Email","type":"string"},"expires_at":{"format":"date-time","title":"Expires At","type":"string"},"invitation_id":{"format":"uuid","title":"Invitation Id","type":"string"},"mail_limited":{"default":false,"description":"Whether the email was held back because the organization or the address reached its invitation mail limit for the day. Share the link yourself, or reissue the invitation later to email a new one.","title":"Mail Limited","type":"boolean"},"mail_sent":{"description":"Whether the invitation email was actually dispatched. False when mail is not configured, the send itself failed, or mail_limited.","title":"Mail Sent","type":"boolean"},"organization_member_id":{"format":"uuid","title":"Organization Member Id","type":"string"},"role":{"title":"Role","type":"string"},"status":{"const":"invited","default":"invited","title":"Status","type":"string"}},"required":["invitation_id","organization_member_id","email","role","mail_sent","expires_at","created_at"],"title":"InviteOrganizationMemberResultPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/organizations/me/member-invitations/bulk":{"post":{"description":"Invite several addresses to the caller's active organization at once.\n\nOrganization owners and admins only. Every address gets the same role and\nworkspace assignments. Each one is checked as ``POST /me/member-invitations``\nwould check it, and an address that is refused lands in ``failed`` with the\nreason rather than failing the request, so the answer is 200 even when some\nor all were refused. Each invited entry carries its own ``mail_sent``, and\nits accept link when that is false.","operationId":"organizations-bulk_invite_active_organization_members","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkInviteOrganizationMembersRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkInviteOrganizationMembersResultPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Bulk Invite Active Organization Members","tags":["organizations"]}}}}
```

## Revoke Active Organization Member Invitation

> Revoke an unaccepted invitation, pending or expired. Organization owners and admins only.\
> \
> Suspends the paired membership rather than deleting it, the same as\
> removing an active member: re-inviting the same address later revives it.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"Message":{"description":"A human-readable acknowledgment for an operation with nothing to return.","properties":{"message":{"description":"What happened.","title":"Message","type":"string"}},"required":["message"],"title":"Message","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/organizations/me/member-invitations/{invitation_id}":{"delete":{"description":"Revoke an unaccepted invitation, pending or expired. Organization owners and admins only.\n\nSuspends the paired membership rather than deleting it, the same as\nremoving an active member: re-inviting the same address later revives it.","operationId":"organizations-revoke_active_organization_member_invitation","parameters":[{"in":"path","name":"invitation_id","required":true,"schema":{"format":"uuid","title":"Invitation Id","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Revoke Active Organization Member Invitation","tags":["organizations"]}}}}
```

## Reissue Active Organization Member Invitation

> Give a pending or expired invitation a new accept link. Organization owners and admins only.\
> \
> The old link stops working. The new one is emailed as an invitation is, and\
> returned in \`\`accept\_link\`\` when it was not, so a lost link can be\
> recovered whether or not the deployment sends mail.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"InviteOrganizationMemberResultPublic":{"description":"What issuing an invitation produces, and whether the email actually went out.","properties":{"accept_link":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The link to share with the invitee yourself, set only when mail_sent is false. A delivered link goes to the invitee's mailbox alone, so the inviter cannot open it as them.","title":"Accept Link"},"created_at":{"format":"date-time","title":"Created At","type":"string"},"email":{"title":"Email","type":"string"},"expires_at":{"format":"date-time","title":"Expires At","type":"string"},"invitation_id":{"format":"uuid","title":"Invitation Id","type":"string"},"mail_limited":{"default":false,"description":"Whether the email was held back because the organization or the address reached its invitation mail limit for the day. Share the link yourself, or reissue the invitation later to email a new one.","title":"Mail Limited","type":"boolean"},"mail_sent":{"description":"Whether the invitation email was actually dispatched. False when mail is not configured, the send itself failed, or mail_limited.","title":"Mail Sent","type":"boolean"},"organization_member_id":{"format":"uuid","title":"Organization Member Id","type":"string"},"role":{"title":"Role","type":"string"},"status":{"const":"invited","default":"invited","title":"Status","type":"string"}},"required":["invitation_id","organization_member_id","email","role","mail_sent","expires_at","created_at"],"title":"InviteOrganizationMemberResultPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/organizations/me/member-invitations/{invitation_id}/reissue":{"post":{"description":"Give a pending or expired invitation a new accept link. Organization owners and admins only.\n\nThe old link stops working. The new one is emailed as an invitation is, and\nreturned in ``accept_link`` when it was not, so a lost link can be\nrecovered whether or not the deployment sends mail.","operationId":"organizations-reissue_active_organization_member_invitation","parameters":[{"in":"path","name":"invitation_id","required":true,"schema":{"format":"uuid","title":"Invitation Id","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteOrganizationMemberResultPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Reissue Active Organization Member Invitation","tags":["organizations"]}}}}
```

## List Active Organization Members

> List the members of the caller's active organization.\
> \
> \`\`search\`\` narrows the page and the count together, so a caller offering\
> these members as options can ask for the matches instead of filtering\
> whatever page it happened to fetch.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"ActiveOrganizationMembersPublic":{"properties":{"count":{"title":"Count","type":"integer"},"data":{"items":{"$ref":"#/components/schemas/ActiveOrganizationMemberPublic"},"title":"Data","type":"array"}},"required":["data","count"],"title":"ActiveOrganizationMembersPublic","type":"object"},"ActiveOrganizationMemberPublic":{"description":"A member row joined to the identity behind it, as the roster shows it.\n\nField-for-field the platform's shape, so the ported roster page is not\nrewritten around a new contract, except that ``email`` is nullable here (a\nlocal operator identity has no sign-in address).\n\n``invitation_id`` and ``invitation_expires_at`` name an ``invited`` row's\ncurrent invitation, which may have expired, so the roster can offer to\nrevoke or reissue it.\n\n``attribution_user_id`` is the addition the platform has no counterpart for.\nKeys, budgets, and usage attach to the gateway's string-keyed ``users`` row,\nnot to this UUID identity, so this carries the ``user_id`` a caller passes to\nwhen minting a key for this member. It is null when no usable row\nexists (nobody minted one, or it was soft-deleted through\nsoft-deleted), which is the signal not to offer this member as a key\nowner: key creation would refuse. How the two ids converge is the open\nquestion in otari-ai#1727; this field is the join until it is answered, and\nis what lets either answer land without the dashboard changing.","properties":{"attribution":{"anyOf":[{"$ref":"#/components/schemas/MemberAttributionPublic"},{"type":"null"}]},"attribution_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Attribution User Id"},"created_at":{"format":"date-time","title":"Created At","type":"string"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"invitation_expires_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"description":"When the invitation in invitation_id expires, or expired. Null when there is none.","title":"Invitation Expires At"},"invitation_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"Invitation Id"},"organization_member_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"Organization Member Id"},"role":{"title":"Role","type":"string"},"status":{"title":"Status","type":"string"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Updated At"},"user_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"User Id"},"workspaces":{"items":{"$ref":"#/components/schemas/MemberWorkspacePlacementPublic"},"title":"Workspaces","type":"array"}},"required":["role","status","created_at"],"title":"ActiveOrganizationMemberPublic","type":"object"},"MemberAttributionPublic":{"description":"What the gateway identity behind a membership has spent, and may reach.\n\nDeployment-wide facts, so they are withheld from a caller who does not\noperate the deployment rather than zeroed: ``/api/v1/users`` refuses them,\nand a zero here would read as a member who has spent nothing.","properties":{"allowed_models":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Models"},"allowed_tools":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Tools"},"blocked":{"title":"Blocked","type":"boolean"},"reserved":{"title":"Reserved","type":"number"},"spend":{"title":"Spend","type":"number"}},"required":["spend","reserved","blocked"],"title":"MemberAttributionPublic","type":"object"},"MemberWorkspacePlacementPublic":{"description":"One workspace a member is in, with their role and ceiling there.\n\nA ceiling is keyed on the *membership*, not on the person, so a member of two\nworkspaces has two of them. The membership id is carried in its own right\nrather than read back off the ceiling, because it is needed precisely when\nthere is no ceiling yet and one is about to be created.","properties":{"ceiling":{"anyOf":[{"$ref":"#/components/schemas/MemberCeilingPublic"},{"type":"null"}]},"role":{"title":"Role","type":"string"},"workspace_id":{"format":"uuid","title":"Workspace Id","type":"string"},"workspace_member_id":{"format":"uuid","title":"Workspace Member Id","type":"string"},"workspace_name":{"title":"Workspace Name","type":"string"}},"required":["workspace_id","workspace_name","workspace_member_id","role"],"title":"MemberWorkspacePlacementPublic","type":"object"},"MemberCeilingPublic":{"description":"The spend ceiling on one workspace membership, as the roster reports it.\n\nThree fields rather than the whole ``scoped_budgets`` row: the figure the\nroster prints, the budget its editor picks, and the id that edit writes to.","properties":{"budget_id":{"title":"Budget Id","type":"string"},"id":{"title":"Id","type":"string"},"max_budget":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max Budget"}},"required":["id","budget_id","max_budget"],"title":"MemberCeilingPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/organizations/me/members":{"get":{"description":"List the members of the caller's active organization.\n\n``search`` narrows the page and the count together, so a caller offering\nthese members as options can ask for the matches instead of filtering\nwhatever page it happened to fetch.","operationId":"organizations-list_active_organization_members","parameters":[{"description":"Number of records to skip","in":"query","name":"skip","required":false,"schema":{"default":0,"description":"Number of records to skip","minimum":0,"title":"Skip","type":"integer"}},{"description":"Maximum number of records to return","in":"query","name":"limit","required":false,"schema":{"default":100,"description":"Maximum number of records to return","maximum":1000,"minimum":1,"title":"Limit","type":"integer"}},{"description":"Narrow to members whose name or email contains this text, case-insensitively.","in":"query","name":"search","required":false,"schema":{"anyOf":[{"maxLength":200,"type":"string"},{"type":"null"}],"description":"Narrow to members whose name or email contains this text, case-insensitively.","title":"Search"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActiveOrganizationMembersPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"List Active Organization Members","tags":["organizations"]}}}}
```

## Create Active Organization Member

> Add a member to the caller's active organization, by email address.\
> \
> Organization owners and admins only. The member is active immediately, with\
> no invitation to accept and no email sent, so the person has to be told\
> some other way. An address that belongs to no identity yet creates one,\
> which gets in by signing up, by "Forgot your password?" or by a Google or\
> GitHub sign-in; on a deployment with no mail and no provider it waits for\
> an operator's password link, so invite it instead\
> (\`\`POST /me/member-invitations\`\`), whose link can set its first password.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"ActiveOrganizationMemberCreateRequest":{"description":"Add someone to the caller's organization, optionally into workspaces at once.","properties":{"email":{"format":"email","maxLength":255,"title":"Email","type":"string"},"role":{"default":"member","enum":["owner","admin","member","viewer"],"title":"Role","type":"string"},"workspace_assignments":{"anyOf":[{"items":{"$ref":"#/components/schemas/WorkspaceAssignmentRequest"},"maxItems":50,"type":"array"},{"type":"null"}],"title":"Workspace Assignments"}},"required":["email"],"title":"ActiveOrganizationMemberCreateRequest","type":"object"},"WorkspaceAssignmentRequest":{"description":"A workspace and the role to grant in it, applied when a member is added.","properties":{"role":{"default":"member","enum":["owner","admin","member","viewer"],"title":"Role","type":"string"},"workspace_id":{"format":"uuid","title":"Workspace Id","type":"string"}},"required":["workspace_id"],"title":"WorkspaceAssignmentRequest","type":"object"},"ActiveOrganizationMemberCreateResultPublic":{"description":"The outcome of adding a member directly.\n\nThe platform answers ``invited`` on both its branches, because being added\nthere always needs acceptance. A direct add here needs none, so it answers\non the other arm of the same union, ``active``, and the invitation fields\nstay null; inviting answers ``InviteOrganizationMemberResultPublic``.","properties":{"attribution_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Attribution User Id"},"created_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Created At"},"email":{"title":"Email","type":"string"},"expires_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Expires At"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"invitation_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"Invitation Id"},"organization_member_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"Organization Member Id"},"role":{"title":"Role","type":"string"},"status":{"enum":["active","invited"],"title":"Status","type":"string"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Updated At"},"user_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"User Id"}},"required":["status","email","role"],"title":"ActiveOrganizationMemberCreateResultPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/organizations/me/members":{"post":{"description":"Add a member to the caller's active organization, by email address.\n\nOrganization owners and admins only. The member is active immediately, with\nno invitation to accept and no email sent, so the person has to be told\nsome other way. An address that belongs to no identity yet creates one,\nwhich gets in by signing up, by \"Forgot your password?\" or by a Google or\nGitHub sign-in; on a deployment with no mail and no provider it waits for\nan operator's password link, so invite it instead\n(``POST /me/member-invitations``), whose link can set its first password.","operationId":"organizations-create_active_organization_member","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActiveOrganizationMemberCreateRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActiveOrganizationMemberCreateResultPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Create Active Organization Member","tags":["organizations"]}}}}
```

## Update Active Organization Member

> Change a member's role or status. Organization owners and admins only.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"ActiveOrganizationMemberUpdateRequest":{"properties":{"role":{"anyOf":[{"enum":["owner","admin","member","viewer"],"type":"string"},{"type":"null"}],"title":"Role"},"status":{"anyOf":[{"enum":["active","suspended"],"type":"string"},{"type":"null"}],"title":"Status"}},"title":"ActiveOrganizationMemberUpdateRequest","type":"object"},"ActiveOrganizationMemberPublic":{"description":"A member row joined to the identity behind it, as the roster shows it.\n\nField-for-field the platform's shape, so the ported roster page is not\nrewritten around a new contract, except that ``email`` is nullable here (a\nlocal operator identity has no sign-in address).\n\n``invitation_id`` and ``invitation_expires_at`` name an ``invited`` row's\ncurrent invitation, which may have expired, so the roster can offer to\nrevoke or reissue it.\n\n``attribution_user_id`` is the addition the platform has no counterpart for.\nKeys, budgets, and usage attach to the gateway's string-keyed ``users`` row,\nnot to this UUID identity, so this carries the ``user_id`` a caller passes to\nwhen minting a key for this member. It is null when no usable row\nexists (nobody minted one, or it was soft-deleted through\nsoft-deleted), which is the signal not to offer this member as a key\nowner: key creation would refuse. How the two ids converge is the open\nquestion in otari-ai#1727; this field is the join until it is answered, and\nis what lets either answer land without the dashboard changing.","properties":{"attribution":{"anyOf":[{"$ref":"#/components/schemas/MemberAttributionPublic"},{"type":"null"}]},"attribution_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Attribution User Id"},"created_at":{"format":"date-time","title":"Created At","type":"string"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"invitation_expires_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"description":"When the invitation in invitation_id expires, or expired. Null when there is none.","title":"Invitation Expires At"},"invitation_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"Invitation Id"},"organization_member_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"Organization Member Id"},"role":{"title":"Role","type":"string"},"status":{"title":"Status","type":"string"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Updated At"},"user_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"User Id"},"workspaces":{"items":{"$ref":"#/components/schemas/MemberWorkspacePlacementPublic"},"title":"Workspaces","type":"array"}},"required":["role","status","created_at"],"title":"ActiveOrganizationMemberPublic","type":"object"},"MemberAttributionPublic":{"description":"What the gateway identity behind a membership has spent, and may reach.\n\nDeployment-wide facts, so they are withheld from a caller who does not\noperate the deployment rather than zeroed: ``/api/v1/users`` refuses them,\nand a zero here would read as a member who has spent nothing.","properties":{"allowed_models":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Models"},"allowed_tools":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Tools"},"blocked":{"title":"Blocked","type":"boolean"},"reserved":{"title":"Reserved","type":"number"},"spend":{"title":"Spend","type":"number"}},"required":["spend","reserved","blocked"],"title":"MemberAttributionPublic","type":"object"},"MemberWorkspacePlacementPublic":{"description":"One workspace a member is in, with their role and ceiling there.\n\nA ceiling is keyed on the *membership*, not on the person, so a member of two\nworkspaces has two of them. The membership id is carried in its own right\nrather than read back off the ceiling, because it is needed precisely when\nthere is no ceiling yet and one is about to be created.","properties":{"ceiling":{"anyOf":[{"$ref":"#/components/schemas/MemberCeilingPublic"},{"type":"null"}]},"role":{"title":"Role","type":"string"},"workspace_id":{"format":"uuid","title":"Workspace Id","type":"string"},"workspace_member_id":{"format":"uuid","title":"Workspace Member Id","type":"string"},"workspace_name":{"title":"Workspace Name","type":"string"}},"required":["workspace_id","workspace_name","workspace_member_id","role"],"title":"MemberWorkspacePlacementPublic","type":"object"},"MemberCeilingPublic":{"description":"The spend ceiling on one workspace membership, as the roster reports it.\n\nThree fields rather than the whole ``scoped_budgets`` row: the figure the\nroster prints, the budget its editor picks, and the id that edit writes to.","properties":{"budget_id":{"title":"Budget Id","type":"string"},"id":{"title":"Id","type":"string"},"max_budget":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max Budget"}},"required":["id","budget_id","max_budget"],"title":"MemberCeilingPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/organizations/me/members/{organization_member_id}":{"patch":{"description":"Change a member's role or status. Organization owners and admins only.","operationId":"organizations-update_active_organization_member","parameters":[{"in":"path","name":"organization_member_id","required":true,"schema":{"format":"uuid","title":"Organization Member Id","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActiveOrganizationMemberUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActiveOrganizationMemberPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Update Active Organization Member","tags":["organizations"]}}}}
```

## Remove Active Organization Member

> Remove a member by suspending their membership, keeping their history resolvable.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"Message":{"description":"A human-readable acknowledgment for an operation with nothing to return.","properties":{"message":{"description":"What happened.","title":"Message","type":"string"}},"required":["message"],"title":"Message","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/organizations/me/members/{organization_member_id}":{"delete":{"description":"Remove a member by suspending their membership, keeping their history resolvable.","operationId":"organizations-remove_active_organization_member","parameters":[{"in":"path","name":"organization_member_id","required":true,"schema":{"format":"uuid","title":"Organization Member Id","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Remove Active Organization Member","tags":["organizations"]}}}}
```

## List Caller Organization Memberships

> List the organizations the caller belongs to, and their role in each.\
> \
> The caller's own active memberships, not a directory of the deployment's\
> organizations: this is what an organization switcher renders, and one row\
> carries \`\`is\_active\_organization\`\` so it can mark the current one. Not to be\
> confused with \`\`GET /me/members\`\`, which is the active organization's\
> roster.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"CallerOrganizationMembershipsPublic":{"properties":{"count":{"title":"Count","type":"integer"},"data":{"items":{"$ref":"#/components/schemas/CallerOrganizationMembershipPublic"},"title":"Data","type":"array"}},"required":["data","count"],"title":"CallerOrganizationMembershipsPublic","type":"object"},"CallerOrganizationMembershipPublic":{"description":"One organization the caller belongs to, and their standing in it.\n\nWhat an organization switcher renders. Deliberately not\n``OrganizationMembershipContextPublic``: that one answers \"the organization\nthis request is acting in\" and carries the caller's workspaces in it, which\nfor an organization they are not currently in would be a second read per\nrow.","properties":{"is_active_organization":{"default":false,"title":"Is Active Organization","type":"boolean"},"organization":{"$ref":"#/components/schemas/OrganizationPublic"},"organization_member_id":{"format":"uuid","title":"Organization Member Id","type":"string"},"role":{"title":"Role","type":"string"},"status":{"title":"Status","type":"string"}},"required":["organization_member_id","organization","role","status"],"title":"CallerOrganizationMembershipPublic","type":"object"},"OrganizationPublic":{"properties":{"created_at":{"format":"date-time","title":"Created At","type":"string"},"created_by_user_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"Created By User Id"},"id":{"format":"uuid","title":"Id","type":"string"},"name":{"maxLength":255,"title":"Name","type":"string"},"slug":{"maxLength":255,"title":"Slug","type":"string"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Updated At"}},"required":["name","slug","id","created_at"],"title":"OrganizationPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/organizations/me/memberships":{"get":{"description":"List the organizations the caller belongs to, and their role in each.\n\nThe caller's own active memberships, not a directory of the deployment's\norganizations: this is what an organization switcher renders, and one row\ncarries ``is_active_organization`` so it can mark the current one. Not to be\nconfused with ``GET /me/members``, which is the active organization's\nroster.","operationId":"organizations-list_caller_organization_memberships","parameters":[{"description":"Number of records to skip","in":"query","name":"skip","required":false,"schema":{"default":0,"description":"Number of records to skip","minimum":0,"title":"Skip","type":"integer"}},{"description":"Maximum number of records to return","in":"query","name":"limit","required":false,"schema":{"default":100,"description":"Maximum number of records to return","maximum":1000,"minimum":1,"title":"Limit","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallerOrganizationMembershipsPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"List Caller Organization Memberships","tags":["organizations"]}}}}
```

## List Caller Pending Memberships

> List the organization invitations still awaiting the caller.\
> \
> The invitee's side of the invitation flow, where \`\`/me/member-invitations\`\`\
> is the admin's. Not to be confused with \`\`GET /me/memberships\`\`, which\
> lists the organizations the caller is already an active member of and\
> deliberately omits an \`\`invited\`\` one.\
> \
> Takes no token, unlike \`\`/api/v1/invitations/\*\`\`: those are public because the\
> recipient of an emailed link holds nothing else to prove anything with,\
> while this caller is authenticated as the addressee and the membership's\
> own \`\`user\_id\`\` is what scopes the answer. An invitation whose deadline has\
> passed is omitted rather than listed as unactionable.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"PendingOrganizationInvitationsPublic":{"properties":{"count":{"title":"Count","type":"integer"},"data":{"items":{"$ref":"#/components/schemas/PendingOrganizationInvitationPublic"},"title":"Data","type":"array"}},"required":["data","count"],"title":"PendingOrganizationInvitationsPublic","type":"object"},"PendingOrganizationInvitationPublic":{"description":"One invitation waiting on the caller, as their own inbox lists it.\n\nThe authenticated counterpart to ``InvitationPreviewPublic``, and wider\nthan it on purpose: that one answers a visitor whose only credential is\nthe token, so it carries nothing it does not strictly need, while this one\nanswers the addressee's own session and can name the ids its accept and\ndecline calls take.\n\n``organization_member_id`` is what those two calls address, not\n``invitation_id``: the membership is the row that outlives a revoke and a\nre-invite (each round mints a fresh ``Invitation`` against the same\nmembership), so a client holding a list from a moment ago names something\nstill resolvable rather than a token-shaped id that has since been\nsuperseded. ``invitation_id`` rides along for the roster's sake, since\n``ActiveOrganizationMemberPublic`` carries the same field.","properties":{"created_at":{"format":"date-time","title":"Created At","type":"string"},"email":{"title":"Email","type":"string"},"expires_at":{"format":"date-time","title":"Expires At","type":"string"},"invitation_id":{"format":"uuid","title":"Invitation Id","type":"string"},"organization_id":{"format":"uuid","title":"Organization Id","type":"string"},"organization_member_id":{"format":"uuid","title":"Organization Member Id","type":"string"},"organization_name":{"title":"Organization Name","type":"string"},"role":{"title":"Role","type":"string"}},"required":["organization_member_id","invitation_id","organization_id","organization_name","email","role","expires_at","created_at"],"title":"PendingOrganizationInvitationPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/organizations/me/pending-memberships":{"get":{"description":"List the organization invitations still awaiting the caller.\n\nThe invitee's side of the invitation flow, where ``/me/member-invitations``\nis the admin's. Not to be confused with ``GET /me/memberships``, which\nlists the organizations the caller is already an active member of and\ndeliberately omits an ``invited`` one.\n\nTakes no token, unlike ``/api/v1/invitations/*``: those are public because the\nrecipient of an emailed link holds nothing else to prove anything with,\nwhile this caller is authenticated as the addressee and the membership's\nown ``user_id`` is what scopes the answer. An invitation whose deadline has\npassed is omitted rather than listed as unactionable.","operationId":"organizations-list_caller_pending_memberships","parameters":[{"description":"Number of records to skip","in":"query","name":"skip","required":false,"schema":{"default":0,"description":"Number of records to skip","minimum":0,"title":"Skip","type":"integer"}},{"description":"Maximum number of records to return","in":"query","name":"limit","required":false,"schema":{"default":100,"description":"Maximum number of records to return","maximum":1000,"minimum":1,"title":"Limit","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingOrganizationInvitationsPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"List Caller Pending Memberships","tags":["organizations"]}}}}
```

## Accept Caller Pending Membership

> Accept an invitation addressed to the caller, resolving it to an active membership.\
> \
> Does the same work as \`\`POST /api/v1/invitations/accept\`\`, including the\
> workspace assignments parked at invite time, and answers the same shape.\
> Addressed by membership id rather than by token: the caller is already the\
> addressee, so a token would add nothing their session does not carry.\
> \
> Idempotent for any membership the caller already holds \`\`active\`\`, which is\
> what two clicks before the list refreshes produces: it answers that\
> membership's organization and role rather than a 404 for an action that\
> worked. Deliberately not narrowed to memberships that got there by\
> accepting, which would cost a lookup to tell the two apart and answer a\
> caller nothing they cannot already read from \`\`GET /me/memberships\`\`.\
> \
> Answers 404 for a membership that is not the caller's own, whether or not\
> it exists, and for one of theirs that is neither \`\`active\`\` nor holding an\
> invitation. An invitation that has lapsed answers 400.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"AcceptInvitationResultPublic":{"description":"What accepting produces: enough for the accept page to say where the visitor landed.\n\nNo session and no token. When the request carried a password, the identity\ncan sign in straight away; otherwise it stays password-less until claimed by\nsignup or a provider sign-in.","properties":{"can_sign_in":{"default":true,"description":"Whether the invited address can sign in now, with a password (this accept's included) or through a provider. False when it accepted without one and has never signed in.","title":"Can Sign In","type":"boolean"},"organization_name":{"title":"Organization Name","type":"string"},"password_set":{"default":false,"description":"Whether this accept set the identity's password.","title":"Password Set","type":"boolean"},"role":{"title":"Role","type":"string"},"verification_pending":{"default":false,"description":"Whether the invited address chose a password through signup and has not verified it yet, so it has to open its verification link before it can sign in.","title":"Verification Pending","type":"boolean"},"ways_in":{"description":"When can_sign_in is false, what this deployment offers instead, as the preview's ways_in says.","items":{"enum":["provider","password_reset"],"type":"string"},"title":"Ways In","type":"array"}},"required":["organization_name","role"],"title":"AcceptInvitationResultPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/organizations/me/pending-memberships/{organization_member_id}/accept":{"post":{"description":"Accept an invitation addressed to the caller, resolving it to an active membership.\n\nDoes the same work as ``POST /api/v1/invitations/accept``, including the\nworkspace assignments parked at invite time, and answers the same shape.\nAddressed by membership id rather than by token: the caller is already the\naddressee, so a token would add nothing their session does not carry.\n\nIdempotent for any membership the caller already holds ``active``, which is\nwhat two clicks before the list refreshes produces: it answers that\nmembership's organization and role rather than a 404 for an action that\nworked. Deliberately not narrowed to memberships that got there by\naccepting, which would cost a lookup to tell the two apart and answer a\ncaller nothing they cannot already read from ``GET /me/memberships``.\n\nAnswers 404 for a membership that is not the caller's own, whether or not\nit exists, and for one of theirs that is neither ``active`` nor holding an\ninvitation. An invitation that has lapsed answers 400.","operationId":"organizations-accept_caller_pending_membership","parameters":[{"in":"path","name":"organization_member_id","required":true,"schema":{"format":"uuid","title":"Organization Member Id","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptInvitationResultPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Accept Caller Pending Membership","tags":["organizations"]}}}}
```

## Decline Caller Pending Membership

> Decline an invitation addressed to the caller.\
> \
> Lands the pair where a revoke does: the invitation cancelled and the\
> membership suspended rather than deleted, which is what stops the emailed\
> link from later reviving a declined invitation. A future invite to the same\
> address revives the membership.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"Message":{"description":"A human-readable acknowledgment for an operation with nothing to return.","properties":{"message":{"description":"What happened.","title":"Message","type":"string"}},"required":["message"],"title":"Message","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/organizations/me/pending-memberships/{organization_member_id}/decline":{"post":{"description":"Decline an invitation addressed to the caller.\n\nLands the pair where a revoke does: the invitation cancelled and the\nmembership suspended rather than deleted, which is what stops the emailed\nlink from later reviving a declined invitation. A future invite to the same\naddress revives the membership.","operationId":"organizations-decline_caller_pending_membership","parameters":[{"in":"path","name":"organization_member_id","required":true,"schema":{"format":"uuid","title":"Organization Member Id","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Decline Caller Pending Membership","tags":["organizations"]}}}}
```

## Switch Active Organization

> Point the caller's identity at another organization they belong to.\
> \
> Distinct from \`\`PATCH /me\`\`, which renames the organization already active.\
> This changes which organization every later request is scoped to, so\
> workspaces, keys, budgets and usage all follow it. Answers 404 for an\
> organization the caller holds no active membership in, whether or not it\
> exists.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"SwitchActiveOrganizationRequest":{"description":"Point the caller's identity at one of the organizations they belong to.\n\nThe one request in this surface that names an organization by id, and it is\nnot a hole in the tenant boundary: an id the caller holds no active\nmembership in answers 404, so it says nothing about whether the\norganization exists.","properties":{"organization_id":{"format":"uuid","title":"Organization Id","type":"string"}},"required":["organization_id"],"title":"SwitchActiveOrganizationRequest","type":"object"},"OrganizationMembershipContextPublic":{"description":"An organization plus the caller's standing in it.\n\nWhat every tenancy page reads first: which organization it is looking at,\nand what the caller may do there.","properties":{"caller":{"anyOf":[{"$ref":"#/components/schemas/CallerIdentityPublic"},{"type":"null"}]},"deployment_operator":{"default":false,"title":"Deployment Operator","type":"boolean"},"organization":{"$ref":"#/components/schemas/OrganizationPublic"},"organization_member_id":{"format":"uuid","title":"Organization Member Id","type":"string"},"provider_key_encryption_available":{"default":false,"title":"Provider Key Encryption Available","type":"boolean"},"role":{"title":"Role","type":"string"},"status":{"title":"Status","type":"string"},"workspace_memberships":{"items":{"$ref":"#/components/schemas/CallerWorkspaceMembershipPublic"},"title":"Workspace Memberships","type":"array"}},"required":["organization_member_id","role","status","organization"],"title":"OrganizationMembershipContextPublic","type":"object"},"CallerIdentityPublic":{"description":"Who the caller is, as against what they may do.\n\nThe account control at the foot of the dashboard's sidebar draws a person,\nand no authenticated route reported the caller's own name or address, so it\ndrew a role for everybody who could reach it: \"Operator\", which is not a\nname and on a multi-tenant deployment was not even true\n(mozilla-ai/otari#832).\n\nCarried on the membership context for the reason ``deployment_operator`` is:\nthe shell reads that context before it paints, so an identity taken from it\nneeds no request of its own and cannot arrive a beat after the chrome it\nnames. Publishing it costs nothing either, since it is the caller's own\nidentity and they are holding the credential that resolved to it.\n\n``email`` and ``full_name`` are nullable for opposite reasons. A local\noperator identity has no address, because first boot provisions it with a\nname and nothing to sign in with but the master key; a member added by\naddress has no name until they claim the identity and supply one. So a shell\nhas to be ready to draw either one alone.","properties":{"claims_deployment":{"description":"Whether setting this identity's password claims the deployment, which stops the master key signing in to the dashboard. True for the deployment's operator until it holds a password, whether or not it already has an address; false for everybody else.","title":"Claims Deployment","type":"boolean"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"has_password":{"description":"Whether this identity holds a dashboard password. False for one that signs in only through an OAuth provider or a passkey, and for a roster entry nobody has claimed yet. PUT /api/v1/auth/password requires current_password from a cookie-authenticated caller exactly while this is true.","title":"Has Password","type":"boolean"},"user_id":{"format":"uuid","title":"User Id","type":"string"}},"required":["user_id","has_password","claims_deployment"],"title":"CallerIdentityPublic","type":"object"},"OrganizationPublic":{"properties":{"created_at":{"format":"date-time","title":"Created At","type":"string"},"created_by_user_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"Created By User Id"},"id":{"format":"uuid","title":"Id","type":"string"},"name":{"maxLength":255,"title":"Name","type":"string"},"slug":{"maxLength":255,"title":"Slug","type":"string"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Updated At"}},"required":["name","slug","id","created_at"],"title":"OrganizationPublic","type":"object"},"CallerWorkspaceMembershipPublic":{"description":"One workspace the caller belongs to, and their role in it.\n\nCarried on the membership context so the shell can populate its workspace\nswitcher and choose a default from the first authenticated call, rather than\nlisting workspaces and then asking for the caller's role in each. Only the\ncaller's own memberships appear, so this is not a directory of the\norganization's workspaces: an admin sees the ones they joined, and the\nworkspace list endpoint remains the way to see the rest.","properties":{"name":{"title":"Name","type":"string"},"role":{"title":"Role","type":"string"},"workspace_id":{"format":"uuid","title":"Workspace Id","type":"string"}},"required":["workspace_id","name","role"],"title":"CallerWorkspaceMembershipPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/organizations/me/switch":{"post":{"description":"Point the caller's identity at another organization they belong to.\n\nDistinct from ``PATCH /me``, which renames the organization already active.\nThis changes which organization every later request is scoped to, so\nworkspaces, keys, budgets and usage all follow it. Answers 404 for an\norganization the caller holds no active membership in, whether or not it\nexists.","operationId":"organizations-switch_active_organization","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SwitchActiveOrganizationRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationMembershipContextPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Switch Active Organization","tags":["organizations"]}}}}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.otari.ai/api-reference/organizations.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
