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
| Name | Type | Required | Description |
|---|---|---|---|
includeInactive | string | No | Pass true to include archived roles. Defaults to active roles only. |
Example request
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
| Name | Type | Required | Description |
|---|---|---|---|
roleName | string | Yes | Display name, 1 to 120 characters. |
roleKey | string | No | Stable key, 1 to 80 characters, unique within the organisation. Generated from the name if omitted. |
hourlyRate | number | Yes | Hourly rate, 0 or higher. |
currency | string | No | Three-letter currency code, for example AUD. |
isActive | boolean | No | Defaults to true. |
Example request
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
| Status | When |
|---|---|
400 | roleName or hourlyRate is missing or out of range. |
409 | A 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
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
| Status | When |
|---|---|
400 | A field fails validation, or roleId is not a UUID. |
404 | The 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.
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
| Name | Type | Required | Description |
|---|---|---|---|
clientOrganizationId | UUID | No | Only return cards for this client organisation. |
includeInactive | boolean | No | Pass true to include archived cards. |
Example request
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
| Name | Type | Required | Description |
|---|---|---|---|
clientOrganizationId | UUID | Yes | The client organisation the card belongs to. Must be a linked client. |
name | string | Yes | Card name, 1 to 150 characters, unique per client. |
isDefault | boolean | No | Make this the client’s default card. |
isActive | boolean | No | Defaults to true. |
Example request
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
| Status | When |
|---|---|
400 | The client organisation is not linked to your organisation, or a field fails validation. |
409 | A 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.
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.
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
| Name | Type | Required | Description |
|---|---|---|---|
baseRoleId | UUID | Yes | The base rate-card role this row overrides. |
overrideHourlyRate | number or null | No | The client-specific rate, 0 or higher. null or omitted inherits the base rate. |
currency | string | No | Three-letter currency code. |
Example request
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
| Status | When |
|---|---|
400 | baseRoleId is missing or not a UUID, or overrideHourlyRate is negative. |
409 | The 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.
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
| Name | Type | Required | Description |
|---|---|---|---|
clientRateCardId | UUID or null | No | The card to assign. Pass null (or omit) to clear the assignment, so the project falls back to the client’s default card. |
Example request
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
| Status | When |
|---|---|
400 | The project has no client, the card is not in your organisation, the card is archived, or the card belongs to a different client. |
404 | The 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.
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.
curl https://api.runnit.io/api/v1/organizations/<org-id>/projects/<project-id>/effective-rate-roles \ -H 'Authorization: Bearer rnk_your_key'Next step
Section titled “Next step”Link the clients these cards bill against with the Clients API.