Users & workspace
Read the workspace profile, plan and products, team members with their roles and teams, and invite new members by email.
View as MarkdownA workspace is one company in TruTec. Every API key belongs to one workspace and sees only its data. These endpoints describe the workspace and the people in it, so your system can show the right company details, map TruTec user ids to your own staff records and onboard new members.
Billing is not available through the API: you can read the plan's name, but never payment details, and nothing here changes the plan.
To change the company profile, branding, estimating defaults, sales tax, job numbering and the other workspace settings, see Settings.
Note
Partner keys add firm_id to name the workspace: in the query string, or in the JSON body on writes. See Partner API.
Workspace#
Get the workspace#
GET /v1/workspace
Permission: Read jobs
curl https://trutec.ai/api/v1/workspace \
-H "X-API-Key: sk_your_api_key_here"
{
"data": {
"id": 77,
"name": "Northgate Paving",
"plan": {"code": "gold", "name": "Gold"},
"subscription_status": "active",
"timezone": "America/Chicago",
"address": "2200 Yard Rd, Austin, TX 78744",
"website": "https://northgatepaving.example",
"branding": {
"logo_url": "https://media.example.com/logos/northgate.png?expires=1760285051&signature=...",
"share_subdomain": "northgate"
},
"products": {
"bid": true,
"build": true,
"aerial_takeoffs": true,
"blueprint_takeoffs": true,
"nearmap_imagery": true,
"api": true,
"landscape": false
},
"integrations": {"quickbooks": true, "hubspot": false, "jobber": false},
"member_count": 14,
"created_at": "2025-03-02T16:20:00Z"
}
}
| Field | Description |
|---|---|
plan |
code and display name of the plan that sets the workspace's features. |
subscription_status |
trialing, active, past_due, canceled or pending. |
timezone |
IANA name. Schedules, timesheets and "today" are computed in it. |
address |
The office or yard address TruTec measures drive times from. null if not set. |
website |
The company website shown on map exports. |
branding.logo_url |
A time-limited link to the logo on proposals and exports. Download it rather than storing the link. |
branding.share_subdomain |
The custom subdomain on shared links, if set. |
products.bid |
Sales: CRM, takeoffs, estimating, proposals. Always on. |
products.build |
Operations: schedule and dispatch, crews, the crew app, timesheets, job costing. |
products.blueprint_takeoffs, nearmap_imagery, api |
Features the plan includes. |
products.landscape |
The workspace measures landscaping (lawn, beds, trees) instead of paving. |
integrations |
Which accounting and CRM connections are on. |
Users#
List users#
GET /v1/users
Permission: Any active key
The workspace's team members. See List users for the parameters and response.
Get a user#
GET /v1/users/{user_id}
Permission: Read jobs
One team member with their role in this workspace and their teams.
curl https://trutec.ai/api/v1/users/311 \
-H "X-API-Key: sk_your_api_key_here"
{
"data": {
"id": 311,
"name": "Dana Ortiz",
"username": "dana",
"email": "[email protected]",
"role": "member",
"job_title": "Estimator",
"phone_number": "+15125550142",
"headshot_url": null,
"is_firm_owner": false,
"created_at": "2025-03-04T15:00:12.000000",
"workspace_role": "member",
"role_label": "Ops Manager",
"custom_role": {"id": 6, "name": "Ops Manager"},
"teams": [{"id": 3, "name": "North Crew"}],
"active": true
}
}
| Field | Description |
|---|---|
workspace_role |
owner, member or crew in this workspace. |
role_label |
What TruTec shows: Owner, Member, Crew, or the company role's name. |
custom_role |
The company-defined role the member holds, or null. |
teams |
Teams the person is on. |
active |
false when the person's access is suspended. |
A user who is not in this workspace returns 404 User not found.
List roles#
GET /v1/roles
Permission: Read jobs
The built-in roles and the roles the company defined in Settings › Team › Roles, with what each company role is allowed to do and how many people hold it. Built-in roles have permissions: null: Owners can do everything, Members have the standard office access, Crew use the crew app only.
{
"data": [
{"id": null, "key": "owner", "name": "Owner", "description": "Everything, always — billing, settings and everyone’s roles.", "built_in": true, "permissions": null, "member_count": 2},
{"id": null, "key": "member", "name": "Member", "description": "Today’s office access. The per-person switches on Members still apply.", "built_in": true, "permissions": null, "member_count": 9},
{"id": null, "key": "crew", "name": "Crew", "description": "The crew app only: their own work, time, photos and notes.", "built_in": true, "permissions": null, "member_count": 3},
{"id": 6, "key": null, "name": "Ops Manager", "description": "Runs the schedule", "built_in": false,
"permissions": [{"key": "view_pricing", "label": "See prices and costs", "group": "money"}], "member_count": 3}
]
}
Roles are assigned in TruTec, not through the API.
List teams#
GET /v1/teams
Permission: Read jobs
Teams are named groups of people (for example branches or crews) that jobs can be filed under: a job's team_id.
{
"data": [
{"id": 3, "name": "North Crew", "color": "#2563eb", "members": [{"user_id": 311, "role": "lead"}], "created_at": "2026-02-01T14:00:00Z"}
]
}
Invitations#
Invite a user#
POST /v1/users/invite
Permission: Write jobs, with a key created by a workspace owner
Warning
This sends an email. TruTec emails the address an invitation to join the workspace, from the same flow as Settings › Members › Invite. The link expires in 7 days.
| Name | Type | Required | Description |
|---|---|---|---|
email |
string | Yes | Who to invite. |
role |
string | No | member (the only value accepted). Owners are invited in TruTec. |
curl -X POST https://trutec.ai/api/v1/users/invite \
-H "X-API-Key: sk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]"}'
Returns 201:
{
"data": {
"id": 418,
"email": "[email protected]",
"role": "member",
"status": "pending",
"invited_by": {"id": 9, "name": "Chris Lane"},
"expires_at": "2026-10-19T15:04:11Z",
"accepted_at": null,
"created_at": "2026-10-12T15:04:11Z"
},
"created": true
}
Inviting an email that already has a pending invitation sends a fresh link and returns 200 with "created": false. The person joins when they sign up from the link; they then appear in List users.
| Status | error |
|---|---|
| 400 | A valid email address is required, role must be member, Unknown fields |
| 403 | Owner key required: the key was not created by an owner of this workspace |
| 409 | Already a member, with the member's user_id |
List invitations#
GET /v1/invites
Permission: Read jobs
Invitations, newest first, in the shape above. Filter with status (pending, accepted, revoked, expired); page with limit and offset. An invitation whose link ran out before it was used is returned with status expired.
Revoke an invitation#
DELETE /v1/invites/{invite_id}
Permission: Write jobs and Delete records, with a key created by a workspace owner
Revokes a pending invitation; its link stops working. Returns 204. 409 Only pending invites can be revoked for accepted ones.