Welcome to the Quote3D Documentation!

Webhooks for Quote and Job Events

Integrate Quote3D into your workflow with real-time notifications.

What are Webhooks?

Webhooks allow your application to receive real-time notifications about events happening in Quote3D. Instead of polling our API for updates (e.g., checking if a quote is finished), Quote3D will send an HTTP POST request to a URL you specify as soon as an event occurs.

This is much more efficient and allows you to trigger automated workflows in your own system, such as updating an order status, notifying a customer, or starting a production job.

Available Events

  • quote.completed: Triggered when a 3D quote calculation is successfully finished.
  • quote.failed: Triggered if a quote calculation fails for any reason.
  • file.uploaded: Triggered when a new model file is successfully uploaded to your account.
  • file.deleted: Triggered when a model file is deleted from your storage.
  • job.status_changed: Triggered on every state transition of an asynchronous job (queued, processing, completed, failed). A single quote produces several of these.
  • widget.added_to_cart: Triggered when a customer adds a model to their cart using your embeddable widget.

Endpoint Requirements

Your endpoint URL is validated when you save it, and again before every delivery attempt. URLs that fail these rules are rejected with a 400 response.

  • Must use http or https. Other schemes are rejected.
  • Must not embed credentials. https://user:[email protected] is rejected.
  • Must not resolve to a private or reserved address: localhost, loopback, link-local (including cloud instance metadata), private ranges, CGNAT, multicast and reserved space.
  • The hostname is resolved via DNS on every attempt, so a public hostname that points into private space is rejected too.
  • Redirects are not followed. Return your response directly from the URL you registered.

Payload Example

All webhook requests are sent as JSON. Here is an example of a quote.completed event payload:

{
  "event": "quote.completed",
  "timestamp": "2026-03-10T14:30:00.000Z",
  "data": {
    "jobId": "job_01HXYZ123456789",
    "quoteId": "job_01HXYZ123456789",
    "status": "completed",
    "fileName": "gearbox-housing.stl",
    "fileId": "fl_987654321",
    "processingTimeMs": 22134,
    "result": {
      "quote_id": "job_01HXYZ123456789",
      "total_price": 25.5,
      "currency": "USD",
      "estimated_time": "2h 15m",
      "estimated_time_seconds": 8100,
      "filament_weight": 42.8,
      "printInfo": {
        "technology": "FDM",
        "material": "PLA",
        "color": "Black"
      }
    },
    "error": null
  }
}

Security & Verification

To ensure that webhook requests are genuinely from Quote3D and haven't been tampered with, we include an HMAC-SHA256 signature in the X-Webhook-Signature header and the event name in X-Webhook-Event.

When you create a webhook in the dashboard, you'll be shown a unique Secret Key. You should use this key to verify the signature of every incoming request.

Verification Steps:

  1. Read the raw request body as a string.
  2. Retrieve the signature from the X-Webhook-Signature header and the event name from X-Webhook-Event.
  3. Compute the HMAC-SHA256 hash of the raw body using your Webhook Secret.
  4. Prefix your calculated digest with sha256= and compare it with the signature. If they match, the request is authentic.
const crypto = require('crypto');

// Secret Key from your Quote3D Dashboard
const SECRET = 'your_webhook_secret_here';

function verifySignature(req) {
  const signature = req.headers['x-webhook-signature'];
  const event = req.headers['x-webhook-event'];
  const body = req.rawBody; // Keep the exact raw JSON string

  const hmac = crypto.createHmac('sha256', SECRET);
  const digest = 'sha256=' + hmac.update(body).digest('hex');

  if (typeof signature !== 'string' || typeof event !== 'string') {
    return false;
  }

  const received = Buffer.from(signature, 'utf8');
  const expected = Buffer.from(digest, 'utf8');

  return received.length === expected.length &&
    crypto.timingSafeEqual(received, expected);
}

Managing Webhooks

You can manage your webhooks directly from the Dashboard:

  1. Go to the Webhooks page in your dashboard.
  2. Click New Webhook to add a new endpoint.
  3. Enter your Endpoint URL and select the Events you want to listen for. HTTPS is strongly recommended; see Endpoint Requirements above.
  4. Copy your Secret Key immediately and store it securely. You won't be able to see it again!
  5. Optionally link an API Token. Leave it empty and the webhook fires for every request on your account; link a token and it fires only for requests made with that token.

Retrying Failed Deliveries

Automatic Retries

A failed delivery is retried automatically before you ever need to intervene. Deliveries are attempted up to 3 times; after that the delivery is closed as failed and stays visible in the dashboard.

  • The first attempt happens as soon as the event fires. Remaining attempts are picked up by a background sweep, so the practical gap between attempts is set by that sweep interval rather than by a fixed number of seconds.
  • If our process is interrupted mid-attempt, the delivery is reclaimed and retried rather than lost.
  • Each delivery is claimed atomically before it is sent, so a retry never results in the same delivery being sent twice concurrently.
  • A retry sends exactly the same body and the same X-Webhook-Signature as the original attempt, so your existing verification logic works unchanged.

If a webhook destination was temporarily unavailable or returned an error, you can inspect the delivery in the dashboard and trigger a resend for that specific delivery record.

POST /v2/webhooks/{webhook_id}/deliveries/{delivery_id}/resend

This endpoint creates a fresh delivery attempt for an existing webhook delivery. It is useful after you have fixed the receiving server or want to replay a specific event for debugging.

  • Use the webhook detail view to identify the delivery ID you want to replay.
  • Call the resend endpoint with both the webhook ID and delivery ID. The API responds with 202 Accepted and creates a new pending delivery record.
  • The webhook must still be active and belong to the authenticated user, otherwise the resend request is rejected.

Best Practices

  • Return a 200 OK status immediately to acknowledge receipt of the webhook.
  • Perform heavy processing asynchronously (e.g., using a background task queue) to avoid timing out.
  • Always verify the signature to protect your endpoint from unauthorized requests.
  • Idempotency: Ensure your system can handle the same webhook multiple times gracefully, as we may retry deliveries in case of transient errors.