> For the complete documentation index, see [llms.txt](https://developer.relaypro.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.relaypro.com/integrations/telit-devicewise.md).

# Telit DeviceWISE

## Overview

[Telit deviceWISE Cloud](https://www.telit.com/iot-platforms-overview/telit-devicewise-cloud/) is an industrial IoT platform that models equipment as **Things** carrying **properties**, **attributes**, and **alarms**. Its **Cloud Triggers** engine watches for changes to those objects and runs a sequence of actions in response.

One of those actions, `http.client`, can call any publicly reachable HTTPS endpoint. That is all you need to turn a PLC fault into a voice alert in a technician's hand: deviceWISE detects the alarm, posts to the [Relay Alerts API](https://developer.relaypro.com/docs/relay-alerts-api), and every device in the target Workflow Group speaks the message until someone taps the talk button to accept the job.

This page builds a complete, working **machine-down** alert.

```mermaid
flowchart LR
    A["PLC / equipment"] --> C["DeviceWISE<br/>Cloud Trigger<br/>alarm.change"]
    C --> E["Relay Server <br/>http.client action<br/>POST /alerts"]
    E --> F["Relay devices<br/>in Workflow Group"]
    F --> G["Acknowledged"]
```

### Why Alerts and not Broadcasts

Relay offers two notification types. Use **Alerts** for machine-down. An alert repeats every 30 seconds until a human acknowledges it and records who accepted the job, which is exactly the accountability a maintenance call needs. A [broadcast](https://developer.relaypro.com/docs/relay-broadcasts-api) plays once with no acknowledgement — fine for shift announcements, wrong for downtime.

***

## Prerequisites

<table><thead><tr><th width="180">Side</th><th>Requirement</th></tr></thead><tbody><tr><td>Relay</td><td>An <strong>enterprise plan</strong> account. API keys are not available on lower tiers.</td></tr><tr><td>Relay</td><td>A <strong>Workflow Group</strong> containing the users who should receive machine-down calls.</td></tr><tr><td>Relay</td><td>Your <strong>subscriber ID</strong> and an <strong>API key</strong>.</td></tr><tr><td>Telit</td><td>A deviceWISE Cloud organization with the <strong>Triggers</strong> feature enabled. If <code>/triggers</code> returns <em>"Triggers are not available. Please contact support to enable this feature,"</em> Triggers is not provisioned on your org — open a ticket with Telit before going further.</td></tr><tr><td>Telit</td><td>A <strong>Thing</strong> representing the machine, publishing an alarm when the machine faults.</td></tr><tr><td>Telit</td><td>A user with <strong>Developer</strong> permissions in the org.</td></tr></tbody></table>

{% hint style="info" %}
This guide assumes your gateway or edge application already publishes a machine-state alarm to the Thing. Modeling that alarm is a deviceWISE-side exercise; see Telit's [Adding a trigger](https://docs.devicewise.com/Content/Products/Management_Portal/Developer/Triggers/Adding-a-trigger.htm) walkthrough for a worked example of alarm publishing.
{% endhint %}

***

{% stepper %}
{% step %}

## Create the Relay Workflow Group

In [Relay Dash](https://dash.relaypro.com/), open **Incidents** in the left-hand navigation.

Select **Workflow Groups**, then **Create Workflow Group**.

Name the group — this guide uses `maintenance`.

Set the type to **No Type**.

Select the new group, choose **Edit Membership**, and add the users who should be called.

The group name is what you will put in the `groups` array of the API payload.
{% endstep %}

{% step %}

## Collect your Relay credentials

| Value           | Where to find it                                                                              |
| --------------- | --------------------------------------------------------------------------------------------- |
| `subscriber_id` | Relay Dash → **Help** → **Diagnostic Logs** → **Server Details**, listed under **Subscriber** |
| `apikey`        | Relay Dash → **Help** → [**API Keys**](https://dash.relaypro.com/api-keys) → create a key     |

{% hint style="warning" %}
Create a **dedicated API key for this integration** rather than reusing one. If the Telit org is ever compromised, or a contractor leaves with Developer access, you want to revoke one key without breaking every other integration you run.
{% endhint %}
{% endstep %}

{% step %}

## Prove the Relay side works

Fire the alert by hand first:

```sh
export subscriber_id=your_subscriber_id
export apikey=your_api_key

curl -X POST \
  -H "Authorization: RelayApiKey $apikey" \
  -H "Content-Type: application/json" \
  "https://api.relaypro.com/subscribers/$subscriber_id/api/v1/alerts" \
  -d '{"notification": "Test alert from deviceWISE integration", "groups": ["maintenance"]}' \
  -i
```

A successful call returns `HTTP/2 200`, and a task ID:

```json
{"task_id":"P9CThKfTsEbJx23trAArHCD"}
```

Devices in `maintenance` should now be speaking the message every 30 seconds. Tap the talk button on one to acknowledge, or cancel it from the terminal:

```sh
curl -X DELETE \
  -H "Authorization: RelayApiKey $apikey" \
  "https://api.relaypro.com/subscribers/$subscriber_id/api/v1/alerts/P9CThKfTsEbJx23trAArHCD"
```

{% hint style="success" %}
If this curl works and the Telit trigger does not, the problem is in the trigger — not in Relay.&#x20;
{% endhint %}
{% endstep %}

{% step %}

## Create the Cloud Trigger

In the deviceWISE Cloud portal, go to **Developer** → **Triggers** → **New Trigger**.

Enter the trigger metadata:

| Field           | Value                                                                    |
| --------------- | ------------------------------------------------------------------------ |
| **Name**        | `Machine down → Relay alert`                                             |
| **Description** | `Voice-alerts the maintenance group when a machine reports a down alarm` |
| **Tags**        | `relay`, `maintenance` (optional, but makes triggers findable at scale)  |
| {% endstep %}   |                                                                          |

{% step %}

## Configure the `alarm.change` event

Right-click the **Start** node on the canvas to open **Add trigger event**, set **Event type** to `alarm.change`, and fill in the form.

The [`alarm.change` event](https://docs.devicewise.com/Content/Products/Management_Portal/Developer/Triggers/Events/alarm.change-event.htm) fires whenever an alarm changes state.&#x20;

### Time in condition is important to configure

Manufacturing lines microstop constantly. Without debounce, a five-second jam pages four technicians, the alert repeats every 30 seconds, and within a week your maintenance team mutes their Relays.

**Time in condition** delays execution until the alarm has held the state for the specified number of seconds. The semantics are specific:

* If the alarm is republished at the **same** state during the window, the trigger still executes at the end of the window.
* If the alarm **changes** state during the window — the machine recovers — execution is **cancelled**.

Sixty seconds is a reasonable opening value for a production line. Tune it to your actual microstop distribution: long enough that self-clearing faults never page anyone, short enough that real downtime is not sitting unreported.

Click **Add**.
{% endstep %}

{% step %}

## Add the `http.client` action

Expand the **Networking** category in the trigger actions list, then click and drag [**http.client**](https://docs.devicewise.com/Content/Products/Management_Portal/Developer/Triggers/Actions/http.client-action.htm) onto the canvas.

<figure><img src="https://2387635908-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FL7dimlztFKGWYQIaJ9DP%2Fuploads%2FwfW1EUDnjxqH3lDlsynK%2FScreenshot%202026-09-14%20at%2011.47.51%E2%80%AFAM.png?alt=media&amp;token=01e1d718-8d45-4a04-b92b-d04f1e3924da" alt=""><figcaption></figcaption></figure>

### Action parameters

| Parameter        | Value                                                                   |
| ---------------- | ----------------------------------------------------------------------- |
| **Method**       | `POST`                                                                  |
| **URL**          | `https://api.relaypro.com/subscribers/YOUR_SUBSCRIBER_ID/api/v1/alerts` |
| **Content type** | `application/json`                                                      |
| **Timeout**      | `30`                                                                    |
| **TLS cert**     | *(blank)*                                                               |
| **TLS key**      | *(blank)*                                                               |

Replace `YOUR_SUBSCRIBER_ID` with the subscriber ID from Step 2.

{% hint style="info" %}
**Timeout** is the deviceWISE HTTP request timeout, and it accepts **0–60 seconds** (default 30). It is unrelated to the Relay `timeout` key in the payload below, which controls how long the alert keeps sounding on devices.

The **TLS cert** and **TLS key** fields are for client-certificate authentication. Relay authenticates with an API key, so leave both empty.
{% endhint %}

### Headers

Add one header:

| Key             | Value                      |
| --------------- | -------------------------- |
| `Authorization` | `RelayApiKey YOUR_API_KEY` |

{% hint style="warning" %}
The literal string `RelayApiKey` and a space must precede the key. `Authorization: YOUR_API_KEY` returns 401.
{% endhint %}

### Body

Select **Body: Raw** and paste the following. Note that **Body: Raw takes precedence over Body: Form** — if you populate both, the form is ignored.

```json
{
  "notification": "Machine down. $(event.thing.name) has faulted. Please respond.",
  "groups": ["maintenance"],
  "quorum": 1,
  "on_ack": "Accepted. You are assigned to $(event.thing.name).",
  "on_quorum": "Another technician has taken this call.",
  "timeout": 600,
  "on_timeout": "Machine down call was not acknowledged.",
  "name": "machine-down"
}
```

Click **Add**.
{% endstep %}

{% step %}

## Wire the routes and activate

The `http.client` node has two routing points: **left/red is the failure route**, **right/green is the success route**.

Drag a route from the `alarm.change` event triangle to the `0.http.client` action.

Expand the **End** category and drag a **Success** node onto the canvas.

Drag the green success route from `0.http.client` to the Success node.

{% hint style="info" %}
A trigger needs **at least one Success route** before it will save. Any branch that does not terminate at a Success node is treated as a failure.

When connecting routes, drag over the destination node until it turns grey, then release.
{% endhint %}

Set **Reporting mode**. The default is **On failure**, which logs only failed executions. During rollout set it to **On** to capture every execution — note that **On** reverts to **On failure** automatically after 30 days.

Click **Add** to save the trigger.

Click **Start** to activate it. A saved trigger does nothing until it is started.

The Triggers list now shows a **Success count** and **Failure count** for the trigger. Those two numbers are your first diagnostic.
{% endstep %}
{% endstepper %}

***

## Payload reference

### Relay alert keys

`notification` and `groups` are required. Everything else is optional.

| Key            | Type          | Default                      | Behavior                                                                                         |
| -------------- | ------------- | ---------------------------- | ------------------------------------------------------------------------------------------------ |
| `notification` | string        | —                            | Spoken on every device in the group via text-to-speech.                                          |
| `groups`       | array         | —                            | Workflow Group names or URNs to alert.                                                           |
| `quorum`       | integer       | all devices must acknowledge | Cancels remaining alerts after this many acknowledgements. **Set this to `1` for machine-down.** |
| `on_ack`       | string        | nothing spoken               | Spoken on the device that acknowledged.                                                          |
| `on_quorum`    | string        | nothing spoken               | Spoken on devices that had not yet acknowledged once quorum is met.                              |
| `timeout`      | integer (sec) | `300`                        | Cancels remaining alerts after this long.                                                        |
| `on_timeout`   | string        | nothing spoken               | Spoken on devices that never acknowledged, after timeout.                                        |
| `name`         | string        | `alerts`                     | Controls serial/concurrent alerting behavior and appears in analytics events for correlation.    |

Leaving `quorum` at its default means the alert keeps sounding on every other device until **all** of them acknowledge — not needed for a machine-down call where one technician takes the job. Set `quorum: 1`.

### deviceWISE event variables available in the body

From the `alarm.change` event:

| Variable                    | Type    | Contents                                |
| --------------------------- | ------- | --------------------------------------- |
| `$(event.thing.key)`        | String  | Key of the Thing that fired the trigger |
| `$(event.thing.name)`       | String  | Human-readable name of the Thing        |
| `$(event.thing.type)`       | String  | Name of the Thing definition            |
| `$(event.alarm.key)`        | String  | Alarm key                               |
| `$(event.alarm.name)`       | String  | Alarm name                              |
| `$(event.alarm.msg)`        | String  | Alarm message                           |
| `$(event.alarm.state)`      | Integer | Numeric alarm state                     |
| `$(event.alarm.string)`     | String  | String form of the alarm state          |
| `$(event.alarm.ts)`         | String  | Alarm timestamp                         |
| `$(event.alarm.corrId)`     | String  | Correlation ID                          |
| `$(event.location.lat)`     | Float   | Latitude of the Thing                   |
| `$(event.location.lng)`     | Float   | Longitude of the Thing                  |
| `$(event.location.speed)`   | Integer | Speed of the Thing                      |
| `$(event.location.heading)` | Float   | Heading of the Thing                    |

Two global variables are available in any action: `$(trigger.exec_time)` and `$(time.now)`. On a failure route you also get `$(trigger.lastErrorMsg)` and `$(trigger.lastErrorCode)`.

{% hint style="warning" %}
**Write for text-to-speech, not for a log file.**

`$(event.thing.key)` is typically a machine-readable key like `press_07_lineB`. Spoken aloud, that is unintelligible. Use `$(event.thing.name)` in the `notification` string allows for more intellible TTS.&#x20;

For the same reason, avoid interpolating `$(event.alarm.msg)` into the spoken message unless you control what your gateway writes there. A message field containing an alarm code, a stack trace, or a double quote will produce either gibberish speech or — see the next warning — a 400.
{% endhint %}

{% hint style="danger" %}
**Variable substitution is textual, and the body is JSON.**

If an interpolated value contains a double quote, a backslash, or a newline, the substituted body is no longer valid JSON and Relay rejects the request with a 400. Values that originate in a PLC string tag or an operator-entered alarm message are the usual culprits.
{% endhint %}

***

For a full execution history, see Telit's [Viewing Trigger Report](https://docs.devicewise.com/Content/Products/Management_Portal/Developer/Triggers/Viewing-Trigger-Report.htm).

***

## Reference

### Telit deviceWISE Cloud

* [Cloud Trigger Events](https://docs.devicewise.com/Content/Products/Management_Portal/Developer/Triggers/Events/Cloud-Trigger-Events.htm)
* [`alarm.change` event](https://docs.devicewise.com/Content/Products/Management_Portal/Developer/Triggers/Events/alarm.change-event.htm)
* [Cloud Trigger Actions](https://docs.devicewise.com/Content/Products/Management_Portal/Developer/Triggers/Actions/Cloud-Trigger-Actions.htm)
* [`http.client` action](https://docs.devicewise.com/Content/Products/Management_Portal/Developer/Triggers/Actions/http.client-action.htm)
* [Adding a trigger](https://docs.devicewise.com/Content/Products/Management_Portal/Developer/Triggers/Adding-a-trigger.htm)
* [Scheduling a trigger](https://docs.devicewise.com/Content/Products/Management_Portal/Developer/Triggers/Scheduling-a-Trigger.htm)

### Relay

* [Alerts overview](https://developer.relaypro.com/docs/relay-alerts-api)
* [Triggering an Alert](https://developer.relaypro.com/docs/relay-alerts-api/triggering-an-alert)
* [Customizing Your Alerts](https://developer.relaypro.com/docs/relay-alerts-api/customizing-your-alerts)
* [Advanced Configuration](https://developer.relaypro.com/docs/relay-alerts-api/advanced-configuration)
* [Broadcasts](https://developer.relaypro.com/docs/relay-broadcasts-api)
