Work Logging & Rate Calculation
The Wingspan Work Logging API transforms how businesses track and pay for contractor work. Instead of wrestling with spreadsheets, manual calculations, and error-prone processes, you can define structured work types, automate complex rate calculations, and seamlessly convert tracked work into invoices.
Introduction & Overview
What is Work Logging?
Work Logging is Wingspan's structured approach to capturing contractor work data. Unlike traditional timesheets or free-form invoices, Work Logging:
- Enforces consistency through predefined work templates
- Automates calculations using configurable rate cards and formulas
- Integrates seamlessly with Wingspan's payment ecosystem
- Scales effortlessly from simple hourly tracking to complex multi-factor calculations
Key Benefits
For businesses managing contractors, Work Logging delivers:
- Reduced Manual Effort: Eliminate hours spent on spreadsheet calculations and data verification
- Improved Accuracy: Minimize errors from manual rate application and data entry
- Enhanced Transparency: Both contractors and payers see exactly how payments are calculated
- Scalability: Handle hundreds of contractors without proportional increases in administrative work
- Flexibility: Support diverse payment models from simple hourly to complex tiered structures
Prerequisites
Before implementing Work Logging, you'll need:
- A Wingspan account with API access
- At least one contractor relationship (PayerPayee) established
- An understanding of your payment structure and rates
- Your Wingspan Account Manager to enable Work Logging for specific engagements
`
Core Concepts & Architecture
The system is built around a set of interconnected objects that model the entire work-to-payment lifecycle. Understanding their relationships is key to using the API effectively.
Object Model Overview
-
Engagement Type (
/payments/engagement
): A reusable template or "type" that defines a category of work. It links together a Work Definition (the "what"), a Rate Calculation (the "how to calculate"), and a default Rate Card (the "rates"). -
Payer-Payee Engagement: The specific assignment of a contractor to an Engagement. This is the central object that provides context for all work performed. It can override the default Rate Card with custom rates for that specific assignment.
-
Work Definition (
/payments/work-definition
): The schema or blueprint for a type of work. It defines the specific data fields (attributes) that must be captured, like "hours", "miles", or "units completed", along with their validation rules. The same definition can be applied to multiple engagements. -
Rate Card (
/payments/rate-card
): A collection of named rate values (e.g., hourlyRate: 75, mileageRate: 0.65). A default Rate Card is set on the Engagement, but a separate Rate Card can be linked to a Payer-Payee Engagement to handle custom rates. -
Rate Calculation: The logic engine that defines the formulas for calculating pay. It uses attributes from a Work Item and values from a Rate Card to compute the final amount.
-
Work Log (
/payments/work-log
): A container or batch that groups multiple Work Items together for a specific Payer-Payee Engagement. It's used to manage submission and approval workflows. Can be converted to an invoice. Only one work log can be opened at a time. -
Work Item (
/payments/work-item
): The most granular object—a single, atomic record of work performed. It must conform to a Work Definition and is always contained within a Work Log.
The Work Logging Hierarchy
graph TD
A[Work Definition<br/>Template] -->|defines structure for| B[Work Items<br/>Individual entries]
B -->|grouped within| C[Work Log<br/>Time period container]
C -->|uses rates from| D[Rate Card<br/>Pricing values]
D -->|processed by| E[Rate Calculation<br/>Formula engine]
E -->|generates| F[Invoice<br/>Payment document]
Conceptual Flow Diagram
flowchart TD
A[Payer creates Engagement Type] --> B[Contractor assigned to Engagement<br/>PayerPayeeEngagement]
B --> C[Contractor creates Work Log for period]
C --> D[Contractor adds Work Items to Log]
D --> E[System validates against Work Definition]
E --> F[System applies Rate Calculation using Rate Card]
F --> G[Work Log converted to Invoice]
G --> H[Invoice approved and paid]
Data Flow: From Work to Payment
flowchart LR
A[Contractor logs work] --> B[Work Item created]
B --> C[Validated against<br/>Work Definition]
C --> D[Work Items grouped<br/>in Work Log]
D --> E[Rate Calculation applied<br/>using Rate Card]
E --> F[Work Log converted<br/>to Invoice]
F --> G[Invoice approved]
G --> H[Payment processed]
API Reference & Technical Specifications
1. Engagement Type
Engagements act as templates for work.
Create an Engagement Type
Creates a new Engagement which serves as a reusable template.
Endpoint: POST /payments/engagement
Request Object: IEngagementCreateRequest
Request Body
{
"type": "string",
"name": "string",
"description": "string",
"rateCardId": "string",
"requirementDefinitionIds": [
"string"
],
"workDefinitions": [
{
"workDefinitionId": "string",
"rateCalculationId": "string"
}
]
}
Field Descriptions
Field | Type | Required | Description |
---|---|---|---|
type | String | Yes | The type of engagement. Currently only ContractorVendor |
name | String | Yes | A unique, human-readable name for the engagement |
description | String | No | A short description of this engagement |
rateCardId | String | No | The ID of the default Rate Card to use for this engagement |
requirementDefinitionIds | Array | No | An array of requirement definition IDs that apply to this engagement |
workDefinitions | Array | No | An array of objects linking Work Definitions and their corresponding Rate Calculations |
workDefinitions[].workDefinitionId | String | Yes | The ID of the Work Definition |
workDefinitions[].rateCalculationId | String | No | The ID of the Rate Calculation to process work for this definition |
Sample Response (IEngagementResponse)
{
"id": "string",
"userId": "string",
"type": "string",
"name": "string",
"description": "string",
"rateCardId": "string",
"requirementDefinitionIds": [
"string"
],
"workDefinitions": [
{
"workDefinitionId": "string",
"rateCalculationId": "string"
}
],
"createdAt": "string",
"updatedAt": "string"
}
Add Work Definition to Engagement Type
Attaches an existing work definition to a specific engagement.
Endpoint: POST /payments/engagement/{id}/work-definition
Request Object: IEngagementAddWorkDefinitionRequest
Request Body
{
"workDefinitionId": "string",
"rateCalculationId": "string"
}
Field | Type | Required | Description |
---|---|---|---|
workDefinitionId | String | Yes | The ID of the Work Definition to attach to the engagement |
rateCalculationId | String | No | The ID of the Rate Calculation to associate with this work definition for this engagement |
Sample Response
The full, updated engagement object is returned.
Remove Work Definition from Engagement Type
Detaches a work definition from a specific engagement. Will not affect existing WorkLogs associated with this engagement.
Endpoint: DELETE /payments/engagement/{id}/work-definition/{workDefinitionId}
This endpoint does not require a request body. The id
of the engagement and the workDefinitionId
to be removed are specified in the URL.
Sample Response
The full, updated engagement object is returned after the work definition has been removed.
2. Work Definitions
Work Definitions provide the schema for logging work.
Create a Work Definition
Creates a new Work Definition, which specifies the structure and validation rules for a type of work.
Endpoint: POST /payments/work-definition
Request Object: IWorkDefinitionCreateRequest
Request Body
{
"name": "string",
"description": "string",
"attributeDefinitions": [
{
"key": "string",
"name": "string",
"type": "string",
"required": true,
"validationRules": {
"minimum": 0
}
}
]
}
Field Descriptions
Field | Type | Required | Description |
---|---|---|---|
name | String | Yes | A unique, human-readable name for the definition |
description | String | No | A detailed description of this work definition |
attributeDefinitions | Array | Yes | An array of objects defining the data fields for this work type |
attributeDefinitions[].key | String | Yes | The technical key for this attribute (used in APIs and calculations) |
attributeDefinitions[].name | String | Yes | The user-friendly display name for this attribute |
attributeDefinitions[].type | String | Yes | Data type. Must be Boolean , String , Number , or DateTime |
attributeDefinitions[].required | Boolean | Yes | Whether this attribute is required when creating a Work Item |
attributeDefinitions[].validationRules | Object | No | A set of validation rules for the attribute's value |
Sample Response (IWorkDefinitionResponse)
{
"id": "string",
"userId": "string",
"name": "string",
"description": "string",
"attributeDefinitions": [
{
"key": "string",
"name": "string",
"type": "string",
"required": true,
"validationRules": {
"minimum": 0
}
}
],
"createdAt": "string",
"updatedAt": "string"
}
3. Rate Cards
Rate Cards store the specific rates used in calculations. Rates and everything required in rate calculations like modifiers, flat and hourly etc.
Create a Rate Card
Creates a new Rate Card containing a set of named rate values.
Endpoint: POST /payments/rate-card
Request Object: IRateCardCreateRequest
Request Body
{
"name": "string",
"description": "string",
"values": {
"yourRateKey": {
"key": "string",
"name": "string",
"value": 0.0,
"description": "string"
}
}
}
Field Descriptions
Field | Type | Required | Description |
---|---|---|---|
name | String | Yes | A unique, human-readable name for the rate card |
description | String | No | A description of the rate card's purpose |
values | Object | Yes | A key-value map of the rates. The top-level key must match the values.key |
values.{key}.key | String | Yes | The technical key for this rate value, used in Rate Calculation formulas |
values.{key}.name | String | Yes | A user-friendly name for this rate |
values.{key}.value | Number | Yes | The numerical rate value |
values.{key}.description | String | No | An optional description for this specific rate |
Sample Response (IRateCardResponse)
{
"id": "string",
"userId": "string",
"name": "string",
"description": "string",
"values": {
"yourRateKey": {
"key": "string",
"name": "string",
"value": 0.0,
"description": "string"
}
},
"createdAt": "string",
"updatedAt": "string"
}
4. Work Logs & Work Items
Work is captured in Work Items, which are grouped inside Work Logs. You must create a Work Log first before you can add Work Items to it. Can only log into opened work log. Can't add to closed and can't edit items in closed work logs.
Create a Work Log
Creates a new Work Log to act as a container for Work Items.
Endpoint: POST /payments/work-log
Request Object: IWorkLogCreateRequest
Request Body
{
"payeeEngagementId": "string",
"periodStartDate": "string",
"periodEndDate": "string"
}
Field Descriptions
Field | Type | Required | Description |
---|---|---|---|
payeeEngagementId | String | Yes | The ID of the Payer-Payee Engagement this log belongs to |
periodStartDate | String | No | The start date of the period this log covers (ISO 8601 format) |
periodEndDate | String | No | The end date of the period this log covers (ISO 8601 format) |
Sample Response (IWorkLogResponse)
{
"id": "string",
"payeeEngagementId": "string",
"status": "string",
"periodStartDate": "string",
"periodEndDate": "string",
"createdAt": "string",
"updatedAt": "string"
}
Create a Work Item
Creates a single, atomic record of work.
Endpoint: POST /payments/work-item
Request Object: IWorkItemCreateRequest
Request Body
{
"workLogId": "string",
"workDefinitionId": "string",
"timestamp": "string",
"attributes": {
"attributeKey1": "value",
"attributeKey2": 0
}
}
Field Descriptions
Field | Type | Required | Description |
---|---|---|---|
workLogId | String | Yes | The ID of the parent Work Log this item belongs to |
workDefinitionId | String | Yes | The ID of the Work Definition this item must conform to |
attributes | Object | Yes | A key-value map holding the specific data for this work instance, as defined in the Work Definition |
rateCalculationId | String | No | Can be specified to force a rate calculation. Usually derived from context |
rateCardId | String | No | Can be specified to force a rate card. Usually derived from context |
timestamp | String | No | The primary timestamp for the work event (ISO 8601). Defaults to creation time if omitted |
Sample Response (IWorkItemResponse)
{
"id": "string",
"workLogId": "string",
"workDefinitionId": "string",
"timestamp": "string",
"attributes": {
"attributeKey1": "value",
"attributeKey2": 0
},
"calculations": null,
"status": "string",
"createdAt": "string",
"updatedAt": "string"
}
Getting Started: Implementation Guide
Let's walk through implementing a simple hourly billing scenario. This example will demonstrate all the core concepts in action.
Scenario: Hourly Consulting Services
You run a consulting firm where contractors bill hourly, with a higher rate for weekend work.
Step 1: Create Your First Work Definition
Start by defining the structure for hourly work entries:
POST /payments/work-definition
{
"name": "Hourly Consulting Work",
"description": "Standard hourly work entry for consulting services",
"attributeDefinitions": [
{
"key": "date",
"name": "Work Date",
"description": "Date when work was performed",
"type": "DateTime",
"required": true
},
{
"key": "hours",
"name": "Hours Worked",
"description": "Number of hours spent",
"type": "Number",
"required": true,
"validationRules": {
"minimum": 0.25,
"maximum": 12
}
},
{
"key": "projectCode",
"name": "Project Code",
"description": "Client project identifier",
"type": "String",
"required": true,
"validationRules": {
"regex": "^PROJ-[0-9]{4}$"
}
},
{
"key": "isWeekend",
"name": "Weekend Work",
"description": "Was this work performed on a weekend?",
"type": "Boolean",
"required": false
},
{
"key": "description",
"name": "Work Description",
"description": "Detailed description of work performed",
"type": "String",
"required": true,
"validationRules": {
"minimum": 10,
"maximum": 500
}
}
]
}
This definition ensures contractors provide all necessary information in a consistent format.
Step 2: Create a Basic Rate Card
Define the monetary values for your calculations:
POST /payments/rate-card
{
"name": "Standard Consulting Rates 2024",
"description": "Default rates for consulting contractors",
"values": {
"standardHourlyRate": {
"key": "standardHourlyRate",
"value": 125,
"name": "Standard Hourly Rate",
"description": "Regular weekday hourly rate"
},
"weekendMultiplier": {
"key": "weekendMultiplier",
"value": 1.5,
"name": "Weekend Multiplier",
"description": "Multiplier for weekend work"
}
}
}
Step 3: Understanding Rate Calculations
Your Wingspan Account Manager will configure a Rate Calculation that:
- Checks if
isWeekend
is true - If yes:
hours * standardHourlyRate * weekendMultiplier
- If no:
hours * standardHourlyRate
Step 4: The Contractor Experience
When a contractor with an active engagement logs work:
- They create a Work Log for the current period:
POST /payments/work-log
{
"payerPayeeEngagementId": "ppe_abc123",
"startDate": "2024-06-01T00:00:00Z"
}
- They add Work Items to the log:
POST /payments/work-item
{
"workLogId": "wl_xyz789",
"workDefinitionId": "wd_hourly_consulting",
"timestamp": "2024-06-03T14:00:00Z",
"attributes": {
"date": "2024-06-03T00:00:00Z",
"hours": 8,
"projectCode": "PROJ-1234",
"isWeekend": false,
"description": "Developed API documentation and conducted code review for authentication module"
}
}
The system automatically calculates: 8 hours × $125 = $1,000
- For weekend work:
{
"attributes": {
"date": "2024-06-08T00:00:00Z",
"hours": 4,
"projectCode": "PROJ-1234",
"isWeekend": true,
"description": "Emergency production issue resolution"
}
}
The system calculates: 4 hours × $125 × 1.5 = $750
Step 5: Converting to an Invoice
Once all work for the period is logged:
POST /payments/work-log/wl_xyz789/convert
This creates an invoice with line items:
- Regular Hours: 8 × $125 = $1,000
- Weekend Hours: 4 × $187.50 = $750
- Total: $1,750
The invoice then follows Wingspan's standard approval and payment flow.
Work Definition Design & Best Practices
Creating well-designed Work Definitions is crucial for successful work logging implementation. They serve as the foundation for data quality, calculation accuracy, and user experience.
Planning Your Data Structure
What to Include
Focus on attributes that are:
-
Essential for payment calculation
- Hours, units, quantities
- Flags that affect rates (overtime, holiday, location)
- Service or task identifiers
-
Required for compliance or reporting
- Project codes for cost allocation
- Client identifiers
- Regulatory requirements
-
Valuable for business insights
- Task categories
- Completion status
- Quality indicators
What to Leave Out
Avoid attributes that:
- Change frequently - Use engagement-level data instead
- Are purely descriptive - Keep descriptions concise
- Duplicate existing data - Don't recreate contractor profile information
- Create unnecessary complexity - Start simple, add attributes as needed
Balancing Flexibility with Structure
Too Rigid
{
"attributeDefinitions": [
{
"key": "taskType",
"type": "String",
"validationRules": {
"enumConfig": {
"acceptedValues": ["Task1", "Task2", "Task3"]
}
}
}
]
}
Problem: Can't add new task types without updating the definition.
Too Flexible
{
"attributeDefinitions": [
{
"key": "workData",
"type": "String",
"description": "Enter all work details"
}
]
}
Problem: No structure for calculations or reporting.
Just Right
{
"attributeDefinitions": [
{
"key": "primaryTaskType",
"type": "String",
"validationRules": {
"enumConfig": {
"acceptedValues": ["Development", "Review", "Documentation", "Other"]
}
}
},
{
"key": "taskDetails",
"type": "String",
"required": false,
"description": "Additional details if 'Other' selected"
}
]
}
Validation Strategies
Numeric Validations
Use min/max for reasonable bounds:
{
"key": "hours",
"type": "Number",
"validationRules": {
"minimum": 0.25, // 15-minute minimum
"maximum": 24 // Sanity check
}
}
String Patterns
Use regex for structured data:
{
"key": "caseNumber",
"type": "String",
"validationRules": {
"regex": "^CASE-[0-9]{6}-[A-Z]{2}$" // CASE-123456-CA
}
}
Static Enumerations
For fixed sets of values:
{
"key": "serviceType",
"type": "String",
"validationRules": {
"enumConfig": {
"acceptedValues": ["Consultation", "Implementation", "Training", "Support"]
}
}
}
Dynamic Enumerations
For values that vary by engagement:
{
"key": "locationCode",
"type": "String",
"validationRules": {
"enumConfig": {
"resourceType": "PayerPayeeEngagement",
"valuePath": "payerOwnedData.customFields.approvedLocations"
}
}
}
Real-World Examples
Example 1: Hourly Consulting with Location Tracking
Use Case: IT consultants working at different client sites with location-based rate adjustments.
{
"name": "IT Consulting with Location",
"attributeDefinitions": [
{
"key": "workDate",
"name": "Date",
"type": "DateTime",
"required": true
},
{
"key": "startTime",
"name": "Start Time",
"type": "DateTime",
"required": true
},
{
"key": "endTime",
"name": "End Time",
"type": "DateTime",
"required": true
},
{
"key": "location",
"name": "Work Location",
"type": "String",
"required": true,
"validationRules": {
"enumConfig": {
"acceptedValues": ["Remote", "ClientSite", "DataCenter", "Conference"]
}
}
},
{
"key": "clientCode",
"name": "Client Code",
"type": "String",
"required": true,
"validationRules": {
"regex": "^[A-Z]{3}[0-9]{3}$"
}
},
{
"key": "ticketNumbers",
"name": "Ticket Numbers",
"description": "Comma-separated ticket IDs",
"type": "String",
"required": false
},
{
"key": "workSummary",
"name": "Work Summary",
"type": "String",
"required": true,
"validationRules": {
"minimum": 20,
"maximum": 200
}
}
]
}
Calculation Logic: Different hourly rates apply based on location. DataCenter work receives hazard pay multiplier.
Example 2: Healthcare Services with Unit Billing
Use Case: Mental health providers billing in 15-minute units with different service types.
{
"name": "Therapy Session Billing",
"attributeDefinitions": [
{
"key": "sessionDate",
"name": "Session Date",
"type": "DateTime",
"required": true
},
{
"key": "patientId",
"name": "Patient ID",
"type": "String",
"required": true,
"validationRules": {
"regex": "^P[0-9]{8}$"
}
},
{
"key": "serviceCode",
"name": "Service Code",
"type": "String",
"required": true,
"validationRules": {
"enumConfig": {
"acceptedValues": ["90834", "90837", "90847", "90853", "99404"]
}
}
},
{
"key": "duration",
"name": "Session Duration (minutes)",
"type": "Number",
"required": true,
"validationRules": {
"minimum": 15,
"maximum": 90
}
},
{
"key": "modality",
"name": "Service Modality",
"type": "String",
"required": true,
"validationRules": {
"enumConfig": {
"acceptedValues": ["InPerson", "Telehealth", "Phone"]
}
}
},
{
"key": "isInitialAssessment",
"name": "Initial Assessment",
"type": "Boolean",
"required": false
}
]
}
Calculation Logic: Duration converts to units (15-min increments), rates vary by service code and modality.
Example 3: Field Services with Complex Requirements
Use Case: Insurance adjusters with per-claim payments, mileage, and conditional fees.
{
"name": "Insurance Claim Adjustment",
"attributeDefinitions": [
{
"key": "claimNumber",
"name": "Claim Number",
"type": "String",
"required": true,
"validationRules": {
"regex": "^CL-[0-9]{10}$"
}
},
{
"key": "inspectionDate",
"name": "Inspection Date",
"type": "DateTime",
"required": true
},
{
"key": "claimType",
"name": "Claim Type",
"type": "String",
"required": true,
"validationRules": {
"enumConfig": {
"acceptedValues": ["Property", "Auto", "Liability", "Workers Comp"]
}
}
},
{
"key": "complexity",
"name": "Claim Complexity",
"type": "String",
"required": true,
"validationRules": {
"enumConfig": {
"acceptedValues": ["Simple", "Moderate", "Complex", "Catastrophic"]
}
}
},
{
"key": "milesDriven",
"name": "Miles Driven",
"type": "Number",
"required": true,
"validationRules": {
"minimum": 0,
"maximum": 500
}
},
{
"key": "photosUploaded",
"name": "Photos Uploaded",
"type": "Number",
"required": true,
"validationRules": {
"minimum": 0
}
},
{
"key": "ladderRequired",
"name": "Ladder/Roof Access Required",
"type": "Boolean",
"required": false
},
{
"key": "reportCompleted",
"name": "Report Completed",
"type": "Boolean",
"required": true
}
]
}
Calculation Logic: Base fee by complexity, mileage reimbursement, photo upload bonus, ladder access fee, completion bonus only if report completed.
Example 4: Creative Services with Milestone Tracking
Use Case: Graphic designers working on projects with defined phases and revision limits.
{
"name": "Design Project Milestone",
"attributeDefinitions": [
{
"key": "projectId",
"name": "Project ID",
"type": "String",
"required": true
},
{
"key": "milestoneType",
"name": "Milestone Type",
"type": "String",
"required": true,
"validationRules": {
"enumConfig": {
"acceptedValues": ["Concept", "InitialDesign", "Revisions", "FinalDelivery"]
}
}
},
{
"key": "completionDate",
"name": "Completion Date",
"type": "DateTime",
"required": true
},
{
"key": "percentComplete",
"name": "Percentage Complete",
"type": "Number",
"required": true,
"validationRules": {
"minimum": 0,
"maximum": 100
}
},
{
"key": "revisionRound",
"name": "Revision Round",
"type": "Number",
"required": false,
"validationRules": {
"minimum": 1,
"maximum": 3
}
},
{
"key": "rushDelivery",
"name": "Rush Delivery",
"type": "Boolean",
"required": false
},
{
"key": "deliverables",
"name": "Deliverables List",
"description": "Comma-separated list of files delivered",
"type": "String",
"required": true
}
]
}
Calculation Logic: Fixed fees per milestone type, additional fee for revisions beyond round 2, rush delivery multiplier.
Rate Cards & Calculations Mastery
Rate Cards and Calculations form the financial engine of Work Logging. Understanding how to design and implement them effectively is crucial for accurate contractor payments.
Rate Card Design Principles
1. Use Descriptive Keys
Good:
"values": {
"standardHourlyRate": { "value": 75 },
"overtimeMultiplier": { "value": 1.5 },
"weekendPremium": { "value": 25 }
}
Avoid:
"values": {
"rate1": { "value": 75 },
"mult1": { "value": 1.5 },
"prem1": { "value": 25 }
}
2. Group Related Rates Logically
{
"name": "Comprehensive Service Rates",
"values": {
// Time-based rates
"hourlyRate": { "value": 100, "name": "Standard Hourly" },
"dailyRate": { "value": 750, "name": "Full Day Rate" },
// Multipliers
"overtimeMultiplier": { "value": 1.5, "name": "Overtime Multiplier" },
"holidayMultiplier": { "value": 2.0, "name": "Holiday Multiplier" },
// Fixed fees
"completionBonus": { "value": 50, "name": "Task Completion Bonus" },
"rushFee": { "value": 200, "name": "Rush Delivery Fee" },
// Reimbursements
"mileageRate": { "value": 0.655, "name": "Per Mile Rate" },
"mealAllowance": { "value": 50, "name": "Daily Meal Allowance" }
}
}
3. Consider Calculation Needs
Include all values your calculations might need:
{
"values": {
// Base rates
"perMinuteRate": { "value": 1.5 },
// Time-of-day adjustments
"dayShiftRate": { "value": 1.5 },
"nightShiftRate": { "value": 2.0 },
"overnightRate": { "value": 2.5 },
// Minimum guarantees
"minimumCallFee": { "value": 25 },
"minimumDailyGuarantee": { "value": 400 }
}
}
Versioning Strategies
Approach 1: Date-Named Rate Cards
Create new rate cards for each period:
{
"name": "Consultant Rates - 2024 Q1",
"description": "Effective January 1 - March 31, 2024"
}
{
"name": "Consultant Rates - 2024 Q2",
"description": "Effective April 1 - June 30, 2024"
}
Pros: Clear historical record, easy to reference
Cons: Requires updating engagement assignments quarterly
Approach 2: Effective Dating in Description
{
"name": "Standard Consultant Rates",
"description": "Version 3.0 - Effective April 1, 2024. Supersedes v2.0"
}
Pros: Simpler naming, clear versioning
Cons: Must track which version is active
Approach 3: Contract-Based Naming
{
"name": "Acme Corp Contract Rates 2024",
"description": "Per MSA dated Jan 1, 2024"
}
Pros: Directly tied to contracts
Cons: May need many similar rate cards
Handling Rate Changes
For Future Work
- Create new rate card with updated values
- Update engagement to reference new rate card
- Future work items use new rates automatically
For Rate Overrides
When a specific contractor needs different rates:
// 1. Clone the standard rate card
POST /payments/rate-card
{
"name": "John Smith - Custom Rates",
"description": "Negotiated rates effective June 1, 2024",
"values": {
"hourlyRate": {
"value": 125, // Standard is 100
"name": "Negotiated Hourly Rate"
}
// ... other values from standard card
}
}
// 2. Update their engagement to use the custom rate card
Simple vs. Complex Calculations
Simple: Basic Multiplication
Use Case: Hourly billing with no variations
Calculation: hours × hourlyRate
Rate Card:
{
"values": {
"hourlyRate": { "value": 85 }
}
}
Moderate: Conditional Rates
Use Case: Different rates for regular vs. overtime hours
If hours <= 40:
amount = hours × regularRate
Else:
amount = (40 × regularRate) + ((hours - 40) × overtimeRate)
Rate Card:
{
"values": {
"regularRate": { "value": 50 },
"overtimeRate": { "value": 75 }
}
}
Complex: Multi-Factor Calculations
Use Case: Healthcare provider with time-of-day, holiday, and seniority factors
baseAmount = minutes × perMinuteRate
If timeOfDay == "Night":
baseAmount = baseAmount × nightMultiplier
If isHoliday == true:
baseAmount = baseAmount × holidayMultiplier
If seniorityLevel == "Senior":
baseAmount = baseAmount + seniorityBonus
finalAmount = max(baseAmount, minimumCallFee)
Rate Card:
{
"values": {
"perMinuteRate": { "value": 1.0 },
"nightMultiplier": { "value": 1.5 },
"holidayMultiplier": { "value": 2.0 },
"seniorityBonus": { "value": 15 },
"minimumCallFee": { "value": 35 }
}
}
Updated about 19 hours ago