Webhook handler throwing 500 on malformed notification_api payload

The Genesys notification stream keeps hammering our webhook endpoint with a malformed JSON blob and Tokio’s router just returns a 500 instead of acknowledging it. I’ve got the serde deserializer failing on the routing.data field, so the event drops straight into the void. Wrote a quick Axum handler to catch the parse error and push the raw bytes to an SQS DLQ, but the retry loop keeps choking on the backoff math.

async fn handle_webhook(body: String) -> Result<StatusCode, String> {
 match serde_json::from_str::<Notification>(&body) {
 Ok(_) => Ok(StatusCode::OK),
 Err(e) => { dead_letter_queue.send(body).await?; Err(e.to_string()) }
 }
}

The 5xx responses are still triggering Genesys’s exponential backoff and I can’t figure out how to force a 200 ACK while queuing the retry locally.

The issue isn’t the backoff math. It’s the HTTP status code you’re returning to the Genesys notification service. If you send a 500, the system assumes the endpoint is broken and retries aggressively with exponential backoff, eventually flooding your DLQ or hitting rate limits. You need to return a 200 OK immediately if you intend to process the payload asynchronously or move it to a queue.

Here is how I handle this in my Rust handlers. You validate the structure loosely. If it fails, you log the raw body and return 200. The notification is considered “delivered” by Genesys, so it stops retrying. Your background job can then pull from the DLQ at its own pace.

use axum::{http::StatusCode, response::IntoResponse};

async fn handle_notification(
 body: String,
) -> impl IntoResponse {
 // Attempt to parse into your strict struct
 match serde_json::from_str::<NotificationPayload>(&body) {
 Ok(payload) => {
 // Process valid payload
 process_event(payload).await;
 (StatusCode::OK, "Accepted").into_response()
 }
 Err(e) => {
 // Log error and push to SQS DLQ here
 println!("Parse error: {}. Pushing to DLQ.", e);
 
 // CRITICAL: Return 200 to stop Genesys retries
 (StatusCode::OK, "Malformed payload queued for retry").into_response()
 }
 }
}

Genesys doesn’t care if your internal logic failed. It only cares about the HTTP response. If you want retries, you have to manage that state yourself in your database or queue. Returning 500 just makes the noise worse.