{
  "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}}"
    }
  ]
}