Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Playbook — Quarterly Finance Reporting (for CFOs)

This playbook walks a CFO or finance lead through the end-of-quarter reporting cycle in Alloy. It covers verifying project setup, ensuring time entries are approved, setting labor rates, generating capitalization reports, exporting CSV for ERP import, and reviewing budget utilization — all through the API. The guide assumes you already have an Alloy account, an organization, and projects with time tracking enabled.

For the full time tracking API, see Time Tracking & Finance. For finance-specific setup, see For Finance Teams.

1. Prerequisites and Setup

Set shell variables so the examples are copy-pasteable:

BASE_URL="http://localhost:3000"
TOKEN="<your-access-token>"
ORG_ID="<your-org-uuid>"

Confirm you can reach the server:

curl -s "$BASE_URL/health" | jq .
{
  "status": "ok",
  "service": "alloy",
  "version": "0.1.0",
  "database": "...",
  "db_healthy": true,
  "migration_version": "...",
  "uptime_seconds": "..."
}

Check your identity and role:

curl -s "$BASE_URL/api/v1/auth/me" \
  -H "Authorization: Bearer $TOKEN" | jq .
{
  "user_id": "...",
  "org_id": "...",
  "email": "...",
  "role": "..."
}

You need Admin or Owner role to approve time entries and manage labor rates. Viewer role is sufficient for read-only report access.

CLI shortcut: alloy auth me

2. Audit Project Finance Fields

Before generating reports, verify every project has the correct finance metadata. List all projects in the organization:

curl -s "$BASE_URL/api/v1/projects?org_id=$ORG_ID&limit=50" \
  -H "Authorization: Bearer $TOKEN" | jq .
{
  "items": [
    {
      "id": "...",
      "org_id": "...",
      "key": "...",
      "name": "...",
      "description": "...",
      "ticket_counter": "...",
      "capitalization_type": "...",
      "development_phase": "...",
      "cost_center_id": "...",
      "budget_cents": "...",
      "budget_period": "..."
    }
  ],
  "next_cursor": null,
  "has_more": false
}

For each project, confirm these fields are set:

FieldWhat to check
capitalization_typeCapex or Opex — determines accounting treatment
development_phasePreliminary, AppDevelopment, or PostImplementation
cost_center_idMatches your GL chart of accounts
budget_centsCorrect for the quarter
budget_periodQuarterly for quarterly reporting

Fix any missing fields before proceeding:

curl -s -X PATCH "$BASE_URL/api/v1/projects/$PROJECT_ID" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{
    "capitalization_type": "Capex",
    "development_phase": "AppDevelopment",
    "cost_center_id": "ENG-002",
    "budget_cents": 10000000,
    "budget_period": "Quarterly"
  }' | jq .
{
  "id": "...",
  "org_id": "...",
  "key": "...",
  "name": "...",
  "description": "...",
  "ticket_counter": "...",
  "capitalization_type": "Capex",
  "development_phase": "AppDevelopment",
  "cost_center_id": "ENG-002",
  "budget_cents": 10000000,
  "budget_period": "Quarterly"
}

CLI shortcut: alloy project list and alloy project update

3. Verify Labor Rates

Capitalization reports multiply hours by loaded labor rates. Verify that every engineer who logged time has a current rate. Check rates for a specific user:

curl -s "$BASE_URL/api/v1/users/$USER_ID/orgs/$ORG_ID/labor-rates" \
  -H "Authorization: Bearer $TOKEN" | jq .
{
  "items": [
    {
      "id": "...",
      "user_id": "...",
      "org_id": "...",
      "loaded_rate_cents": "...",
      "effective_date": "...",
      "created_at": "...",
      "updated_at": "..."
    }
  ],
  "next_cursor": null,
  "has_more": false
}

If a user is missing a rate, or the rate needs updating for the new quarter, set one:

curl -s -X POST "$BASE_URL/api/v1/labor-rates" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"user_id\": \"$USER_ID\",
    \"org_id\": \"$ORG_ID\",
    \"loaded_rate_cents\": 17500,
    \"effective_date\": \"2026-04-01\"
  }" | jq .
{
  "id": "...",
  "user_id": "...",
  "org_id": "...",
  "loaded_rate_cents": 17500,
  "effective_date": "2026-04-01",
  "created_at": "...",
  "updated_at": "..."
}

