there is a longer write-up about this script available: see post

but this is the latest version of my script, which I try to update whenever possible

Python create_alias.py
import json
import os
import subprocess
import sys

import pyperclip

# --------------------

# stalwart server should be configured as mail server for DOMAIN
# and live on HOSTNAME, like mail.example.de should be reachable for jmap etc.
# and there must be a MX record for a.example.de (just let stalwart handle the details)
# and after setting it up add a new domain (a.example.de), set the MX record and it should be done
HOSTNAME = "mail.example.de"
DOMAIN = "a.example.de"
TOKEN = "API_AAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"  # your own API token <WEBINTERFACE>/account/Account/x:ApiKey
USERNAME = "user" # your username (like if you usually use me@example.de for your mails (example.de is your primary mail), then this field would be 'me')
ALIAS_POSTFIX = ""  # e.g. -alias, or leave blank, I recommend setting this to set if you set the DOMAIN to your main domain
                    # which is also possible, in that case you won't need to add any records
CACHE_FILE = ".stalwart-alias-cache.json" # just to make it more efficient, as the stalwart-cli is somewhat slow otherwise handling the commands in succession

# --------------------

# Usage: uv run create_alias.py <alias_prefix>
# or     uv run create_alias.py

# ------------

if not len(sys.argv) > 1:
    new_alias_prefix = input("alias: ")
else:
    new_alias_prefix = sys.argv[1]


user_id_cached = None
domain_id_cached = None

if os.path.isfile(CACHE_FILE):
    with open(CACHE_FILE, "r") as f:
        cache = json.load(f)
        if "users" in cache and USERNAME in cache["users"]:
            user_id_cached = cache["users"][USERNAME]

        if "domains" in cache and DOMAIN in cache["domains"]:
            domain_id_cached = cache["domains"][DOMAIN]


def run_stalwart_cmd(cmd, nojson=False):
    env = os.environ.copy()
    env["STALWART_URL"] = f"https://{HOSTNAME}"
    env["STALWART_TOKEN"] = TOKEN

    cmd = "stalwart-cli " + cmd

    if not nojson:
        cmd += " --json"

    # print(cmd)

    result = subprocess.check_output(
        cmd,
        env=env,
        shell=True,
        text=True,
    )

    if nojson:
        return result
    else:
        return json.loads(result)


if not user_id_cached:
    user_account = run_stalwart_cmd(
        f"query Account --where name={USERNAME} --fields id,name,emailAddress"
    )

    user_id = user_account["id"]
    print(f"[+] obtained user id: {user_id}")
else:
    user_id = user_id_cached
    print(f"[+] loaded user id from cache: {user_id}")


if not domain_id_cached:
    domain_id = run_stalwart_cmd(
        f"query Domain --fields id,name --where name={DOMAIN}"
    )["id"]
    print(f"[+] obtained domain id: {domain_id}")
else:
    domain_id = domain_id_cached
    print(f"[+] loaded domain id from cache: {domain_id}")

user_aliases = run_stalwart_cmd(f"get Account {user_id} --fields aliases")["aliases"]


if len(list(map(int, list(user_aliases)))) == 0:
    new_alias_id = "0"
else:
    new_alias_id = str(max(list(map(int, list(user_aliases)))) + 1)
alias_list = [user_aliases[a]["name"] for a in user_aliases]


print(f"[+] obtained next alias id: {new_alias_id}")

new_alias = f"{new_alias_prefix}{ALIAS_POSTFIX}"

if new_alias in alias_list:
    print("[-] FAILURE: alias already exists! - exiting")
    exit()

print(f"[+] alias can be created: {new_alias}@{DOMAIN}")

alias_patch = {
    f"aliases/{new_alias_id}": {
        "enabled": True,
        "name": new_alias,
        "domainId": domain_id,
        "description": None,
    }
}

result = run_stalwart_cmd(
    "update Account " + user_id + " --json '" + json.dumps(alias_patch) + "'",
    nojson=True,
)

if result.startswith("Updated"):
    print("[+] created alias - copying to clipboard")

    cache = {"users": {USERNAME: user_id}, "domains": {DOMAIN: domain_id}}

    with open(CACHE_FILE, "w") as f:
        json.dump(cache, f, indent=4)

    pyperclip.copy(f"{new_alias}@{DOMAIN}")

else:
    print(f"[-] FAILURE: {result}")