Freeflow

Guides

Roblox Open Cloud Webhooks: Complete Setup and Security Guide

Roblox Open Cloud webhooks allow Roblox to send notifications directly to an external application when supported events occur.

Instead of repeatedly checking Roblox for changes, your application can receive an HTTP request automatically.

A basic setup looks like this:

Roblox
   ↓
Webhook notification
   ↓
Your HTTPS endpoint
   ↓
Verify signature
   ↓
Process event
   ↓
Database / Discord / Application

This is different from sending a webhook from inside a Roblox game using HttpService.

For outbound Roblox requests, read:

Complete Roblox Webhooks Guide

For Discord logging from inside your game, see:

Roblox Discord Webhook Guide

Freeflow Team•Sep 28, 2026•10 min read
01 /

What Are Roblox Open Cloud Webhooks?

Traditional APIs work by asking a service for information.

For example:

Your App
   ↓
"Has anything changed?"
   ↓
Roblox API

You might have to repeat that request every few minutes.

A webhook reverses this relationship.

Something happens on Roblox
        ↓
Roblox sends the event
        ↓
Your server receives it

This is more suitable for events that need to be processed shortly after they occur.

02 /

Where Do You Configure Roblox Webhooks?

Webhook notifications can be configured through Roblox Creator Hub.

A general setup is:

  1. Open Creator Hub.
  2. Select your experience.
  3. Open its configuration.
  4. Select Webhooks.
  5. Add a webhook.
  6. Enter your webhook URL.
  7. Give the webhook a name.
  8. Add a secret.
  9. Choose supported triggers.
  10. Test the endpoint.
  11. Save your configuration.

For a group-owned experience, webhook configuration permissions may depend on the group owner and the supported event.

03 /

Roblox Webhook Endpoint Requirements

Your endpoint should:

  • Be publicly accessible
  • Accept HTTP POST requests
  • Use HTTPS
  • Process JSON
  • Return a successful 2XX HTTP response quickly
  • Verify incoming requests
  • Protect against replay attacks

Your endpoint should not perform slow work before responding.

A better architecture is:

Roblox
   ↓
Webhook endpoint
   ↓
Validate request
   ↓
Queue event
   ↓
Return HTTP 200
   ↓
Process asynchronously
04 /

Basic Node.js Roblox Webhook Receiver

Install Express:

npm init -y
npm install express

Create:

server.js

Add:

const express = require("express");

const app = express();

app.use(express.json());

app.post("/roblox-webhook", (req, res) => {
  console.log("Webhook received");

  console.log(req.headers);

  console.log(req.body);

  res.sendStatus(200);
});

app.listen(3000, () => {
  console.log("Listening on port 3000");
});

This is useful for learning, but it is not enough for production.

You should verify the webhook's Roblox signature.

05 /

Why Roblox Webhook Signatures Matter

Imagine your endpoint is:

https://example.com/roblox-webhook

Without authentication, somebody else could potentially send fake requests to it.

That becomes dangerous if incoming webhook events perform actions such as:

  • Deleting data
  • Changing account information
  • Triggering moderation
  • Sending staff alerts
  • Updating external databases

Roblox supports webhook secrets so your application can verify that an event really came from Roblox.

06 /

Roblox-Signature Header

With a webhook secret configured, the request includes a signature with a structure similar to:

t=TIMESTAMP,v1=SIGNATURE

For example:

t=1700000000,v1=BASE64_SIGNATURE

Your application can validate this.

07 /

How Roblox Webhook Signature Verification Works

The process is:

timestamp
+
"."
+
raw request JSON

Example conceptual signing string:

1700000000.{"example":"data"}

Then calculate:

HMAC-SHA256(
    secret,
    signingString
)

The result is converted to Base64.

Your calculated signature should match the v1 signature Roblox sent.

08 /

Important: Preserve the Raw Request Body

For signature verification, you should verify the exact body Roblox sent.

If your framework parses and then re-serializes JSON differently, whitespace or formatting may change.

That can cause a signature mismatch.

A production receiver should capture the raw body.

09 /

Secure Express Webhook Receiver

Install:

npm install express

Then:

const express = require("express");

const crypto = require("crypto");

