Skip to content

Staff Profiles & Work History API

The users endpoints live under /api/v1/users and cover individual staff profiles: personal details, profile pictures, portfolio, preferences, work history, organisation role, and client assignments.

Authentication: every request needs a per-user API key. See API Keys & Authentication.

Most endpoints here follow one permission pattern: you can act on your own profile, and active organisation admins and owners can act on the profiles of people in the credential’s organisation. Where an endpoint differs, its section says so.

A demo, sandbox or training credential cannot change a staff profile shared with Production, including its own profile. Profile, portfolio, picture and manual work-history writes return 403; authorised reads still work. Use a Production credential for these edits. Accounts that exist only in the dataset remain editable. Account preferences remain available. The named MCP profile/history tools and call_public_api enforce the same restrictions.

Returns a user’s full profile. You can fetch your own profile; owners, admins, and managers can also fetch the profile of anyone who shares an organisation with them.

Path parameters

NameTypeRequiredDescription
idUUIDYesThe user’s id.

Example request

Terminal window
curl https://api.runnit.io/api/v1/users/<user-id> \
-H 'Authorization: Bearer rnk_your_key'

Response is the user object. Your own profile includes preferences, emailVerified, twoFactorEnabled and linkedProviders (which sign-in providers are connected, never their ids). Another person’s profile leaves those out. Credentials are never included. The main fields:

{
"id": "<user-uuid>",
"email": "priya@example.com",
"firstName": "Priya",
"lastName": "Shah",
"displayName": "Priya Shah",
"avatarUrl": null,
"phone": "+61 400 000 000",
"shortBio": "Producer with a broadcast background.",
"longBio": "...",
"industryStartDate": "2017-07-14",
"roleStartDate": "2023-07-14",
"yearsInIndustry": 9,
"yearsInCurrentRole": 3,
"currentTitle": "Senior Producer",
"portfolioUrl": "https://priya.example.com",
"skills": ["production", "editing"],
"preferredWorkTypes": ["campaigns"],
"acceptingProjects": true,
"preferences": { "...": "..." },
"hourlyRate": 160,
"actualCostRate": 95,
"defaultRateCardRoleId": "<rate-card-role-uuid>",
"status": "active",
"lastLoginAt": "2026-07-06T23:41:12.000Z",
"createdAt": "2024-11-20T22:14:00.000Z"
}

Errors

StatusWhen
403You are not the user and you do not hold an owner, admin, or manager role in an organisation the user belongs to.
404No user exists with that id.

Updates a user’s core profile fields. You can update your own profile; active organisation admins and owners can update people who belong to the credential’s organisation. Memberships in another organisation cannot grant access through this key. Managers cannot edit other people through this endpoint.

email and status are treated differently from the other fields because they decide who can sign in as the account. An admin or owner can change them for someone else only when all of that person’s active or pending memberships are in the credential’s organisation and the person ranks below them there. When an admin changes someone’s email, all of that person’s sessions are signed out and a notice is sent to the previous address.

Request body (all optional)

NameTypeRequiredDescription
emailstringNoA valid email address. Must not be in use by another account.
firstNamestringNoCannot be empty when supplied.
lastNamestringNoCannot be empty when supplied.
displayNamestringNoPreferred display name.
phonestringNoPhone number.
currentTitlestringNoJob title.
acceptingProjectsbooleanNoWhether the person is open to new project assignments.
statusstringNoOne of active, archived, or disabled.
preferencesobjectNoReplaces the user’s preferences object. Use the preferences endpoint below if you want merge behaviour.

Example request

Terminal window
curl -X PATCH https://api.runnit.io/api/v1/users/<user-id> \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "currentTitle": "Executive Producer", "acceptingProjects": false }'

Response is the full updated user object, in the same shape as GET /api/v1/users/:id.

Errors

