Skip to content

Rate Cards API

Rate cards define the hourly rates your organisation bills for each role. There are two layers:

  • Organisation rate-card roles are the base rates, one per role (for example “Senior Designer” at 180 AUD an hour).
  • Client rate cards overlay the base rates for a specific client. Creating a card imports every base role, and you can then override individual rates.

A project bills using the first of: the rate card assigned to the project, the client’s default rate card, then the base roles. The effective-rate endpoints resolve that chain for you.

All endpoints live under /api/v1/organizations/:id, where id is your organisation’s id.

Authentication: every request needs a per-user API key bound to the organisation in the path. See API Keys & Authentication.

Rates are financial data, so note who can see and change them: any active member of the organisation can read every endpoint on this page, including the rates themselves. Creating, updating, or archiving rate-card roles and client rate cards needs an organisation admin or owner. Assigning a rate card to a project needs project-update access, which managers, admins, and owners have by default. Organisations that use custom roles may grant these permissions differently.

GET /api/v1/organizations/:id/rate-card-roles

Section titled “GET /api/v1/organizations/:id/rate-card-roles”

Lists the organisation’s base rate-card roles. Any active member can call it.

Query parameters

NameTypeRequiredDescription
includeInactivestringNoPass true to include archived roles. Defaults to active roles only.

Example request

Terminal window
curl https://api.runnit.io/api/v1/organizations/<org-id>/rate-card-roles \
-H 'Authorization: Bearer rnk_your_key'

Response

{
"roles": [
{
"id": "<role-uuid>",
"organizationId": "<org-uuid>",
"roleKey": "senior-designer",
"roleName": "Senior Designer",
"hourlyRate": 180,
"currency": "AUD",
"isActive": true,
"createdAt": "2026-01-10T00:00:00.000Z",
"updatedAt": "2026-06-01T00:00:00.000Z"
}
]
}

POST /api/v1/organizations/:id/rate-card-roles

Section titled “POST /api/v1/organizations/:id/rate-card-roles”

Creates a base rate-card role. Only organisation admins and owners can call it.

Request body

NameTypeRequiredDescription
roleNamestringYesDisplay name, 1 to 120 characters.
roleKeystringNoStable key, 1 to 80 characters, unique within the organisation. Generated from the name if omitted.
hourlyRatenumberYesHourly rate, 0 or higher.
currencystringNoThree-letter currency code, for example AUD.
isActivebooleanNoDefaults to true.

Example request

Terminal window
curl -X POST https://api.runnit.io/api/v1/organizations/<org-id>/rate-card-roles \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "roleName": "Senior Designer", "hourlyRate": 180, "currency": "AUD" }'

Response is 201 with the created role, in the same shape as the list endpoint.

Errors

StatusWhen
400roleName or hourlyRate is missing or out of range.
409A role with the same roleKey already exists in the organisation.

PATCH /api/v1/organizations/:id/rate-card-roles/:roleId

Section titled “PATCH /api/v1/organizations/:id/rate-card-roles/:roleId”

Updates a base rate-card role. Only organisation admins and owners can call it. All body fields are optional and follow the same constraints as creation: roleName, roleKey, hourlyRate, currency, and isActive.

Example request

Terminal window
curl -X PATCH https://api.runnit.io/api/v1/organizations/<org-id>/rate-card-roles/<role-id> \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "hourlyRate": 190 }'

Response is the updated role.

Errors

StatusWhen
400A field fails validation, or roleId is not a UUID.
404The role does not exist in the organisation.

DELETE /api/v1/organizations/:id/rate-card-roles/:roleId

Section titled “DELETE /api/v1/organizations/:id/rate-card-roles/:roleId”

Archives a base rate-card role. Only organisation admins and owners can call it. The role is deactivated rather than deleted, so historical rates are preserved; archived roles reappear in listings with includeInactive=true.

Terminal window
curl -X DELETE https://api.runnit.io/api/v1/organizations/<org-id>/rate-card-roles/<role-id> \
-H 'Authorization: Bearer rnk_your_key'

Response is 204 No Content. Returns 404 if the role does not exist in the organisation.

GET /api/v1/organizations/:id/client-rate-cards

Section titled “GET /api/v1/organizations/:id/client-rate-cards”

Lists client rate cards, optionally for one client. Any active member can call it.

Query parameters

