Agent skill

Onshape Webhooks Skill

Receive and verify Onshape (PTC cloud CAD) webhooks. Use when setting up an Onshape webhook endpoint, verifying the X-onshape-webhook-signature-primary header (base64 HMAC-SHA256 of timestamp.body), debugging Onshape signature verification failures, or handling events like onshape.model.lifecycle.changed, onshape.model.translation.complete, onshape.document.lifecycle.created, onshape.revision.created and onshape.comment.create.

Install this skill

npx skills add hookdeck/webhook-skills --skill 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.

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/, examples/nextjs/, examples/fastapi/.

Common Event Types

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

EventTriggered When
onshape.model.lifecycle.changedA model (part studio, assembly) changes
onshape.model.translation.completeA translation (import/export) finishes (translationId)
onshape.document.lifecycle.createdA document is created
onshape.document.lifecycle.statechangeA document moves to ACTIVE, TRASH or DELETED (documentState)
onshape.revision.createdA release revision is created
onshape.comment.createA comment is created (commentId)
onshape.user.lifecycle.updateappsettingsA user's app settings change (userId, clientId)
webhook.register / webhook.ping / webhook.unregisterSent automatically when a webhook is registered, pinged or removed; just return 200

Environment Variables

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

Local Development

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

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 10, 2026

View on GitHub →