Signals standalone

Use Signals' own API through Ovok at /v1/signals/api: app types, authentication, what Ovok adds to each call, and a first patient, reading and alert.

Use Signals' own API through Ovok. You keep your patients and readings in your own system, send them to Signals under your own ids, and read back the alerts Signals raises. You need no FHIR data and no Signals credentials: Ovok holds them and scopes every call to your project.

Every route is Signals' path under /v1/signals/api. For example, Signals' GET /api/alert is GET /v1/signals/api/alert. The API reference lists each route Ovok serves; any other path answers 404.

Before you start

RequirementDetails
Your own Signals tenantYour project needs a Signals tenant of its own. A project on the shared tenant gets 409 SIGNALS_TENANT_NOT_OWN.
Standalone only?Switch SIGNALS_SEND_AUTO and SIGNALS_ANNOTATE_OBSERVATIONS off. Both are on by default, and annotations would write a FHIR DetectedIssue for each of your alerts, pointing at a Patient that does not exist.
Base URLhttps://api.sandbox.ovok.com/v1/signals/api in the sandbox.
AuthenticationAn Ovok access token as Authorization: Bearer, or a client application's id and secret as HTTP Basic.
CapabilitiesThe caller's access policy needs the signals_* rows each route names. See Signals route policies.

App types

Signals has three roles: a manager runs the tenant, a submitter posts readings, and a subscriber receives webhooks. In Ovok, create a Manager or Submitter application in the Console (Applications). Its access policy is made from the routes that role may call, so it holds exactly those capabilities.

App typeSignals roleWhat it can call
ManagerMANAGEREvery route except submitting readings: patients, configs, alerts, settings, users, webhooks and events.
SubmitterSUBMITTERSubmitting readings, and reading patients.

A manager application answers 403 when it posts a reading: use a submitter application for that. Subscribers are not Ovok applications. You create them through the API, and they never sign in; see Webhooks.

Project admins pass every capability check. Client applications never get that bypass, even when marked as admins: they need the capabilities in their own policy.

What Ovok adds to each call

Bodies and answers pass through as Signals sends them, with these exceptions.

AreaWhat Ovok does
Project scopeAlert reads and acknowledgements are pinned to your project: Ovok sets project, and naming another project answers 403.
New patientsOvok adds your project's submitter to each new patient and stamps metadata.project with your project, so your submitter can post their readings and their alerts show in your project.
Alerting changesWriting a patient's config, or alertsPaused, alertIfEmpty, alertIfNote or alertInactiveDays, also needs signals_config:update and signals_silence:update.
Tenant writesPATCH /tenant takes only orgSettings, resolvedAt and settings.episodicAlerts. Any other field answers 403.
UsersOnly subscribers are created, changed or deleted. Ovok generates their id and Signals login.
BillingPatients you create count toward your plan's monitored-patient cap; past it, the create answers 402. Each accepted reading counts its patient as monitored.
BodiesBodies must be JSON (415 otherwise). On patient create, patient update and subscriber create, which Ovok rewrites, a number out of range answers 422.
DeprecationsGET and PUT /settings/org carry Deprecation and Sunset headers. Use orgSettings on PATCH /tenant instead.

Errors

Errors come in two shapes. Ovok's own refusals are Problem Details (application/problem+json): a missing token (401), a missing capability or another project (403), no tenant of your own (409), and the cap (402). Signals' own refusals, such as a validation failure or an unknown patient, keep Signals' JSON shape.

When Signals cannot be reached, Ovok answers 502, 503 with Retry-After, or 504 on a timeout. Signals' own answers, 5xx included, come back as Signals sent them, with one exception: Signals never refuses your call with 401. If it rejects Ovok's own credential, you get 502.

Quickstart

These calls give your tenant a heart-rate band, create a patient, post a reading above the band and read the alert it raises. Set OVOK_MANAGER_ID/OVOK_MANAGER_SECRET and OVOK_SUBMITTER_ID/OVOK_SUBMITTER_SECRET to your two applications.

1. Set a heart-rate band

Signals' default rules judge heart rate (LOINC 8867-4) against a band, and a new tenant has none, so a reading raises nothing yet. Set one for the whole tenant:

curl --request PATCH 'https://api.sandbox.ovok.com/v1/signals/api/tenant' \
  --user "${OVOK_MANAGER_ID}:${OVOK_MANAGER_SECRET}" \
  --header 'Content-Type: application/json' \
  --data '{"orgSettings":{"bands":{"8867-4":{"low":40,"high":110}}}}'

orgSettings is merged by key. A patient's own band, set with PUT /patient/:id/settings, wins over the tenant's. If a breaching reading still raises nothing, check the tenant's triggers with GET /tenant.

2. Create a patient

refId is your own id for the patient.

curl --request POST 'https://api.sandbox.ovok.com/v1/signals/api/patient' \
  --user "${OVOK_MANAGER_ID}:${OVOK_MANAGER_SECRET}" \
  --header 'Content-Type: application/json' \
  --data '{"patients":[{"refId":"resident-001","name":"Resident 001"}]}'

The answer lists the patients as stored, each with its Signals id. Creating the same refId again updates it rather than duplicating it.

3. Post a reading

The body is a FHIR R4 Observation. Name the patient with patientRefId.

curl --request POST 'https://api.sandbox.ovok.com/v1/signals/api/observation' \
  --user "${OVOK_SUBMITTER_ID}:${OVOK_SUBMITTER_SECRET}" \
  --header 'Content-Type: application/json' \
  --data '{
    "patientRefId": "resident-001",
    "identifier": [{ "value": "reading-0001" }],
    "code": { "coding": [{ "system": "http://loinc.org", "code": "8867-4" }] },
    "valueQuantity": { "value": 132, "unit": "/min" },
    "effectiveDateTime": "2026-10-25T08:00:00Z"
  }'

Signals judges the reading before it answers. The answer lists the alerts it raised only if you send Prefer: respond-sync, and then no webhook is sent for them; otherwise read them with GET /alert or receive them by webhook. The first identifier is stored as the reading's refId; sending the same one again within an hour answers 409 DUPLICATE_READING.

4. Read the alerts

curl --get 'https://api.sandbox.ovok.com/v1/signals/api/alert' \
  --user "${OVOK_MANAGER_ID}:${OVOK_MANAGER_SECRET}" \
  --data-urlencode 'status=ACTIVE'

For episodes, one row per clinical event rather than one per breach, add episodes=true and use status=OPEN or CLOSED. Episodes need episodicAlerts on for your tenant; see Update tenant settings.

5. Acknowledge one

curl --request POST 'https://api.sandbox.ovok.com/v1/signals/api/alert/:id/ack' \
  --user "${OVOK_MANAGER_ID}:${OVOK_MANAGER_SECRET}" \
  --header 'Content-Type: application/json' \
  --data '{"actor":{"reference":"Practitioner/123","display":"Dr. Max Mustermann"}}'

Acknowledging closes the alert. The next breaching reading opens a new one.

API reference

The route pages are generated from the API Explorer document, so they always match what Ovok serves. Each page lists the capabilities the route needs, what Ovok changes, and every answer.

Last updated on

On this page