A loaded_rate_cents of 17500 means $175.00/hour. The system uses the most recent rate on or before the time entry date, so historical reports remain accurate when rates change.

4. Review and Approve Pending Time Entries

Only Approved time entries appear in capitalization reports. Check for any submitted entries still awaiting approval:

curl -s "$BASE_URL/api/v1/time-entries/submitted" \
  -H "Authorization: Bearer $TOKEN" | jq .
{
  "items": [],
  "next_cursor": null,
  "has_more": false
}

When there are pending entries, each item includes id, user_id, ticket_id, project_id, date, duration_minutes, description, activity_type, and status (Submitted).

Approve each entry:

curl -s -X POST "$BASE_URL/api/v1/time-entries/$TIME_ENTRY_ID/approve" \
  -H "Authorization: Bearer $TOKEN" | jq .
{
  "id": "...",
  "user_id": "...",
  "ticket_id": "...",
  "project_id": "...",
  "date": "...",
  "duration_minutes": "...",
  "description": "...",
  "activity_type": "...",
  "status": "Approved",
  "approved_by": "...",
  "approved_at": "...",
  "created_at": "...",
  "updated_at": "..."
}

Ensure all time entries for the quarter are in Approved status before generating reports. Unapproved entries will not be included.

Tip: Send a reminder to engineering leads a week before quarter-end to submit all outstanding time entries.

5. Generate the Capitalization Report

Run the capitalization report for the quarter. Use the first month of the quarter as the period (the report aggregates the full quarter when budget_period is Quarterly):

curl -s "$BASE_URL/api/v1/reports/capitalization?period=2026-01" \
  -H "Authorization: Bearer $TOKEN" | jq .
{
  "period": "2026-01",
  "projects": []
}

When projects have approved time entries with labor rates configured, each project entry includes:

FieldDescription
project_idProject UUID
project_keyShort project key (e.g. PAY)
project_nameHuman-readable name
capitalization_typeCapex or Opex
development_phaseASC 350-40 phase
cost_center_idGL mapping code
total_hoursSum of approved hours
total_amount_centsHours multiplied by labor rates
breakdownPer-activity-type detail

6. Generate Budget Utilization Report

Add include_budget=true to see budget vs. actual spend:

curl -s "$BASE_URL/api/v1/reports/capitalization?period=2026-01&include_budget=true" \
  -H "Authorization: Bearer $TOKEN" | jq .
{
  "period": "2026-01",
  "projects": []
}

When there are approved entries, each project also includes:

FieldDescription
budget_centsTotal budget for the period
spent_centsApproved time cost for the period
budget_remaining_centsbudget_cents - spent_cents
budget_utilization_pctPercentage of budget consumed

Flag any projects over 90% utilization for CFO review.

7. Group by Cost Center

For department-level reporting, filter by cost center:

curl -s "$BASE_URL/api/v1/reports/capitalization?period=2026-01&cost_center_id=ENG-002" \
  -H "Authorization: Bearer $TOKEN" | jq .
{
  "period": "2026-01",
  "projects": []
}

This narrows the report to only projects assigned to cost center ENG-002. Run this for each cost center to produce department-level summaries for the CFO deck.

8. Group by Team

For team-level reporting, use the group_by=team parameter:

curl -s "$BASE_URL/api/v1/reports/capitalization?period=2026-01&group_by=team&include_budget=true" \
  -H "Authorization: Bearer $TOKEN" | jq .
{
  "period": "2026-01",
  "teams": []
}

When teams have approved time, each entry includes team_id, team_name, total_hours, total_amount_cents, and a projects breakdown with per-project budget utilization.

9. Export CSV for ERP Import

Export the full capitalization report as CSV for import into SAP, Oracle, NetSuite, or your spreadsheet workflow:

curl -s "$BASE_URL/api/v1/reports/capitalization/export?period=2026-01" \
  -H "Authorization: Bearer $TOKEN" -o capitalization-Q1-2026.csv

The CSV includes these columns:

