Our webhooks deliver weather alerts for locations within your Perry Weather account to any destination you specify. Your platform can then distribute those events as you see fit.
Webhook Data Model
See the API reference for the complete request and event data models.
Webhook Validation
If your webhook exposes sensitive parts of your platform you might want to verify requests are coming from Perry Weather and not a malicious third party. Perry Weather cryptographically signs every webhook payload using HMAC-SHA256 with a signing secret unique to your account.
Retrieving Your Signing Secret
Signing secrets are managed in the Perry Weather web app, similar to API keys.
- In the Perry Weather dashboard, navigate to Integrations > Perry Weather APIs.
- Select the webhooks tab.
- Click the Signing Secret button to get the key.
Important: Treat the signing secret with the same care as an API key. Store it securely (environment variables or a secrets manager) and never expose it in client-side code or version control. If compromised, contact support@perryweather.com to rotate it.
How Perry Weather Signs Webhooks - v1.2+
- Perry Weather prepares the full JSON body of the webhook payload exactly as it will be sent.
- We compute an HMAC-SHA256 signature of that entire body using your signing secret as the key.
- The signature (Base64-encoded) is attached to the request in the
x-pw-signatureheader.
Note: This signing method (signing the entire webhook body) is used starting with version 1.2 of Perry Weather webhooks. Previous versions (1.0 and 1.1) use the legacy method of signing only the URL with metadata. See this page for details on the legacy method..
Validating Requests in Your Application
To verify a webhook came from Perry Weather:
- Extract the raw request body as a string/byte array (do not parse it first, as whitespace or encoding differences can invalidate the signature).
- Compute the HMAC-SHA256 hash of that body using your signing secret.
- Base64-encode the resulting hash.
- Compare it (constant-time comparison) to the value in the
x-pw-signatureheader.
If the signatures match, the request is authentic and untampered. If they do not match or the header is missing, discard the request.
Example implementations (adapt to your language/framework):
Node.js / Express:
const crypto = require('crypto');
function validateWebhook(req, res, next) {
const signature = req.headers['x-pw-signature'];
if (!signature) return res.status(401).send('Missing signature');
const body = req.rawBody || JSON.stringify(req.body); // Use raw body
const hmac = crypto.createHmac('sha256', YOUR_SIGNING_SECRET);
const digest = hmac.update(body).digest('base64');
if (crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature))) {
next();
} else {
res.status(401).send('Invalid signature');
}
}
Python:
const crypto = require('crypto');
function validateWebhook(req, res, next) {
const signature = req.headers['x-pw-signature'];
if (!signature) return res.status(401).send('Missing signature');
const body = req.rawBody || JSON.stringify(req.body); // Use raw body
const hmac = crypto.createHmac('sha256', YOUR_SIGNING_SECRET);
const digest = hmac.update(body).digest('base64');
if (crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature))) {
next();
} else {
res.status(401).send('Invalid signature');
}
}
C# (.NET):
using System.Security.Cryptography;
using System.Text;
public bool ValidateSignature(string body, string receivedSignature, string secret)
{
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
byte[] hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(body));
string computed = Convert.ToBase64String(hash);
return CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(computed),
Encoding.UTF8.GetBytes(receivedSignature));
}
Best Practices
- Always validate the signature before processing any webhook data.
- Use raw request body for hashing to avoid serialization differences.
For questions about webhooks, versioning, or your signing secret, contact support@perryweather.com.