# Notes & tasks

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. |

```json
{
  "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`. |

```bash
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.

```bash
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"}'
```

```json
{
  "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.

## 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). |

```json
{
  "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. |

**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. |

```bash
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"}'
```

```json
{
  "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`; `404` for a job or user outside the workspace.

### 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.

```bash
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>`, ... |

`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 `day`s, `week`s or `month`s (1-365). |

```bash
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}}`.

### 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`.

```json
{
  "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.
