Back

Quick Guide to Implementing Webhooks in Harvest

Aug 15, 20248 minute read

Hey there, fellow JavaScript enthusiast! Ready to supercharge your Harvest integration with some webhook magic? Let's dive right in and get those real-time updates flowing!

Introduction

Webhooks are like the cool kids of the API world – they don't wait around for you to ask for updates, they proactively ping you when something interesting happens. For Harvest, this means instant notifications about new time entries, invoices, and more. If you're building a user-facing integration, webhooks are your best friend for keeping everything in sync without constantly polling the API.

Prerequisites

Before we jump into the code, make sure you've got:

  • A Harvest account with API access (duh!)
  • Node.js installed on your machine
  • Some experience with Express.js (we'll be using it for our webhook endpoints)

Got all that? Great! Let's roll up our sleeves and get coding.

Setting Up Webhook Endpoints

First things first, we need somewhere for Harvest to send those juicy webhook payloads. Let's whip up a quick Express server:

const express = require('express'); const app = express(); const PORT = process.env.PORT || 3000; app.use(express.json()); app.post('/webhooks/harvest', (req, res) => { console.log('Received webhook:', req.body); res.sendStatus(200); }); app.listen(PORT, () => console.log(`Webhook server running on port ${PORT}`));

Simple, right? This sets up a basic endpoint at /webhooks/harvest that'll log incoming webhooks and send a 200 OK response.

Registering Webhooks with Harvest API

Now that we've got our endpoint, let's tell Harvest about it. We'll use the Harvest API to register our webhook:

const axios = require('axios'); const HARVEST_ACCOUNT_ID = 'your_account_id'; const HARVEST_ACCESS_TOKEN = 'your_access_token'; async function registerWebhook() { try { const response = await axios.post( 'https://api.harvestapp.com/v2/webhooks', { event_type: 'timeentry.created', url: 'https://your-server.com/webhooks/harvest' }, { headers: { 'Harvest-Account-ID': HARVEST_ACCOUNT_ID, 'Authorization': `Bearer ${HARVEST_ACCESS_TOKEN}`, 'Content-Type': 'application/json' } } ); console.log('Webhook registered:', response.data); } catch (error) { console.error('Error registering webhook:', error.response.data); } } registerWebhook();

Make sure to replace 'your_account_id', 'your_access_token', and 'https://your-server.com/webhooks/harvest' with your actual details.

Handling Webhook Payloads

When those webhooks start rolling in, you'll want to do something useful with them. Let's expand our endpoint to handle different event types:

app.post('/webhooks/harvest', (req, res) => { const { event_type, payload } = req.body; switch (event_type) { case 'timeentry.created': handleNewTimeEntry(payload); break; case 'invoice.paid': handlePaidInvoice(payload); break; // Add more cases as needed default: console.log(`Unhandled event type: ${event_type}`); } res.sendStatus(200); }); function handleNewTimeEntry(payload) { console.log('New time entry:', payload); // Do something cool with the new time entry } function handlePaidInvoice(payload) { console.log('Invoice paid:', payload); // Maybe update your own database or notify the user }

Error Handling and Retries

Webhooks can fail for various reasons, so it's crucial to implement proper error handling and retries. Here's a simple example:

app.post('/webhooks/harvest', async (req, res) => { try { await processWebhook(req.body); res.sendStatus(200); } catch (error) { console.error('Error processing webhook:', error); res.sendStatus(500); } }); async function processWebhook(data, retries = 3) { try { // Process the webhook data // ... } catch (error) { if (retries > 0) { console.log(`Retrying... (${retries} attempts left)`); await new Promise(resolve => setTimeout(resolve, 1000)); return processWebhook(data, retries - 1); } throw error; } }

This setup will retry processing the webhook up to 3 times before giving up.

Testing Webhooks

Harvest provides a handy webhook tester in their developer tools. But for local testing, you can simulate webhook events like this:

// test-webhook.js const axios = require('axios'); async function testWebhook() { try { const response = await axios.post('http://localhost:3000/webhooks/harvest', { event_type: 'timeentry.created', payload: { id: 123456, user_id: 789, hours: 2.5, notes: 'Test time entry' } }); console.log('Test webhook sent:', response.status); } catch (error) { console.error('Error sending test webhook:', error.message); } } testWebhook();

Run this script to send a test webhook to your local server.

Security Considerations

Remember, with great power comes great responsibility. Keep these security tips in mind:

  • Always use HTTPS for your webhook endpoints
  • Consider implementing IP whitelisting for Harvest's webhook IPs
  • Implement rate limiting to prevent potential abuse

Wrapping Up

And there you have it! You're now equipped to implement webhooks in your Harvest integration like a pro. Remember, this is just scratching the surface – there's a whole world of possibilities when it comes to real-time data syncing with Harvest.

Keep experimenting, and don't hesitate to dive into Harvest's API docs for more advanced features. Happy coding, and may your integrations be ever responsive!