const app = express();

const WEBHOOK_SECRET = process.env.ROBLOX_WEBHOOK_SECRET;

app.use(
  express.json({
    verify: (req, res, buffer) => {
      req.rawBody = buffer.toString("utf8");
    },
  }),
);

Now create the verification function.

function parseRobloxSignature(header) {
  if (!header) {
    return null;
  }

  const parts = header.split(",");

  const timestampPart = parts.find((part) => part.startsWith("t="));

  const signaturePart = parts.find((part) => part.startsWith("v1="));

  if (!timestampPart || !signaturePart) {
    return null;
  }

  return {
    timestamp: timestampPart.substring(2),

    signature: signaturePart.substring(3),
  };
}
10 /

Generate the Expected Roblox Signature

function createExpectedSignature(timestamp, rawBody) {
  const signingString = `${timestamp}.${rawBody}`;

  return crypto
    .createHmac("sha256", WEBHOOK_SECRET)
    .update(signingString)
    .digest("base64");
}
11 /

Timing-Safe Signature Comparison

Avoid basic string equality for security-sensitive signature checks.

function safeCompare(expected, received) {
  const expectedBuffer = Buffer.from(expected);

  const receivedBuffer = Buffer.from(received);

  if (expectedBuffer.length !== receivedBuffer.length) {
    return false;
  }

  return crypto.timingSafeEqual(expectedBuffer, receivedBuffer);
}
12 /

Protect Against Replay Attacks

A valid webhook request could potentially be captured and resent later.

Check the timestamp.

Example five-minute window:

function validTimestamp(timestamp) {
  const requestTime = Number(timestamp);

  if (!Number.isFinite(requestTime)) {
    return false;
  }

  const now = Math.floor(Date.now() / 1000);

  const difference = Math.abs(now - requestTime);

  return difference <= 300;
}
13 /

Complete Roblox Webhook Verification

function verifyRobloxWebhook(req) {
  const signatureHeader = req.headers["roblox-signature"];

  const parsed = parseRobloxSignature(signatureHeader);

  if (!parsed) {
    return false;
  }

  if (!validTimestamp(parsed.timestamp)) {
    return false;
  }

  const expected = createExpectedSignature(parsed.timestamp, req.rawBody);

  return safeCompare(expected, parsed.signature);
}

Now use it:

app.post("/roblox-webhook", (req, res) => {
  if (!verifyRobloxWebhook(req)) {
    return res.status(401).send("Invalid webhook");
  }

  console.log("Verified Roblox webhook");

  console.log(req.body);

  res.sendStatus(200);
});
14 /

Complete Node.js Example

Put everything together:

const express = require("express");

const crypto = require("crypto");

const app = express();

const PORT = process.env.PORT || 3000;

const WEBHOOK_SECRET = process.env.ROBLOX_WEBHOOK_SECRET;

app.use(
  express.json({
    verify: (req, res, buffer) => {
      req.rawBody = buffer.toString("utf8");
    },
  }),
);

function parseRobloxSignature(header) {
  if (!header) {
    return null;
  }

  const parts = header.split(",");

  const timestampPart = parts.find((part) => part.startsWith("t="));

  const signaturePart = parts.find((part) => part.startsWith("v1="));

  if (!timestampPart || !signaturePart) {
    return null;
  }

  return {
    timestamp: timestampPart.substring(2),

    signature: signaturePart.substring(3),
  };
}

function validTimestamp(timestamp) {
  const requestTime = Number(timestamp);

  if (!Number.isFinite(requestTime)) {
    return false;
  }

  const currentTime = Math.floor(Date.now() / 1000);

  return Math.abs(currentTime - requestTime) <= 300;
}

function createExpectedSignature(timestamp, rawBody) {
  const signingString = `${timestamp}.${rawBody}`;

  return crypto
    .createHmac("sha256", WEBHOOK_SECRET)
    .update(signingString)
    .digest("base64");
}

function safeCompare(first, second) {
  const firstBuffer = Buffer.from(first);

  const secondBuffer = Buffer.from(second);

  if (firstBuffer.length !== secondBuffer.length) {
    return false;
  }

  return crypto.timingSafeEqual(firstBuffer, secondBuffer);
}

