Axiym

Verifying signatures

Axiym signs the raw HTTP request body. Verify the signature before trusting or processing the event.

Do not parse and re-serialize JSON for verification. Whitespace, field order, or encoding changes produce different bytes and cause verification to fail.

Verification steps

  1. Capture the raw body bytes before JSON parsing.
  2. Read X-Signature, X-Key-Id, and X-Algorithm.
  3. Reject the request unless X-Algorithm is Ed25519.
  4. Load the cached public key for X-Key-Id, or retrieve it from the API.
  5. Verify the base64 signature against the raw body.
  6. Parse the JSON only after verification succeeds.
  7. De-duplicate by the event id.

Return a non-2xx response when verification fails.

JavaScript example

import { createPublicKey, verify } from "node:crypto";

export function verifyAxiymWebhook(rawBody, headers, keyResponse) {
  const signature = headers["x-signature"];
  const keyId = headers["x-key-id"];
  const algorithm = headers["x-algorithm"];

  if (!signature || !keyId || algorithm !== "Ed25519") return false;
  if (keyResponse.publicKeyId !== keyId) return false;

  const publicKey = createPublicKey({
    key: Buffer.from(keyResponse.publicKey, "base64"),
    format: "der",
    type: "spki",
  });

  return verify(
    null,
    rawBody,
    publicKey,
    Buffer.from(signature, "base64"),
  );
}

Operational checks

  • Cache keys by publicKeyId so signing-key rotation is safe.
  • Validate the event timestamp against your accepted clock-skew window.
  • Persist event IDs so de-duplication survives restarts.
  • Return 2xx only after durable persistence or queueing.
  • Fetch the current payout if ordering or current state matters.