Skip to main content

Remote Tracker API

Connect Harvest, preview remote work, check mappings, and import confirmed entries while provider credentials stay hidden.

The Remote Tracker API connects an organization to Harvest through a personal access token. Synchronization is a reviewed two-step flow: preview remote entries first, then confirm the rows and mappings you want to import.

/v1/work/tracker/harvest

Every endpoint needs the dedicated tracker:remote scope. This keeps provider credentials and remote-import authority separate from ordinary tracker:read and tracker:write access.

Endpoints

MethodEndpointPurpose
GET/harvestRead safe connection and sync status
POST/harvest/connectValidate and store Harvest credentials
PATCH/harvest/settingsConfigure scheduled sync
DELETE/harvestRemove credentials and sync configuration
POST/harvest/sync/previewFetch, normalize, deduplicate, and map remote entries
POST/harvest/sync/confirmImport reviewed preview rows
GET/harvest/sync/historyList completed and failed Harvest import jobs

Credential Boundary

Harvest accessToken is write-only. The connect endpoint accepts it, but status, settings, history, preview, and confirmation responses never return it. Responses also omit refresh tokens, provider payloads, internal hashes, and stored connection metadata.

Keep Harvest credentials in a server-side secret manager. Do not place them in browser storage, URLs, analytics events, error trackers, or application logs.

Connect Harvest

POST /v1/work/tracker/harvest/connect

FieldTypeNeededDescription
accessTokennon-empty stringYesHarvest personal access token; accepted once and never returned
accountIdstring, 1–100 charactersYesHarvest account identifier associated with the token
curl -X POST "https://api.conduitt.io/v1/work/tracker/harvest/connect" \
  -H "Authorization: Bearer $CONDUITT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accessToken": "${HARVEST_ACCESS_TOKEN}",
    "accountId": "${HARVEST_ACCOUNT_ID}"
  }'

Conduitt calls Harvest to validate the credential before it stores the credential. Invalid credentials return 400 and do not echo the token.

{
  "data": {
    "connected": true,
    "accountName": "Northwind Advisory",
    "lastSyncAt": null,
    "lastSyncStatus": "idle",
    "lastSyncEntriesImported": 0,
    "syncEnabled": false,
    "syncIntervalMinutes": 60
  }
}

When no connection exists, status returns:

{ "data": { "connected": false } }

Configure Scheduled Sync

PATCH /v1/work/tracker/harvest/settings

{
  "syncEnabled": true,
  "syncIntervalMinutes": 60
}

You must send syncEnabled. syncIntervalMinutes is optional and must be an integer from 15 to 1,440. The response uses the same safe connection shape as the status endpoint.

Preview Remote Work

POST /v1/work/tracker/harvest/sync/preview

{
  "dateFrom": "2026-07-01",
  "dateTo": "2026-07-12"
}

Both fields are optional YYYY-MM-DD dates. If you omit them, Conduitt requests the most recent 30 days. When you send both, dateFrom must not be later than dateTo.

Preview does not create work entries. It returns:

FieldDescription
providerAlways harvest_api for this integration
totalEntriesEntries returned by Harvest
newEntriesRows whose import hash is not already stored
duplicateEntriesRows already present for this organization and source
rowsNormalized work rows available for review
unmappedClientsHarvest client names without a confident Conduitt match
unmappedProjectsHarvest project names without a confident Conduitt match
clientMappingProposed map from the remote name to the Conduitt client UUID
projectMappingProposed map from the remote name to the tracker project UUID
dateRangeEffective from and to dates sent to Harvest

Each normalized row has this shape:

{
  "date": "2026-07-10",
  "durationMinutes": 90,
  "description": "Invoice approval workflow",
  "project": "Website redesign",
  "client": "Northwind Advisory",
  "billable": true,
  "rate": 175,
  "currency": "USD",
  "user": "Ada Lovelace",
  "externalId": "41820519"
}

Check unmappedClients, unmappedProjects, billable state, rates, and descriptions before confirmation. A preview is point-in-time. Run it again if you delay the review or if native tracker data changes.

Confirm an Import

POST /v1/work/tracker/harvest/sync/confirm

Send the reviewed rows plus optional corrected mappings. A request can contain up to 10,000 rows.

{
  "rows": [
    {
      "date": "2026-07-10",
      "durationMinutes": 90,
      "description": "Invoice approval workflow",
      "project": "Website redesign",
      "client": "Northwind Advisory",
      "billable": true,
      "rate": 175,
      "currency": "USD",
      "user": "Ada Lovelace",
      "externalId": "41820519"
    }
  ],
  "clientMapping": {
    "Northwind Advisory": "74957d67-9132-42b5-9238-b6fe520611d5"
  },
  "projectMapping": {
    "Website redesign": "a97b0cda-1c68-4fae-b84e-46d4340f63c6"
  }
}

The clientMapping and projectMapping values can be a UUID or null. Conduitt revalidates every non-null UUID against the active organization at confirmation time. A caller therefore cannot link imported work to another organization's client or project.

{
  "data": {
    "importJobId": "2745108d-2a9a-4bd8-9df4-e92a4dfcd0d9",
    "importedCount": 1,
    "skippedCount": 0
  }
}

Import hashes are unique per organization and source. If you confirm rows again that Conduitt already stored, it skips them and does not create duplicates. Use the returned counts and the sync history. Do not assume that Conduitt inserted every row you sent.

Sync History

GET /v1/work/tracker/harvest/sync/history?limit=50&offset=0

History returns status, row counts, start and completion timestamps, and audit identifiers. It omits credentials, row payloads, mappings, validation details, and internal error text.

{
  "data": [
    {
      "id": "2745108d-2a9a-4bd8-9df4-e92a4dfcd0d9",
      "status": "imported",
      "rowCountTotal": 12,
      "rowCountValid": 10,
      "rowCountInvalid": 0,
      "rowCountDeduped": 2,
      "startedAt": "2026-07-12T14:00:00.000Z",
      "completedAt": "2026-07-12T14:00:02.000Z",
      "createdAt": "2026-07-12T14:00:00.000Z",
      "updatedAt": "2026-07-12T14:00:02.000Z"
    }
  ],
  "meta": { "limit": 50, "offset": 0, "total": 1 }
}

Disconnect Harvest

DELETE /v1/work/tracker/harvest

Disconnect removes the stored credential and sync settings. Previously imported work entries and history remain available for billing and audit continuity.

Failure and Retry Behavior

  • A missing connection returns 404 for settings, preview, confirm, or disconnect.
  • Invalid Harvest credentials return 400 during connect.
  • Invalid dates, rows, or cross-organization mappings return 400 or 422 before import.
  • Harvest network or service failures can return 5xx. Retry preview with backoff because it has no write side effect.
  • Do not blindly retry confirmation after a timeout. Check sync history and list entries first. Duplicate hash detection makes replay safe for unchanged rows, but Conduitt still recommends a review step.
  • A failed confirmation marks its import job failed. It does not leave the job in processing.

We use cookies to improve your experience, analyze traffic, and personalize content.