Skip to content
TruTecDevelopers
Get an API key

Bulk data export

Stream whole datasets (customers, jobs, estimates, invoices, costs, labor, events) as JSON Lines, full or incremental, whenever you want.

View as Markdown

The bulk export streams whole tables of your workspace's data as newline-delimited JSON (JSON Lines). Use it to load a data warehouse, to back up your data, or to take your data with you.

  • On demand. Nothing runs on a schedule and no file is stored. Each request reads your data at that moment and streams it to you. You can pull as often as you like.
  • Full or incremental. Leave out updated_after for every row. Pass it to get only rows that changed.
  • One format. JSON Lines, one JSON object per line, UTF-8. Add .gz to the URL for gzip. CSV and Parquet are not offered. Most warehouse loaders read JSON Lines directly.
  • Your workspace only. Each request returns rows for the workspace that owns the key. Partner keys add ?firm_id= (see Partner API).
  • A link instead of a key. To hand a dataset to a person, or get one through an AI assistant, ask for a 24-hour link with POST /v1/exports and kind: workspace_data (see File exports).

Datasets#

Dataset One row per Changed-since fields
customers Customer (billing entity) updated_at
properties Property / jobsite, with coordinates updated_at
jobs Job updated_at
estimates Estimate header with totals updated_at
estimate_lines Estimate line item. All lines of an estimate are re-sent when the estimate changes updated_at (of the estimate)
change_orders Change order with value deltas updated_at
invoices Invoice with payment state updated_at
job_costs Job cost entry created_at, reviewed_at, reconciled_at
work_orders Crew work order (schedule and completion) updated_at
time_entries Crew clock-in/out: hours, category, wage rate clock_in, clock_out, approved_at, voided_at
allocations Work order, with its crew, people and equipment updated_at (of the work order)
events Event from the event log occurred_at

With updated_after, a row is included when any of its changed-since fields is later than the value you pass.

Row shapes:

  • customers: the customer object from Customers & sites, plus archived_at.
  • properties: id, name, address, external_ref, client_account_id, latitude, longitude, property_type, pci, archived_at, created_at, updated_at.
  • jobs: the job object from Jobs, including custom_fields.
  • estimates: the estimate summary from Estimates & proposals (totals, current_version, awarded_at, archived_at, ...), without line items.
  • estimate_lines: the line item object from the estimate detail, plus estimate_id, job_id, area_id, area_name, estimate_updated_at.
  • change_orders: id, job_id, number, title, kind, status, source, stage, amount_delta, tax_delta, cost_delta, days_delta, ep_estimate_id, decision_comment, sent_at, signed_at, created_at, updated_at.
  • invoices, job_costs, time_entries, allocations: the same objects as GET /v1/jobs/{id}/invoices, /costs, /labor and /allocation in Job costing.
  • work_orders: id, job_id, title, status, phase, crew_id, display_qty, display_unit, completed_qty, completed_unit, estimated_days, scheduled_date, dispatched_at, completed_at, created_at, updated_at.
  • events: id, event_id, event_type, occurred_at, entity_type, entity_id, job_id, data. Note that entity_type and entity_id are flat here, unlike the nested entity object in GET /v1/events.

List datasets#

GET /v1/exports

Permission: Read jobs

curl https://trutec.ai/api/v1/exports \
  -H "X-API-Key: sk_your_api_key_here"
{
  "data": [
    {
      "dataset": "customers",
      "description": "Customers (billing entities)",
      "changed_since_fields": ["updated_at"],
      "url": "/api/v1/exports/customers.jsonl.gz"
    },
    {
      "dataset": "job_costs",
      "description": "Job cost entries by category",
      "changed_since_fields": ["created_at", "reviewed_at", "reconciled_at"],
      "url": "/api/v1/exports/job_costs.jsonl.gz"
    }
  ]
}

Download a dataset#

GET /v1/exports/{dataset}.jsonl or GET /v1/exports/{dataset}.jsonl.gz

Permission: Read jobs

Name Type Required Description
dataset path Yes A dataset name from the table above
updated_after ISO 8601 No Only rows changed after this time. Use the watermark from your last pull
# Full export, gzipped
curl -o jobs.jsonl.gz https://trutec.ai/api/v1/exports/jobs.jsonl.gz \
  -H "X-API-Key: sk_your_api_key_here" -D headers.txt