NameTypeRequiredDescription
clientOrganizationIdUUIDNoOnly return cards for this client organisation.
includeInactivebooleanNoPass true to include archived cards.

Example request

Terminal window
curl 'https://api.runnit.io/api/v1/organizations/<org-id>/client-rate-cards?clientOrganizationId=<client-org-id>' \
-H 'Authorization: Bearer rnk_your_key'

Response

{
"cards": [
{
"id": "<card-uuid>",
"organizationId": "<org-uuid>",
"clientOrganizationId": "<client-org-uuid>",
"name": "Retainer 2026",
"isDefault": true,
"isActive": true,
"createdAt": "2026-02-01T00:00:00.000Z",
"updatedAt": "2026-02-01T00:00:00.000Z"
}
]
}

POST /api/v1/organizations/:id/client-rate-cards

Section titled “POST /api/v1/organizations/:id/client-rate-cards”

Creates a client rate card and imports every base role onto it as inherited rows. Only organisation admins and owners can call it. The client must already be linked to your organisation (see the Clients API).

Request body

NameTypeRequiredDescription
clientOrganizationIdUUIDYesThe client organisation the card belongs to. Must be a linked client.
namestringYesCard name, 1 to 150 characters, unique per client.
isDefaultbooleanNoMake this the client’s default card.
isActivebooleanNoDefaults to true.

Example request

Terminal window
curl -X POST https://api.runnit.io/api/v1/organizations/<org-id>/client-rate-cards \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "clientOrganizationId": "<client-org-id>", "name": "Retainer 2026", "isDefault": true }'

Response is 201 with the card plus the imported role rows:

{
"id": "<card-uuid>",
"organizationId": "<org-uuid>",
"clientOrganizationId": "<client-org-uuid>",
"name": "Retainer 2026",
"isDefault": true,
"isActive": true,
"importedRoles": [
{
"id": "<card-role-uuid>",
"clientRateCardId": "<card-uuid>",
"baseRoleId": "<role-uuid>",
"roleKey": "senior-designer",
"roleName": "Senior Designer",
"overrideHourlyRate": null,
"currency": "AUD",
"isActive": true
}
]
}

Errors

StatusWhen
400The client organisation is not linked to your organisation, or a field fails validation.
409A card with this name already exists for the client.

PATCH /api/v1/organizations/:id/client-rate-cards/:cardId

Section titled “PATCH /api/v1/organizations/:id/client-rate-cards/:cardId”

Updates a client rate card’s metadata. Only organisation admins and owners can call it. Optional body fields: name (1 to 150 characters), isDefault, and isActive.

Terminal window
curl -X PATCH https://api.runnit.io/api/v1/organizations/<org-id>/client-rate-cards/<card-id> \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "isDefault": true }'

Response is the updated card. Returns 404 if the card does not exist in the organisation.

DELETE /api/v1/organizations/:id/client-rate-cards/:cardId

Section titled “DELETE /api/v1/organizations/:id/client-rate-cards/:cardId”

Archives a client rate card. Only organisation admins and owners can call it. Returns 204 No Content, or 404 if the card does not exist in the organisation.

GET /api/v1/organizations/:id/client-rate-cards/:cardId/roles

Section titled “GET /api/v1/organizations/:id/client-rate-cards/:cardId/roles”

Lists the card’s own role rows: the overlay entries, not the merged view. Any active member can call it. Use the effective-rate endpoints below when you want the resolved rates. Pass includeInactive=true to include archived rows.

Terminal window
curl https://api.runnit.io/api/v1/organizations/<org-id>/client-rate-cards/<card-id>/roles \
-H 'Authorization: Bearer rnk_your_key'

Response

{
"roles": [
{
"id": "<card-role-uuid>",
"clientRateCardId": "<card-uuid>",
"baseRoleId": "<role-uuid>",
"roleKey": "senior-designer",
"roleName": "Senior Designer",
"overrideHourlyRate": 165,
"currency": "AUD",
"isActive": true
}
]
}

A row with overrideHourlyRate: null inherits the base role’s rate.

POST /api/v1/organizations/:id/client-rate-cards/:cardId/roles

Section titled “POST /api/v1/organizations/:id/client-rate-cards/:cardId/roles”

Adds a role override to a client rate card. Only organisation admins and owners can call it. Every card role must link to a base role.

Request body

