Deals
Read and write deal records — Cirrus Insight's pipeline / opportunity objects. Deals are the parent that 8Ps and Q&As hang off; they carry a schema-driven customFields payload and reference the underlying account.
| Operation | Scope required |
|---|---|
Read (GET) | deals:read |
| Create / update | deals:write |
Write endpoints require Idempotency-Key on every call. Same 24-hour replay window as the scheduling endpoints.
Endpoints on this page
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1alpha1/deals | List deals visible to the org. Filter by account, stage, owner, close date. Details ↓ |
GET | /api/v1alpha1/deals/{dealId} | Fetch one deal's full record — including customFields and 8P pointers. Details ↓ |
GET | /api/v1alpha1/deals/by-source | Look up a deal by CRM source id (Salesforce, etc.). Details ↓ |
POST | /api/v1alpha1/deals | Create a deal. Idempotency-Key required. Details ↓ |
PATCH | /api/v1alpha1/deals/{dealId} | Partial-merge update. Any subset of mutable fields. Details ↓ |
GET | /api/v1alpha1/deals/{dealId}/meetings | List meetings linked to a deal. Details ↓ |
GET | /api/v1alpha1/accounts/{accountId}/deals | List deals on one account. Requires accounts:read too. Details ↓ |
List deals
GET /api/v1alpha1/deals
Query parameters
| Parameter | Type | Description |
|---|---|---|
cursor | string | Standard keyset cursor. |
limit | number | Clamped 1–100, default 25. |
sort | string | updatedAt, createdAt, name, closeDate, amount. Default -updatedAt. |
search | string | (optional) Case-insensitive substring on deal name. |
accountId | string | (optional) Filter to one account. |
stage | string | (optional, repeatable) Filter to specific pipeline stages. Values are org-configured — discover via the schema attached to the deal. |
owner | string | (optional) usr_... id. Filter to deals owned by one user. |
closingBefore | string | (optional) ISO 8601 date. Deals with closeDate ≤ this. |
closingAfter | string | (optional) ISO 8601 date. Deals with closeDate ≥ this. |
Response
See the Dealmodel for the full field reference.
amountis always{value, currency}, not a bare number. Explicit currency prevents ambiguity in multi-currency orgs.stagevalues are org-configured — the available stages come from the schema attached to the deal.owneris the deal owner (Cirrus user accountable for the deal), not the org owner.
Errors
Standard pagination errors.
curl -s "https://altus.cirrusinsight.com/api/v1alpha1/deals?stage=Proposal&limit=25" \
-H "X-Cirrus-Api-Key: $TOKEN"import requests
response = requests.get(
"https://altus.cirrusinsight.com/api/v1alpha1/deals",
headers={"X-Cirrus-Api-Key": token},
params={"stage": "Proposal", "limit": 25},
)
response.raise_for_status()
page = response.json()using System.Net.Http;
using System.Net.Http.Headers;
using System.Web;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Cirrus-Api-Key", token);
var query = HttpUtility.ParseQueryString(string.Empty);
query["stage"] = "Proposal";
query["limit"] = "25";
var uri = new UriBuilder("https://altus.cirrusinsight.com/api/v1alpha1/deals")
{
Query = query.ToString()
}.Uri;
var response = await client.GetAsync(uri);
response.EnsureSuccessStatusCode();
var page = await response.Content.ReadAsStringAsync();{
"items": [
{
"id": "deal_...",
"orgId": "org_...",
"name": "Acme Q3 Expansion",
"stage": "Proposal",
"amount": {
"value": 125000,
"currency": "USD"
},
"closeDate": "2026-09-30",
"account": {
"id": "acct_...",
"name": "Acme Corporation"
},
"owner": {
"userId": "usr_...",
"displayName": "Alex Rivera"
},
"sourceRefs": {
"salesforce": {
"id": "0061U00000abcXYZ"
}
},
"createdAt": "2026-05-01T12:00:00Z",
"updatedAt": "2026-07-14T09:30:00Z"
}
],
"nextCursor": null
}Get a deal
GET /api/v1alpha1/deals/{dealId}
Full detail — includes the schema-driven customFields payload and pointers to the deal's 8P records.
Response
customFields— bag of untyped values per the deal's schema. Value types match the schema (string, number, boolean, array). A partner integrating deeply against a specific schema should fetch the schema definition once and cache the field shapes.insightSummary— pointers to the eight 8P records for this deal. Any pointer isnullwhen the 8P hasn't been created yet.schemaVersion— pinned per deal. Allows partners to branch on shape changes.
Errors
404 deal-not-found— id unknown or cross-org
curl -s "https://altus.cirrusinsight.com/api/v1alpha1/deals/deal_01H8YKQ2N9RXVT" \
-H "X-Cirrus-Api-Key: $TOKEN"import requests
deal_id = "deal_01H8YKQ2N9RXVT"
response = requests.get(
f"https://altus.cirrusinsight.com/api/v1alpha1/deals/{deal_id}",
headers={"X-Cirrus-Api-Key": token},
)
response.raise_for_status()
deal = response.json()using System.Net.Http;
using System.Net.Http.Headers;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Cirrus-Api-Key", token);
var dealId = "deal_01H8YKQ2N9RXVT";
var response = await client.GetAsync(
$"https://altus.cirrusinsight.com/api/v1alpha1/deals/{dealId}");
response.EnsureSuccessStatusCode();
var deal = await response.Content.ReadAsStringAsync();{
"id": "deal_...",
"orgId": "org_...",
"name": "Acme Q3 Expansion",
"stage": "Proposal",
"amount": { "value": 125000, "currency": "USD" },
"closeDate": "2026-09-30",
"account": { "id": "acct_...", "name": "Acme Corporation" },
"owner": { "userId": "usr_...", "displayName": "Alex Rivera" },
"primaryContact": { "id": "cont_...", "displayName": "Sample Guest" },
"schemaId": "schm_...",
"schemaVersion": 3,
"customFields": {
"leadSource": "Inbound - Webinar",
"productLine": "Enterprise",
"renewalMonths": 12
},
"insightSummary": {
"profileId": "prof_...",
"problemId": "prob_...",
"painId": "pain_...",
"powerId": "powr_...",
"positionId": "posn_...",
"phasesId": "phas_...",
"processId": "proc_...",
"planId": "plan_..."
},
"sourceRefs": {
"salesforce": {
"id": "0061U00000abcXYZ",
"url": "https://acme.my.salesforce.com/0061U00000abcXYZ"
}
},
"createdAt": "2026-05-01T12:00:00Z",
"updatedAt": "2026-07-14T09:30:00Z"
}Look up by source
GET /api/v1alpha1/deals/by-source
CRM-sync integrations wake up with a Salesforce opportunity id and need the corresponding Cirrus deal. This endpoint is the lookup path.
Query parameters
| Parameter | Type | Description |
|---|---|---|
sourceType | string | Currently salesforce in v1alpha1. Room for hubspot, etc. |
sourceId | string | The external system's id (e.g., a Salesforce 0061U00000abcXYZ). |
Both parameters required.
Response
Same shape as GET /api/v1alpha1/deals/{dealId}.
Errors
404 deal-not-found— no deal in the calling org references the given(sourceType, sourceId)pair. Same 404 as an unknown Cirrus deal id — never leaks whether the external record exists.
curl -s "https://altus.cirrusinsight.com/api/v1alpha1/deals/by-source?sourceType=salesforce&sourceId=0061U00000abcXYZ" \
-H "X-Cirrus-Api-Key: $TOKEN"import requests
response = requests.get(
"https://altus.cirrusinsight.com/api/v1alpha1/deals/by-source",
headers={"X-Cirrus-Api-Key": token},
params={"sourceType": "salesforce", "sourceId": "0061U00000abcXYZ"},
)
response.raise_for_status()
deal = response.json()using System.Net.Http;
using System.Net.Http.Headers;
using System.Web;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Cirrus-Api-Key", token);
var query = HttpUtility.ParseQueryString(string.Empty);
query["sourceType"] = "salesforce";
query["sourceId"] = "0061U00000abcXYZ";
var uri = new UriBuilder("https://altus.cirrusinsight.com/api/v1alpha1/deals/by-source")
{
Query = query.ToString()
}.Uri;
var response = await client.GetAsync(uri);
response.EnsureSuccessStatusCode();
var deal = await response.Content.ReadAsStringAsync();Create a deal
POST /api/v1alpha1/deals
Headers
| Header | Description |
|---|---|
Idempotency-Key | Required. Opaque client-generated string up to 255 chars. Same replay semantics as scheduling. |
Request body
| Field | Required | Notes |
|---|---|---|
name | yes | |
accountId | yes | Must belong to the calling org. |
stage | yes | Must be a valid stage in the deal's schema. |
amount.currency | conditional | Required when amount.value is set. |
closeDate | no | ISO 8601 date. |
ownerId | no | Defaults to the token's owner. |
primaryContactId | no | |
schemaId | no | Defaults to the org's default deal schema. |
customFields | no | Must match the schema. |
sourceRefs.salesforce.id | no | Sets the CRM cross-reference at creation. Prevents Salesforce sync from creating a duplicate later. |
Response
201 Created with the newly-created deal in the same shape as GET /api/v1alpha1/deals/{dealId}.
Errors
400 idempotency-key-required— header missing404 account-not-found—accountIdunknown or cross-org404 contact-not-found—primaryContactIdunknown or cross-org422 schema-mismatch—customFieldsdon't match the schema422 source-ref-conflict— another deal in your org already references the same(sourceType, sourceId)pair422 idempotency-key-conflict— same key, different body
curl -s -X POST "https://altus.cirrusinsight.com/api/v1alpha1/deals" \
-H "X-Cirrus-Api-Key: $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"name": "Acme Q3 Expansion",
"accountId": "acct_01H8YKQ2N9ACME",
"stage": "Discovery",
"amount": { "value": 125000, "currency": "USD" },
"closeDate": "2026-09-30",
"ownerId": "usr_01H8YKQ2N9ARIVERA",
"sourceRefs": {
"salesforce": { "id": "0061U00000abcXYZ" }
}
}'import requests
import uuid
headers = {
"X-Cirrus-Api-Key": token,
"Idempotency-Key": str(uuid.uuid4()),
}
response = requests.post(
"https://altus.cirrusinsight.com/api/v1alpha1/deals",
headers=headers,
json={
"name": "Acme Q3 Expansion",
"accountId": "acct_01H8YKQ2N9ACME",
"stage": "Discovery",
"amount": {"value": 125000, "currency": "USD"},
"closeDate": "2026-09-30",
"ownerId": "usr_01H8YKQ2N9ARIVERA",
"sourceRefs": {
"salesforce": {"id": "0061U00000abcXYZ"},
},
},
)
response.raise_for_status()
deal = 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 body = new
{
name = "Acme Q3 Expansion",
accountId = "acct_01H8YKQ2N9ACME",
stage = "Discovery",
amount = new { value = 125000, currency = "USD" },
closeDate = "2026-09-30",
ownerId = "usr_01H8YKQ2N9ARIVERA",
sourceRefs = new
{
salesforce = new { id = "0061U00000abcXYZ" },
},
};
var request = new HttpRequestMessage(
HttpMethod.Post,
"https://altus.cirrusinsight.com/api/v1alpha1/deals")
{
Content = new StringContent(
JsonSerializer.Serialize(body),
Encoding.UTF8,
"application/json"),
};
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
var deal = await response.Content.ReadAsStringAsync();{
"name": "Acme Q3 Expansion",
"accountId": "acct_...",
"stage": "Discovery",
"amount": { "value": 125000, "currency": "USD" },
"closeDate": "2026-09-30",
"ownerId": "usr_...",
"primaryContactId": "cont_...",
"schemaId": "schm_...",
"customFields": {
"leadSource": "Inbound - Webinar"
},
"sourceRefs": {
"salesforce": { "id": "0061U00000abcXYZ" }
}
}Update a deal
PATCH /api/v1alpha1/deals/{dealId}
Partial-merge update. Any subset of mutable fields.
Headers
| Header | Description |
|---|---|
Idempotency-Key | Required. |
Request body
Every field optional. Only present fields are updated.
customFieldsmerges, doesn't replace. Top-level keys in the request are set; existing keys not in the request are preserved. To remove a field, passnull.
Response
200 OK with the updated deal.
Errors
Same as POST plus:
404 deal-not-found— id unknown or cross-org409 deal-closed— deal is in a terminal stage (Won / Lost). Closed deals are immutable via the API in v1alpha1.
Closed-deal immutability. Reopening a Won/Lost deal is a sales-process concern the dashboard handles with confirmation flows. Deferred to a future write-scope expansion.
curl -s -X PATCH "https://altus.cirrusinsight.com/api/v1alpha1/deals/deal_01H8YKQ2N9RXVT" \
-H "X-Cirrus-Api-Key: $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"stage": "Negotiation",
"amount": { "value": 145000, "currency": "USD" },
"customFields": {
"leadSource": "Inbound - Referral"
}
}'import requests
import uuid
deal_id = "deal_01H8YKQ2N9RXVT"
headers = {
"X-Cirrus-Api-Key": token,
"Idempotency-Key": str(uuid.uuid4()),
}
response = requests.patch(
f"https://altus.cirrusinsight.com/api/v1alpha1/deals/{deal_id}",
headers=headers,
json={
"stage": "Negotiation",
"amount": {"value": 145000, "currency": "USD"},
"customFields": {
"leadSource": "Inbound - Referral",
},
},
)
response.raise_for_status()
deal = 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 dealId = "deal_01H8YKQ2N9RXVT";
var body = new
{
stage = "Negotiation",
amount = new { value = 145000, currency = "USD" },
customFields = new
{
leadSource = "Inbound - Referral",
},
};
var request = new HttpRequestMessage(
HttpMethod.Patch,
$"https://altus.cirrusinsight.com/api/v1alpha1/deals/{dealId}")
{
Content = new StringContent(
JsonSerializer.Serialize(body),
Encoding.UTF8,
"application/json"),
};
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
var deal = await response.Content.ReadAsStringAsync();{
"stage": "Negotiation",
"amount": { "value": 145000, "currency": "USD" },
"customFields": {
"leadSource": "Inbound - Referral"
}
}List linked meetings
GET /api/v1alpha1/deals/{dealId}/meetings
List of meetings that produced 8P insights, Q&As, or other context for this deal.
Query parameters
| Parameter | Type | Description |
|---|---|---|
cursor | string | Standard keyset cursor. |
limit | number | Clamped 1–100, default 25. |
sort | string | startTime only. Default -startTime. |
since | string | (optional) Cap the lookback. |
Response
Lightweight meeting references (not full Meeting objects):
hasTranscript: true signals that a GET /api/v1alpha1/scheduled-meetings/{id}/transcript call will succeed. Pre-filtering here saves partners from having to try each meeting.
For the full Meeting object, click through to GET /api/v1alpha1/scheduled-meetings/{id}.
Errors
404 deal-not-found— id unknown or cross-org
curl -s "https://altus.cirrusinsight.com/api/v1alpha1/deals/deal_01H8YKQ2N9RXVT/meetings?limit=25&since=2026-01-01" \
-H "X-Cirrus-Api-Key: $TOKEN"import requests
deal_id = "deal_01H8YKQ2N9RXVT"
response = requests.get(
f"https://altus.cirrusinsight.com/api/v1alpha1/deals/{deal_id}/meetings",
headers={"X-Cirrus-Api-Key": token},
params={"limit": 25, "since": "2026-01-01"},
)
response.raise_for_status()
page = response.json()using System.Net.Http;
using System.Net.Http.Headers;
using System.Web;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Cirrus-Api-Key", token);
var dealId = "deal_01H8YKQ2N9RXVT";
var query = HttpUtility.ParseQueryString(string.Empty);
query["limit"] = "25";
query["since"] = "2026-01-01";
var uri = new UriBuilder(
$"https://altus.cirrusinsight.com/api/v1alpha1/deals/{dealId}/meetings")
{
Query = query.ToString()
}.Uri;
var response = await client.GetAsync(uri);
response.EnsureSuccessStatusCode();
var page = await response.Content.ReadAsStringAsync();{
"items": [
{
"id": "mtg_...",
"title": "Acme Discovery Call",
"startTime": "2026-06-16T17:00:00Z",
"endTime": "2026-06-16T17:30:00Z",
"host": { "userId": "usr_...", "displayName": "Alex Rivera" },
"hasTranscript": true
}
],
"nextCursor": null
}List deals on an account
GET /api/v1alpha1/accounts/{accountId}/deals
Convenience wrapper equivalent to GET /api/v1alpha1/deals?accountId={id}.
Requires both deals:read and accounts:read.
Query params identical to GET /api/v1alpha1/deals except accountId (nonsensical here).
Errors
404 account-not-found
curl -s "https://altus.cirrusinsight.com/api/v1alpha1/accounts/acct_01H8YKQ2N9ACME/deals?stage=Proposal&limit=25" \
-H "X-Cirrus-Api-Key: $TOKEN"import requests
account_id = "acct_01H8YKQ2N9ACME"
response = requests.get(
f"https://altus.cirrusinsight.com/api/v1alpha1/accounts/{account_id}/deals",
headers={"X-Cirrus-Api-Key": token},
params={"stage": "Proposal", "limit": 25},
)
response.raise_for_status()
page = response.json()using System.Net.Http;
using System.Net.Http.Headers;
using System.Web;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Cirrus-Api-Key", token);
var accountId = "acct_01H8YKQ2N9ACME";
var query = HttpUtility.ParseQueryString(string.Empty);
query["stage"] = "Proposal";
query["limit"] = "25";
var uri = new UriBuilder(
$"https://altus.cirrusinsight.com/api/v1alpha1/accounts/{accountId}/deals")
{
Query = query.ToString()
}.Uri;
var response = await client.GetAsync(uri);
response.EnsureSuccessStatusCode();
var page = await response.Content.ReadAsStringAsync();