ATOM Documentation

← Back to App

Budget Approval Request Flow

This feature allows team members to request budget overrides when they exceed their monthly limits, requiring admin approval for temporary budget increases.

Architecture

Database Model

**Table:** budget_approval_requests

Located in /Users/rushiparikh/projects/atom-saas/backend-saas/core/models.py

class BudgetApprovalRequest(Base):
    id = Column(String, primary_key=True)
    tenant_id = Column(String, ForeignKey("tenants.id"))
    requester_id = Column(String)  # User who made the request
    reason = Column(Text)  # Explanation for the override
    additional_budget_requested = Column(Float)  # USD amount
    status = Column(String(20))  # "pending", "approved", "denied"
    reviewer_id = Column(String)  # Admin who reviewed
    reviewed_at = Column(DateTime)
    reviewer_notes = Column(Text)
    override_expires_at = Column(DateTime)  # When the approved override expires
    created_at = Column(DateTime)
    updated_at = Column(DateTime)

API Endpoints

All endpoints are in /Users/rushiparikh/projects/atom-saas/backend-saas/api/routes/billing_routes.py

1. Create Approval Request

**POST** /api/billing/budget/approval-request

Requires: Admin/Owner role

const response = await fetch('/api/billing/budget/approval-request', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    reason: 'Need additional budget for critical client project',
    additional_budget_requested: 100.00
  })
})

**Response:**

{
  "request_id": "uuid-123",
  "status": "pending",
  "created_at": "2026-04-05T21:22:45Z"
}

**Email Notifications:** Sent to all workspace admins (excluding requester)

2. List Approval Requests

**GET** /api/billing/budget/approval-requests?status=pending

  • Admins see all requests
  • Non-admins see only their own requests
  • Optional filter: ?status=pending|approved|denied

**Response:**

[
  {
    "id": "uuid-123",
    "requester_id": "user-456",
    "reason": "Need additional budget for critical client project",
    "additional_budget_requested": 100.00,
    "status": "pending",
    "reviewed_at": null,
    "reviewer_notes": null,
    "override_expires_at": null,
    "created_at": "2026-04-05T21:22:45Z"
  }
]

3. Approve Request

**POST** /api/billing/budget/approval-request/{request_id}/approve

Requires: Admin role

const response = await fetch(`/api/billing/budget/approval-request/${requestId}/approve`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    reviewer_notes: "Approved for Q1 project",
    override_duration_hours: 24  // Default: 24, Max: 168 (1 week)
  })
})

**Response:**

{
  "success": true,
  "override_expires_at": "2026-04-06T21:22:45Z",
  "duration_hours": 24
}

**Email Notification:** Sent to requester

4. Deny Request

**POST** /api/billing/budget/approval-request/{request_id}/deny

Requires: Admin role

const response = await fetch(`/api/billing/budget/approval-request/${requestId}/deny`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    reviewer_notes: "Budget already exceeded for this month"
  })
})

**Response:**

{
  "success": true
}

**Email Notification:** Sent to requester

Frontend Component

**Location:** /Users/rushiparikh/projects/atom-saas/src/components/billing/ApprovalRequestDialog.tsx

**Features:**

  • Dual mode: Create request + List requests
  • Admin actions: Approve/Deny with notes
  • Auto-refresh every 30 seconds for pending requests
  • Form validation (reason length, budget amount)
  • Status badges (pending/approved/denied)
  • Override duration configuration (1-168 hours)

**Usage:**

import { ApprovalRequestDialog } from '@/components/billing/ApprovalRequestDialog'

function BudgetPage() {
  const [isOpen, setIsOpen] = useState(false)
  const isAdmin = session?.user?.role === 'admin'

  return (
    <>
      <Button onClick={() => setIsOpen(true)}>
        Request Budget Override
      </Button>

      <ApprovalRequestDialog
        open={isOpen}
        onOpenChange={setIsOpen}
        isAdmin={isAdmin}
        onSuccess={() => {
          // Refresh budget status
          mutate()
        }}
      />
    </>
  )
}

Integration with Budget Exceeded Banner

The approval request dialog integrates seamlessly with the existing BudgetExceededBanner component:

import { BudgetExceededBanner } from '@/components/billing/BudgetExceededBanner'
import { ApprovalRequestDialog } from '@/components/billing/ApprovalRequestDialog'

<BudgetExceededBanner
  currentPlan={plan}
  currentSpend={spend}
  budgetLimit={limit}
  utilizationPercent={percent}
  onUpgrade={() => router.push('/settings/billing')}
  onIncreaseBudget={() => setApprovalDialogOpen(true)}
