# Authoring HTTP Device Core parameter files with an AI assistant

This document teaches an AI assistant (Claude, ChatGPT, etc.) everything it needs to
**generate parameter files for the SKAARHOJ HTTP Device Core**. Paste this whole file
into the assistant, then describe the HTTP request you want to control. The assistant
returns one `.json` file per parameter that you **import directly** into the HTTP Core
web UI — no hand-editing required.

> **How to use it (copy/paste prompt):**
> *"Here is the spec for SKAARHOJ HTTP Core parameter files. Using the API described
> below, produce one importable parameter JSON file for each action I list. Follow the
> schema and rules exactly and return each file in its own code block with the file name
> it should be saved as. \<paste this document\> \<paste your API docs / describe the
> endpoints\>"*

---

## 1. What a parameter file is

The HTTP Device Core connects a SKAARHOJ controller to **any device or service with an
HTTP/REST API**. Each **parameter** describes a single HTTP request (method, path, body,
headers) plus how to interpret the response. Parameters belong to a **Model**; every
device assigned to that model exposes the same parameters in Reactor.

A parameter file is a single JSON object. You create it in the web UI by exporting a
parameter, and you load it again with **Import**. An AI assistant can produce the same
JSON from scratch.

### The file name *is* the parameter label

When you import a file, the core sets the parameter's label to the **file name with
`.json` removed**. So save the file as the name you want the parameter to have, e.g.
`Recall Preset.json` → a parameter called *Recall Preset*. The `Label` field inside the
JSON is ignored on import (keep it in sync anyway for readability).

### Where to import

In the HTTP Core web UI:
- **New parameter from a file:** open a Model → **Import parameter** (the import icon at
  the top of the model page, or *Add parameter → Import from file…*). The file becomes a
  brand-new parameter.
- **Overwrite an existing parameter:** open the parameter → **Import** (toolbar). The
  uploaded file replaces that parameter's configuration.

You can select **multiple files at once** to import a whole batch of parameters.

After importing you may be prompted to **Reload Core** to push the new structure to
Reactor. Make all your changes first, then reload once.

---

## 2. File schema

A parameter file is exactly this shape (top-level keys `Info`, `Headers`, `Variables`).
Field names are **case-sensitive**.

```json
{
  "Info": {
    "Label": "string",
    "Description": "string",
    "RequestType": "GET | POST | PUT | PATCH | DELETE | Variable",
    "DoNotUseBaseURL": false,
    "RequestPath": "string",
    "Body": "string",
    "Type": "Trigger | Toggle | String | Integer | Float",
    "StatusRegex": "string",
    "FeedbackType": "None | Confirm on status 2xx | Regex match value | Confirm on regex match",
    "Min": 0,
    "Max": 0,
    "OffVal": "string",
    "OnVal": "string"
  },
  "Headers": {
    "Headers": [
      { "Key": "string", "Value": "string" }
    ]
  },
  "Variables": [
    {
      "Label": "string",
      "Description": "string",
      "Tag": "string",
      "Type": "String | Int | Float | Binary",
      "Min": 0,
      "Max": 1
    }
  ]
}
```

### 2.1 `Info` — the request and parameter behaviour

| Field | Type | Meaning |
|---|---|---|
| `Label` | string | Display name. **Overwritten by the file name on import** — keep them matching. |
| `Description` | string | Shown in Reactor and in the editor. Optional, but recommended. |
| `RequestType` | enum | HTTP method: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, or `Variable`. `Variable` lets the Reactor user pick the method at runtime (adds a "Request Type" meta field). |
| `DoNotUseBaseURL` | bool | `false` → `RequestPath` is appended to the device's BaseURL. `true` → `RequestPath` must be a full absolute URL (`https://…`). |
| `RequestPath` | string | The path (or full URL). May contain `{value}` and `{tag}` placeholders. |
| `Body` | string | Request body for POST/PUT/PATCH. Free text — usually JSON. May contain placeholders. Leave `""` for GET/DELETE. |
| `Type` | enum | The control type Reactor presents (see §3). |
| `StatusRegex` | string | Regular expression run against the response body. The **last capture group** of the match is used (see §5). Empty when unused. |
| `FeedbackType` | enum | How the response is interpreted (see §5). |
| `Min` / `Max` | number | Value range for `Integer`/`Float` types. `0,0` when unused. |
| `OffVal` / `OnVal` | string | For `Toggle`: the literal text sent for off/on. Empty → `false`/`true` (see §3). |

