Skip to content
TruTecDevelopers
Get an API key

Customer notices & dispatch

Tell the customer when their work is scheduled, record their approval of a date, send the dispatch packet to crews, and manage the crew's packet link.

View as Markdown

Once a visit is on the schedule, two groups of people need to hear about it: the customer (when the crew is coming) and the crew (where to go and what to do). These endpoints do both, the same way the visit drawer and the Dispatch board do in TruTec.

Warning

notify-customer and retry-customer-text email or text your customer. send-dispatch and schedule/send-dispatch text and email your crew. None of these can be unsent. Show the user who it goes to before you call them.

Who the API acts as. Writes are made as the person who created the API key, with their own TruTec permissions. Customer emails go out from that person's connected mailbox (the one they connected in TruTec), so replies come back to them. Customer texts go out from the workspace's own texting number, only between 8am and 9pm workspace time. If the key's creator has left or switched workspaces, writes return 403 Key owner unavailable. Partner keys act as the workspace owner.

Operations must be on. These endpoints are part of TruTec's operations tools. If they are not turned on for the workspace, they return 403 Operations not enabled.

Holds don't notify anyone. A "hold a date" block (see Holds & schedule requests) is never sent to a customer or a crew; these endpoints return 400 for a hold.

Customer schedule notice#

Preview the notice#

GET /v1/work-orders/{id}/customer-notice

Permission: Read jobs

What Email customer would send for this visit, and who it can go to. Sends nothing.

{
  "data": {
    "work_order_id": 4120,
    "job_name": "Northgate Plaza reseal",
    "work_type": "Sealcoating",
    "scheduled_for": "Tuesday, November 3, 2026",
    "subject": "Schedule update: Northgate Plaza reseal — Tuesday, November 3, 2026",
    "body": "Northgate Plaza reseal has been scheduled for Tuesday, November 3, 2026.\nWork: Sealcoating\n\nPlease reply to this email if you have any questions or site-access updates.",
    "email_contacts": [
      { "name": "Property Manager", "email": "[email protected]", "role": "Primary", "primary": true }
    ],
    "cc_options": [
      { "name": "Sales Rep", "email": "[email protected]", "role": "Salesperson" }
    ],
    "text_contacts": [
      { "name": "Property Manager", "phone": "+15125550100", "role": "Job contact", "primary": true }
    ],
    "texting_ready": true,
    "texting_open_now": true,
    "confirmation": { "requested_at": null, "confirmed_at": null, "note": null },
    "customer_notified_at": null,
    "history": [
      { "sent_at": "2026-10-20T15:02:11Z", "status": "sent", "to": ["[email protected]"], "cc": [], "opened_at": "2026-10-20T16:40:00Z", "open_count": 2 }
    ]
  }
}
Field Description
email_contacts The job's contacts with an email address, primary first.
text_contacts The job's customer phones. Texts can only go to these numbers.
texting_ready The workspace has its own texting number. Without it, texts fail; email instead.
texting_open_now It's between 8am and 9pm in the workspace's time zone.
confirmation When you last asked the customer to confirm the date, and when they did.
history The last ten schedule emails for this visit, with when they were opened.

Email or text the customer#

POST /v1/work-orders/{id}/notify-customer

Permission: Write jobs

Warning

This emails and/or texts your customer, and cannot be unsent.

Field Type Required Description
to array of strings One of to or text_to Customer email addresses.
cc array of strings No Copies, for example the salesperson from cc_options. Up to 30 addresses in all.
text_to array of strings One of to or text_to Up to 5 phones from text_contacts. Any other number is refused (400).
subject string No Defaults to the standard subject from the preview.
body string No The email message. Defaults to the standard message. Texts always use the short standard text.
request_confirmation boolean No Adds a one-click Confirm this date button to the email (and the link to texts). The customer needs no account.
curl -X POST "https://trutec.ai/api/v1/work-orders/4120/notify-customer" \
  -H "X-API-Key: sk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "to": ["[email protected]"], "text_to": ["+15125550100"], "request_confirmation": true }'
{
  "data": { "id": 4120, "status": "scheduled", "scheduled_date": "2026-11-03", "customer_notified_at": "2026-10-28T16:02:11Z", "...": "the full work order" },
  "emailed_to": ["[email protected]"],
  "cc": [],
  "texts": [{ "to": "+15125550100", "sent": true, "error": null }],
  "confirmation_requested": true
}

The email is logged on the job's timeline and its opens are tracked. A text outside texting hours comes back with "error": "quiet_hours" and is not sent. If the email itself can't be sent (no connected mailbox, for example), or only texts were asked for and none went out, the response is 502.