StatusWhen
400A supplied field fails validation.
403You are not the user and not an active admin or owner of the credential’s organisation, or you tried to change email or status for someone with a membership outside the credential’s organisation or ranked at or above you.
404No user exists with that id.
409The email address is already in use.

Returns the user’s normalised profile picture as image/webp. The response includes an ETag and a long-lived immutable cache policy. Use the user’s avatarUrl field as the image source so the content-version value changes after each replacement.

Errors

StatusWhen
404The user has no saved profile picture.

Uploads or replaces your own profile picture. This endpoint is self-only, so an admin or owner can’t change another person’s image.

Send multipart/form-data with one file field named avatar. Accepted input formats are JPEG, PNG, and WebP, with a maximum file size of 10 MB. Runnit corrects image orientation and saves a square 512 by 512 WebP image.

Example request

Terminal window
curl -X POST https://api.runnit.io/api/v1/users/<your-user-id>/avatar \
-H 'Authorization: Bearer rnk_your_key' \
-F 'avatar=@profile-photo.jpg'

Response

{
"avatarUrl": "/api/users/<your-user-id>/avatar?v=<content-version>"
}

Errors

StatusWhen
400The file is missing, too large, unsupported, or doesn’t contain a valid image.
403The path id is not your user id.
404No user exists with that id.

Deletes your own profile picture and restores the initials avatar. This endpoint is self-only.

Terminal window
curl -X DELETE https://api.runnit.io/api/v1/users/<your-user-id>/avatar \
-H 'Authorization: Bearer rnk_your_key'

Response is 204 No Content.

Errors

StatusWhen
403The path id is not your user id.
404No user exists with that id.

Updates the portfolio section of a profile. Same permissions as PATCH /api/v1/users/:id: yourself, or an active admin or owner of the credential’s organisation editing a person who belongs to it.

String arrays preserve literal characters, including backslashes and quotes. Every entry must be a string; non-string entries return 400. An empty array clears the list. Changes record the connected user as their author.

Request body (all optional)

NameTypeRequiredDescription
shortBiostringNoShort biography.
longBiostringNoLong-form biography.
industryStartDatestringNoISO 8601 date the person entered the industry, for example 2017-07-14. Must not be in the future. null clears it.
roleStartDatestringNoISO 8601 date the person started their current role. Must not be in the future. null clears it.
yearsInIndustrynumberNoBetween 0 and 50. Accepted for compatibility: Runnit converts it to the industryStartDate it implies (today minus the value). Ignored when industryStartDate is also supplied.
yearsInCurrentRolenumberNoBetween 0 and 50. Accepted for compatibility: converted to the roleStartDate it implies. Ignored when roleStartDate is also supplied.
currentTitlestringNoJob title.
portfolioUrlstringNoMust start with http:// or https://. An empty string clears it.
skillsarray of stringsNoReplaces the skills list. An empty array clears it.
preferredWorkTypesarray of stringsNoReplaces the preferred work types. An empty array clears it.

Example request

Terminal window
curl -X PATCH https://api.runnit.io/api/v1/users/<user-id>/portfolio \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "shortBio": "Producer with a broadcast background.", "skills": ["production", "editing"] }'

Response returns the updated portfolio fields:

{
"id": "<user-uuid>",
"shortBio": "Producer with a broadcast background.",
"longBio": null,
"industryStartDate": "2017-07-14",
"roleStartDate": "2023-07-14",
"yearsInIndustry": 9,
"yearsInCurrentRole": 3,
"currentTitle": "Senior Producer",
"portfolioUrl": "https://priya.example.com",
"skills": ["production", "editing"],
"preferredWorkTypes": ["campaigns"]
}

If the body contains no recognised fields, the response is { "message": "No updates provided" } and nothing changes.

Errors

StatusWhen
400A field fails validation, for example a start date in the future or yearsInIndustry outside 0 to 50.
403You are not the user and not an active admin or owner of the credential’s organisation.
404No user exists with that id.

Updates your preferences. This endpoint is self-only: even admins cannot change another person’s preferences. The supplied object is merged into the existing preferences at the top level, so keys you omit are kept.

