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#

bash

tendrl-cli contact entities list --limit 50
tendrl-cli contact entities get <name>

Entities are addressed by name. list supports --limit / --offset.

entities create#

bash

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#

bash

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#

bash

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#

bash

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#

bash

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#

bash

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)
bash

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#

bash

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).

bash

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#

bash

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#

bash

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).

bash

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#

bash

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:

bash

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#

bash

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#

bash

tendrl-cli contact alerts test <alert-id>     # fire a test delivery
tendrl-cli contact alerts delete <alert-id> -y

flows#

bash

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#

bash

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#

bash

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).

bash

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#

bash

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
bash

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).

bash

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-in roles and policies

Built-ins (Admin, ReadOnly, DefaultEntity, …) appear in list but can't be fetched by id, edited, or deleted.

audit#

bash

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).

bash

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).