Build an OpenClaw Skill for Minitally

May 7, 2026

OpenClaw skills are a good fit when you want repeatable agent behavior. A Minitally skill can expose one clear action: turn a confirmed spending note into a Minitally record.

There are two practical implementations:

  1. Use the Minitally CLI when Node.js and the CLI are available.
  2. Use a Python script when the environment cannot install or run the CLI.

Both should use MINITALLY_API_KEY and MINITALLY_API_BASE from the environment.

Skill Shape

Create a skill folder like this:

minitally-record-expense/
  SKILL.md
  scripts/
    add_expense.py

SKILL.md can be short and strict:

# Minitally Record Expense

Use this skill when the user asks to record a spending or income item in Minitally.

Before writing:
- Confirm the amount.
- Identify whether it is expense or income.
- Preserve the user's original note.
- Ask if the amount, currency, or budget-exclusion status is unclear.

Preferred path:
- If `minitally` CLI is available, call it.
- Otherwise run `python3 scripts/add_expense.py`.

Never print `MINITALLY_API_KEY`.

CLI Implementation

If the skill runtime can run the CLI, keep the action simple:

minitally expense add --amount 38 \
  --note "OpenClaw skill lunch test" \
  --merchant "Noodle House" \
  --request-id "openclaw-skill-20260507-001"

For the current source checkout, use:

pnpm --filter @minitally/cli dev expense add --amount 38 \
  --note "OpenClaw skill lunch test" \
  --merchant "Noodle House" \
  --request-id "openclaw-skill-20260507-001"

Python Fallback

Use only Python standard library so the skill works in locked-down environments:

#!/usr/bin/env python3
import json
import os
import sys
import urllib.error
import urllib.request


def arg_value(name, default=None):
    if name not in sys.argv:
        return default
    index = sys.argv.index(name)
    if index + 1 >= len(sys.argv):
        return default
    return sys.argv[index + 1]


api_key = os.environ.get("MINITALLY_API_KEY")
base_url = os.environ.get("MINITALLY_API_BASE", "https://mn.hekmon.com").rstrip("/")

if not api_key:
    raise SystemExit("MINITALLY_API_KEY is required")

payload = {
    "amount": float(arg_value("--amount", "0")),
    "type": arg_value("--type", "expense"),
    "note": arg_value("--note", ""),
    "counterparty": arg_value("--counterparty", arg_value("--merchant", "")),
    "requestId": arg_value("--request-id", ""),
}

if "--exclude-from-budget" in sys.argv:
    payload["excludeFromBudget"] = True

request = urllib.request.Request(
    f"{base_url}/api/v1/expenses",
    data=json.dumps(payload).encode("utf-8"),
    headers={
        "accept": "application/json",
        "content-type": "application/json",
        "authorization": f"Bearer {api_key}",
        "x-minitally-agent-channel": "cli",
    },
    method="POST",
)

try:
    with urllib.request.urlopen(request, timeout=20) as response:
        print(response.read().decode("utf-8"))
except urllib.error.HTTPError as error:
    print(error.read().decode("utf-8"), file=sys.stderr)
    raise SystemExit(error.code)

Run it from the skill:

python3 scripts/add_expense.py \
  --amount 38 \
  --note "OpenClaw skill lunch test" \
  --merchant "Noodle House" \
  --request-id "openclaw-skill-20260507-001"

Operational Rules

  • Keep one API Key per OpenClaw workspace.
  • Always set a request id.
  • Search first when the user says "record what I just mentioned" and the conversation may already contain a previous write.
  • Use --exclude-from-budget only when the user says it is reimbursement, transfer-like spending, or otherwise outside budget progress.
Admin

Admin