AI & Agents

How to Verify Fastio Webhook Signatures

Fastio webhooks deliver real-time notifications for file uploads, modifications, and access in your workspaces. To keep your agentic workflows secure, verify each webhook signature before processing. Webhook signature verification in Fastio uses HMAC-SHA256 signatures to confirm events come from Fastio and remain untampered. This blocks SSRF and injection attacks common in unverified webhooks. Copy the middleware below for Express.js or FastAPI. Both capture the raw request body needed for accurate HMAC computation and use constant-time comparison to prevent timing attacks.

Fastio Editorial Team 8 min read
Track file events and webhook deliveries in your workspace audit log

What Fastio Webhooks Do

Fastio sends HTTP POST requests to your endpoint for events like file.uploaded, file.modified, file.accessed, workspace.member_added.

Events include details: file ID, version, user who triggered it, timestamp.

Set up webhooks in your org dashboard under Settings > Webhooks. Select events, enter URL. Each webhook gets a unique signing secret.

Respond with 2xx status within 30 seconds or Fastio retries (up to 24 hours, exponential backoff).

Why Signature Verification Matters

Attackers forge webhooks to trigger actions: delete files, exfiltrate data via SSRF.

Unverified webhooks rank as a top vector for SSRF and injection in agentic systems handling file events.

Verification confirms origin and integrity. Fastio signs the timestamped raw payload.

Secure data room with webhook notifications
Fastio features

Secure Your Agent Workflows Now

React to file changes instantly with verified Fastio webhooks. Free agent plan includes 50GB storage, 5 workspaces, and 5,000 monthly credits.

How Signatures Work

Fastio adds Fastio-Signature header:

Fastio-Signature: t=1694206100,v1=abc123def456...

Signature format: t=<unix_timestamp>,v1=<base64_hmac>

Compute expected signature:

  1. Concat timestamp + "." + raw_request_body
  2. HMAC-SHA256 with your webhook secret (hex or base64)
  3. Base64-encode result
  4. Constant-time compare with v1 value

Timestamps expire after 5 minutes to block replays.

Quick Verification Snippet (Node.js)

const crypto = require('crypto');
function verifySignature(rawBody, signature, secret) {
  const [timestamp, sig] = signature.split(',').map(s => s.split('=')[1]);
  const payload = `${timestamp}.${rawBody}`;
  const expected = crypto.createHmac('sha256', secret)
                        .update(payload)
                        .digest('base64');
  return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}

Find Your Signing Secret

Log into Fastio dashboard > your org > Settings > Webhooks.

Click webhook > Reveal secret (hex or base64).

Store securely, rotate periodically via Regenerate.

API alternative: GET /current/org/{org_id}/webhooks/{webhook_id} (requires admin).

Node.js Express Middleware

Express parses JSON by default, corrupting raw body for HMAC. Use raw-body and buffer.

Install: npm i raw-body express

const express = require('express');
const getRawBody = require('raw-body');
const crypto = require('crypto');

const app = express();

app.use(express.json({ verify: (req, res, buf) => { req.rawBody = buf; } }));

app.post('/webhook', async (req, res) => {
  const signature = req.get('Fastio-Signature');
  if (!signature) return res.status(400).send('No signature');

const secret = process.env.FASTIO_SECRET;
  const rawBody = req.rawBody.toString();

const [timePart, sigPart] = signature.split(',');
  const timestamp = timePart.split('=')[1];
  const expectedSig = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('base64');

if (!crypto.timingSafeEqual(Buffer.from(sigPart.split('=')[1]), Buffer.from(expectedSig))) {
    return res.status(401).send('Invalid signature');
  }

if (Math.abs(Date.now() / 1000 - parseInt(timestamp)) > 300) {
    return res.status(401).send('Timestamp expired');
  }

const event = req.body;
  console.log(`Event: ${event.event_type}`, event.data);

res.status(200).send('OK');
});

app.listen(3000);

Middleware captures raw body before JSON parse.

Python FastAPI Middleware

FastAPI needs raw bytes. Use RawRequest.

Install: pip install fastapi uvicorn cryptography

from fastapi import FastAPI, Request, HTTPException
from hmac import digest
from hashlib import sha256
import secrets
import time

app = FastAPI()

@app.post("/webhook")
async def webhook(request: Request):
  signature = request.headers.get("Fastio-Signature")
  if not signature:
    raise HTTPException(400, "No signature")

secret = "your_webhook_secret".encode()
  body = await request.body()

t, v1 = signature.split(",")
  timestamp = int(t.split("=")[1])
  payload = f"{timestamp}.{body.decode()}".encode()
  expected = digest(secret, payload, sha256)

received_sig = v1.split("=")[1].encode()

if not secrets.compare_digest(received_sig, expected):
    raise HTTPException(401, "Invalid signature")

if abs(time.time() - timestamp) > 300:
    raise HTTPException(401, "Timestamp expired")

event = await request.json()
  print(f"Event: {event['event_type']}", event['data'])

return {'status': 'ok'}

await request.body() gets raw bytes.

Handle Webhook Events

Verified payload is JSON:

{
  "event_type": "file.uploaded",
  "data": {
    "file_id": "f3jm5-zqzfx...",
    "workspace_id": "1234567890123456789",
    "user_id": "9876543210987654321",
    "timestamp": 1694206100
  },
  "id": "evt_abc123"
}

Idempotency: check id against DB to skip duplicates.

Common events:

Event Trigger
file.uploaded New file added
file.modified File updated
file.accessed File viewed/downloaded
workspace.member_added New collaborator

Respond 200 fast; process async with queue.

Troubleshooting

Invalid signature:

  • Body modified by middleware (use raw body).
  • Wrong secret (check hex/base64).
  • Timestamp mismatch (use exact format).
  • Encoding issue (UTF-8 raw).

No events: Verify endpoint public, returns 2xx, not rate limited.

Replay attacks: Enforce 5-min timestamp window.

Test: Dashboard test button sends sample event.

Frequently Asked Questions

How do I verify a Fastio webhook?

Parse `Fastio-Signature` header, compute HMAC-SHA256 of `t=... + "." + rawBody` using secret, compare constant-time. Use middleware above.

Why is my Fastio webhook signature invalid?

Common causes: JSON middleware alters body, wrong secret format, timestamp drift, non-UTF8 encoding. Capture raw body first and check logs.

What header holds the Fastio signature?

`Fastio-Signature: t=<unix_timestamp>,v1=<base64_hmac>`

Does Fastio support webhook retries?

Yes, exponential backoff up to 24 hours if no 2xx response within 30s.

Can I test webhooks without production events?

Use dashboard test button or simulate events via curl with valid payload/signature.

Related Resources

Fastio features

Secure Your Agent Workflows Now

React to file changes instantly with verified Fastio webhooks. Free agent plan includes 50GB storage, 5 workspaces, and 5,000 monthly credits.