# Warblet > An IoT backend for creators, builders and startup teams: point any device that can send a packet at Warblet over HTTP, MQTT, TCP or UDP, attach a small decoder script, and get named fields on a dashboard. You choose the security level, from open to signed — and Enforce TLS is a separate per-device flag, because who is talking and whether the wire is readable are two different questions. Everything the web portal does is also available to an agent or a script through one account API key — REST, WebSocket tail, and an MCP server. **Warblet was formerly called ChirpIoT (also written Chirp IoT), and very briefly Warble.** Same product, same account, same API. As of 2026-09-01 every hostname below is on `warbletiot.com`; the old `*.chirpiot.com` endpoints are RETIRED and `api.` and `https.` no longer resolve there. The wire protocol is unchanged and keeps its `CHIRP` tokens and `X-Chirp-*` headers — that vocabulary is a protocol, not an address. If you have older notes under any of those names, they still apply apart from the hostnames. Warblet exists because getting a homemade board's bytes onto a chart should not require a certificate authority, a broker cluster, or an IAM policy. A device is anything that can send a packet. A blank ESP32 can be flashed, provisioned and claimed from a browser tab in one session; equally, a Python script on a Raspberry Pi or a C firmware you already wrote is a first-class device. Decoders and routines are short scripts in Starlark (Python-like) that you own, run in a sandbox, and can dry-run before saving. A routine's `specId` sets its trigger scope: bound to a spec, only messages from that spec's devices evaluate it — reads are unaffected, `state.get()` sees every device in the account either way. A routine may also call `ai(question, choices)` to have Claude make one judgement per evaluation, on your own Anthropic key. To be told when a board stops sending, there is no routine to write: turn on its quiet alert (`PATCH /v1/devices/{id} {"quietAlert": true}`, or the `device_quiet_alert` tool) and Warblet learns its rhythm and alerts once when it goes quiet and once when it sends again. REST base: `https://api.warbletiot.com/v1` — JSON, and every endpoint accepts `Authorization: Bearer chk_…` (an account API key from the portal's Settings page) or a Clerk session JWT. Errors are always `{"error":{"code":"not_found|unauthorized|invalid|throttled|unavailable|upstream","message":"…"}}`. MCP endpoint: `https://api.warbletiot.com/mcp` — streamable HTTP, same key. Connect Claude Code with: `claude mcp add --transport http warblet https://api.warbletiot.com/mcp --header "Authorization: Bearer chk_YOUR_KEY"` The MCP tools cover devices, claim codes, specs, decoders, routines and alerts. They deliberately cannot delete anything, manage API keys, or write secrets. If Warblet gets in your way, tell us. `feedback_submit` over MCP, or `POST https://api.warbletiot.com/v1/feedback` with `{kind: "bug"|"feature", title, body, context?, contact?}` and the same key, files a report about Warblet itself. Reach for it when you hit a missing capability, a wrong result or an error here: a tool that cannot do what your user needs, a response that contradicts these docs, a failure you had to work around. Put the tool or endpoint you were using in `context`, and ask your user first if the report would include their data. It answers `{id, status}`; `feedback_list` and `GET /v1/feedback` show what this account has filed. ## Docs - [Docs index](https://warbletiot.com/docs): every page below, grouped by what you are trying to do. - [Send your first packet](https://warbletiot.com/docs/first-packet): the bring-your-own-firmware quickstart — a one-line snippet per transport, what `202 {"status":"unclaimed"}` means, the 72h sighting expiry, the ≥4-character suffix search rule, and search-and-claim (binds at Open, so the device's next tokenless packet still works). - [Connect your agent](https://warbletiot.com/docs/agents): OAuth sign-in from claude.ai, Claude Desktop or ChatGPT, API keys, the MCP tool catalogue, curl equivalents, and a downloadable Claude Code skill. - [Security levels](https://warbletiot.com/docs/security): the auth ladder — `securityLevel: 0` (Open), `1` (Token), `2` (Signed) — the separate Encrypted switch (`enforceTls`), and how to choose. The labels are the words alone (contract v0.29.1); the integers are the wire values. - [Open](https://warbletiot.com/docs/security/level-0): hardware id only, no token. Bench experiments, and the "verify an HMAC in your own decoder" pattern. Gear that can post to a URL can join at Open; a fixed frame aimed at the TCP or UDP door cannot, because those need the `CHIRP1` line first. - [Open starter](https://github.com/Robota-LLC/Warblet-IoT/tree/main/open-starter): the smallest firmware that talks both ways, in MicroPython, one Arduino sketch, or one C file on the sockets API. Every 10 seconds `POST http://http.warbletiot.com/ingest//down` with no body (200 = a command as the body, 204 = none), then `POST http://http.warbletiot.com/ingest/` with the body ` `, so whatever is typed in the device's Message box comes back in the next line. No token, no TLS, no clock; claimed by its id. - [Token](https://warbletiot.com/docs/security/level-1): a per-device token on every message. - [Signed](https://warbletiot.com/docs/security/level-2): token plus an HMAC-SHA256 on every transport, over the signed input ` "\n" "tag=" "\n" ` — both lines always present, empty fields left empty, so a message with no nonce and no tag signs `"\ntag=\n" + payload` (contract v0.31). - [Encrypted](https://warbletiot.com/docs/security/enforce-tls): the Enforce TLS switch — the transport axis, and NOT a level. Per-device `enforceTls` (off by default) can be turned on at Open, Token or Signed and is the only thing that rejects plaintext — confidentiality is enforced, and claimed, only when it is on; until then the platform shows what arrives without going dark. There is no fourth level (contract v0.19): the old "L3" was Signed plus this switch, and two of the five ingest links can carry TLS at all: `https.`, and `mqtt.` on port 8883. `/docs/security/level-3` redirects here. - [Provisioning over USB](https://warbletiot.com/docs/provisioning): CHIRP-PROV v1.6, the four-line UART protocol (ordered Wi-Fi list, Thread datasets, merge-pushes, the board's own `radios=` announce, v1.5's optional `key` — the base64 HMAC signing key, pushed down the same cable as the token — `sig=1` in the announce for firmware that signs, and v1.6's optional `t=` naming the demo the firmware IS; the portal's probe window is 15 s so a board still joining Wi-Fi is never called blank), with MicroPython and Arduino reference implementations. ## Blog - [Blog index](https://warbletiot.com/blog): posts, newest first. - [IoT Development with ChatGPT, Claude, and Warblet](https://warbletiot.com/blog/let-the-agent-writing-your-code-configure-warblet): Close the loop on IoT development with ChatGPT or Claude: write code, configure Warblet, inspect arriving data, and validate the result. ## Tools and pages - [Browser flasher](https://warbletiot.com/flash): flash MicroPython or your own binary to an ESP32 over Web Serial, or a `.bin` to an STM32 over WebUSB DFU (the ROM bootloader, entered with BOOT0), then provision Wi-Fi and a claim code. Nothing to install. STM32F1 has no USB DFU in ROM and is not reachable this way. - [Claude Code skill](https://warbletiot.com/skills/warblet/SKILL.md): the Warblet workflow as a skill file — install at `~/.claude/skills/warblet/SKILL.md`. - [Portal](https://warbletiot.com/app): a dashboard card per device — live fields, sparklines, image and audio frames, and whatever switches, buttons and text boxes its spec declares — plus devices (each with a quiet alert switch, and its recent commands with what became of each), specs, decoders, routines, alerts and settings. - [Report a bug or request a feature](https://warbletiot.com/docs/agents#feedback): the `feedback_submit` tool and `POST /v1/feedback` — how an agent files what it ran into, on its user's behalf. - [Terms of use](https://warbletiot.com/terms) ## Device ingest hosts - `https://https.warbletiot.com/ingest/{hwId}`: HTTP ingest over TLS. POST raw payload bytes; `POST /ingest/{hwId}/down` collects a queued command: `200` with the command as the body, or `204` when none is waiting. A `GET` of that address collects only with the token in the `X-Chirp-Token` header or a signed poll, so an address repeated by a proxy or a browser never takes a command (contract v0.43). Header names are case-insensitive, and this door delivers its custom ones lower-cased (`x-chirp-down`, `x-chirp-budget`), so match them case-insensitively. Optional `X-Chirp-Tag: ` labels the message (— contract v0.29; `[A-Za-z0-9_.-]{1,32}`, an invalid value is a 400 naming the rule). (Every endpoint is named for its transport — v0.12. The old `ingest.` name is RETIRED and no longer serves: it has no certificate, so a request to it dies in the TLS handshake.) - `http://http.warbletiot.com/ingest/{hwId}`: the same thing on port 80 with **no TLS**, for devices without a TLS stack. Use this host rather than sending plain HTTP to `https.` — that one redirects to TLS and naive firmware loses the data. - `mqtt.warbletiot.com:1883`: publish `chirp/{hwId}/up` (or `chirp/{hwId}/up/` to label the message — one extra level, contract v0.29), subscribe `chirp/{hwId}/down`. Username = hardware id, password = token or claim code. Plaintext — readable to anyone on the path. An Open device sets NO password flag; a flag over an empty password is a protocol violation and the broker answers CONNACK 0x82. - `mqtt.warbletiot.com:8883`: MQTTS — the same broker, the same topics and the same credential, with TLS terminated on the relay (contract v0.22). No client certificate, no ALPN, TLS 1.2 minimum. Sessions here are observed as `mqtts` and are the only MQTT sessions that satisfy `enforceTls`; 1883 is not deprecated, so a board with no TLS stack keeps the port it can reach. HTTPS and this are the two TLS paths today. - `tcp.warbletiot.com:7700`: newline-framed. Hello line `CHIRP1 {hwId} {token}` (`-` as the token at Open), then one payload per line (`b64:` prefix for binary). To label messages, open with `CHIRP1T {hwId} {token}` instead and write ` ` on every line, `-` for no tag (contract v0.29). - `udp.warbletiot.com:7701`: datagram prefix `CHIRP1 {hwId} {token} ` followed by raw payload bytes (`-` as the token at Open), or `CHIRP1T {hwId} {token} {tag} ` to label it (`-` for no tag, contract v0.29). No downlink. Both raw doors refuse anything that does not start with the `CHIRP1` line or prefix. ## Optional - [How it works](https://warbletiot.com/#how) - [Pricing](https://warbletiot.com/#pricing)