CONNECT APP
Build with Harvest
Productivity
- OAuth
MCP
Give your agent Harvest tools
Every Harvest action is exposed as an MCP tool on Pipedream's remote server. Point a client at it with your end user's ID and Connect resolves that user's Harvest account for each tool call — you store no tokens.
// accessToken: mint a short-lived token with the Connect SDK — see the MCP guide
const transport = new StreamableHTTPClientTransport(
new URL("https://remote.mcp.pipedream.net/v3"),
{
requestInit: {
headers: {
Authorization: `Bearer ${accessToken}`,
"x-pd-project-id": "{project_id}",
"x-pd-environment": "production",
"x-pd-external-user-id": "{external_user_id}", // any stable ID for this user in your system
"x-pd-app-slug": "harvest",
},
},
},
)
const mcp = new Client({ name: "my-agent", version: "1.0.0" })
await mcp.connect(transport)
const { tools } = await mcp.listTools()
// e.g. run Create Invoice:
const result = await mcp.callTool({
name: "harvest-create-invoice",
arguments: {
accountId: "Account ID",
clientId: "Client ID",
},
})# access_token: mint a short-lived token with the Connect SDK — see the MCP guide
headers = {
"Authorization": f"Bearer {access_token}",
"x-pd-project-id": "{project_id}",
"x-pd-environment": "production",
"x-pd-external-user-id": "{external_user_id}", # any stable ID for this user in your system
"x-pd-app-slug": "harvest",
}
async with streamablehttp_client("https://remote.mcp.pipedream.net/v3", headers=headers) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
# e.g. run Create Invoice:
result = await session.call_tool("harvest-create-invoice", {
"accountId": "Account ID",
"clientId": "Client ID",
})API PROXY
Call the Harvest API directly
For an endpoint with no pre-built tool, the Connect proxy forwards your request to the Harvest API with the connected user's credentials attached. You store no tokens and write no refresh logic.
const resp = await pd.proxy.get({
externalUserId: "{external_user_id}", // any stable ID for this user in your system
accountId: "apn_xxxxxxx",
url: "https://id.getharvest.com/api/v2/accounts",
})
// Any allowed Harvest endpoint works here. Pipedream attaches the
// connected account's credentials to the outgoing request.# The path segment is the target URL, URL-safe base64 encoded:
# https://id.getharvest.com/api/v2/accounts
curl "https://api.pipedream.com/v1/connect/{project_id}/proxy/aHR0cHM6Ly9pZC5nZXRoYXJ2ZXN0LmNvbS9hcGkvdjIvYWNjb3VudHM?external_user_id={external_user_id}&account_id=apn_xxxxxxx" \
-H "Authorization: Bearer {access_token}" \
-H "x-pd-environment: production"SDK
Run Harvest actions from your backend
Connect a user's Harvest account once, then run Create Invoice on their behalf from your own code — TypeScript, Python, or plain HTTP.
import { PipedreamClient } from "@pipedream/sdk"
const pd = new PipedreamClient({
projectId: process.env.PIPEDREAM_PROJECT_ID!,
clientId: process.env.PIPEDREAM_CLIENT_ID!,
clientSecret: process.env.PIPEDREAM_CLIENT_SECRET!,
projectEnvironment: "production",
})
const result = await pd.actions.run({
id: "harvest-create-invoice",
externalUserId: "{external_user_id}", // any stable ID for this user in your system
configuredProps: {
harvest: { authProvisionId: "apn_xxxxxxx" },
accountId: "Account ID",
clientId: "Client ID",
},
})from pipedream import Pipedream
pd = Pipedream(
client_id="{oauth_client_id}",
client_secret="{oauth_client_secret}",
project_id="{project_id}",
project_environment="production",
)
result = pd.actions.run(
id="harvest-create-invoice",
external_user_id="{external_user_id}", # any stable ID for this user in your system
configured_props={
"harvest": {"authProvisionId": "apn_xxxxxxx"},
"accountId": "Account ID",
"clientId": "Client ID",
},
)curl -X POST https://api.pipedream.com/v1/connect/{project_id}/actions/run \
-H "Content-Type: application/json" \
-H "X-PD-Environment: production" \
-H "Authorization: Bearer {access_token}" \
-d '{
"external_user_id": "{external_user_id}",
"id": "harvest-create-invoice",
"configured_props": {
"harvest": { "authProvisionId": "apn_xxxxxxx" },
"accountId": "Account ID",
"clientId": "Client ID"
}
}'TOOLS
Harvest actions
On-demand operations your product or agent can configure and run on behalf of a connected user.
-
Create Invoice
actionCreate a new free-form invoice for a client. Provide Line Items as a JSON array to itemize the invoice. Example: call with clientId set to InGen Corp's client ID, subject="Consulting Services", lineItems=[{"kind":"Service","description":"Genetics consulting","unit_price":2000,"quantity":1}]. See the documentation.Writev0.0.1 -
Create Project
actionCreate a new project. Example: call with clientId set to InGen Corp's client ID, projectName="Compound Perimeter Fence Upgrade", isBillable=true, billBy="Project", budgetBy="none". See the documentation.Writev0.0.1 -
Create Task Assignment
actionAssign a task to a project. Use Get Projects to find a project ID and List Tasks to find a task ID. Example: call with projectId set to the Jurassic Park Construction project's ID and taskId set to the Genetics Research task's ID to put that task on the project. See the documentation.Writev0.0.1 -
Create Timesheet Entry
actionCreate a new time entry. Two ways to log time, matching the account's Company setting: on duration-based accounts (wants_timestamp_timersisfalse— the common case) set Hours to log a completed entry directly. On timestamp-based accounts (wants_timestamp_timersistrue) leave Hours blank and use Started Time/Ended Time instead — leave both time fields blank too to start a running timer now, or set both to log a completed entry with explicit start/end times. Passing Started/Ended Time on a duration-based account is silently ignored by Harvest (it starts a running timer instead) — if that happens, switch to Hours. Use Get Projects to find a Project ID, List Tasks to find a Task ID, and List Users to find a User ID. Example (duration-based): call with projectId, taskId, spentDate="2026-09-17", hours=3.5. Example (timestamp-based): the same call with hours omitted and startedTime/endedTime set instead. See the documentation and start/end time variant.Writev1.0.0 -
Create User
actionCreate a new user in the Harvest account. Example: call with firstName="Ian", lastName="Malcolm", email="ian.malcolm@example.com" to add a new team member. See the documentation.Writev0.0.1 -
Create User Assignment
actionAssign a user to a project. Use Get Projects to find a project ID, and List Users to find user IDs. Example: call with projectId and userId set to staff that person on the project. See the documentation.Writev0.0.1 -
Delete Invoice
actionPermanently delete an invoice. Use List Invoices to find a valid ID. Example: call with invoiceId set to a draft invoice's ID to remove it before it's sent. See the documentation.Writev0.0.1 -
Delete Project
actionPermanently delete a project and all time entries and expenses tracked to it (invoices are not deleted). Use Get Projects to find a project ID. Example: call with projectId set to a project's ID to permanently remove it. See the documentation.Writev0.0.1 -
Delete Task Assignment
actionDelete a task assignment from a project (only possible if it has no time entries). Use List Task Assignments to find IDs. Example: call with projectId and taskAssignmentId to remove that task from the project. See the documentation.Writev0.0.1 -
Delete Time Entry
actionPermanently delete a time entry. Use List Time Entries to find a valid ID. Example: call with timeEntryId set to a mistakenly logged entry's ID to remove it. See the documentation.Writev0.0.1 -
Delete User
actionPermanently delete a user (only possible if they have no time entries or expenses). Use List Users to find a valid ID. Example: call with userId set to a departed team member's ID to remove their Harvest access. See the documentation.Writev0.0.1 -
Delete User Assignment
actionDelete a user assignment from a project (only possible if it has no time entries or expenses). Use List User Assignments to find IDs. Example: call with projectId and userAssignmentId to unstaff that person from the project. See the documentation.Writev0.0.1 -
Get Invoice
actionRetrieve a single invoice by ID. Use List Invoices to find a valid ID. Example: call with invoiceId set to a known invoice's ID to check its amount and payment state. See the documentation.Read-onlyv0.0.1 -
Get Me
actionRetrieve the currently authenticated user fromGET /users/me. Takes no ID input. Example: call with no parameters to answer "who am I logged in as in Harvest?". See the documentation.Read-onlyv0.0.1 -
Get Project
actionRetrieve a single project by ID. Example: call with projectId set to a known project's ID to see its client, budget, and billing method. See the documentation.Read-onlyv0.0.1 -
Get Projects
actionRetrieve one or more projects. Omit Project IDs to list every project in the account (up to 200) — use this to discover project IDs for other Harvest tools. Provide Project IDs to fetch specific projects instead. Example: call with no Project IDs to find the ID for "Jurassic Park Construction" before creating a task assignment on it. See the documentation.Read-onlyv0.1.0 -
Get Task Assignment
actionRetrieve a single task assignment on a project. Use List Task Assignments to find IDs. Example: call with projectId and taskAssignmentId to check whether that task is billable on the project. See the documentation.Read-onlyv0.0.1 -
Get Time Entry
actionRetrieve a single time entry by ID. Use List Time Entries to find a valid ID. Example: call with timeEntryId set to a known entry's ID to see its hours, project, and task. See the documentation.Read-onlyv0.0.1 -
Get Time Report
actionRetrieve total hours logged, aggregated by a chosen dimension (clients, projects, tasks, or team) over a date range, automatically following pagination up to 200 results. Use when the user asks "how many hours were logged/worked on X", "how much time did the team spend on Y", or wants a time report/summary/breakdown — this is the only tool that returns aggregated totals; List Time Entries returns individual raw entries you would have to sum yourself. The Report By value selects which/reports/time/{dimension}endpoint is called. Example: call with reportBy="projects", from="20260901", to="20260930" to see total hours logged per project for the month. See the documentationRead-onlyv0.0.1 -
Get User
actionRetrieve a single user by ID. Use List Users to find a valid ID. To fetch the authenticated user, use Get Me instead. Example: call with userId set to a known user's ID to see their name, email, and role. See the documentation.Read-onlyv0.0.1 -
Get User Assignment
actionRetrieve a single user assignment on a project. Use List User Assignments to find IDs. Example: call with projectId and userAssignmentId to check that person's hourly rate on the project. See the documentation.Read-onlyv0.0.1 -
List Account ID Options
actionList the Harvest accounts (companies) the authenticated user has access to, each with its Account ID. Use this first to find the Account ID required by every other Harvest tool. Example: call with no parameters, then pass the returned id as Account ID on any other Harvest tool call. See the documentation.Read-onlyv0.0.2 -
List Clients
actionList clients in the Harvest account, automatically following pagination up to 200 clients. Use this to discover client IDs for Create Invoice, Create Project, and other tools that accept a Client ID. Example: to find InGen Corp's client ID, call with no filters and look for the client named "InGen Corp" in the results. See the documentation.Read-onlyv0.0.1 -
List Invoices
actionList invoices with optional filters, automatically following pagination up to 200 invoices. Use this to discover invoice IDs for Get Invoice, Update Invoice, or Delete Invoice. Example: call with clientId set to InGen Corp's client ID and state="open" to find their unpaid invoices. See the documentation.Read-onlyv0.0.1 -
List Task Assignments
actionList task assignments, automatically following pagination up to 200. Omit Project ID to use the global endpoint; provide a Project ID to list assignments for a single project. Example: call with projectId set to the Jurassic Park Construction project's ID to see which tasks are billable on it. See the documentation.Read-onlyv0.0.1 -
List Tasks
actionList tasks in the Harvest account, automatically following pagination up to 200 tasks. Use this to discover task IDs for Create Task Assignment, Create Timesheet Entry, and other tools that accept a Task ID. Example: call with isActive=true to find the ID for the "Fence Maintenance" task before assigning it to a project. See the documentation.Read-onlyv0.0.1 -
List Time Entries
actionRetrieve a list of time entries from Harvest, with optional filters, automatically following pagination up to 200 entries. Use this to discover time entry IDs for Get Time Entry, Update Time Entry, or Delete Time Entry. For a pure "how many entries are there" question, setcountOnlyto skip fetching any entries and just return the true total — safe even on a huge, unfiltered result set that would otherwise exceed the response-size limit. Passnotesto filter results to entries whose notes contain that text (client-side, case-insensitive substring) — useful for finding one entry inside a large result set without hitting the output-size limit. Example: call with projectId set to a project's ID and from/to set to a date range to total up hours logged that week. See the documentation.Read-onlyv0.0.1 -
List User Assignments
actionList user assignments, automatically following pagination up to 200. Omit Project ID to use the global endpoint; provide a Project ID to list assignments for a single project. Example: call with projectId set to the Isla Sorna Genetics Lab project's ID to see who is staffed on it. See the documentation.Read-onlyv0.0.1 -
List Users
actionList users in the Harvest account, automatically following pagination up to 200 users. Use this to discover user IDs for Create User Assignment, Create Timesheet Entry, and other tools that accept a User ID. Example: call with isActive=true to find Alan Grant's user ID before logging time on his behalf. See the documentation.Read-onlyv0.0.1 -
Start Time Entry
actionRestart a stopped time entry so it resumes running. Use List Time Entries with Is Running set to false to find a stopped entry to restart. Example: call with timeEntryId set to a stopped entry's ID to resume tracking time on it. See the documentation.Writev0.0.4 -
Stop Time Entry
actionStop a currently running time entry. Use List Time Entries with Is Running set to true to find a running entry to stop. Example: call with timeEntryId set to a currently running entry's ID to stop the clock on it. See the documentation.Writev0.0.5 -
Update Invoice
actionUpdate an existing invoice. Use List Invoices to find a valid ID. Example: call with invoiceId set to a draft invoice's ID and subject set to a new value to revise it before sending. See the documentation.Writev0.0.1 -
Update Project
actionUpdate an existing project. Use Get Projects to find a project ID. Example: call with projectId set to a project's ID and projectName set to a new name to rename it. See the documentation.Writev0.0.1 -
Update Task Assignment
actionUpdate a task assignment on a project. Use List Task Assignments to find IDs. Example: call with projectId, taskAssignmentId, and billable=false to stop billing that task on the project. See the documentation.Writev0.0.1 -
Update Time Entry
actionUpdate an existing time entry. Use List Time Entries to find a valid ID. Example: call with timeEntryId set to a logged entry's ID and hours set to a corrected value to fix a mistaken time entry. See the documentation.Writev0.0.1 -
Update User
actionUpdate an existing user. Use List Users to find a valid ID. Example: call with userId set to a user's ID and lastName set to a new value to update their name. See the documentation.Writev0.0.1 -
Update User Assignment
actionUpdate a user assignment on a project. Use List User Assignments to find IDs. Example: call with projectId, userAssignmentId, and hourlyRate="150" to set that person's custom billing rate on the project. See the documentation.Writev0.0.1
EVENTS
Harvest triggers
Event sources your backend can deploy for users and receive through a webhook.
MULTI-APP
Use Harvest with other popular apps
Most products don't stop at one integration. Pair Harvest with the other apps your users rely on, and ship use cases that span both.
- App slug
- harvest
- Authentication
- OAuth
- Categories
- Productivity
- Actions
- 37
- Triggers
- 2
- API proxy
- Available