diff --git a/apps/portal/src/app/bridge/webhooks/assets/bridge-webhooks.png b/apps/portal/src/app/bridge/webhooks/assets/bridge-webhooks.png
new file mode 100644
index 00000000000..956787f7a61
Binary files /dev/null and b/apps/portal/src/app/bridge/webhooks/assets/bridge-webhooks.png differ
diff --git a/apps/portal/src/app/bridge/webhooks/page.mdx b/apps/portal/src/app/bridge/webhooks/page.mdx
index 6f2e1c71bb3..f56c55fe978 100644
--- a/apps/portal/src/app/bridge/webhooks/page.mdx
+++ b/apps/portal/src/app/bridge/webhooks/page.mdx
@@ -2,6 +2,7 @@ import {
Callout,
OpenSourceCard,
createMetadata,
+ DocImage,
InstallTabs,
SDKCard,
Grid,
@@ -19,25 +20,31 @@ import {
UnrealEngineIcon,
} from "@/icons";
+import BridgeWebhookScreen from "./assets/bridge-webhooks.png";
+
export const metadata = createMetadata({
image: {
- title: "Payments Webhooks",
+ title: "Bridge Webhooks",
icon: "payments",
},
- title: "Payments Webhooks",
- description: "Learn how to set up and handle webhooks for thirdweb Payments transactions.",
+ title: "Bridge Webhooks",
+ description: "Learn how to set up and handle webhooks for thirdweb Bridge transactions.",
});
-# Webhooks
+# Bridge Webhooks
+
+Create webhooks to be notified on each bridge, swap, and onramp transaction.
-Create webhooks to be notified each time a payment is completed in your app.
+## Dashboard
-## Create a Webhook
+You can create a webhook in your project dashboard by navigating to Bridge > Webhooks tab.
-You can create a webhook in your project dashboard under the Webhooks > Bridge tab. You'll be prompted to copy a secret key before saving the webhook. This will be used for verification on all webhook requests received by your backend.
+You'll be prompted to copy a secret key before saving the webhook. This will be used for verification on all webhook requests received by your backend.
+
-## Parse Webhook Payload with thirdweb SDK
+
+## Parse Webhook Payload
The thirdweb SDK has a built-in function for parsing bridge webhook payloads
@@ -103,7 +110,6 @@ const payload = await Bridge.Webhook.parse(
```
-
## Example Payloads
diff --git a/apps/portal/src/app/tokens/sidebar.tsx b/apps/portal/src/app/tokens/sidebar.tsx
index d62645ba26c..1dd8d9f33e3 100644
--- a/apps/portal/src/app/tokens/sidebar.tsx
+++ b/apps/portal/src/app/tokens/sidebar.tsx
@@ -37,6 +37,10 @@ export const sidebar: SideBar = {
],
name: "Deploy Tokens",
},
+ {
+ href: `${slug}/webhooks`,
+ name: "Webhooks",
+ },
],
name: "Guides",
},
diff --git a/apps/portal/src/app/tokens/webhooks/assets/token-webhooks.png b/apps/portal/src/app/tokens/webhooks/assets/token-webhooks.png
new file mode 100644
index 00000000000..b6d7ef16ff5
Binary files /dev/null and b/apps/portal/src/app/tokens/webhooks/assets/token-webhooks.png differ
diff --git a/apps/portal/src/app/tokens/webhooks/page.mdx b/apps/portal/src/app/tokens/webhooks/page.mdx
new file mode 100644
index 00000000000..825deecad99
--- /dev/null
+++ b/apps/portal/src/app/tokens/webhooks/page.mdx
@@ -0,0 +1,30 @@
+import { DocImage} from "@doc"
+import TokenWebhooksScreen from "./assets/token-webhooks.png"
+
+# Webhooks
+
+You can create webhooks to receive notifications for contract events such as token transfers or state changes or for blockchain transactions on any contract.
+
+With webhooks, you'll receive event data in real-time without making constant requests, reducing RPC load and the risk of missing events.
+
+## Use Cases
+
+Your app may trigger an action when an onchain event occurs, such as:
+
+- ETH or an ERC20 currency is transferred to or from a wallet.
+- A token is minted from your NFT collection.
+- A token in your NFT collection is burned or transferred.
+- Metadata for an oracle contract is updated.
+
+## Create Webhooks
+
+You can configure token webhooks from the dashboard by navigating to your project and selecting Tokens > Webhooks
+
+
+
+
+
+
+
+
+
diff --git a/apps/portal/src/app/wallets/security/page.mdx b/apps/portal/src/app/wallets/security/page.mdx
index 147cffed684..811ead22a37 100644
--- a/apps/portal/src/app/wallets/security/page.mdx
+++ b/apps/portal/src/app/wallets/security/page.mdx
@@ -196,7 +196,7 @@ The response also arrives in the same payload format. We just perform the steps
## Wallets Audits
-View the full audit reports for any wallets related features and specs:
+View the full audit reports for any wallets-related features and specs:
- [7702 (Minimal Account)](https://0xmacro.com/library/audits/thirdweb-22)
- [Managed Account Factory](https://ipfs.io/ipfs/Qmc36VUCuwG2u7kZrqmXmJsH5c8sF7SHySVbPnwVmo3XYX/thirdweb%20A-14%20_%20Macro%20Audits%20_%20The%200xMacro%20Library.pdf)
diff --git a/apps/portal/src/app/webhooks/layout.tsx b/apps/portal/src/app/webhooks/layout.tsx
deleted file mode 100644
index 8acb56172d8..00000000000
--- a/apps/portal/src/app/webhooks/layout.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { createMetadata } from "@/components/Document";
-import { DocLayout } from "@/components/Layouts/DocLayout";
-import { sidebar } from "./sidebar";
-export default async function Layout(props: { children: React.ReactNode }) {
- return (
-
- {props.children}
-
- );
-}
-
-export const metadata = createMetadata({
- description: "Receive real-time updates for onchain and offchain events.",
- image: {
- icon: "webhooks",
- title: "Thirdweb Webhooks",
- },
- title: "thirdweb Webhooks",
-});
diff --git a/apps/portal/src/app/webhooks/page.mdx b/apps/portal/src/app/webhooks/page.mdx
deleted file mode 100644
index 1060567515d..00000000000
--- a/apps/portal/src/app/webhooks/page.mdx
+++ /dev/null
@@ -1,160 +0,0 @@
-import { createMetadata, Details } from "@/components/Document";
-import { DocImage, FeatureCard, OpenSourceCard, Callout } from "@doc";
-import { ArrowLeftRightIcon, UserLockIcon, UsersIcon, WalletIcon } from "lucide-react";
-
-
-export const metadata = createMetadata({
- title: "thirdweb Webhooks",
- description: "Receive real-time updates for onchain and offchain events.",
-});
-
-# Webhooks
-
-Webhooks are a way to receive real-time updates for onchain and offchain events.
-
-An **event** is a real-time update or action that occured for your team or project.
-
-A **webhook** is a configured endpoint on your backend that receives events.
-
-A **topic** is an identifier that groups events (example: `engine.transaction.sent`).
-- The thirdweb product ("engine")
-- The object ("engine.transaction")
-- The event that occurred ("sent")
-
-Each webhook can subscribe to multiple topics.
-
-## Quickstart
-
-1. Configure an HTTP endpoint on your backend to receive events.
-1. Create a webhook in the thirdweb dashboard to subscribe to topics.
-1. (Recommended) Verify the webhook signature to secure your endpoint.
-
-## Manage your webhooks
-
-Manage your webhooks from the thirdweb dashboard.
-
-1. Select your team and project.
-1. Select **Webhooks** in the left sidebar.
-
-### Create a webhook
-
-Select **Create Webhook** to create a new webhook.
-
-Provide the following details:
-- Description: A name for your webhook.
-- Destination URL: The URL to send the webhook to. Only HTTPS URLs are supported.
-- Topics: The thirdweb topics to subscribe this webhook to.
-- Start Paused: Whether the webhook should immediately start receiving events.
-
-### Update or delete a webhook
-
-Find your webhook in the list.
-- Select **... > Edit** to update your webhook details or subscribed topics.
-- Select **... > Delete** to delete it.
-
-### View analytics
-
-Monitor your webhooks' requests over time to identify errors or latency issues.
-
-## Retries
-
-Webhook delivery attempts only consider a **2xx status code** returned within the **10-second timeout** as successful.
-
-Otherwise the delivery will retry multiple times over the next 24 hours with exponential backoff.
-
-### Automatic suspension of failing webhooks
-
-Webhooks experiencing high error rates (non-2xx status codes) sustained over several hours will be paused automatically.
-A paused webhook cannot receive any webhook events until it is manually resumed from the dashboard.
-
-You will be notified via email and a dashboard notification when your webhook is paused.
-
-## Handle webhook events
-
-Your HTTP endpoint should handle webhook events and return a 200 status code quickly. Avoid slow or long-running synchronous operations, or move them to a queue in your backend.
-
-### HTTP format
-
-Webhooks are sent as a `POST` request to your configured endpoint.
-
-**Headers**
-
-- `content-type`: `application/json`
-- `x-webhook-id`: A unique identifier for this webhook
-- `x-webhook-signature`: HMAC-SHA256 signature
- - See *Verify webhook signature* below
-- `x-webhook-timestamp`: Timestamp of delivery attempt in Unix seconds
- - See *Reject expired webhooks* below
-
-**Request body**
-
-```json
-{
- "id": "evt_cllcqqie908ii4q0ugld6noeu",
- "type": "engine.transaction.sent",
- "triggered_at": 1752471197,
- "object": "engine.transaction",
- "data": {
- // ...engine.transaction fields
- },
-}
-```
-
-- `id`: A unique identifier for the event for this topic. Multiple delivery attempts for the same event will use the same ID.
-- `type`: The topic that an event was triggered for.
-- `triggered_at`: The timestamp the event was triggered in Unix seconds.
- - Note: This value does not change for each delivery attempt, but the `x-webhook-timestamp` header does.
-- `object`: The object that defines the shape of `data`.
-- `data`: The object payload for the event.
-
-### Secure your webhook endpoint
-
-The `x-webhook-signature` header is a signature that hashes the raw request body and the delivery timestamp.
-This signature ensures that neither the request body nor the timestamp were modified and can be trusted as sent from thirdweb.
-
-Follow these steps to verify the webhook signature:
-1. Concatenate `{TIMESTAMP_IN_UNIX_SECONDS}.{REQUEST_JSON_BODY}`.
-1. Hash the result with the webhook secret using SHA256.
-1. Compare the result with the `x-webhook-signature` header.
-
-**Code examples**
-
-
-```typescript
-import { createHmac, timingSafeEqual } from "crypto";
-
-const webhookSecret = "whsecret_..."; // Your webhook secret from the dashboard
-const actualSignature = req.headers["x-webhook-signature"];
-const timestamp = req.headers["x-webhook-timestamp"];
-const body = "..." // raw HTTP body as string
-
-// Generate the expected signature.
-const expectedSignature = createHmac("sha256", webhookSecret)
- .update(`${timestamp}.${body}`)
- .digest("hex");
-
-// Use `timingSafeEqual` to compare the signatures safely.
-const expected = Buffer.from(expectedSignature, "hex");
-const actual = Buffer.from(actualSignature, "hex");
-const isValidSignature =
- expected.length === actual.length &&
- timingSafeEqual(expected, actual);
-
-if (!isValidSignature) {
- throw new Error("Invalid webhook signature");
-}
-```
-
-
-### Reject expired webhooks (optional)
-
-You can reject webhook attempts that are received after a certain duration. This prevents requests from being replayed.
-
-```typescript
-const MAX_AGE_SECONDS = 10 * 60 * 1000; // 10 minutes
-const timestamp = req.headers["x-webhook-timestamp"];
-if (Date.now() / 1000 - timestamp > MAX_AGE_SECONDS) {
- throw new Error("Webhook expired");
-}
-```
-
diff --git a/apps/portal/src/app/webhooks/sidebar.tsx b/apps/portal/src/app/webhooks/sidebar.tsx
deleted file mode 100644
index 56bcdb2a51b..00000000000
--- a/apps/portal/src/app/webhooks/sidebar.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { WebhookIcon } from "lucide-react";
-import type { SideBar } from "@/components/Layouts/DocLayout";
-
-export const sidebar: SideBar = {
- links: [
- {
- href: "/webhooks",
- icon: ,
- name: "Overview",
- },
- ],
- name: "Webhooks",
-};