How to create a Cloud Function

A full walkthrough — from a fresh workspace to a deployed function in IXON Cloud — using a realistic example.

This guide walks you through creating a Cloud Function from scratch and getting it running both locally and in IXON Cloud. We'll build a small service logbook — two functions that let operators add and retrieve maintenance notes, stored persistently in the Document Store.

The function does not require a UI Component — we'll trigger it directly from the terminal via curl. To pair it with a UI Component, see How to visualize a Cloud Function in a UI Component after this guide.

A basic understanding of Python is assumed.

What you need

  • Linux, macOS, or Windows with WSL2. Windows users: complete Prerequisites for Microsoft Windows systems first.
  • Python 3.12 or higher (matching ixoncdkingress 1.x). Download from python.org.
  • Docker — see docker.com. The local Document Store runs in a Docker container.
  • A code editor that understands Python — VS Code or PyCharm Community work well.
  • curl — pre-installed on macOS and Linux; included with WSL on Windows.
  • A user account in IXON Cloud with the Develop apps permission (for the deploy step).

Part 1 — Set up the workspace

❗️

Do not edit the Makefile

The Makefile in the workspace is part of IXON's tooling contract. Modifying it will break local builds.

  1. Download the workspace from ixoncloud/backend-component-workspace as a ZIP and extract it. (Don't clone — the workspace is intended to be checked into your own repository, and cloning IXON's directly creates Git history conflicts.)

    OR

    Open any terminal in the folder where you want to store your project, and use the following command to create a clean, unversioned workspace:

    npx degit ixoncloud/backend-component-workspace notes-project
  2. Open the workspace folder in your editor.

    Windows users: open it inside a WSL session (see Prerequisites). In VS Code, navigate to View > Command Palette > WSL: Open folder in WSL...

  3. Run make run from the workspace root in your terminal.

    Note: Before you run this command, make sure that your Docker engine is running, or you will receive an error.

    $ make run

    On the first run, the Makefile creates a Python virtual environment, installs dependencies, starts a MongoDB Docker container for the local Document Store, and starts the Ingress on http://127.0.0.1:8020/. You'll see something like:

    CBC_PATH=./functions ./venv/bin/python3 -m ixoncdkingress
    2026-04-03 11:54:24 - ixoncdkingress - INFO - Starting DocumentDB server
    2026-04-03 11:54:29 - ixoncdkingress - INFO - DocumentDB server started successfully
    2026-04-03 11:54:29 - ixoncdkingress - INFO - wsgiref listening on http://127.0.0.1:8020/
    2026-04-03 11:54:29 - ixoncdkingress - INFO - CBC_PATH: ./functions

    Leave this terminal open — your function host runs here, and you'll see logs in this window every time the function is called.

    🚧

    If make run fails with a .venv error

    sudo apt update
    sudo apt install python3.12-venv
    rm -rf venv
    make run

Part 2 — Write the function

As a very first step,

Create a new file functions/logbook.py in your editor and add the following:

from datetime import datetime, timezone
from bson.json_util import dumps
import json

from ixoncdkingress.function.context import FunctionContext

@FunctionContext.expose
def add_note(context: FunctionContext, text: str) -> dict:
    """
    Add a maintenance note to the logbook.
    The note is tagged with the calling user's name and the current timestamp.
    """
    if not context.document_db_client:
        return {'success': False, 'error': 'Document Store is not available.'}

    note = {
        'text': text,
        'author': context.user.name if context.user else 'Unknown',
        'timestamp': datetime.now(timezone.utc).isoformat(),
    }
    context.document_db_client.insert_one(note)
    return {'success': True}

@FunctionContext.expose
def get_notes(context: FunctionContext) -> list:
    """
    Return the most recent logbook notes.
    The number of notes returned is controlled by the `maxNotes` setting.
    """
    if not context.document_db_client:
        return []

    limit = context.config.get('maxNotes', 50)
    notes = list(context.document_db_client.find({}))
    return json.loads(dumps(notes[-limit:]))