function verifyRobloxWebhook(req) {
  const header = req.headers["roblox-signature"];

  const values = parseRobloxSignature(header);

  if (!values) {
    return false;
  }

  if (!validTimestamp(values.timestamp)) {
    return false;
  }

  const expected = createExpectedSignature(values.timestamp, req.rawBody);

  return safeCompare(expected, values.signature);
}

app.post("/roblox-webhook", async (req, res) => {
  if (!verifyRobloxWebhook(req)) {
    return res.status(401).json({
      error: "Invalid signature",
    });
  }

  const event = req.body;

  console.log("Verified Roblox webhook:", event);

  /*
   * Put event into your queue,
   * database or processing system.
   */

  res.sendStatus(200);
});

app.listen(PORT, () => {
  console.log(`Webhook server listening on ${PORT}`);
});

Environment variable:

ROBLOX_WEBHOOK_SECRET=YOUR_RANDOM_SECRET
15 /

Python Roblox Webhook Verification Example

You can also create a receiver in Python.

Install Flask:

pip install flask

Create:

from flask import Flask
from flask import request

import base64
import hashlib
import hmac
import time

app = Flask(__name__)

WEBHOOK_SECRET = "YOUR_SECRET"

Parse the signature:

def parse_signature(header):

    if not header:
        return None, None

    timestamp = None
    signature = None

    parts = header.split(",")

    for part in parts:

        if part.startswith("t="):
            timestamp = part[2:]

        if part.startswith("v1="):
            signature = part[3:]

    return timestamp, signature

Generate expected signature:

def generate_signature(
    timestamp,
    raw_body
):

    message = (
        timestamp +
        "." +
        raw_body
    )

    digest = hmac.new(
        WEBHOOK_SECRET.encode(),
        message.encode(),
        hashlib.sha256
    ).digest()

    return base64.b64encode(
        digest
    ).decode()

Timestamp check:

def valid_timestamp(timestamp):

    try:
        timestamp = int(timestamp)
    except:
        return False

    now = int(time.time())

    difference = abs(
        now - timestamp
    )

    return difference <= 300

Endpoint:

@app.route(
    "/roblox-webhook",
    methods=["POST"]
)
def roblox_webhook():

    signature_header = (
        request.headers.get(
            "roblox-signature"
        )
    )

    timestamp, signature = (
        parse_signature(
            signature_header
        )
    )

    if not timestamp or not signature:

        return (
            "Missing signature",
            401
        )

    if not valid_timestamp(timestamp):

        return (
            "Expired webhook",
            401
        )

    raw_body = (
        request
        .get_data(
            as_text=True
        )
    )

    expected = generate_signature(
        timestamp,
        raw_body
    )

    if not hmac.compare_digest(
        expected,
        signature
    ):

        return (
            "Invalid signature",
            401
        )

    payload = request.get_json()

    print(
        "Verified webhook:",
        payload
    )

    return "", 200

Run:

if __name__ == "__main__":

    app.run(
        port=3000
    )
16 /

Forward Roblox Webhooks to Discord

Once you've verified a Roblox webhook, you can send a staff notification.

Node example:

async function sendToDiscord(title, message) {
  const webhook = process.env.DISCORD_WEBHOOK;

  await fetch(webhook, {
    method: "POST",

    headers: {
      "Content-Type": "application/json",
    },

    body: JSON.stringify({
      embeds: [
        {
          title,
          description: message,

          timestamp: new Date().toISOString(),
        },
      ],
    }),
  });
}

After verification:

await sendToDiscord("Roblox Webhook", JSON.stringify(req.body, null, 2));

For a fuller Discord setup, read:

Roblox Discord Webhooks Guide

17 /

Store Roblox Webhook Events in a Database

A simplified database structure could be:

webhook_events
---------------------------------
id
event_type
received_at
payload
processed

Example SQL:

CREATE TABLE webhook_events (
    id BIGSERIAL PRIMARY KEY,
    event_type TEXT,
    received_at TIMESTAMP DEFAULT NOW(),
    payload JSONB NOT NULL,
    processed BOOLEAN DEFAULT FALSE
);

Insert:

