Functions SDK reference
Every axis.* method available inside a function: records, analytics, stores, PDFs, messaging, work queues and calendars.
Every function can use a global object named axis to read and write workspace data, run reports, call a connected store, make PDFs, send messages, work queues and manage calendars. This page lists every method, its arguments and what it returns.
How a function is shaped
A function defines a global handler that receives the input and returns a JSON value. It can be a normal or an async function.
async function handler(input) {
const open = axis.records.search("invoices", {
filters: [{ field: "status", op: "eq", value: "open" }],
limit: 100,
});
console.log("open invoices:", open.total);
return { open: open.total, first: open.records[0] ? open.records[0].id : null };
}
- The return value must be JSON-serialisable. Returning nothing gives
null. - Throwing an error ends the run with that message in
error. - Every
axis.*call is synchronous. It returns the value directly (noawaitneeded, though harmless). Useawaitonly for your own promises. - A failed call throws an
Errorwhose message says why (for examplepermission denied). Wrap calls intry/catchif you want to carry on. - The sandbox has no
fetch, norequire, no timers, no file system and no network. Reach other systems throughaxis.marketplace.request, or have a flow call an HTTP endpoint. console.log,info,warnanderrorare captured and returned with the result.
Permissions
A function acts as whoever runs it, and can do nothing that person (or key) could not do directly. records.create needs records:create on that record type, analytics.query needs insights:query, and so on. When something other than a person starts the function (a flow, a queue button, a form), it runs with the fewer permissions of the trigger's owner and the function's author. If neither of them covers everything the other can do, the run is refused. A public HTTP function runs with the workspace's public-visitor role, so by default it can compute but not touch data. See Public endpoints.
Records and types
axis.types.list()
Returns the record types: [{ key, label, record_count }].
axis.records.search(type, opts?)
Search records of a type. Needs records:read.
| Option | Notes |
|---|---|
q |
Text to find in text fields |
filters |
[{ field, op, value }]. Same operators as the REST search: eq, ne, contains, gt, gte, lt, lte by field type |
sort, dir |
A field key (or created_at) and asc or desc |
limit |
1 to 200. Default 50 |
offset |
Start position |
Returns { records: [...], total }. Each record is { id, created_at, values }. Fields the caller may not read are omitted.
axis.records.get(type, id)
Returns { id, created_at, values }.
axis.records.create(type, values)
Creates a record and returns { id, created_at, values }. Validation is the same as the REST API, including required fields, unique values and the free-plan limits. Currency values are { amount, code }.
axis.records.update(type, id, values)
Changes the given fields and returns the record. null clears a field.
axis.records.delete(type, id)
Deletes a record. Returns { deleted: true }. axis.records.remove is an alias.
Reports
axis.analytics.query(sql)
Runs one read-only SELECT over your record types and returns { columns, rows, truncated }. Rows are arrays in column order. Results are capped at 1,000 rows and truncated says when more existed. The same rules apply as for the REST query. Needs insights:query.
const r = axis.analytics.query("SELECT status, COUNT(*) AS n FROM invoices GROUP BY status");
// r.columns = ["status", "n"]; r.rows = [["open", 17], ["paid", 40]]
Connected stores
Needs marketplaces:use.
axis.marketplace.connections()
Returns [{ id, name, provider, base_url, status }]. Credentials are never included.
axis.marketplace.request(connectionId, { method, path, query, body })
Makes one call to the connected Shopify or WooCommerce store, with the store's credentials added for you. path must lie under the store's own API. query is an object of strings. body is JSON.
Returns { status, headers, body }. A non-2xx answer from the store (such as 404 or 429) is returned as data, not thrown, so your code decides whether to retry.
PDF documents
axis.pdf.render(templateId, { record_id, data })
Generates a PDF from a layout template and stores it in Drive. record_id fills the template from a record. data adds extra values. Returns { id, bucket_id, key, size }. Needs pdf:generate and write access to the template's Drive space.
Messaging
axis.comms.connections()
Returns [{ id, key, name, channel, provider, enabled, status }] for the connections the caller may use.
axis.comms.send(connection, opts)
Queues one email, SMS or WhatsApp message. connection is the connection's key (or id).
| Option | Notes |
|---|---|
to |
Recipient address or number. Leave out to take it from a record |
template_id or template |
An approved message template (id or key) |
record_type, record_id, to_field |
A record to fill the template from, and the field holding the recipient |
subject, body_text, body_html |
A message written inline instead of a template |
data |
Extra template values |
vars |
Ordered values for providers that take positional variables (WhatsApp templates) |
Returns { id, status, to }. The status is usually queued: sending finishes in the background, and recipients on the suppression list are skipped (suppressed). Needs comms:send on the connection.
Work queues
Queues are addressed by their key. Needs the matching queue permission: queues:enqueue for push, queues:transition for scan, claim and move, queues:read for list and summary.
axis.queue.push(queue, opts)
Adds items. Single item: { key, stage, batch, priority, record_id, payload }. Many: { items: [ {...}, ... ], stage, batch }. Pushing a key that already exists is not an error. Returns { created, duplicates, items }.
axis.queue.scan(queue, key, { stage, to_stage, batch, note, payload })
Looks an item up by key and advances it, or creates it if the queue accepts unknown keys. Returns { verdict, message, item } where verdict is moved, created, duplicate, unknown or blocked.
axis.queue.claim(queue, { stage, count })
Takes up to count (at most 50) waiting items for yourself. Returns { items, claimed }.
axis.queue.move(queue, itemId, toStage, { status, note })
Moves an item. status can be open, done, failed or cancelled. Moving into a final stage closes the item as done unless you say otherwise. Returns the item.
axis.queue.list(queue, { stage, status, batch, key, record_id, limit, offset })
Returns { items, total }.
axis.queue.summary(queue, { batch })
Returns the counts per stage and the totals of open, closed, overdue and unexpected items.
Item fields are described in Work Queues.
Calendars
Times are RFC 3339, or YYYY-MM-DD for all-day. Calendars are addressed by id or key; an empty string means the caller's own calendar.
| Method | Returns |
|---|---|
axis.calendar.calendars() |
The calendars the caller can see |
axis.calendar.events(from, to, { calendars }) |
Occurrences in the window, with repeating events expanded. calendars is an optional list of ids or keys |
axis.calendar.get(id) |
One event |
axis.calendar.create(event) |
The new event |
axis.calendar.update(id, patch, { scope, occurrence }) |
The changed event. For a repeating event scope is all, this or following, and occurrence names which one |
axis.calendar.remove(id, { scope, occurrence }) |
{ deleted: true } |
axis.calendar.freebusy({ from, to, users, calendars }) |
Busy periods per person or calendar |
axis.calendar.slots(calendarKey, from, to, durationMinutes) |
{ slots: [...] }, the free start times on a booking page |
axis.calendar.book(calendarKey, { start, duration, name, email, notes }) |
{ event_id, start, end, manage_url } |
create takes { calendar, title, start, end, all_day, timezone, location, description, recurrence, attendees, reminders, notify, status, visibility, color, record_type, record_id }. title and start are required. recurrence is a repeat rule such as FREQ=WEEKLY;BYDAY=MO. attendees is a list of email addresses or member ids. reminders is a list of minutes before the start.
slots and book use the calendar's booking page settings (they work only when the booking page is on) and follow the same rules as a visitor booking through the public page, so a double booking answers with an error that says the time was just taken.
Errors thrown by the SDK
| Message starts with | Meaning |
|---|---|
permission denied |
The caller lacks that permission |
| a field name and reason | A value broke a rule, such as amount: expected a number |
not found |
The record, queue, template or connection does not exist or is not visible |
unknown host method … |
A method name that does not exist |
Related
- Functions API
- Records
- In the product: Functions