Skip to content
Preview. The endpoints on this page are illustrative and are likely to change as they move toward general availability. Documentation is published in advance so you can start shaping your integration; treat request and response details as subject to revision.

Q&As

Read and write the Q&A backlog on a deal — the questions the sales team has asked (or been asked) and the current state of each answer.

Two directions

  • Discovery — the sales team hasn't yet learned about a 8P field. A Discovery Q&A points at a specific 8P schema field (e.g., profile.tenure) that's blank or under-supported, so the seller knows what to ask next.
  • RFI (Request For Information) — the buyer asked a question the seller couldn't answer in the moment. Attached to a contact.

Three states

  • Open — new / unanswered.
  • Answered — has an answer attached. Transitions automatically when an answer with confidence ≥ 0.6 is attached.
  • Dismissed — won't-answer. Not a hard delete — preserves audit trail.

Scopes

OperationScope required
Read (GET)qna:read
Create / answer / transitionqna:write

Writes require Idempotency-Key.

Endpoints on this page

MethodEndpointDescription
GET/api/v1alpha1/deals/{dealId}/qnasFetch the bundled Q&A backlog for a deal — 2 directions × 3 states = 6 buckets. Details ↓
GET/api/v1alpha1/deals/{dealId}/qnas/meetings/{meetingId}Filter to Q&As arising from one specific meeting. Details ↓
GET/api/v1alpha1/qnas/{qnaId}Fetch one Q&A item. Details ↓
POST/api/v1alpha1/deals/{dealId}/qnasCreate a Q&A item (Discovery or RFI). Details ↓
POST/api/v1alpha1/qnas/{qnaId}/answerAttach an answer. Auto-transitions to Answered on high confidence. Details ↓
POST/api/v1alpha1/qnas/{qnaId}/transitions/{targetState}Explicit state transition. Details ↓

Get bundled backlog

GET /api/v1alpha1/deals/{dealId}/qnas

Fetch the full Q&A backlog, bucketed by direction and state. Matches the dashboard's Live View pane exactly.

Response

  • Six buckets — 2 directions × 3 states. Every bucket is always present.
  • count is the full server-side count. items[] is capped at 25 items per bucket in v1alpha1. Partners rendering "47 open items" show the count and let users drill into the list via GET /api/v1alpha1/qnas/{id} per item.

Each item follows the QnaItemmodel shape:

  • linkedInsight — present on Discovery Q&As. Points at the 8P field being asked about.
  • contact — present on RFI Q&As. { id, displayName } reference to the contact who asked.
  • occurrences[] — every time this Q&A was raised. The same question can arise in multiple meetings; each occurrence is captured.
  • occurrences[].source enum: cortex-agent | user | webhook.

Errors

  • 404 deal-not-found — id unknown or cross-org

bash
curl -s "https://altus.cirrusinsight.com/api/v1alpha1/deals/deal_01H8YKQ2N9RXVT/qnas" \
  -H "X-Cirrus-Api-Key: $TOKEN"
python
import requests

deal_id = "deal_01H8YKQ2N9RXVT"
response = requests.get(
    f"https://altus.cirrusinsight.com/api/v1alpha1/deals/{deal_id}/qnas",
    headers={"X-Cirrus-Api-Key": token},
)
response.raise_for_status()
backlog = response.json()
csharp
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}/qnas");
response.EnsureSuccessStatusCode();
var backlog = await response.Content.ReadAsStringAsync();
json
{
  "dealId": "deal_...",
  "discovery": {
    "open":      { "count": 3, "items": [ /* QnaItem shape */ ] },
    "answered":  { "count": 8, "items": [ /* ... */ ] },
    "dismissed": { "count": 1, "items": [ /* ... */ ] }
  },
  "rfi": {
    "open":      { "count": 2, "items": [ /* ... */ ] },
    "answered":  { "count": 5, "items": [ /* ... */ ] },
    "dismissed": { "count": 0, "items": [ /* ... */ ] }
  }
}
json
{
  "id": "qna_...",
  "direction": "discovery",
  "state": "open",
  "questionText": "What's the buyer's role tenure?",
  "answerText": null,
  "importance": "high",
  "linkedInsight": {
    "schemaType": "profile",
    "fieldKey": "tenure"
  },
  "contact": null,
  "occurrences": [
    {
      "meetingId": "mtg_...",
      "occurredAt": "2026-06-16T17:03:22Z",
      "source": "cortex-agent"
    }
  ],
  "createdAt": "2026-06-16T17:03:22Z",
  "updatedAt": "2026-06-16T17:03:22Z"
}

