# Host API

> Every function a plugin can call: http, kv, log, json, crypto, money, time, url and the ctx table.

Source: https://shopfa.st/docs/plugins/host-api

Hooks are called as `plugin.<hook>(ctx, input)`. `ctx` is the same for every kind:

| Field | |
| --- | --- |
| `ctx.store` | `{ id, name, handle, currency }` |
| `ctx.plugin` | `{ id, name, version }` |
| `ctx.settings` | Values for the settings in the manifest, secrets decrypted (only inside the run) |
| `ctx.test` | `true` in the test runner |

JSON `null` members of `input` are absent (they read as `nil`), like most Lua JSON libraries.

## http

```lua
local res, err = http.fetch({
  url = "https://api.example.com/v1/payments",
  method = "POST",                      -- GET by default
  headers = { authorization = "Bearer " .. ctx.settings.api_key },
  body = { amount = 2499 },             -- a table is sent as JSON; a string as is
})
if not res then error("network: " .. err) end
if not res.ok then error("HTTP " .. res.status .. ": " .. res.body) end
local data = res.json()                 -- res.status, res.headers, res.body, res.ok
```

- Only hosts in `permissions.http` (`api.example.com`, or `*.example.com` for subdomains), only `https`.
- Names that resolve to private, loopback, link-local or metadata addresses are refused, and the connection goes to
  the address that was checked.
- 5 s timeout, 64 KB request body, 256 KB response, redirects returned (not followed), `set-cookie` dropped.
- `http.fetch` returns `nil, message` instead of raising, so you decide what a failure means.
- Calls are made from the hook's main thread; calling it inside a coroutine raises.

## kv

A private store per plugin (needs `"kv": true`): up to 1,000 keys of 16 KB.

```lua
kv.set("invoice:" .. id, json.encode({ amount = 2499 }), 86400)  -- optional TTL in seconds
local raw = kv.get("invoice:" .. id)                             -- string or nil
local n = kv.incr("issued", 2)                                    -- atomic counter
kv.delete("invoice:" .. id)
local keys = kv.list("invoice:", 50)                              -- by prefix
```

## log

`log.debug`, `log.info`, `log.warn`, `log.error` and `print` take any values (tables are logged as JSON). Up to 200
lines of 2,000 characters per run, shown with the run in the workbench.

## json

`json.encode(value)`, `json.decode(text)`, `json.null`, and `json.array(t)` to encode a table as `[]` even when it is
empty. Object keys are sorted, so the same table always encodes to the same bytes (handy for signatures).

## crypto

| Function | |
| --- | --- |
| `crypto.hmac(alg, key, data, enc)` | `alg`: sha1, sha256, sha384, sha512, md5; `enc`: hex (default), base64, base64url |
| `crypto.hash(alg, data, enc)`, `crypto.sha256(data, enc)` | Digests |
| `crypto.equal(a, b)` | Constant-time comparison: use it for every signature check |
| `crypto.random_hex(bytes)` | Cryptographically random, up to 256 bytes |
| `crypto.uuid()` | A random UUID |
| `crypto.base64_encode/decode`, `crypto.base64url_encode`, `crypto.hex_encode/decode` | Encodings |

## money

Amounts are integers in minor units, everywhere. These helpers never use floats for an amount:

```lua
money.format(2499, "USD")        --> "$24.99"
money.to_major(2499, "USD")      --> "24.99"   (a string, for provider APIs that want decimals)
money.from_major("24.99", "USD") --> 2499      (refuses "24.999")
money.percent(1999, 250)         --> 50        (basis points, rounded half up)
money.split(100, 3)              --> {34, 33, 33}
money.exponent("JPY")            --> 0
```

## time and url

`time.now()` (ms), `time.unix()` (seconds), `time.iso(ms)`; `url.encode`, `url.decode`, `url.query(table)` and
`url.parse_query(text)`.
