webhookWebhooks

Stay informed about important events in your Duro library with real-time webhook notifications.

circle-info

Webhooks follow a "Ping then Pull" pattern - you receive lightweight event notifications with key metadata, then fetch full resource details using our GraphQL API when needed.

Overview

Duro webhooks let your applications receive real-time notifications when important events occur in your library. Instead of constantly polling for changes, webhooks push event notifications directly to your specified endpoints, helping you build responsive integrations that react immediately to data changes.

Key Benefits

  • Real-time updates - Get notified instantly when data changes

  • Efficient integration - No need for constant API polling

  • Selective subscriptions - Subscribe only to the events you care about

  • Reliable delivery - Built-in retry mechanisms with exponential backoff

  • Secure - HMAC-SHA256 signature verification to ensure authenticity

How Webhooks Work

  1. Event occurs - Something happens in Duro (e.g., a component is updated)

  2. Notification sent - Duro sends a lightweight JSON payload to your webhook URL

  3. Fetch full data - Your application uses the provided metadata to fetch complete details via GraphQL as needed

This pattern keeps webhook payloads small and fast while giving you access to all the data you need.


Available Events

Webhooks are scoped to a specific library. Each webhook can subscribe to one or more event types.

Component Events

These events fire when components in your library are created, updated, or deleted:

GraphQL Enum
Payload Value
Description

COMPONENT_CREATED

components.created

A new component was created in the library

COMPONENT_UPDATED

components.updated

An existing component was modified

COMPONENT_DELETED

components.deleted

A component was removed from the library

circle-info

When subscribing to events via GraphQL, use the enum name (e.g., COMPONENT_CREATED). When processing webhook payloads, the event field contains the string value (e.g., components.created).

Change Order Events

Track the full lifecycle of change orders in your library with these events:

GraphQL Enum
Payload Value
Description

CHANGE_ORDER_OPENED

change_orders.opened

A change order transitioned from draft to open status

CHANGE_ORDER_UPDATED

change_orders.updated

Change order details were modified (name, description, etc.)

CHANGE_ORDER_DELETED

change_orders.deleted

A change order was archived

CHANGE_ORDER_STAGE_TRANSITION

change_orders.stage_transition

Change order moved to a different workflow stage

CHANGE_ORDER_STAGE_REVIEWER_DECISION

change_orders.stage_reviewer_decision

A reviewer approved or rejected their stage

CHANGE_ORDER_RESOLUTION

change_orders.resolution

Change order was fully approved, rejected, or withdrawn

circle-info

Semantic Event Priority: When a change triggers multiple events (e.g., updating status from draft to open), Duro sends the more specific semantic event (CHANGE_ORDER_OPENED) rather than the generic CHANGE_ORDER_UPDATED. This prevents duplicate notifications and makes event handling more predictable.


Setting Up Webhooks

Create a Webhook

To create a webhook, you'll need:

  • The x-library header set to specify which library to monitor

  • A url - your HTTPS endpoint that will receive notifications

  • A list of events - which event types to subscribe to

circle-info

Required Headers: All webhook operations require the x-api-key, x-organization, and x-library headers. The webhook will be created for the library specified in the x-library header.

Configuration Options

Field
Required
Description
Default

name

Yes

Unique name for this webhook (1-100 characters)

-

url

Yes

HTTPS endpoint URL (must be valid HTTPS)

-

events

Yes

Array of event types to subscribe to

-

description

No

Optional description (max 500 characters)

null

signingSecret

No

Secret for HMAC signature verification (min 16 characters if provided)

null

timeoutSeconds

No

Request timeout in seconds (5-300)

30

maxRetries

No

Maximum retry attempts on failure (0-10)

3

isEnabled

No

Whether the webhook is active

true

isArchived

Read-only

Whether the webhook has been archived (set via archive mutation)

false

List Your Webhooks

Retrieve all webhooks for the library specified in your x-library header:

circle-info

The findAll query returns webhooks for the library specified in the x-library header. Only active (non-archived) webhooks are returned.

Get a Specific Webhook

