Skip to main content

Overview

Webhooks are HTTP callbacks that notify your application about important events on the didx:me platform — eliminating the need to continuously poll the API for status updates.

Supported Events

EventDescription
openid4vc.issuance.offeredCredential offer sent to user
openid4vc.issuance.completedUser accepted and received the credential
openid4vc.issuance.failedIssuance attempt failed
openid4vc.issuance.expiredCredential offer expired before user accepted

Registering a Webhook Endpoint

curl -X POST "https://test.didxtech.com/me-creds/api/endpoints" \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-domain.com/webhooks/credentials"
  }'

Response (201 Created)

{
  "data": {
    "id": "du398r0fqd",
    "url": "https://your-domain.com/webhooks/credentials",
    "createdAt": "2025-01-01T00:00:00.000Z",
    "updatedAt": "2025-01-01T00:00:00.000Z"
  }
}

Webhook Payload Structure

All webhook payloads include an eventType and a payload with a resource reference:
{
  "eventType": "openid4vc.issuance.completed",
  "payload": {
    "openId4VcIssuanceId": "cmfwfjxdq003js601bmgy9tva"
  }
}

Technical Requirements

Your webhook endpoint must:
  • Accept POST requests
  • Use HTTPS exclusively
  • Respond promptly with a 2xx status code
  • Handle duplicate deliveries (the platform may retry on failure)
  • Be idempotent — processing the same event twice should produce the same result

Implementation Example

Here’s a Node.js/Express implementation with deduplication handling and asynchronous processing:
const processedWebhooks = new Set();
const eventQueue = [];

app.post('/webhooks/credentials', async (req, res) => {
  try {
    const webhookId = req.headers['webhook-id'];
    const { eventType, payload } = req.body;

    // Deduplicate — the platform may send the same webhook more than once
    if (processedWebhooks.has(webhookId)) {
      console.log(`Webhook ${webhookId} already processed, skipping`);
      return res.status(200).send('OK');
    }

    const webhookEvent = {
      webhookId,
      eventType,
      payload,
      receivedAt: new Date(),
      processed: false
    };

    eventQueue.push(webhookEvent);
    processedWebhooks.add(webhookId);

    // Respond immediately, then process asynchronously
    res.status(200).send('OK');
    setImmediate(() => processWebhookAsync(webhookEvent));

  } catch (error) {
    console.error('Failed to store webhook:', error);
    res.status(500).json({ error: 'Failed to process webhook' });
  }
});

async function processWebhookAsync(webhookEvent) {
  try {
    console.log(`Processing webhook ${webhookEvent.webhookId}: ${webhookEvent.eventType}`);

    if (webhookEvent.eventType === 'openid4vc.issuance.completed') {
      console.log(`Credential issued: ${webhookEvent.payload.openId4VcIssuanceId}`);
      // Add your business logic here
    }

    webhookEvent.processed = true;

  } catch (error) {
    console.error(`Failed to process webhook ${webhookEvent.webhookId}:`, error);
  }
}

Local Development

Use ngrok to expose your local server via a public HTTPS URL during development:
ngrok http 3000
Register the resulting https:// ngrok URL as your webhook endpoint. The platform will retry failed deliveries automatically.