# Inbox & notifications

These endpoints answer "did the customer reply?", "what's waiting on me?" and "what did the team do today?". They read what TruTec already has; none of them sends anything.

They work as the person who created the API key (a partner key acts as the workspace owner): you see the email that person can see in TruTec, and *their* notifications and mentions.

## Emails

The Emails page in TruTec puts every customer email in one list:

- **Sent**: proposals, signature requests, invoices, schedule and warranty notices, automatic follow-ups, campaigns and emails people wrote, from a connected mailbox or TruTec's own sender. Internal mail (mailbox tests, owner reviews) is left out.
- **Received**: customer replies TruTec filed on jobs.

A received email **needs a reply** until someone emails that address back or ticks it **handled** (answered by phone, nothing to answer). Automatic follow-ups don't count as an answer.

Someone limited to their own (or assigned) jobs sees the email on those jobs plus anything they sent; owners and people who see every job see everything.

### The email object

| Field | Type | Description |
|---|---|---|
| `id` | integer | Unique within its `direction`. Use both to read it: `GET /v1/emails/{direction}/{id}`. |
| `direction` | string | `sent` or `received`. |
| `at` | string | When it was sent or arrived (UTC). |
| `subject`, `snippet` | string | `snippet` is the first line of what was written, when TruTec has it. |
| `from`, `to`, `cc` | | `from` is `{name, email}`; `to` and `cc` are addresses. |
| `contact_name` | string | The customer contact, when the address belongs to one. |
| `type`, `type_group` | string | A plain label (`Proposal`, `Automatic follow-up`, `Reply`) and, for sent email, the filter value (`proposal`, `follow_up`, ...). |
| `automatic` | boolean | An automatic follow-up. |
| `job`, `company` | object | `{id, name}` when known. |
| `attachments` | array | File names. |
| `sent_by` | object | Sent email: `{id, name}` of the person. |
| `status` | string | Received email: `needs_reply`, `answered` or `handled`. |
| `needs_reply`, `overdue` | boolean | `overdue` once it has waited more than 24 hours. |
| `answered_at`, `handled_at`, `handled_by` | | When it was answered (the first email to that address after it), or ticked handled and by whom. |
| `in_reply_to_email_id` | integer | Received email: the sent email it answers, when known. |

### List emails

`GET /v1/emails`

**Permission:** Read jobs

Newest first.

| Name | Type | Required | Description |
|---|---|---|---|
| `direction` | string | No | `all` (default), `sent` or `received`. |
| `needs_reply` | boolean | No | Only customer email still waiting on you, from the last 60 days. |
| `job_id` | integer | No | Only email about this job. |
| `type` | string | No | Sent email kind: `proposal`, `signature`, `signed`, `email`, `follow_up`, `campaign`, `quote_request`, `quote_accepted`, `schedule`, `warranty`, `agreement`, `invoice`. |
| `include_automatic` | boolean | No | `false` hides automatic follow-ups. |
| `mine` | boolean | No | Only mail you sent, or on jobs you own. |
| `q` | string | No | Search sender, recipient, subject, job or company. |
| `limit`, `offset` | integer | No | Default 50, maximum 200. |

```bash
curl "https://trutec.ai/api/v1/emails?job_id=70412&direction=received" \
  -H "X-API-Key: sk_your_api_key_here"
```

```json
{
  "data": [
    {
      "id": 88213,
      "direction": "received",
      "at": "2026-10-12T18:22:05Z",
      "subject": "Re: Proposal for Northgate Plaza reseal",
      "snippet": "Looks good. Can you start the week of the 3rd?",
      "from": { "name": "Pat Lee", "email": "pm@northgate.example" },
      "to": ["sales@northgatepaving.example"],
      "contact_name": "Pat Lee",
      "type": "Reply",
      "job": { "id": 70412, "name": "Northgate Plaza reseal" },
      "company": { "id": 5120, "name": "Northgate Properties" },
      "status": "needs_reply",
      "needs_reply": true,
      "overdue": false,
      "in_reply_to_email_id": 77120
    }
  ],
  "pagination": { "total": 1, "limit": 50, "offset": 0 }
}
```

### Get an email and its conversation

`GET /v1/emails/{direction}/{id}`

**Permission:** Read jobs

The email plus every email to or from the same person with the same subject (`Re:` and `Fwd:` ignored) within 120 days, oldest first, up to 40 (`earlier_count` says how many older ones were left out). Each message has `body` (the words TruTec kept) and `body_state`:

| `body_state` | Meaning |
|---|---|
| `ready` | `body` is the whole email. |
| `fetch` | Only part was kept; read the whole email with the body endpoint below. |
| `reconnect` | It's in a mailbox that needs reconnecting in TruTec. |
| `preview` | Only the start of a reply was kept and no mailbox can supply more. |
| `none` | Sent before TruTec kept copies of emails. |

`reply_to` names who to answer and a `Re:` subject.

### Read a whole email

`GET /v1/emails/{direction}/{id}/body`

**Permission:** Read jobs

Reads the email from the workspace's connected mailbox the first time (TruTec keeps it after that), like opening it in the email window. `state` is `ready` with `text` and display-safe `html`, or `unavailable` with a `reason` (`reconnect`, `gone`, `busy`, `error`) and a `message`.

### Mark an email handled

`POST /v1/emails/received/{id}/mark-handled`

**Permission:** Write jobs

Ticks a received email handled so it leaves Needs reply. Send `{"handled": false}` to put it back. Returns the email.

### Mark many emails handled

`POST /v1/emails/mark-handled`

**Permission:** Write jobs

`{"email_ids": [88213, 88240], "handled": true}`, up to 500 received email ids. Every id must be a received email the key's creator can see; otherwise nothing changes and the `404` lists the ids.

## Notifications

The bell in TruTec: schedule changes, crew and customer messages, field alerts and reminders such as a warranty packet waiting to be sent. They come with scheduling and crews; a workspace without them gets `403`. Mentions in team notes are separate (below).

| Field | Type | Description |
|---|---|---|
| `id` | integer | |
| `kind`, `title`, `body` | string | |
| `link` | string | Where it points in TruTec. |
| `read`, `read_at`, `created_at` | | |

### List notifications

`GET /v1/notifications`

**Permission:** Read jobs

Newest first. `unread=true` for unread only. `limit`, `offset`. The response also has `unread`, the unread count.

### Mark a notification read

`POST /v1/notifications/{id}/mark-read`

**Permission:** Write jobs

### Mark all notifications read

`POST /v1/notifications/mark-all-read`

**Permission:** Write jobs

## Mentions

Team notes where someone tagged the key's creator, as in the Mentions inbox. Office members only (`403` otherwise).

| Field | Type | Description |
|---|---|---|
| `id` | integer | |
| `read` | boolean | |
| `job` | object | `{id, name}`. |
| `scope` | string | `Job`, or the service or visit the note is about. |
| `note_id` | integer | The note. |
| `author` | object | `{id, name}`. |
| `content`, `created_at`, `link` | | |

### List mentions

`GET /v1/mentions`

**Permission:** Read jobs

Newest first. `unread=true` for unread only. The response also has `unread`.

### Mark a mention read

`POST /v1/mentions/{id}/mark-read`

**Permission:** Write jobs

## Daily Summary

### Get the daily summary

`GET /v1/daily-summary`

**Permission:** Read jobs

The dashboard's Daily Summary card: what happened in a local period, counted and listed. Jobs are fenced to what the key's creator can see.

| Name | Type | Required | Description |
|---|---|---|---|
| `period` | string | No | `today` (default), `yesterday`, `week` (from Monday) or `month`. |
| `who` | string | No | `team` (default), `me` (only what the key's creator did) or `members` (with `member_ids`). |
| `member_ids` | string | No | Comma-separated user ids, with `who=members`. |
| `scope` | string | No | `all` (default) or `mine` (only jobs the key's creator owns). |
| `timezone` | string | No | IANA zone for the day boundaries, e.g. `America/Chicago`. Default: the workspace's. |

`counts` has `new_leads`, `status_changes`, `emails`, `calls`, `won`, `lost`, `site_visits`, `followups` and `takeoff_minutes`. `items` lists up to 100 activities, newest first, each with `kind`, local `time`, `who`, `text`, `detail` and `link`. For `week` and `month`, `people` has the same counts per person.

```json
{
  "data": {
    "period": "today",
    "period_label": "Today",
    "date_label": "Tue, Oct 13",
    "timezone": "America/Chicago",
    "who": "team",
    "counts": { "new_leads": 3, "status_changes": 5, "emails": 12, "calls": 7, "won": 1, "lost": 0,
                "site_visits": 2, "followups": 4, "takeoff_minutes": 95 },
    "items": [
      { "kind": "won", "time": "2:05 PM", "who": "Office Manager",
        "text": "Status: Proposal Sent → Won · Northgate Plaza reseal", "detail": null,
        "link": "https://trutec.ai/jobs/70412" }
    ],
    "has_activity": true
  }
}
```

**Errors:** `400` a bad filter value; `403` missing permission, the key's creator is no longer in the workspace, or the feature isn't available to them; `404` an id that isn't in this workspace or that the key's creator can't see.
