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

  • Having completed Preparing your development environment.
  • Docker Desktop — see Docker Desktop > Setup > Install. This is needed because the IXON Cloud Functions SDK, as well as the 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. The examples in IXON Documentation pages use VS Code.
  • A user account in IXON Cloud with the Develop apps permission (for the deploy step).

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 with a name of your choice:

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

  3. Open Docker Desktop on your computer and make sure that your Docker engine is running, or you will receive an error.

  4. Follow the steps found in Dev Container (recommended) to reopen the project in a Dev Container.

    The container takes some time to start, both on UNIX systems and Microsoft Windows: be patient!

  5. Once the project is open in a container, open a new terminal window on your editor and run make run from the workspace root in your terminal.

    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/. It may take one or two minutes for the page to load, and in the end you'll see something like this in the terminal:

    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.

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.

    📘

    Do not rename the /functions folder!

    The Ingress is exposed on the ./functions module by default. Renaming it breaks make run with a "No such file or directory" error. If you add a new file alongside logbook.py with a different name, also create an empty __init.py__ so Python treats the folder as a package.

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.



Did this page help you?