> For the complete documentation index, see [llms.txt](https://docs.amigo.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.amigo.ai/developer-guide/platform-api/data-world-model/connector-runner.md).

# Connector Runner

The connector runner moves data between supported external systems and the workspace's [world model](/developer-guide/platform-api/data-world-model.md). It polls configured EHR, FHIR, REST, and workspace-data sources; writes source-attributed events; delivers policy-eligible event payloads to supported destinations; and dispatches scheduled outbound calls. The legacy CRM connector and general event-review pipeline are not active connector paths.

Inbound polling is implemented for `rest_api`, `fhir_store`, `smart_fhir`, `lakebase_schema`, and the supported vendor configurations under `ehr`. Connector-specific webhooks can ingest changes when the external system provides a supported event or subscription flow. A source type accepted by an older stored record is not proof that a current polling handler exists.

Outbound write-back supports the connector types listed in [Write-Back](#write-back). Unsupported connector types are reported rather than routed through a fallback handler.

{% hint style="info" %}
**The connector runner has no public-facing API.** It operates as a background service. Data sources and sync configuration are managed through the [Data Sources](https://docs.amigo.ai/api-reference/readme/platform/data-sources) endpoints on the Platform API, and its health is visible through the [Pipeline Observability](#pipeline-observability) endpoints below. For the full architecture narrative - sync design, confidence gating, and review workflow - see the [Connectors & EHR conceptual documentation](https://docs.amigo.ai/data/connectors-and-ehr).
{% endhint %}

## Inbound Sync

Data sources feed the world model through two mechanisms:

* **Polling**: supported sources are polled according to their [sync strategy](/developer-guide/platform-api/data-world-model.md#data-sources). Locking, checkpoints, deduplication, and retry behavior vary by adapter. A committed batch remains available after a later batch fails, but customers should use source health and history to confirm recovery.
* **Real-time webhooks**: supported EHR event and subscription flows can push change notifications. Verification, deduplication, and resource retrieval follow the contract of the selected connector.

Both paths produce source-attributed world-model events. Mapping, entity resolution, and recovery behavior can differ by connector.

**Polling behavior:**

| Feature                        | How It Works                                                                                                             |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
| **Full vs light cycles**       | Supported EHR adapters can separate broad resource refreshes from higher-frequency resource polling.                     |
| **Business hours gate**        | Polling can be restricted to defined operating hours to respect external API usage patterns                              |
| **Reference data caching**     | Frequently-accessed reference data (locations, carrier lists) is reused between polls to reduce external API call volume |
| **Configurable poll cadences** | Per-resource poll intervals via `poll_cadences` in the data source connection config                                     |

### Entity Resolution

Events can arrive with references to external entities (for example, an appointment references a patient by external ID). Supported resolver paths map stable source references to canonical IDs, create or find matching world-model entities, link events, and project graph relationships. Retry and deduplication guarantees depend on the connector's identifiers, checkpoints, and resolver path; entity resolution is not a universal request-idempotency guarantee.

| External Type        | Entity Type       | Canonical ID Format           |
| -------------------- | ----------------- | ----------------------------- |
| Patient              | `person`          | `{source}:Patient:{id}`       |
| Practitioner         | `person`          | `{source}:Practitioner:{id}`  |
| Location             | `place`           | `{source}:Location:{id}`      |
| Appointment          | `appointment`     | `{source}:Appointment:{id}`   |
| Coverage / Encounter | Linked to patient | Via participant references    |
| Slot                 | Skipped           | Marked to prevent re-fetching |

The current resolver does not create new cross-source `same_as` merge edges from demographic similarity. Records from different sources remain distinct unless they share a stable canonical mapping or another supported producer links them explicitly. Historical `same_as` edges remain visible in entity-intelligence responses but should not be read as a current automatic-merge promise.

Derived entity views and enrichment can update asynchronously after event ingestion. A successful event write can therefore precede its appearance in every derived view.

## Outbound Sync

Data captured by the [voice agent](/developer-guide/platform-api/conversations/voice-agent.md) during calls is gated before syncing to external systems:

* **Source allowlist**: only `voice_agent`, `encounter_finalized`, and `platform_api_mcp` (clinical writes made through the MCP server, which must reach the EHR like the voice path) events are eligible for outbound sync. Events from `api`, `outbound_sync_agent`, `ehr_sync`, and `connector_runner` sources are excluded. This is an allowlist, not a blocklist - new sources must be explicitly added to become eligible.
* **Confidence gate**: the live unattended subscriber requires verified confidence after separating review-required sinks. Approved proposal egress is a distinct human-authorized path and does not reapply that automatic confidence gate.
* **Review-gated sinks**: where the private-preview proposal flow is enabled, a sink can stage an external write proposal instead of auto-pushing. An operator approves or rejects the proposal through the [Review Queue](/developer-guide/platform-api/integrations/review-queue.md). That decision authorizes the specific payload and target; it does not change the source event's confidence. Staging deduplicates the same originating write intent. This connector flow is separate from a Platform Integration's conversation-scoped `approval_policy`.

{% hint style="warning" %}
The live pub/sub path for sinks that do not require review is delivered at most once. A dropped or failed write is not automatically reconciled. Where private-preview proposals are enabled, they expose decision and delivery status, but customers must still verify failed or ambiguous destination outcomes before retrying a mutation.
{% endhint %}

### Write-Back

A workspace can target multiple supported external systems. Each sink is configured independently with its own `outbound_entity_types`, and delivery outcomes are tracked per sink. A failure at one destination does not block evaluation for another.

Outbound events are routed through a **handler registry** that maps each sink's `connector_type` to the correct write-back handler. The registry is the single routing authority for all outbound paths.

| Connector Type    | Write-Back                                                                | Auth                                               |
| ----------------- | ------------------------------------------------------------------------- | -------------------------------------------------- |
| `fhir_store`      | FHIR R4 (ETag optimistic locking)                                         | Managed credentials                                |
| `smart_fhir`      | SMART-compliant FHIR R4                                                   | SMART Backend Services (RS384/ES384 JWT assertion) |
| `athenahealth`    | athenahealth EHR adapter (Patient create/update, Appointment book/cancel) | OAuth2 client credentials                          |
| `charmhealth`     | Charm EHR adapter                                                         | OAuth2 + API key                                   |
| `eclinicalworks`  | eClinicalWorks EHR adapter                                                | Managed credentials                                |
| `meditab`         | Meditab EHR adapter                                                       | Managed credentials                                |
| `hazelhealth`     | Hazel Health EHR adapter                                                  | Managed credentials                                |
| `mbp`             | MBP EHR adapter                                                           | Managed credentials                                |
| `lakebase_schema` | Workspace database schema adapter                                         | Managed credentials                                |

For eligible events, the connector runner maps the event payload into the target system's format:

| Event Type                            | What Happens                                                                            |
| ------------------------------------- | --------------------------------------------------------------------------------------- |
| `patient.created`                     | Create a patient record through the selected connector's deterministic payload mapping. |
| `patient.updated`                     | Merge demographics with current EHR record                                              |
| `appointment.booked`                  | Create appointment (patient resolved from entity external IDs)                          |
| `appointment.confirmed` / `cancelled` | Update appointment status                                                               |
| `coverage.created`                    | Create insurance record (carrier fuzzy-matched from EHR carrier list)                   |

**Write-back safeguards:**

* **Optimistic locking**: for FHIR stores, ETag-based locking prevents lost updates. On conflict (412), the write is re-read and retried up to 3 times.
* **Confidence-aware dependencies**: supported handlers can evaluate dependency confidence (for example, before creating an appointment for a patient). Confirm the selected connector's dependency behavior rather than assuming every write path performs the same check.
* **Duplicate prevention**: supported patient-create paths perform connector-specific matching before record creation. Confirm the selected connector's behavior before treating this as a universal guarantee.
* **Loop prevention**: the live subscriber's source policy excludes `ehr_sync` events from unattended write-back. Other import, proposal, and connector flows have their own loop and replay contracts.

## Outbound Call Dispatch

The connector runner dispatches outbound calls when scheduled `outbound_task` entities become due. This implements the execution side of the [voice agent's outbound system](/developer-guide/platform-api/conversations/voice-agent.md#outbound-call-flow). For each due task, the runner loads the patient entity from the world model, builds a rich system prompt from the patient projection, resolves the outbound phone number, and dispatches the call - writing an `outbound.dispatched` or `outbound.failed` event with the result.

| Feature                 | How It Works                                                                                         |
| ----------------------- | ---------------------------------------------------------------------------------------------------- |
| **Business hours gate** | Per-task timezone-aware window (e.g., 9am-5pm ET). Outside hours, the task waits for the next window |
| **Priority ordering**   | Tasks dispatched highest-priority first (1-10 scale)                                                 |
| **Dispatch locking**    | Per-entity locking prevents double-dispatch                                                          |
| **Retry with backoff**  | Configurable max attempts and backoff interval. Failed tasks automatically schedule the next attempt |
| **Context**             | Selected patient projection data is added to the outbound task context.                              |
| **Idempotency**         | Dispatch keyed by `outbound:{entity_id}:{attempt}`, safe to retry                                    |

## Pipeline Observability

The Platform API exposes 10 read-only pipeline observability endpoints under `/v1/{workspace_id}/pipeline/` that combine live connector runner state with stored pipeline data. In the current Console, per-source health appears under **Data > Sources**; legacy Sync Health links redirect there.

All operations require `viewer+` access and are workspace-scoped.

### Pipeline Status

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/pipeline/status" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### List Sources

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/pipeline/sources" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Source Overview

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/pipeline/sources/{source\_id}/overview" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Source History

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/pipeline/sources/{source\_id}/history" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Source Events

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/pipeline/sources/{source\_id}/events" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Outbound Summary

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/pipeline/outbound" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Outbound Log

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/pipeline/outbound/{data\_source\_id}/log" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Entity Resolution Metrics

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/pipeline/entity-resolution" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Legacy Review Counters

The retired event-review pipeline has no producer. Its compatibility counters remain zero and do not represent external write proposals.

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/pipeline/review" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Throughput

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/pipeline/throughput" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Source Health

**Connection health** is tracked per source using consecutive error counts. A source is marked degraded after 3+ consecutive poll failures and recovers automatically on the next successful poll.

Live health metrics reflect recent poll activity and reset when the connector runner restarts; stored event counts and sync history are unaffected.

### Graceful Degradation

If the connector runner is temporarily unavailable, the pipeline endpoints still return stored event counts, sync history, and entity resolution metrics. Only live component states and active poll counts are omitted. The dashboard stays functional during connector runner restarts or deployments.

## Connector Settings

Connector definitions are managed through workspace settings with typed configuration models. Each connector definition includes:

| Field               | Description                                                                      |
| ------------------- | -------------------------------------------------------------------------------- |
| `connector_type`    | The external system type (maps to the handler registry)                          |
| `sync_strategy`     | Sync cadence: `continuous`, `scheduled`, `manual`, or `webhook`                  |
| `connection_config` | System-specific connection parameters (endpoints, credentials, resource scoping) |
| `poll_cadences`     | Per-resource type polling intervals                                              |

The connector settings endpoint validates sync strategy values with strict type checking, preventing misconfiguration bugs. The data source response includes an `is_stale` field computed from the connector's last successful sync and its configured cadence. Configuration changes are picked up automatically on the next sync cycle - no restart is needed.


---

# 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.amigo.ai/developer-guide/platform-api/data-world-model/connector-runner.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.
