API Reference
Rates
Manage organization rates and effective rate versions.
REST /api/v1API key authentication7 endpoints
GET
/api/v1/ratesList rates with filters
Query schema
const listQuerySchema = z.object({
rateType: z.enum(['billable', 'cost', 'other']).optional(),
isActive: z
.string()
.transform((val) => val === 'true')
.optional(),
isDefault: z
.string()
.transform((val) => val === 'true')
.optional(),
search: z.string().optional(),
tags: z
.string()
.transform((val) => val.split(','))
.optional(),
limit: z.coerce.number().int().min(1).max(100).default(50),
offset: z.coerce.number().int().min(0).default(0),
});Response200JSON response with data and pagination envelopes.
POST
/api/v1/ratesCreate a new rate with initial version
JSON body schema
const createBodySchema = z.object({
code: z.string().optional(),
name: z.string().min(1),
description: z.string().optional(),
rateType: z.enum(['billable', 'cost', 'other']).optional(),
currency: z.string().length(3).optional(),
period: z.enum(['hour', 'day', 'week', 'month', 'year']).optional(),
hoursPerPeriod: z.number().min(0).optional(),
applicableRole: z.string().optional(),
isDefault: z.boolean().optional(),
tags: z.array(z.string()).optional(),
amount: z.number().min(0),
effectiveFrom: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
effectiveTo: z
.string()
.regex(/^\d{4}-\d{2}-\d{2}$/)
.optional(),
changeReason: z.string().optional(),
});Response201JSON response with a data envelope.
DELETE
/api/v1/rates/:idDeactivate a rate (soft delete)
Path parametersid: string
Response204No response body.
GET
/api/v1/rates/:idGet rate by ID with current version
Path parametersid: string
Response200JSON response with a data envelope.
PATCH
/api/v1/rates/:idUpdate rate properties (not amount - use versions for that)
Path parametersid: string
JSON body schema
const updateBodySchema = z.object({
name: z.string().min(1).optional(),
description: z.string().nullable().optional(),
rateType: z.enum(['billable', 'cost', 'other']).optional(),
currency: z.string().length(3).optional(),
period: z.enum(['hour', 'day', 'week', 'month', 'year']).optional(),
hoursPerPeriod: z.number().min(0).nullable().optional(),
applicableRole: z.string().nullable().optional(),
isDefault: z.boolean().optional(),
tags: z.array(z.string()).optional(),
});Response200JSON response with a data envelope.
GET
/api/v1/rates/:id/versionsList all versions for a rate
Path parametersid: string
Response200JSON response with a data envelope.
POST
/api/v1/rates/:id/versionsCreate a new version for a rate (used when changing the amount)
Path parametersid: string
JSON body schema
const createVersionBodySchema = z.object({
amount: z.number().min(0),
effectiveFrom: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
effectiveTo: z
.string()
.regex(/^\d{4}-\d{2}-\d{2}$/)
.optional(),
changeReason: z.string().optional(),
});Response201JSON response with a data envelope.