Request body

NameTypeRequiredDescription
preferencesobjectYesPreference keys to set or overwrite.

Example request

Terminal window
curl -X PATCH https://api.runnit.io/api/v1/users/<your-user-id>/preferences \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "preferences": { "timezone": "Australia/Melbourne" } }'

Response is the full merged preferences object.

Errors

StatusWhen
400preferences is missing or not an object.
403The id is not your own user id.

Changes a user’s role in an organisation. The caller must be an admin or owner of that organisation. The same rules apply as on PATCH /api/v1/organizations/:id/members/:userId: only an owner assigns owner or changes an owner’s role, an admin assigns up to admin and only changes people ranked admin or below, you can’t change your own role, and the last active owner can’t be demoted.

Request body

NameTypeRequiredDescription
organizationIdUUIDYesThe organisation to change the role in. With an API key this must be the key’s organisation.
rolestringYesOne of member, manager, admin, or owner. The freelancer role cannot be assigned here.

Example request

Terminal window
curl -X PATCH https://api.runnit.io/api/v1/users/<user-id>/organization-role \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "organizationId": "<org-id>", "role": "manager" }'

Response

{
"message": "User role updated successfully",
"userId": "<user-uuid>",
"organizationId": "<org-uuid>",
"newRole": "manager"
}

Errors

StatusWhen
400organizationId is not a UUID, or role is not one of the four allowed values.
403You are not an active member of the organisation, you are not an admin or owner, or the change breaks one of the rules above (the message says which).
404The target user is not a member of the organisation.
409The target is the only active owner.

Work history entries (also called portfolio projects) describe past projects a person has worked on. They come from two sources: entries added through this API are always manual, and Runnit also creates entries automatically when assigned tasks or delivery projects complete. Editing an automatic entry through the API converts it to manual, which stops the automatic sync from overwriting your change.

Automatic entries use deliverables for the person’s completed task names and briefSummary for a short contribution summary, rather than the full project brief. Members with no completed tasks receive an empty deliverables list and an explicit summary. Task completion keeps the entry in progress until the project completes. A later sync refreshes old automatic summaries; manual entries are preserved. The same behaviour applies through MCP call_public_api.

All five work history endpoints use the same permissions: yourself, or active admins and owners of the credential’s organisation, which the person must also belong to. A key for another organisation cannot read or change this history.

Lists a user’s work history entries.

Terminal window
curl https://api.runnit.io/api/v1/users/<user-id>/projects \
-H 'Authorization: Bearer rnk_your_key'

Response is a JSON array:

[
{
"id": "<entry-uuid>",
"userId": "<user-uuid>",
"clientName": "Harbour Retail Group",
"projectName": "Winter Launch",
"briefSummary": "Completed 1 task as Producer on Winter Launch.",
"startDate": "2025-06-01",
"endDate": "2025-08-31",
"durationMonths": 3,
"projectRole": "Producer",
"technologies": ["premiere"],
"deliverables": ["30s hero film"],
"teamSize": 6,
"projectUrl": null,
"status": "completed",
"isFeatured": false,
"displayOrder": 0,
"source": "auto",
"sourceProjectId": "<project-uuid>"
}
]

source is manual or auto; sourceProjectId links an automatic entry to the delivery project it came from.

Adds a work history entry. Entries created here are always stored as manual.

Request body

NameTypeRequiredDescription
clientNamestringYesWho the work was for.
projectNamestringYesThe project’s name.
briefSummarystringYesShort description of the work.
startDatestringYesISO 8601 date, for example 2025-06-01.
endDatestringNoISO 8601 date.
projectRolestringYesThe person’s role on the project.
durationMonthsintegerNo0 or higher.
durationDaysintegerNo0 or higher.
teamSizeintegerNo1 or higher.
projectUrlstringNoMust be a valid URL.
technologiesarray of stringsNoTools and technologies used.
deliverablesarray of stringsNoWhat was delivered.

