Organizations
Read and update organization records — the top-level tenant boundary. Every token is scoped to exactly one org (via API Management-injected X-Cirrus-Org-Id), so "list orgs" isn't a partner-facing operation — you either read the org your token belongs to, or you pass an orgId that must match.
| Operation | Scope required |
|---|---|
Read (GET) | organizations:read |
| Update | organizations:write |
PATCH /organizations/{orgId} requires Idempotency-Key and If-Match (from a prior GET's ETag).
/organizations/me returns the org the token belongs to. /me refers to the token's identity, not a user — every v1alpha1 token is org-scoped, so the token's identity is the org. When per-user OAuth ships, /me will resolve the authorising user on the users endpoint; the URL convention is consistent across both.
Endpoints on this page
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1alpha1/organizations/me | The org the caller's token belongs to. Details ↓ |
GET | /api/v1alpha1/organizations/{orgId} | Fetch one organization. {orgId} must equal the token's org context. Details ↓ |
GET | /api/v1alpha1/organizations/me/entitlements | Feature entitlements — which Cirrus features the org has licensed, plus seat counts. Details ↓ |
PATCH | /api/v1alpha1/organizations/{orgId} | Partial-merge update. Name, industry, primary email domain. Details ↓ |
Get my organization
GET /api/v1alpha1/organizations/me
The org the token belongs to. Partners often don't want to hardcode the org_... id.
Response
Identical shape to GET /organizations/{orgId}. Includes an ETag header.
/mereturns the resource directly, not an HTTP redirect. Saves a round trip vs a302.- A token that isn't org-scoped returns
401 token-not-org-scoped. Should never happen in v1alpha1 (all tokens are org-scoped), but the contract is explicit for future auth models.
curl -s "https://altus.cirrusinsight.com/api/v1alpha1/organizations/me" \
-H "X-Cirrus-Api-Key: $TOKEN"import requests
response = requests.get(
"https://altus.cirrusinsight.com/api/v1alpha1/organizations/me",
headers={"X-Cirrus-Api-Key": token},
)
response.raise_for_status()
org = response.json()
etag = response.headers["ETag"]using System.Net.Http;
using System.Net.Http.Headers;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Cirrus-Api-Key", token);
var response = await client.GetAsync(
"https://altus.cirrusinsight.com/api/v1alpha1/organizations/me");
response.EnsureSuccessStatusCode();
var org = await response.Content.ReadAsStringAsync();
var etag = response.Headers.ETag?.Tag;Get an organization
GET /api/v1alpha1/organizations/{orgId}
{orgId} must equal the token's org context; otherwise 404 organization-not-found (cross-org access never leaks).
Response
Response headers:
| Header | Value | Notes |
|---|---|---|
ETag | W/"<version>" | Required on subsequent PATCH via If-Match. |
See the Organizationmodel for the full field reference.
administratoris a single user — the org's contractual owner (billing contact, signatory). Other users with theAdministratorrole show up viaGET /users?role=Administrator.emailDomains[]returns the domain string only, not per-domain verification-status flags.
Errors
404 organization-not-found—orgIddoesn't equal the token's org context
curl -s "https://altus.cirrusinsight.com/api/v1alpha1/organizations/org_01H8YKQ2N9ACME" \
-H "X-Cirrus-Api-Key: $TOKEN"import requests
org_id = "org_01H8YKQ2N9ACME"
response = requests.get(
f"https://altus.cirrusinsight.com/api/v1alpha1/organizations/{org_id}",
headers={"X-Cirrus-Api-Key": token},
)
response.raise_for_status()
org = response.json()
etag = response.headers["ETag"]using System.Net.Http;
using System.Net.Http.Headers;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Cirrus-Api-Key", token);
var orgId = "org_01H8YKQ2N9ACME";
var response = await client.GetAsync(
$"https://altus.cirrusinsight.com/api/v1alpha1/organizations/{orgId}");
response.EnsureSuccessStatusCode();
var org = await response.Content.ReadAsStringAsync();
var etag = response.Headers.ETag?.Tag;{
"id": "org_...",
"name": "Acme Corporation",
"primaryEmailDomain": "acme.com",
"emailDomains": ["acme.com", "acme.co.uk"],
"administrator": {
"userId": "usr_...",
"displayName": "Sam Chen"
},
"industry": "SaaS",
"employeeCount": 1250,
"logoUrl": "https://cdn.example.com/orgs/org_....png",
"sourceRefs": {
"salesforce": {
"orgId": "00D1U000000abcXYZ"
}
},
"createdAt": "2024-03-15T00:00:00Z",
"updatedAt": "2026-07-14T09:30:00Z"
}Get entitlements
GET /api/v1alpha1/organizations/me/entitlements
Which Cirrus features the org has licensed. Separate sub-resource — different cache TTL (bill-driven, not profile-driven).
Response
Response headers:
| Header | Value | Notes |
|---|---|---|
Cache-Control | private, max-age=60 | Suggested client caching. Billing events are minute-scale, not real-time. |
featuresis a fixed enum, not an open-ended map. New features get added with a documentedavailable_sincedate; removed features stay asfalsefor one major version before being retired.seatsis exposed even though billing detail isn't, because the invite-flow error (seat-limit-exceeded) needs a way for partners to preflight. Explicitseats.availablemakes the flow honest.licenseTier.slugis coarse-grained:free,starter,professional,enterprise. Partners key onslug. For per-feature capability checks, usefeatures.{name}— don't infer from tier.- No trial-end date. Partners running upsell flows use
licenseTier.slug === "free"or"starter"as their signal.
Errors
None beyond the standard 401 / 403 / 429. The endpoint always has an answer for a valid caller.
curl -s "https://altus.cirrusinsight.com/api/v1alpha1/organizations/me/entitlements" \
-H "X-Cirrus-Api-Key: $TOKEN"import requests
response = requests.get(
"https://altus.cirrusinsight.com/api/v1alpha1/organizations/me/entitlements",
headers={"X-Cirrus-Api-Key": token},
)
response.raise_for_status()
entitlements = response.json()using System.Net.Http;
using System.Net.Http.Headers;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Cirrus-Api-Key", token);
var response = await client.GetAsync(
"https://altus.cirrusinsight.com/api/v1alpha1/organizations/me/entitlements");
response.EnsureSuccessStatusCode();
var entitlements = await response.Content.ReadAsStringAsync();{
"orgId": "org_...",
"features": {
"smartScheduling": true,
"teamCalendarScheduling": true,
"replyTracking": true,
"emailBlast": false,
"cortexApp": true,
"meetingAi": true
},
"seats": {
"total": 25,
"used": 18,
"available": 7
},
"licenseTier": {
"slug": "professional",
"name": "Professional"
},
"asOf": "2026-08-20T15:00:00Z"
}Update an organization
PATCH /api/v1alpha1/organizations/{orgId}
Update org display name, industry, primary email domain. Requires organizations:write, Idempotency-Key, and If-Match.
Mutable fields
| Field | Type | Notes |
|---|---|---|
name | string | Display name. Max 200 chars. |
primaryEmailDomain | string | Must be an already-verified domain from emailDomains[]. Rotates which domain is used for invitation flows. |
industry | string | Free-text, aligned with Cortex's account industry taxonomy. |
employeeCount | number | 1–1,000,000. |
Response
The updated org record — same shape as GET /organizations/{orgId}. Fresh ETag header.
- Verifying a new email domain is out of scope for
PATCH. Adding a domain requires DNS verification (interactive TXT-record challenge).PATCHcan only rotate the primary domain among already-verified entries. administratoris not mutable via PATCH. Transferring org ownership requires the outgoing admin's consent + the incoming admin's acceptance — dashboard-only in v1alpha1.- No writes to
sourceRefs. External-system links are managed through the integration-connection flows, not this PATCH.
Errors
| Status | type | When |
|---|---|---|
| 400 | invalid-field | Unknown field or invalid value |
| 400 | email-domain-not-verified | primaryEmailDomain isn't in the org's verified domain list |
| 403 | write-scope-required | Token has organizations:read but not organizations:write |
| 404 | organization-not-found | orgId doesn't equal the token's org context |
| 412 | precondition-failed | If-Match doesn't match current version |
| 422 | idempotency-key-conflict | Standard replay violation |
curl -s -X PATCH "https://altus.cirrusinsight.com/api/v1alpha1/organizations/org_01H8YKQ2N9ACME" \
-H "X-Cirrus-Api-Key: $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-H 'If-Match: W/"12"' \
-d '{
"name": "Acme Global, Inc.",
"industry": "Enterprise Software",
"employeeCount": 1400
}'import requests
import uuid
org_id = "org_01H8YKQ2N9ACME"
response = requests.patch(
f"https://altus.cirrusinsight.com/api/v1alpha1/organizations/{org_id}",
headers={
"X-Cirrus-Api-Key": token,
"Idempotency-Key": str(uuid.uuid4()),
"If-Match": etag,
},
json={
"name": "Acme Global, Inc.",
"industry": "Enterprise Software",
"employeeCount": 1400,
},
)
response.raise_for_status()
updated = response.json()using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Cirrus-Api-Key", token);
var orgId = "org_01H8YKQ2N9ACME";
var body = new
{
name = "Acme Global, Inc.",
industry = "Enterprise Software",
employeeCount = 1400,
};
var request = new HttpRequestMessage(HttpMethod.Patch,
$"https://altus.cirrusinsight.com/api/v1alpha1/organizations/{orgId}")
{
Content = new StringContent(
JsonSerializer.Serialize(body),
Encoding.UTF8,
"application/json"),
};
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Headers.Add("If-Match", etag);
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
var updated = await response.Content.ReadAsStringAsync();Related models
- Organization — the full field reference.
- User — every user has an
orgIdthat resolves to an Organization.