Signature step
Sign a message, or check a webhook's signature, with HMAC.
The Signature step signs a message or checks a signature with HMAC. Use it to make sure a webhook really came from Shopify or a partner before you act on it, or to sign requests you send to a partner who checks yours.
Set it up
| In the app | data field | What it does |
|---|---|---|
| Preset | preset | None, or Shopify webhook (shopify_webhook) to check an incoming Shopify webhook with no other setup. |
| Operation | operation | Sign or Verify. |
| Algorithm | algorithm | SHA-256 (the default) or SHA-1. |
| Encoding | encoding | Hex (the default) or Base64. Use what the other side uses. |
| Secret | secret | The shared secret. Liquid works, so keep it in private data and use {{ partner_secret }}. Required unless you use the Shopify preset. |
| Message | message | What's signed. Leave it blank to use the raw body that started the job, exactly as it arrived. Liquid works. |
| Signature | signature | Verify only: the signature to check. Use header:Name to read it from a request header, like header:X-Signature, or a variable like {{ sig }}. |
| Fail on mismatch | fail_on_mismatch | Verify only: stops the job with an error when the signature doesn't match. |
| Override secret | secret | Shopify webhook preset only. See below. |
| Response variable | response_variable | Sign saves the signature. Verify saves true or false. |
Verifying: valid and invalid branches
A Verify step has two branches, like an If Condition:
- Signature valid: steps here run when the signature matches.
- Signature invalid: steps here run when it doesn't. With no invalid branch, an invalid signature skips this step's nested steps and the job carries on after it.
A Verify step with no nested steps doesn't skip anything. It just saves true or false, for you to check in a later If Condition.
With Fail on mismatch on, a bad signature stops the job with signature verification failed, and the invalid branch doesn't run. That error is final: the job isn't retried, because retrying wouldn't change the answer.
Retries check against the original bodyIf the job is retried, the steps under the Signature step run again, and the signature is checked again against the same body the job first received.
Hex signatures with a prefix
Some senders put the algorithm in front of a hex signature, like sha256=3f1a…. With Hex encoding, that prefix is ignored, so you don't need to strip it. With Base64, send the signature as-is, without a prefix.
Shopify webhooks
Choose the Shopify webhook preset and the step does the rest: it checks the raw Shopify body against the X-Shopify-Hmac-Sha256 header, using base64 SHA-256. You don't set the message, signature, algorithm or encoding.
- For webhooks set up through your Smooth POS Shopify connection, leave Override secret blank. Your Shopify app's client secret is used.
- For webhooks you created yourself in Shopify admin › Settings › Notifications, put that webhook's signing secret in Override secret (ideally
{{ shopify_webhook_secret }}from private data).
Example 1: Check a Shopify webhook
Trigger: Webhook. Point a Shopify webhook at your job's URL.
- API Request: Get Private Data, Key
shopify_webhook_secret, Response variablesecret_res. - Script: Read the secret:
$.shopify_webhook_secret = JSON.parse($.secret_res.body).shopify_webhook_secret.trim(); - Signature: Check the Shopify signature, Preset Shopify webhook, Override secret
{{ shopify_webhook_secret }}, Fail on mismatch on. - Header: Read the topic, Header key
X-Shopify-Topic, Response variabletopic.
With Fail on mismatch on, nothing after the Signature step runs for a forged request. Steps 1 and 2 are only needed when you override the secret.
Example 2: Sign a request to a partner
Trigger: System Hook, Create Sale.
- API Request: Get Private Data, Key
partner_secret, Response variablesecret_res. - Script: Read the secret:
$.partner_secret = JSON.parse($.secret_res.body).partner_secret.trim(); - Script: Build the message:
$.body = JSON.stringify({ sale_id: JSON.parse($.default).id }); - Signature: Sign the message, Operation Sign, Algorithm SHA-256, Encoding Hex, Secret
{{ partner_secret }}, Message{{ body }}, Response variablesignature. - Script: Read the signature:
$.headers = JSON.stringify({ "X-Signature": "sha256=" + $.signature, "Content-Type": "application/json" }); - REST Request: Send the signed request,
POST, Headers{{ headers }}, Body{{ body }}.
Sign exactly the text you send: here both use {{ body }}.
Example 3: Check a partner's signature and tell yourself about bad ones
Trigger: Webhook.
- API Request: Get Private Data, Key
partner_secret, Response variablesecret_res. - Script: Read the signing secret, which sets
$.partner_secret. - Signature: Check the partner signature, Operation Verify, Algorithm SHA-256, Encoding Hex, Secret
{{ partner_secret }}, Message blank (the raw body), Signatureheader:X-Signature.- Signature valid: Script: Read the update, then the steps that use it.
- Signature invalid: Email: Email yourself, to say a request with a bad signature came in.
Tips
- Leave Message blank for webhooks. The raw body is exactly what the sender signed. Re-encoding it (for example with
JSON.stringify(JSON.parse(...))) changes the bytes and breaks the check. - Errors you might see:
signature: no secret (set the secret field)means Secret came out empty, often because the private data key is missing.
Updated about 23 hours ago
