Webhooks

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

Webhooks follow a "Ping then Pull" pattern - you'll receive lightweight event notifications with minimal data, then fetch the full resource details using our GraphQL API.

Overview

Duro webhooks enable your applications to receive real-time notifications when important events occur in your library. Instead of continuously polling for changes, webhooks push event notifications to your specified endpoints, allowing you to 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 - Choose only the events you care about

  • Reliable delivery - Built-in retry mechanisms ensure event delivery

How Webhooks Work

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

  2. Notification sent - Duro sends a lightweight 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

Phase 1: Item Events

  • ITEM_CREATE - New component, product, or assembly created

  • ITEM_UPDATE - Existing item modified

  • ITEM_DELETE - Item removed from library

Phase 2: Change Order Events

  • CO_OPEN - New change order created

  • CO_UPDATE - Change order details modified

  • CO_DELETE - Change order removed

  • CO_STAGE_TRANSITION - Change order moves between workflow stages

  • CO_STAGE_REVIEWER_DECISION - Individual reviewer approves/rejects

  • CO_RESOLUTION - Change order fully approved, rejected, or withdrawn

Phase 3: Configuration Events (Coming Soon)

  • Category updates and other configuration changes

Setting Up Webhooks

Create a Webhook

mutation CreateWebhook {
  webhooks {
    create(input: {
      name: "Production System Sync"
      description: "Sync component updates to our ERP system"
      url: "https://api.mycompany.com/duro-webhook"
      events: [ITEM_CREATE, ITEM_UPDATE, CO_RESOLUTION]
      isEnabled: true
    }) {
      id
      name
      url
      events
    }
  }
}

List Your Webhooks

query GetMyWebhooks {
  webhooks {
    get {
      id
      name
      description
      url
      events
      isEnabled
      createdAt
    }
  }
}

Update a Webhook

mutation UpdateWebhook {
  webhooks {
    update(
      filter: { ids: ["webhook_123"] }
      input: {
        events: [ITEM_CREATE, ITEM_UPDATE, ITEM_DELETE]
        isEnabled: false
      }
    ) {
      id
      events
      isEnabled
    }
  }
}

Webhook Payloads

All webhook notifications follow a consistent structure:

{
  "id": "evt_abc123",
  "webhook_id": "webhook_xyz789",
  "event": "ITEM_UPDATE",
  "created_at": "2024-12-20T10:30:00Z",
  "data": {
    "item_id": "comp_123456"
  }
}

Event-Specific Data

Item Events

{
  "event": "ITEM_CREATE",
  "data": {
    "item_id": "comp_789012"
  }
}

Change Order Stage Transition

{
  "event": "CO_STAGE_TRANSITION",
  "data": {
    "change_order_id": "co_345678",
    "stage_id": "stage_901234",
    "decision": "approved"
  }
}

Change Order Resolution

{
  "event": "CO_RESOLUTION",
  "data": {
    "change_order_id": "co_345678",
    "resolution": "approved"
  }
}

Fetching Full Resource Data

After receiving a webhook notification, use the provided IDs to fetch complete resource details:

Fetch Item Details

query GetItemDetails($itemId: ID!) {
  components {
    get(filter: { ids: [$itemId] }) {
      connection {
        edges {
          node {
            id
            cpn
            name
            description
            revision
            status
            category {
              name
              code
            }
          }
        }
      }
    }
  }
}

Fetch Change Order Details

query GetChangeOrderDetails($coId: ID!) {
  changeOrders {
    get(filter: { ids: [$coId] }) {
      connection {
        edges {
          node {
            id
            name
            description
            status
            resolution
            stages {
              id
              name
              decisionState
              reviewers {
                id
                decisionState
                user {
                  name
                  primaryEmail
                }
              }
            }
          }
        }
      }
    }
  }
}

Security

Webhook Signatures

Each webhook request includes a signature header for verification:

X-Duro-Signature: sha256=abc123...

Verify signatures using your webhook's signing secret:

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  return `sha256=${expected}` === signature;
}

Best Practices

  • Use HTTPS - Always use secure HTTPS endpoints

  • Verify signatures - Validate every webhook request

  • Respond quickly - Return 200 OK immediately, process asynchronously

  • Handle retries - Duro retries failed deliveries with exponential backoff

  • Monitor health - Track webhook success rates and response times

Monitoring Webhook Activity

Query your webhook logs to monitor delivery status:

query GetWebhookLogs {
  webhooks {
    getLogs(
      webhookID: "webhook_123"
      orderBy: [{ createdAt: DESC }]
    ) {
      connection(first: 20) {
        edges {
          node {
            id
            event
            metadata
            responseCode
            isAcknowledged
            createdAt
          }
        }
        pageInfo {
          hasNextPage
          endCursor
        }
      }
    }
  }
}

Common Use Cases

ERP Integration

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

app.post('/webhook/duro', async (req, res) => {
  // Verify webhook signature
  if (!verifySignature(req)) {
    return res.status(401).send('Unauthorized');
  }

  // Acknowledge receipt immediately
  res.status(200).send('OK');

  // Process asynchronously
  const { event, data } = req.body;

  if (event === 'ITEM_UPDATE') {
    const item = await fetchItemFromDuro(data.item_id);
    await syncToERP(item);
  }
});

Slack Notifications

Alert your team about important changes:

if (event === 'CO_RESOLUTION') {
  const co = await fetchChangeOrder(data.change_order_id);

  await postToSlack({
    text: `Change Order "${co.name}" was ${data.resolution}!`,
    color: data.resolution === 'approved' ? 'good' : 'warning'
  });
}

Automated Workflows

Trigger downstream processes based on lifecycle events:

if (event === 'CO_STAGE_TRANSITION' && data.decision === 'approved') {
  // Automatically create manufacturing work orders
  // when engineering approval is complete
  await createWorkOrder(data.change_order_id);
}

Error Handling

Duro webhooks include automatic retry logic:

  • Retry schedule: 5s, 30s, 2m, 10m, 30m, 1h, 2h, 4h

  • Success codes: 200-299

  • Failure codes: All others trigger retries

Your endpoint should:

  1. Return 200 immediately to acknowledge receipt

  2. Process the webhook asynchronously

  3. Log failures for debugging

  4. Implement idempotency to handle duplicate deliveries

Next Steps

Last updated