### 2.2 `Headers` — per-parameter HTTP headers

```json
"Headers": { "Headers": [ { "Key": "Content-Type", "Value": "application/json" } ] }
```

- Use `"Headers": { "Headers": null }` (or `[]`) when there are no parameter-specific
  headers.
- These are merged on top of the Model's **Global Headers** and the device's **Header
  Overrides**. Precedence (highest wins): **parameter header → device override → model
  global**.
- For `POST`/`PUT`/`PATCH` with a JSON body, set `Content-Type: application/json`.

### 2.3 `Variables` — `{tag}` placeholders

Every `{tag}` you put in the path, body, or a header value becomes a runtime input the
Reactor user can set. Declare each one here.

| Field | Type | Meaning |
|---|---|---|
| `Label` | string | Display name of the input. |
| `Description` | string | Shown in Reactor. Optional. |
| `Tag` | string | The placeholder name **without braces**. `"id"` matches `{id}` in the path/body/header. Letters and digits only. |
| `Type` | enum | `String`, `Int`, `Float`, or `Binary`. |
| `Min` / `Max` | number | Range for `Int`/`Float`. For `String`/`Binary` use `0`/`1`. |

The placeholder match is **case-insensitive**. Do **not** declare a variable for
`{value}` — that one is built in (see §4).

---

## 3. Parameter types (`Info.Type`)

| `Type` | Reactor control | Sends | Notes |
|---|---|---|---|
| `Trigger` | A button | One request when pressed | No `{value}`. Use for fire-and-forget actions (recall, reboot, next…). |
| `Toggle` | An on/off switch | One request per state change | `{value}` becomes `OnVal` when on and `OffVal` when off. If `OnVal`/`OffVal` are empty, `{value}` is the literal `true`/`false`. |
| `String` | A text field | A request carrying the typed text | `{value}` = the text. |
| `Integer` | A number field | A request carrying the number | Requires `Min`/`Max`. `{value}` = the integer. |
| `Float` | A number field | A request carrying the decimal | Requires `Min`/`Max`. `{value}` = the float. |

**Rules / gotchas**
- `Integer` and `Float` **must** have a non-zero `Min`/`Max` range. (`0,0` is rejected.)
- `Trigger` ignores `Confirm on status 2xx`/`Confirm on regex match` feedback (a button
  has no value to confirm). A `Trigger` *can* return a string via `Regex match value`
  (see "Get Todo" example) — useful to surface a value on a button press.
- `Toggle` with custom `OnVal`/`OffVal` is how you target APIs that expect e.g.
  `"on"`/`"off"`, `1`/`0`, or `enabled`/`disabled`.

---

## 4. Placeholders: `{value}` and `{tag}`

- **`{value}`** — the parameter's own value (the text/number/toggle state). Available for
  every type **except `Trigger`**. Put it wherever the value belongs:
  - Path: `lights/{id}/brightness?level={value}`
  - Body: `{"bri": {value}}`
- **`{tag}`** — a declared variable (see §2.3). Use as many as you need.
- Matching is **case-insensitive** (`{ID}` == `{id}`).
- **Literal braces:** to send a real `{` or `}` (not a placeholder), escape it as `\{` or
  `\}`. The backslash is removed before sending. In JSON that backslash itself must be
  escaped, so write `\\{` and `\\}` in the file.
- The query string is URL-encoded automatically; you don't pre-encode placeholder values.