Period,Project,ProjectKey,Employee,Department,CostCenter,Hours,ActivityType,Phase,CapExOpEx,LoadedRate,Amount,Team,Tags,BudgetCents,SpentCents,Utilization

Export filtered by cost center for department-level files:

curl -s "$BASE_URL/api/v1/reports/capitalization/export?period=2026-01&cost_center_id=ENG-002" \
  -H "Authorization: Bearer $TOKEN" -o eng-002-Q1-2026.csv

Export grouped by team:

curl -s "$BASE_URL/api/v1/reports/capitalization/export?period=2026-01&group_by=team" \
  -H "Authorization: Bearer $TOKEN" -o team-report-Q1-2026.csv

Automation tip: Schedule CSV exports as a cron job at the start of each month. The API is stateless — the same curl command produces a fresh report each time.

10. Review Activity Type Classification

During audit prep, verify that activity types are correctly classified. The classification determines CapEx vs. OpEx treatment:

Activity TypeCapEx EligibleNotes
CodingYesCore development work
TestingYesWriting and running tests
CodeReviewYesPull request reviews
DesignYesUI/UX design work
ArchitectureYesSystem design decisions
DocumentationYesTechnical documentation
DeploymentYesCI/CD and release work
BugFixingYes (Development)Only during AppDevelopment phase
PMDependsCapitalizable during AppDevelopment
RequirementsPlanning onlyCapitalizable during Preliminary phase
MeetingsDependsContext-dependent
TrainingNoAlways OpEx
MaintenanceNoAlways OpEx

Filter the report by activity type to spot-check classification:

curl -s "$BASE_URL/api/v1/reports/capitalization?period=2026-01&activity_type=Maintenance" \
  -H "Authorization: Bearer $TOKEN" | jq .
{
  "period": "2026-01",
  "projects": []
}

All Maintenance hours should appear as OpEx regardless of project phase. If capitalizable hours appear under Maintenance, flag the entries for reclassification.

11. Quarterly Close Checklist

Run through this checklist at the end of each quarter:

  1. Audit project fields — Verify capitalization_type, development_phase, cost_center_id, and budget_cents on every project (Section 2)
  2. Verify labor rates — Confirm all engineers have current loaded rates with correct effective dates (Section 3)
  3. Approve all time — Clear the submitted queue; reject or return any entries that need correction (Section 4)
  4. Generate capitalization report — Run for the quarter period with include_budget=true (Sections 5-6)
  5. Review by cost center — Generate per-department reports for GL mapping (Section 7)
  6. Review by team — Generate team-level reports for management review (Section 8)
  7. Export CSV — Export full and filtered CSVs for ERP import (Section 9)
  8. Spot-check activity types — Filter by Maintenance and Training to verify OpEx classification (Section 10)
  9. Archive exports — Store CSV files in your document management system with the quarter label
  10. Update budgets — Set budget_cents for the next quarter on all active projects
  11. Adjust rates — Add new labor rates with future effective dates for any pending promotions or annual adjustments
  12. Sign off — Record quarter-close completion in your audit trail

12. Automating the Quarterly Cycle

Script the entire quarterly cycle for hands-off execution. Here is the reporting portion as a shell script:

QUARTER="2026-01"
OUTPUT_DIR="./reports/Q1-2026"
mkdir -p "$OUTPUT_DIR"

Export the main capitalization report:

curl -s "$BASE_URL/api/v1/reports/capitalization/export?period=2026-01" \
  -H "Authorization: Bearer $TOKEN" -o "$OUTPUT_DIR/capitalization.csv"

Export by cost center (repeat for each department):

curl -s "$BASE_URL/api/v1/reports/capitalization/export?period=2026-01&cost_center_id=ENG-001" \
  -H "Authorization: Bearer $TOKEN" -o "$OUTPUT_DIR/eng-001.csv"

Export by team:

curl -s "$BASE_URL/api/v1/reports/capitalization/export?period=2026-01&group_by=team" \
  -H "Authorization: Bearer $TOKEN" -o "$OUTPUT_DIR/by-team.csv"

MCP shortcut: Use the get_capitalization_report and get_time_report MCP tools to pull reports programmatically from any MCP-compatible client.


Further reading: