Skip to content
TruTecDevelopers
Get an API key

Notes & tasks

Write notes and log calls, emails and meetings on jobs, contacts and customers; sync activity with another CRM; read activity logs and customer history; create and complete follow-ups.

View as Markdown

Three kinds of records live here:

  • Notes and logged interactions. A team note on a job, or a call / email / meeting / site visit that already happened. Calls, emails and meetings logged on a job also land on the customer's and the person's feed, exactly as when they are logged in the app. Logging records the conversation; nothing here sends an email or a text.
  • Activity and history. A job's activity log (stage moves, notes, task lines, files) and a customer's or person's merged history (emails and texts, calls, notes, follow-ups, stage moves, proposals, signatures, invoices).
  • Follow-ups (tasks). Job follow-ups (/v1/tasks) and company follow-ups that need no job (/v1/account-tasks). They show in each person's My day queue and send the assignee a reminder.

API writes act as the user who created the API key: notes and completed follow-ups show that person as the author.

Note

Partner keys that act for several workspaces must also send firm_id (query string, or JSON body on writes).

Lists return {"data": [...], "pagination": {...}} (default 50, max 200). Timestamps are UTC ISO 8601 with Z.

Job notes and activity#

The activity object#

Field Type Description
id integer TruTec id.
job_id integer The job.
type string note, call, email, meeting, site_visit, status_change, task, created, details_updated, assigned, ...
content string or null The text.
is_team_note boolean An authored team note (not a system line).
user object or null {id, name} of the author.
details object Extras when present: old_status, new_status, outcome (call outcome, or a lost reason's answers), phone, task_id, customer_response, ...
created_at string UTC.

List job activity#

GET /v1/jobs/{job_id}/activity

Permission: Read jobs

Everything on the job's activity log, newest first.

Name Type Required Description
type string No Comma-separated types to keep, e.g. status_change,call.
created_after string No ISO 8601.
limit, offset integer No Paging.
{
  "data": [
    {
      "id": 990211,
      "job_id": 41872,
      "type": "call",
      "content": "Left voicemail · 512-555-0142\nWill try Thursday",
      "is_team_note": false,
      "user": {"id": 118, "name": "Dana Whitfield"},
      "details": {"source": "click_to_call", "outcome": "left_voicemail", "phone": "512-555-0142"},
      "created_at": "2026-10-12T15:10:44Z"
    }
  ],
  "pagination": {"total": 1, "limit": 50, "offset": 0}
}

List job notes#

GET /v1/jobs/{job_id}/notes

Permission: Read jobs

What people wrote: team notes plus logged calls, emails, meetings and site visits (no system lines). Same parameters as the activity list.

Add a job note or log a call#

POST /v1/jobs/{job_id}/notes

Permission: Write jobs

Name Type Required Description
content string Yes Up to 8,000 characters.
type string No note (default), call, email, meeting or site_visit.
outcome string No Calls: e.g. connected, left_voicemail, no_answer. Shown before the text.
phone string No Calls: the number dialed.
customer_response boolean No The customer replied (calls, emails, meetings). Stops the job's automatic sales sequence, as in the app.
task_id integer No A follow-up on this job: the entry becomes that follow-up's notes.
complete_task boolean No With task_id: also complete it. Default true.
curl -X POST "https://trutec.ai/api/v1/jobs/41872/notes" \
  -H "X-API-Key: sk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"type": "call", "outcome": "left_voicemail", "phone": "512-555-0142", "content": "Will try Thursday"}'

Returns 201 with {"data": {activity}}, plus task_id / task_completed when filed against a follow-up and stopped_sequence_tasks with customer_response. Connected integrations hear about the note as they do for notes added in the app.

Tagging teammates in a note is not available through the API.

Errors: 400 content is required, type must be one of: ...; 404 Job not found, task_id is not a follow-up on this job.

Contact and customer notes#

A person's feed and a customer's feed (company notes plus its people's notes, calls, emails, meetings and texts).

The feed note object#

Field Type Description
id integer TruTec id.
type string note, call, email, meeting or text.
content string or null The text.
contact_id, client_account_id, job_id integer or null What it is about.
user object or null {id, name} of the author.
details object Extras when present (call outcome, email subject, direction, ...).
created_at string UTC.

List a contact's notes / a customer's notes#

GET /v1/contacts/{id}/notes

GET /v1/client-accounts/{id}/notes

Permission: Read jobs

Newest first. Filters: type (comma-separated), created_after, limit, offset.

Add a note to a contact / a customer#

POST /v1/contacts/{id}/notes

POST /v1/client-accounts/{id}/notes

Permission: Write jobs

Name Type Required Description
content string Yes Up to 4,000 characters.
type string No note (default), call, email or meeting.
outcome, phone string No Calls only, as for job notes.
contact_id integer No Customer notes only: one of the customer's people the note is about.

A note on a person also shows on their company's feed.

curl -X POST "https://trutec.ai/api/v1/contacts/20417/notes" \
  -H "X-API-Key: sk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"type": "meeting", "content": "Walked the lot with the board"}'
{
  "data": {
    "id": 55120,
    "type": "meeting",
    "content": "Walked the lot with the board",
    "contact_id": 20417,
    "client_account_id": 4812,
    "job_id": null,
    "user": {"id": 118, "name": "Dana Whitfield"},
    "details": {"source": "api"},
    "created_at": "2026-10-12T15:12:01Z"
  }
}

Errors: 400 content is required, That contact belongs to another company; 404 for a record outside the workspace.

Activity sync (two-way CRM timelines)#

Keep a CRM's timeline and TruTec's in step without duplicates:

  • Into TruTec: POST /v1/activities logs a call, text, email, meeting, site visit or note that happened in your CRM, on the job and the customer's feed, keyed on your source + external_id.
  • Out of TruTec: the activity_logged webhook fires once for every call, text, email, meeting or site visit logged in TruTec. GET /v1/activities returns the same objects for a backfill.
  • No echoes: anything you post with a source other than trutec is never sent back out as activity_logged.
  • Calls made in TruTec: each estimator can have their own calling number and call from TruTec (in the browser, or TruTec rings their phone first). The call is logged as it starts and activity_updated follows as it ends, as the outcome is saved and as an AI summary arrives. See the call object below.
  • Built-in HubSpot sync: a workspace connected on Settings → HubSpot can switch on "Log calls, texts, emails and meetings in HubSpot" (TruTec creates real HubSpot calls, emails, meetings and SMS communications on the contact, company and deal, and updates the same call when its outcome arrives) and "Show HubSpot activity in TruTec" (HubSpot calls, emails, meetings, notes and communications appear on the linked job, customer and person, source: hubspot). An engagement TruTec created is known by its HubSpot id: posting it back with source: hubspot and that id as external_id returns the TruTec activity with "created": false and changes nothing.
  • One HubSpot, several workspaces: when several TruTec workspaces connect the same HubSpot account, every HubSpot change goes to each of them, and each only updates the jobs it already links by HubSpot id. A new deal is imported only by a workspace with "Import new HubSpot records into this workspace" on that owns it: the only such workspace, the one whose team includes the deal's HubSpot owner, or the only one using the deal's pipeline.

Log an activity from another system#

POST /v1/activities (Write jobs)

Field Required Notes
type Yes call, text, email, meeting, site_visit or note
body Yes, unless subject (or a call's outcome) is sent What happened. Up to 8,000 characters. content is accepted as the same field
associations Yes, at least one job_id, job_external_ref, client_account_id, client_account_external_ref, contact_id, lead_id. All must be in this workspace (404 otherwise)
occurred_at No ISO 8601 UTC. Default now. The activity sorts by it
direction No inbound or outbound
subject No Email subject or meeting title
outcome No Calls: spoke, voicemail, no_answer, callback, wrong_number (also connected, left_voicemail, left_live_message, busy)
duration_seconds No 0 to 86,400
source No Default api. Your system's name, e.g. hubspot
external_id No Your id for it. With source, the dedupe key
user_email No The author, if they are a workspace member. Otherwise the API key's user, and the response says "author_matched": false

Where it lands: with a job, on the job's activity (calls, texts, emails and meetings also on the customer's and the person's feed); with only a customer or a person, on their feed; a lead that has a job, customer or person logs there; a lead with none of those (a basic lead) keeps it on the lead, and an outbound call, text, email, meeting or site visit marks a New lead Contacted.

curl -X POST https://trutec.ai/api/v1/activities \
  -H "X-API-Key: $TRUTEC_API_KEY" -H "Content-Type: application/json" \
  -d '{"type": "email", "direction": "inbound", "occurred_at": "2026-10-16T14:20:00Z",
       "subject": "Re: Sealcoat quote", "body": "Can you start the week of the 26th?",
       "source": "hubspot", "external_id": "51234998801",
       "associations": {"job_external_ref": "deal-88412"}}'

Returns 201 {"data": {...activity...}, "created": true}. Sending the same source + external_id again returns 200 with "created": false and updates the body, subject, outcome, duration, direction (and occurred_at, when sent) if they changed. The object is the one under List activities.

List activities#

GET /v1/activities (Read jobs). Oldest first, one entry per interaction.

Parameter Notes
type Comma list. Default call,text,email,meeting,site_visit; add note for notes
source trutec = logged in TruTec; any other value = posted with that source
updated_after Logged (or updated through this API) after this time
job_id, contact_id, client_account_id, lead_id Only that record's activity. lead_id also returns entries kept on a basic lead
limit, cursor Page size (default 50, max 200) and the previous page's next_cursor
{
  "data": [{
    "id": "ca_8812",
    "type": "call",
    "direction": "outbound",
    "subject": null,
    "body": "Spoke with them\nWants the price by Friday",
    "outcome": "spoke",
    "duration_seconds": 312,
    "call": null,
    "photos": [],
    "location": null,
    "condition": null,
    "occurred_at": "2026-10-16T15:04:00Z",
    "source": "hubspot",
    "external_id": "51234998877",
    "logged_via": "api",
    "user": {"id": 1204, "email": "[email protected]", "name": "Sam Estimator", "hubspot_owner_id": "7781123"},
    "associations": {"job_id": 4812, "job_number": "J-1042", "job_name": "Northgate Plaza",
                     "job_external_ref": "deal-88412", "hubspot_deal_id": "88412",
                     "client_account_id": 915, "client_account_name": "Northgate Property Group",
                     "client_account_external_ref": "CUST-0091", "contact_id": 5521,
                     "contact_name": "Alex Rivera", "contact_email": "[email protected]",
                     "contact_phone": "555-0142", "hubspot_contact_id": "1201",
                     "lead_id": null, "lead_external_ref": null},
    "job_activity_id": 990211,
    "contact_activity_id": 8812,
    "created_at": "2026-10-16T15:04:00Z"
  }],
  "next_cursor": "eyJ0IjoiMjAyNi0xMC0xNlQxNTowNDowMCIsInIiOjAsImkiOjg4MTJ9",
  "has_more": false
}

call is set on calls made or taken in TruTec (null for a call someone logged by hand or posted from another system):

Field Meaning
status ringing, in-progress, completed, no-answer, busy, failed, canceled or missed (an inbound call nobody answered)
answered true once the customer picked up
from_number, to_number E.164, e.g. +14155550142. Outbound calls come from the estimator's own TruTec number
ended_at When the call ended (UTC), or null
has_recording, recording_seconds Workspaces that record calls. The recording plays in TruTec for signed-in users
summary AI summary of a recorded call (a few bullets and a next step), or null

Site visits logged in the field ("Log a site visit" on a phone, or the Site visit tab on a lead) also carry what was captured; other types return [] / null:

Field Meaning
photos Links to the visit's photos
location {lat, lng, accuracy_m} where it was logged (the phone's location, or the typed address when location was off), or null
condition good, fair, poor or needs_repair ("Needs repair now"), or null

Page until has_more is false and keep the last next_cursor: calling again with it later returns only what was logged since. id is an opaque, stable string (ca_… company/contact feed row, ja_… job line with no feed row, la_… entry on a basic lead).

Customer history#

A contact's or customer's history#

GET /v1/contacts/{id}/timeline

GET /v1/client-accounts/{id}/timeline

Permission: Read jobs

One newest-first feed of everything that happened with the person or company, as on their page in the app: emails and texts in and out, calls and meetings, notes, follow-ups set and done, job stage moves, new jobs, proposals sent / viewed / signed, e-signatures and invoices. It covers every job the person or company is on.

Name Type Required Description
limit integer No Default 50, maximum 100.
before string No Cursor: the previous page's next_before.
kinds string No Comma-separated groups: messages, calls, notes, tasks, deals, money.
q string No Only entries mentioning this text (or on a job whose name, address or contact matches).
{
  "data": [
    {
      "key": "ca:email:55102",
      "kind": "email",
      "group": "messages",
      "at": "2026-10-11T18:22:09Z",
      "title": "Email to Jamie Rivera",
      "subject": "Reseal proposal",
      "body": "Attached is the proposal...",
      "actor": "Dana Whitfield",
      "chips": [],
      "links": [{"kind": "job", "label": "Maple Ridge HOA reseal", "url": "/jobs/41872"}]
    }
  ],
  "has_more": true,
  "next_before": "2026-10-11T18:22:09~ca:email:55102"
}

Page with before until has_more is false. Money entries (invoice amounts, deposits) appear only when the key's creator can see pricing.

Job follow-ups#

The follow-up object#

Field Type Description
id integer TruTec id.
job_id, job integer, object The job ({id, name}).
title, description string What to do.
task_type string follow_up, call, email, meeting, appointment, site_visit or other.
due_date string or null UTC.
timezone string Time zone the reminder is shown in.
reminder_minutes_before integer or null Email reminder lead time.
completed, completed_at, completion_notes Done state and what happened.
is_overdue boolean Open and past due.
assigned_to_user_id, assigned_to integer, object Who does it.
created_by_user_id, created_at Who added it and when.
external_ref object or null {source, external_id} when it is linked to a task in another system (see Syncing tasks from your CRM); null when it lives only in TruTec.

Due times. Send ISO 8601. With Z or an offset (2026-10-20T15:30:00Z) it is that exact time. Without one (2026-10-20T10:30) it is read in timezone (an IANA name such as America/Chicago), or the workspace's time zone. A bare date means 9:00 AM that day.

List job follow-ups#

GET /v1/tasks

Permission: Read jobs

Soonest due first; undated ones last.

Name Type Required Description
status string No open (default), completed or all.
job_id integer No One job.
assigned_to_user_id integer No One assignee.
due_before, due_after string No ISO 8601.
limit, offset integer No Paging.

Get a job follow-up#

GET /v1/tasks/{id}

Permission: Read jobs

Create a job follow-up#

POST /v1/tasks

Permission: Write jobs

Name Type Required Description
job_id integer Yes The job.
title string Yes What to do.
task_type string No Default follow_up.
description string No Details.
due_date string No See Due times.
timezone string No For a due time without an offset, and for the reminder.
assigned_to_user_id integer No Default: the key's creator.
reminder_minutes_before integer No Default 30 (60 for meetings and site visits) when there is a due time.
source string No Your system's name, e.g. hubspot. With external_id, makes this POST an upsert.
external_id string No Your id for the task, e.g. the HubSpot task id.
completed boolean No Only with source + external_id: closes (or reopens) it.
curl -X POST "https://trutec.ai/api/v1/tasks" \
  -H "X-API-Key: sk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"job_id": 41872, "title": "Call Jamie back", "task_type": "call", "due_date": "2026-10-20T15:30:00Z"}'
{
  "data": {
    "id": 7731,
    "job_id": 41872,
    "job": {"id": 41872, "name": "Maple Ridge HOA reseal"},
    "title": "Call Jamie back",
    "description": null,
    "task_type": "call",
    "due_date": "2026-10-20T15:30:00Z",
    "timezone": "America/Chicago",
    "reminder_minutes_before": 30,
    "completed": false,
    "completed_at": null,
    "completion_notes": null,
    "is_overdue": false,
    "assigned_to_user_id": 118,
    "assigned_to": {"id": 118, "name": "Dana Whitfield"},
    "created_by_user_id": 118,
    "created_at": "2026-10-12T14:05:00Z"
  }
}

A "Task created" line is added to the job's activity.

Errors: 400 job_id is required, title is required, task_type must be one of: ..., due_date must be an ISO 8601 date or date-time, source and external_id go together; 404 for a job or user outside the workspace; 409 external_id_in_use when that source + external_id is a follow-up on another job or a company follow-up.

Syncing tasks from your CRM#

Send source + external_id on POST /v1/tasks or POST /v1/account-tasks and the POST becomes an idempotent upsert keyed on them:

  • The first POST creates the follow-up and links it: 201 with "created": true and external_ref set.
  • The same source + external_id again updates that follow-up with the fields you send (title, notes, type, due time, assignee, completed) and returns 200 with "created": false. Posting the same body twice, or two at once, never makes a second follow-up.
  • completed: true closes it the quiet way: no call, email or meeting is logged for it, because it happened in your system.
  • No echoes. A change made through the API to a linked follow-up (POST, PATCH, /complete) is treated as coming from that system and is never sent back to it.
  • Built-in HubSpot tasks. A workspace connected on Settings → HubSpot can switch on "Show HubSpot tasks in TruTec": each person's HubSpot tasks (people matched to HubSpot owners) show in their My day, on the deal's job or the company, tagged HubSpot; checking one off in TruTec completes it in HubSpot, and a reschedule moves its due date. Those follow-ups carry external_ref {"source": "hubspot", "external_id": "<HubSpot task id>"}, the same key a workflow posting HubSpot tasks with source: hubspot uses, so the two never make the task twice.
curl -X POST "https://trutec.ai/api/v1/tasks" \
  -H "X-API-Key: sk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"job_id": 41872, "title": "Call Jamie back", "task_type": "call", "due_date": "2026-10-20T15:30:00Z", "source": "hubspot", "external_id": "88812345901"}'

Update a job follow-up#

PATCH /v1/tasks/{id}

Permission: Write jobs

Send any of title, description, task_type, due_date (null clears it), timezone, assigned_to_user_id, reminder_minutes_before, completion_notes and completed. Rescheduling sends the reminder again. completed: true completes it the way the task window does: with completion_notes, they post to the job (a call task logs a call, a meeting task a meeting) and to the customer's feed; without notes a "Task completed" line is added. completed: false reopens it.

Complete a job follow-up#

POST /v1/tasks/{id}/complete

Permission: Write jobs

Shortcut for completing with notes (optional) as the completion notes. A follow-up that is already done is returned unchanged.

curl -X POST "https://trutec.ai/api/v1/tasks/7731/complete" \
  -H "X-API-Key: sk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"notes": "Booked a walk-through for Friday"}'

Delete a job follow-up#

DELETE /v1/tasks/{id}

Permission: Write jobs and Delete records

Returns 204.

Company follow-ups#

Follow-ups on a customer when there is no job ("call the board back after their meeting"). They can repeat: completing one opens the next on schedule.

The company follow-up object#

Like the job follow-up, with client_account_id / client_account instead of the job, plus:

Field Type Description
contact_id integer or null The person to reach.
repeat object or null {every, unit, label}, e.g. {"every": 3, "unit": "month", "label": "Every 3 months"}.
completed_by_user_id integer or null Who completed it.
source string manual, my_day, repeat, crm_intake:<lead id> (a lead's "Call new lead"), automation:<rule id>, hubspot:task:<id> (a HubSpot task), ...
external_ref object or null {source, external_id} when linked to a task in another system.

task_type is one of follow_up, call, email, meeting, site_visit, other.

List company follow-ups#

GET /v1/account-tasks

Permission: Read jobs

Filters: status (open default, completed, all), client_account_id, contact_id, assigned_to_user_id, due_before, due_after, limit, offset.

Get a company follow-up#

GET /v1/account-tasks/{id}

Permission: Read jobs

Create a company follow-up#

POST /v1/account-tasks

Permission: Write jobs

Name Type Required Description
client_account_id integer Yes The customer (not archived).
title string No Default: "Follow up · Maple Ridge HOA".
task_type string No Default follow_up.
description string No Details.
due_date, timezone string No See Due times.
contact_id integer No One of the customer's people.
assigned_to_user_id integer No An office user. Default: the key's creator.
repeat_every, repeat_unit integer, string No Repeat every N days, weeks or months (1-365).
source, external_id string No Together: an upsert keyed on them (see Syncing tasks from your CRM).
completed boolean No Only with source + external_id.
curl -X POST "https://trutec.ai/api/v1/account-tasks" \
  -H "X-API-Key: sk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"client_account_id": 4812, "title": "Quarterly check-in", "task_type": "call", "due_date": "2026-11-02T16:00:00Z", "repeat_every": 3, "repeat_unit": "month"}'

Returns 201 with {"data": {company follow-up}}. With source + external_id, "created" says whether it was new (200 when it already existed); 409 external_id_in_use when the key is another customer's follow-up or a job follow-up.

Update a company follow-up#

PATCH /v1/account-tasks/{id}

Permission: Write jobs

Send any of title, task_type, description, due_date, timezone, contact_id, assigned_to_user_id (null unassigns), repeat_every / repeat_unit (null stops repeating). Errors: 409 already_completed.

Complete a company follow-up#

POST /v1/account-tasks/{id}/complete

Permission: Write jobs

Marks it done, as on My day: a call, email, meeting or site visit is logged on the company feed (with notes) and counts as a touch; another kind with notes adds a company note. A repeating follow-up opens the next one, returned as next_task.

{
  "data": {"id": 3305, "completed": true, "completion_notes": "Happy with the work", "...": "..."},
  "next_task": {"id": 3340, "title": "Quarterly check-in", "due_date": "2027-02-02T16:00:00Z", "repeat": {"every": 3, "unit": "month", "label": "Every 3 months"}, "...": "..."}
}

Delete a company follow-up#

DELETE /v1/account-tasks/{id}

Permission: Write jobs and Delete records

Deletes the follow-up, open or done, for good (a repeating one stops repeating), as its delete button in the app does. Returns 204. Workspaces where the app has no delete for company follow-ups get 403 with code: not_available; complete the follow-up instead.

Changing or removing notes#

Notes, logged calls, emails, meetings and site visits can't be edited or deleted, in the app or through the API: they are the record of what happened. To correct one, add another note.