NameTypeRequiredDescription
baseRoleIdUUIDYesThe base rate-card role this row overrides.
overrideHourlyRatenumber or nullNoThe client-specific rate, 0 or higher. null or omitted inherits the base rate.
currencystringNoThree-letter currency code.

Example request

Terminal window
curl -X POST https://api.runnit.io/api/v1/organizations/<org-id>/client-rate-cards/<card-id>/roles \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "baseRoleId": "<role-id>", "overrideHourlyRate": 165 }'

Response is 201 with the created card role.

Errors

StatusWhen
400baseRoleId is missing or not a UUID, or overrideHourlyRate is negative.
409The card already has a row for that role.

PATCH /api/v1/organizations/:id/client-rate-cards/:cardId/roles/:roleId

Section titled “PATCH /api/v1/organizations/:id/client-rate-cards/:cardId/roles/:roleId”

Updates a card role’s override values. Only organisation admins and owners can call it. Optional body fields: overrideHourlyRate (number, 0 or higher, or null to revert to the base rate) and currency.

Terminal window
curl -X PATCH https://api.runnit.io/api/v1/organizations/<org-id>/client-rate-cards/<card-id>/roles/<card-role-id> \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "overrideHourlyRate": null }'

Response is the updated card role. Returns 404 if the role, card, or organisation do not match.

DELETE /api/v1/organizations/:id/client-rate-cards/:cardId/roles/:roleId

Section titled “DELETE /api/v1/organizations/:id/client-rate-cards/:cardId/roles/:roleId”

Archives a card role. Only organisation admins and owners can call it. Returns 204 No Content, or 404 if the role does not exist on that card.

PATCH /api/v1/organizations/:id/projects/:projectId/rate-card

Section titled “PATCH /api/v1/organizations/:id/projects/:projectId/rate-card”

Assigns a client rate card to a project, or clears the assignment. Requires project-update access, which managers, admins, and owners have by default. The card must belong to the project’s client.

Request body

NameTypeRequiredDescription
clientRateCardIdUUID or nullNoThe card to assign. Pass null (or omit) to clear the assignment, so the project falls back to the client’s default card.

Example request

Terminal window
curl -X PATCH https://api.runnit.io/api/v1/organizations/<org-id>/projects/<project-id>/rate-card \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "clientRateCardId": "<card-id>" }'

Response

{ "project": { "id": "<project-uuid>", "name": "Winter Launch", "clientRateCardId": "<card-uuid>", "...": "..." } }

Errors

StatusWhen
400The project has no client, the card is not in your organisation, the card is archived, or the card belongs to a different client.
404The project does not exist in the organisation.

GET /api/v1/organizations/:id/clients/:clientOrgId/effective-rate-roles

Section titled “GET /api/v1/organizations/:id/clients/:clientOrgId/effective-rate-roles”

Resolves the effective rates for a client: the client’s default rate card if one exists, otherwise the base roles. Any active member can call it.

Terminal window
curl https://api.runnit.io/api/v1/organizations/<org-id>/clients/<client-org-id>/effective-rate-roles \
-H 'Authorization: Bearer rnk_your_key'

Response

{
"roles": [
{
"id": "<role-uuid>",
"roleName": "Senior Designer",
"hourlyRate": 165,
"currency": "AUD",
"sourceType": "client_overlay",
"baseRoleId": "<role-uuid>",
"clientRateCardRoleId": "<card-role-uuid>",
"clientRateCardId": "<card-uuid>"
}
],
"clientRateCardId": "<card-uuid>"
}

sourceType is base (rate comes straight from the base role), client_overlay (a card row overrides a base role), or client_custom (a card row with no base role). clientRateCardId is omitted when the rates come from the base roles alone.

GET /api/v1/organizations/:id/projects/:projectId/effective-rate-roles

Section titled “GET /api/v1/organizations/:id/projects/:projectId/effective-rate-roles”

Resolves the effective rates for a project, following the full chain: the project’s assigned card, then the client’s default card, then the base roles. Any active member can call it. The response has the same shape as the client effective-rate endpoint. Pass a projectId that is a valid UUID; an unknown project resolves against the base roles.

Terminal window
curl https://api.runnit.io/api/v1/organizations/<org-id>/projects/<project-id>/effective-rate-roles \
-H 'Authorization: Bearer rnk_your_key'

Link the clients these cards bill against with the Clients API.