Expense Approval Blueprint
Submit and approve employee expense reports with receipt validation
| Feature | expense-approval |
| Category | Data |
| Version | 1.0.0 |
| Tags | expense, approval, workflow, finance, reimbursement |
| YAML Source | View on GitHub |
| JSON API | expense-approval.json |
Actors
| ID | Name | Type | Description |
|---|---|---|---|
employee | Employee | human | Submits expense reports for reimbursement |
manager | Direct Manager | human | First-level approval for all expenses |
finance_manager | Finance Manager | human | Second-level approval required for expenses over threshold |
payment_system | Payment System | system | Processes approved reimbursements via payroll or direct deposit |
audit_system | Audit Logger | system | Records all state transitions for compliance |
Fields
| Name | Type | Required | Label | Description |
|---|---|---|---|---|
title | text | Yes | Expense Title | Validations: required, maxLength |
amount | number | Yes | Amount | Validations: required, min, max |
currency | select | Yes | Currency | |
category | select | Yes | Expense Category | |
date_incurred | date | Yes | Date of Expense | Validations: required, custom |
description | rich_text | Yes | Description & Business Justification | Validations: required, minLength |
receipt | file | No | Receipt | Validations: custom |
status | select | Yes | Status | |
rejection_reason | text | No | Rejection Reason | |
submitted_by | hidden | Yes | Submitted By | |
approved_by | hidden | No | Approved By |
States
State field: status
Values:
| State | Initial | Terminal |
|---|---|---|
draft | Yes | |
submitted | ||
manager_approved | ||
pending_finance | ||
approved | ||
rejected | Yes | |
paid | Yes |
Transitions:
| Name | From | To | Actor | Condition |
|---|---|---|---|---|
| submit | draft | submitted | employee | |
| manager_approve | submitted | manager_approved | manager | |
| escalate_to_finance | manager_approved | pending_finance | system | amount > rules.approval.finance_threshold |
| finance_approve | pending_finance | approved | finance_manager | |
| auto_approve | manager_approved | approved | system | amount <= rules.approval.finance_threshold |
| reject | submitted,pending_finance | rejected | manager,finance_manager | |
| revise | rejected | draft | employee | |
| process_payment | approved | paid | payment_system |
Rules
- approval:
- finance_threshold: 1000
- max_single_expense: 50000
- receipt:
- required_above: 25
- accepted_formats: pdf, png, jpg, jpeg
- max_size_mb: 10
- submission:
- max_age_days: 90
- duplicate_detection: true
- security:
- rate_limit:
- window_seconds: 3600
- max_requests: 20
- scope: per_user
- rate_limit:
- audit:
- log_all_transitions: true
- retention_days: 2555
SLA
| Scope | Max Duration | Escalation |
|---|---|---|
| manager_review | 48h | notify_department_head |
| finance_review | 72h | notify_cfo |
| payment_processing | 5d | |
| overall_completion | 30d |
Flows
Submit_expense
Employee submits a new expense report
- validate_fields (employee) — Validate all required fields and formats
- check_receipt_required (system) — Require receipt upload if amount exceeds $25
- detect_duplicate_expense (system) — Warn if a similar expense already exists
- transition_state (employee) — Move status from draft → submitted
- send_notification (system) — Email the direct manager that a new expense needs review
- emit
Manager_review
Manager reviews and approves or rejects the expense
- load_expense_with_receipt (manager) — Manager opens the expense report with all attachments
- review_justification (manager) — Verify the expense has a legitimate business purpose
- approve_or_reject (manager) — Manager approves or rejects (rejection requires reason)
- check_finance_threshold (system) — Route to finance if over $1,000, otherwise auto-approve
- emit
Finance_review
Finance manager reviews high-value expenses
- load_expense_with_history (finance_manager) — Review expense with audit trail and manager approval
- check_department_budget (finance_manager) — Verify department has remaining budget for this expense
- approve_or_reject (finance_manager) — Finance approves or rejects
- emit
Rejection_flow
Expense rejected by a reviewer
- validate_rejection_reason — Rejection reason is mandatory
- transition_state — Move status to rejected
- send_notification (system) — Email the employee with rejection reason
- emit
Payment_flow
System processes approved expense for reimbursement
- create_payment_record (payment_system) — Create reimbursement entry in payroll system
- execute_payment (payment_system) — Process via direct deposit or payroll
- transition_state — Move status to paid
- send_notification (system) — Confirm reimbursement to the employee
- emit
Receipt_missing
Receipt required but not attached
- show_error
Payment_failed
Payment processing failed
- emit
- send_notification (system) — Alert finance team of payment failure
- show_error
Outcomes
Expense_rejected (Priority: 5) — Error: EXPENSE_INVALID_TRANSITION | Transaction: atomic
Given:
reviewer_roleinmanager,finance_managerstatusinsubmitted,pending_financerejection_reasonexists
Then:
- status transitions to rejected
- employee is notified with rejection reason
- expense.rejected event is emitted
Result: employee can revise (status back to draft) and resubmit
Expense_submitted (Priority: 10) | Transaction: atomic
Given:
- user is authenticated
titleexistsamountgte0.01amountlte50000categoryintravel,meals,supplies,software,equipment,otherdate_incurredgtetoday - 90 daysdescriptiongte10receiptexistsrequest_countlte20
Then:
- status transitions from draft to submitted
- direct manager is notified via email
- expense.submitted event is emitted
- all state transitions are logged for audit (actor, timestamp, IP)
Result: expense appears in manager’s review queue
Manager_approves_under_threshold (Priority: 10) | Transaction: atomic
Given:
reviewer_roleeqmanagerstatuseqsubmittedamountlte1000
Then:
- status transitions to approved (auto-skip finance)
- expense.manager_reviewed event is emitted
Result: expense is queued for payment
Manager_approves_over_threshold (Priority: 10) | Transaction: atomic
Given:
reviewer_roleeqmanagerstatuseqsubmittedamountgt1000
Then:
- status transitions to pending_finance
- expense.manager_reviewed event is emitted
Result: expense is escalated to finance for review
Finance_approves (Priority: 10) | Transaction: atomic
Given:
reviewer_roleeqfinance_managerstatuseqpending_finance- department has remaining budget for this expense
Then:
- status transitions to approved
- expense.approved event is emitted
Result: expense is queued for payment processing
Payment_processed (Priority: 10) | Transaction: atomic
Given:
statuseqapproved- payment system is available
Then:
- reimbursement is created in payroll system
- status transitions to paid
- employee is notified of reimbursement
- expense.paid event is emitted
Result: employee receives reimbursement via direct deposit or payroll
Payment_failed (Priority: 20) — Error: EXPENSE_PAYMENT_FAILED
Given:
- payment system returns error
Then:
- expense.payment_failed event is emitted
- finance team is alerted
Result: expense stays in approved status, finance investigates
Sla_breached (Priority: 21)
Given:
time_in_submittedgt48htime_in_pending_financegt72htime_since_submittedgt30d
Then:
- expense.sla_breached event is emitted
- escalation notification sent (department head or CFO)
Result: escalation path activated per SLA rules
Errors
| Code | Status | Message | Retry |
|---|---|---|---|
EXPENSE_VALIDATION_ERROR | 422 | Please check your input and try again | Yes |
EXPENSE_RECEIPT_REQUIRED | 422 | A receipt is required for expenses over $25 | Yes |
EXPENSE_DUPLICATE_DETECTED | 409 | A similar expense already exists. Please review before submitting. | Yes |
EXPENSE_NOT_FOUND | 404 | Expense report not found | No |
EXPENSE_INVALID_TRANSITION | 422 | This action is not allowed in the current status | No |
EXPENSE_UNAUTHORIZED | 403 | You do not have permission to perform this action | No |
EXPENSE_BUDGET_EXCEEDED | 422 | This expense would exceed the department budget. Please contact finance. | No |
EXPENSE_PAYMENT_FAILED | 500 | Payment processing failed. Finance has been notified. | No |
EXPENSE_RATE_LIMITED | 429 | Too many submissions. Please try again later. | No |
Events
| Event | Description | Payload |
|---|---|---|
expense.submitted | New expense submitted for approval | expense_id, submitted_by, amount, category, timestamp |
expense.manager_reviewed | Manager completed review (approve or reject) | expense_id, reviewer_id, decision, amount, timestamp |
expense.approved | Expense fully approved (all levels) | expense_id, approved_by, amount, timestamp |
expense.rejected | Expense rejected at any level | expense_id, rejected_by, reason, amount, timestamp |
expense.paid | Reimbursement processed successfully | expense_id, user_id, amount, payment_method, timestamp |
expense.payment_failed | Reimbursement processing failed | expense_id, user_id, amount, error_detail, timestamp |
expense.sla_breached | An SLA deadline was exceeded | expense_id, sla_name, expected_duration, actual_duration, timestamp |
Related Blueprints
| Feature | Relationship | Reason |
|---|---|---|
| login | required | Users must be authenticated to submit expenses |
| roles-permissions | recommended | Manager and finance roles determine who can approve |
| notifications | recommended | Email and in-app notifications at each step |
| file-upload | required | Receipt upload functionality |
| audit-log | recommended | All transitions logged for compliance |
AGI Readiness
Goals
Compliant Expense Processing
Process expense reports accurately while enforcing company policy and maintaining full audit trails
Success Metrics:
| Metric | Target | Measurement |
|---|---|---|
| processing_time | < 48h from submission to reimbursement | Time from employee submission to payment execution |
| policy_violation_rate | 0% | Expenses approved that violate company policy |
| duplicate_detection_rate | >= 99% | Percentage of duplicate expenses caught before approval |
Constraints:
- regulatory (non-negotiable): All expenses above threshold require receipts and multi-level approval
- cost (negotiable): Processing cost per expense report must not exceed 2% of average expense amount
Autonomy
Level: supervised
Human Checkpoints:
- before approving expenses above the finance threshold
- before overriding a policy violation flag
- before processing reimbursement to a new bank account
Escalation Triggers:
pending_approvals > 50duplicate_expense_flags > 5
Safety
| Action | Permission | Cooldown | Max Auto |
|---|---|---|---|
| auto_approve_under_limit | autonomous | - | 10 |
| approve_over_limit | human_required | - | - |
| flag_duplicate | autonomous | - | - |
| process_reimbursement | supervised | - | - |
| override_policy_violation | human_required | - | - |
| modify_approval_thresholds | human_required | - | - |
Explainability
Log Decisions: Yes
Reasoning Depth: full
Audit Events:
| Decision | Must Log |
|---|---|
| expense_approved | expense_id, amount, category, approver, policy_rule, timestamp |
| expense_rejected | expense_id, amount, reason, reviewer, timestamp |
| policy_override | expense_id, original_decision, override_reason, authorizer, timestamp |
| duplicate_flagged | expense_id, matched_expense_id, similarity_score, timestamp |