Host API
Every function a plugin can call: http, kv, log, json, crypto, money, time, url and the ctx table.
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
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.comfor subdomains), onlyhttps. - 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-cookiedropped. http.fetchreturnsnil, messageinstead 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.
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:
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).