# Guide to Rocketlane Webhooks: Features and Best Practices

Rocketlane webhooks send customer onboarding and project delivery activity to your own systems. When a task is created, updated or deleted in a Rocketlane project, an automation can send the change to your service, so you can update a CRM, alert an account team or keep a reporting database current.

This guide covers how Rocketlane webhooks and automations fit together, how to authenticate requests with Basic auth, what the payload contains, and the practices that make a Rocketlane receiver dependable.

## What are Rocketlane webhooks?

In Rocketlane, a **webhook** is a saved destination: a name, an HTTPS URL and optional credentials. On its own it sends nothing. You fire it from an **automation**: pick a trigger event, optionally add conditions, then add a **Send webhook** action that names the webhook. Rocketlane notes that "currently, we only support task triggers for webhooks".

Each delivery is an HTTP `POST` with a JSON envelope describing the event, the task, what changed and who changed it.

Rocketlane doesn't sign webhook requests. The only authentication it offers is HTTP Basic auth: "Only Basic Authentication is supported currently." It's optional, but Rocketlane recommends it, and it's the only way to tell a real request from a forged one.

## Rocketlane webhook features

| Feature | Details |
| --- | --- |
| Configuration | **Profile Icon** > **Settings** > **Webhooks** > **Add Webhook** |
| Trigger | An automation's **Send webhook** action, on task triggers |
| URL | HTTPS only |
| Method and format | `POST`, JSON |
| Authentication | Optional HTTP Basic auth (username and password) |
| Signature and timestamp | None |
| Success | `200` or `204` |
| Timeout | 10 seconds |
| Ordering | Not guaranteed |
| Duplicates | Rocketlane says it guarantees exactly one delivery per event |
| Retries, source IPs | Not documented |

## Events and payloads

The automation's trigger decides when the webhook fires, and the payload's `eventType` says what happened. Rocketlane's payload docs list these values:

| `eventType` | Resource |
| --- | --- |
| `TASK_CREATED`, `TASK_UPDATED`, `TASK_DELETED` | Task |
| `PROJECT_CREATED`, `PROJECT_UPDATED`, `PROJECT_ARCHIVED`, `PROJECT_DELETED` | Project |
| `PHASE_CREATED`, `PHASE_UPDATED`, `PHASE_DELETED` | Phase |

The same page lists `TASK` as the only supported `resourceType`, and the setup docs say only task triggers are supported, so expect task events in practice and handle anything else as unknown. The payload docs' structure example prints `"TASK_UPDATED."` with a stray full stop. The sample payloads don't have it, so match the plain value.

This is a trimmed version of Rocketlane's `TASK_UPDATED` sample:

```json
{
  "eventId": "a3c7825b-9c89-42ac-8859-50e243d0c131",
  "resourceType": "TASK",
  "eventType": "TASK_UPDATED",
  "version": "1.0",
  "data": {
    "task": {
      "taskId": 318,
      "taskName": "Hire Chief Operating Officer",
      "project": { "projectId": 110, "projectName": "Hamlin, Hamlin & McGill" },
      "phase": { "phaseId": 115, "phaseName": "Hire for different roles" },
      "status": { "value": 3, "label": "Completed" }
    }
  },
  "changeLog": {
    "changedFields": ["taskName"],
    "from": { "taskName": "Hire operations head" },
    "to": { "taskName": "Hire Chief Operating Officer" }
  },
  "source": {
    "webhook": { "webhookId": 1, "webhookName": "Receive Rocketlane Notification" },
    "actor": {
      "emailId": "bobo@rocketlane.com",
      "userId": 1,
      "firstName": "Bob",
      "lastName": "Odenkrik"
    }
  },
  "eventOccurredAt": 1704199907693,
  "eventTriggeredAt": 1704199908692
}

```

The fields to know:

* `eventId` identifies the event. Rocketlane asks you to quote it to support.
* `data.task` follows the shape of Rocketlane's Task API response.
* `changeLog` appears only on `_UPDATED` events. `changedFields` names what changed, and `from` and `to` hold the old and new values.
* `source.actor` is the user who made the change, and `source.webhook` names the webhook that sent it.
* `eventOccurredAt` is when the change happened and `eventTriggeredAt` is when Rocketlane sent it, both in epoch milliseconds.

