Tendrl CLI: Contact Commands
Everything under tendrl-cli contact uses your Contact account API key (TENDRL_API_KEY). Create and update commands take a JSON body via --data '<json>' (or --file body.json, or --data - for stdin); the field tables below document each body. Fields marked required are enforced by the API.
entities#
Devices and services registered on your account.
entities list / get#
tendrl-cli contact entities list --limit 50
tendrl-cli contact entities get <name>
Entities are addressed by name. list supports --limit / --offset.
entities create#
tendrl-cli contact entities create -d '{"name":"greenhouse-7","role":"DefaultEntity","mqtt":true}'
| Field | Type | Notes |
|---|---|---|
name |
string | required — unique on the account |
role |
string | required — DefaultEntity for devices, or a custom role |
mqtt |
bool | MQTT-capable device; required for reboot, provisioning, and key rotation |
enabled |
bool | default true |
service |
string | bind messages to a service schema for validation |
metadata |
object | free-form key/value metadata |
directoryId |
string | place in a fleet directory |
description |
string | |
pinnedSdkVersion |
string | pin the SDK version served to this device |
The response includes the entity's API key, shown once — that key is what the device itself uses (TENDRL_KEY in the SDKs).
entities update#
tendrl-cli contact entities update greenhouse-7 -d '{"role":"DefaultEntity","metadata":{"site":"north"}}'
Same fields as create. The API validates the full body, so include role even when only changing other fields.
entities delete / reboot / heartbeat / state#
tendrl-cli contact entities delete greenhouse-7 # prompts; -y to skip
tendrl-cli contact entities reboot greenhouse-7 # MQTT devices only
tendrl-cli contact entities heartbeat greenhouse-7 # last heartbeat
tendrl-cli contact entities state greenhouse-7 # the entity's state table
entities provision-payload / rotate-key#
tendrl-cli contact entities provision-payload greenhouse-7
tendrl-cli contact entities rotate-key greenhouse-7
Both require an MQTT-enabled entity. rotate-key returns the new entity key once.
entities boards#
tendrl-cli contact entities boards
Supported board profiles for MicroPython devices (memory tiers, capabilities).
messages#
Read, search, and export — publishing is the device SDKs' job.
messages list / get#
tendrl-cli contact messages list --entity greenhouse-7 --start 2026-09-01T00:00:00Z --limit 20
tendrl-cli contact messages get <message-id>
| Option | Meaning |
|---|---|
--entity |
filter by entity name |
--start / --end |
ISO 8601 window |
--limit / --offset |
paging (limit caps at 1000) |
messages search#
tendrl-cli contact messages search -d '{
"filters": [{"field": "entity_name", "op": "eq", "value": "greenhouse-7"}],
"timeRange": "24h",
"sort": "created_at", "order": "desc", "limit": 50
}'
| Field | Type | Notes |
|---|---|---|
filters |
array | {field, op, value} — ops: eq, neq, contains, in, gt, lt, gte, lte, exists |
or |
array | nested search groups OR-ed together |
timeRange |
string | 1h, 6h, 24h, 7d, 30d, all |
sort / order |
string | column name; asc or desc |
limit / offset |
int | paging |
messages export#
tendrl-cli contact messages export --format csv --out fleet-messages.csv
files#
Account file storage and device file transfer (see file transfer for the delivery model).
tendrl-cli contact files list
tendrl-cli contact files upload ./config.json --dest greenhouse-7 --tags config
tendrl-cli contact files get <file-id>
tendrl-cli contact files download <file-id> --out ./config.json
tendrl-cli contact files rescan <file-id> # re-run the Surface scan
tendrl-cli contact files delete <file-id> -y
upload takes --dest <entity> (delivery target) and optional --tags a,b. File ids in commands are the transfer_id from files list. Two transfer semantics to know: a transferred file's bytes are deleted once the recipient downloads them (the receipt row remains), and files delete applies to gallery clips — transfers clean themselves up on download instead.
fanouts#
Entity groups — one publish reaches every member.
fanouts create#
tendrl-cli contact fanouts create -d '{"name":"all-sensors","role":"DefaultEntity","entityIds":["greenhouse-7"]}'
| Field | Type | Notes |
|---|---|---|
name |
string | required |
role |
string | required |
entityIds |
array | required, at least one — entity names or resource paths |
tag |
string | route messages carrying this tag into the group |
description |
string |
fanouts add / remove / publish / delete#
tendrl-cli contact fanouts add all-sensors greenhouse-8 # bare names are resolved for you
tendrl-cli contact fanouts remove all-sensors greenhouse-8
tendrl-cli contact fanouts publish all-sensors -d '{"data":{"cmd":"calibrate"}}'
tendrl-cli contact fanouts delete all-sensors -y
publish takes {"data": {...}} — the payload delivered to every member.
directories#
The fleet tree. Directories are addressed by id (get one from directories list).
tendrl-cli contact directories create -d '{"name":"west-farm","parentId":""}'
tendrl-cli contact directories move <dir-id> -d '{"parentId":"<other-dir-id>"}'
tendrl-cli contact directories delete <dir-id> -y # must be empty first
Create/move body: name (create only, required) and parentId (empty string = top level).
dashboards#
tendrl-cli contact dashboards list
tendrl-cli contact dashboards get <dashboard-id>
tendrl-cli contact dashboards fields --service env-monitor
dashboards query#
The batched widget-data endpoint — up to 48 queries per call, each failing independently:
tendrl-cli contact dashboards query -d '{
"range": "24h",
"compare": true,
"queries": [
{"id": "w1", "service": "env-monitor", "field": "data.temp", "agg": "avg", "bucket": "15m"},
{"id": "w2", "service": "env-monitor", "field": "data.temp", "agg": "avg", "groupBy": "entity"},
{"id": "w3", "service": "env-monitor", "field": "data.status", "agg": "count_by_value"}
]
}'
Top level: range (or from/to), compare, bucket. Per query: id, service, field (a data.* path), agg (avg, count_by_value, latest_by_entity, …), bucket, groupBy. Full body semantics: dashboards guide.
alerts#
alerts create / update#
tendrl-cli contact alerts create -d '{
"name": "sensor-offline",
"enabled": true,
"trigger": "entity_offline",
"selector": {"mode": "ids", "ids": ["greenhouse-7"]},
"cooldownSecs": 300,
"notify": true
}'
| Field | Type | Notes |
|---|---|---|
name |
string | required; immutable after create |
trigger |
string | required — entity_offline, entity_online, or message_condition |
selector |
object | {mode, ids, tags, directoryPath} — which entities the rule watches |
condition |
object | {field, op, value} — required when trigger is message_condition |
cooldownSecs |
int | minimum seconds between firings |
tags |
array | route the alert into flows via tags |
notify |
bool | email notification; at least one of tags or notify: true is required |
enabled |
bool |
update validates like create — send the full rule, not a partial patch.
alerts test / delete#
tendrl-cli contact alerts test <alert-id> # fire a test delivery
tendrl-cli contact alerts delete <alert-id> -y
flows#
tendrl-cli contact flows list
tendrl-cli contact flows get <flow-id>
tendrl-cli contact flows connectors
tendrl-cli contact flows connector-templates
tendrl-cli contact flows test-connector -d '{...}' # body = a connector config from the templates
services#
Payload schemas (services guide).
services create / validate#
tendrl-cli contact services create -d '{
"name": "env-monitor",
"description": "Greenhouse telemetry",
"dynamicActions": [{
"name": "message:publish",
"requiredFields": ["temp"],
"rules": [{"field": "temp", "operator": "between", "value": [-40, 60]}],
"tags": ["telemetry"]
}]
}'
| Field | Type | Notes |
|---|---|---|
name |
string | required |
description |
string | |
dynamicActions |
array | per-action validation: name, requiredFields, rules ({field, operator, value, message}), tags |
forwardValidation |
bool | forward validation failures |
validate takes the same body and checks it without saving — use it before create.
insights#
tendrl-cli contact insights summary # account overview
tendrl-cli contact insights usage # data usage vs plan limits
tendrl-cli contact insights messages | entities | validation | flows
keys#
Account API keys (distinct from entity keys — see API keys).
tendrl-cli contact keys create -d '{"name":"ci-key","role":"ReadOnly"}'
tendrl-cli contact keys list
tendrl-cli contact keys rotate <key-id>
tendrl-cli contact keys delete <key-id> -y
Create body: name (required), role (required — Admin, ReadOnly, FleetOperator, or a custom role), description, enabled, exp (RFC 3339; default 2 years). The token is returned once on create and rotate.
users, roles, policies#
tendrl-cli contact users list
tendrl-cli contact users invite [email protected] --role ReadOnly
tendrl-cli contact users invites # pending invites
tendrl-cli contact users revoke-invite [email protected] # by invited email
tendrl-cli contact users set-role <user-id> FleetOperator
tendrl-cli contact users remove <user-id> -y
tendrl-cli contact roles create -d '{"name":"deploy-bot","description":"CI","policy":"ReadOnly"}'
Role body: name (required), description, policy (required — the policy this role grants).
tendrl-cli contact policies create -d '{
"name": "list-only",
"policy": {"Statement": [
{"Action": ["entity:ListEntities", "entity:GetEntities"], "Resource": "*", "Effect": "Allow"}
]}
}'
Policy body: name (required), description, parentPolicyId, and policy.Statement[] with Action (list of IAM actions), Resource, Effect (Allow/Deny), optional Condition. Action names are listed under IAM.
Built-ins (Admin, ReadOnly, DefaultEntity, …) appear in list but can't be fetched by id, edited, or deleted.
audit#
tendrl-cli contact audit list --limit 50
tendrl-cli contact audit export --out audit.csv
Standard plan and up.
deployments#
OTA app-code deployments (deployments guide).
tendrl-cli contact deployments create ./main.py ./lib/sensors.py --name v1.2 --entry main
tendrl-cli contact deployments list
tendrl-cli contact deployments get <deploy-id>
tendrl-cli contact deployments deploy <deploy-id> # roll out to targets
create uploads local files (multipart): positional file paths, --name, --entry (entry module; defaults to the device's app_entry), --no-scan to skip the malware scan (default: scanned with your account's file-transfer profile).
Tendrl