Get meeting slice

GET /api/v1alpha1/deals/{dealId}/qnas/meetings/{meetingId}

Filter the deal's Q&A backlog to items that arose from one specific meeting.

Useful when rendering a meeting-detail surface with "questions from this meeting" alongside the transcript.

Response

Same 6-bucket shape as GET /qnas, each items[] filtered to Q&As whose occurrences[] includes the given meetingId. Counts reflect the filtered set.

Errors

  • 404 deal-not-found
  • 404 meeting-not-found

Meeting doesn't need to be linked to the deal. Passing an unrelated (dealId, meetingId) returns empty buckets — the collection is scoped to the deal; the meeting filter is just a filter.


bash
curl -s "https://altus.cirrusinsight.com/api/v1alpha1/deals/deal_01H8YKQ2N9RXVT/qnas/meetings/mtg_01H9ABCDEF" \
  -H "X-Cirrus-Api-Key: $TOKEN"
python
import requests

deal_id = "deal_01H8YKQ2N9RXVT"
meeting_id = "mtg_01H9ABCDEF"
response = requests.get(
    f"https://altus.cirrusinsight.com/api/v1alpha1/deals/{deal_id}/qnas/meetings/{meeting_id}",
    headers={"X-Cirrus-Api-Key": token},
)
response.raise_for_status()
slice_ = response.json()
csharp
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 meetingId = "mtg_01H9ABCDEF";
var response = await client.GetAsync(
    $"https://altus.cirrusinsight.com/api/v1alpha1/deals/{dealId}/qnas/meetings/{meetingId}");
response.EnsureSuccessStatusCode();
var slice = await response.Content.ReadAsStringAsync();

Get one Q&A

GET /api/v1alpha1/qnas/{qnaId}

Fetch one Q&A item by its qna_... id.

Response

QnaItem shape as documented above.

Errors

  • 404 qna-item-not-found — id unknown or cross-org

bash
curl -s "https://altus.cirrusinsight.com/api/v1alpha1/qnas/qna_01H8YKR3P4STUV" \
  -H "X-Cirrus-Api-Key: $TOKEN"
python
import requests

qna_id = "qna_01H8YKR3P4STUV"
response = requests.get(
    f"https://altus.cirrusinsight.com/api/v1alpha1/qnas/{qna_id}",
    headers={"X-Cirrus-Api-Key": token},
)
response.raise_for_status()
qna = response.json()
csharp
using System.Net.Http;
using System.Net.Http.Headers;

var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Cirrus-Api-Key", token);

var qnaId = "qna_01H8YKR3P4STUV";
var response = await client.GetAsync(
    $"https://altus.cirrusinsight.com/api/v1alpha1/qnas/{qnaId}");
response.EnsureSuccessStatusCode();
var qna = await response.Content.ReadAsStringAsync();

Create a Q&A

POST /api/v1alpha1/deals/{dealId}/qnas

Create a Q&A. Two flavours based on direction:

Headers

HeaderDescription
Idempotency-KeyRequired.

Request body — Discovery

Request body — RFI

FieldRequiredNotes
directionyesdiscovery or rfi.
questionTextyes
importancenoDefault medium.
linkedInsightDiscovery only.schemaType + .fieldKey (must exist in the 8P schema).
contactIdRFI onlyMust belong to the calling org.
occurrencesyesAt least one entry. meetingId + occurredAt per entry.

Response

201 Created with the newly-created QnaItem.

Errors

  • 400 idempotency-key-required
  • 404 deal-not-found
  • 404 contact-not-found — RFI contactId unknown or cross-org
  • 404 meeting-not-found — occurrence meetingId unknown or cross-org
  • 422 qna-shape-invalid — missing linkedInsight on Discovery, or missing contactId on RFI
  • 422 insight-field-invalidlinkedInsight.fieldKey isn't in the schema

