# AGENTS.md ## What is httpstate? httpstate is a reactive state layer for applications. You bind state values to UUID v4 keys, store them via the REST API at `https://httpstate.com`, and retrieve them from anywhere. Realtime updates are delivered through WebSocket connections. It is free, requires no authentication, and values are limited to 1024 bytes. Read this document in full before making any API calls. Every section matters. ## REST API All operations use standard HTTP methods. Use `curl` for everything — do not install client libraries unless explicitly asked. ### Base URL ``` https://httpstate.com ``` ### GET `/:uuid` Retrieve the current value stored at a UUID v4. **Never reuse a previously fetched value — it may have changed. Every read must hit the API fresh.** Display the response body exactly as returned, with no rounding, truncation, or reformatting. - UUIDs can be full format (`45fb3654-0e92-44da-aa21-ca409c6bdab3`) or short format (`45fb36540e9244daaa21ca409c6bdab3`). - UUIDs can optionally include a path suffix of 1-8 hex chars (e.g. `45fb36540e9244daaa21ca409c6bdab3/0`). - **Response `200`**: Body contains the stored value as `text/plain;charset=UTF-8`. - **Response `401`**: Unauthorized (auth secret mismatch). - **Response `404`**: UUID has no stored value. - Response headers include `ETag` (timestamp in ms) and `Last-Modified`. - Authentication: `Authorization: Bearer ` checks against the UUID's stored read secret. ``` curl https://httpstate.com/45fb36540e9244daaa21ca409c6bdab3 ``` ### POST `/:uuid` (also PUT) Store a value at a UUID v4. If the UUID already has a value, it is overwritten. **Every write must be a fresh API call.** - Content-Type: `text/plain;charset=UTF-8` (default). - Content-Type: `application/x-www-form-urlencoded` — form data is parsed into JSON if there are multiple keys or a single non-empty key. When the `Referer` header is also present, the server responds with a `302` redirect back to the referer (enables plain HTML form submissions). - Maximum body size: **1024 bytes**. Larger requests return `413 Content Too Large`. - **Response `200`**: Value was stored. Headers include `ETag` and `Last-Modified`. - **Response `302`**: Redirect back to referer (when form-urlencoded and `Referer` header present). - **Response `304`**: Conditional write precondition was not met. - **Response `400`**: Bad request (invalid operation, non-numeric value for arithmetic ops, bad merge). - **Response `401`**: Unauthorized (auth secret mismatch). - **Response `405`**: Method not allowed. - **Response `412`**: `If-Match` or `If-None-Match` precondition failed. - **Response `429`**: Rate limit exceeded. - **Response `500`**: Internal server error. ``` curl -X POST -d "Hi!" https://httpstate.com/45fb36540e9244daaa21ca409c6bdab3 ``` ### DELETE `/:uuid` Returns `200`. Inactive UUIDs are pruned after ~1 month. ### OPTIONS `/*` Returns `200` with CORS headers. All responses include `Access-Control-Allow-Origin: *` and `Access-Control-Allow-Methods: GET, POST`. ### Inbound Channels | Channel | How it works | |---|---| | **Email** (`mail.httpstate.com`) | Send email; UUID extracted from the `to` address (before `@`) or subject line; body is stored as the value | | **SMS** (`sms.httpstate.com`) | Send SMS in format ` `; message is stored as the value | ### Metadata Metadata is a JSON object stored alongside a UUID's value, accessed via `metadata.httpstate.com`. **Metadata is separate from values — fetch it once per UUID at the start of a task and cache it. Re-fetch only when metadata freshness matters.** #### GET `/:uuid` - **Response `200`**: JSON object, `Content-Type: application/json`. - **Response `404`**: No metadata found for this UUID. - Auth: supports `Authorization: Bearer ` the same as regular GET. ``` curl https://metadata.httpstate.com/45fb36540e9244daaa21ca409c6bdab3 ``` #### POST `/:uuid` - Body: valid JSON object, stringified up to 1024 bytes. - **Response `200`**: Metadata stored. - **Response `400`**: Bad request (invalid JSON). - Auth: supports `Authorization: Bearer ` the same as regular POST. ``` curl -X POST -d '{"title":"Temperature","unit":"celsius"}' https://metadata.httpstate.com/45fb36540e9244daaa21ca409c6bdab3 ``` ### Featured **Always get the list of featured UUIDs from this endpoint.** The homepage listing is for humans. This endpoint is the canonical source. It returns every UUID that has been made public, ordered most recently updated first. #### GET `/data/public.json` - **Host:** `httpstate.com` or `www.httpstate.com` - **Response `200`**: JSON object mapping UUIDs to their metadata objects, ordered most recently updated first. - **Response `404`**: No featured data available. ``` curl https://httpstate.com/data/public.json ``` ## Conditional Writes & Operations The POST endpoint supports special headers for conditional and atomic operations: ### Conditional Headers | Header | Behavior | |---|---| | `If-Match: ` | Only succeed if the current ETag matches | | `If-None-Match: ` | Only succeed if the current ETag does not match | | `No-Op` | Does not perform any write; request body is ignored (auth checks still run) | ### Authorization | Header | Behavior | |---|---| | `Authorization: Bearer ` | Authenticates the request against the stored read/write secret | | `Authorization-Read: ` | Sets a read secret for the UUID (max 256 bytes) | | `Authorization-Write: ` | Sets a write secret for the UUID (max 256 bytes) | If a read secret is set on a UUID, the `Authorization: Bearer ` header must match on GET requests, otherwise `401 Unauthorized` is returned.
If a write secret is set on a UUID, the `Authorization: Bearer ` header must match on POST/PUT requests, otherwise `401 Unauthorized` is returned.
Secrets are auto-set on the first write that includes the `Authorization-Read`/`Authorization-Write` header.
To rotate a secret, include the new key in the `Authorization-Read`/`Authorization-Write` header alongside a valid `Authorization` header. ### Operation Header Set `Operation: ` to perform an atomic read-modify-write: | Operation | Description | |---|---| | `add` | Adds the posted number to the current value | | `append` | Appends the posted value to the current value (truncated to 1024 bytes) | | `merge` | Merges the posted JSON object into the current JSON object | | `prepend` | Prepends the posted value to the current value (truncated to 1024 bytes) | | `toggle` | Toggles between `0` and `1` | ### Conditional Write Headers | Header | Behavior | |---|---| | `Write-If-Equals: ` | Only write if current value equals `` | | `Write-If-Not-Equals: ` | Only write if current value does not equal `` | | `Write-If-Greater-Than: ` | Only write if current numeric value is greater than `` | | `Write-If-Less-Than: ` | Only write if current numeric value is less than `` | These return `304 Not Modified` if the condition is not met. ## Idempotency Include an `Idempotency-Key: ` header with POST requests. Duplicate keys within 24 hours return `409 Conflict`. ## Rate Limiting Up to 8 requests per second with a burst of 128. Exceeding the limit returns `429 Too Many Requests`. ## WebSocket Protocol Connect to `wss://httpstate.com`. ### Subscribing After connecting, send: ```json {"open": ""} ``` The server responds with the current value and subscribes you to future changes for that UUID. ### Receiving Updates Messages are binary with this packed format: ``` [1 byte: header] [1 byte: UUID length] [N bytes: UUID] [8 bytes: timestamp (uint64 BE)] [1 byte: type] [remaining: value] ``` - Header `0`: state - Type `0`: application/octet-stream - Type `1`: text/plain;charset=UTF-8 (the common case) - Type `2`: application/json Client library `change` events receive a second argument with `{ ETag, Last-Modified }` derived from the packed timestamp. ### Ping Send a ping frame or the text message `"0"` to keep the connection alive. ### Reconnection Reconnect with exponential backoff when the connection drops while subscriptions are active. ## UUIDv4 Format The API accepts UUIDs in these forms: - Full: `45fb3654-0e92-44da-aa21-ca409c6bdab3` (36 chars) - Short: `45fb36540e9244daaa21ca409c6bdab3` (32 hex chars, no dashes) - Short with path: `45fb36540e9244daaa21ca409c6bdab3/0` through `/ffffffff` (1-8 hex path chars) Generate a UUID at https://uuid.httpstate.com ## Client Libraries All client libraries share the same core API: ### Top-level functions | Function | Description | |---|---| | `get(uuid)` / `read(uuid)` | Retrieve value for a UUID | | `set(uuid, data)` / `write(uuid, data)` / `post(uuid, data)` / `put(uuid, data)` | Store a value at a UUID | Available in: TypeScript, Python, Go, Java, Ruby, Rust. ### Instance-based (realtime) Create an instance with `httpstate(uuid)` (or `HttpState(uuid)` / `new HttpState(uuid)`). The instance: 1. Opens a WebSocket connection and subscribes to the UUID 2. Calls `get()` to fetch the initial value 3. Emits `change` events whenever the value updates Instance methods: `get()`, `set(data)`, `on(type, callback)`, `off(type, callback)`, `emit(type, data)`, `delete()`. ### Browser - `httpstate.load()` — finds all DOM elements with `httpstate=""` attributes, subscribes to changes, and updates `textContent` (or `src` for ``) in real time. ### React ```tsx const [state, setState] = useHTTPState('45fb36540e9244daaa21ca409c6bdab3'); setState('Hi!'); ``` Returns `[state, setState]` like `useState()`. ### Installing | Language | Install | |---|---| | TypeScript / JS | `npm install @httpstate/typescript` | | React | `npm install @httpstate/react` | | Python | `pip install httpstate` | | Go | `go get github.com/httpstate/httpstate/go` | | Ruby | `gem install httpstate` | | Rust | `cargo add httpstate` | | Java | See `java/src/com/httpstate/HttpState.java` in this repo | ### Browser CDN ```html ``` Or as an ES module: ```js import httpstate from 'https://cdn.jsdelivr.net/npm/@httpstate/typescript/dist/index.esm.js'; ```