Skip to content
TruTecDevelopers
Get an API key

Merge & search

Search the whole workspace like the search box in TruTec, and merge duplicate customers, contacts and sites after previewing what moves.

View as Markdown

Search the workspace#

GET /v1/search?q=

Permission: Read jobs

The search box at the top of TruTec, as one call. It looks through jobs (name, job number, company, address, contact, Property ID, work order and accounting numbers), customers, contacts, sites, leads, proposals, invoices, logged notes and emails, projects and measurements, and returns the best few matches of each type. It sees what the person who created the API key sees: a member who only sees their own jobs gets only those.

Name Type Required Description
q string Yes 2 to 120 characters.
types string No Comma list to keep: job, client_account, contact, location, lead, proposal, invoice, note, project, measurement.
curl "https://trutec.ai/api/v1/search?q=oak%20hills" -H "X-API-Key: sk_your_api_key_here"
{
  "data": [
    {"type": "job", "id": 70412, "title": "Oak Hills Plaza reseal", "subtitle": "Sealcoating · 1200 Commerce Dr",
     "status": "proposal_sent", "app_url": "https://trutec.ai/jobs/70412"},
    {"type": "client_account", "id": 3110, "title": "Oak Hills Property Group", "subtitle": "3 contacts · 12 jobs",
     "status": null, "app_url": "https://trutec.ai/jobs/companies/3110"},
    {"type": "location", "id": 812, "title": "Oak Hills Plaza", "subtitle": "1200 Commerce Dr",
     "status": null, "app_url": "https://trutec.ai/jobs/locations/812"}
  ],
  "query": "oak hills"
}

There is no paging: add words to narrow. Use the id with the matching resource (/v1/jobs/{id}, /v1/client-accounts/{id}, /v1/contacts/{id}, /v1/locations/{id}, ...).

Merge duplicates#

Two records for the same customer, person or site? Merge one into the other. The record in the URL is merged away; the one you name in the body stays. Everything on the merged record moves over: jobs, contacts, sites, notes and logged calls and emails, follow-ups, reminders, files, divisions, capital plan items, re-bid history and campaign recipients. Then:

  • a merged customer or contact is deleted;
  • a merged site is archived and points to the site it went into (its old Site ID keeps resolving).

Field values follow the app's defaults: the surviving record's value, unless it is blank (then the other one's). Lead details follow special rules (the further-along stage and the larger value win, tags and notes are combined). To pick the other side for a field, send choices.

Warning

A merge can't be undone through the API. Preview first, and confirm with whoever owns the data.

Merges run the app's duplicate-review merge as the key's user, who must be an office user (not a crew login).

Preview a merge#

GET /v1/client-accounts/{account_id}/merge-preview?into={keep_id} GET /v1/contacts/{contact_id}/merge-preview?into={keep_id} GET /v1/locations/{location_id}/merge-preview?into={keep_id}

Permission: Read jobs

Changes nothing.

{
  "data": {
    "keep": {"id": 3110, "name": "Oak Hills Property Group", "archived": false},
    "merge_away": {"id": 3187, "name": "Oak Hills Property Grp", "archived": false},
    "fields": [
      {"field": "billing_address", "label": "Billing address", "keep_value": "PO Box 18, Austin TX",
       "merge_away_value": "1200 Commerce Dr, Austin TX", "needs_choice": true, "default": "keep", "can_keep_both": false},
      {"field": "billing_contact_email", "label": "Billing email", "keep_value": null,
       "merge_away_value": "[email protected]", "needs_choice": false, "default": "drop", "can_keep_both": false}
    ],
    "moves": [{"count": 4, "label": "jobs"}, {"count": 2, "label": "contacts"}],
    "blockers": []
  }
}

fields lists only the fields that differ. needs_choice: true means both records have a value; default says which side is kept unless you choose (keep = the surviving record's). Site previews add blockers (reasons the merge is refused, such as both sites carrying different Site IDs from a connected system) and invoiced_job_count.

Merge#

POST /v1/client-accounts/{account_id}/merge with into_client_account_id POST /v1/contacts/{contact_id}/merge with into_contact_id POST /v1/locations/{location_id}/merge with into_location_id

Permission: Write jobs + Delete records

Name Type Required Description
into_client_account_id / into_contact_id / into_location_id integer Yes The record that stays.
choices object No {field: "keep" \| "drop"} for fields from the preview. Custom fields are custom:<key>. On a contact, "phone": "both" keeps both numbers.
move_invoiced_jobs boolean No Sites only: a site with invoiced jobs is refused unless this is true (check with accounting first).
curl -X POST "https://trutec.ai/api/v1/client-accounts/3187/merge" \
  -H "X-API-Key: sk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"into_client_account_id": 3110, "choices": {"billing_address": "drop"}}'
{
  "data": {
    "kept": {"id": 3110, "name": "Oak Hills Property Group", "archived": false},
    "merged_id": 3187,
    "merged_name": "Oak Hills Property Grp",
    "message": "Merged ‘Oak Hills Property Grp’ into ‘Oak Hills Property Group’ — 4 jobs, 2 contacts moved."
  }
}

Errors: 400 merging a record into itself, or an unknown field in choices; 403 when the key lacks Delete records or its user isn't an office user; 404 for either id outside the workspace; 409 when the app refuses the merge (for example the workspace has turned off deleting records, or a site has invoiced jobs), with its reason in message.