circle-info

The findOne query returns a webhook_not_found error if the webhook has been archived.

Update a Webhook

You can update any webhook configuration field:

Add Events to a Webhook

Add additional event subscriptions without removing existing ones:

Remove Events from a Webhook

Remove specific event subscriptions:

Archive a Webhook

When you no longer need a webhook but want to preserve its configuration history, you can archive it instead of deleting it. Archived webhooks:

  • Stop receiving events - No new event deliveries will be attempted

  • Are hidden from queries - Won't appear in findAll or findOne results

  • Free up the name - You can create a new webhook with the same name

  • Cannot be unarchived - This action is permanent

circle-exclamation

When to Archive vs. Disable

Action
Use Case

Disable (isEnabled: false)

Temporarily pause deliveries; webhook remains queryable and can be re-enabled

Archive

Permanently retire a webhook; frees up the name for reuse


Webhook Payloads

All webhook notifications follow a consistent JSON structure:

Payload Fields Explained

Field
Type
Description

event

string

The event type (e.g., components.created)

eventId

string

Unique identifier for this webhook delivery (UUID)

sourceId

string

Internal event ID for tracking and debugging

timestamp

string

ISO 8601 timestamp when the event occurred

metadata

object

Event-specific data (see below)

Component Event Metadata

For component events, the metadata object contains:

Field
Type
Description

componentId

string

UUID of the affected component

revisionValue

string

Current revision value (e.g., "1.A", "2.B")

version

number

Current version number

Example: Component Created

Example: Component Updated

Change Order Event Metadata

Each change order event type includes different metadata fields based on the event context.

change_orders.opened

Fired when a change order transitions from draft to open status (submitted for review).

Field
Type
Description

changeOrderId

string

UUID of the change order

status

string

New status value (open)

change_orders.updated

Fired when change order details are modified (name, description, etc.) without triggering a semantic event like opened or resolution.

Field
Type
Description

changeOrderId

string

UUID of the change order

change_orders.deleted

Fired when a change order is archived.

Field
Type
Description

changeOrderId

string

UUID of the change order

change_orders.stage_transition

Fired when a change order moves between workflow stages.

Field
Type
Description

changeOrderId

string

UUID of the change order

previousStageId

string

UUID of the previous stage (omitted if starting workflow)

newStageId

string

UUID of the new stage (omitted if resolved/completed)

transitionReason

string

Reason for the transition (e.g., stage_approved, stage_rejected)

change_orders.stage_reviewer_decision

Fired when a reviewer makes a decision (approve/reject) on their assigned stage.

Field
Type
Description

changeOrderId

string

UUID of the change order

stageId

string

UUID of the stage being reviewed

reviewerId

string

UUID of the reviewer who made the decision

decision

string

The decision made (approved or rejected)

change_orders.resolution

Fired when a change order reaches a final resolution (approved, rejected, or withdrawn).

Field
Type
Description

changeOrderId

string

UUID of the change order

resolution

string

Final resolution (approved, rejected, or withdrawn)

HTTP Headers

Each webhook request includes these headers:

Header
Value

Content-Type

application/json

User-Agent

Duro-Webhook-Service/1.0

X-Webhook-Signature

HMAC-SHA256 signature (only if signingSecret is configured)


Security

Webhook Signatures

If you configure a signingSecret for your webhook, each request will include an X-Webhook-Signature header containing an HMAC-SHA256 signature of the payload. Always verify this signature to ensure the request came from Duro.

Verifying Signatures (Node.js)

Verifying Signatures (Python)

Security Best Practices

  • Always use HTTPS - Webhook URLs must use HTTPS (enforced by Duro)

  • Verify signatures - Always validate the X-Webhook-Signature header

  • Use timing-safe comparison - Prevent timing attacks when comparing signatures

  • Keep secrets secure - Store your signingSecret in environment variables, never in code

  • Rotate secrets periodically - Update your signing secret regularly


Fetching Full Resource Data

After receiving a webhook notification, use the provided IDs to fetch complete resource details via GraphQL.