Code workflow

  • The decorator @FunctionContext.expose is what makes a function callable from outside the workspace. Without it, the function would be unreachable. A single file can expose as many Python functions as you need.

  • Both functions guard against context.document_db_client being None — the Document Store is not guaranteed to be available in all contexts. See Document Store for details.

  • context.user is populated when a real user is calling the function — add_note uses it to automatically tag the author of each note. See Resources for details.

  • context.config reads from context_values.yaml locally, and from the encrypted Settings in IXON Cloud. The .get('maxNotes', 50) provides a sensible default if no limit is configured.

  • bson.json_util.dumps is needed to serialize the documents returned by the Document Store, because MongoDB assigns a BSON ObjectId as _id on insert — a type that Python's standard json module cannot handle on its own.

    📘

    Auto-reload

    The Ingress reloads function code on save. You don't need to restart make run after editing logbook.py or context_values.yaml.

Part 3 — Test the functions in your terminal

Open a second terminal and add a note via curl:

curl -X POST http://127.0.0.1:8020/ \
  -H 'Content-Type: application/json' \
  -d '{"name": "logbook.add_note", "arguments": {"text": "Replaced motor belt on conveyor line 3."}, "context": {"company": {"publicId": "1111-2222-3333-4444-5555", "name": "My Company", "custom": {}}}}'

You should see a response like:

{"success": true}

Now retrieve the notes you've added:

curl -X POST http://127.0.0.1:8020/ \
  -H 'Content-Type: application/json' \
  -d '{"name": "logbook.get_notes", "arguments": {}, "context": {"company": {"publicId": "1111-2222-3333-4444-5555", "name": "My Company", "custom": {}}}}'

You should see a response like:

[{"_id": {"$oid": "..."}, "text": "Replaced motor belt on conveyor line 3.", "author": "Unknown", "timestamp": "2026-05-21T10:00:00+00:00"}]
📘

Notes on Author and Company

  • The author field shows "Unknown" here because no user is set when calling via curl locally. When a real user triggers the function from a UI Component, context.user.name will be populated automatically. In the terminal running make run, you'll see an HTTP log entry for each call.
  • For the same reason, the publicId and name of a company must be simulated and added to the cURL command.

Registering the function in IXON Cloud

Before you can deploy, you need to register the function in IXON Cloud.

  1. Open Studio > Cloud Functions.
  2. Click Add new Cloud Function.
  3. Enter a name end-users will recognise, then click Add.
  4. Copy the Public ID that's displayed — you'll need it for deploying and publishing the function.

Deploy and publish

To deploy and publish your local code as a new version of the registered function, the workspace needs two values in a .env file at the workspace root:

  • IXON_API_COMPANY_ID — your company's Public ID. You can find it in the URL of the IXON Portal when your company is open.
  • IXON_API_TEMPLATE_ID — the Public ID of the Cloud Function you just registered.

Create .env:

IXON_API_COMPANY_ID=your-company-public-id
IXON_API_TEMPLATE_ID=your-function-public-id

Make sure .accesstoken exists in the workspace root. Then deploy following the steps in the workspace's deployment section.

📘

Notes

  • The user account used for deploying must have the Develop apps permission.
  • The deploy uploads a new version. To make it active, publish the new version from Studio > Cloud Functions > your function.
  • The bearer token can be automatically generated when logging in from a UI Component's workspace: You can do that and copy the bearer token in the .accesstoken file and then paste it in the backend-component-workspace root folder, or you can get the token by inspecting the browser with F12 when visiting the IXON Portal.

Finally, deploy and publish your function with the following command:

make deploy

Once published, the function becomes available under Admin > Apps for anyone in your company to enable.

Downloading a Cloud Function's logs

To inspect what happened during real calls:

  • Studio > Cloud Functions > your function > More options (three dots) > Download logs.

Logs cover the current version of the Cloud Function deployed and published in the Portal, for the last 48 hours, with up to a 5-minute delay between a call and its log entry. Anything you print() or send via Python's logging module at level INFO or higher will appear in the download. Be careful not to log secrets — anyone with the Manage Apps permission can see the log file.

Sharing your Cloud Function

To share the function with another company, go to Admin > Apps and use the share option on your function. Linked UI Components are shared automatically.

Where to go next