Skip to main content

Harvest ExecModule

Overview

HarvestModule connects ValkyrAI workflows to Harvest API v2 for client and time-entry management plus bounded project, expense, and invoice inspection. It exposes thirteen operations through the native map I/O ExecModule ABI and keeps the bearer token and numeric Harvest account ID in a READY IntegrationAccount.

The module fixes the production origin to https://api.harvestapp.com/v2, sends the token only in Authorization: Bearer, sends the account binding only in Harvest-Account-Id, bounds request and response sizes, rejects credential-like filter and payload fields, returns one provider page per execution, retries transient reads only, and sends create, update, and delete operations once. Time-entry deletion requires confirmDestructive: true.

n8n's Harvest integration informed the resource and operation split across clients, projects, time entries, expenses, and invoices. ValkyrAI adds a generated IntegrationAccount boundary, fixed-origin enforcement, explicit destructive confirmation, provider-identity checks, bounded output, write reconciliation, and shared credential redaction.

Usage

  1. Create a Harvest personal access token or OAuth authorization for the minimum account access required.
  2. Store the token only in the encrypted apiKey or password field of a ValkyrAI IntegrationAccount, store the numeric Harvest account ID in accountId, set the account to READY, and bind it through ExecModuleConfig.authConfig.integrationAccount.
  3. Choose one operation and provide its conditional fields.
  4. For list operations, pass nextPage into page on a later execution when hasMore is true.
  5. Reconcile any ambiguous create, update, or delete in Harvest before manually retrying.

Inputs

NameTypeRequiredDescriptionConstraints
operationstringYesOperation listed below.Exact allowlist of thirteen values.
recordIdintegerConditionalClient, project, or time-entry ID.Positive integer with at most 19 digits.
filtersobjectConditionalResource-specific list filters.At most 12 allowlisted scalar fields; each value at most 2,048 characters.
recordobjectConditionalClient or time-entry create/update fields.Non-empty, at most 16 allowlisted fields and 256 KiB serialized; credential-like fields rejected.
pageintegerNoOne-based provider page.1–100,000; default 1.
perPageintegerNoProvider page size.1–2,000; default 100.
confirmDestructivebooleanConditionalExplicit approval for time-entry deletion.Must be true for delete_time_entry.

Client filters support is_active and updated_since. Project filters support is_active, client_id, and updated_since. Time-entry filters support user, client, project, task, external-reference, billed/running/approval state, update time, and inclusive date range. Expense filters support user, client, project, billed state, update time, and date range. Invoice filters support client, project, update time, date range, and invoice state.

Client writes allow name, is_active, address, and currency; creation requires a non-empty name. Time-entry writes allow user_id, project_id, task_id, spent_date, hours, start/end times, notes, and external_reference. Creation requires project_id, task_id, and spent_date.

Outputs

NameTypeWhen presentDescription
statusstringAlwayssuccess or error.
operationstringAlwaysNormalized operation.
dataobjectSingle-resource successVerified Harvest resource.
idstringResource response has an IDPositive provider identity.
deletedIdstringDelete successConfirmed requested time-entry ID.
itemsarrayList successOne bounded provider page.
countintegerList successItems returned in this page.
pageintegerList successCurrent one-based page.
totalintegerHarvest supplies total_entriesProvider-reported total entries.
hasMorebooleanList successWhether another page may exist.
nextPageintegerAnother page existsPage for the next execution.
httpStatusintegerProvider call succeedsHarvest response status.
attemptsintegerProvider call or errorHTTP attempts consumed.
errorobjectFailureRedacted code, message, and retryable fields.

Harvest results can contain employee, customer, billing, invoice, and time-allocation data and are classified confidential. Tokens and provider bodies are never copied into progress logs.

IntegrationAccount Requirements

SettingRequirement
ProviderHarvest API v2
AuthenticationOAuth2 or personal access token in Authorization: Bearer
StatusExactly READY
accountNameHuman-readable Harvest account identity
accountIdNumeric Harvest account ID used in Harvest-Account-Id
apiKey or passwordEncrypted bearer token, 20–8,192 non-whitespace characters
RelationshipExecModuleConfig.authConfig.integrationAccount
Suggested accessOnly the selected Harvest account and the least administrative permission needed

Harvest personal access tokens can access authorized accounts broadly. Prefer OAuth with the smallest account scope when multiple users will run workflows, and rotate/revoke tokens independently from workflow definitions.

Configuration

Illustrative persisted configuration:

{
"version": "1.0.0",
"authConfig": {
"authStrategy": 1,
"integrationAccount": "integration-account:harvest-revenue-ops"
},
"payloadConfig": {
"parameters": "{\"operation\":\"list_time_entries\",\"page\":1,\"perPage\":100,\"filters\":{\"project_id\":14308069,\"is_billed\":false}}"
}
}

The relationship is symbolic. The generated IntegrationAccount record owns encrypted credentials and the account binding.

Operations

