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

  • If using Microsoft Windows: WSL2 and Ubuntu 24.04. Windows users should complete Prerequisites for Microsoft Windows systems first.
  • Python 3.12 or higher (matching ixoncdkingress 1.x). Download from python.org.
    • Microsoft Windows users should install it on WSL.
  • Docker Desktop — see Docker Desktop. This is needed because local Document Store and Object Storage instances run in a Docker container.
  • A code editor that understands Python — VS Code or PyCharm Community work well. We recommend VS Code.
  • curl — pre-installed on macOS and Linux; should be included with WSL on Windows in recent WSL distros; The developer can check this by running curl --version in a WSL terminal. If a message saying command not found pops up, curl can be installed with sudo apt update && sudo apt install curl.
  • A user account in IXON Cloud with the Develop apps permission (for the deploy step).
  • The IXON Cloud Function's SDK.
📘

Before you start: Set up Docker Desktop

To work with Cloud Functions, you will need to allow Docker to be used through the WSL's CLI. On Docker Desktop, navigate to Settings > Resources > WSL Integrations and check Enable integration with my default WSL as well as Enable integration with additional distros and select your installed WSL distros.


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. Open Docker Desktop on your computer and make sure that your Docker engine is running, or you will receive an error.

  4. Run make run from the workspace root in your terminal. To prevent errors in selecting the right Python version in your system, we recommend forcing the right version by running either of these commands based on your OS:

    # For Windows users, in WSL terminal:
    make run HOST_PYTHON_BIN=$(command -v python3.12) 
    
    # For Mac users: 
    make run HOST_PYTHON_BIN=$(which python3.12)

    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.

How can I create an interface for my Cloud Function?

Now that you are done with the Cloud Function's logic, you can proceed to call it from a UI Component. For a first approach, check How to visualize a Cloud Function in a UI Component and proceed from there.

Registering, deploying and publishing your Cloud Function

Information about registration, deployment and publishing can be found in How to register, deploy and publish a Cloud Function.

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.

📘

What if my UI Component is linked to a Cloud Function?

If the component is part of an app linked to a Cloud Function, it must be shared via the Cloud Function menu instead. In both cases, the recipient company must manually enable the app under Admin > Apps before it becomes usable.