---

## 5. Feedback (`Info.FeedbackType` + `Info.StatusRegex`)

Feedback decides whether/how the parameter reflects the device's real state back in
Reactor.

| `FeedbackType` | What happens |
|---|---|
| `None` | Fire-and-forget. The value is assumed applied; no response parsing. |
| `Confirm on status 2xx` | The new value is confirmed only if the HTTP status is 200–299. (Ignored for `Trigger`.) |
| `Regex match value` | `StatusRegex` is run on the response body; the **last capture group** becomes the parameter's value. Use to read state back from JSON/XML/text. |
| `Confirm on regex match` | The value is confirmed if `StatusRegex` matches anything in the response. |

**Regex notes**
- Go (RE2) regex syntax. The value used is the **innermost / last capturing group** of
  the first match. Example: `"state"\s*:\s*(\d+)` → captures the number after `"state":`.
- For `Regex match value` the captured text is coerced to the parameter `Type`
  (`true`/`false` for `Toggle`, parsed number for `Integer`/`Float`, raw text for
  `String`/`Trigger`).
- Remember JSON escaping: a regex `"\d+"` is written `"\\d+"` in the file, and a literal
  `"` inside the regex becomes `\"`.

---

## 6. Worked examples (public API: `https://jsonplaceholder.typicode.com/`)

These are real, importable files. The device BaseURL is
`https://jsonplaceholder.typicode.com/`.

### 6.1 `Get Todo.json` — GET + variable + read a value back

A button that fetches `todos/{id}` and surfaces the todo's `title` as the parameter value.

```json
{
  "Info": {
    "Label": "Get Todo",
    "Description": "Fetches a todo item by ID and returns its title as the parameter value.",
    "RequestType": "GET",
    "DoNotUseBaseURL": false,
    "RequestPath": "todos/{id}",
    "Body": "",
    "Type": "Trigger",
    "StatusRegex": "\"title\":\\s*\"([^\"]*)\"",
    "FeedbackType": "Regex match value",
    "Min": 0,
    "Max": 0,
    "OffVal": "",
    "OnVal": ""
  },
  "Headers": { "Headers": null },
  "Variables": [
    { "Label": "Todo ID", "Description": "Which todo to fetch (1-200)", "Tag": "id", "Type": "Int", "Min": 1, "Max": 200 }
  ]
}
```

### 6.2 `Create Post.json` — POST + JSON body + headers + variables

```json
{
  "Info": {
    "Label": "Create Post",
    "Description": "Creates a new post from a title and body.",
    "RequestType": "POST",
    "DoNotUseBaseURL": false,
    "RequestPath": "posts",
    "Body": "{\"title\": \"{title}\", \"body\": \"{body}\", \"userId\": {userId}}",
    "Type": "Trigger",
    "StatusRegex": "",
    "FeedbackType": "None",
    "Min": 0,
    "Max": 0,
    "OffVal": "",
    "OnVal": ""
  },
  "Headers": { "Headers": [ { "Key": "Content-Type", "Value": "application/json; charset=UTF-8" } ] },
  "Variables": [
    { "Label": "Title",   "Description": "Post title",          "Tag": "title",  "Type": "String", "Min": 0, "Max": 1 },
    { "Label": "Body",    "Description": "Post body text",       "Tag": "body",   "Type": "String", "Min": 0, "Max": 1 },
    { "Label": "User ID", "Description": "Author user ID (1-10)","Tag": "userId", "Type": "Int",    "Min": 1, "Max": 10 }
  ]
}
```

### 6.3 `Set Post Title.json` — String value via PUT, confirmed on 2xx

