Skip to main content
phealth

Lifecycle

4 endpoints + webhook

create cue decide read close

Multi-stream timeline

5 priority rules · 1/day cap

safety time-bound receptivity enrolled-first template-id

How it works

From patient context to the right message, in one call.

One POST returns the next message, the minute to send it, and the audience-safety metadata behind it.

Two core mechanisms make that one call work: the stream lifecycle is how a stream begins and ends; the multi-stream timeline honors the one-message-per-day cap when a patient is enrolled in more than one stream.

For onboarding details, see /signal/integrate. For audience-safety governance, see /signal/safety. For stream versioning, see /signal/changelog.

Display mode never touches PHI — no BAA required. Start in 5 minutes.

Stream lifecycle

One stream. One patient. One sequence of cues.

Each stream is its own object. The cue loop is the only ongoing contract — create happens once, the Deliver-mode webhook fires per event, and you read state on demand. A stream closes itself when its curated subset is exhausted or the patient opts out; there is no separate close call. Streams are bound to one template at create-time; switching templates means ending the stream and opening a new one.

1 Create
POST /v1/signal/streams

Pass a template_id, patient context, and audience tags. The member is enrolled in this one call; the response carries first_cue.

2 Cue
POST /streams/:id/cue

Return feedback for the last message, get next_cue back. Feedback is required before the next cue.

did_it check_it know_it skipped
3 Decide
webhook

Send-time intelligence picks the action and the minute. In Deliver mode your webhook fires with the decision.

4 Read
GET /streams/:id

Pull stream state and the current cue any time — idempotent, safe to poll.

5 Close
state: ended

The stream ends when the curated subset is exhausted (sandbox_exhausted) or you stop cueing.

Cue again before returning feedback and the API answers 409 feedback_required — feedback is the moat that keeps every stream honest. Browse the curated subset first with GET /v1/signal/sandbox-topics.

What every cue response looks like

Every cue returns three fields: message (the text to render or send), safety (verdict plus the include/exclude tag set that was checked), and receptivity (a 0-to-1 advisory score). Display mode partners use receptivity to pick the moment; Deliver mode partners let Reach use it.

The stream object itself carries day (advances on each cue), total_days (the template's planned arc length, or null for indefinite streams), and last_cued_at. State is durable — interrupting your worker mid-loop does not reset progression.

Multi-stream · conflict resolution

One patient. Three concurrent streams. One message a day.

A patient enrolled in three streams does not get three messages a day. Signal honors a per-patient 1-message-per-day cap across all streams. The picker runs five priority rules to decide which stream wins each day; the others mark the day suppressed (with reason) so your audit trail stays honest.

p_5572 · enrolled in 3 streams

tags · t2d · cardiac_aware · medication_init

Mon May 13
Tue 14
Wed 15
Thu 16
Fri 17
Sat 18
Sun May 19

Diabetes · Pharma Init

day 16 / 30 · time-bound

SENT

Day 13 dose check

SENT

Day 14 side effects

SENT

Day 15 food stack

SUPPRESSED

dropped for safety

SENT

Day 17 refill heads-up

SENT

Day 18 weekend pacing

SENT

Day 19 how it lands

General Wellness

indefinite · always loses

SUPPRESSED

SUPPRESSED

SUPPRESSED

SUPPRESSED

SUPPRESSED

SUPPRESSED

SUPPRESSED

Safety push · one-off

on-demand · always wins

IDLE

IDLE

IDLE

SENT

Grapefruit interaction

IDLE

IDLE

IDLE

LEGEND sent suppressed idle

1 message/day cap honored · 7 of 7 days

The five priority rules

These run in order. The first match wins for that day; the rest are suppressed with reason so the audit trail explains every drop.

  1. Safety beats everything

    On-demand safety pushes (drug interactions, recall notices, urgent clinical advisories) always win the day. Other streams that would have fired today mark the day suppressed with reason="safety_preempt".

  2. Time-bound beats indefinite

    A stream with total_days set (Pharma Init, Postpartum, Relapse Prevention) outranks an indefinite stream (General Wellness, Cancer Survivorship). The time-bound arc only runs for N days; the indefinite one can catch up later.

  3. Higher receptivity wins ties

    When two streams share a tier, the one with the higher current-day receptivity score wins. This is the only place receptivity drives a Signal-internal decision; in cue responses it is advisory only.

  4. Earlier enrollment wins receptivity ties

    If receptivity scores match too, the stream that enrolled earlier wins. Patients in their first week of a new stream are not pulled away by streams they have been in for months.

  5. Stream-template ID is the final tiebreak

    If all four above tie (rare — usually means two streams of the same template started on the same day), the alphanumerically lower template_id wins. Deterministic; identical inputs always produce the same outcome.

Why the cap exists

More than one message a day from one sender saturates fast. The 1-per-day cap is not a configuration knob; it is a structural commitment. If a buyer needs more than one daily touch on a patient (combined safety + lifestyle, for instance), that is a multi-stream design and the priority rules above resolve it cleanly.

Evaluating Signal? Let Claude or ChatGPT do the vendor legwork — paste one prompt.

See how →