Skip to content

Endpoints

Who can do this: viewers can read; operators can acknowledge, mute and schedule downtime; organization administrators can also change service settings. The role required is stated on each endpoint.

All paths are relative to your Pulse address (the examples use https://pulse.labmonitors.com). Every request except the token request and the health check needs an Authorization: Bearer <token> header; see Authentication.

Conventions used on this page:

  • Form requests use application/x-www-form-urlencoded bodies (curl's -d).
  • Id path parameters must be whole numbers from 1 to 2147483647. Endpoints that validate this answer 422 for anything else; the exceptions are noted.
  • Timestamps in JSON are ISO 8601 with an explicit offset, for example 2026-09-07T21:14:03.512345+00:00. Pulse stores and returns times in UTC.
  • Errors are {"detail": "<message>"}. The messages below are quoted exactly.
  • Do not send an HX-Request header; it switches the response to an HTML fragment meant for the web application.
  • Archived services are left out of the alarms and downtimes lists, and their page and CSV export answer 404. Edit, band removal and mute refuse an archived service: edit answers 404 Service not found or not authorized, mute answers 404 Service not found, and band removal returns the Service not found fragment. Acknowledge, un-acknowledge, unmute, downtime scheduling and the recommendation endpoints do not check for archiving and behave exactly as for a live service; because an archived service has no active alarm, acknowledge answers 409.

Alarms

List active alarms

Item Value
Method and path GET /api/v1/alarms
Role any role (viewer or higher) in at least one organization
Request none

Returns every active alarm on the services in your organizations, ordered critical, then warning, then stale; within a severity, the most recently triggered first. Services that are archived or currently in downtime are not listed. A user with no organization role receives an empty list [].

Success, 200 OK:

[
  {
    "id": 118,
    "service_id": 118,
    "service_name": "Freezer 3 (-80)",
    "channel": "temp_1",
    "channel_label": "Temperature",
    "location_name": "Room 214",
    "severity": "critical",
    "message": "Temperature is critical (CRITICAL_HIGH) (value -61.2)",
    "triggered_at": "2026-09-07T20:41:00.183221+00:00",
    "acknowledged": false,
    "acknowledged_by": null,
    "acknowledged_at": null,
    "is_stale": false,
    "last_telemetry_time": "2026-09-07T21:13:30+00:00",
    "stale_since": null,
    "can_operate": true
  },
  {
    "id": 97,
    "service_id": 97,
    "service_name": "Incubator B",
    "channel": "co2",
    "channel_label": "CO2",
    "location_name": "Cell culture",
    "severity": "stale",
    "message": "No data since Sep 07, 2026 18:02 UTC",
    "triggered_at": "2026-09-07T18:02:11+00:00",
    "acknowledged": false,
    "acknowledged_by": null,
    "acknowledged_at": null,
    "is_stale": true,
    "last_telemetry_time": "2026-09-07T17:47:05+00:00",
    "stale_since": "2026-09-07T18:02:11+00:00",
    "can_operate": false
  }
]
Field Meaning
id, service_id Both hold the service id. Use it in the acknowledge, mute and downtime calls.
service_name, location_name Names as shown in the web application. location_name is null when the asset has no location.
channel, channel_label The channel that is alarming (raw name and friendly label). Either may be null.
severity critical, warning or stale. A value alarm on a service that has also stopped reporting keeps critical or warning and sets is_stale to true.
message Human-readable summary. The time inside a stale message is UTC.
triggered_at When the current severity began, or when the data went stale.
acknowledged, acknowledged_by, acknowledged_at The most recent active acknowledgement on the service, if any.
is_stale, last_telemetry_time, stale_since Data freshness for the service.
can_operate true when your role lets you acknowledge, mute or schedule downtime on this service.

Errors:

Status Message
401 Authentication required (no token) or Could not validate credentials (bad token)

Acknowledge an alarm

Item Value
Method and path POST /api/v1/alarms/{service_id}/acknowledge
Role operator or organization administrator in the service's organization
Request form field comment (optional, free text; stored with the acknowledgement and shown in the audit log)

Success, 200 OK:

{
  "id": 5123,
  "service_id": 118,
  "service_name": "Service 118",
  "severity": "acknowledged",
  "message": "Tech dispatched, compressor fault",
  "triggered_at": "2026-09-07T21:20:44.020113+00:00",
  "acknowledged": true,
  "acknowledged_by": "Jane Doe",
  "acknowledged_at": "2026-09-07T21:20:44.011870+00:00",
  "acknowledged_severity": "CRITICAL"
}
Field Meaning
id The acknowledgement id (not the service id).
service_name Always the placeholder Service <id>; look the real name up in the alarms list.
message Your comment, or Acknowledged when you sent none.
triggered_at The time of this request, not the time the alarm started.
acknowledged_severity The severity you acknowledged, in upper case (CRITICAL or WARNING).

Errors:

Status Message When
404 Alarm not found or already acknowledged No service with that id.
403 Not authorized for this service The service is not in one of your organizations.
403 Operator role or higher required for this action You are a viewer in that organization.
409 No active alarm to acknowledge for this service The service is currently normal.
409 Alarm already acknowledged at this severity An acknowledgement for the current severity already exists.

Note

Acknowledgements are scoped to a severity. Acknowledging a warning does not cover a later escalation to critical: when the service escalates, a new acknowledge call is accepted (200, with acknowledged_severity CRITICAL) even though the warning acknowledgement still exists. The alarms list does not show this. Its acknowledged, acknowledged_by and acknowledged_at fields reflect the most recent acknowledgement of any severity, so after an escalation the entry still reads "acknowledged": true with the earlier time. Compare triggered_at (the escalation time) with acknowledged_at to tell whether the current severity has been acknowledged. Acknowledgements for the same service are processed one at a time: if two callers acknowledge at the same moment, the first succeeds and the second receives 409.

Note

The {service_id} on this endpoint and on un-acknowledge is not range-checked. An id above 2147483647 returns 404 here rather than 422.

Saving new alarm limits for a service, applying a recommendation, or removing a band clears every acknowledgement on that service. See Acknowledging alarms for the behaviour in the web application. The acknowledgement link in notification emails performs the same action as this endpoint, using the browser session of whoever clicks it.

Remove an acknowledgement

Item Value
Method and path POST /api/v1/alarms/{service_id}/unacknowledge
Role operator or organization administrator in the service's organization
Request none

Removes the most recent active acknowledgement on the service. If both a warning and a critical acknowledgement exist, one call removes only the newer one; call again to remove the other.

Success, 200 OK:

{"status": "unacknowledged", "service_id": 118}

Errors:

Status Message When
404 Alarm not found or already acknowledged No service with that id.
403 Not authorized for this service The service is not in one of your organizations.
403 Operator role or higher required for this action You are a viewer in that organization.
409 No acknowledgement to remove The service has no active acknowledgement.

Warning

If this request fails for an unexpected reason (including an id above 2147483647), it answers 200 with {"status": "error", "message": "Operation failed"}. Check the status field, not only the HTTP status code.

Mute and unmute

Muting stops notifications for a service until you unmute it. It does not change the alarm state, and it is separate from acknowledging. See Muting and downtimes.

Mute a service

Item Value
Method and path POST /api/v1/services/{service_id}/mute
Role operator or organization administrator in the service's organization
Request form field reason (optional, free text; recorded in the audit log)

Success, 200 OK:

{"status": "muted", "service_id": 118}

Errors:

Status Message When
404 Service not found No service with that id, or the service is archived.
403 Not authorized for this service The service is not in one of your organizations.
403 Operator role or higher required for this action You are a viewer in that organization.
422 (field details) The id is not between 1 and 2147483647.

Each call records a new mute; muting an already-muted service succeeds and adds another record. One unmute clears them all.

Unmute a service

Item Value
Method and path POST /api/v1/services/{service_id}/unmute
Role operator or organization administrator in the service's organization
Request none

Clears every active mute on the service. Unmuting a service that is not muted also succeeds.

Success, 200 OK:

{"status": "unmuted", "service_id": 118}

Errors are the same as for mute, except that unmute does not check for archiving: unmuting an archived service answers 200.

Warning

If mute or unmute fails for an unexpected reason, it answers 200 with {"status": "error", "message": "Operation failed"}. Check the status field.

Downtimes

A downtime is a time window during which a service's alarms are hidden from the alarms list and not notified. See Muting and downtimes.

Times without an offset are read in your profile timezone

Downtime start and end times sent without a timezone offset are interpreted in the profile timezone of the account whose token you use (GET /api/v1/auth/me returns it as timezone), not as UTC. Pulse converts the window to UTC for storage, and the list endpoint and the web application return it in UTC and in your profile timezone respectively. To make a script independent of the profile setting, send an explicit offset (2026-09-08T09:00:00-07:00) or a Z suffix for UTC (2026-09-08T16:00Z).

List current downtimes

Item Value
Method and path GET /api/v1/downtimes
Role any role (viewer or higher) in at least one organization
Request none

Lists only windows that are active right now on non-archived services in your organizations, newest start first. Windows scheduled for the future and windows that have ended are not returned. A user with no organization role receives [].

Success, 200 OK:

[
  {
    "id": 731,
    "service_id": 118,
    "service_name": "Freezer 3 (-80)",
    "start_time": "2026-09-07T21:00:00+00:00",
    "end_time": "2026-09-08T03:00:00+00:00",
    "reason": "Compressor replacement",
    "created_by_user_id": 42,
    "created_by_name": "Jane Doe",
    "created_at": "2026-09-07T20:58:12.404511+00:00"
  }
]

created_by_name is Unknown when the creating account no longer exists.

Errors:

Status Message
401 Authentication required (no token) or Could not validate credentials (bad token)

Schedule a downtime

Item Value
Method and path POST /api/v1/downtimes
Role operator or organization administrator in the service's organization
Request form fields below
Field Required Format and validation
service_id yes Whole number id of the service.
start_time yes ISO 8601 date-time: YYYY-MM-DDTHH:MM, YYYY-MM-DDTHH:MM:SS, optionally followed by Z or an offset such as -07:00. Without an offset the value is read in your profile timezone. A date on its own (YYYY-MM-DD) is also accepted and means 00:00 in your profile timezone on that day.
end_time yes Same format. Must be after start_time and must not already have passed.
reason yes Free text; must not be blank. Shown in the web application and the audit log.

A start time in the past is accepted (the window starts immediately). A start time in the future is accepted and the window becomes active at that time.

Success: 303 See Other with Location: /services/{service_id} and no JSON body. The downtime id is not returned. To find it, call the list endpoint once the window is active and match on service_id and reason.

Errors:

Status Message When
403 Not authorized You hold no role in any organization.
403 You do not have permission to schedule downtime for this service The service does not exist or is not in one of your organizations.
403 Operator role or higher required to schedule downtime You are a viewer in that organization.
400 Invalid service The service_id could not be looked up (for example, above 2147483647).
400 Reason is required reason is empty or only spaces.
400 Invalid datetime format. Use datetime-local format. A time could not be parsed.
400 End time must be after the start time
400 Downtime window is in the past (its end time has already passed)
422 (field details) A required field is missing.
500 Failed to schedule downtime The window could not be saved. Retry, then contact Lab Monitors support to investigate.

Cancel a downtime

Item Value
Method and path DELETE /api/v1/downtimes/{downtime_id}
Role operator or organization administrator in the service's organization
Request none

Deletes the window immediately, whether it is active or still in the future. Any operator in the organization can cancel any window, not only their own. Cancelling re-enables notifications for the service.

Success, 200 OK:

{"status": "cancelled", "id": 731}

Errors:

Status Message When
404 Downtime not found or not authorized No such window, or its service is outside your organizations.
403 Not authorized You hold no role in any organization.
403 Operator role or higher required to cancel downtime You are a viewer in that organization.
422 (field details) The id is not between 1 and 2147483647.
500 Failed to cancel downtime The window could not be removed.

Service settings

These endpoints change alarm limits and notification settings. They back the Edit panel on the service page, so their responses are redirects or HTML fragments rather than JSON. See Alarm limits and policies for what the fields mean.

Edit a service

Item Value
Method and path POST /api/v1/services/{service_id}/edit
Role organization administrator in the service's organization
Request form fields below

This call replaces the whole configuration

Every notification setting is taken from this request. A checkbox you omit is saved as off; a number you omit is saved as its default. Limits for the selected channel are replaced by the bands in this request, and blank bands are removed. Send the complete configuration every time.

Required fields: metric_channel and all four of ch_high, cl_low, wh_high, wl_low must be present (each may be blank). A request that omits the channel or any of the four band fields is refused and nothing is saved, because a saved policy contains exactly the bands in the request and a partial set would drop the others.

Field Meaning and validation
name New service name. Blank leaves the name unchanged.
metric_channel Channel whose limits you are setting (for example temp_1). Empty or default means the service-wide limits.
is_percentage on, true or 1 to treat limits as percentage deviation from a setpoint; anything else is absolute.
setpoint_channel, state_channel Related channels used in percentage mode. Ignored otherwise.
ch_high, ch_delay, ch_sustained Critical high limit (number), delay in minutes, sustained minutes.
cl_low, cl_delay, cl_sustained Critical low limit and its minutes.
wh_high, wh_delay, wh_sustained Warning high limit and its minutes.
wl_low, wl_delay, wl_sustained Warning low limit and its minutes.
repeat_interval_minutes Minutes between repeat notifications. Blank or non-numeric saves 60.
send_critical, send_warning, send_acknowledgements, send_recoveries on, true or 1 to enable; omitted means off.
notification_group_id Id of the group to notify. Blank or non-numeric saves no group.
enable_system_notifications on, true or 1 to enable; omitted means off.
system_notification_delay_minutes Blank or non-numeric saves 5.
stale_after_minutes Blank or non-numeric saves 15. 0 disables stale detection. Used only while the device has no uplink interval sent from Pulse; otherwise twice that interval applies.

A band whose limit, delay and sustained values are all blank or zero is not saved. A non-numeric limit is treated as blank; a non-numeric delay or sustained value is treated as 0.

Success: 303 See Other with Location: /services/{service_id}. Pulse retires the previous limits for that channel as a new version (history is kept), writes the notification settings, removes every acknowledgement on the service, re-evaluates the alarm state immediately, and records service.edit in the audit log.

Validation failures also answer 303 See Other, with the message in the Location header as /services/{service_id}?edit_error=<message> (spaces encoded as +). The messages are:

Message When
Incomplete edit submission; no changes were saved metric_channel or any of the four band fields is missing.
Critical high must be strictly greater than warning high Absolute mode.
Critical low must be strictly less than warning low Absolute mode.
All low thresholds must be strictly less than all high thresholds Absolute mode.
For percentage mode, critical high deviation must be strictly greater than warning high deviation Percentage mode.
For percentage mode, critical low deviation must be strictly less than warning low deviation Percentage mode.
Delay, sustained, repeat, stale-after and system-notification-delay must be 0 or more A negative minute value.
Internal error while saving the policy update - see server logs for full details The save failed. Contact Lab Monitors support to investigate.

Only pairs where both limits are given are compared; a missing limit never fails a rule.

Errors with JSON bodies:

Status Message When
403 Not authorized You hold no role in any organization.
404 Service not found or not authorized No such service, it is outside your organizations, or it is archived.
403 Organization administrator role required to edit this service You are a viewer or operator in that organization.
422 (field details) The id is not between 1 and 2147483647.

Request an alarm-settings recommendation

Item Value
Method and path POST /api/v1/services/{service_id}/recommend
Role organization administrator in the service's organization
Request none

Queues an analysis of the service's recent data that proposes alarm limits. The response is 200 with an HTML fragment (an id outside 1 to 2147483647 answers 422 instead); read its text:

Text in the response Meaning
✓ AI recommendation request queued successfully (service 118). Queued. Poll the status endpoint.
A recommendation request is already pending for service 118. One is already queued; no second request was made.
Not authorized You hold no role in any organization.
Service not found or not authorized No such service, or it is outside your organizations.
Insufficient permissions to request AI recommendations You are not an organization administrator there.

Read the recommendation status

Item Value
Method and path GET /api/v1/services/{service_id}/recommendation
Role any role (viewer or higher) in the service's organization
Request none

Answers 200 with an HTML fragment showing one of: a Recommend alarm settings (AI) button (nothing requested), a pending status line, a failure with its reason and a Retry AI recommendation button, or a completed recommendation with the current and proposed bands, the reasoning, and an Apply recommendation to policy_bands button. The button's form carries the result_id you need for the apply call. The web application polls this endpoint every 20 seconds.

Viewers and operators can read the status but cannot request or apply a recommendation. Authorization failures also answer 200: the fragment reads Not authorized when you hold no role in any organization, or Service not found when there is no such service or it is outside your organizations. An id outside 1 to 2147483647 answers 422.

Apply a recommendation

Item Value
Method and path POST /api/v1/services/{service_id}/apply-recommendation
Role organization administrator in the service's organization
Request form field result_id (required, whole number from 1 to 9223372036854775807, taken from the status fragment)

Writes the proposed bands as a new version of the service's limits for the recommended channel, removes every acknowledgement on the service, re-evaluates the alarm state, and records policy.apply_recommendation in the audit log. A result can only be applied to the service it was produced for. Answers 200 with an HTML fragment:

Text in the response Meaning
✓ Applied 4 band(s) from AI rec. Refresh page or re-open edit to see in current bands / manual form. Applied; the number of bands varies.
No such recommendation result. No result with that result_id.
This recommendation belongs to a different service. The result was produced for another service. Nothing changed on either service.
No unapplied result or already applied. That result was applied earlier. A result can be applied once.
Result has no proposed_bands (structured output pending in AI tool). The analysis produced no bands to apply.
Result has no usable proposed_bands (AI output was not a structured list of bands). Cannot apply. The analysis result could not be used.
Not authorized, Service not found, Insufficient permissions to apply recommendations Authorization failures, as for the request call.

A {service_id} outside 1 to 2147483647, a result_id outside 1 to 9223372036854775807, and a missing result_id each answer 422 with field details.

Remove a band

Item Value
Method and path POST /api/v1/services/{service_id}/delete-band
Role organization administrator in the service's organization
Request form fields below
Field Meaning
metric_channel Channel whose limits to change. Empty or default means the service-wide limits.
band_name One of CRITICAL_HIGH, CRITICAL_LOW, WARNING_HIGH, WARNING_LOW (the short forms CRIT_HIGH, WARN_LOW and so on are also accepted).
clear_all true to remove every band on the channel. When band_name is empty, all bands are removed even without clear_all.

Removing a band creates a new version of the channel's limits without it (history is kept), removes every acknowledgement on the service, re-evaluates the alarm state, and records service.band.delete in the audit log. Answers 200 with an HTML fragment (an id outside 1 to 2147483647 answers 422 instead):

Text in the response Meaning
✓ Removed band WARNING_LOW (new policy version). Close/re-open the edit panel or refresh the page to see the form updated with no values for the cleared band(s). One band removed.
✓ Cleared all bands/limits for the channel (new policy version, none configured). Close/re-open the edit panel or refresh the page to see the form updated with no values for the cleared band(s). All bands removed.
✓ Created policy entry for the channel with no limits/delays configured. The channel had no limits yet; an empty entry was created so it shows in the editor.
No active policy found for that channel. You named a band on a channel that has no limits. Nothing changed.
Could not clear the band(s). Please try again. The change could not be saved.
Not authorized, Service not found, Insufficient permissions to edit policy bands Authorization failures. Service not found is also the answer for an archived service.

Health

Check that Pulse is up

Item Value
Method and path GET /api/v1/health
Role none; no token needed
Request none

Success, 200 OK:

{"status": "healthy"}

Use this for uptime monitors. It confirms the web application is answering; it does not report on sensors, gateways or notification delivery. A second public check, GET /health, answers 200 with {"status": "ok", "service": "pulse-interface", "mode": "live", ...} plus descriptive fields intended for Lab Monitors; treat anything beyond status as informational.

Detailed health

Item Value
Method and path GET /api/v1/health/detailed
Role any signed-in user (a token is required)
Request none

Answers 200 with a JSON report on the platform's internal components. It exists for Lab Monitors and its contents may change; use the public check above for your own monitoring. Without a token it answers 401 with {"detail": "Authentication required"}.

Common patterns

The examples assume TOKEN holds a bearer token (see Authentication) and PULSE=https://pulse.labmonitors.com.

Poll for alarms

curl -s "$PULSE/api/v1/alarms" -H "Authorization: Bearer $TOKEN"

Poll no more often than the data changes; once a minute is plenty. Filter on severity and acknowledged in your own code; the endpoint has no query parameters. Remember that services in downtime are absent from the list, not marked.

Acknowledge with a comment

curl -s -X POST "$PULSE/api/v1/alarms/118/acknowledge" \
  -H "Authorization: Bearer $TOKEN" \
  --data-urlencode "comment=Tech dispatched, compressor fault"

Handle 409 as "already handled" rather than as a failure: it means the alarm is no longer active or is already acknowledged at this severity.

Schedule a downtime

Two hours starting now, with times written in UTC and marked with Z so that the account's profile timezone does not matter:

START=$(date -u +%Y-%m-%dT%H:%MZ)
END=$(date -u -d "+2 hours" +%Y-%m-%dT%H:%MZ)

curl -s -o /dev/null -w "%{http_code} %{redirect_url}\n" \
  -X POST "$PULSE/api/v1/downtimes" \
  -H "Authorization: Bearer $TOKEN" \
  -d "service_id=118" -d "start_time=$START" -d "end_time=$END" \
  --data-urlencode "reason=Compressor replacement"

A 303 with a redirect to /services/118 means the window was saved. Any 4xx carries the reason in its JSON body.

Find and cancel it

ID=$(curl -s "$PULSE/api/v1/downtimes" -H "Authorization: Bearer $TOKEN" \
  | python -c "import sys, json; print([d['id'] for d in json.load(sys.stdin) if d['service_id'] == 118][0])")

curl -s -X DELETE "$PULSE/api/v1/downtimes/$ID" -H "Authorization: Bearer $TOKEN"

The list only shows windows that are active now, so a window scheduled for later cannot be looked up this way until it starts; cancel it from the service page in the web application instead.