INSERT INTO webhook_events (
    event_type,
    payload
)
VALUES (
    $1,
    $2
);

Storing the original payload can make debugging easier.

Be careful about retaining personal information unnecessarily.

18 /

Make Roblox Webhook Processing Idempotent

A webhook handler should be able to receive the same event twice without causing a serious problem.

Bad:

Webhook arrives
→ give reward

Webhook arrives again
→ give same reward again

Better:

Webhook arrives
→ check event ID
→ already processed?
   → ignore
→ new?
   → process
   → record ID

Conceptual JavaScript:

if (await eventAlreadyProcessed(event.id)) {
  return res.sendStatus(200);
}

await processEvent(event);

await markEventProcessed(event.id);
19 /

Return a Successful Response Quickly

Your endpoint should avoid waiting for slow tasks.

Bad:

Webhook
→ huge database query
→ external API
→ Discord
→ email
→ analytics
→ response

Better:

Webhook
→ verify
→ queue
→ 200 OK

Worker
→ database
→ Discord
→ email
→ analytics

Example:

app.post("/roblox-webhook", (req, res) => {
  if (!verifyRobloxWebhook(req)) {
    return res.sendStatus(401);
  }

  queue.push(req.body);

  res.sendStatus(200);
});
20 /

Common Roblox Webhook Errors

Roblox Cannot Reach the Endpoint

Make sure your endpoint is publicly accessible.

This will not work:

http://localhost:3000

Roblox cannot access your computer's localhost.

You need something publicly reachable such as:

https://api.example.com/roblox-webhook

Invalid Signature

Common causes include:

  • Wrong secret
  • Incorrect timestamp
  • Modified request body
  • Re-encoding JSON before verification
  • Incorrect Base64 conversion
  • Incorrect HMAC algorithm

Endpoint Takes Too Long

Return a successful response quickly and process heavy work afterwards.

Repeated Events

Design your handlers to be idempotent.

Fake Requests

Never perform important actions before validating the webhook signature.

21 /

Roblox Webhook Security Checklist

Before using a webhook in production:

  • Use HTTPS
  • Add a webhook secret
  • Verify roblox-signature
  • Use HMAC-SHA256
  • Verify the raw request body
  • Compare signatures safely
  • Validate the timestamp
  • Reject old requests
  • Validate the payload
  • Log errors
  • Prevent duplicate processing
  • Store secrets in environment variables
  • Return 2XX quickly
  • Keep sensitive actions server-side
22 /

Roblox Webhooks vs Roblox API Polling

Polling

Your server
→ Roblox API
→ no change

Your server
→ Roblox API
→ no change

Your server
→ Roblox API
→ event found

Webhook

Event happens
→ Roblox contacts you

Webhooks are usually preferable for supported event-driven workflows because you don't need to continually check for changes.

23 /

When Should You Use Roblox Webhooks?

Webhooks make sense when:

  • An external application needs near-real-time information
  • You need automated platform notifications
  • You're connecting Roblox with another service
  • You're creating a creator dashboard
  • You're building automated workflows

For events produced directly by your game server, HttpService may instead be the right tool.

Read:

Complete Roblox Webhooks Guide

Frequently Asked Questions

Does Roblox support webhooks?+

Yes. Roblox provides webhook notifications through its cloud tooling for supported events, alongside HttpService for outbound requests from experiences.

Where are Roblox webhooks configured?+

Supported webhook notifications can be configured through Creator Hub for an experience.

Can Roblox webhooks go to Discord?+

Roblox supports Discord as a webhook destination for supported Creator Hub webhook notifications. You can also receive events at your own endpoint and forward selected information to Discord.

What is roblox-signature?+

roblox-signature allows your custom webhook endpoint to verify that a webhook was generated using the secret configured for the Roblox webhook.

What algorithm does Roblox use for webhook signatures?+

Roblox webhook signature verification uses HMAC with SHA-256 and Base64 encoding of the result.

Why should I verify the webhook timestamp?+

Checking the timestamp helps protect your application against replayed requests.

Can localhost receive Roblox webhooks?+

No. Roblox needs to reach a publicly accessible endpoint.

24 /

More Roblox Webhook Guides