bash
curl -s -X POST "https://altus.cirrusinsight.com/api/v1alpha1/deals/deal_01H8YKQ2N9RXVT/qnas" \
  -H "X-Cirrus-Api-Key: $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "direction": "discovery",
    "questionText": "What'\''s the buyer'\''s role tenure?",
    "importance": "high",
    "linkedInsight": { "schemaType": "profile", "fieldKey": "tenure" },
    "occurrences": [
      { "meetingId": "mtg_01H9ABCDEF", "occurredAt": "2026-06-16T17:03:22Z" }
    ]
  }'
python
import requests
import uuid

deal_id = "deal_01H8YKQ2N9RXVT"
headers = {
    "X-Cirrus-Api-Key": token,
    "Idempotency-Key": str(uuid.uuid4()),
}
body = {
    "direction": "discovery",
    "questionText": "What's the buyer's role tenure?",
    "importance": "high",
    "linkedInsight": {"schemaType": "profile", "fieldKey": "tenure"},
    "occurrences": [
        {"meetingId": "mtg_01H9ABCDEF", "occurredAt": "2026-06-16T17:03:22Z"}
    ],
}
response = requests.post(
    f"https://altus.cirrusinsight.com/api/v1alpha1/deals/{deal_id}/qnas",
    headers=headers,
    json=body,
)
response.raise_for_status()
qna = response.json()
csharp
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
{
    direction = "discovery",
    questionText = "What's the buyer's role tenure?",
    importance = "high",
    linkedInsight = new { schemaType = "profile", fieldKey = "tenure" },
    occurrences = new[]
    {
        new { meetingId = "mtg_01H9ABCDEF", occurredAt = "2026-06-16T17:03:22Z" }
    }
};

var request = new HttpRequestMessage(
    HttpMethod.Post,
    $"https://altus.cirrusinsight.com/api/v1alpha1/deals/{dealId}/qnas")
{
    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 qna = await response.Content.ReadAsStringAsync();
json
{
  "direction": "discovery",
  "questionText": "What's the buyer's role tenure?",
  "importance": "high",
  "linkedInsight": {
    "schemaType": "profile",
    "fieldKey": "tenure"
  },
  "occurrences": [
    { "meetingId": "mtg_...", "occurredAt": "2026-06-16T17:03:22Z" }
  ]
}
json
{
  "direction": "rfi",
  "questionText": "Does Cirrus support SSO with Okta?",
  "importance": "high",
  "contactId": "cont_...",
  "occurrences": [
    { "meetingId": "mtg_...", "occurredAt": "2026-06-16T17:14:00Z" }
  ]
}

Attach an answer

POST /api/v1alpha1/qnas/{qnaId}/answer

Attach an answer. Auto-transitions the item to Answered when the answer's confidence ≥ 0.6. Otherwise the item stays Open with an answer draft attached.

Headers

HeaderDescription
Idempotency-KeyRequired.

Request body

FieldRequiredNotes
answerTextyes
source.typeyesmeeting / manual / grounding-doc / external.
source.meetingIdconditionalRequired when type: "meeting". chunkId optional.
source.documentIdconditionalRequired when type: "grounding-doc".
confidenceno0.0–1.0. Default: 1.0 for manual, 0.5 for grounding-doc / external, Cortex's own score for meeting-sourced.

Response

200 OK with the updated QnaItem. If confidence triggered auto-transition, state will be answered.

Errors

  • 400 idempotency-key-required
  • 404 qna-item-not-found
  • 409 qna-transition-invalid — item is dismissed. Dismissed items can't be answered without an explicit transition-back-to-open first.
  • 422 answer-source-invalid — missing meetingId on type: "meeting", or documentId on type: "grounding-doc"

bash
curl -s -X POST "https://altus.cirrusinsight.com/api/v1alpha1/qnas/qna_01H8YKR3P4STUV/answer" \
  -H "X-Cirrus-Api-Key: $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "answerText": "Sample Guest has been at Acme for 3 years, per LinkedIn.",
    "source": {
      "type": "meeting",
      "meetingId": "mtg_01H9ABCDEF",
      "chunkId": "chnk_01H9XYZ123"
    },
    "confidence": 0.85
  }'
