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:

  1. Reduced Manual Effort: Eliminate hours spent on spreadsheet calculations and data verification
  2. Improved Accuracy: Minimize errors from manual rate application and data entry
  3. Enhanced Transparency: Both contractors and payers see exactly how payments are calculated
  4. Scalability: Handle hundreds of contractors without proportional increases in administrative work
  5. 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

  1. 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").

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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
FieldTypeRequiredDescription
typeStringYesThe type of engagement. Currently only ContractorVendor
nameStringYesA unique, human-readable name for the engagement
descriptionStringNoA short description of this engagement
rateCardIdStringNoThe ID of the default Rate Card to use for this engagement
requirementDefinitionIdsArrayNoAn array of requirement definition IDs that apply to this engagement
workDefinitionsArrayNoAn array of objects linking Work Definitions and their corresponding Rate Calculations
workDefinitions[].workDefinitionIdStringYesThe ID of the Work Definition
workDefinitions[].rateCalculationIdStringNoThe 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"
}
FieldTypeRequiredDescription
workDefinitionIdStringYesThe ID of the Work Definition to attach to the engagement
rateCalculationIdStringNoThe 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
FieldTypeRequiredDescription
nameStringYesA unique, human-readable name for the definition
descriptionStringNoA detailed description of this work definition
attributeDefinitionsArrayYesAn array of objects defining the data fields for this work type
attributeDefinitions[].keyStringYesThe technical key for this attribute (used in APIs and calculations)
attributeDefinitions[].nameStringYesThe user-friendly display name for this attribute
attributeDefinitions[].typeStringYesData type. Must be Boolean, String, Number, or DateTime
attributeDefinitions[].requiredBooleanYesWhether this attribute is required when creating a Work Item
attributeDefinitions[].validationRulesObjectNoA 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
FieldTypeRequiredDescription
nameStringYesA unique, human-readable name for the rate card
descriptionStringNoA description of the rate card's purpose
valuesObjectYesA key-value map of the rates. The top-level key must match the values.key
values.{key}.keyStringYesThe technical key for this rate value, used in Rate Calculation formulas
values.{key}.nameStringYesA user-friendly name for this rate
values.{key}.valueNumberYesThe numerical rate value
values.{key}.descriptionStringNoAn 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
FieldTypeRequiredDescription
payeeEngagementIdStringYesThe ID of the Payer-Payee Engagement this log belongs to
periodStartDateStringNoThe start date of the period this log covers (ISO 8601 format)
periodEndDateStringNoThe 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
FieldTypeRequiredDescription
workLogIdStringYesThe ID of the parent Work Log this item belongs to
workDefinitionIdStringYesThe ID of the Work Definition this item must conform to
attributesObjectYesA key-value map holding the specific data for this work instance, as defined in the Work Definition
rateCalculationIdStringNoCan be specified to force a rate calculation. Usually derived from context
rateCardIdStringNoCan be specified to force a rate card. Usually derived from context
timestampStringNoThe 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:

  1. Checks if isWeekend is true
  2. If yes: hours * standardHourlyRate * weekendMultiplier
  3. If no: hours * standardHourlyRate

Step 4: The Contractor Experience

When a contractor with an active engagement logs work:

  1. They create a Work Log for the current period:
POST /payments/work-log

{
  "payerPayeeEngagementId": "ppe_abc123",
  "startDate": "2024-06-01T00:00:00Z"
}
  1. 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

  1. 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:

  1. Essential for payment calculation

    • Hours, units, quantities
    • Flags that affect rates (overtime, holiday, location)
    • Service or task identifiers
  2. Required for compliance or reporting

    • Project codes for cost allocation
    • Client identifiers
    • Regulatory requirements
  3. Valuable for business insights

    • Task categories
    • Completion status
    • Quality indicators

What to Leave Out

Avoid attributes that:

  1. Change frequently - Use engagement-level data instead
  2. Are purely descriptive - Keep descriptions concise
  3. Duplicate existing data - Don't recreate contractor profile information
  4. 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

  1. Create new rate card with updated values
  2. Update engagement to reference new rate card
  3. 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 }
  }
}