| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
A .NET Core 9 Web API for creating time activity with pay type and linking it to a project with complete OAuth 2.0 integration for QuickBooks Online.
Official Documentation: https://developer.intuit.com/app/developer/qbo/docs/workflows/track-time/get-started
git clone <repository-url>
cd SampleApp-EmployeeCompensation-DotNet
dotnet restore{
"QuickBooks": {
"ClientId": "YOUR_QUICKBOOKS_APP_CLIENT_ID",
"ClientSecret": "YOUR_QUICKBOOKS_APP_CLIENT_SECRET",
"RedirectUri": "http://localhost:5037/api/oauth/callback",
"Environment": "production"
}
}Note: Employee Compensation is only enabled in Production, so make sure to use production environment.
dotnet runThe application will be available at:
The application requires these QuickBooks scopes:
The application includes a comprehensive web-based setup wizard that guides you through the complete configuration process:
OAuth Authentication
System Pre-Checks
Employee Data Fetching
Compensation Data
Project Management
Customer Information
Item Data
Time Activity
Simply navigate to http://localhost:5037 in your browser after starting the application. The wizard will guide you through each step automatically.
As this is a sample application to show the integration with QuickBooks, the tokens are stored in a token.json file In a production application, you should store the tokens with AES-256 encryption (recommended) in a database or secure key vault.
All examples below have been tested with the running application and include actual response data.
curl -s "http://localhost:5037/api/employeecompensation/health" | jq .Response:
{
"success": true,
"data": {
"status": "Healthy",
"isAuthenticated": true,
"realmId": "9341452071117966",
"tokenExpiresAt": "2025-09-11T10:40:45.357736Z",
"timestamp": "2025-09-11T09:40:45.362488Z"
},
"errorMessage": null,
"validationErrors": null
}curl -s "http://localhost:5037/api/oauth/status" | jq .Response:
{
"success": true,
"data": {
"isAuthenticated": true,
"realmId": "9341452071117966",
"expiresAt": "2025-09-11T10:40:45.357736Z",
"isExpired": false,
"minutesUntilExpiry": 59
},
"errorMessage": null,
"validationErrors": null
}curl -s "http://localhost:5037/api/oauth/authorize" | jq .Response:
{
"success": true,
"data": {
"authorizationUrl": "https://appcenter.intuit.com/connect/oauth2?client_id=CLIENT_ID&response_type=code&scope=com.intuit.quickbooks.accounting%20com.intuit.quickbooks.payroll%20project-management.project%20openid%20payroll.compensation.read&redirect_uri=https%3A%2F%any.ngrok-free.app%2Fapi%2Foauth%2Fcallback&state=ewEiI0RZsViayAjhzY-tSWVZSqPzfouGVCQ0yrXFrSg",
"state": "ewEiI0RZsViayAjhzY-tSWVZSqPzfouGVCQ0yrXFrSg",
"message": "Redirect to this URL to authorize with QuickBooks"
},
"errorMessage": null,
"validationErrors": null
}curl -s "http://localhost:5037/api/setup/employees" | jq .Response:
{
"success": true,
"data": [
{
"id": "400000011",
"name": "Jane Smith",
"displayName": "Jane Smith",
"email": "janesmith@email.com",
"ssn": "",
"employeeNumber": "",
"active": true,
"hireDate": "2025-06-01T00:00:00",
"terminationDate": null,
"compensationItems": []
},
{
"id": "400000001",
"name": "John Doe",
"displayName": "John Doe",
"email": "johndoe@email.com",
"ssn": "",
"employeeNumber": "",
"active": true,
"hireDate": "2025-08-01T00:00:00",
"terminationDate": null,
"compensationItems": []
}
],
"errorMessage": null,
"validationErrors": null
}curl -s "http://localhost:5037/api/employeecompensation/employees/400000011" | jq .Response:
{
"success": true,
"data": {
"id": "400000011",
"name": "Jane Smith",
"displayName": "Jane Smith",
"email": "janesmith@email.com",
"ssn": "",
"employeeNumber": "",
"active": true,
"hireDate": "2025-06-01T00:00:00",
"terminationDate": null,
"compensationItems": []
},
"errorMessage": null,
"validationErrors": null
}curl -s "http://localhost:5037/api/setup/employees" | jq .Response:
{
"success": true,
"data": {
"employees": [
{
"id": "400000011",
"displayName": "Jane Smith",
"givenName": "Jane",
"familyName": "Smith",
"active": true,
"email": "janesmith@email.com",
"phone": null,
"employeeNumber": null,
"hireDate": "2025-06-01"
},
{
"id": "400000001",
"displayName": "John Doe",
"givenName": "John",
"familyName": "Doe",
"active": true,
"email": "johndoe@email.com",
"phone": null,
"employeeNumber": null,
"hireDate": "2025-08-01"
}
],
"pagination": {
"currentPage": 1,
"pageSize": 10,
"totalCount": 2,
"totalPages": 1,
"hasNextPage": false,
"hasPreviousPage": false
}
},
"errorMessage": null,
"validationErrors": null
}curl -s "http://localhost:5037/api/setup/company" | jq .Response:
{
"success": true,
"data": {
"id": "1",
"companyName": "Test",
"legalName": "Test",
"companyAddr": {
"id": "2",
"line1": null,
"line2": null,
"line3": null,
"line4": null,
"line5": null,
"city": null,
"country": "US",
"countryCode": null,
"county": null,
"countrySubDivisionCode": null,
"postalCode": "94012",
"postalCodeSuffix": null,
"lat": null,
"long": null,
"tag": null,
"note": null
},
"country": "US",
"fiscalYearStartMonth": 0
},
"errorMessage": null,
"validationErrors": null
}curl -s "http://localhost:5037/api/setup/customers" | jq .Response:
{
"success": true,
"data": [
{
"id": "8",
"displayName": "Test Customer 3",
"companyName": "Test Sandbox",
"contactInfo": "",
"billingAddress": "",
"shippingAddress": "",
"active": true,
"balance": 0,
"isProject": false,
"metaData": {
"createTime": "2024-04-04T14:57:07+05:30",
"lastUpdatedTime": "2025-09-10T20:51:27+05:30"
}
},
{
"id": "6",
"displayName": "Test Sandbox Customer 1",
"companyName": "Test Sandbox Customer 1",
"contactInfo": "Email: kavita.parmar@ottimate.com | Phone: +91 9619662681 | Mobile: +91 9619662681",
"billingAddress": "",
"shippingAddress": "",
"active": true,
"balance": 0,
"isProject": false,
"metaData": {
"createTime": "2024-04-04T14:40:39+05:30",
"lastUpdatedTime": "2025-09-10T20:50:33+05:30"
}
}
],
"errorMessage": null,
"validationErrors": null
}Note: QuickBooks Online has a feature where creating projects automatically generates corresponding customer records with IsProject = true. These are not actual customers but rather project placeholders that appear in the customer entity list. By filtering them out, we now show only genuine customer records.
curl -s "http://localhost:5037/api/setup/items" | jq .Response:
{
"success": true,
"data": [
{
"id": "7",
"name": "Hours",
"type": 8,
"active": true,
"description": null
},
{
"id": "6",
"name": "Services",
"type": 8,
"active": true,
"description": null
},
{
"id": "8",
"name": "Taxes",
"type": 8,
"active": true,
"description": null
},
{
"id": "9",
"name": "Wine",
"type": 4,
"active": true,
"description": null
}
],
"errorMessage": null,
"validationErrors": null
}curl -s "http://localhost:5037/api/setup/dashboard/timeactivities" | jq .Response:
{
"success": true,
"data": [
{
"id": "1073741829",
"txnDate": "2025-09-09T00:00:00",
"employeeRef": "400000011",
"employeeName": "400000011",
"customerRef": "8",
"customerName": "8",
"itemRef": "7",
"itemName": "7",
"hours": 8,
"minutes": 0,
"hourlyRate": 0,
"description": "Time activity created from setup wizard",
"billableStatus": "NotBillable",
"billable": false,
"totalHours": 8,
"metaData": {
"createTime": "2025-09-11T01:10:36+05:30",
"lastUpdatedTime": "2025-09-11T01:10:36+05:30"
}
}
],
"pagination": {
"currentPage": 1,
"pageSize": 20,
"totalItems": 6,
"totalPages": 1,
"hasNextPage": false,
"hasPreviousPage": false
}
}curl -s "http://localhost:5037/api/setup/dashboard/timeactivities?employeeId=400000011" | jq .curl -s "http://localhost:5037/api/setup/dashboard/timeactivities?startDate=2024-01-01&endDate=2024-12-31" | jq .curl -X POST -H "Content-Type: application/json" \
-d '{
"employeeId": "400000011",
"customerId": "8",
"projectId": "647933362",
"itemId": "7",
"date": "2024-01-15T00:00:00Z",
"hours": 8.0,
"minutes": 0,
"description": "Development work on project features"
}' \
"http://localhost:5037/api/setup/timeactivity" | jq .A given time activity can only have one ItemRef and one CustomerRef. For the scope of this application, when we choose multiple items, we will create multiple time activities. You will be able to see them in the dashboard.
curl -s "http://localhost:5037/api/setup/projects?DueDateFrom1=2025-01-01&DueDateTo1=2026-01-01" | jq .Response:
{
"success": true,
"data": [
{
"customerId": "8",
"id": "647540715",
"name": "Test 1",
"status": "IN_PROGRESS",
"description": "Test 1",
"dueDate": "2025-08-30T00:00:00.000Z",
"startDate": "",
"completedDate": "",
"active": true
},
{
"customerId": "8",
"id": "647933362",
"name": "project 1 test",
"status": "COMPLETE",
"description": "",
"dueDate": "2025-08-29T00:00:00.000Z",
"startDate": "",
"completedDate": "2025-08-28T09:09:49.012Z",
"active": true
}
],
"errorMessage": null,
"validationErrors": null
}curl -s "http://localhost:5037/api/setup/precheck" | jq .Response:
{
"success": true,
"data": {
"projectsEnabled": true,
"timeTrackingEnabled": true,
"preferencesAccessible": true,
"allChecksPassed": true
},
"errorMessage": null,
"validationErrors": null
}curl -s "http://localhost:5037/api/setup/precheck/projects" | jq .Response:
{
"success": true,
"data": {
"projectsEnabled": true
},
"errorMessage": null,
"validationErrors": null
}curl -s "http://localhost:5037/api/setup/precheck/timetracking" | jq .Response:
{
"success": true,
"data": {
"timeTrackingEnabled": true,
"message": "Time tracking is enabled in your QuickBooks account"
},
"errorMessage": null,
"validationErrors": null
}| Parameter | Type | Description | Example |
|---|---|---|---|
| employeeId | string | Filter by specific employee | 400000011 |
| customerId | string | Filter by specific customer | 8 |
| startDate | date | Start date (YYYY-MM-DD) | 2024-01-01 |
| endDate | date | End date (YYYY-MM-DD) | 2024-12-31 |
| page | int | Page number for pagination | 1 |
| pageSize | int | Items per page | 20 |
| Parameter | Type | Description | Example |
|---|---|---|---|
| DueDateFrom1 | date | Filter projects with due date >= this date | 2025-01-01 |
| DueDateTo1 | date | Filter projects with due date <= this date | 2026-01-01 |
| StartDateFrom1 | date | Filter projects with start date >= this date | 2025-01-01 |
| StartDateTo1 | date | Filter projects with start date <= this date | 2026-01-01 |
# Get valid employee IDs
curl -s "http://localhost:5037/api/setup/employees" | jq '.data.employees[].id'
# Get valid customer IDs
curl -s "http://localhost:5037/api/setup/customers" | jq '.data[].id'
# Get valid item IDs
curl -s "http://localhost:5037/api/setup/items" | jq '.data[].id'The API supports multiple compensation types:
{
"compensationType": "Salary",
"name": "Base Salary",
"effectiveDate": "2024-01-01",
"annualAmount": 75000,
"payFrequency": "Monthly"
}{
"compensationType": "Hourly",
"name": "Hourly Wage",
"effectiveDate": "2024-01-01",
"hourlyRate": 25.00,
"overtimeRate": 37.50
}{
"compensationType": "Commission",
"name": "Sales Commission",
"effectiveDate": "2024-01-01",
"commissionRate": 5.0,
"commissionBasis": "Gross Sales"
}{
"compensationType": "Bonus",
"name": "Performance Bonus",
"effectiveDate": "2024-01-01",
"bonusAmount": 5000,
"bonusType": "Performance"
}{
"compensationType": "Benefit",
"name": "Health Insurance",
"effectiveDate": "2024-01-01",
"employeeContribution": 100,
"employerContribution": 400,
"benefitType": "Health",
"provider": "Health Corp"
}├── Controllers/ # API Controllers │ ├── BaseController.cs # Base controller with common functionality │ ├── OAuthController.cs # OAuth 2.0 authentication endpoints │ ├── EmployeeCompensationController.cs # Employee compensation API endpoints │ └── SetupController.cs # Setup wizard API endpoints ├── Models/ # Data Models │ ├── SharedModels.cs # Common shared models │ ├── EmployeeCompensationModels.cs # Employee and compensation models │ ├── ProjectModels.cs # Project-related models │ ├── ProjectResponse.cs # Project response models │ ├── ProjectFilterOptions.cs # Project filtering options │ └── TimeActivityModels.cs # Time activity models ├── Services/ # Business Logic │ ├── ITokenManagerService.cs # Token management interface │ ├── TokenManagerService.cs # OAuth token management │ ├── IEmployeeCompensationService.cs # Employee compensation interface │ ├── EmployeeCompensationService.cs # Employee compensation business logic │ └── GraphQLHelper.cs # GraphQL query helper ├── wwwroot/ # Static Web Assets │ ├── css/ # Stylesheets │ │ ├── style.css │ │ └── components.css │ ├── js/ # JavaScript files │ │ ├── setup-wizard.js # Setup wizard functionality │ │ ├── dashboard.js # Dashboard functionality │ │ ├── api-service.js # API communication │ │ ├── templates.js # UI templates │ │ ├── models.js # JavaScript models │ │ ├── validation.js # Form validation │ │ ├── utils.js # Utility functions │ │ ├── constants.js # Application constants │ │ ├── state-manager.js # State management │ │ ├── event-bus.js # Event handling │ │ ├── loading-manager.js # Loading states │ │ ├── error-boundary.js # Error handling │ │ └── tests/ # JavaScript tests │ ├── index.html # Setup wizard UI │ └── dashboard.html # Dashboard UI ├── Properties/ # Project properties │ └── launchSettings.json ├── Program.cs # Application startup and configuration ├── QuickBooks-EmployeeCompensation-API.csproj #Application ├── QuickBooks-EmployeeCompensation-API.http ├── QuickBooks-EmployeeCompensation-API.sln ├── appsettings.json # Application configuration ├── appsettings.Development.json # Development configuration └── token.json # OAuth token storage (runtime)
The API uses a consistent error response format:
{
"success": false,
"errorMessage": "Error description",
"validationErrors": ["Field validation errors"]
}dotnet builddotnet testdotnet publish -c ReleaseCreate a Dockerfile:
FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY ./publish .
ENTRYPOINT ["dotnet", "QuickBooks-EmployeeCompensation-API.dll"]For support and questions:
| Back | FazBrowse Home | New Git URL |