Fetch Component Details

Fetch Change Order Details

Example: Complete Webhook Handler

Here's a complete example showing how to receive a webhook and fetch the full component data:


Retry Logic & Error Handling

Duro automatically retries failed webhook deliveries using exponential backoff.

Retry Schedule

When a webhook delivery fails, Duro will retry with the following delays:

Attempt
Delay After Failure

1st retry

1 second

2nd retry

2 seconds

3rd retry

4 seconds

4th retry

8 seconds

5th retry

16 seconds

6th retry

32 seconds

7th retry

64 seconds

8th retry

128 seconds

9th retry

256 seconds

10th retry

300 seconds (5 min max)

The retry schedule follows exponential backoff (2x multiplier) with a maximum delay of 5 minutes.

What Counts as Success?

  • Success: HTTP status codes 200-299

  • Failure: All other status codes, timeouts, or connection errors

Your Endpoint Should

  1. Respond quickly - Return a 200 status code immediately, then process asynchronously

  2. Handle duplicates - Use eventId for idempotency; you may receive the same event more than once

  3. Log failures - Track and investigate webhook processing failures

  4. Be available - Ensure your endpoint is highly available to receive webhooks

Example: Idempotent Processing


Monitoring Webhook Activity

Query your webhook logs to monitor delivery status and troubleshoot issues:

Log Status Values

Status
Description

PENDING

Delivery in progress

DELIVERED

Successfully delivered (2xx response)

RETRYING

Failed, waiting for retry

FAILED

All retry attempts exhausted

Example: Monitoring Script


Common Use Cases

ERP Integration

Sync component and BOM changes to your ERP system in real-time:

Slack Notifications

Alert your team about important component changes:

Audit Logging

Maintain a detailed audit trail of all changes:

Change Order Workflow Tracking

Monitor change order progress and sync approval status to external systems:


Troubleshooting

Common Issues

Webhook not receiving events

  1. Check if webhook is enabled - Query the webhook and verify isEnabled: true

  2. Verify event subscriptions - Ensure the correct events are in the events array

  3. Check your endpoint - Verify your URL is accessible from the internet

  4. Review logs - Use the getLogs query to see delivery attempts and errors

Signature verification failing

  1. Check the secret - Ensure you're using the exact same signingSecret you configured

  2. Use raw body - Signature is computed on the raw JSON string, not a parsed object

  3. Check encoding - Ensure UTF-8 encoding throughout

Missing webhook deliveries

  1. Check retry status - Some deliveries may be queued for retry

  2. Verify library scope - Webhooks only fire for events in their configured library

  3. Check component filters - Events fire for all components in the library

Testing Your Webhook Endpoint

Before configuring a production webhook, test your endpoint:


Migrating from v1 to v2

v1 webhook payloads included complete resource data. v2 uses a "Ping then Pull" pattern—you receive minimal metadata and fetch full details via GraphQL when needed.

Event Mapping

v1 Event
v2 Event
Notes

co.Submitted

CHANGE_ORDER_OPENED

co.Approved

CHANGE_ORDER_RESOLUTION

Check metadata.resolution === 'approved'

co.Rejected

CHANGE_ORDER_RESOLUTION

Check metadata.resolution === 'rejected'

Example: Change Order Approved

v1 payload (complete data embedded):

v2 payload (metadata only):

What's Not Included in v2

The following v1 fields are not in the v2 webhook payload. Use the GraphQL API to retrieve this data:

v1 Field
How to Get in v2

con, name, description

Query changeOrders.get()

status, type, resolution

Query changeOrders.get()

creator, firstName, lastName, email

Query changeOrders.get() with createdBy field

approverList

Query changeOrders.get() with reviewers field

children.components, children.products

Query changeOrders.get() with items field

history

Query change order audit log

created, lastModified

Query changeOrders.get() with createdAt, updatedAt

Updating Your Handler

v1:

v2:

circle-info

See Fetch Change Order Details for the GraphQL query and Security for signature verification.


Next Steps

Last updated

Was this helpful?