Template · Accounting & finance

Expense policy check

Your expense tool posts each new expense to a webhook. Claude reads it against your policy and answers OK or FLAG with a reason. A condition step sends clean expenses straight back as approved, and stops flagged ones at an approval gate with the reason attached.

Download .json

WebhookEXP-20931Dana Lee · Delta Air Lines · $1,240.00expense.submitted
Pipeline
  1. Check policy Claude API · Sonnet 5
  2. Flagged? Condition
  3. Review flagged expense You
  4. Send: approved after review Shell
  5. Send: within policy Shell
Expense toolEXP-20931Flagged: economy-plus over the domestic fare cap. Approved by you with a note.Approved
The pipeline

What each step does

  1. Check policy

    Claude API · Sonnet 5

    Reads the expense against your policy and answers OK or FLAG with the rule that applies.

  2. Flagged?

    Condition

    Sends flagged expenses to you and clean ones straight on.

  3. Review flagged expense

    You

    Shows you the flagged expense and the reason. Reject to stop and handle it in your expense tool.

  4. Send: approved after review

    Shell

    Sends the expense back as approved, with your comment.

  5. Send: within policy

    Shell

    Sends the expense back as within policy.

    Runs when Flagged? answers no.

Set it up

From copy to first run

  1. Copy the pipeline JSON with the button above.
  2. In Jimothy, open Pipelines → New pipeline, paste it under “Or import a pipeline JSON” and click Import.
  3. Fill in the variables below under Pipeline settings → Variables.
  4. Triggers → Add trigger → Webhook. In Zapier, Make or n8n, post each new expense to the URL with the fields below.

Send title (employee · merchant · amount), description (the memo), key (the expense id) and the extra fields employee, amount, currency, merchant, category, date and receipt (yes or no). They reach the prompts as {{issue.raw.amount}} and so on.

Variables

What to fill in

The prompts and commands read these as {{vars.<name>}}. The examples are placeholders: replace them with your own.

VariableWhat to put in itExample
expensePolicyYour expense policy in plain language. The check only enforces what is written here.Meals: up to $75 per person per day while travelling; alcohol is not reimbursed. Flights…
resultWebhookUrlWhere decisions are sent, e.g. a Zapier catch hook that approves or comments on the expense.—
Show the pipeline JSON
{
  "jimothyPipeline": 1,
  "name": "Expense policy check",
  "description": "Checks a submitted expense against your policy, auto-approves clean ones and asks you about flagged ones.",
  "icon": "workflow",
  "color": "#10b981",
  "repo": {
    "mode": "scratch"
  },
  "concurrency": 5,
  "variables": {
    "expensePolicy": "Meals: up to $75 per person per day while travelling; alcohol is not reimbursed. Flights: economy for trips under 6 hours. Hotels: up to $250 per night. Software subscriptions need a manager-approved request. Receipts are required over $25. Personal items, fines and upgrades are never reimbursed.",
    "resultWebhookUrl": ""
  },
  "steps": [
    {
      "id": "check",
      "name": "Check policy",
      "type": "agent",
      "description": "Reads the expense against your policy and answers OK or FLAG with the rule that applies.",
      "harnessId": "anthropic-api",
      "model": "claude-sonnet-5",
      "timeoutMinutes": 5,
      "retries": 1,
      "passPattern": "^\\s*DECISION:\\s*(OK|FLAG)\\b",
      "systemPrompt": "You check employee expenses against this policy and nothing else:\n\n{{vars.expensePolicy}}\n\nWhen the policy does not cover an expense, flag it rather than guessing.",
      "prompt": "Expense {{issue.key}}\nEmployee: {{issue.raw.employee | default:\"(unknown)\"}}\nMerchant: {{issue.raw.merchant | default:\"(unknown)\"}}\nAmount: {{issue.raw.amount | default:\"(unknown)\"}} {{issue.raw.currency | default:\"\"}}\nCategory: {{issue.raw.category | default:\"(none)\"}}\nDate: {{issue.raw.date | default:\"(unknown)\"}}\nReceipt attached: {{issue.raw.receipt | default:\"unknown\"}}\nMemo: {{issue.description | default:\"(none)\"}}\n\nAnswer in exactly this format:\nDECISION: OK or DECISION: FLAG\nREASON: one sentence naming the policy rule that applies"
    },
    {
      "id": "flagged",
      "name": "Flagged?",
      "type": "condition",
      "description": "Sends flagged expenses to you and clean ones straight on.",
      "dependsOn": [
        "check"
      ],
      "condition": {
        "match": "all",
        "rules": [
          {
            "value": "{{steps.check.output}}",
            "op": "matches",
            "compare": "DECISION:\\s*FLAG"
          }
        ]
      }
    },
    {
      "id": "approve",
      "name": "Review flagged expense",
      "type": "approval",
      "description": "Shows you the flagged expense and the reason. Reject to stop and handle it in your expense tool.",
      "dependsOn": [
        "flagged"
      ],
      "approvalMessage": "**{{issue.title}}** was flagged.\n\n{{steps.check.output}}\n\nMemo: {{issue.description | default:\"(none)\"}}\n\nApprove to send it back as approved (your comment is attached). Reject to stop here and handle it in your expense tool."
    },
    {
      "id": "send_reviewed",
      "name": "Send: approved after review",
      "type": "shell",
      "description": "Sends the expense back as approved, with your comment.",
      "dependsOn": [
        "approve"
      ],
      "timeoutMinutes": 2,
      "command": "printf '{\"expense\":%s,\"decision\":\"approved_after_review\",\"check\":%s,\"note\":%s}' {{issue.key | json}} {{steps.check.output | json}} {{steps.approve.comment | default:\"\" | json}} | curl -fsS -X POST -H 'Content-Type: application/json' --data @- {{vars.resultWebhookUrl}}"
    },
    {
      "id": "send_ok",
      "name": "Send: within policy",
      "type": "shell",
      "description": "Sends the expense back as within policy.",
      "dependsOn": [
        "flagged"
      ],
      "onBranch": {
        "flagged": "no"
      },
      "timeoutMinutes": 2,
      "command": "printf '{\"expense\":%s,\"decision\":\"within_policy\",\"check\":%s}' {{issue.key | json}} {{steps.check.output | json}} | curl -fsS -X POST -H 'Content-Type: application/json' --data @- {{vars.resultWebhookUrl}}"
    }
  ]
}