API Reference

Rates

Manage organization rates and effective rate versions.

REST /api/v1API key authentication7 endpoints
GET/api/v1/rates

List 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/rates

Create 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/:id

Deactivate a rate (soft delete)

Path parametersid: string
Response204No response body.
GET/api/v1/rates/:id

Get rate by ID with current version

Path parametersid: string
Response200JSON response with a data envelope.
PATCH/api/v1/rates/:id

Update 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/versions

List all versions for a rate

Path parametersid: string
Response200JSON response with a data envelope.
POST/api/v1/rates/:id/versions

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