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 (no await needed, though harmless). Use await only for your own promises.
  • A failed call throws an Error whose message says why (for example permission denied). Wrap calls in try/catch if you want to carry on.
  • The sandbox has no fetch, no require, no timers, no file system and no network. Reach other systems through axis.marketplace.request, or have a flow call an HTTP endpoint.
  • console.log, info, warn and error are 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