Responding to an event

When events occur within Optum Connections, we will send POST requests to your callback URL. The way you handle these events will depend on your system's requirements. However, every service must adhere to the following guidelines:

Basic Requirements

  • Use HTTPS: Ensure your callback URL uses HTTPS to secure the communication.
  • Verify Authenticity: Provide a secret value to verify the authenticity of the call. This is described in greater detail on the Authentication and security page.

Best Practices

To ensure continuity of service, every service that receives events should follow these guidelines:

  • Rotate Secrets Regularly: Regularly update your secret values to maintain security.
  • Handle Errors Gracefully: Implement error handling to manage any issues that arise during event processing.
  • Poll Missed Events API: Use our Missed Events API to stay in sync and ensure no events are missed.

Example Implementation

Here is an example of how to handle an incoming event in an Express server using TypeScript::

import express from 'express';

const app = express();
const port = 3000;
const secret = 'your-secret-key';

function verifyOptumWebhookSignature(req: Request, secretKey: string): boolean {
  const signature = req.headers['x-optum-webhook-callback-hmac-signature'] as string;
  const payload = JSON.stringify(req.body);

  // Compute the hash of the payload using HMAC-SHA256 and your secret key
  const computedHash = crypto.createHmac('sha256', secretKey).update(payload).digest('hex');
  
  // Use a constant-time comparison method to prevent timing attacks
  return crypto.timingSafeEqual(Buffer.from(computedHash), Buffer.from(signature));
}


app.post('/your-callback-url', (req, res) => {
 	const secretKey = process.env.OPTUM_SECRET_KEY as string;

  // Verify the secret
  if (!verifyOptumWebhookSignature(req, secretKey)) {
    console.error('Signature verification failed');
    return res.status(400).send('Invalid signature');
  }

  // Process the event
  const event = req.body;
  console.log('Received event:', event);

  // Handle the event based on your system's requirements
  // For example, update your database, notify users, etc.

  // Respond with a 2xx status code to acknowledge receipt
  res.sendStatus(200);
});

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

public class Startup
{
    private const string SecretKey = "your-secret-key";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapPost("/your-callback-url", async context =>
            {
                if (!await VerifyOptumWebhookSignature(context.Request, SecretKey))
                {
                    logger.LogError("Signature verification failed");
                    context.Response.StatusCode = 400;
                    await context.Response.WriteAsync("Invalid signature");
                    return;
                }

                using var reader = new StreamReader(context.Request.Body);
                var body = await reader.ReadToEndAsync();
                logger.LogInformation("Received event: {Event}", body);

                // Handle the event based on your system's requirements
                // For example, update your database, notify users, etc.

                context.Response.StatusCode = 200;
            });
        });
    }

    private async Task<bool> VerifyOptumWebhookSignature(HttpRequest request, string secretKey)
    {
        if (!request.Headers.TryGetValue("x-optum-webhook-callback-hmac-signature", out var signature))
        {
            return false;
        }

        using var reader = new StreamReader(request.Body);
        var payload = await reader.ReadToEndAsync();
        request.Body.Position = 0;

        using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secretKey));
        var computedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
        var computedHashString = BitConverter.ToString(computedHash).Replace("-", "").ToLower();

        return CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes(computedHashString), Encoding.UTF8.GetBytes(signature));
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

In this example:

  • The server listens for POST requests on /your-callback-url.
  • It verifies the secret provided in the request headers.
  • If the secret is valid, it processes the event and logs it.
  • The server responds with a 2xx status code to acknowledge receipt of the event.

By following these guidelines and implementing the example, you can ensure that your service handles events securely and efficiently.