OperationHarvest pathBehavior
get_current_userGET /users/meRead the authenticated Harvest user.
list_clientsGET /clientsList clients with bounded active/update filters and pagination.
get_clientGET /clients/{clientId}Read one client and verify its ID.
create_clientPOST /clientsCreate one client; single-attempt write.
list_projectsGET /projectsList projects by client, active state, or update time.
get_projectGET /projects/{projectId}Read one project and verify its ID.
list_time_entriesGET /time_entriesList bounded time entries with documented filters.
get_time_entryGET /time_entries/{timeEntryId}Read one time entry and verify its ID.
create_time_entryPOST /time_entriesCreate one time entry; single-attempt write.
update_time_entryPATCH /time_entries/{timeEntryId}Update one entry and require the same returned ID.
delete_time_entryDELETE /time_entries/{timeEntryId}Permanently delete one eligible entry after explicit confirmation.
list_expensesGET /expensesInspect expenses with account-safe filters and pagination.
list_invoicesGET /invoicesInspect invoices by client/project/date/state.

Project/client update or deletion, expense mutations/receipt transfer, invoice creation/sending/payment, estimates, assignments, approval submission, timer start/stop shortcuts, and reports are deferred. These surfaces introduce separate attachment, outbound-message, billing, approval, or destructive contracts.

Errors and Failure Modes

FailureCauseRetry guidance
VALIDATION_ERRORMissing field, invalid ID/filter/bound, absent delete confirmation, unsafe write key, invalid credential binding, or mismatched provider identity.Correct the request; local validation sends no provider call.
INTEGRATION_ACCOUNT_REQUIREDNo bound secure account.Bind the intended Harvest account.
INTEGRATION_ACCOUNT_NOT_READYAccount status is not READY.Repair or reauthorize the account.
HARVEST_HTTP_401Token is missing, expired, or invalid.Reauthorize or rotate the token.
HARVEST_HTTP_403User or token lacks permission for the account/resource.Grant only the required account permission.
HARVEST_HTTP_404Resource is absent or not visible to this account.Reconcile the account and resource ID.
HARVEST_HTTP_422Provider rejected a field, state transition, locked entry, or permission-dependent mutation.Correct the request or provider state; do not blindly retry.
HARVEST_HTTP_429API rate limit exceeded.Reads use bounded retry and numeric Retry-After; writes remain single-attempt.
HARVEST_HTTP_5xx / NETWORK_ERRORProvider or transport failure.Safe reads retry up to three total attempts. Reconcile writes before manual retry.
RESPONSE_TOO_LARGEProvider response exceeded 5 MiB.Narrow filters or page size.

Provider messages are bounded to 500 characters and pass through token replacement plus the shared sensitive-data policy. A failed write may have reached Harvest, so the module refuses automatic replay.

Example

Find unbilled August time for one project:

{
"operation": "list_time_entries",
"page": 1,
"perPage": 100,
"filters": {
"project_id": 14308069,
"is_billed": false,
"from": "2026-08-01",
"to": "2026-08-31"
}
}

Expected normalized result:

{
"status": "success",
"operation": "list_time_entries",
"items": [
{
"id": 636708723,
"spent_date": "2026-08-12",
"hours": 2.5,
"project": {"id": 14308069, "name": "Enterprise AI"}
}
],
"count": 1,
"page": 1,
"total": 1,
"hasMore": false,
"httpStatus": 200,
"attempts": 1
}

Notes

  • Pagination: one execution returns one provider page. Reuse nextPage as page; no silent all-pages loop occurs. Harvest permits per_page from 1 through 2,000, while this module also caps page at 100,000 and response bytes at 5 MiB.
  • Rate limits: 429, 408, and selected 5xx statuses are transient only for reads. The module honors a numeric Retry-After up to five seconds and caps reads at three attempts.
  • API limits: request JSON is capped at 256 KiB, list filters at 12 scalar values, mutable records at 16 fields, and filter values at 2,048 characters. Harvest permissions still determine which users, entries, projects, expenses, and invoices are visible.
  • Idempotency: reads are transport-retry-safe. Client and time-entry creation, time-entry update, and deletion are single-attempt and may have committed before a network failure. Reconcile by resource ID, project/task/date, or client name before retrying.
  • Destructive behavior: delete_time_entry permanently removes an eligible entry and requires confirmDestructive: true. Locked/approved/invoiced state and project/task archival can restrict deletion. No bulk deletion is exposed.
  • Billing and privacy: time, expense, invoice, client, and employee data is confidential. Apply least privilege and human review before using output for payroll, billing, performance evaluation, or external communication.
  • External verification: deterministic tests cover validation, request construction, bearer/account headers, pagination, create/update/delete behavior, destructive confirmation, transient read retries, write non-retry, redaction, identity checks, fixed origin, response normalization, and metadata discovery. Live provider execution is deferred until a separately authorized Harvest account and token are supplied.

See the official Harvest API v2 overview, authentication contract, pagination guidance, time-entry API, and invoice API for provider behavior. The official n8n Harvest node's client, project, time-entry, expense, invoice, user, task, and contact resources informed the functional reference.