Integrations

Connection health

What each connection status actually means, how a failing vendor degrades instead of disappearing, how to triage one, and why a replayed vendor webhook is a no-op.

A connection has two independent health signals, and conflating them is how integrations get debugged badly. status says whether the connection is usable. credentialStatus says whether the key behind it is good. A connection can be connected with a credential that is about to expire, and it can be degraded with a perfectly valid key because the vendor is down. Reading both is the difference between rotating a key you didn't need to and rotating one three days late.

Connection status#

Status Meaning What's happening to traffic
pending Created, probe passed, BAA not on file Nothing flows. PHI does not move without a BAA
connected Probe passed and the BAA is on file Normal operation
degraded The last probe failed Calls are attempted and failing; the failure is visible
revoked Disconnected Terminal. Nothing flows and nothing will

The status machine resolves in a fixed precedence, and the order matters:

Revoked wins over everything. A health check against a revoked connection returns the connection unchanged. A probe must never silently resurrect a vendor somebody deliberately disconnected — that's an access-control decision being undone by a background job.

A failed probe is degraded, not revoked. A vendor outage is not a disconnection. Degraded keeps the connection, keeps its configuration, and clears itself the moment a probe succeeds; you don't reconnect and re-enter credentials because a lab had a bad afternoon.

A passing probe with no BAA is pending, not connected. Working credentials are not authorization to send PHI. This is the one case where everything technically works and the connection still refuses to carry traffic, and it is intentional.

Credential status#

Value Meaning
missing No credential reference is set
present A credential resolved and the last live call used it
invalid A reference is set but the vendor rejected it
expired The credential resolved but is past its validity
rotating A rotation is in flight; two secrets are currently accepted

Alongside these, a connection carries a credential fingerprint — a stable derived identifier, never the secret or any part of it. It answers "is this still the same key I set last quarter?" without anyone reading the key, which is the only version of that question worth being able to ask.

Degrade behaviour#

Nothing on this platform fails silently, so every degrade has a defined visible landing place.

What fails What happens
An EMR can't accept an order write Order held, provider notified in-app, degraded: true on the response
A lab result can't be written to the chart Result held in neolife, provider notified
A vendor probe fails Connection degraded, error code recorded, failure counter increments
A signature source can't be resolved Order blocked — never signed by a fallback
A vendor adapter isn't built, in live mode The call throws. A real tenant is never run through a fixture

That last row is worth dwelling on. In sandbox, an unbuilt adapter falls back to a mock so an integration round-trips before credentials exist. In live, that same fallback throws — routing a real patient through fixture data is the same class of corruption as writing a fake patient into a real chart, and it has to fail loudly at the boundary rather than produce plausible-looking output.

Triage#

Read the error code, not the message. lastErrorCode, lastErrorAt, and consecutiveFailures are on the connection. The stored detail is deliberately terse — a vendor error body can echo patient data, so what's persisted is the error's class, never the vendor's response verbatim. If you want more, the sync log is where to look.

Read the sync log.

curl https://api.neolife.health/vendors/connections/{id}/sync-logs?limit=50 \
  -H "Authorization: Bearer $NEOLIFE_API_KEY"

Each row carries direction, operation, status, latency, the vendor's own id, the canonical id it mapped to, and a summary. The encrypted payload is not in the projection — the log is PHI-free by selection, so it's safe to read while someone is watching your screen.

Then re-probe.

curl -X POST https://api.neolife.health/vendors/connections/{id}/health \
  -H "Authorization: Bearer $NEOLIFE_API_KEY"

A successful probe clears degraded, zeroes the failure counter, and refreshes the credential fingerprint in one move.

Reading consecutiveFailures. One failure is a blip. A counter climbing while latency stays flat is usually authentication — the vendor is answering quickly, with a rejection. A counter climbing alongside rising latency is usually the vendor itself. A counter that resets and climbs again on a cycle is usually a token refresh that succeeds and then expires faster than you expect.

Rotating a webhook secret#

curl -X POST https://api.neolife.health/vendors/connections/{id}/rotate-webhook-secret \
  -H "Authorization: Bearer $NEOLIFE_API_KEY"

The response contains the new secret once. It is not retrievable from any endpoint afterwards.

The previous secret stays valid until the next rotation, and inbound signature verification accepts either. Without that grace window, rotating drops every delivery already in flight signed with the old secret — you'd be trading a routine hygiene task for a small outage, so nobody would do it.

Inbound vendor webhooks arrive on an opaque path token, not on the connection id. The endpoint isn't enumerable, and an internal identifier is never handed to a third party.

Idempotency#

Vendors replay. A retry after a timeout, a redelivery after a 5xx, a backfill after their outage — all of it arrives as a second copy of something you already processed.

Inbound: every normalized vendor event carries a dedupe key. A replayed event resolves to the same key and is a no-op. A lab that redelivers a resulted event four times produces one result, not four, and does not re-notify the clinician three more times.

Outbound: every mutating call you make takes an Idempotency-Key and acts at most once — see idempotency for the full contract. On this surface it matters more than usual: a retried consult request is a second clinician's time, and a retried lab order is a second needle.

Rule: treat every vendor callback as at-least-once delivery. The dedupe key makes that safe on our side; your own handlers should assume the same about the webhooks we send you.

Next steps#

  • Idempotency — the full at-most-once contract.
  • Webhooks — verifying the signature on what we send you.
  • EMR & charts — where the read-only degrade comes from.
  • Errors — the platform-wide error shape.