The signed string is the timestamp, a dot, then the exact request body. Compute the HMAC over the RAW body, before any JSON parsing: re-serialising changes whitespace and breaks the comparison. The signature covers what actually goes out, whatever its shape: JSON, form, or an empty body for a GET.
x-yacla-timestamp: 1755000000 x-yacla-signature: <hex> signature = HMAC_SHA256(secret, timestamp + "." + body)
The secret belongs to the flow and is shown to you only once, when it is created. Signing can also be turned off on a flow: both headers are then simply absent.
Compare in constant time and reject a timestamp older than five minutes, otherwise the signature does not protect against replay.