```json
{
  "Info": {
    "Label": "Set Post Title",
    "Description": "Updates the title of an existing post. The new title is the parameter value.",
    "RequestType": "PUT",
    "DoNotUseBaseURL": false,
    "RequestPath": "posts/{id}",
    "Body": "{\"title\": \"{value}\"}",
    "Type": "String",
    "StatusRegex": "",
    "FeedbackType": "Confirm on status 2xx",
    "Min": 0,
    "Max": 0,
    "OffVal": "",
    "OnVal": ""
  },
  "Headers": { "Headers": [ { "Key": "Content-Type", "Value": "application/json; charset=UTF-8" } ] },
  "Variables": [
    { "Label": "Post ID", "Description": "Which post to update (1-100)", "Tag": "id", "Type": "Int", "Min": 1, "Max": 100 }
  ]
}
```

### 6.4 `Publish Post.json` — Toggle with custom on/off values

```json
{
  "Info": {
    "Label": "Publish Post",
    "Description": "Toggles the published flag of a post. Sends true on, false off.",
    "RequestType": "PUT",
    "DoNotUseBaseURL": false,
    "RequestPath": "posts/{id}",
    "Body": "{\"id\": {id}, \"published\": {value}}",
    "Type": "Toggle",
    "StatusRegex": "",
    "FeedbackType": "Confirm on status 2xx",
    "Min": 0,
    "Max": 0,
    "OffVal": "false",
    "OnVal": "true"
  },
  "Headers": { "Headers": [ { "Key": "Content-Type", "Value": "application/json; charset=UTF-8" } ] },
  "Variables": [
    { "Label": "Post ID", "Description": "Which post to publish (1-100)", "Tag": "id", "Type": "Int", "Min": 1, "Max": 100 }
  ]
}
```

### 6.5 `Delete Post.json` — DELETE trigger

```json
{
  "Info": {
    "Label": "Delete Post",
    "Description": "Deletes a post by ID.",
    "RequestType": "DELETE",
    "DoNotUseBaseURL": false,
    "RequestPath": "posts/{id}",
    "Body": "",
    "Type": "Trigger",
    "StatusRegex": "",
    "FeedbackType": "None",
    "Min": 0,
    "Max": 0,
    "OffVal": "",
    "OnVal": ""
  },
  "Headers": { "Headers": null },
  "Variables": [
    { "Label": "Post ID", "Description": "Which post to delete (1-100)", "Tag": "id", "Type": "Int", "Min": 1, "Max": 100 }
  ]
}
```

---

## 7. Checklist for the assistant before returning a file

1. **Valid JSON.** No trailing commas; all strings quoted; braces/brackets balanced.
2. **All keys present** with the exact names and casing from §2 (`Info`, `Headers`,
   `Variables`, and every `Info` sub-field). Missing fields default to empty/zero but it
   is safest to include them all.
3. **Enums are exact** (including spaces): `RequestType`, `Type`, `FeedbackType`,
   variable `Type`. e.g. `"Confirm on status 2xx"`, not `"confirm 2xx"`.
4. **Every `{tag}`** used in path/body/headers has a matching entry in `Variables`
   (except `{value}`).
5. **`{value}` is present** for non-`Trigger` types and absent for `Trigger`.
6. **`Integer`/`Float` have a real `Min`/`Max`** (not `0,0`).
7. **JSON-in-JSON is escaped**: bodies and regexes containing `"` and `\` use `\"` and
   `\\` inside the file's string.
8. **Body is empty** for `GET`/`DELETE`; `Content-Type` header set when a JSON body is
   sent.
9. **File name = intended parameter label**, ending in `.json`. State it clearly.
10. **One file per parameter.** Return each in its own code block, named.

---

## 8. Quick reference — allowed values

- `Info.RequestType`: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `Variable`
- `Info.Type`: `Trigger`, `Toggle`, `String`, `Integer`, `Float`
- `Info.FeedbackType`: `None`, `Confirm on status 2xx`, `Regex match value`,
  `Confirm on regex match`
- `Variables[].Type`: `String`, `Int`, `Float`, `Binary`
- Built-in placeholder: `{value}` (all types except `Trigger`)
- Escape literal braces: `\\{` and `\\}` (in the JSON file)