# Incremental: only rows changed since the last watermark
curl "https://trutec.ai/api/v1/exports/jobs.jsonl?updated_after=2026-10-11T06:00:00Z" \
  -H "X-API-Key: sk_your_api_key_here"

Response body (one object per line, shortened here):

{"id":4812,"name":"Northgate Plaza","company":"Northgate Property Group","address":"1200 Commerce Dr, Austin, TX","status":"won","status_label":"Won","estimated_value":48250.0,"client_account_id":915,"location_id":2290,"external_ref":"ERP-JOB-20931","custom_fields":{},"created_at":"2026-10-01T14:02:09.331201","updated_at":"2026-10-12T13:48:41.102938"}
{"id":4813,"name":"Riverside Medical Sealcoat","company":"Riverside Health","address":"88 Lake Rd, Austin, TX","status":"lead","status_label":"Lead","estimated_value":null,"client_account_id":917,"location_id":2295,"external_ref":null,"custom_fields":{},"created_at":"2026-10-12T09:15:00.000000","updated_at":"2026-10-12T09:15:00.000000"}

Response headers:

Header Meaning
Content-Type application/x-ndjson, or application/gzip for .gz
X-TruTec-Export-Dataset The dataset name
X-TruTec-Export-Watermark Save this. Send it as updated_after on your next pull
Content-Disposition attachment; filename="<dataset>.jsonl[.gz]"

Rows are streamed in id order. The response has no total count and no pagination. Read until the stream ends. An empty dataset returns 200 with an empty body.

Errors:

  • 404 Unknown dataset, with a datasets list of valid names
  • 404 Use /api/v1/exports/<dataset>.jsonl or .jsonl.gz (any other extension, such as .csv)
  • 400 updated_after must be an ISO 8601 timestamp
  • 403 API key lacks job read permission

Keeping a copy in sync#

  1. Run a full export of every dataset once.
  2. Store each dataset's X-TruTec-Export-Watermark.
  3. On a schedule (for example nightly), pull each dataset with updated_after=<stored watermark> and upsert rows by id (work_order_id for allocations; estimate_id plus line id for estimate_lines). Then store the new watermark.
  4. Run a full export now and then (for example weekly) to catch deletes.

The watermark is set one minute before the export started. A row saved while an export is running is sent again on the next pull, so upserts are safe and nothing is missed.

Warning

Incremental pulls cannot show hard deletes. A deleted row simply stops appearing. Archived customers, properties and estimates carry archived_at. Compare a periodic full export with your copy to find rows that were removed.

Other limits of incremental pulls:

  • job_costs has no updated_at. An edit to a cost entry after it was created shows up incrementally only when it is reviewed or reconciled. Full exports always have the current values.
  • estimate_lines re-sends every line of an estimate when the estimate's updated_at moves. Replace all lines for that estimate_id rather than upserting single lines.

What the export does not include#

The export covers the business records above. These are not in it:

  • Measurements and takeoffs (geometry, areas, counts, quantities). Use GET /v1/measurements and GET /v1/measurements/{id} (Measurements).
  • Photos, videos, documents and other files. Use GET /v1/jobs/{id}/photos for job photos. Other files are not available by API.
  • Proposal documents, e-signatures and proposal messages. The proposal_* webhooks carry status only.
  • Issued estimate versions and the estimate edit log. The export has the live estimate. Use GET /v1/estimates/{id}/versions, GET /v1/estimates/{id}?version=N and GET /v1/estimates/{id}/revisions.
  • Estimate lines that are hidden from bid totals. Suppressed and inactive services and inactive areas are left out, the same as on the customer proposal.
  • Invoice line items and invoice bill-to and remit-to text. The invoices dataset has amounts, tax, discount, dates, status and QuickBooks sync state.
  • Notes, activity timeline, tasks, emails and text messages.
  • Contacts (people) as their own records. Customer billing contact fields and job contact fields are included.
  • Your settings and catalog: services and pricing, rate books, pipeline stages, custom field definitions, crews and equipment lists (crews, people and equipment appear only as assigned on work orders in allocations), users and permissions.
  • Assessments and condition ratings, and webhook delivery history.

Access and your data#

  • Your data belongs to you. You can export it at any time while your workspace has API access.
  • API access needs an active plan that includes it (Gold or higher). If the subscription is canceled, past due or the trial has ended, API calls, including exports, are refused. Run a full export before you cancel.
  • Exports do not count toward your key's usage. See Limits & billing.
  • Exports are generated live and never written to storage, so there is no export file to expire or leak.