https://retellai.com/?utm_source=discord&utm_medium=seo logo
Webhook signature verification failing with expone...
# ❓・bug-report
m
Hi, we integrated webhook signature verification a long time ago and it mostly works, but it fails sometimes. We’ve tracked the failures down to webhook payloads that include numbers in exponential notation, for example:
4.0000000126383384e-05
This doesn’t happen often, but when it does, the Python verification method from the docs fails to us: https://docs.retellai.com/features/secure-webhook Happens in `call_ended`/`call_analyzed` events. Example:
Copy code
{
   "event":"call_ended",
   "call":{
      "call_id":"...",
      "call_type":"phone_call",
      "agent_id":"...",
      "call_status":"ended",

      // ... other fields

      "latency":{
         // ... other fields
         "asr":{
            "p50":20,
            "p90":29.999859999999718,
            "p95":29.999929999999857,
            "p99":29.99998599999997,
            "min":4.0000000126383384e-05,
            "max":30,
            "num":8,
            "values":[
               4.0000000126383384e-05,
               10,
               20,
               20.000049999999646,
               19.999899999999798,
               20,
               29.999799999999595,
               30
            ]
         }
      }
   }
}
Some call IDs that had this issue: - call_aac44d76f3f39b3a60604649ea7 - call_21801367bd6885b2ced92e16715 - call_3163a399d36cfea27e2c2671ec5 Can you clarify if exponential notation is expected on webhook events? Thanks.
s
Hi @maxvolume Yes, exponential scientific notation is expected and valid in Retell webhook payloads. JSON allows this format, and it can occasionally appear in latency metrics like asr timing when values are very small. The signature verification failures you’re seeing are most likely caused by re-serializing the JSON body before verification. When numbers like 4.0000000126383384e-05 are parsed and re-stringified, their formatting can change, which causes the HMAC signature check to fail. What we Recommend is Always verify the webhook using the raw request body exact bytes as received, not a parsed/re-stringified JSON object. This will make verification reliable and avoid issues with float formatting
m
@Shehroze -Support @Retell got it thanks for confirming that. Is there any chance you can provide a concrete Python example showing how to verify the signature taking that into account? Just want to make sure we have that right, since the current documentation seems to be re-serializing the JSON.
s
You can try to verify using the raw request body , not re serialized JSON
m
Sorry that doesn't help at all without a proper example. The following does not work FYI:
Copy code
post_data = await request.body()
valid_signature = retell.verify(
    post_data,
    api_key=str(os.environ["RETELL_API_KEY"]),
    signature=str(request.headers.get("X-Retell-Signature")),
)
s
await request.body() returns bytes, but retell.verify() expects a UTF-8 string. Passing bytes will always fail. Example: raw_body = (await request.body()).decode("utf-8") valid_signature = retell.verify( raw_body, api_key=os.environ["RETELL_API_KEY"], signature=request.headers.get("X-Retell-Signature"), )
m
Will try that, thanks
d
Hi — we are still seeing signature mismatches even when using the raw request body. We have verified the following: ✅ Using the API key with the webhook badge (full key_... prefix) ✅ Using raw request body bytes (no JSON re-stringify) ✅ Using HMAC-SHA256 ✅ Signed payload constructed as:
${timestamp}.${raw_body}
✅ Timestamp extracted from X-Retell-Signature: v=,d= However, none of these models match the received signature: HMAC(apiKey, raw_body) HMAC(apiKey, timestamp + "." + raw_body) HMAC(apiKey, timestamp + raw_body) HMAC(apiKey, JSON.stringify(parsed_body)) Can you please confirm: Is the timestamp always in milliseconds, or can it be seconds? Is the signed payload exactly ${timestamp}.${raw_body}? Is the digest always lowercase hex HMAC-SHA256? Is any canonicalization applied before signing (e.g., JSON normalization)? Can you confirm the exact algorithm currently used in production (as of Feb 2026)? We need the exact signing construction to implement manual verification, since the SDK's Retell.verify() method does not exist in retell-sdk@5.0.0.
o
Hello @dcitelly.Art-Intel , the team is working on it, will update you when its fixed.
d
Thanks for the update. For clarity on our side — should we pause manual HMAC verification attempts and rely exclusively on the SDK’s Retell.verify() once the fix is rolled out? Also, can you confirm whether the current production signing behavior differs from the documentation (timestamp.rawBody + HMAC-SHA256), or if this is a known inconsistency being patched? We just want to ensure we’re aligning with the intended long-term verification model.
Hi — quick clarification. We installed retell-sdk@5.1.0 from npm. At runtime, Retell.verify is not exported (it does not exist on the Retell object). The documentation shows: Retell.verify(body, apiKey, signature) But this method is not present in the published SDK version. Can you confirm: Which exact npm version exposes Retell.verify? Whether it is a static method or instance method? If the docs refer to a different package or internal version? We cannot use the SDK for verification because the method does not exist in the public package. Thanks.
o
Hello This is fixed update to 5.2.0