KDPika Licensing Sign inStart free
API v1
Show all languages
Browse documentation

Choose a page in the navigation drawer.

KDPika Licensing · API v1 Advanced

Verify webhook signatures

Execute raw-byte HMAC verifiers in Node, Python, PHP, C#, Go, Java and Ruby with positive and negative notification tests.

On this page
Updated 2026-10-18 UTCDownload Markdown

Verify the original request bytes before trusting a notification or storing work for it.

What you will learn

  • Run the same complete verifier contract in seven languages.
  • Generate a current signed synthetic test request.
  • Distinguish signature verification from durable deduplication and license authorization.

Prerequisites

  • Your endpoint's signing secret from setup.
  • The source archive, extracted locally.
  • A language runtime from the requirements below.
  • A private process environment; do not publish its secret values.

The download manifest lists the archive SHA-256 and every included source-file hash. Generated private requests, compiled programs and local inbox databases are not included.

Prepare the files

  1. Extract the archive and work inside its webhooks directory.
  2. Keep contract.json beside the verifier files.
  3. Configure KDP_WEBHOOK_SECRET from your secret store with a Sandbox test endpoint secret.
  4. Set KDP_WEBHOOK_ENVIRONMENT to sandbox.
  5. Generate a new request directory with the following command.
bashbash
python3 make-test.py events/test-ping.json private/request-1

Expected output: Created private test request files. The program creates body.json and headers.json without printing the secret or signature. It refuses to overwrite existing request files; use a new directory for another test.

This complete test body is a real captured synthetic local notification. It is old by design; make-test.py signs its exact bytes with the current attempt timestamp. Checking its created_at against the current clock would reject a valid delayed event incorrectly.

test-ping.jsonjson
{
  "id": "evt_ffa11b785e0ede73d9e69801",
  "data": {
    "message": "Signed webhook test",
    "webhook_id": "whk_2bf9c0559190a2101b3b9d1b"
  },
  "event": "test.ping",
  "created_at": "2026-10-14T17:46:36+00:00",
  "environment": "sandbox"
}

Shared receiver contract

The archive includes contract.json beside the sources. Keep it unchanged with the local test helper; all seven programs read the same file.

The event list is checked against the real sender. The 300-second freshness window and 1048576-byte body limit are explicit receiver example policy, not sender inbound limits. Header-file input is capped at 32768 bytes. The verifier rejects a missing or malformed timestamp/signature pair, wrong HMAC, stale/future attempt timestamp, unknown event, header/body mismatch and wrong pinned environment.

Use the plaintext endpoint secret as the HMAC key, not its hash and not an Ed25519 public key. Verification is over timestamp, one period, and raw body bytes. No JSON canonicalization step belongs in HMAC verification.

Requirements and commands

LanguageRequirementsRun after request generation
Node.jsNode 22+, standard librarynode verify.mjs private/request-1/body.json private/request-1/headers.json
PythonPython 3.11+, standard librarypython3 verify.py private/request-1/body.json private/request-1/headers.json
PHPPHP 8.2+, JSON/hashphp verify.php private/request-1/body.json private/request-1/headers.json
RubyJSON/OpenSSL, compatible syntax from 2.6; deploy maintained Rubyruby verify.rb private/request-1/body.json private/request-1/headers.json
GoGo 1.24+, standard libraryBuild verify.go, then run verify-go with both file paths
JavaJava 21, Gson 2.14.0Compile, then run VerifyWebhook with both file paths
C#.NET 7 target, standard libraryBuild the project, then run its DLL with both file paths

Go build:

bashbash
go build -o verify-go verify.go
./verify-go private/request-1/body.json private/request-1/headers.json

Java build and run, after downloading the exact Gson JAR from the official artifact repository:

bashbash
javac -cp gson-2.14.0.jar VerifyWebhook.java
java -cp .:gson-2.14.0.jar VerifyWebhook private/request-1/body.json private/request-1/headers.json

The tested JAR SHA-256 is 2cbd119bf1961c28788310963dc80ba65f58cdeec1dd139c8bdb1240faa2c36f. On Windows use a semicolon in the Java classpath: -cp ".;gson-2.14.0.jar", and run verify-go.exe instead of ./verify-go. Use the same working directory containing contract.json for Go, Java and C#.

bashbash
dotnet build csharp/WebhookVerifier.csproj -c Release
dotnet csharp/bin/Release/net7.0/WebhookVerifier.dll private/request-1/body.json private/request-1/headers.json

The .NET target matches the current SDK baseline; verify an upgrade to your supported deployment runtime before launch. This phase executes the verifiers on macOS, not native Windows or Linux. Ruby was executed on the legacy macOS runtime. Prefer maintained Ruby with the native fixed_length_secure_compare primitive for deployment; the older fallback compares every fixed-size byte position without early exit but does not establish a managed-VM constant-time guarantee.

Complete verifier sources

Choose a language to read its complete executed source and setup. Separate pages keep the initial download small without truncating or hiding code. All are public documentation and work without JavaScript.

HTTP adapter checklist

  1. Require POST on a dedicated route and application/json content.
  2. Reject duplicate signature, event-name and event-ID HTTP headers before joining header values.
  3. Enforce body/header limits and a read timeout before buffering.
  4. Pass exact raw body bytes and normalized lowercase header names to the verifier.
  5. Pass only the secret and expected environment assigned to that endpoint.
  6. On success, atomically record the event ID and durable work item.
  7. Return a generic refusal on failure without echoing secrets, signatures or payloads.

Expected result: an authenticated duplicate is handled by your durable inbox, not by calling the verifier alone. The CLI tools authenticate but deliberately do not perform any business effect or store a deduplication record. The local HTTP inbox shows the receive/commit/acknowledge boundary.

Expected result

For test-ping.json, each program exits 0 and prints JSON containing verified=true, event_id=evt_ffa11b785e0ede73d9e69801, event=test.ping and environment=sandbox. Object property ordering is not part of the result contract.

A changed body byte, wrong secret, stale/future attempt, invalid envelope or wrong environment prints exactly {"verified":false} and exits 1. Test every failure before connecting a business action. Regenerate the request when its signature timestamp is outside the 300-second window.

Common mistakes

  • Signing a pretty-printed reconstruction instead of preserving the request bytes.
  • Passing the webhook secret hash instead of the original secret.
  • Reusing a saved signature indefinitely instead of generating a new test timestamp.
  • Assuming the CLI verifier prevents duplicate emails or license renewals.

Security notes

The header JSON file is a local testing interface, not a public endpoint. Securely remove private generated requests under your retention policy; do not check them into Git. Reject duplicated HTTP signature headers at the adapter. Application validity uses Ed25519-signed license responses, not these HMAC notification verifiers.

Troubleshooting

SymptomCauseFix
verified=false in all languagesWrong secret, stale signature or changed bodyRegenerate a private request and use its exact files
Java cannot find GsonJAR absent from compile/run classpathInstall the pinned artifact and use the OS-specific separator
Go or C# cannot read contract.jsonWrong working directoryRun from the extracted webhooks directory
A duplicate verifies twiceAuthentication is not deduplicationAdd a durable unique event-ID inbox before business processing

Next steps

Run local replay and failure tests before enabling your production notification workflow.

Was this page helpful?

Choose an answer

Do not include personal data, license keys or credentials. Retained for up to 30 days. This is not a support inbox.

Search documentation

Arrow keys to move, Enter to open, Escape to close. Search stays in your browser.

Cookie settings