Error handling

Errors in webhook handling can lead to unintended retries, so it's important to handle them properly to avoid repetitive processing. Here's how you can approach error handling:

Respond with 4xx/5xx status codes for failures

If you encounter an issue with processing (e.g., invalid data, authentication failure), return an HTTP 4xx or 5xx status code. Optum Connections' platform may retry failed webhooks, so returning an error status allows retries for transient issues.


Graceful degradation

For non-critical failures (like a temporary service delay), consider logging the issue and handling it later rather than blocking the response. This minimizes disruptions in your application's workflow.


Retry policy

Implement retry logic on your end if the webhook processing relies on third-party services or APIs that may temporarily fail. Make sure these retries are idempotent to avoid duplicate processing.


Example code

Here's a code example that incorporates these best practices including logging, signature verification, and error handling:

import express, { Request, Response } from 'express';
import crypto from 'crypto';

const app = express();
app.use(express.json());

// Verify the webhook signature
function verifySignature(req: Request): boolean {
    const signature = req.headers['optum-signature'] as string;
    const payload = JSON.stringify(req.body);
    const secret = process.env.OPTUM_WEBHOOK_SECRET as string;

    const hash = crypto.createHmac('sha256', secret).update(payload).digest('hex');
    return hash === signature
}

const processedEvents = new Set<string>();

app.post('/optum/webhook', (req: Request, res: Response) => {
    try {
        // Verify the signature to ensure the request is from Optum
        if (!verifySignature(req)) {
            console.error('Invalid Signature');
            return res.status(400).send('Invalid Signature');
        }

        const event = req.body;

        if (processedEvents.has(event.eventId)) {
            console.log(`Evemt ${event.eventId} already processed`);
            return res.status(200).end()
        }

        const { id, payload: { type, accountId } } = event
        switch (type) {
            case "batch_created":
                // Handle batch created event
                console.log(`Handling batch created for batch ${id} ${accountId}`);
                break

            case "batch_completed":
                // handle batch completed event
                console.log(`Handling batch completed for batch ${id} ${accountId}`);
                break
            default:
                console.log(`Unhandled event type ${type}`)
        }

        processedEvents.add(event.eventId);
        res.status(200).end()

    } catch (error) {
        console.error("Error processing webhook", error);

        // Return a 500 status code to allow Optum to retry the webhook
        res.status(500).send("Internal Server Error")
    }
});

app.listen(3000, () => {
    console.log("Server is running on port 3000");
})