> **Inspect Rocketlane webhooks as they arrive.** Point a webhook at a [Hookdeck Inspect](https://inspect.hookdeck.com) URL and test the automation to see the exact headers and body, with no account or setup required.

## Setting up Rocketlane webhooks

Choose a username and a long random password first, for example with `openssl rand -hex 32`.

1. In Rocketlane, click your **Profile Icon** > **Settings**, then **Webhooks**, then **Add Webhook**.
2. Enter a **Webhook Name** and your HTTPS **Webhook URL**.
3. Choose Basic authentication and enter the username and password.
4. Save the webhook.
5. Go to **Automations** and create an automation with a task trigger.
6. Choose the trigger event, and add conditions if you need them.
7. Click **Add an action**, choose **Send webhook**, and select your webhook. Rocketlane shows a sample payload.
8. Save, then test the automation.

To route through Hookdeck, create a source with the **Rocketlane** type and use its URL as the webhook URL. Enter the same values in the source's **Username** and **Password** fields. The source accepts `POST`. Hookdeck never answers with a `401` challenge, so the credentials must arrive with each request. Rocketlane's docs don't say whether it sends them up front, so send a test from the automation and confirm it passes before you rely on it.

## Securing Rocketlane webhooks

Decode the `Authorization: Basic` header, split on the first colon (a password can contain colons), and compare the username and password with your stored values in constant time.

```javascript
const crypto = require('crypto');
const express = require('express');

const USERNAME = process.env.ROCKETLANE_WEBHOOK_USERNAME;
const PASSWORD = process.env.ROCKETLANE_WEBHOOK_PASSWORD;

function safeEqual(a, b) {
  // timingSafeEqual throws on unequal lengths, so check first.
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

function verifyBasicAuth(header) {
  if (!USERNAME || !PASSWORD || typeof header !== 'string') return false;
  const match = /^basic\s+([A-Za-z0-9+/]+={0,2})\s*$/i.exec(header);
  if (!match) return false;
  const decoded = Buffer.from(match[1], 'base64');
  const colon = decoded.indexOf(0x3a); // split on the first colon
  if (colon === -1) return false;
  const userOk = safeEqual(decoded.subarray(0, colon), Buffer.from(USERNAME, 'utf8'));
  const passOk = safeEqual(decoded.subarray(colon + 1), Buffer.from(PASSWORD, 'utf8'));
  return userOk && passOk;
}

const app = express();

app.post('/webhooks/rocketlane', express.raw({ type: '*/*' }), (req, res) => {
  if (!verifyBasicAuth(req.get('authorization'))) {
    return res.sendStatus(401);
  }

  let event;
  try {
    event = JSON.parse(req.body.toString('utf8'));
  } catch {
    return res.sendStatus(400);
  }

  // Queue the event keyed on event.eventId, then respond within 10 seconds.
  res.sendStatus(200);
});

```

The same check in Python:

```python
import base64
import binascii
import hmac
import os

USERNAME = os.environ.get("ROCKETLANE_WEBHOOK_USERNAME", "")
PASSWORD = os.environ.get("ROCKETLANE_WEBHOOK_PASSWORD", "")

def verify_basic_auth(header) -> bool:
    if not USERNAME or not PASSWORD or not header:
        return False
    scheme, _, encoded = header.partition(" ")
    if scheme.lower() != "basic":
        return False
    try:
        decoded = base64.b64decode(encoded.strip(), validate=True)
    except (binascii.Error, ValueError):
        return False
    user, sep, password = decoded.partition(b":")
    if not sep:
        return False
    user_ok = hmac.compare_digest(user, USERNAME.encode("utf-8"))
    pass_ok = hmac.compare_digest(password, PASSWORD.encode("utf-8"))
    return user_ok and pass_ok

```

Basic credentials are static. They authenticate the sender, not the body, so a modified payload still passes, and with no signed timestamp a captured request can be replayed. Serve the endpoint over HTTPS only, use a long random password, and change it on the webhook and in your environment together if it might have leaked.

> **Make Rocketlane webhooks production-ready.** [Hookdeck Event Gateway](/event-gateway) checks the Basic credentials on every Rocketlane event, stores each one durably, and lets you replay any delivery after a fix.

## Rocketlane webhook limitations and pain points

### Authentication is optional

**The Problem:** Requests arrive at your endpoint with nothing to authenticate them.

**Why It Happens:** Basic auth is an optional step when you add a webhook, and Rocketlane offers no signature.

**Workarounds:**

* Set Basic auth on every webhook, and fail closed when the header is missing.
* Review existing webhooks under **Settings** > **Webhooks**.

**How Hookdeck Can Help:** Hookdeck's Rocketlane source requires the username and password. Hookdeck answers Rocketlane with a `200` either way, but a request with missing or wrong credentials is recorded as failed verification and never reaches your destination.

### A 10-second timeout

**The Problem:** Rocketlane records a delivery as failed even though your service processed it.

**Why It Happens:** Rocketlane fails a delivery that gets no response within 10 seconds, or that gets any status other than `200` or `204`.

**Workarounds:**

* Check the credentials, store the event, and respond before doing any work.
* Don't return `201` or `202`: Rocketlane counts them as failures.

**How Hookdeck Can Help:** Hookdeck responds to Rocketlane as soon as a request arrives, then delivers it to your service with a [delivery timeout](/docs/destinations#set-a-delivery-timeout) and [retries](/docs/retries) you configure.

### No documented retries

**The Problem:** You can't tell whether an event that failed during your outage will be sent again.

**Why It Happens:** Rocketlane documents what counts as failure, and says it delivers each event exactly once, but not whether a failed delivery is retried.

**Workarounds:**

* Treat a failed delivery as lost, and re-sync affected projects from the Rocketlane API after an outage.

**How Hookdeck Can Help:** Hookdeck stores every event it receives and holds it while your service is down. Failed deliveries are retried automatically, and you can replay any of them once your service is back.

### Out-of-order events

**The Problem:** An older update overwrites a newer one.

**Why It Happens:** Rocketlane says delivery order "is not guaranteed to be strictly chronological".

**Workarounds:**

* Compare `eventOccurredAt` with the time of the last change you applied to the task, and skip older events.
* For important fields, re-read the task from the API before writing.

**How Hookdeck Can Help:** Hookdeck can [set a max delivery rate](/docs/destinations#set-a-max-delivery-rate) on your destination, which reduces how many events your service handles at once.

## Best practices

### Deduplicate on eventId

Rocketlane promises one delivery per event, but a replay from Hookdeck or a retry on your side can still repeat one. Store `eventId` before acting. See our [guide to webhook idempotency](/webhooks/guides/implement-webhook-idempotency).

### Route on eventType, not the automation

Several automations can send to the same webhook. Route on `eventType` and use `changeLog.changedFields` to skip updates you don't care about.

### Keep credentials in configuration

Store the username and password as secrets, and update the webhook and your receiver together when you change them.

### Acknowledge fast and process asynchronously

With a 10-second limit, verify, store, return `200`, and do the work in a worker. See [why to process webhooks asynchronously](/webhooks/guides/why-implement-asynchronous-processing-webhooks).

## Conclusion

Rocketlane webhooks are fired by automations on task triggers and send a JSON envelope with the task, a change log for updates, and the user who acted, authenticated only by optional Basic auth. Turn Basic auth on for every webhook, compare the username and password in constant time, respond with `200` within 10 seconds, and deduplicate and order events using `eventId` and `eventOccurredAt`.

[Hookdeck Event Gateway](https://hookdeck.com) sits in front of your receiver, checks the credentials, and keeps every event until your service has processed it.

[Get started with Hookdeck](https://dashboard.hookdeck.com/signup) for free and handle Rocketlane webhooks reliably in minutes.