python
import requests
import uuid

qna_id = "qna_01H8YKR3P4STUV"
headers = {
    "X-Cirrus-Api-Key": token,
    "Idempotency-Key": str(uuid.uuid4()),
}
body = {
    "answerText": "Sample Guest has been at Acme for 3 years, per LinkedIn.",
    "source": {
        "type": "meeting",
        "meetingId": "mtg_01H9ABCDEF",
        "chunkId": "chnk_01H9XYZ123",
    },
    "confidence": 0.85,
}
response = requests.post(
    f"https://altus.cirrusinsight.com/api/v1alpha1/qnas/{qna_id}/answer",
    headers=headers,
    json=body,
)
response.raise_for_status()
qna = response.json()
csharp
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 qnaId = "qna_01H8YKR3P4STUV";
var body = new
{
    answerText = "Sample Guest has been at Acme for 3 years, per LinkedIn.",
    source = new
    {
        type = "meeting",
        meetingId = "mtg_01H9ABCDEF",
        chunkId = "chnk_01H9XYZ123"
    },
    confidence = 0.85
};

var request = new HttpRequestMessage(
    HttpMethod.Post,
    $"https://altus.cirrusinsight.com/api/v1alpha1/qnas/{qnaId}/answer")
{
    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 qna = await response.Content.ReadAsStringAsync();
json
{
  "answerText": "Sample Guest has been at Acme for 3 years, per LinkedIn.",
  "source": {
    "type": "meeting",
    "meetingId": "mtg_...",
    "chunkId": "chnk_..."
  },
  "confidence": 0.85
}

Transition state

POST /api/v1alpha1/qnas/{qnaId}/transitions/{targetState}

Explicit state transition. Use when the answer-attach endpoint doesn't fit — dismissing without answering, reopening a dismissed item, forcing a transition regardless of confidence.

targetState is open, answered, or dismissed.

Headers

HeaderDescription
Idempotency-KeyRequired.

Request body (optional)

Response

200 OK with the updated QnaItem.

Allowed transitions

From→ allowed target states
openanswered, dismissed
answeredopen, dismissed
dismissedopen

Errors

  • 400 idempotency-key-required
  • 400 invalid-target-statetargetState not one of the three
  • 404 qna-item-not-found
  • 409 qna-transition-invalid — transition not allowed from current state. Response body includes currentState so racing callers can recover:
bash
curl -s -X POST "https://altus.cirrusinsight.com/api/v1alpha1/qnas/qna_01H8YKR3P4STUV/transitions/dismissed" \
  -H "X-Cirrus-Api-Key: $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "reason": "Answered externally; no longer relevant to Cirrus"
  }'
python
import requests
import uuid

qna_id = "qna_01H8YKR3P4STUV"
target_state = "dismissed"
headers = {
    "X-Cirrus-Api-Key": token,
    "Idempotency-Key": str(uuid.uuid4()),
}
body = {"reason": "Answered externally; no longer relevant to Cirrus"}
response = requests.post(
    f"https://altus.cirrusinsight.com/api/v1alpha1/qnas/{qna_id}/transitions/{target_state}",
    headers=headers,
    json=body,
)
response.raise_for_status()
qna = response.json()
csharp
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 qnaId = "qna_01H8YKR3P4STUV";
var targetState = "dismissed";
var body = new
{
    reason = "Answered externally; no longer relevant to Cirrus"
};

var request = new HttpRequestMessage(
    HttpMethod.Post,
    $"https://altus.cirrusinsight.com/api/v1alpha1/qnas/{qnaId}/transitions/{targetState}")
{
    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 qna = await response.Content.ReadAsStringAsync();
json
{
  "reason": "Answered externally; no longer relevant to Cirrus"
}
json
{
  "type": "https://docs.cirrusinsight.com/platform-api/errors/qna-transition-invalid",
  "title": "Q&A transition not allowed",
  "status": 409,
  "detail": "Cannot transition to 'dismissed' from current state 'dismissed'.",
  "currentState": "dismissed"
}

Raleigh, NC — a Cirruspath, Inc. company