Webhooks
Fuuffy will notify your application when subscribed order events occur. Instead of polling the API for updates, configure an HTTPS endpoint and Fuuffy will send an HTTP POST request when an event is triggered.
How it works
- An order event occurs in Fuuffy.
- Fuuffy sends a JSON payload to your HTTPS endpoint.
- Your endpoint verifies and accepts the request.
- Your endpoint returns HTTP 200 to acknowledge receipt.
Endpoint requirements
Your webhook endpoint should:
- Accept HTTPS POST requests.
- Respond with HTTP 200 within 15 seconds.
- Acknowledge the request immediately and process the event asynchronously whenever possible.
- Implement idempotent event handling, as the same event may be delivered more than once.
- Return HTTP 200 only after the request has been successfully accepted.
Fuuffy delivers webhooks only to HTTPS endpoints. Treat your webhook endpoint as a production integration and store all related credentials securely.
Delivery & retry policy
If a webhook delivery fails because your endpoint does not return HTTP 200 or cannot be reached, Fuuffy automatically retries the delivery using exponential backoff.
- Maximum retry attempts: 5
- Retry strategy: Exponential backoff
- Duplicate deliveries: The same webhook event may be delivered multiple times.
Your application should process events idempotently.
Delivery behavior
| Condition | Status code | Behavior |
|---|---|---|
| Successful acknowledgement | 200 | Delivery succeeds. |
| Failed acknowledgement | Any status other than 200 | Delivery is treated as failed and may be retried. |
| Timeout or connection failure | No status code received | Delivery is treated as failed and may be retried. |
Securing webhook requests
Fuuffy supports two independent mechanisms for authenticating webhook requests. You may use either or both depending on your security requirements.
| Mechanism | Description | Example |
|---|---|---|
| Authentication header | A configurable header containing your shared token. | Authorization, X-API-Key |
| HMAC signature | An HMAC-SHA256 signature generated using your webhook_secret. | X-Signature |
A unique webhook_secret is generated when your webhook endpoint is configured.
If the secret is ever compromised, contact our Support team to generate a new one.
Treat both your authentication token and webhook_secret as sensitive credentials and store them securely.
Request
Header Parameters
| Parameter | Type | Required | Example | Description |
|---|---|---|---|---|
Content-Type | string | Yes | application/json | Request body content type. |
Authorization (or your configured header) | string | Yes | your-configured-token | Authentication token configured for your webhook. |
X-Signature | string | Yes | t=1492774577,v1=5257a869e7ecebeda32... | HMAC signature for verifying the request. |
Example request
Payload fields vary depending on the event type configured for your webhook. The endpoint path is controlled by your application.
curl -X POST "https://your-server.com/webhooks/fuuffy" \
-H "Content-Type: application/json" \
-H "Authorization: your-configured-token" \
-H "X-Signature: t=1492774577,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd" \
-d '{
"invoice_number": "#12345",
"carrier": "UPS",
"tracking_number": "1Z999AA10123456784"
}'
Verifying the signature
When a webhook_secret is configured, every webhook request includes an X-Signature header.
X-Signature: t=<unix_timestamp>,v1=<signature>
Where:
t— Unix timestamp indicating when the request was signed.v1— HMAC-SHA256 signature (hex encoded) generated using{timestamp}.{raw_request_body}and your fullwebhook_secret(including thewhsec_prefix).
Example signed payload:
1492774577.{"invoice_number":"#12345","carrier":"UPS","tracking_number":"1Z999AA10123456784"}
Verification steps
- Read the
X-Signatureheader and extract thetandv1values. - Read the raw request body exactly as received. The signature is calculated from the exact request body, so any change to whitespace, formatting, or property ordering will cause verification to fail.
- Construct the signed payload:
{timestamp}.{raw_body}. - Compute the HMAC-SHA256 digest using your
webhook_secret. - Compare the computed digest with
v1using a constant-time comparison function. - Reject requests where the timestamp differs from your server time by more than 10 minutes to help prevent replay attacks. Recommended
Code examples
PHP
$rawBody = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
$t = $v1 = null;
foreach (explode(',', $header) as $part) {
[$k, $v] = array_pad(explode('=', trim($part), 2), 2, null);
if ($k === 't') $t = $v;
if ($k === 'v1') $v1 = $v;
}
if ($t === null || $v1 === null) {
http_response_code(401);
exit;
}
$signedPayload = $t . '.' . $rawBody;
$expected = hash_hmac('sha256', $signedPayload, $webhookSecret);
if (!hash_equals($expected, $v1)) {
http_response_code(401);
exit;
}
// Signature verified.
// You may now safely decode and process the payload.
$data = json_decode($rawBody, true);
Node.js
const crypto = require('crypto');
function verifyWebhook(rawBody, signatureHeader, webhookSecret) {
const parts = Object.fromEntries(
signatureHeader.split(',').map((p) => p.trim().split('='))
);
const t = parts.t;
const v1 = parts.v1;
if (!t || !v1) return false;
const signedPayload = `${t}.${rawBody}`;
const expected = crypto
.createHmac('sha256', webhookSecret)
.update(signedPayload, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(v1)
);
}
Python
import hashlib
import hmac
def verify_webhook(raw_body: bytes, signature_header: str, webhook_secret: str) -> bool:
parts = dict(
p.strip().split("=", 1)
for p in signature_header.split(",")
)
t = parts.get("t")
v1 = parts.get("v1")
if not t or not v1:
return False
signed_payload = f"{t}.".encode() + raw_body
expected = hmac.new(
webhook_secret.encode(),
signed_payload,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, v1)
Best practices
- Verify the
X-Signaturebefore processing the payload. - Acknowledge requests quickly and process events asynchronously.
- Implement idempotent handlers because webhook deliveries may be retried.
- Avoid long-running operations before returning HTTP 200.
Enable webhooks
To register a webhook endpoint, contact our Support team at support@fuuffy.com.
After your endpoint is configured, you can:
- Subscribe to the required event types.
- Process events as they are delivered.