Record the customer's approval#

POST /v1/work-orders/{id}/record-customer-approval

Permission: Write jobs

When the customer said yes to the date by phone, by email or in person, record it so the visit shows the date as approved. The approval, who gave it and how are written to the job's history. Sends nothing.

Field Type Required Description
customer_name string Yes Who approved, up to 120 characters.
method string Yes phone, email or in_person.
note string No Up to 300 characters.
scheduled_date string No The date they approved. Defaults to the visit's current date; if the visit has moved since, the response is 409.

Returns the work order and already_recorded (true when the date was already approved, by the customer's own click or an earlier record; nothing changes then).

Retry a failed schedule text#

When a schedule text to the customer failed before it went out (texting not set up yet, quiet hours, a bad number), the office can send it again after checking the number. It's two steps, so the person sees the exact number and message first.

GET /v1/work-orders/{id}/customer-text-retry

Permission: Read jobs

Returns the phone, contact_name, the exact message, can_retry (with a reason when it can't go now) and a retry_token valid for 5 minutes. When there is no failed text to retry, or its outcome is uncertain, or the visit changed since, the response is 409 and says why.

POST /v1/work-orders/{id}/retry-customer-text with {"retry_token": "..."}

Permission: Write jobs

Warning

This texts your customer.

Sends the text once. outcome is accepted, failed (not sent; check texting setup), not_sent (the contact or visit changed; nothing was sent) or unknown (check its sending status before trying again).

Dispatch to crews#

Dispatching sends the crew everything they need for a visit: an in-app alert to each crew member with TruTec access that opens the visit in the crew app, plus a text and an email with a link to the dispatch packet (site, scope, map, safety sheets and job documents) for members with a phone or email on file. It is separate from publishing the schedule: publishing tells crews what's on their calendar; dispatching sends the packet for a specific visit.

Send a visit's dispatch#

POST /v1/work-orders/{id}/send-dispatch

Permission: Write jobs

Like Send dispatch in TruTec. The visit needs a crew with members. Assignment rules (for example a required certification) are checked first; a rule that isn't met returns 409 with violations.

{
  "data": { "id": 4120, "dispatched_at": "2026-11-02T22:10:00Z", "...": "the full work order" },
  "crew_notified_count": 3,
  "texts_sent": 3,
  "texts_failed": 0,
  "emails_sent": 2,
  "missing_contacts": ["Laborer 2 (no app access)"],
  "packet_url": "https://trutec.ai/dispatch/packet/Xy3…",
  "jsa": { "required": true, "complete": false, "missing": ["JSA acknowledgement"] }
}

missing_contacts lists people the packet could not reach. jsa is the visit's safety-sheet status (job safety analysis): what the crew still has to complete.

Send a day's dispatch#

POST /v1/schedule/send-dispatch

Permission: Write jobs

Like Send to crews: dispatches every scheduled or in-progress visit with a crew on one day.

Field Type Required Description
scheduled_date string Yes The day, YYYY-MM-DD.
crew_ids array of integers No Only these crews. Default: every crew with work that day.

Returns totals (work_order_count, sent_count, skipped_count, texts_sent, texts_failed, emails_sent, missing_contacts) and one results row per visit with sent and, for a skipped visit, error.

POST /v1/work-orders/{id}/dispatch-link

Permission: Write jobs

The secure link to the visit's dispatch packet, to share however you like. Anyone with the link can open the packet without signing in until expires_at. Returns the current link when there is one, otherwise makes a new one. Sends nothing.

{
  "data": {
    "work_order_id": 4120,
    "url": "https://trutec.ai/dispatch/packet/Xy3…",
    "expires_at": "2026-12-02T22:10:00Z",
    "crew_map_url": "https://trutec.ai/dispatch/packet/Xy3…/crew-map",
    "schedule_published": true,
    "crew": { "id": 18, "name": "Seal Crew 1", "members": [{ "name": "Crew Lead", "role": "lead" }] },
    "recipients": [
      { "user_id": 344, "name": "Crew Lead", "role": "lead", "has_app_access": true, "has_phone": true, "has_email": true }
    ],
    "jsa": { "required": false, "complete": true, "missing": [] }
  }
}

DELETE /v1/work-orders/{id}/dispatch-link

Permission: Write jobs and Delete records

Every current packet link for the visit stops working (for example after it was shared with the wrong person). Returns 204. The next dispatch, or POST .../dispatch-link, makes a new link.