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:
- Use the Minitally CLI when Node.js and the CLI are available.
- 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.pySKILL.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-budgetonly when the user says it is reimbursement, transfer-like spending, or otherwise outside budget progress.