/>

<ApprovalRequestDialog
  open={approvalDialogOpen}
  onOpenChange={setApprovalDialogOpen}
  isAdmin={isAdmin}
  onSuccess={() => mutate()}
/>

Email Templates

1. Request Notification (to Admins)

**Subject:** 🔔 Budget Approval Requested - user@example.com

**Content:**

  • Requester email
  • Additional budget amount
  • Reason for request
  • Request ID
  • Link to approval page

2. Approval Notification (to Requester)

**Subject:** ✅ Budget Override Approved

**Content:**

  • Additional budget amount
  • Override expiration date/time
  • Duration of override
  • Admin notes (if provided)

3. Denial Notification (to Requester)

**Subject:** ❌ Budget Override Request Denied

**Content:**

  • Requested amount
  • Original reason
  • Admin notes explaining denial

Security & Permissions

Create Request

  • **Role Required:** Admin, Owner, Super Admin
  • **Validation:**
  • additional_budget_requested > 0
  • reason.length >= 10 characters

Review (Approve/Deny)

  • **Role Required:** Admin, Owner, Super Admin
  • **Validation:**
  • Can only review pending requests
  • Override duration: 1-168 hours
  • Denial requires reviewer notes

List Requests

  • **Admins:** See all tenant requests
  • **Non-Admins:** See only their own requests

Database Migration

**Migration File:** /Users/rushiparikh/projects/atom-saas/backend-saas/alembic/versions/20260405_212245_ec6a654117b0.py

**Status:** Applied (stamped)

**Table Created:** budget_approval_requests

**Indexes:**

  • ix_budget_approval_requests_id (primary)
  • ix_budget_approval_requests_tenant_id
  • ix_budget_approval_requests_requester_id
  • ix_budget_approval_requests_status
  • idx_budget_approval_tenant_status (composite)
  • idx_budget_approval_created

Testing

Manual Testing Steps

  1. **Create Request:**
  1. **List Requests:**
  1. **Approve Request (Admin):**
  1. **Deny Request (Admin):**

Future Enhancements

  1. **Multi-step Approval:** Require multiple admins for large amounts
  2. **Auto-approval Rules:** Pre-approve requests under certain conditions
  3. **Request Templates:** Common reasons with pre-filled descriptions
  4. **Approval Analytics:** Track approval rates, average times, amounts
  5. **Budget Forecasting:** Show impact of approval on monthly budget
  6. **SLA Notifications:** Alert if request pending > X hours

Troubleshooting

Email Not Sending

Check email service configuration:

# Backend
echo $EMAIL_PROVIDER  # smtp, sendgrid, or ses
echo $EMAIL_FROM_ADDRESS

Migration Issues

If migration fails, check table exists:

from core.database import engine
import sqlalchemy as sa

inspector = sa.inspect(engine)
print(inspector.get_table_names())

Permission Errors

Verify user role:

console.log(session?.user?.role)  // Should be "admin", "owner", or "super_admin"

Files Modified

  1. /Users/rushiparikh/projects/atom-saas/backend-saas/core/models.py
  • Added BudgetApprovalRequest model
  • Fixed PricingFetchLog indexes
  1. /Users/rushiparikh/projects/atom-saas/backend-saas/api/routes/billing_routes.py
  • Added 4 new endpoints
  • Email notification integration
  1. /Users/rushiparikh/projects/atom-saas/backend-saas/alembic/versions/20260405_212245_ec6a654117b0.py
  • Database migration
  1. /Users/rushiparikh/projects/atom-saas/src/components/billing/ApprovalRequestDialog.tsx
  • New React component
  1. /Users/rushiparikh/projects/atom-saas/backend-saas/alembic/versions/20260405_063936_c1a5813cfd35.py
  • Fixed missing imports (String, Text)

Deployment Checklist

  • [ ] Database migration applied
  • [ ] Email service configured (SMTP/SendGrid/SES)
  • [ ] FRONTEND_URL environment variable set
  • [ ] Test with real admin accounts
  • [ ] Verify email notifications work
  • [ ] Check budget enforcement respects approved overrides
  • [ ] Monitor approval request volume

Support

For issues or questions:

  1. Check logs: /Users/rushiparikh/projects/atom-saas/backend-saas/logs/
  2. Review database: SELECT * FROM budget_approval_requests ORDER BY created_at DESC LIMIT 10;
  3. Test email service: Use core.email_service.email_service.send_email() directly