Skip to main content

Payload Signature

Saleor calculates a payload signature and puts the result into Saleor-Signature header:

  • DEPRECATED if secretKey was set for a webhook - the HMAC SHA-256 header based on it and the payload
  • if secretKey wasn't set for a webhook - the JWS signature using RS256 with payload detached, to verify the signature you can use a public key, which can be fetched from http(s)://<your-backend-domain>/.well-known/jwks.json (available since Saleor 3.5, before 3.5 the payload from webhooks without secretKey set were not signed)

Validating signature

To validate signatures in Saleor Apps, use withWebhookSignatureVerified middleware provided by @saleor/app-sdk

import type { Handler } from "retes";
import { Response } from "retes/response";
import { toNextHandler } from "retes/adapter";
import {
} from "@saleor/app-sdk/middleware";

const handler: Handler = async (request) => {
// ...

return Response.OK({ success: true });

export default toNextHandler([
Expand ▼

You can also manually validate the JWS signature in JavaScript with the jose package. Remember that you need to supply it with a raw body string, not a parsed object.

import getRawBody from "raw-body";

const JWKS = jose.createRemoteJWKSet(
new URL("" + "/.well-known/jwks.json")

// In Next.js you need to disable `bodyParser` in order to get raw body string
export const config = {
api: {
bodyParser: false,

export default function(req, res) {
const jws = req.headers["saleor-signature"];
const buffer = getRawBody(req, {
length: req.headers["content-length"],
limit: "1mb",
const [header, _, signature] = jws.split(".");
try {
await jose.flattenedVerify({
protected: header,
payload: buffer.toString("utf-8"),
}, JWKS);
} catch (e) {
// return error

// handle your request
Expand ▼