# Onshape Webhooks

## When to Use This Skill

* How do I receive Onshape webhooks?
* How do I verify the Onshape `X-onshape-webhook-signature-primary` signature?
* Why is my Onshape webhook signature verification failing?
* How do I handle `onshape.model.lifecycle.changed`, `onshape.model.translation.complete` or `onshape.comment.create` events?
* How do I register an Onshape webhook (`POST /api/v6/webhooks`) and set a signature key?

## Verification (core)

> Onshape signature keys are an Enterprise company setting. Paste the primary key; only the primary signature is verified.

Apps register webhooks with the REST API (`POST /api/v6/webhooks`). Company administrators then set signature keys in Company/Classroom/Enterprise settings > Webhooks. Each delivery is an HTTP POST with a JSON body and these headers:

* `X-onshape-webhook-signature-primary` — present when a primary key is set: **standard padded base64 HMAC-SHA256**, no prefix
* `X-onshape-webhook-timestamp` — when the webhook was sent; it is part of the signed content
* `X-onshape-webhook-signature-secondary` — present when a secondary key is set (ignore it: this skill verifies the primary signature only)

Signed content is the timestamp header value exactly as received, a literal `.`, then the **raw request body bytes**. The key is the primary signature key used as a UTF-8 string.

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

function verifyOnshapeSignature(rawBody, timestamp, signature, secret) {
  if (!timestamp || !signature || !secret) return false;
  if (!/^[A-Za-z0-9+/]{43}=$/.test(signature)) return false; // validate before comparing
  const message = Buffer.concat([Buffer.from(timestamp, 'latin1'), Buffer.from('.'), rawBody]);
  const expected = crypto.createHmac('sha256', secret).update(message).digest('base64');
  const a = Buffer.from(signature);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

```

Missing or empty headers, a malformed value or a mismatch: respond `401`. In Express use `express.raw({ type: '*/*' })`; in Next.js `await request.arrayBuffer()`; in FastAPI `await request.body()`. Never parse and re-serialize the body before verifying.

**Timestamp window:** off by default. Hookdeck retries and replays resend the original headers and timestamp, so a fixed window would reject legitimate retries. To opt in, set `ONSHAPE_WEBHOOK_TOLERANCE_SECONDS` (unset or `0` = off); the examples then reject deliveries whose `X-onshape-webhook-timestamp` is older than that.

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/onshape-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/onshape-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/onshape-webhooks/examples/fastapi/).

## Common Event Types

Dispatch on the body's `event` field. Onshape documents more event types in its API Explorer (`createWebhook`).

| Event | Triggered When |
| --- | --- |
| `onshape.model.lifecycle.changed` | A model (part studio, assembly) changes |
| `onshape.model.translation.complete` | A translation (import/export) finishes (`translationId`) |
| `onshape.document.lifecycle.created` | A document is created |
| `onshape.document.lifecycle.statechange` | A document moves to `ACTIVE`, `TRASH` or `DELETED` (`documentState`) |
| `onshape.revision.created` | A release revision is created |
| `onshape.comment.create` | A comment is created (`commentId`) |
| `onshape.user.lifecycle.updateappsettings` | A user's app settings change (`userId`, `clientId`) |
| `webhook.register` / `webhook.ping` / `webhook.unregister` | Sent automatically when a webhook is registered, pinged or removed; just return 200 |

## Environment Variables

```bash
ONSHAPE_WEBHOOK_SECRET=your_primary_signature_key
# Optional, off when unset or 0
ONSHAPE_WEBHOOK_TOLERANCE_SECONDS=0

```

## Local Development

```bash
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 onshape --path /webhooks/onshape

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/onshape-webhooks/references/overview.md) - Onshape webhooks, events, payload fields, delivery
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/onshape-webhooks/references/setup.md) - Register a webhook and configure signature keys
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/onshape-webhooks/references/verification.md) - Signature details, gotchas, debugging