> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coreflux.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Soracom

> Client-mode REST API connector for Soracom: manage IoT SIMs (activate, suspend, speed class, tags, groups), read data usage, Harvest Data and Harvest Files, configure Beam, Funnel and Harvest per group, send HTTP, ping and SMS downlinks, manage event handlers and LwM2M devices from MQTT through the Soracom API, plus an opt-in webhook receiver for Beam forwarding and event handlers

Technical Preview

## Overview

[Soracom](https://soracom.io) is an IoT connectivity platform: IoT SIMs for cellular devices (Air), data forwarding to clouds and servers (Beam, Funnel, Funk, Unified Endpoint), data and file storage (Harvest Data, Harvest Files), event handlers and LwM2M device management (Inventory). These connectors connect the Coreflux broker to the Soracom API, so plant logic on MQTT can watch and control the SIMs of field gateways and machines, read their data usage and stored data, reconfigure forwarding, and reach devices behind the cellular network with downlink HTTP, ping and SMS.

* **SoracomApi** (`SoracomRestApi.lot`) is a client connector on `https://g.api.soracom.io` (global coverage) with 44 events from the [Soracom API reference](https://developers.soracom.io/en/api/).
* **SoracomWebhookReceiver** (`cloud-iot_soracom_webhook.lot`, opt-in, server mode) receives device data that [Soracom Beam](https://developers.soracom.io/en/docs/beam/) forwards over HTTP and event handler web requests, so device data and SIM events reach MQTT without polling.

Every endpoint below matches Soracom's published OpenAPI definition, and every event was checked against the live API (see Status).

> [!TIP]
>
> For Japan coverage SIMs, change `BASE_ADDRESS` to `https://api.soracom.io`. The endpoints are the same, but each coverage holds its own SIMs.

## Authentication

> [!WARNING]
>
> An AuthKey grants API access as its user. Create the AuthKey on a SAM user whose permissions cover only the events you use. Keep it in Hub secrets, map it in with `GET SECRET`, and never publish it, the API key or the token on a topic other clients can read.

Soracom uses a two-step session:

1. Publish `{"body": {"authKeyId": "...", "authKey": "...", "tokenTimeoutSeconds": 3600}}` on `cloud-iot/soracom/token/create` (`Auth`, `POST /v1/auth`). The response on `cloud-iot/soracom/token/create/response` carries `apiKey`, `token` and `operatorId`.
2. Every other event takes `"api_key"` and `"token"` in its MQTT payload and sends them as `X-Soracom-API-Key` and `X-Soracom-Token`.

Tokens expire after `tokenTimeoutSeconds` (180 to 172800 seconds, default 86400), so call `Auth` again before then. `Auth` also accepts a SAM user (`operatorId`, `userName`, `password`) or the root account (`email`, `password`), but AuthKeys are the right choice for a broker. Suggested secrets: `SORACOM_AUTH_KEY_ID` and `SORACOM_AUTH_KEY`.

A wrong or expired API key or token returns `403` with `{"code":"AGW0011","message":"Invalid API Key or Token"}`. A wrong AuthKey on `Auth` returns `401` with `{"code":"AUM0029","message":"Invalid AuthKey supplied."}`.

## Payload conventions

* **Ids.** `"sim_id"` is the SIM ID (`simId` in `ListSims` and `GetSim`), `"imsi"` a subscriber IMSI, `"group_id"` and `"handler_id"` UUIDs, `"device_id"` a Soracom Inventory device id. LwM2M events take `"object_id"`, `"instance_id"` and `"lwm2m_resource_id"` (for example `3303`, `0`, `5700`).
* **Query strings.** Events with `?{query}` take a pre-encoded query string. Send `""` for none. Usage reports require `from`, `to` (Unix seconds) and `period` (`month`, `day` or `minutes`). Harvest Data, session and status history take `from` and `to` in milliseconds.
* **Bodies.** Write events and downlinks send `"body"` unchanged.
* **Paging.** List events return up to `limit` items. Soracom returns the next-page key in the `x-soracom-next-key` response header, to pass back as `last_evaluated_key`. The connector publishes the response body, so prefer a large `limit` or narrower filters (`SearchSims`, `ListSimsInGroup`) when the header is not available.

## Authentication

| Event | Request | Topic | Notes |
| - | - | - | - |
| Auth | `POST /v1/auth` | `cloud-iot/soracom/token/create` | Auth smoke: exchange an AuthKey ID + secret (or SAM operatorId/userName/password, or root email/password) for apiKey + token + operatorId; tokenTimeoutSeconds 180-172800 (default 86400) |

**Auth** (`cloud-iot/soracom/token/create`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "body": {
    "authKeyId": "<authkey id>",
    "authKey": "<authkey secret>",
    "tokenTimeoutSeconds": 3600
  }
}
```

## IoT SIMs

| Event | Request | Topic | Notes |
| - | - | - | - |
| ListSims | `GET /v1/sims?{query}` | `cloud-iot/soracom/sim/list` | Catalog: IoT SIMs (paged); query limit=100\&last\_evaluated\_key=\<simId> (send "" for none) |
| GetSim | `GET /v1/sims/{sim_id}` | `cloud-iot/soracom/sim/get` | One IoT SIM: status, speedClass, sessionStatus (online, IMEI, cell, ueIpAddress), groupId, tags, profiles, expiry |
| SearchSims | `GET /v1/query/sims?{query}` | `cloud-iot/soracom/sim/search` | Search SIMs by name, group, imsi, iccid, msisdn, tag, status, session\_status (ONLINE / OFFLINE), imei ... (search\_type and \| or) |
| ActivateSim | `POST /v1/sims/{sim_id}/activate` | `cloud-iot/soracom/sim/activate` | Catalog: set the SIM to active (billing starts, data allowed) **(write)** |
| DeactivateSim | `POST /v1/sims/{sim_id}/deactivate` | `cloud-iot/soracom/sim/deactivate` | Set the SIM to inactive (no data; basic fee still charged) **(write)** |
| SuspendSim | `POST /v1/sims/{sim_id}/suspend` | `cloud-iot/soracom/sim/suspend` | Suspend the SIM (for example a lost or stolen device) **(write)** |
| SetSimToStandby | `POST /v1/sims/{sim_id}/set_to_standby` | `cloud-iot/soracom/sim/standby` | Put a ready SIM in standby (becomes active on first connection) **(write)** |
| UpdateSimSpeedClass | `POST /v1/sims/{sim_id}/update_speed_class` | `cloud-iot/soracom/sim-speed-class/update` | Change the speed class; body \{"speedClass": "s1.minimum"} (s1.minimum \| s1.slow \| s1.standard \| s1.fast \| s1.4xfast \| s1.8xfast \| u1.standard \| u1.slow, per plan) **(write)** |
| PutSimTags | `PUT /v1/sims/{sim_id}/tags` | `cloud-iot/soracom/sim-tags/update` | Add or update SIM tags; body \[\{"tagName": "name", "tagValue": "porto-line1-gateway"}] **(write)** |
| SetSimGroup | `POST /v1/sims/{sim_id}/set_group` | `cloud-iot/soracom/sim-group/set` | Move the SIM into a group (the group's Air / Beam / Funnel / Funk / Harvest settings then apply); body \{"groupId": "..."} **(write)** |
| UnsetSimGroup | `POST /v1/sims/{sim_id}/unset_group` | `cloud-iot/soracom/sim-group/unset` | Remove the SIM from its group **(write)** |
| ListSimSessionEvents | `GET /v1/sims/{sim_id}/events/sessions?{query}` | `cloud-iot/soracom/sim-session-event/list` | Session history (Created / Modified / Deleted with cell, IMEI, RAT); query from / to (ms), limit |
| ListSimStatusHistory | `GET /v1/sims/{sim_id}/statuses/history?{query}` | `cloud-iot/soracom/sim-status-history/list` | Status operation history of the SIM (who changed status and when) |

**SearchSims** (`cloud-iot/soracom/sim/search`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key": "<api key>",
  "token": "<token>",
  "query": "session_status=ONLINE&tag=line1&search_type=and&limit=100"
}
```

**UpdateSimSpeedClass** (`cloud-iot/soracom/sim-speed-class/update`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key": "<api key>",
  "token": "<token>",
  "sim_id": "8942310222000000000",
  "body": {
    "speedClass": "s1.minimum"
  }
}
```

**PutSimTags** (`cloud-iot/soracom/sim-tags/update`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key": "<api key>",
  "token": "<token>",
  "sim_id": "8942310222000000000",
  "body": [
    {
      "tagName": "name",
      "tagValue": "porto-line1-gateway"
    },
    {
      "tagName": "line",
      "tagValue": "1"
    }
  ]
}
```

## Usage reports

| Event | Request | Topic | Notes |
| - | - | - | - |
| GetSimAirStats | `GET /v1/stats/air/sims/{sim_id}?{query}` | `cloud-iot/soracom/sim-usage/get` | Catalog: data usage of one SIM (upload / download bytes and packets per period); query from, to (Unix seconds) and period=month\|day\|minutes (all required) |
| GetGroupAirStats | `GET /v1/stats/air/groups/{group_id}?{query}` | `cloud-iot/soracom/group-usage/get` | Data usage of all SIMs in a group; query from, to (Unix seconds), period (required) |
| GetHarvestStats | `GET /v1/stats/harvest/subscribers/{imsi}?{query}` | `cloud-iot/soracom/harvest-usage/get` | Harvest Data write counts of a subscriber (IMSI); query from, to, period (required) |
| GetBeamStats | `GET /v1/stats/beam/subscribers/{imsi}?{query}` | `cloud-iot/soracom/beam-usage/get` | Beam request counts per entry point of a subscriber; query from, to, period (required) |
| GetFunnelStats | `GET /v1/stats/funnel/subscribers/{imsi}?{query}` | `cloud-iot/soracom/funnel-usage/get` | Funnel request counts of a subscriber; query from, to, period (required) |

**GetSimAirStats** (`cloud-iot/soracom/sim-usage/get`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key": "<api key>",
  "token": "<token>",
  "sim_id": "8942310222000000000",
  "query": "from=1790812800&to=1791244800&period=day"
}
```

## Groups (Air, Beam, Funnel, Funk, Harvest settings)

| Event | Request | Topic | Notes |
| - | - | - | - |
| ListGroups | `GET /v1/groups?{query}` | `cloud-iot/soracom/group/list` | Groups (paged); query tag\_name / tag\_value / tag\_value\_match\_mode / limit (send "" for none) |
| GetGroup | `GET /v1/groups/{group_id}` | `cloud-iot/soracom/group/get` | One group with its full configuration per namespace (SoracomAir, SoracomBeam, SoracomFunnel, SoracomFunk, SoracomHarvest ...) |
| CreateGroup | `POST /v1/groups` | `cloud-iot/soracom/group/create` | Create a group; body \{"tags": \{"name": "porto-line1"}, "configuration": \{"SoracomHarvest": \{"enabled": true}}} **(write)** |
| PutGroupConfiguration | `PUT /v1/groups/{group_id}/configuration/{namespace}` | `cloud-iot/soracom/group-config/update` | Set Beam / Funnel / Funk / Harvest / Air / UnifiedEndpoint parameters of a group; namespace SoracomBeam with key [http://beam.soracom.io:8888/](http://beam.soracom.io:8888/) (or udp\://beam.soracom.io:23080) forwards device data to SoracomWebhookReceiver; SoracomHarvest \[\{"key": "enabled", "value": true}] turns on Harvest Data **(write)** |
| PutGroupTags | `PUT /v1/groups/{group_id}/tags` | `cloud-iot/soracom/group-tags/update` | Add or update group tags; body \[\{"tagName": "site", "tagValue": "porto"}] **(write)** |
| ListSimsInGroup | `GET /v1/groups/{group_id}/sims?{query}` | `cloud-iot/soracom/group-sim/list` | SIMs in a group; query status\_filter / speed\_class\_filter / limit (send "" for none) |

**CreateGroup** (`cloud-iot/soracom/group/create`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key": "<api key>",
  "token": "<token>",
  "body": {
    "tags": {
      "name": "porto-line1"
    },
    "configuration": {
      "SoracomHarvest": {
        "enabled": true
      },
      "SoracomAir": {
        "metadata": {
          "enabled": true
        }
      }
    }
  }
}
```

**PutGroupConfiguration** (`cloud-iot/soracom/group-config/update`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key": "<api key>",
  "token": "<token>",
  "group_id": "3c9a1f2e-7b4d-4e8a-9c21-5f0d2b6a8e11",
  "namespace": "SoracomBeam",
  "body": [
    {
      "key": "http://beam.soracom.io:8888/",
      "value": {
        "name": "Coreflux",
        "destination": "https://broker.example.com/soracom/webhook/beam",
        "enabled": true,
        "addSubscriberHeader": true,
        "addSimIdHeader": true,
        "addSignature": false
      }
    }
  ]
}
```

## Harvest Data

| Event | Request | Topic | Notes |
| - | - | - | - |
| GetSimData | `GET /v1/sims/{sim_id}/data?{query}` | `cloud-iot/soracom/harvest-sim-data/get` | Harvest Data entries sent by one SIM (time ms, contentType, content); query from / to (ms), sort, limit, last\_evaluated\_key |
| GetDataEntries | `GET /v1/data/{resource_type}/{resource_id}?{query}` | `cloud-iot/soracom/harvest-data/get` | Harvest Data of any resource; resource\_type Sim \| Subscriber \| Device \| LoraDevice \| SigfoxDevice \| SoraCam, resource\_id the SIM id / IMSI / device id |
| ListDataResources | `GET /v1/data/resources?{query}` | `cloud-iot/soracom/harvest-resource/list` | Resources that have Harvest Data (resourceType, resourceId, lastModifiedTime); send query "" for none |

**GetSimData** (`cloud-iot/soracom/harvest-sim-data/get`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key": "<api key>",
  "token": "<token>",
  "sim_id": "8942310222000000000",
  "query": "from=1790812800000&to=1791244800000&sort=desc&limit=100"
}
```

## Harvest Files

| Event | Request | Topic | Notes |
| - | - | - | - |
| FindFiles | `GET /v1/files?{query}` | `cloud-iot/soracom/harvest-file/find` | Find Harvest Files by path prefix; query scope=private\|public and prefix (required) |
| ListFiles | `GET /v1/files/{scope}/{file_path}/?{query}` | `cloud-iot/soracom/harvest-directory/list` | Files and folders in a Harvest Files directory (file\_path without leading or trailing slash) |
| GetFile | `GET /v1/files/{scope}/{file_path}` | `cloud-iot/soracom/harvest-file/get` | Download a Harvest Files object (text / JSON content suits MQTT; large binaries do not) |
| PutFile | `PUT /v1/files/{scope}/{file_path}` | `cloud-iot/soracom/harvest-file/upload` | Upload a JSON file to Harvest Files (scope private), e.g. a config file devices fetch from harvest-files.soracom.io; body is the file content **(write)** |

**PutFile** (`cloud-iot/soracom/harvest-file/upload`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key": "<api key>",
  "token": "<token>",
  "scope": "private",
  "file_path": "config/line1.json",
  "body": {
    "setpoint": 180,
    "line": "1"
  }
}
```

## Downlink to devices

| Event | Request | Topic | Notes |
| - | - | - | - |
| SendDownlinkHttp | `POST /v1/sims/{sim_id}/downlink/http` | `cloud-iot/soracom/downlink-http/send` | Catalog: HTTP request from Soracom to the device on its SIM IP (no public IP or VPN needed); body \{"method", "path", "port", "ssl", "headers", "body" (string)}; response carries the device's status, headers and body **(write)** |
| SendDownlinkPing | `POST /v1/sims/{sim_id}/downlink/ping` | `cloud-iot/soracom/downlink-ping/send` | Reachability check: ICMP ping to the device (max 5 requests, timeout max 3 s); read-only |
| SendSmsToSim | `POST /v1/sims/{sim_id}/send_sms` | `cloud-iot/soracom/sms/send` | Send an SMS to the SIM (wake-up or command); encodingType 1 = GSM 7-bit (160 chars), 2 = UCS-2 (70 chars) **(write)** |

**SendDownlinkHttp** (`cloud-iot/soracom/downlink-http/send`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key": "<api key>",
  "token": "<token>",
  "sim_id": "8942310222000000000",
  "body": {
    "method": "POST",
    "path": "/api/setpoint",
    "port": 8080,
    "ssl": false,
    "headers": {
      "Content-Type": "application/json"
    },
    "body": "{\"setpoint\": 180}"
  }
}
```

**SendSmsToSim** (`cloud-iot/soracom/sms/send`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key": "<api key>",
  "token": "<token>",
  "sim_id": "8942310222000000000",
  "body": {
    "encodingType": 1,
    "payload": "reboot"
  }
}
```

## Event handlers

| Event | Request | Topic | Notes |
| - | - | - | - |
| ListEventHandlers | `GET /v1/event_handlers?{query}` | `cloud-iot/soracom/event-handler/list` | Event handlers; query target=operator\|imsi\|sim\|group (send "" for none) |
| GetEventHandler | `GET /v1/event_handlers/{handler_id}` | `cloud-iot/soracom/event-handler/get` | One event handler: target, ruleConfig, actionConfigList, status |
| CreateEventHandler | `POST /v1/event_handlers` | `cloud-iot/soracom/event-handler/create` | Create an event handler (rule on status, speed class, traffic volume, session ...); an ExecuteWebRequestAction pointed at SoracomWebhookReceiver pushes it to MQTT **(write)** |
| UpdateEventHandler | `PUT /v1/event_handlers/{handler_id}` | `cloud-iot/soracom/event-handler/update` | Replace an event handler (send the full definition, e.g. status inactive to mute it) **(write)** |

**CreateEventHandler** (`cloud-iot/soracom/event-handler/create`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key": "<api key>",
  "token": "<token>",
  "body": {
    "name": "porto-sim-status",
    "description": "SIM status changes to Coreflux",
    "status": "active",
    "targetGroupId": "3c9a1f2e-7b4d-4e8a-9c21-5f0d2b6a8e11",
    "ruleConfig": {
      "type": "SimStatusAttributeRule",
      "properties": {
        "inactiveTimeoutDateConst": "NEVER",
        "targetStatus": "inactive"
      }
    },
    "actionConfigList": [
      {
        "type": "ExecuteWebRequestAction",
        "properties": {
          "executionDateTimeConst": "IMMEDIATELY",
          "url": "https://broker.example.com/soracom/webhook/event-handler",
          "httpMethod": "POST",
          "contentType": "application/json",
          "headers": "{\"X-Coreflux-Secret\":\"<shared secret>\"}",
          "body": "{\"simId\":\"${simId}\",\"imsi\":\"${imsi}\",\"oldStatus\":\"${oldStatus}\",\"newStatus\":\"${newStatus}\"}"
        }
      }
    ]
  }
}
```

## Soracom Inventory (LwM2M devices)

| Event | Request | Topic | Notes |
| - | - | - | - |
| ListInventoryDevices | `GET /v1/devices?{query}` | `cloud-iot/soracom/inventory-device/list` | Soracom Inventory (LwM2M) devices; query tag\_name / tag\_value / limit (send "" for none) |
| ReadDeviceResource | `GET /v1/devices/{device_id}/{object_id}/{instance_id}/{lwm2m_resource_id}?{query}` | `cloud-iot/soracom/inventory-resource/get` | Read one LwM2M resource from the device, e.g. 3303/0/5700 (temperature sensor value); query model=true adds the object model |
| WriteDeviceResource | `PUT /v1/devices/{device_id}/{object_id}/{instance_id}/{lwm2m_resource_id}` | `cloud-iot/soracom/inventory-resource/update` | Write an LwM2M resource, e.g. 3308/0/5900 (set point value); body \{"value": 180} **(write)** |
| ExecuteDeviceResource | `POST /v1/devices/{device_id}/{object_id}/{instance_id}/{lwm2m_resource_id}/execute` | `cloud-iot/soracom/inventory-resource/execute` | Execute an LwM2M resource, e.g. 3/0/4 (device reboot); optional body \{"value": "\<argument>"} **(write)** |

**ReadDeviceResource** (`cloud-iot/soracom/inventory-resource/get`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key": "<api key>",
  "token": "<token>",
  "device_id": "d-0123456789abcdefghij",
  "object_id": "3303",
  "instance_id": "0",
  "lwm2m_resource_id": "5700",
  "query": "model=false"
}
```

**WriteDeviceResource** (`cloud-iot/soracom/inventory-resource/update`)

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key": "<api key>",
  "token": "<token>",
  "device_id": "d-0123456789abcdefghij",
  "object_id": "3308",
  "instance_id": "0",
  "lwm2m_resource_id": "5900",
  "body": {
    "value": 180
  }
}
```

## Audit logs

| Event | Request | Topic | Notes |
| - | - | - | - |
| GetApiAuditLogs | `GET /v1/audit_logs/api?{query}` | `cloud-iot/soracom/audit-log/list` | Soracom API call audit log (who called what, from where); query api\_kind, from\_epoch\_ms, to\_epoch\_ms, limit |

## Webhook receiver

`SoracomWebhookReceiver` listens on port `9007` and publishes each delivery on MQTT unchanged:

| Path | Soracom source | MQTT topic |
| - | - | - |
| `/soracom/webhook/beam` | Beam HTTP entry point (`http://beam.soracom.io:8888/`) or Unified Endpoint over HTTP | `cloud-iot/soracom/webhook-beam/received` |
| `/soracom/webhook/beam-udp` | Beam UDP → HTTP or TCP → HTTP entry point (`beam.soracom.io:23080`), body `{"payload": "<base64>"}` | `cloud-iot/soracom/webhook-beam-udp/received` |
| `/soracom/webhook/event-handler` | Event handler `ExecuteWebRequestAction` | `cloud-iot/soracom/webhook-event-handler/received` |

1. **Beam.** Configure the group with `PutGroupConfiguration` (namespace `SoracomBeam`) or in the User Console. Use key `http://beam.soracom.io:8888/` with destination `https://<public host>/soracom/webhook/beam`, or key `udp://beam.soracom.io:23080` (or `tcp://beam.soracom.io:23080`) with destination `https://<public host>/soracom/webhook/beam-udp`. Turn on the IMSI and SIM ID headers and the signature header with a pre-shared key. Devices then send to `http://uni.soracom.io` (Unified Endpoint) or the Beam entry point. Only `POST` is received.
2. **Event handlers.** Add an `ExecuteWebRequestAction` (see `CreateEventHandler`) with `url` `https://<public host>/soracom/webhook/event-handler`, `httpMethod` `POST`, `contentType` `application/json`, a shared-secret header and a body template such as `{"simId":"${simId}","imsi":"${imsi}","newStatus":"${newStatus}"}`.
3. Soracom calls the URL from its cloud. Put a reverse proxy or tunnel with a public TLS certificate in front of port `9007`, have it check `X-Soracom-Signature` or the shared-secret header, and forward to `http://<broker>:9007/soracom/webhook/...`. The connector itself does not check headers, and headers are not forwarded to MQTT.

> [!WARNING]
>
> Deploying the receiver opens an HTTP port on the broker. Deploy it only where you need push delivery.

> [!TIP]
>
> Soracom Beam also has an MQTT entry point that can forward device MQTT messages straight to a Coreflux broker. That path needs an MQTT bridge, not this REST connector.

## Examples

Read one day of data usage for a SIM:

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key": "<api key>",
  "token": "<token>",
  "sim_id": "<sim id>",
  "query": "from=1790812800&to=1791244800&period=day"
}
```

Throttle a SIM that is using too much data:

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{
  "api_key": "<api key>",
  "token": "<token>",
  "sim_id": "<sim id>",
  "body": {
    "speedClass": "s1.minimum"
  }
}
```

Helper actions (refresh the session every 12 hours, keep the key and token in the broker with `KEEP TOPIC`, which is never published to MQTT, poll a SIM, send a set point to a device over HTTP downlink, fan event handler calls out to plant topics):

```lot theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ACTION SoracomLogin
ON EVERY 12 HOURS DO
    SET "kid" WITH GET SECRET "SORACOM_AUTH_KEY_ID"
    SET "key" WITH GET SECRET "SORACOM_AUTH_KEY"
    PUBLISH TOPIC "cloud-iot/soracom/token/create" WITH { "body": { "authKeyId": {kid}, "authKey": {key}, "tokenTimeoutSeconds": 86400 } }

DEFINE ACTION SoracomKeepSession
ON TOPIC "cloud-iot/soracom/token/create/response" DO
    SET "apikey" WITH GET JSON "apiKey" IN PAYLOAD AS STRING
    SET "token" WITH GET JSON "token" IN PAYLOAD AS STRING
    KEEP TOPIC "internal/soracom/api-key" WITH {apikey}
    KEEP TOPIC "internal/soracom/token" WITH {token}

DEFINE ACTION SoracomPollLineSimStatus
ON EVERY 1 HOUR DO
    SET "apikey" WITH GET TOPIC "internal/soracom/api-key" AS STRING
    SET "token" WITH GET TOPIC "internal/soracom/token" AS STRING
    SET "sim" WITH GET SECRET "SORACOM_SIM_ID"
    PUBLISH TOPIC "cloud-iot/soracom/sim/get" WITH { "api_key": {apikey}, "token": {token}, "sim_id": {sim} }

DEFINE ACTION SoracomSendSetpoint
ON TOPIC "porto/line1/setpoint" DO
    SET "apikey" WITH GET TOPIC "internal/soracom/api-key" AS STRING
    SET "token" WITH GET TOPIC "internal/soracom/token" AS STRING
    SET "sim" WITH GET SECRET "SORACOM_SIM_ID"
    SET "sp" WITH PAYLOAD AS STRING
    PUBLISH TOPIC "cloud-iot/soracom/downlink-http/send" WITH { "api_key": {apikey}, "token": {token}, "sim_id": {sim}, "body": { "method": "POST", "path": "/api/setpoint", "port": 8080, "headers": { "Content-Type": "text/plain" }, "body": {sp} } }

DEFINE ACTION SoracomSimEventToPlant
ON TOPIC "cloud-iot/soracom/webhook-event-handler/received" DO
    SET "sim" WITH (GET JSON "simId" IN PAYLOAD AS STRING)
    PUBLISH TOPIC "plant/soracom/sim/" + {sim} + "/event" WITH PAYLOAD
```

> [!NOTE]
>
> The helper actions keep the API key and token on retained `internal/soracom/...` topics. Restrict those topics with broker ACLs so only the broker's own actions can read them.

## Writes

Write events: ActivateSim, DeactivateSim, SuspendSim, SetSimToStandby, UpdateSimSpeedClass, PutSimTags, SetSimGroup, UnsetSimGroup, CreateGroup, PutGroupConfiguration, PutGroupTags, PutFile, SendDownlinkHttp, SendSmsToSim, CreateEventHandler, UpdateEventHandler, WriteDeviceResource, ExecuteDeviceResource. Activating a SIM starts billing, and status, speed class and group changes take effect on the live connection. `SendDownlinkHttp` and `ExecuteDeviceResource` act on the device itself, and `SendSmsToSim` is charged per message. `SendDownlinkPing` is read-only. No preset deletes anything or terminates a SIM.

## Not packaged

Every delete (SIMs, groups, tags, configuration, files, event handlers, devices), SIM termination and registration, virtual SIM creation, Arc, Napter, Krypton, SoraCam, Lagoon, Query Intelligence, billing and payment, users, roles and credential sets, Sigfox and LoRaWAN device APIs, and the Japan host (change `BASE_ADDRESS`). Harvest Data is written by devices through Unified Endpoint, not through the API.

## Status

Technical preview, blocked on the authenticated live test. Every event reaches `g.api.soracom.io`, and an invalid credential is rejected on all 44 events (`403 AGW0011`, or `401 AUM0029` on `Auth`). A SAM user AuthKey and a test SIM are still needed for the read and write round trip.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.