Example request

Terminal window
curl -X POST https://api.runnit.io/api/v1/users/<user-id>/projects \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{
"clientName": "Harbour Retail Group",
"projectName": "Winter Launch",
"briefSummary": "Completed 1 task as Producer on Winter Launch.",
"startDate": "2025-06-01",
"endDate": "2025-08-31",
"projectRole": "Producer"
}'

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

Errors

StatusWhen
400A required field is missing, a date is not ISO 8601, or a numeric field is out of range.
403You are not the user and not an active admin or owner of the credential’s organisation.

PATCH /api/v1/users/:id/projects/:projectId

Section titled “PATCH /api/v1/users/:id/projects/:projectId”

Updates a work history entry. Accepts the same fields as creation, all optional, with the same constraints. Editing marks the entry as manual, so the automatic sync stops updating it.

Terminal window
curl -X PATCH https://api.runnit.io/api/v1/users/<user-id>/projects/<entry-id> \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "teamSize": 8 }'

Response is the updated entry. Returns 404 if the entry does not belong to the user.

DELETE /api/v1/users/:id/projects/:projectId

Section titled “DELETE /api/v1/users/:id/projects/:projectId”

Deletes a work history entry. Returns 204 No Content, or 404 if the entry does not belong to the user.

Terminal window
curl -X DELETE https://api.runnit.io/api/v1/users/<user-id>/projects/<entry-id> \
-H 'Authorization: Bearer rnk_your_key'

Sets the display order of a user’s work history entries.

Request body

NameTypeRequiredDescription
projectOrdersarrayYesOne object per entry: id (UUID of the entry) and order (integer, 0 or higher).

Example request

Terminal window
curl -X POST https://api.runnit.io/api/v1/users/<user-id>/projects/reorder \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "projectOrders": [ { "id": "<entry-id-1>", "order": 0 }, { "id": "<entry-id-2>", "order": 1 } ] }'

Response

{ "message": "Projects reordered successfully" }

These endpoints manage which clients a person is restricted to, within the organisation the API key is bound to. They pair with the client-side view in the Clients API.

Returns the clients this user is explicitly assigned to. You can view your own assignments; owners, admins, and managers can view anyone’s in the organisation.

Terminal window
curl https://api.runnit.io/api/v1/users/<user-id>/clients \
-H 'Authorization: Bearer rnk_your_key'

Response

{
"hasSpecificAssignments": true,
"assignedClients": [
{ "id": "<client-org-uuid>", "name": "Harbour Retail Group", "slug": "harbour-retail" }
]
}

When hasSpecificAssignments is false, the person isn’t explicitly named on any client staffing list. They remain eligible on clients using the all-staff default and on projects where they are an explicit project member.

Errors

StatusWhen
403You are not an active member of the organisation, or you are viewing someone else without an owner, admin, or manager role.

Replaces the user’s client assignments. Only organisation admins and owners can call it.

Request body

NameTypeRequiredDescription
clientIdsarray of UUIDsYesClient organisation ids whose default staffing lists include the user. An empty array removes the user from explicit client lists without changing project memberships.

Example request

Terminal window
curl -X PUT https://api.runnit.io/api/v1/users/<user-id>/clients \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "clientIds": ["<client-org-id>"] }'

Response

{
"success": true,
"hasAllClientAccess": false,
"clients": [
{ "id": "<client-org-uuid>", "name": "Harbour Retail Group", "slug": "harbour-retail" }
]
}

Errors

StatusWhen
400clientIds is missing, not an array, or contains a value that is not a UUID.
403You are not an admin or owner of the organisation.

DELETE /api/v1/users/:id exists but is designed for the Runnit app: it soft deletes your own account and requires your password in the request body. Avatar upload endpoints are also reserved for the app. None of these are recommended for integrations.

Manage memberships, roles, and rates at the organisation level with the Organisations & Staff API.