> For the complete documentation index, see [llms.txt](https://docs.concurrence.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.concurrence.com/api-reference/change-logs/amigo-api/amigo-api-history-06.md).

# API History: Archive 6

Retained API history, archive 6, with original entry headings and links back to current integration guidance.

Historical entries retain their original release/source context. Use [Current Upgrade Guidance](/api-reference/change-logs/upgrade-guide.md) for current integration boundaries and [API](/api-reference/change-logs/amigo-api.md) for the recent history and archive index. An older entry does not establish current availability.

<details>

<summary>Platform API: Audit Export Downloads, Actor Email on Audit Entries, Permission Enforcement (July 2026)</summary>

#### Audit Export Downloads, Actor Email on Audit Entries, Permission Enforcement <a href="#audit-export-downloads-actor-email-on-audit-entries-permission-enforcement" id="audit-export-downloads-actor-email-on-audit-entries-permission-enforcement"></a>

Audit export artifacts can now be streamed through a dedicated download endpoint, audit log entries carry the actor's email, and several endpoints now enforce documented permission gates.

**What changed:**

* **New audit export download endpoint.** `GET /v1/{workspace_id}/audit/exports/download/{filename}` streams an audit export artifact (NDJSON) directly to the caller without buffering the full file in memory. The filename must be a bare `{export_id}.ndjson` name - path separators and traversal sequences are rejected with 422. Returns 404 if the artifact does not exist. Each download is audit-logged as a PHI access event. Requires `Audit.export` permission (admin or owner).
* **`download_url` values updated.** The `download_url` returned by the create-export and list-exports endpoints now returns a workspace-scoped proxy download path (`/{workspace_id}/audit/exports/download/{filename}`) instead of the previous path format.
* **`actor_email` field on identity audit log entries.** Each audit log entry now includes an `actor_email` field (string or null) resolved server-side from the actor entity's federation identities. The email identifies who performed the action. It is null when the actor has no email-bearing federation identity (for example, machine credentials). This field is never sourced from target or invited email metadata.
* **`last_modified` format on export list items.** The `last_modified` field on audit export list items is now returned as an ISO 8601 UTC string instead of a raw numeric timestamp.
* **Permission enforcement on audit endpoints.** The list-events, PHI access report, entity access log, and audit summary endpoints now enforce `Audit.view` permission (admin or owner). Previously these endpoints relied on role checks only.
* **Permission enforcement on workspace update and provision.** The update-workspace and provision-workspace endpoints now enforce `Workspace.update` permission (admin or owner). Previously update-workspace accepted any authenticated role.
* **Region is immutable on workspace update.** The update-workspace endpoint now rejects any non-null `region` value with 422. Workspaces are pinned to the region they were created in (data-residency guarantee) and cannot be migrated.
* **Permission enforcement on retention policy update.** The update-retention-policy endpoint now enforces `Workspace.update` permission (admin or owner).

**What you need to do:**

* **Use the new download endpoint for export artifacts.** If you previously constructed download URLs manually, update to use the `download_url` value returned by the create or list endpoints.
* **Handle `actor_email` on audit entries.** If you consume identity audit log entries, the new `actor_email` field is available for display. It is null for machine credentials.
* **Check role requirements.** If you have integrations using non-admin API keys to update workspaces, provision workspaces, update retention policies, or read audit data, ensure those keys have the required permissions (admin or owner).
* **Do not send `region` on workspace updates.** If your integration includes `region` in update-workspace requests, remove it or set it to null. Non-null values are now rejected.

</details>

<details>

<summary>Platform API: Turn Identity on Streaming Done Frame (July 2026)</summary>

#### Turn Identity on Streaming Done Frame <a href="#turn-identity-on-streaming-done-frame" id="turn-identity-on-streaming-done-frame"></a>

The SSE `done` event emitted at the end of a streaming conversation turn now carries the same stable turn identity fields as the non-streaming `POST /turns` response, so streaming clients can anchor durable per-turn artifacts (such as feedback or annotations) without issuing a follow-up read.

**What changed:**

* **New `turn_id` field on the `done` SSE frame.** The `done` event now includes a `turn_id` (UUID) that matches the `turn_id` on the non-streaming turn response and on conversation history turns. The identifier is deterministic - it is derived from the conversation and the exchange ordinal, so it is identical across streaming and non-streaming paths. Null when the conversation has no user exchange yet (a greeting kickoff on a fresh conversation).
* **New `turn_index` field on the `done` SSE frame.** The `done` event now includes a `turn_index` (integer, zero-based) indicating the ordinal of the user exchange (0 = first user turn). Null exactly when `turn_id` is null.
* **Durable turn counter.** The turn count that drives turn identity now survives session-store expiry. When a conversation is resumed after the session store has evicted its journal, the platform seeds the turn counter from the last persisted value rather than restarting at zero. This prevents duplicate turn identifiers from being issued on resumed conversations.

**What you need to do:**

* **No action required.** These are additive fields on the existing `done` SSE event. Existing streaming clients that do not read these fields are unaffected.
* **Streaming clients that need per-turn anchoring** can now read `turn_id` and `turn_index` directly from the `done` frame instead of issuing a separate conversation history read.

</details>

<details>

<summary>Platform API: Voice Model Family Rename - session_provider Taxonomy (July 2026)</summary>

#### Voice Model Family Rename <a href="#voice-model-family-rename" id="voice-model-family-rename"></a>

The `session_provider` field on voice configuration now uses model family names instead of internal runtime codenames. This is a breaking change to the accepted enum values.

**What changed:**

* **New family-based enum values.** The `session_provider` field now accepts three values: `amigo` (the default Amigo pipeline - STT, reasoning, TTS), `gpt_realtime` (real-time speech-to-speech), and `gpt_live` (full-duplex). These names describe model families.
* **Previous values removed from the API schema.** The former values `inhouse`, `openai_realtime`, and `atlas` are no longer part of the public API contract. The API schema advertises only the three new family names.
* **Server-side migration.** Existing stored configurations are automatically migrated to the new values. In-flight requests using the old values are normalized server-side during a transition period, so existing integrations will not break immediately - but clients should update to the new values.
* **Inheritance simplified.** The inheritance chain is now service to agent to environment default (workspace-level override has been removed from the inheritance path).

**Value mapping:**

| Previous value    | New value              |
| ----------------- | ---------------------- |
| `inhouse`         | `amigo`                |
| `openai_realtime` | `gpt_realtime`         |
| `atlas`           | `gpt_realtime`         |
| `gpt_live`        | `gpt_live` (unchanged) |

**What you need to do:**

* **Update any integrations that set `session_provider`.** Replace `inhouse` with `amigo`, and replace `openai_realtime` or `atlas` with `gpt_realtime`. The old values are accepted temporarily but will be removed in a future release.
* **Update any code that reads `session_provider` values.** Responses now return the new family names. If your code switches on `inhouse`, `openai_realtime`, or `atlas`, update those checks.

</details>

<details>

<summary>Platform API: Full-Duplex Voice Model Family (July 2026)</summary>

#### Full-Duplex Voice Model Family <a href="#full-duplex-voice-model-family" id="full-duplex-voice-model-family"></a>

A new voice model family option is available for the voice configuration `session_provider` field. The `gpt_live` family is a full-duplex model family that handles turn-taking natively - the model decides when to speak, listen, pause, interrupt, or backchannel continuously during the conversation, rather than relying on voice activity detection.

**What changed:**

* **New `gpt_live` session provider value.** The `session_provider` field on voice configuration now accepts `gpt_live` in addition to `amigo` and `gpt_realtime`. Full-duplex calls keep feature parity with other voice calls: context graph navigation, tool calls, turn history, and usage metering all work the same way.
* **Independent model configuration.** The full-duplex family is configured separately from the real-time speech-to-speech family, so enabling or testing one family never affects the other.
* **Native turn-taking.** Full-duplex models own their speak, listen, and tool decisions natively rather than relying on voice activity detection.
* **Graceful fallback.** Where the full-duplex family is not yet enabled, selecting `gpt_live` falls back to the Amigo pipeline rather than failing the call.

**What you need to do:**

* **No action required.** The family is disabled by default and no existing configurations are affected.
* **Do not configure `gpt_live` in production yet.** Calls configured with `gpt_live` fall back to the Amigo pipeline until the family is enabled for your environment.

</details>

<details>

<summary>Platform API: Stable Turn Identity on Conversation Turns (July 2026)</summary>

#### Stable Turn Identity on Conversation Turns <a href="#stable-turn-identity-on-conversation-turns" id="stable-turn-identity-on-conversation-turns"></a>

Conversation turn messages now carry a stable, deterministic identity that links each message to the user exchange it belongs to. The identity is consistent across `POST /turns` responses and conversation history reads, so clients can anchor durable per-turn artifacts (such as feedback or annotations) across page reloads and re-fetches.

**What changed:**

* **New `turn_id` field on conversation turn messages.** Each message in a conversation's turn list now includes a `turn_id` (UUID). The identifier is derived deterministically from the conversation and the exchange ordinal, so it is identical whether the message is returned from `POST /turns` or read back from the conversation history. User and agent messages from the same exchange share the same `turn_id`. The field is null on messages that precede the first user turn (proactive greetings, channel-event preludes).
* **New `turn_index` field on conversation turn messages.** Each message also includes a `turn_index` (integer, zero-based) indicating the ordinal of the user exchange it belongs to (0 = first user turn). Derived server-side and never stored. Null exactly when `turn_id` is null.
* **`turn_id` on `POST /turns` response updated.** The top-level `turn_id` on the turn response is now a deterministic UUID (previously a random opaque string). It matches the `turn_id` stamped on the returned messages and on the same conversation's history turns, so it can be used as a durable key for per-turn artifacts. Null only when the conversation has no user exchange yet (a poll or kickoff before the first user message).
* **Consistent identity across voice and text.** Both text and voice conversation histories derive turn identity using the same logic, so the identifiers are stable regardless of which read path serves the history.

**What you need to do:**

* **No action required for existing integrations.** The new fields are additive. Existing clients that do not read `turn_id` or `turn_index` are unaffected.
* **Adopt `turn_id` for durable per-turn references.** If you anchor feedback, annotations, or other artifacts to a conversation turn, switch from any client-generated identifier to the platform-provided `turn_id`. It is stable across responses and history re-reads.
* **Update `turn_id` parsing if needed.** If your integration previously parsed the top-level `turn_id` on `POST /turns` responses as an opaque string (e.g., `turn_abc123def456`), note that it is now a UUID (or null). Update any type assumptions accordingly.

</details>

<details>

<summary>Platform API: Text Conversation Lifecycle Hardening - Terminal Close, Force-New, and Rebind Recovery (July 2026)</summary>

#### Text Conversation Lifecycle Hardening <a href="#text-conversation-lifecycle-hardening" id="text-conversation-lifecycle-hardening"></a>

Text conversation lifecycle management on thread-keyed channels (SMS, iMessage) has been hardened with three capabilities: terminal close on completion, force-new reset on outbound creation, and automatic use-case rebind recovery.

**What changed:**

* **Terminal close on conversation completion.** When a text conversation's context graph reaches its terminal state (completed), the platform durably marks the conversation as completed. The next inbound message on the same provider thread starts a fresh conversation instead of resuming the finished one. Previously, a completed conversation could remain active, causing inbound messages to route to a dead engine. Conversations that end for other reasons (idle timeout, disconnect, error) remain active and resumable.
* **Force-new on outbound conversation creation.** The create-conversation endpoint now accepts a `force_new` boolean field for thread-keyed channels (SMS, iMessage). When set to `true`, the platform closes any existing active conversation on the target provider thread (recipient + use case) before dispatching the outbound opener, ensuring a brand-new conversation is materialized. The closed conversation emits a conversation-closed event and an audit entry. `force_new` is rejected with `422` on `channel=web` because web conversations always start fresh.
* **Use-case rebind recovery.** When a workspace reassigns a use case to a different service while a conversation is still active on a provider thread, inbound messages on that thread would previously fail because the active conversation was bound to the old service. The platform now detects the mismatch, retires the stale conversation, and re-resolves the thread to materialize a fresh conversation bound to the current service. Recovery is capped at one retry per inbound turn to prevent loops.

**What you need to do:**

* **No action required for terminal close or rebind recovery.** Both are automatic. Completed text conversations will now correctly free their provider thread for new conversations, and use-case rebinds will self-heal on the next inbound message.
* **Use `force_new` for outbound resets.** If you need to start a fresh outbound conversation on a thread that may have an existing active conversation, pass `force_new: true` in your create-conversation request. This is only valid for SMS and iMessage channels.
* **Update integrations that assume conversation permanence.** If your integration assumes a text conversation on a given thread is permanent and never replaced, be aware that completed conversations and force-new resets now close the prior conversation and start a new one.

</details>

<details>

<summary>Platform API: Normalized Checks View for Simulation Evaluation Results (July 2026)</summary>

#### Normalized Checks View for Simulation Evaluation Results <a href="#normalized-checks-view-for-simulation-evaluation-results" id="normalized-checks-view-for-simulation-evaluation-results"></a>

Simulation run responses and per-case assertion details now include a `checks` field - a normalized, read-only projection that flattens each metric and assertion verdict into a uniform shape for consistent rendering.

**What changed:**

* **New `checks` field on simulation run responses.** The response from the get-simulation-run endpoint now includes a `checks` array alongside the existing `eval_results`. Each element is a flat object with `source_type` (metric or assertion), `key`, `label`, `value_type`, `evaluation_method`, `expected`, `actual`, display-ready strings (`expected_display`, `actual_display`), `verdict`, optional `score` and `score_label`, `rationale`, `references`, and identifiers (`run_id`, `case_id`, `session_id`, `trace_id`).
* **New `checks` field on per-case assertion details.** The case assertion detail response now includes a `checks` array alongside the existing `results`, using the same normalized shape.
* **Raw results unchanged.** The existing `eval_results` and `results` fields are not modified. The `checks` array is a computed view that rides alongside them.
* **Consistent rendering.** The normalized shape eliminates the need for clients to determine where the useful value lives across different result shapes (metric thresholds, deterministic assertions, LLM judge verdicts). Every check renders with the same field set.
* **Value type and evaluation method are independent axes.** `value_type` describes the measured value (numeric, boolean, categorical, text, structured). `evaluation_method` describes how it is checked (threshold, equals, contains, regex, tool\_called, llm\_judge, custom). For LLM judge assertions, the primary actual value is the verdict, not the numeric score - the score appears on the `score` field as secondary data.

**What you need to do:**

* **No action required.** The `checks` field is additive. Existing integrations that read `eval_results` or `results` continue to work without changes.
* **Adopt `checks` for rendering.** If you render simulation check results in a custom UI, you can switch to reading the `checks` array for a simpler, uniform rendering path.

</details>

<details>

<summary>Platform API: Escalation Policy and Risk Signal Configuration Removed from Service API (July 2026)</summary>

#### Escalation Policy and Risk Signal Configuration Removed from Service API <a href="#escalation-policy-and-risk-signal-configuration-removed-from-service-api" id="escalation-policy-and-risk-signal-configuration-removed-from-service-api"></a>

The escalation policy, risk signal configuration, hard escalation rules, and safety filters toggle have been removed from the Service resource. These fields were part of an earlier escalation routing design that has been superseded by the agent's built-in safety evaluation and escalation reasoning.

**What changed:**

* **`escalation_policy` removed from Service create, update, and response.** The per-service escalation policy object (which mapped trigger sources to actions like operator handoff, call forwarding, or hangup) has been removed from the Service API. Services no longer carry per-trigger routing configuration for engine-detected escalations.
* **`risk_signal_config` removed from Service response.** The per-workspace risk scoring configuration (thresholds, weights, enabled flag) has been removed from the Service resource.
* **`hard_escalation_rules` removed from Service response.** The list of non-negotiable escalation rules (healthcare compliance rules with detection modes and intent patterns) has been removed from the Service resource.
* **`safety_filters_enabled` removed from Service create, update, and response.** The boolean safety filters toggle has been removed from the Service resource.
* **`escalation_config` removed from context graph action states.** The per-state escalation tuning override (topic risk score, auto-escalate threshold, max loop count, operator skill) has been removed from action state configuration.
* **`summarize` context strategy removed.** The `summarize` option for context management strategy has been removed. The supported strategies are now `full` (default) and `compact`. Existing configurations using `summarize` should be updated to use `compact`.
* **Active call intelligence response simplified.** The `current_risk_score` and `risk_trend` fields have been removed from the active call intelligence response.
* **Context window management simplified.** The context window warning threshold (which previously triggered an intermediate summarize step) has been removed. The engine now transitions directly from full context to compact mode when token usage approaches the escalation threshold.

**What you need to do:**

* **Remove `escalation_policy` from Service create and update requests.** If you set escalation policies on services through the API, remove the field from your request bodies. The field is no longer accepted.
* **Remove `safety_filters_enabled` from Service create and update requests.** If you set this field, remove it. Safety evaluation is now handled by the agent's core reasoning.
* **Update context strategy references.** If you configure context strategies and use `summarize`, change to `compact`. The `summarize` strategy is no longer supported.
* **Update integrations reading removed fields.** If your integration reads `escalation_policy`, `risk_signal_config`, `hard_escalation_rules`, `safety_filters_enabled`, `current_risk_score`, or `risk_trend` from API responses, update your code to handle their absence.
* **No change to escalation behavior.** The agent continues to evaluate safety concerns and trigger escalations as part of its core reasoning loop. Operator escalation remains available through the standard operator join flow.

</details>

<details>

<summary>Platform API: Per-Turn Prompt Log Emission for Voice Calls (July 2026)</summary>

#### Per-Turn Prompt Log Emission for Voice Calls <a href="#per-turn-prompt-log-emission-for-voice-calls" id="per-turn-prompt-log-emission-for-voice-calls"></a>

Voice calls now emit prompt logs incrementally after each conversation turn instead of batching all logs at call teardown. This improves log durability and makes prompt logs available for inspection while a call is still in progress.

**What changed:**

* **Incremental prompt log emission.** After each voice conversation turn, the platform emits any new prompt logs generated during that turn. Previously, all prompt logs for a call were batched and emitted only during call teardown.
* **Improved durability.** If a call ends unexpectedly (for example, due to infrastructure failure), at most the current turn's prompt log is lost rather than the entire call's log history. Logs from all prior completed turns have already been emitted and are safely persisted.
* **Live queryability.** Prompt logs are now available from the prompt-logs API while the call is still active, matching the behavior of usage metering which already emits per-turn. Operators and debugging tools can inspect prompt data without waiting for the call to end.
* **Teardown flush unchanged.** The existing teardown flush at call end continues to run, pushing any remaining logs from the final turn and session summary. The teardown flush is aware of what has already been emitted and only sends logs that have not yet been delivered.
* **Voice only.** This change applies to voice calls. Text and simulation channels retain their existing emission mechanisms.

**What you need to do:**

* **No action required.** Per-turn prompt log emission is automatic for all voice calls. There are no new API endpoints, request fields, or response fields. Prompt logs appear through the same API surface as before - they are simply available sooner during a call.
* **Review monitoring dashboards.** If you monitor prompt log delivery, you may notice logs arriving throughout a call rather than in a single batch at the end. This is expected behavior.

</details>

<details>

<summary>Platform API: LLM Token Metering for Conversation Navigation and Production-Call Evaluation (July 2026)</summary>

#### LLM Token Metering for Conversation Navigation and Production-Call Evaluation <a href="#llm-token-metering-for-conversation-navigation-and-production-call-evaluation" id="llm-token-metering-for-conversation-navigation-and-production-call-evaluation"></a>

Two previously unmetered LLM call sites now emit token usage as billing events: the conversation navigation step on text and simulation turns, and the AI judge and AI-query metric calls made during production call evaluation.

**What changed:**

* **Navigation tokens metered on text and simulation turns.** Each text (SMS, iMessage, web chat) and simulation turn runs a navigation LLM call that decides how the conversation moves through the context graph. These tokens were previously computed but never billed. They are now emitted as usage events per turn, under the navigation model rather than the response model. Voice calls already metered navigation tokens and are unchanged.
* **Production-eval judge and AI-query tokens metered.** Running evaluations against a completed production call invokes an AI judge for LLM-judge assertions and an AI model for AI-query metrics. These calls now emit token usage events, matching simulation evaluations, which already metered both.
* **Metering never affects results.** A metering failure never affects the turn or the evaluation outcome.
* **No API surface changes.** There are no new endpoints, request fields, or response fields. This update adds billing-side metering so these tokens are counted in usage reporting.

**What you need to do:**

* **No action required.** Metering is automatic.
* **Review usage reports.** If you track LLM token consumption, note that navigation tokens on text/simulation turns and production-eval judge tokens - previously invisible in billing - will now be included in your usage totals.

</details>

<details>

<summary>Platform API: LLM Token Metering for Framework Agent Runs (July 2026)</summary>

#### LLM Token Metering for Framework Agent Runs <a href="#llm-token-metering-for-framework-agent-runs" id="llm-token-metering-for-framework-agent-runs"></a>

Framework agent runs now emit per-run LLM token usage as a billing event at run completion. This means inference consumed by framework runs (partner agent frameworks dispatched through the platform) appears in standard usage reporting alongside all other platform LLM usage.

**What changed:**

* **Per-run token billing.** When a framework agent run completes successfully, the platform emits a billing event containing the run's total input tokens, output tokens, and cached tokens, along with the model used. These tokens now appear in the same usage meters as every other platform inference call site.
* **Metering never affects run status.** A metering failure never affects the run's terminal status - the run still succeeds or fails based on its own outcome.
* **No API surface changes.** There are no new endpoints, request fields, or response fields. The token usage already reported in the run response (input, output, and cached token counts) is unchanged. This update adds billing-side metering so those tokens are counted for usage reporting.

**What you need to do:**

* **No action required.** Token metering is automatic. Framework agent run usage will appear in your workspace's usage reports.
* **Review usage reports.** If you track LLM token consumption for cost management, note that framework agent run tokens - previously invisible in billing - will now be included in your usage totals.

</details>

<details>

<summary>Platform API: Audio Verification Removed - Reduced Per-Turn Voice Latency (July 2026)</summary>

#### Audio Verification Removed - Reduced Per-Turn Voice Latency <a href="#audio-verification-removed-reduced-per-turn-voice-latency" id="audio-verification-removed-reduced-per-turn-voice-latency"></a>

The per-turn audio verification step that sent conversation audio to a secondary model for speech-to-text correction has been removed. This eliminates a blocking delay on every voice conversation turn, significantly reducing end-to-end response latency.

**What changed:**

* **Audio verification removed from the voice pipeline.** The platform previously ran an optional audio verification step on each voice turn that analyzed raw conversation audio to detect and correct speech-to-text errors (misspelled names, misheard phone numbers, etc.). This step added a blocking delay to every turn. It has been removed entirely.
* **Voice settings: `correction_categories` field removed.** The `correction_categories` field has been removed from the voice settings API request and response models. This field previously provided domain hints (e.g., "medication names", "insurance carriers") to the audio verification model. Since audio verification no longer exists, the field is no longer accepted or returned.
* **No change to other voice settings.** All other voice configuration fields (keyterms, pronunciation dictionaries, sensitive topics, post-call intelligence toggles, language, speed, volume, etc.) are unaffected.

**What you need to do:**

* **Remove `correction_categories` from API calls.** If you set `correction_categories` in voice settings update requests, remove the field. The API no longer accepts it.
* **Update response parsing.** If you read `correction_categories` from voice settings GET responses, remove that field from your response models. It is no longer returned.
* **No action needed for voice quality.** The keyterms feature (which boosts specific words in real-time speech recognition) remains available and is the recommended approach for improving transcription accuracy for domain-specific vocabulary.

</details>

<details>

<summary>Platform API: Returning-User Entry State for Known Callers (July 2026)</summary>

#### Returning-User Entry State for Known Callers <a href="#returning-user-entry-state-for-known-callers" id="returning-user-entry-state-for-known-callers"></a>

When the platform resolves a sole-match caller at session open, it now checks whether that person has a prior completed conversation. If they do, the session starts in a returning-user entry state and the agent greets the caller with awareness of the prior interaction rather than treating every call as a first contact.

**What changed:**

* **Returning-user detection at session open.** When the platform resolves a single known entity for the caller, it looks up that entity's most recent completed conversation. If one exists, the session is flagged as returning-user and the agent uses the returning-user entry state defined in the context graph.
* **Conversation recency in caller context.** A natural-language recency line (e.g., "Last conversation: 12 minutes ago" or "Last conversation: 3 days ago") is appended to the caller context block. This line persists across context refreshes during the session so the greeting always has content to anchor on.
* **Sole-match only.** The returning-user signal is derived only when a single entity is resolved. Ambiguous lookups (multiple matches) and household-level resolutions stay on the new-user path - no "welcome back" promise is made without loaded entity knowledge.
* **Operator sessions reset to new-user.** When the resolved entity is an external principal (operator or clinician), the returning-user flag is reset to new-user. The operator is not a subject of care, so their prior conversation history should not drive a clinical welcome-back greeting.
* **Graceful degradation.** If the conversation history lookup fails, the session silently falls back to the new-user entry state. The recency signal is a best-effort enhancement that never blocks session creation.
* **New telemetry.** Sessions where returning-user derivation runs now emit a metric indicating whether the caller was classified as returning or new, giving visibility into returning-user distribution.

**What you need to do:**

* **No action required for most integrations.** The returning-user entry state is derived automatically from existing conversation history and context graph configuration. If your context graph defines a `returning_user_initial_state`, callers with prior conversations will now use it.
* **Review your context graph entry states.** If you have not authored a returning-user entry state in your context graph, the default new-user entry state continues to apply for all sessions. To take advantage of personalized returning-user greetings, define a returning-user initial state in your context graph.

</details>

<details>

<summary>Platform API: Legacy Clinical Event Review Surface Retired (July 2026)</summary>

#### Legacy Clinical Event Review Surface Retired <a href="#legacy-clinical-event-review-surface-retired" id="legacy-clinical-event-review-surface-retired"></a>

The legacy clinical event review pipeline has been retired. This surface provided model-based and human review of flagged clinical events from voice agent sessions. Workspaces enrolled in the private preview can use External Write Review to approve or reject external write proposals before delivery to target systems.

**What changed:**

* **Review queue API endpoints removed.** The `/v1/{workspace_id}/review-queue` endpoint group (list, detail, approve, reject, correct, claim, unclaim, batch approve, batch reject, stats, dashboard, trends, performance, history, my-queue, correction schema, and diff) has been removed from the Platform API.
* **Review queue data surface removed.** Review queue records are no longer available through the generic data API.
* **Pipeline dashboard review loop field.** The `review_loop` field in pipeline status responses is now always null. The field is preserved for wire compatibility but carries no data.
* **Pipeline review metrics.** The `GET /pipeline/review` endpoint now returns zeroed metrics (queue depth 0, no pending items, no approval or rejection counts). The endpoint remains available for compatibility but reflects no active data.
* **Command center data quality.** The command center data quality panel no longer reports review queue depth or approval rate from the retired surface. These fields return zero or null values.
* **Entity intelligence.** The entity intelligence provenance response no longer includes review history from the retired surface. The `review_history` field is preserved but always returns an empty list.

**What you need to do:**

* **Update any integrations using the review queue endpoints.** Remove dependencies on the retired `/v1/{workspace_id}/review-queue` endpoints. If your workspace is enrolled in the private preview, use `/v1/{workspace_id}/external-write-proposals` for human review of external writes.
* **Update dashboard integrations.** If you consume the `review_loop` field from pipeline status or entity intelligence `review_history`, these fields now return null or empty values. Remove any UI or logic that depends on them.
* **External Write Review remains private preview.** Do not treat the proposal-review endpoints as generally available unless your workspace is enrolled.

</details>

<details>

<summary>Platform API: External Write Proposal Review API - Private Preview (July 2026)</summary>

#### External Write Proposal Review API <a href="#external-write-proposal-review-api" id="external-write-proposal-review-api"></a>

Private-preview workspaces can list, inspect, approve, and reject external write proposals through workspace-scoped REST endpoints.

**What changed:**

* **List proposals.** `GET /v1/{workspace_id}/external-write-proposals` returns a paginated, newest-first list of external write proposals. Supports optional `status` filtering (`proposed`, `approved`, `rejected`, `pushing`, `pushed`, `failed`, `superseded`) and includes a total count for building paged UIs.
* **Get proposal detail.** `GET /v1/{workspace_id}/external-write-proposals/{proposal_id}` returns a single proposal by ID.
* **Approve a proposal.** `POST /v1/{workspace_id}/external-write-proposals/{proposal_id}/approve` records an approval decision. Only proposals in `proposed` status can be approved.
* **Reject a proposal.** `POST /v1/{workspace_id}/external-write-proposals/{proposal_id}/reject` records a rejection decision with a required reason (1-1000 characters). Only proposals in `proposed` status can be rejected.
* **Server-derived reviewer identity.** The reviewer's identity is derived from the authenticated session - never accepted from the request body. Callers without an authenticated user identity (e.g., legacy API keys with no bound person) receive a `403` response.
* **Concurrency-safe decisions.** If two reviewers attempt to decide the same proposal simultaneously, only one succeeds. The other receives a `409 Conflict` response.
* **Audit logging.** Every decision is audit-logged with the connector type, resource type, and reviewer identity. The proposed payload (which may contain PHI) is never included in audit logs.
* **Permission-gated.** Listing and viewing require `ReviewQueue.view`. Approving and rejecting require `ReviewQueue.review`.

**What you need to do:**

* **Confirm preview enrollment before integrating.** These endpoints are not generally available.
* **For enrolled workspaces:** Use the list endpoint to display pending proposals and the approve or reject endpoints to record decisions. See the [Review Queue developer guide](https://docs.concurrence.com/developer-guide/platform-api/integrations/review-queue) for the request and response contracts.

</details>

<details>

<summary>Platform API: External Write Proposal Delivery - Private Preview (July 2026)</summary>

#### External Write Proposal Delivery <a href="#external-write-proposal-delivery" id="external-write-proposal-delivery"></a>

Private-preview workspaces can deliver human-approved external write proposals with retry behavior based on the destination's idempotency characteristics.

**What changed:**

* **Asynchronous delivery for approved proposals.** When a reviewer approves a proposal, the platform attempts delivery to the configured external system and records the result on the proposal.
* **Idempotency-aware delivery guarantees.** FHIR sinks with PUT-by-id semantics receive at-least-once delivery - transient failures and mid-delivery crashes are safely retried. Non-idempotent sinks (booking, cancellation, patient creation) receive at most one automatic delivery attempt. If a failure occurs on a non-idempotent sink, the proposal is recorded for human re-drive rather than retried, preventing double-writes.
* **Bypasses automatic sync safety nets.** The delivery engine does not apply confidence thresholds, source allowlists, or entity type filters when dispatching approved proposals. A human approval is a stronger authority than unattended egress heuristics.
* **Proposal lifecycle tracking.** Each proposal tracks its lifecycle (proposed, approved, pushing, pushed, failed, rejected) with attempt counts, reviewer identity, decision timestamps, and delivery outcomes.

**What you need to do:**

* **Confirm preview enrollment and destination semantics.** Before approving proposals, verify whether the destination supports idempotent retries and define a human re-drive procedure for failed non-idempotent writes.

</details>

<details>

<summary>Platform API: Self-Serve Custom Memory Dimensions via Enrichment Key Tags (July 2026)</summary>

#### Self-Serve Custom Memory Dimensions via Enrichment Key Tags <a href="#self-serve-custom-memory-dimensions-via-enrichment-key-tags" id="self-serve-custom-memory-dimensions-via-enrichment-key-tags"></a>

Workspaces can now opt enrichment keys into the memory extraction pipeline by tagging them through the enrichment-keys API, without requiring engineer intervention. A new `tags` field on enrichment keys controls routing into platform subsystems.

**What changed:**

* **New `tags` field on enrichment keys.** The create and patch endpoints for enrichment keys now accept an optional `tags` array. The response model for all enrichment key endpoints (create, list, get, patch) includes the `tags` field.
* **`memory_extract` routing tag.** Tagging a key with `"memory_extract"` opts it into the memory extraction pipeline as a workspace-custom memory dimension. The conversation extractor will infer this key from transcripts alongside the system-default memory dimensions.
* **Validation on tagging.** The API validates that a key tagged with `"memory_extract"` meets the extraction pipeline's requirements: the key must belong to a `person` entity type, use a valid snake\_case identifier, have a non-empty `description` (which serves as the extraction target definition for the LLM), use `value_type` of `"string"` (the extractor emits free-text narratives), and must not shadow a system-default memory dimension. Keys that fail these checks receive a `400` response with a specific error message.
* **Patch behavior.** Setting `tags` on a patch replaces the full tags list. Pass `[]` to remove all tags. Validation runs against the post-update state, so adding the `memory_extract` tag in the same patch that sets a description is supported. The update is rolled back if validation fails.
* **Consolidation cadence.** The memory consolidation pipeline now runs on a more frequent cadence. Most runs are no-ops - total processing volume stays proportional to conversations per day. A failure backoff mechanism prevents a single problematic entity from consuming repeated processing attempts.

**What you need to do:**

* **To add a custom memory dimension:** Create or update an enrichment key with `entity_type` set to `"person"`, `value_type` set to `"string"`, a non-empty `description` that defines what the extractor should look for, and `tags` set to `["memory_extract"]`. The key will be picked up by the extraction pipeline automatically.
* **No action required for existing integrations.** The `tags` field defaults to an empty array. Existing enrichment keys are unaffected.

</details>

<details>

<summary>Platform API: Real-Time Live Voice Overlay on Runs List and Summary (July 2026)</summary>

#### Real-Time Live Voice Overlay on Runs List and Summary <a href="#real-time-live-voice-overlay-on-runs-list-and-summary" id="real-time-live-voice-overlay-on-runs-list-and-summary"></a>

The unified runs list and summary endpoints now include active voice calls in real time, so live voice runs appear with `running` status while the call is in progress rather than only after it ends.

**What changed:**

* **Live voice runs in the list.** `GET /runs` now includes synthetic `running` conversation runs for voice calls that are currently active. These entries appear alongside database-sourced runs and are sorted, filtered, and paginated consistently. When a call ends and its terminal database record is written, the synthetic entry is automatically replaced by the authoritative record.
* **Live voice count in the summary.** `GET /runs/summary` now includes active voice calls in the `running` count and the `conversation` kind count, so the summary reflects calls in progress in real time.
* **Deduplication.** If a call has both a live entry and a completed database record (for example, during the brief overlap after a call ends), the database record takes precedence and the live entry is dropped. No duplicate runs appear in the list.
* **Best-effort overlay.** The live voice data source is consulted on a best-effort basis. If it is unavailable or slow, the endpoints return database-only results without error. The overlay never causes a request to fail.
* **Filter compatibility.** Live voice runs respect the existing `kind`, `channel`, and `status` filters. They appear only when the query includes conversation runs, the voice channel (or no channel filter), and the `running` status (or no status filter, or the virtual `live` filter).

**What you need to do:**

* **No action required for existing integrations.** The list and summary endpoints return the same shape as before. Live voice entries use the same run contract and field set as other conversation runs.
* **For dashboards showing live run counts:** The `running` count in the summary response now accurately reflects voice calls in progress. If you previously supplemented the runs summary with a separate active-calls query to get real-time voice counts, you can remove that workaround.

</details>

<details>

<summary>Platform API: Unified Runs Summary Endpoint (July 2026)</summary>

#### Unified Runs Summary Endpoint <a href="#unified-runs-summary-endpoint" id="unified-runs-summary-endpoint"></a>

A new endpoint returns aggregate run counts across the workspace's unified run surface (framework + conversation runs), providing the data needed for the Operations Runs page summary strip without requiring clients to page through the full run list.

**What changed:**

* **New endpoint: `GET /runs/summary`.** Returns total run count, live count (running + paused), per-status breakdown (running, paused, completed, failed, timed\_out), a full `by_status` map, and a `by_kind` map (framework vs conversation).
* **Optional `kind` filter.** Pass `framework` or `conversation` to scope the summary to one run source. Omit to include both.
* **Optional `channel` filter.** Pass a conversation channel (`voice`, `text`, `sms`, `email`, `web`) to scope the summary to conversation runs on that channel. When set, framework runs are excluded from the counts since framework runs do not carry a channel.
* **`by_status` field for forward compatibility.** The response includes a `by_status` object that carries every status value present in the data, so new run lifecycle states surface automatically without requiring a schema change.
* **`by_kind` field.** Shows the total count split between `framework` and `conversation` sources.

**What you need to do:**

* **No action required for existing integrations.** This is a new read-only endpoint. The existing list endpoint is unchanged.
* **To display run summaries:** Call `GET /runs/summary` (optionally with `kind` and/or `channel`) and use the returned counts to populate summary cards or dashboard strips. The `by_status` object provides the full breakdown if you need to display statuses beyond the named convenience fields.

</details>

<details>

<summary>Platform API: Simulation Runs Summary Endpoint (July 2026)</summary>

#### Simulation Runs Summary Endpoint <a href="#simulation-runs-summary-endpoint" id="simulation-runs-summary-endpoint"></a>

A new endpoint returns aggregate counts across a workspace's simulation runs, providing the data needed for the Operations Runs page summary strip without requiring clients to page through the full run list.

**What changed:**

* **New endpoint: `GET /simulations/runs/summary`.** Returns total run count, per-status breakdown (running, completed, failed, plus any additional statuses), total sessions, total turns, and the most recent run creation timestamp.
* **Optional `service_id` filter.** Pass a service ID to scope the summary to runs for a specific service. This mirrors the list endpoint's service filter so the summary stays consistent with a filtered run list.
* **`by_status` field for forward compatibility.** The response includes a `by_status` object that carries every status value present in the data, so new run lifecycle states surface automatically without requiring a schema change.
* **`last_created_at` field.** ISO 8601 timestamp of the most recently created run, or `null` when no runs match.

**What you need to do:**

* **No action required for existing integrations.** This is a new read-only endpoint.
* **To display run summaries:** Call `GET /simulations/runs/summary` (optionally with `service_id`) and use the returned counts to populate summary cards or dashboard strips. The `by_status` object provides the full breakdown if you need to display statuses beyond the three named convenience fields.

**Permissions:** Requires service view permission.

</details>

<details>

<summary>Platform API: Per-Request Wait-for-Final and Filler Suppression for Background Tools (July 2026)</summary>

#### Per-Request Wait-for-Final and Filler Suppression for Background Tools <a href="#per-request-wait-for-final-and-filler-suppression-for-background-tools" id="per-request-wait-for-final-and-filler-suppression-for-background-tools"></a>

The text conversation turn endpoint now accepts two optional per-request flags - `wait_for_final` and `suppress_filler` - that give synchronous and batch callers explicit control over how background tool results are delivered.

**What changed:**

* **New optional field: `wait_for_final`.** When set to `true` on a message turn, the platform holds the request open and waits for the background tool to finish (bounded to approximately 30 seconds) instead of returning the acknowledgement immediately. If the tool completes within the budget, the response carries the final answer with `background_pending: false`. If the wait times out, the response returns `background_pending: true` with the conversation ID so the caller can resolve it later with `poll: true`. Default is `null`, which inherits the channel policy (web/sync text = do not wait).
* **New optional field: `suppress_filler`.** When set to `true` on a message turn that ends with background work pending, the filler/acknowledgement text is omitted from the response output. The caller receives an empty output list with `background_pending: true` as the unambiguous poll-later signal, preventing batch callers from mistaking the acknowledgement for the real answer. Default is `null`, which inherits the channel policy.
* **Validation with `poll: true`.** Both flags are rejected (422) when combined with `poll: true`. Polling is itself the drain-and-report primitive, so turn-control flags are meaningless on a poll request.
* **Existing behavior unchanged.** When both flags are `null` (the default), every existing integration path behaves identically to before - no wait, no suppression.

**What you need to do:**

* **No action required for existing integrations.** The defaults preserve current behavior.
* **For batch or synchronous callers:** Consider setting `wait_for_final: true` to receive background tool results inline without a separate poll cycle. If the tool takes longer than the budget, fall back to `poll: true` as before.
* **For callers that parse agent output programmatically:** Consider setting `suppress_filler: true` to ensure that filler acknowledgement text is never returned when a background tool is pending. Use `background_pending: true` as the authoritative signal to poll later.
* **Remove any `wait_for_final` or `suppress_filler` from poll requests.** If you set these flags on a `poll: true` request, the endpoint returns 422.

</details>

<details>

<summary>Platform API: JWT Bearer Integration Auth - RFC 7523 Flow Selection (July 2026)</summary>

#### JWT Bearer Integration Auth - RFC 7523 Flow Selection <a href="#jwt-bearer-integration-auth-rfc-7523-flow-selection" id="jwt-bearer-integration-auth-rfc-7523-flow-selection"></a>

The `oauth2_jwt_bearer` integration auth type now requires an `assertion_usage` field that selects which RFC 7523 flow to use when requesting an access token. This replaces the previous behavior where all JWT bearer integrations used the authorization grant flow (§ 2.1) unconditionally.

**What changed:**

* **New required field: `assertion_usage`.** When creating or updating an integration with `oauth2_jwt_bearer` auth, you must now specify `assertion_usage` as one of two values:
  * `authorization_grant` (§ 2.1) - sends the JWT as `assertion` under `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer`. This is the flow previously used by all JWT bearer integrations.
  * `client_authentication` (§ 2.2) - sends the JWT as `client_assertion` with `client_assertion_type` under `grant_type=client_credentials`. This is the `private_key_jwt` method required by SMART Backend Services and similar providers.
* **Existing integrations backfilled.** All existing `oauth2_jwt_bearer` integrations have been backfilled with `assertion_usage: authorization_grant`, preserving their current behavior. No action is needed for existing integrations unless you want to switch to the `client_authentication` flow.
* **Breaking change for new integrations.** The `assertion_usage` field is required on all new `oauth2_jwt_bearer` integration configurations. Requests that omit this field will be rejected.

**What you need to do:**

* **For existing integrations:** No action required. Existing integrations continue to use the authorization grant flow as before.
* **For new integrations:** Include `assertion_usage` in your `oauth2_jwt_bearer` auth configuration. Choose `authorization_grant` for the standard JWT bearer grant flow, or `client_authentication` for providers that require the `private_key_jwt` client credentials flow.
* **For SMART Backend Services / Epic integrations:** Use `assertion_usage: "client_authentication"` to send the JWT as a client assertion under the client credentials grant, which is the flow these providers require.

</details>

<details>

<summary>Platform API: Server-Side Operator Identity Enforcement (July 2026)</summary>

#### Server-Side Operator Identity Enforcement <a href="#server-side-operator-identity-enforcement" id="server-side-operator-identity-enforcement"></a>

All operator action endpoints now enforce that the authenticated caller's identity matches the operator profile they are acting as. This closes a security gap where any user with the admin role could perform operator actions (join call, send guidance, switch mode, leave call, wrap up, get access token) attributed to a different operator.

**What changed:**

* **Identity binding on all operator actions.** The platform now verifies that the caller's authenticated identity corresponds to the target `operator_id` on every operator action request. The check matches the caller's verified user identity against the operator profile. If the identities do not match, the request is rejected with `403 Forbidden`.
* **No admin bypass.** Even users with admin or owner roles cannot act as a different operator. The identity enforcement applies uniformly regardless of role - acting as another operator is the vulnerability this change addresses.
* **API key callers without user identity are rejected.** Legacy API keys that do not carry an associated user identity cannot perform operator actions. Only callers with a verified user session are accepted.
* **Affected endpoints.** Join call, switch mode, leave call, get access token, send guidance, and wrap up endpoints for operators all enforce this check.

**What you need to do:**

* **Ensure operator profiles have correct email addresses.** The platform matches the caller's verified email against the operator profile's email. If an operator's profile does not have the same email as their sign-in account, they will be unable to perform operator actions. Verify operator email addresses in the Operators configuration.
* **Update any automation that acts as a different operator.** If you have scripts or integrations that use one user's credentials to perform actions as a different operator, those will now be rejected. Each operator must authenticate with their own credentials.
* **No API contract changes.** Request and response schemas are unchanged. The only behavioral change is stricter authorization - requests that would previously succeed when the caller and operator identities did not match will now return `403`.

</details>

<details>

<summary>Platform API: Event-Driven Delivery for Background Tools on Non-Live Channels (July 2026)</summary>

#### Event-Driven Delivery for Background Tools on Non-Live Channels <a href="#event-driven-delivery-for-background-tools-on-non-live-channels" id="event-driven-delivery-for-background-tools-on-non-live-channels"></a>

Non-live channels - email, SMS, iMessage, and WhatsApp - now suppress interim filler replies when the agent dispatches a background tool, and automatically deliver the real answer once the tool completes. Previously, these channels could send a placeholder acknowledgment followed by the actual answer as a separate message, which was confusing for recipients who receive messages asynchronously.

**What changed:**

* **Filler suppression on non-live channels.** When a background tool is dispatched during a conversation on email, SMS, iMessage, or WhatsApp, the platform buffers the agent's interim reply instead of sending it immediately. If the turn completes without pending background work, the reply is sent normally. If the turn ends with a background tool still running, the buffered filler is discarded.
* **Automatic re-drive on tool completion.** When a background tool finishes on a non-live channel, the platform automatically re-drives the session so the agent can incorporate the tool results and deliver the real answer. The recipient receives a single message with the final answer rather than a placeholder followed by a correction.
* **Live channels unchanged.** Phone and web chat conversations are unaffected. These channels maintain a persistent connection, so the existing behavior of sending an interim acknowledgment and then delivering the real answer on the same session continues to work as before.
* **No configuration required.** The platform determines the appropriate delivery behavior based on the channel type. No API changes, new parameters, or workspace configuration are needed.
* **Rolling deployment safe.** The platform uses structural separation to ensure mixed-version deployments cannot mis-deliver messages during upgrades. No operator coordination is required.

**What you need to do:**

* **No action required.** This change applies automatically to all non-live channel conversations. Recipients on email, SMS, iMessage, and WhatsApp will receive a single definitive reply instead of an interim placeholder when background tools are involved.
* **Review any workflows that depend on interim replies.** If you have downstream systems that monitor for interim acknowledgment messages on non-live channels, those messages will no longer be sent when a background tool is pending.

</details>

<details>

<summary>Platform API: Identity Binding Test Values in Simulation Sessions (July 2026)</summary>

#### Identity Binding Test Values in Simulation Sessions <a href="#identity-binding-test-values-in-simulation-sessions" id="identity-binding-test-values-in-simulation-sessions"></a>

Simulation sessions now support identity binding test value resolution for `external_user.subject_key` bindings on integrations. This lets simulations exercise integrations that require an external user identity without needing a verified external user session.

**What changed:**

* **Simulation sessions resolve test values.** When a simulation session is created by a caller with integration view permissions, the session automatically enables identity binding test value resolution. Integration auth bindings that carry author-configured test values for `external_user.subject_key` will use those test values during the simulation, mirroring the behavior already available for workspace-authenticated text conversation turns.
* **Server-derived, not caller-supplied.** The test value resolution decision is made server-side based on the caller's role permissions. Callers with integration view permissions get test value resolution automatically. This applies to simulation session creation, test conversation creation, simulation case runs, and benchmark runs.
* **Propagated across session lifecycle.** The setting is stored with the session metadata and propagated to forked sessions, so branching scenarios within a simulation inherit the same identity binding behavior as the parent session.
* **No change to production paths.** Test values remain inert on voice calls, production external-user sessions, and any path where a verified external subject key is present. The gating rules are unchanged - this change only adds simulation sessions as an additional gated path.

**What you need to do:**

* **No action required.** If you use simulations to test integrations with identity bindings, those integrations will now resolve test values automatically when your role has integration view permissions. No API changes or new parameters are needed for the standard simulation endpoints.
* **Direct agent engine session creation.** If you create simulation sessions directly against the agent engine (not through the Platform API simulation endpoints), a new optional `allow_identity_binding_test_values` boolean field is available on the session creation request. The Platform API simulation endpoints set this automatically.

</details>

<details>

<summary>Platform API: Eager Post-Conversation Evaluation for All Channels (July 2026)</summary>

#### Eager Post-Conversation Evaluation for All Channels <a href="#eager-post-conversation-evaluation-for-all-channels" id="eager-post-conversation-evaluation-for-all-channels"></a>

Eager post-conversation evaluation now applies to all conversation channels - text sessions, SMS, email, and web - in addition to voice calls. Previously, the eager evaluation trigger only fired at the end of voice calls. Now every completed conversation, regardless of channel, is automatically evaluated against the workspace's active eval definitions when the feature is enabled.

**What changed:**

* **All channels trigger evaluation.** When eager evaluation is enabled for a workspace, completing a text session, SMS conversation, email conversation, or web conversation triggers the same automatic evaluation that previously only applied to voice calls. The evaluation uses the conversation's durable identifier to resolve the completed conversation.
* **Same evaluation definitions and scoring.** Text-channel evaluations use the same eval definitions, scoring pipeline, and daily cost cap as voice evaluations. Results are directly comparable across channels.
* **Same opt-in model.** No additional configuration is required. Workspaces that already have active eval definitions and the eager evaluation flag enabled will automatically begin evaluating text-channel conversations.
* **Best-effort and non-blocking.** The evaluation trigger is best-effort and does not affect conversation teardown. If a conversation cannot be resolved for evaluation (for example, if it has no durable conversation identifier), the trigger is skipped and the daily budget is not consumed.

**What you need to do:**

* **No action required for existing voice-only workspaces.** If eager evaluation is already enabled, text-channel conversations will begin being evaluated automatically. Review your daily evaluation cap if you expect a significant increase in evaluated conversation volume.
* **Review evaluation definitions.** Evaluation definitions originally written for voice conversations may need adjustments to score text-channel conversations appropriately. Both modes produce comparable results, but dimension-specific criteria (such as audio quality or hold time) may not apply to text channels.

</details>

<details>

<summary>Platform API: Triggerable Event Type Validation (July 2026)</summary>

#### Triggerable Event Type Validation <a href="#triggerable-event-type-validation" id="triggerable-event-type-validation"></a>

Trigger definitions now validate the `event_type` field against a closed set of supported platform events at write time. Previously, any string was accepted, which meant a typoed event name would silently create a trigger that never matched any incoming event.

**What changed:**

* **Write-time validation on create and update.** When you create or update a trigger, the `event_type` field must be one of the supported platform event types. Unsupported or misspelled values are rejected with a validation error.
* **Supported event types.** The supported set covers appointment lifecycle events (`appointment.booked`, `appointment.cancelled`, `appointment.confirmed`), booking requests, call intelligence and outcome events, channel events (email bounced/clicked/complained/delayed/delivered/opened/received/rejected, message received, voicemail status), conversation events (channel switched, started, turn recorded), coverage creation, entity enrichment and resolution, intake file receipt, medication refill requests, outbound initiation and scheduling, patient creation and updates, relationship establishment, review actions (approve, correct, reject), surface creation and submission, ticket creation, triage completion, trigger lifecycle events (completed, failed, fired), and the cron schedule event.
* **Existing triggers are unaffected.** Triggers already stored with event types outside the supported set continue to exist and fire as before. The validation applies only to new writes through the API.

**What you need to do:**

* **Review trigger creation workflows.** If you create triggers programmatically, ensure the `event_type` value matches one of the supported event types. Requests with unsupported values will now receive a validation error.
* **No changes needed for existing triggers.** Previously created triggers are not affected by this validation.

</details>

<details>

<summary>Platform API: Trigger Run Observability Metrics (July 2026)</summary>

#### Trigger Run Observability Metrics <a href="#trigger-run-observability-metrics" id="trigger-run-observability-metrics"></a>

Trigger action execution now emits granular observability metrics and structured log events, giving operators visibility into each step of a trigger run.

**What changed:**

* **Per-round timing.** Each action execution round emits a timing metric capturing how long the model call took, tagged by trigger name and model. Operators can use this to identify slow rounds and model-level latency trends.
* **Per-tool timing and status.** Every tool invocation within an action emits a timing metric with the tool name and outcome status (success, error, or timeout). This lets operators pinpoint which tools contribute to run duration and which tools are failing.
* **Structured log events.** Action execution now emits structured log events at each stage - round start (with tool count and model), model response (with tool use count), tool call start, and tool completion (with status and duration). These events provide a full trace of the execution path for debugging and audit.
* **Exhausted-run monitoring.** Runs that permanently fail after exhausting their retry budget now contribute to a dedicated operational metric for alerting.
* **Additional dispatch metrics.** The dispatcher now emits claim latency and queue age metrics through additional metric names, improving compatibility with monitoring dashboards that expect standardized metric naming.

**What you need to do:**

* **No action required.** These are additive observability improvements. No API contracts, trigger behavior, or scheduling semantics have changed. If you operate monitoring dashboards, new metric names are available for per-round timing, per-tool timing, dead-run counts, claim latency, and queue age.

</details>

<details>

<summary>Platform API: Durable Trigger Run Pipeline (July 2026)</summary>

#### Durable Trigger Run Pipeline <a href="#durable-trigger-run-pipeline" id="durable-trigger-run-pipeline"></a>

Trigger fires - both cron-scheduled and manual - now route through the durable run pipeline instead of executing actions inline. This gives every fire automatic retries, dead-lettering, and full run-lifecycle observability.

**What changed:**

* **Cron fires produce durable runs.** When the cron scheduler detects a due trigger, it emits a fired event, advances the next fire time, and inserts a durable run record. Action execution is handled by the durable dispatcher rather than running inline in the scheduler. This decouples schedule advancement from action execution, so a slow or failing action no longer delays other triggers.
* **Manual fires produce durable runs.** Manually firing a trigger now follows the same path - a fired event is emitted and a durable run is enqueued. The response still returns the fired event identifier immediately.
* **Retry budgets from action configuration.** The durable run's maximum attempt count is derived from the backing action's configuration. Deterministic actions (such as outbound task scheduling) can opt into bounded retry budgets, while agentic actions default to at-most-once execution.
* **Consistent queue contract.** All fire sources - cron, manual, and future sources such as webhooks and platform events - use the same durable run queue. The dispatcher claims, executes, and records terminal status for runs regardless of how they were enqueued.
* **Duplicate prevention.** A per-trigger distributed mutex prevents duplicate cron enqueue across platform replicas. The existing idempotency guard at the action-execution layer continues to prevent double-execution when a run is retried or redelivered.

**What you need to do:**

* **No action required.** This is an internal execution change. Trigger behavior, scheduling semantics, and API contracts are unchanged. Fires that previously executed inline now execute through the durable pipeline with the same at-most-once or bounded-retry semantics.
* **Observability improvement.** Every fire now produces a run record visible through the trigger runs surface, including fires that previously executed and completed without a durable trace.

</details>

<details>

<summary>Platform API: SMS Pre-Send Content-Compliance Check (July 2026)</summary>

#### SMS Pre-Send Content-Compliance Check <a href="#sms-pre-send-content-compliance-check" id="sms-pre-send-content-compliance-check"></a>

Outbound SMS messages sent through US A2P 10DLC or US/CA toll-free sender pools now undergo an automated content-compliance check before delivery. The check verifies that the message body is consistent with every governing registration (A2P campaign and toll-free verifications) associated with the sender pool.

**What changed:**

* **Pre-send content verification.** When an outbound SMS carries a text body and the sender pool includes US A2P or US/CA toll-free numbers, the platform verifies the message content against all governing registrations before sending. The check considers the registered use-case categories, description, message samples, and declared content properties (age-gating, embedded links, embedded phone numbers, direct lending) for each registration.
* **Clear rejection on mismatch.** If the message content does not match the registered use case, the send is rejected with HTTP 422 and a reason explaining which registration the content is inconsistent with. This lets callers fix the message before reattempting.
* **Fail-closed design.** If the compliance check cannot produce a verdict (for example, due to a transient error or an unparseable result), the send is blocked rather than allowed through. There is no kill switch - content compliance is always enforced for eligible sends.
* **Media-only sends are unaffected.** Messages with only media attachments and no text body skip the content-compliance check, since there is no textual content to verify.
* **Opt-in gate runs first.** The recorded-consent (opt-in) check runs before the content-compliance check, so a missing opt-in is still a cheap rejection that does not trigger the compliance check.
* **Updated 422 response description.** The 422 error response documentation now includes "message content does not match the registered use case" as a possible rejection reason.

**What you need to do:**

* **No action required for compliant messages.** If your outbound SMS content matches your registered A2P campaign or toll-free verification use case, no changes are needed.
* **Review rejection responses.** If you receive a 422 with a content-mismatch reason, review the message body against your registered use case and adjust the content accordingly.
* **New environment variable.** Deployments that self-host the channel manager service need to provide an additional API key environment variable for the compliance check. Contact your Amigo representative for configuration details.

</details>

<details>

<summary>Platform API: Eager Post-Call Production Evaluation (July 2026)</summary>

#### Eager Post-Call Production Evaluation <a href="#eager-post-call-production-evaluation" id="eager-post-call-production-evaluation"></a>

Completed voice calls can now be evaluated automatically against a workspace's active evaluation definitions immediately after the call ends, without requiring an explicit API call.

**What changed:**

* **Automatic post-call evaluation.** When enabled for a workspace, the platform triggers evaluation of each completed call as soon as teardown finishes. The evaluation runs the same definitions and produces the same verdicts as the existing on-demand evaluation endpoint, so results are directly comparable.
* **Per-workspace daily cap.** A configurable daily cap limits the number of eager evaluations a single workspace can trigger per UTC day. This bounds evaluation spend as a cost guardrail. The cap resets automatically at the UTC day boundary. Set the cap to zero to disable the limit entirely.
* **Slot refund on resolution miss.** The daily cap slot is reserved before evaluation begins. If the call cannot be resolved (for example, because data has not yet been committed), the slot is refunded so transient timing issues do not permanently consume the workspace's daily budget.
* **Dark-launched.** The eager trigger is gated behind a per-workspace feature flag and is off by default. Workspaces opt in by having active evaluation definitions - no additional configuration is required beyond defining what to evaluate and enabling the flag.
* **Dedicated fault isolation.** The eager evaluation path uses its own fault isolation boundary, separate from the paths used during live calls. Evaluation failures or slowdowns cannot affect in-call behavior.
* **Call resolution by durable key.** The eager path resolves the completed call using the call's durable telephony identifier, so evaluation works correctly even when the conversation identifier is not known at teardown time.

**What you need to do:**

* **No action required.** The feature is dark-launched and off by default. To enable eager post-call evaluation for a workspace, contact your Amigo representative to enable the feature flag. Once enabled, any workspace with active evaluation definitions will begin receiving automatic post-call evaluations.
* **Existing on-demand evaluation is unchanged.** The existing endpoint for triggering evaluation on a specific call continues to work as before.

</details>

<details>

<summary>Platform API: Run List Enrichment - Entity Name and Service Name (July 2026)</summary>

#### Run List Enrichment - Entity Name and Service Name <a href="#run-list-enrichment-entity-name-and-service-name" id="run-list-enrichment-entity-name-and-service-name"></a>

Conversation runs returned by the unified runs endpoint now include resolved entity and service names so list views can display human-readable labels without fetching each entity or service separately.

**What changed:**

* **New `entity_name` field.** The run object now includes `entity_name`, which contains the display name of the entity associated with the run. The name is resolved from the workspace's entity data as a best-effort batch lookup on the built page.
* **New `service_name` field.** The run object now includes `service_name`, which contains the name of the service associated with the run. The name is resolved from the workspace's service configuration as a best-effort batch lookup on the built page.
* **Best-effort, all optional.** Both fields are populated on a best-effort basis. Framework runs and conversation runs without an associated entity or service return null for these fields. If name resolution is temporarily unavailable, the runs are returned without names rather than failing the request.
* **No change to existing fields.** All previously available fields (`caller_id`, `phone_number`, `direction`, `turn_count`, `completion_reason`) remain unchanged.

**What you need to do:**

* **No action required.** These are additive, optional fields. Existing integrations that consume the runs list will continue to work without changes. To display entity and service names in your UI, read the new fields from the run object in the response.

</details>

<details>

<summary>Platform API: Trigger Run Idempotency Guard (July 2026)</summary>

#### Trigger Run Idempotency Guard <a href="#trigger-run-idempotency-guard" id="trigger-run-idempotency-guard"></a>

Trigger action execution now includes an idempotency guard that prevents duplicate runs from re-executing an action that already succeeded for the same fired event.

**What changed:**

* **Idempotency check before execution.** Before executing a trigger action, the platform checks whether a previous run for the same fired event has already succeeded. If so, the action is skipped and the run is marked as a duplicate skip. This prevents a retry from repeating an action whose successful result was already recorded.
* **Fail-closed on check failure.** If the idempotency check itself fails (for example, due to a transient data access issue), the run is marked as failed rather than proceeding without the safety guard. This ensures actions are never executed without the duplicate protection in place.
* **Per-action retry budgets.** Deterministic actions (such as outbound task scheduling) can specify a bounded retry budget, allowing safe redelivery up to a configurable maximum. Agentic actions (LLM-backed) default to at-most-once execution to avoid unpredictable side effects from repeated runs.
* **Observability.** New metrics track idempotency skips and check failures per trigger, so operators can monitor duplicate suppression and diagnose guard failures.

**What you need to do:**

* **No action required.** The idempotency guard is applied automatically to all trigger action executions. Existing trigger configurations continue to work as before. No API changes, no new parameters, and no client-side changes needed.

</details>

<details>

<summary>Platform API: Durable Trigger Run Processing (July 2026)</summary>

#### Durable Trigger Run Processing <a href="#durable-trigger-run-processing" id="durable-trigger-run-processing"></a>

Trigger firings now create durable run records that progress asynchronously through action execution and bounded retries to a terminal status.

**What changed:**

* **Durable run processing.** Trigger firings create run records that progress asynchronously to a terminal `succeeded` or `failed` result. Transient failures are eligible for retry up to the configured maximum attempts.
* **Automatic recovery.** Interrupted runs with attempts remaining are made eligible for another try. Runs that exhaust all attempts receive a terminal failure state.
* **Observable outcome.** Clients can inspect the run's attempt count, timestamps, terminal status, and failure detail through the trigger-run API instead of inferring completion from the initial fire response.

**What you need to do:**

* **No action required.** Trigger runs process asynchronously. Existing trigger configurations, schedules, and webhook-fired triggers continue to work as before. Clients that need completion should read the run status rather than treat the initial fire response as a delivery receipt.

</details>

<details>

<summary>Platform API: Run List Enrichment - Caller, Direction, Turns, and Outcome (July 2026)</summary>

#### Run List Enrichment - Caller, Direction, Turns, and Outcome <a href="#run-list-enrichment-caller-direction-turns-and-outcome" id="run-list-enrichment-caller-direction-turns-and-outcome"></a>

Conversation runs returned by the unified runs endpoint now include descriptive enrichment fields so list views can display caller identity, direction, turn count, and outcome without fetching each run's detail.

**What changed:**

* **New optional fields on conversation runs.** The run object returned by `GET /v1/{workspace_id}/runs` now includes five additional fields for conversation runs: `caller_id` (resolved caller identity), `phone_number` (raw contact number), `direction` (inbound or outbound), `turn_count` (number of conversational turns), and `completion_reason` (how the conversation ended).
* **Best-effort, all optional.** These fields are populated on a best-effort basis from the originating channel. Framework runs leave all five fields null. Conversation runs may also leave individual fields null when the source channel does not provide the data.
* **Free-form strings, not strict enums.** `direction` and `completion_reason` are free-form strings rather than restricted enums. This means an unexpected value from a producer never causes a run to be dropped from the list - resilience over strictness.
* **Caller and contact kept separate.** `caller_id` (resolved caller) and `phone_number` (raw contact field) are returned as separate fields, not merged, so consumers can distinguish a resolved identity from a fallback value.

**What you need to do:**

* **No action required.** These are additive, optional fields. Existing integrations that consume the runs list will continue to work without changes. To display the new data, read the new fields from the run object in the response.

</details>

<details>

<summary>Platform API: Unified Runs List Endpoint (July 2026)</summary>

#### Unified Runs List Endpoint <a href="#unified-runs-list-endpoint" id="unified-runs-list-endpoint"></a>

A new endpoint federates framework runs and conversation runs into a single paginated list, giving a merged, channel-inclusive view of all run activity in a workspace.

**What changed:**

* **New endpoint: `GET /v1/{workspace_id}/runs`.** Returns a paginated, newest-first list of runs that merges framework runs (partner agent frameworks) and conversation runs (voice, text, SMS, email, web) at read time behind a single run contract. Each run carries a deterministic identifier, canonical status, kind, channel, and source references.
* **Filtering by kind, channel, and status.** Filter by `kind` (`framework` or `conversation`) to see only one source. Filter by `channel` (`voice`, `text`, `sms`, `email`, `web`) to narrow conversation runs by channel - setting a channel filter automatically excludes framework runs, which carry no channel. Filter by `status` using canonical values (`running`, `paused`, `completed`, `failed`, `timed_out`) or the virtual `live` value, which expands to running + paused.
* **Sort control.** The `sort_by` parameter accepts `+started_at` or `-started_at` to control ordering. Default is newest first (`-started_at`). Only `started_at` is supported as a sort field because it is the timestamp carried on the run wire model, which allows the cross-source merge to reproduce each source's ordering exactly.
* **Opaque continuation-token pagination.** The response includes `has_more` and an opaque `continuation_token` for fetching subsequent pages. Page size is controlled by `limit` (1-200, default 50).
* **Canonical status mapping.** Each run's status is normalized to a canonical value regardless of source. Voice conversation runs derive status from their completion reason. Non-voice conversation runs derive status from their conversation state. Framework runs carry their native status directly.
* **Agent-runs endpoint unchanged.** The existing agent-runs endpoint continues to serve as the framework-only proxy surface. The new `/runs` endpoint is the merged view.

**What you need to do:**

* **No action required for existing integrations.** The agent-runs endpoint is unchanged. Adopt the new `/runs` endpoint when you want a unified view across framework and conversation runs.
* **To use the new endpoint:** send a `GET` request to `/v1/{workspace_id}/runs` with a workspace API key or operator identity token. Use query parameters to filter and paginate.

</details>

<details>

<summary>Platform API: Terminal Lifecycle Marker for Failed and Timed-Out Framework Runs (July 2026)</summary>

#### Terminal Lifecycle Marker for Failed and Timed-Out Framework Runs <a href="#terminal-lifecycle-marker-for-failed-and-timed-out-framework-runs" id="terminal-lifecycle-marker-for-failed-and-timed-out-framework-runs"></a>

Framework agent runs that end with a failure or timeout now emit a terminal lifecycle marker, making them visible in the durable run projection and historical run listings.

**What changed:**

* **Non-success runs now appear in durable run listings.** Previously, runs that failed or timed out produced no completion record. Because the durable run projection derives its view from completion steps, these runs were invisible in historical queries and the Framework Runs table. A terminal lifecycle marker is now emitted for any run that ends in a failed or timed-out state.
* **Best-effort telemetry.** The terminal marker is emitted on a best-effort basis. If the emit fails, the failure is logged and the run's terminal status is unaffected - a run that failed still reports as failed, and a run that timed out still reports as timed out.
* **No duplicate markers for successful runs.** Successful runs already emit a completion step as part of their trajectory. The terminal marker is only emitted for non-success runs, so there is no duplicate completion for runs that succeed.

**What you need to do:**

* **No action required.** Failed and timed-out runs will now appear automatically in the Framework Runs table and the durable run list endpoint. No API changes, no new parameters, and no configuration needed.

</details>

<details>

<summary>Platform API: Tenant Isolation for Session Event Injection (July 2026)</summary>

#### Tenant Isolation for Session Event Injection <a href="#tenant-isolation-for-session-event-injection" id="tenant-isolation-for-session-event-injection"></a>

Session event injection (used by operator guidance and external event endpoints) now enforces workspace ownership, preventing cross-workspace injection.

**What changed:**

* **Workspace ownership check on injection.** When injecting an event into an active voice session, the platform now verifies that the caller's workspace matches the workspace that owns the call. If the workspaces do not match, the request is rejected with a `403 Forbidden` response.
* **Closes cross-workspace injection vector.** Previously, a valid session identifier was sufficient to inject events into any active call. An operator in workspace A could potentially inject guidance or external events into workspace B's calls. The workspace ownership check closes this vector.
* **Workspace field added to injection payload.** The injection request now includes a workspace identifier. This field is populated automatically by the platform when forwarding injection requests - no changes are required by API consumers calling the operator guidance or session event endpoints.
* **Observability for rejected attempts.** Cross-workspace injection attempts are logged for security observability. No sensitive content is included in the log entry.

**What you need to do:**

* **No action required for most integrations.** If you use the operator guidance endpoint or the session event injection endpoint through the Platform API, the workspace context is handled automatically. Your existing calls will continue to work as long as the operator and the call belong to the same workspace (which is the expected case).
* **If you operate multiple workspaces:** be aware that injection requests targeting calls in a different workspace will now receive a `403 Forbidden` response instead of succeeding.

</details>

<details>

<summary>Platform API: Provider Access Grant API with Role-Based Scopes (July 2026)</summary>

#### Provider Access Grant API with Role-Based Scopes <a href="#provider-access-grant-api-with-role-based-scopes" id="provider-access-grant-api-with-role-based-scopes"></a>

Provider access grants now support role-based scope resolution and a full lifecycle management API, enabling workspace administrators to create, list, and revoke provider grants with fine-grained control over scribe access.

**What changed:**

* **Grant roles.** Each provider access grant now carries a role - either `provider` or `scribe_admin`. The role determines which scope set the grant conveys: provider grants receive the base scribe provider scopes, while scribe admin grants receive the extended administrative scope set (which includes cross-provider session visibility, access management, impersonation, and record deletion).
* **Create provider grant.** A new endpoint creates a workspace-scoped provider access grant. The request specifies the target workspace, email, role, whether MFA is required, and optionally a provider entity binding. The grant status is determined automatically: grants without a provider entity start as `pending_entity`, grants with an unverified email start as `pending_verification`, and fully provisioned grants start as `active`. Duplicate active grants for the same email or entity are rejected with a `409 Conflict` response.
* **List provider grants.** A new endpoint lists provider grants in a workspace with optional status filtering. Results are ordered by grant creation time (newest first).
* **Revoke provider grant.** A new endpoint revokes a provider grant and automatically invalidates all sessions and refresh tokens bound to that grant. The response includes counts of sessions and refresh tokens revoked, providing full visibility into the downstream impact of the revocation.
* **Role-aware token minting and refresh.** Access tokens minted from provider grants now carry the scope set corresponding to the grant's role. Token refresh operations validate that the requested scopes match the grant's role-derived scopes - attempts to alter scopes during refresh are rejected.
* **Audit logging.** Grant creation, creation failures (duplicates and integrity errors), and revocation are all audit-logged with grant metadata including role, scopes, and downstream revocation counts.

**What you need to do:**

* **No action required for existing provider grants.** Existing grants default to the `provider` role and continue to receive the same scribe provider scopes as before.
* **To create scribe admin grants:** specify `role: "scribe_admin"` when creating a provider access grant. The grant will convey the administrative scope set.
* **To manage provider grants programmatically:** use the new create, list, and revoke endpoints through the internal grants API surface. All endpoints require workspace admin credentials and the `identity:admin` scope.

</details>

<details>

<summary>Platform API: Scribe Provider Scopes (July 2026)</summary>

#### Scribe Provider Scopes <a href="#scribe-provider-scopes" id="scribe-provider-scopes"></a>

The identity system now defines dedicated scope sets for scribe provider sessions and scribe administration, enabling fine-grained access control for clinical scribe workflows.

**What changed:**

* **Provider scribe scopes.** A new set of scopes grants providers access to scribe session recording, note authoring (read and write on own notes), and reading their own encounters and appointments. These scopes are purpose-built for provider-facing scribe sessions.
* **Scribe admin scopes.** An administrative scope set extends provider scopes with cross-provider session visibility, access management, impersonation, and record deletion. These scopes are intended for scribe platform administrators.
* **Scribe scopes excluded from role expansion.** Scribe scopes are never granted through standard workspace roles (viewer, member, admin, owner). They are available only through explicit scribe-specific session flows, preventing unintended privilege escalation through role inheritance.
* **New `provider_scribe_sessions:create` scope.** A dedicated session-creation scope for provider scribe sessions. This scope is non-delegatable, consistent with other session-creation scopes.
* **Provider scope restrictions updated.** Scribe admin scopes and the provider scribe session creation scope are excluded from provider token grants, ensuring providers receive only their own scribe scopes and not administrative capabilities.

**What you need to do:**

* **No action required for existing integrations.** Standard workspace roles and existing session flows are unchanged. Scribe scopes only appear when explicitly requested through scribe session flows.
* **To use scribe provider sessions:** request scribe provider scopes when minting provider scribe sessions. Your session credentials must carry the `provider_scribe_sessions:create` scope.
* **To administer scribe access:** use credentials that carry scribe admin scopes for cross-provider session management, access control, and record deletion.

</details>

<details>

<summary>Platform API: Managed Integrations (July 2026)</summary>

#### Managed Integrations <a href="#managed-integrations" id="managed-integrations"></a>

Integrations can now be marked as system-managed, protecting them from modification or deletion through normal workspace API keys. Only principals carrying the platform admin scope can create, update, or delete managed integrations and their endpoints.

**What changed:**

* **New `managed` field on integrations.** A boolean field (default `false`) on the integration resource indicates whether the integration is system-managed. The field appears in create, update, list, and get responses.
* **Create protection.** Setting `managed: true` when creating an integration requires platform admin scope. Requests without the required scope receive a `403 Forbidden` response.
* **Update protection.** Updating any field on a managed integration requires platform admin scope. Changing the `managed` flag itself also requires platform admin scope. Requests without the required scope receive a `409 Conflict` response.
* **Delete protection.** Deleting a managed integration requires platform admin scope. Requests without the required scope receive a `409 Conflict` response.
* **Endpoint protection.** Creating, updating, or deleting endpoints on a managed integration requires platform admin scope. Requests without the required scope receive a `409 Conflict` response.
* **Audit logging.** Blocked mutation attempts against managed integrations are audit-logged with the action, resource, and reason.

**What you need to do:**

* **No action required for existing integrations.** All existing integrations default to `managed: false` and behave exactly as before.
* **To create a managed integration:** pass `managed: true` in the create request body. Your credentials must carry platform admin scope.
* **To modify or delete a managed integration:** ensure your credentials carry platform admin scope.
* **To check whether an integration is managed:** inspect the `managed` field in the integration list or detail response.

</details>

<details>

<summary>Platform API: Agent Runs - Durable List Endpoint (July 2026)</summary>

#### Agent Runs - Durable List Endpoint <a href="#agent-runs-durable-list-endpoint" id="agent-runs-durable-list-endpoint"></a>

A new list endpoint on the Agent Runs surface returns a paginated, filterable list of framework agent runs for a workspace. Run data is sourced from a durable read model, so runs are available for querying beyond the lifetime of a single session.

**What changed:**

* **New `GET /v1/{workspace_id}/agent-runs` endpoint.** Returns a paginated list of framework agent runs for the workspace, ordered newest first. Each item includes the run ID, framework, status, origin source, entity ID, token usage (input and output), step count, duration, start time, and creation time.
* **Filtering.** Optional `framework` and `status` query parameters let you narrow results. Accepted framework values are `claude-agent-sdk` and `openai-agents`. Accepted status values are `running`, `succeeded`, `failed`, and `timed_out`. Invalid filter values return a 422 validation error.
* **Pagination.** The endpoint accepts `limit` (1-200, default 50) and an opaque `continuation_token` for cursor-based pagination. The response includes `has_more` and a `continuation_token` for fetching the next page. Pass the returned token as `continuation_token` on the next request to advance.
* **Response shape.** The response body contains `items` (array of run summaries), `has_more` (boolean), and `continuation_token` (opaque, present only when `has_more` is true).
* **Durable read model.** Run data is sourced from a durable projection rather than live session state, so historical runs remain queryable after runtime restarts or session expiry.
* **Read-rate-limited.** The endpoint is gated by the standard read rate limit.

**What you need to do:**

* **To list framework runs:** call `GET /v1/{workspace_id}/agent-runs` with your workspace API key or operator identity token. Use the optional `framework` and `status` query parameters to filter, and `continuation_token` to paginate through large result sets.
* **Existing agent run endpoints are unchanged.** Dispatch, polling, result retrieval, and the harness context endpoint work exactly as before.

This framework-only list endpoint has since been retired. For current run listing and filtering, see the [Unified Runs](https://docs.concurrence.com/developer-guide/platform-api/conversations/runs) developer guide.

</details>

<details>

<summary>Platform API: Voice Conversations - Per-Turn Transcript Recovery (July 2026)</summary>

#### Voice Conversations - Per-Turn Transcript Recovery <a href="#voice-conversations-per-turn-transcript-recovery" id="voice-conversations-per-turn-transcript-recovery"></a>

Voice conversation detail now recovers per-turn transcripts from a durable analytical projection when the live session cache has expired. Previously, if the real-time session data was no longer available, voice conversation detail fell back to a single concatenated transcript displayed as one system message - losing the turn-by-turn structure. With this change, the platform reads per-turn voice data from a dedicated projection, restoring ordered user and agent turns with full metadata.

**What changed:**

* **Per-turn voice transcript recovery.** When the live session cache no longer holds turn data for a voice call, the platform now reads from a dedicated per-turn analytical projection before falling back to the single-transcript display. This restores the turn-by-turn conversation structure including user transcripts, agent transcripts, agent actions, state information, and state transitions.
* **`include_tool_calls` parameter support for voice detail.** The `GET` conversation detail endpoint now passes the `include_tool_calls` query parameter through to the voice detail path. When `include_tool_calls=true`, recovered voice turns include tool call data on agent turns. Tool calls are omitted by default to keep the response payload small.
* **Graceful degradation preserved.** If the analytical projection is unavailable or the read fails, the platform falls back to the previous behavior (single concatenated transcript). No existing behavior is removed.

**What you need to do:**

* **No action required.** Per-turn transcript recovery is automatic for all voice conversations. The conversation detail endpoint returns richer turn data without any changes to your integration.
* **To include tool calls in recovered voice turns:** pass `include_tool_calls=true` on the conversation detail request. This parameter was already supported for text conversations and now applies to voice conversations as well.

</details>

<details>

<summary>Platform API: Agent Runs - Harness Context Endpoint (July 2026)</summary>

#### Agent Runs - Harness Context Endpoint <a href="#agent-runs-harness-context-endpoint" id="agent-runs-harness-context-endpoint"></a>

A new read endpoint on the Agent Runs surface returns the neutral session-bootstrap context for a service - the same projection the hosted runner renders from - so a customer's own framework can bootstrap a session against the same world model.

**What changed:**

* **New `GET /v1/{workspace_id}/agent-runs/harness-context` endpoint.** Returns the harness context for a given service, including agent identity, reference instructions, world scope, tool descriptors, guardrails, and the server-enforced write floor.
* **Query parameters.** Accepts `service_id` (required, UUID) and `version_set` (optional, defaults to `release`, max 255 characters).
* **PHI-free projection.** The response carries no scoped entities or rendered caller prose. It is safe for external framework bootstrapping.
* **Byte-identical to hosted render.** The context is produced through the same resolution and projection path used by the hosted runner, so a remote fetch and a hosted session see the same world model.
* **Read-rate-limited.** The endpoint is gated by the standard read rate limit.

**What you need to do:**

* **To bootstrap your own framework session:** call `GET /v1/{workspace_id}/agent-runs/harness-context?service_id={id}` with your workspace API key or operator identity token. Use the returned context to configure your framework's session with the same identity, tools, guardrails, and world scope the platform provides.
* **Existing agent run endpoints are unchanged.** Dispatch, polling, and result retrieval work exactly as before.

For details, see the [Harness Context](https://docs.concurrence.com/developer-guide/platform-api/functions/harness-context) developer guide.

</details>

<details>

<summary>Platform API: Agent Runs and Definitions - Drop CrewAI Framework Support (July 2026)</summary>

#### Agent Runs and Definitions - Drop CrewAI Framework Support <a href="#agent-runs-and-definitions-drop-crewai-framework-support" id="agent-runs-and-definitions-drop-crewai-framework-support"></a>

The `crewai` framework has been removed from the platform. The two supported bring-your-own frameworks for native agent definitions and runs are now `openai-agents` (declarative handoff graph) and `claude-agent-sdk` (single agent with optional subagents). Both are fully executable end-to-end.

**What changed:**

* **CrewAI removed from accepted frameworks.** The `crewai` value is no longer accepted in the `framework` field on Agent Definitions or Agent Runs endpoints. Requests that specify `crewai` will receive a validation error.
* **Two supported frameworks.** The supported frameworks are `openai-agents` and `claude-agent-sdk`. Both are registrable and runnable - there is no longer a distinction between registrable-but-not-runnable and fully runnable frameworks.
* **Existing CrewAI definitions.** Previously registered CrewAI definitions remain in the registry as archived records but cannot be used to create new versions or dispatch runs.

**What you need to do:**

* **If you had CrewAI definitions:** Migrate to one of the two supported frameworks (`openai-agents` or `claude-agent-sdk`) and register a new definition. Archive any existing CrewAI definitions.
* **If you were not using CrewAI:** No action required.

For details on the supported framework shapes, see the [Agent Definitions](https://docs.concurrence.com/developer-guide/platform-api/functions/agent-definitions) developer guide.

</details>

<details>

<summary>Platform API: Agent Runs - Durable Trajectory Persistence (July 2026)</summary>

#### Agent Runs - Durable Trajectory Persistence <a href="#agent-runs-durable-trajectory-persistence" id="agent-runs-durable-trajectory-persistence"></a>

Agent runs now retain their normalized trajectory beyond the lifetime of the live execution. Previously, trajectory data could disappear after a runtime restart or run eviction. Completed trajectory steps are now available for supported downstream analytics, distillation, and quality evaluation.

**What changed:**

* **Trajectory persistence on run completion.** When a framework run succeeds, the platform writes each normalized trajectory step to the analytical data store. Each step carries the same actor attribution, framework tag, tool call metadata, usage counts, and state transitions already visible in the run result.
* **Provenance stamping.** Every persisted trajectory step is stamped as platform-executed, indicating the platform ran the framework and directly observed its tool calls. This distinguishes platform-executed history from future client-reported trajectories.
* **Content-tier fields excluded.** Verbatim transcript text and raw model reasoning (chain-of-thought) are not persisted - only structural metadata (tool names, token counts, state transitions, actor attribution) is written. This is consistent with the platform's data handling posture for analytical data.
* **Best-effort, never affects run status.** Trajectory persistence is best-effort. A persistence failure is logged and skipped but never changes the run's terminal status (succeeded, failed, or timed out). A per-step failure skips that step; the remaining steps still persist.
* **Dark-ship safe.** When the persistence path is not yet configured for an environment, trajectory capture is a no-op. The run path is completely unaffected, so the feature can be rolled out incrementally.
* **Deduplicated writes.** Each trajectory step uses a stable identifier for at-least-once deduplication, so retries of the same logical step do not produce duplicate records.

**What you need to do:**

* **No action required.** Trajectory persistence is automatic for all completed framework runs. There are no new API fields, parameters, or endpoints. Run dispatch, polling, and result retrieval work exactly as before.

</details>

<details>

<summary>Platform API: Agent Runs - Native Definition Runs End-to-End (July 2026)</summary>

#### Agent Runs - Native Definition Runs End-to-End <a href="#agent-runs-native-definition-runs-end-to-end" id="agent-runs-native-definition-runs-end-to-end"></a>

The Agent Runs endpoint now supports dispatching native agent definitions end-to-end. You can run a customer-authored agent definition - either a registered definition by ID or an inline definition body - directly against the workspace's data surface, with full trajectory normalization, token usage, and actor attribution.

**What changed:**

* **Native run mode on the create-run endpoint.** `POST /v1/{workspace_id}/agent-runs` now accepts a `native` object as an alternative to `service_id` + `framework`. Exactly one mode must be specified per request.
* **Run by registered definition.** Set `native.definition_id` (and optionally `native.version`) to run a previously registered agent definition. When `version` is omitted, the latest version is used. The definition must belong to the same workspace.
* **Run by inline definition.** Set `native.inline` to a definition document for dev/playground iteration. The inline body is validated against the platform clamp schema before dispatch - validation errors return a 422 with field-level detail.
* **Two supported frameworks.** Native runs are supported for the single-agent-with-subagents (`claude-agent-sdk`) and declarative-handoff-graph (`openai-agents`) framework shapes. Both are fully executable end-to-end.
* **Clamp validation at dispatch.** Native definition bodies are re-validated at dispatch time even for registered definitions, so a schema change that invalidates a previously-registered body surfaces as a 422 rather than a mystery failed run.
* **Same trajectory output.** Native runs produce the same normalized trajectory, actor attribution, token usage (including cache tokens), and framework tagging as platform runs.

**What you need to do:**

* **To run a registered definition:** `POST /v1/{workspace_id}/agent-runs` with `native: {definition_id: "..."}` and a `message`. Omit `service_id` and `framework`.
* **To run an inline definition:** `POST /v1/{workspace_id}/agent-runs` with `native: {inline: {...}}` and a `message`. The inline body must include a `framework` field and conform to the clamp schema for that framework.
* **To run a specific version:** Add `version` to the `native` object (e.g., `native: {definition_id: "...", version: 3}`).
* **Existing platform runs are unchanged.** The `service_id` + `framework` path works exactly as before.

For endpoint details, see the [Agent Runs](https://docs.concurrence.com/developer-guide/platform-api/functions/agent-runs) developer guide. For definition registration, see [Agent Definitions](https://docs.concurrence.com/developer-guide/platform-api/functions/agent-definitions).

</details>

<details>

<summary>Platform API: Agent Definitions - Native Agent Definition Registry (July 2026)</summary>

#### Agent Definitions - Native Agent Definition Registry <a href="#agent-definitions-native-agent-definition-registry" id="agent-definitions-native-agent-definition-registry"></a>

The Platform API now includes a dedicated registry for native agent definitions. Customers can register, version, validate, list, and archive their own framework-native agent definitions as immutable, versioned resources within a workspace.

**What changed:**

* **New Agent Definitions CRUD endpoints.** A new set of endpoints under `/v1/{workspace_id}/agent-definitions` lets you register, list, retrieve, validate, and archive native agent definitions. Definitions are workspace-scoped and identified by a stable name and framework.
* **Immutable versioning with idempotent push.** Every push of a changed definition body mints a new version number. Re-pushing a byte-identical body returns the existing version without creating a duplicate, so CI pipelines can push on every run safely.
* **Clamp validation.** Definition bodies are validated against a strict whitelist schema. Only fields the platform will honor are accepted - any unrecognized field is a 422 validation error naming the offending path, never silently ignored or rewritten. A dry-run validation endpoint (`POST .../validate`) lets you check a body without storing anything.
* **Framework lock per name.** A definition's framework is set on first registration and cannot be changed. Attempting to register the same name with a different framework returns a 409 Conflict. To switch frameworks, archive the existing definition and register a new one.
* **Soft archive.** `DELETE .../agent-definitions/{definition_id}` soft-archives a definition, freeing the name for reuse. Existing versions remain immutable and retrievable.
* **Two supported frameworks.** `openai-agents` (declarative handoff graph) and `claude-agent-sdk` (single agent with optional subagents).
* **Write-tool and agent-count metadata.** Each version is tagged with whether it references write tools and how many agents it declares, so callers can inspect these properties without parsing the body.
* **Paginated listing with framework filter.** The list endpoint supports filtering by framework, including or excluding archived definitions, and pagination with continuation tokens.

**What you need to do:**

* **To register a native definition:** `POST /v1/{workspace_id}/agent-definitions` with a `name` (slug) and `body` (the framework-native definition document including a `framework` field). The response includes the definition ID, version number, and whether a new version was created.
* **To validate without storing:** `POST /v1/{workspace_id}/agent-definitions/validate` with the same request shape. Returns validation results or a 422 with field-level errors.
* **To list definitions:** `GET /v1/{workspace_id}/agent-definitions` with optional `framework`, `include_archived`, `limit`, and `continuation_token` query parameters.
* **To retrieve a definition with version history:** `GET /v1/{workspace_id}/agent-definitions/{definition_id}`.
* **To retrieve a specific version body:** `GET /v1/{workspace_id}/agent-definitions/{definition_id}/versions/{version}`.
* **To archive:** `DELETE /v1/{workspace_id}/agent-definitions/{definition_id}` (requires admin+ role).

**Permissions:** Register and validate require Service create permission (member+). List and get require Service view permission (viewer+). Archive requires Service delete permission (admin+).

For full endpoint documentation, see the [Agent Definitions](https://docs.concurrence.com/developer-guide/platform-api/functions/agent-definitions) developer guide.

</details>

<details>

<summary>Platform API: Customer Data Intake - Folder Path Materialization (July 2026)</summary>

#### Customer Data Intake - Folder Path Materialization <a href="#customer-data-intake-folder-path-materialization" id="customer-data-intake-folder-path-materialization"></a>

Documents ingested from mapped cloud storage folders now carry the original folder path, enabling retrieval filtering by source folder location.

**What changed:**

* **Folder path preserved on intake documents.** When documents are ingested from a connected cloud storage folder, the platform now records the relative folder path within the mapped folder tree. The path uses folder names joined by `/` (e.g. `clinical/notes`), with an empty string at the root level.
* **Path updated on re-sync.** If a file moves to a different subfolder and is re-synced, the stored folder path updates to reflect its new location rather than retaining the original path.
* **Exposed in the file listing.** The `source_folder_path` field is returned on each file in the intake file list endpoint, so the console and integrations can display or filter by original folder structure.
* **Enables folder-based retrieval filtering.** With folder paths available, retrieval queries can filter documents by their original folder location - for example, restricting results to documents that lived under `clinical/` or `billing/reports/` in the source folder structure.
* **Null for manual uploads and older documents.** Documents uploaded manually (not through a connected folder source) carry no folder path. Documents ingested before this change also have no folder path. In both cases the field is null.

**What you need to do:**

* **No action required.** The folder path is recorded automatically during intake for documents from connected folder sources. Existing documents are unaffected.
* **To use folder-based filtering:** Use the `source_folder_path` field in the intake file list to scope views or retrieval to specific source folders.

</details>

<details>

<summary>Platform API: Memory - Clinical State Projection (Phase-2b Structured Dimensions) (July 2026)</summary>

#### Memory - Clinical State Projection (Phase-2b Structured Dimensions) <a href="#memory-clinical-state-projection-phase-2b-structured-dimensions" id="memory-clinical-state-projection-phase-2b-structured-dimensions"></a>

The memory system now includes a deterministic clinical state dimension that projects active conditions, medications, and allergies from connector/EHR data directly into the patient's memory profile - no LLM extraction required.

**What changed:**

* **New `clinical_state` memory dimension.** A new system-default memory dimension called `clinical_state` is now computed for every workspace. It contains a concise, human-readable summary of the patient's active clinical facts - conditions, medications with doses, and allergies - projected directly from structured connector/EHR data in the world model.
* **Deterministic, not agent-inferred.** Unlike LLM-extracted memory dimensions, the clinical state projection is fully deterministic. It rolls up already-structured clinical data without involving an LLM, which eliminates hallucination risk and gives it the highest precision tier (clinical/safety).
* **Higher confidence tier than LLM memory.** The clinical state projection is emitted at a confidence tier above LLM-extracted memory. When both an LLM-inferred observation and the structured projection exist for the same patient on the same key, the projection always wins. This ensures source-of-truth clinical facts from connector data are never overwritten by agent-inferred observations.
* **Loaded at session start.** The clinical state summary is available to the agent at session start alongside the user model - no tool call needed. Clinical facts that were previously reachable only through runtime tool calls are now part of the agent's initial context.
* **Genuinely current statuses.** Conditions and medications are filtered to all genuinely current clinical statuses, not just "active." Recurrences and relapses are included so they are never silently dropped from the clinical picture.
* **Bounded with visible overflow.** The summary is bounded by recency: conditions and medications are ordered by most recent first and capped, with a visible overflow marker (e.g., "+3 more conditions") rather than silent truncation. Allergies are never truncated due to their safety-critical nature.
* **Automatic updates.** The projection updates automatically as underlying clinical data changes through connector syncs. The updated summary is available on the next session without manual intervention.

**What you need to do:**

* **No action required.** The clinical state dimension is computed automatically for all workspaces with connected clinical data sources. It appears in the patient's memory profile alongside existing dimensions.
* **No change to existing memory behavior.** Existing LLM-extracted dimensions (emotional state, engagement patterns, communication preferences, and so on) continue to work exactly as before. The clinical state projection is additive.

</details>


---

# 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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.concurrence.com/api-reference/change-logs/amigo-api/amigo-api-history-06.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
