diff --git a/.optimize-cache.json b/.optimize-cache.json index 1d2039ef0d..f6a965f06a 100644 --- a/.optimize-cache.json +++ b/.optimize-cache.json @@ -194,6 +194,7 @@ "images/blog/announcing-timestamp-overrides/cover.png": "5bfc2ba16b8ca4a82188c0f67b300ed0a7f38b4abc04b06a10ee52b2832fa65b", "images/blog/announcing-transactions-api/cover.png": "604a7721b7bf0a752460a721ffcaff10598abc5f398e7b16a8a58195c2ebf7ea", "images/blog/announcing-user-impersonation/cover.png": "0d637f5d3a748e033945741a9e926a001b84c51854077ac5de4fedbc8801a40a", + "images/blog/announcing-webhooks-api/cover.png": "a029dca7fc776d3d81ec0502a8fe3fb0d3e170e474a515b4ad9d61c9854ca745", "images/blog/apply-appwrite-how/cover.png": "d23f45ced245b42c8712c021f5d2068c17aebd94fd049cb90222cb9647a41a4a", "images/blog/appwrite-1-8-0-self-hosted-release/cover.png": "c15a9d88ccd16c2dc8333dc74e715e1f4a6c7818d3b4a05f4d68342eacdc0523", "images/blog/appwrite-1-8-1-self-hosted-release/cover.png": "82f0a396c56b6b299b24133079acc6a317c66b2bf02fd91f4862bd3be0f8f373", @@ -1400,8 +1401,10 @@ "images/docs/network/regions-map.png": "c65f1423ab19c3048bf8bf93117e8f2e1d13a2bc705c00307de7ee821e5668a1", "images/docs/platform/add-platform.png": "5a05bb9d75a8d5270bfa5e67df7e6de20a9fad174476a112b5bdab72e7bdad30", "images/docs/platform/create-api-key.png": "36a80b363e6ba8ebd271e830a3b2d0bc766b2ec3e7d46ff481516f1e50ea5b7d", + "images/docs/platform/create-webhook.png": "77e08173da6ac534524e025433cf75e532d853a135944a7c6ba2278357d88b2d", "images/docs/platform/dark/add-platform.png": "1bb0e7dba22556e64064951882d625532285fa80bed43fd77774f31545a15b0f", "images/docs/platform/dark/create-api-key.png": "dbc3ce919f849d09ef7789676d00e954bf364b9b23126b551767b86891c83fb2", + "images/docs/platform/dark/create-webhook.png": "88f7f5c857fe986b5803c7df003c4db55faa79e3fa3135cf9491073d0b2736be", "images/docs/platform/dark/execution-details.png": "c0481ddc206447460f9d317ba8d421615066f67a50bc9ef41a8f71766ecffb14", "images/docs/platform/execution-details.png": "ece1364b8b00254bbd982421b6eed6d7f519d34c4e80377fcaaa4cb5d5dd3f89", "images/docs/quick-starts/add-platform.png": "3b13ba983ea1d2529a1f34a719acef903ec0b58879ed511012280a28ccbde17e", diff --git a/src/routes/blog/post/announcing-webhooks-api/+page.markdoc b/src/routes/blog/post/announcing-webhooks-api/+page.markdoc new file mode 100644 index 0000000000..0f8333ca8d --- /dev/null +++ b/src/routes/blog/post/announcing-webhooks-api/+page.markdoc @@ -0,0 +1,602 @@ +--- +layout: post +title: "Announcing the Webhooks API: Manage webhooks programmatically with Server SDKs" +description: Webhooks are no longer console-only. Create, update, and delete webhooks using the Appwrite Server SDKs and API keys with the new webhooks.read and webhooks.write scopes. +date: 2026-04-16 +cover: /images/blog/announcing-webhooks-api/cover.png +timeToRead: 4 +author: matej-baco +category: announcement +featured: false +callToAction: true +--- + +Webhooks have always been a core part of how developers integrate Appwrite into their workflows. Subscribe to an event, receive an HTTP POST, and let your server handle the rest. Until now, managing those webhooks meant logging into the Appwrite Console and configuring them by hand. + +Today, we are announcing the **Webhooks API**, a fully programmable interface for managing webhooks through Server SDKs and API keys. + +# Why this matters + +If you have ever needed to set up webhooks across multiple projects, automate webhook provisioning as part of a deployment pipeline, or manage webhook lifecycles from code, you know the friction of doing it through a UI. + +The Webhooks API removes that friction entirely. Webhooks become just another resource you can create, update, and delete programmatically, the same way you already manage databases, functions, and storage buckets. + +This is especially valuable for: + +- **CI/CD pipelines** that need to register webhooks as part of environment setup +- **Multi-tenant platforms** that provision webhooks per customer or workspace +- **Migration and seeding scripts** that replicate webhook configurations across environments +- **Admin dashboards** that let non-technical team members manage webhooks without Console access + +# How it works + +The Webhooks API introduces two new [API key scopes](/docs/advanced/platform/api-keys#scopes): + +- **`webhooks.read`** for listing and retrieving webhooks +- **`webhooks.write`** for creating, updating, and deleting webhooks + +Once your API key has the appropriate scopes, you can manage webhooks through any Appwrite Server SDK. + +## Create a webhook + +{% multicode %} +```server-nodejs +import { Client, Webhooks, ID } from 'node-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const webhooks = new Webhooks(client); + +const result = await webhooks.create({ + webhookId: ID.unique(), + url: 'https://example.com/webhook', + name: 'My Webhook', + events: ['users.*.create', 'users.*.update'], + tls: true +}); +``` +```server-deno +import { Client, Webhooks, ID } from "npm:node-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const webhooks = new Webhooks(client); + +const result = await webhooks.create({ + webhookId: ID.unique(), + url: 'https://example.com/webhook', + name: 'My Webhook', + events: ['users.*.create', 'users.*.update'], + tls: true +}); +``` +```server-php +setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setKey(''); + +$webhooks = new Webhooks($client); + +$result = $webhooks->create( + webhookId: ID::unique(), + url: 'https://example.com/webhook', + name: 'My Webhook', + events: ['users.*.create', 'users.*.update'], + tls: true +); +``` +```server-python +from appwrite.client import Client +from appwrite.id import ID +from appwrite.services.webhooks import Webhooks + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_key('') + +webhooks = Webhooks(client) + +result = webhooks.create( + webhook_id=ID.unique(), + url='https://example.com/webhook', + name='My Webhook', + events=['users.*.create', 'users.*.update'], + tls=True +) +``` +```server-ruby +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_key('') + +webhooks = Webhooks.new(client) + +response = webhooks.create( + webhook_id: ID.unique(), + url: 'https://example.com/webhook', + name: 'My Webhook', + events: ['users.*.create', 'users.*.update'], + tls: true +) +``` +```server-dotnet +using Appwrite; +using Appwrite.Services; +using Appwrite.Models; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetKey(""); + +Webhooks webhooks = new Webhooks(client); + +var result = await webhooks.Create( + webhookId: ID.Unique(), + url: "https://example.com/webhook", + name: "My Webhook", + events: new List {"users.*.create", "users.*.update"}, + tls: true +); +``` +```server-dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +Webhooks webhooks = Webhooks(client); + +final result = await webhooks.create( + webhookId: ID.unique(), + url: 'https://example.com/webhook', + name: 'My Webhook', + events: ['users.*.create', 'users.*.update'], + tls: true, +); +``` +```server-kotlin +import io.appwrite.Client +import io.appwrite.ID +import io.appwrite.services.Webhooks + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") + +val webhooks = Webhooks(client) + +val result = webhooks.create( + webhookId = ID.unique(), + url = "https://example.com/webhook", + name = "My Webhook", + events = listOf("users.*.create", "users.*.update"), + tls = true +) +``` +```server-java +import io.appwrite.Client; +import io.appwrite.ID; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Webhooks; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey(""); + +Webhooks webhooks = new Webhooks(client); + +webhooks.create( + ID.unique(), // webhookId + "https://example.com/webhook", // url + "My Webhook", // name + List.of("users.*.create", "users.*.update"), // events + true, // enabled + true, // tls + null, // authUsername + null, // authPassword + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + System.out.println(result); + }) +); +``` +```server-swift +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") + +let webhooks = Webhooks(client) + +let result = try await webhooks.create( + webhookId: ID.unique(), + url: "https://example.com/webhook", + name: "My Webhook", + events: ["users.*.create", "users.*.update"], + tls: true +) +``` +```server-go +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/appwrite" + "github.com/appwrite/sdk-for-go/id" +) + +func main() { + client := appwrite.NewClient( + appwrite.WithEndpoint("https://.cloud.appwrite.io/v1"), + appwrite.WithProject(""), + appwrite.WithKey(""), + ) + + webhooks := appwrite.NewWebhooks(client) + result, err := webhooks.Create( + id.Unique(), + "https://example.com/webhook", + "My Webhook", + []string{"users.*.create", "users.*.update"}, + ) + + if err != nil { + panic(err) + } + + fmt.Println(result) +} +``` +```server-rust +use appwrite::Client; +use appwrite::id::ID; +use appwrite::services::Webhooks; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new() + .set_endpoint("https://.cloud.appwrite.io/v1") + .set_project("") + .set_key(""); + + let webhooks = Webhooks::new(&client); + + let result = webhooks.create( + ID::unique(), // webhook_id + "https://example.com/webhook", // url + "My Webhook", // name + &["users.*.create", "users.*.update"], // events + None, // enabled + Some(true), // tls + None, // auth_username + None, // auth_password + ).await?; + + println!("{:?}", result); + Ok(()) +} +``` +{% /multicode %} + +## Update a webhook + +{% multicode %} +```server-nodejs +const result = await webhooks.update({ + webhookId: '', + name: 'Updated Webhook', + url: 'https://example.com/webhook-v2', + events: ['users.*.create', 'users.*.update', 'users.*.delete'], + tls: true +}); +``` +```server-deno +const result = await webhooks.update({ + webhookId: '', + name: 'Updated Webhook', + url: 'https://example.com/webhook-v2', + events: ['users.*.create', 'users.*.update', 'users.*.delete'], + tls: true +}); +``` +```server-php +$result = $webhooks->update( + webhookId: '', + name: 'Updated Webhook', + url: 'https://example.com/webhook-v2', + events: ['users.*.create', 'users.*.update', 'users.*.delete'], + tls: true +); +``` +```server-python +result = webhooks.update( + webhook_id='', + name='Updated Webhook', + url='https://example.com/webhook-v2', + events=['users.*.create', 'users.*.update', 'users.*.delete'], + tls=True +) +``` +```server-ruby +response = webhooks.update( + webhook_id: '', + name: 'Updated Webhook', + url: 'https://example.com/webhook-v2', + events: ['users.*.create', 'users.*.update', 'users.*.delete'], + tls: true +) +``` +```server-dotnet +var result = await webhooks.Update( + webhookId: "", + name: "Updated Webhook", + url: "https://example.com/webhook-v2", + events: new List {"users.*.create", "users.*.update", "users.*.delete"}, + tls: true +); +``` +```server-dart +final result = await webhooks.update( + webhookId: '', + name: 'Updated Webhook', + url: 'https://example.com/webhook-v2', + events: ['users.*.create', 'users.*.update', 'users.*.delete'], + tls: true, +); +``` +```server-kotlin +val result = webhooks.update( + webhookId = "", + name = "Updated Webhook", + url = "https://example.com/webhook-v2", + events = listOf("users.*.create", "users.*.update", "users.*.delete"), + tls = true +) +``` +```server-java +webhooks.update( + "", // webhookId + "Updated Webhook", // name + "https://example.com/webhook-v2", // url + List.of("users.*.create", "users.*.update", "users.*.delete"), // events + true, // enabled + true, // tls + null, // authUsername + null, // authPassword + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + System.out.println(result); + }) +); +``` +```server-swift +let result = try await webhooks.update( + webhookId: "", + name: "Updated Webhook", + url: "https://example.com/webhook-v2", + events: ["users.*.create", "users.*.update", "users.*.delete"], + tls: true +) +``` +```server-go +result, err := webhooks.Update( + "", + "Updated Webhook", + "https://example.com/webhook-v2", + []string{"users.*.create", "users.*.update", "users.*.delete"}, +) +``` +```server-rust +let result = webhooks.update( + "", // webhook_id + "Updated Webhook", // name + "https://example.com/webhook-v2", // url + &["users.*.create", "users.*.update", "users.*.delete"], // events + None, // enabled + Some(true), // tls + None, // auth_username + None, // auth_password +).await?; +``` +{% /multicode %} + +## Delete a webhook + +{% multicode %} +```server-nodejs +await webhooks.delete({ + webhookId: '' +}); +``` +```server-deno +await webhooks.delete({ + webhookId: '' +}); +``` +```server-php +$webhooks->delete( + webhookId: '' +); +``` +```server-python +webhooks.delete( + webhook_id='' +) +``` +```server-ruby +webhooks.delete( + webhook_id: '' +) +``` +```server-dotnet +await webhooks.Delete( + webhookId: "" +); +``` +```server-dart +await webhooks.delete( + webhookId: '', +); +``` +```server-kotlin +webhooks.delete( + webhookId = "" +) +``` +```server-java +webhooks.delete( + "", // webhookId + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + System.out.println(result); + }) +); +``` +```server-swift +try await webhooks.delete( + webhookId: "" +) +``` +```server-go +_, err := webhooks.Delete( + "", +) +``` +```server-rust +webhooks.delete( + "", +).await?; +``` +{% /multicode %} + +## Rotate signing key + +{% multicode %} +```server-nodejs +const result = await webhooks.updateSecret({ + webhookId: '' +}); +``` +```server-deno +const result = await webhooks.updateSecret({ + webhookId: '' +}); +``` +```server-php +$result = $webhooks->updateSecret( + webhookId: '' +); +``` +```server-python +result = webhooks.update_secret( + webhook_id='' +) +``` +```server-ruby +response = webhooks.update_secret( + webhook_id: '' +) +``` +```server-dotnet +var result = await webhooks.UpdateSecret( + webhookId: "" +); +``` +```server-dart +final result = await webhooks.updateSecret( + webhookId: '', +); +``` +```server-kotlin +val result = webhooks.updateSecret( + webhookId = "" +) +``` +```server-java +webhooks.updateSecret( + "", // webhookId + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + System.out.println(result); + }) +); +``` +```server-swift +let result = try await webhooks.updateSecret( + webhookId: "" +) +``` +```server-go +result, err := webhooks.UpdateSecret( + "", +) +``` +```server-rust +let result = webhooks.update_secret( + "", // webhook_id +).await?; +``` +{% /multicode %} + +The API supports all the same configuration options available in the Console, including event selection, SSL/TLS certificate verification, and HTTP basic authentication. + +{% info title="Update your SDK" %} +The Webhooks API requires the latest version of your Appwrite Server SDK. Make sure to update before using the new `Webhooks` service. +{% /info %} + +# Get started + +The Webhooks API is available on **Appwrite Cloud** today. + +1. Navigate to **Overview** > **Integration** > **API keys** and create or update an API key with the `webhooks.read` and `webhooks.write` scopes. +2. Initialize a Server SDK with your API key. +3. Use the `Webhooks` service to manage your webhooks from code. + +Full documentation, including examples for all supported SDKs, is available on the [webhooks documentation page](/docs/advanced/platform/webhooks). + +# Resources + +- [Webhooks documentation](/docs/advanced/platform/webhooks) +- [API keys and scopes](/docs/advanced/platform/api-keys) +- [Webhook events reference](/docs/advanced/platform/events) +- [Appwrite Webhooks: triggering events the right way](/blog/post/appwrite-webhooks) diff --git a/src/routes/changelog/(entries)/2026-04-10.markdoc b/src/routes/changelog/(entries)/2026-04-10.markdoc new file mode 100644 index 0000000000..215cb298ef --- /dev/null +++ b/src/routes/changelog/(entries)/2026-04-10.markdoc @@ -0,0 +1,14 @@ +--- +layout: changelog +title: "Webhooks API: manage webhooks with Server SDKs" +date: 2026-04-10 +cover: /images/blog/announcing-webhooks-api/cover.png +--- + +Webhooks can now be created, updated, and deleted programmatically using the Appwrite Server SDKs. Two new API key scopes, `webhooks.read` and `webhooks.write`, control access to the new endpoints. + +This makes it possible to provision webhooks as part of CI/CD pipelines, migration scripts, or any workflow where managing webhooks through the Console is not practical. + +{% arrow_link href="/blog/post/announcing-webhooks-api" %} +Read the announcement +{% /arrow_link %} diff --git a/src/routes/docs/advanced/platform/api-keys/+page.markdoc b/src/routes/docs/advanced/platform/api-keys/+page.markdoc index 5faf72dbd4..f35c07055e 100644 --- a/src/routes/docs/advanced/platform/api-keys/+page.markdoc +++ b/src/routes/docs/advanced/platform/api-keys/+page.markdoc @@ -195,6 +195,8 @@ If you need to replace your API Key, create a new key, update your app credentia | `subscribers.write` | Access to create, update, and delete your project's subscribers | | `targets.read` | Access to read your project's targets | | `targets.write` | Access to create, update, and delete your project's targets | +| `webhooks.read` | Access to read your project's webhooks | +| `webhooks.write` | Access to create, update, and delete your project's webhooks | | `rules.read` | Access to read your project's proxy rules | | `rules.write` | Access to create, update, and delete your project's proxy rules | | `migrations.read` | Access to read your project's migrations | diff --git a/src/routes/docs/advanced/platform/webhooks/+page.markdoc b/src/routes/docs/advanced/platform/webhooks/+page.markdoc index e8bf8902ca..95299d3d51 100644 --- a/src/routes/docs/advanced/platform/webhooks/+page.markdoc +++ b/src/routes/docs/advanced/platform/webhooks/+page.markdoc @@ -8,7 +8,1422 @@ Webhooks allow you to build or set up integrations which subscribe to certain ev # Getting started {% #getting-started %} -You can set your webhook by adding it from your Appwrite project dashboard. You can access your webhooks settings from your project dashboard or on the left navigation panel. Click the 'Add Webhook' button and choose your webhook name and the events that should trigger it. You can also set an optional basic HTTP authentication username and password to protect your endpoint from unauthorized access. +To add a webhook from the Appwrite Console: + +1. Navigate to your project's **Settings** page. +2. Select the **Webhooks** tab. +3. Click **Create webhook**. +4. Enter a **Name** and the **POST URL** for your webhook endpoint. +5. Click **Add an event** to choose the [events](#events) that should trigger the webhook. +6. Optionally, enable **Certificate verification (SSL/TLS)** and set **HTTP authentication** credentials to secure your endpoint. +7. Click **Create webhook**. + +{% only_dark %} +![Create webhook screen](/images/docs/platform/dark/create-webhook.png) +{% /only_dark %} +{% only_light %} +![Create webhook screen](/images/docs/platform/create-webhook.png) +{% /only_light %} + +# Manage webhooks with a Server SDK {% #manage-webhooks-with-a-server-sdk %} + +You can also manage webhooks programmatically using a Server SDK. This requires an API key with the `webhooks.read` and `webhooks.write` [scopes](/docs/advanced/platform/api-keys#scopes). + +## Create a webhook {% #create-a-webhook %} + +{% multicode %} +```server-nodejs +import { Client, Webhooks, ID } from 'node-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const webhooks = new Webhooks(client); + +const result = await webhooks.create({ + webhookId: ID.unique(), + url: 'https://example.com/webhook', + name: 'My Webhook', + events: ['users.*.create'], + tls: true +}); +``` +```server-deno +import { Client, Webhooks, ID } from "npm:node-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const webhooks = new Webhooks(client); + +const result = await webhooks.create({ + webhookId: ID.unique(), + url: 'https://example.com/webhook', + name: 'My Webhook', + events: ['users.*.create'], + tls: true +}); +``` +```server-php +setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setKey(''); + +$webhooks = new Webhooks($client); + +$result = $webhooks->create( + webhookId: ID::unique(), + url: 'https://example.com/webhook', + name: 'My Webhook', + events: ['users.*.create'], + tls: true +); +``` +```server-python +from appwrite.client import Client +from appwrite.id import ID +from appwrite.services.webhooks import Webhooks + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_key('') + +webhooks = Webhooks(client) + +result = webhooks.create( + webhook_id=ID.unique(), + url='https://example.com/webhook', + name='My Webhook', + events=['users.*.create'], + tls=True +) +``` +```server-ruby +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_key('') + +webhooks = Webhooks.new(client) + +response = webhooks.create( + webhook_id: ID.unique(), + url: 'https://example.com/webhook', + name: 'My Webhook', + events: ['users.*.create'], + tls: true +) +``` +```server-dotnet +using Appwrite; +using Appwrite.Services; +using Appwrite.Models; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetKey(""); + +Webhooks webhooks = new Webhooks(client); + +var result = await webhooks.Create( + webhookId: ID.Unique(), + url: "https://example.com/webhook", + name: "My Webhook", + events: new List {"users.*.create"}, + tls: true +); +``` +```server-dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +Webhooks webhooks = Webhooks(client); + +final result = await webhooks.create( + webhookId: ID.unique(), + url: 'https://example.com/webhook', + name: 'My Webhook', + events: ['users.*.create'], + tls: true, +); +``` +```server-kotlin +import io.appwrite.Client +import io.appwrite.ID +import io.appwrite.services.Webhooks + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") + +val webhooks = Webhooks(client) + +val result = webhooks.create( + webhookId = ID.unique(), + url = "https://example.com/webhook", + name = "My Webhook", + events = listOf("users.*.create"), + tls = true +) +``` +```server-java +import io.appwrite.Client; +import io.appwrite.ID; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Webhooks; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey(""); + +Webhooks webhooks = new Webhooks(client); + +webhooks.create( + ID.unique(), // webhookId + "https://example.com/webhook", // url + "My Webhook", // name + List.of("users.*.create"), // events + true, // enabled + true, // tls + null, // authUsername + null, // authPassword + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + System.out.println(result); + }) +); +``` +```server-swift +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") + +let webhooks = Webhooks(client) + +let result = try await webhooks.create( + webhookId: ID.unique(), + url: "https://example.com/webhook", + name: "My Webhook", + events: ["users.*.create"], + tls: true +) +``` +```server-go +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/appwrite" + "github.com/appwrite/sdk-for-go/id" +) + +func main() { + client := appwrite.NewClient( + appwrite.WithEndpoint("https://.cloud.appwrite.io/v1"), + appwrite.WithProject(""), + appwrite.WithKey(""), + ) + + webhooks := appwrite.NewWebhooks(client) + result, err := webhooks.Create( + id.Unique(), + "https://example.com/webhook", + "My Webhook", + []string{"users.*.create"}, + ) + + if err != nil { + panic(err) + } + + fmt.Println(result) +} +``` +```server-rust +use appwrite::Client; +use appwrite::id::ID; +use appwrite::services::Webhooks; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new() + .set_endpoint("https://.cloud.appwrite.io/v1") + .set_project("") + .set_key(""); + + let webhooks = Webhooks::new(&client); + + let result = webhooks.create( + ID::unique(), // webhook_id + "https://example.com/webhook", // url + "My Webhook", // name + &["users.*.create"], // events + Some(true), // enabled + Some(true), // tls + None, // auth_username + None, // auth_password + ).await?; + + println!("{:?}", result); + Ok(()) +} +``` +{% /multicode %} + +## List webhooks {% #list-webhooks %} + +{% multicode %} +```server-nodejs +import { Client, Webhooks } from 'node-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const webhooks = new Webhooks(client); + +const result = await webhooks.list({ + queries: [], // optional + total: false // optional +}); +``` +```server-deno +import { Client, Webhooks } from "npm:node-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const webhooks = new Webhooks(client); + +const result = await webhooks.list({ + queries: [], // optional + total: false // optional +}); +``` +```server-php +setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setKey(''); + +$webhooks = new Webhooks($client); + +$result = $webhooks->list( + queries: [], // optional + total: false // optional +); +``` +```server-python +from appwrite.client import Client +from appwrite.services.webhooks import Webhooks + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_key('') + +webhooks = Webhooks(client) + +result = webhooks.list( + queries=[], # optional + total=False # optional +) +``` +```server-ruby +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_key('') + +webhooks = Webhooks.new(client) + +response = webhooks.list( + queries: [], # optional + total: false # optional +) +``` +```server-dotnet +using Appwrite; +using Appwrite.Services; +using Appwrite.Models; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetKey(""); + +Webhooks webhooks = new Webhooks(client); + +var result = await webhooks.List( + queries: new List(), // optional + total: false // optional +); +``` +```server-dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +Webhooks webhooks = Webhooks(client); + +final result = await webhooks.list( + queries: [], // optional + total: false, // optional +); +``` +```server-kotlin +import io.appwrite.Client +import io.appwrite.services.Webhooks + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") + +val webhooks = Webhooks(client) + +val result = webhooks.list( + queries = listOf(), // optional + total = false // optional +) +``` +```server-java +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Webhooks; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey(""); + +Webhooks webhooks = new Webhooks(client); + +webhooks.list( + List.of(), // queries (optional) + false, // total (optional) + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + System.out.println(result); + }) +); +``` +```server-swift +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") + +let webhooks = Webhooks(client) + +let result = try await webhooks.list( + queries: [], // optional + total: false // optional +) +``` +```server-go +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/appwrite" +) + +func main() { + client := appwrite.NewClient( + appwrite.WithEndpoint("https://.cloud.appwrite.io/v1"), + appwrite.WithProject(""), + appwrite.WithKey(""), + ) + + webhooks := appwrite.NewWebhooks(client) + result, err := webhooks.List() + + if err != nil { + panic(err) + } + + fmt.Println(result) +} +``` +```server-rust +use appwrite::Client; +use appwrite::services::Webhooks; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new() + .set_endpoint("https://.cloud.appwrite.io/v1") + .set_project("") + .set_key(""); + + let webhooks = Webhooks::new(&client); + + let result = webhooks.list( + Some(vec![]), // queries (optional) + Some(false), // total (optional) + ).await?; + + println!("{:?}", result); + Ok(()) +} +``` +{% /multicode %} + +## Get a webhook {% #get-a-webhook %} + +{% multicode %} +```server-nodejs +import { Client, Webhooks } from 'node-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const webhooks = new Webhooks(client); + +const result = await webhooks.get({ + webhookId: '' +}); +``` +```server-deno +import { Client, Webhooks } from "npm:node-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const webhooks = new Webhooks(client); + +const result = await webhooks.get({ + webhookId: '' +}); +``` +```server-php +setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setKey(''); + +$webhooks = new Webhooks($client); + +$result = $webhooks->get( + webhookId: '' +); +``` +```server-python +from appwrite.client import Client +from appwrite.services.webhooks import Webhooks + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_key('') + +webhooks = Webhooks(client) + +result = webhooks.get( + webhook_id='' +) +``` +```server-ruby +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_key('') + +webhooks = Webhooks.new(client) + +response = webhooks.get( + webhook_id: '' +) +``` +```server-dotnet +using Appwrite; +using Appwrite.Services; +using Appwrite.Models; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetKey(""); + +Webhooks webhooks = new Webhooks(client); + +var result = await webhooks.Get( + webhookId: "" +); +``` +```server-dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +Webhooks webhooks = Webhooks(client); + +final result = await webhooks.get( + webhookId: '', +); +``` +```server-kotlin +import io.appwrite.Client +import io.appwrite.services.Webhooks + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") + +val webhooks = Webhooks(client) + +val result = webhooks.get( + webhookId = "" +) +``` +```server-java +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Webhooks; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey(""); + +Webhooks webhooks = new Webhooks(client); + +webhooks.get( + "", // webhookId + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + System.out.println(result); + }) +); +``` +```server-swift +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") + +let webhooks = Webhooks(client) + +let result = try await webhooks.get( + webhookId: "" +) +``` +```server-go +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/appwrite" +) + +func main() { + client := appwrite.NewClient( + appwrite.WithEndpoint("https://.cloud.appwrite.io/v1"), + appwrite.WithProject(""), + appwrite.WithKey(""), + ) + + webhooks := appwrite.NewWebhooks(client) + result, err := webhooks.Get( + "", + ) + + if err != nil { + panic(err) + } + + fmt.Println(result) +} +``` +```server-rust +use appwrite::Client; +use appwrite::services::Webhooks; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new() + .set_endpoint("https://.cloud.appwrite.io/v1") + .set_project("") + .set_key(""); + + let webhooks = Webhooks::new(&client); + + let result = webhooks.get( + "", // webhook_id + ).await?; + + println!("{:?}", result); + Ok(()) +} +``` +{% /multicode %} + +## Update a webhook {% #update-a-webhook %} + +{% multicode %} +```server-nodejs +import { Client, Webhooks } from 'node-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const webhooks = new Webhooks(client); + +const result = await webhooks.update({ + webhookId: '', + name: 'Updated Webhook', + url: 'https://example.com/webhook-updated', + events: ['users.*.update'], + tls: true +}); +``` +```server-deno +import { Client, Webhooks } from "npm:node-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const webhooks = new Webhooks(client); + +const result = await webhooks.update({ + webhookId: '', + name: 'Updated Webhook', + url: 'https://example.com/webhook-updated', + events: ['users.*.update'], + tls: true +}); +``` +```server-php +setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setKey(''); + +$webhooks = new Webhooks($client); + +$result = $webhooks->update( + webhookId: '', + name: 'Updated Webhook', + url: 'https://example.com/webhook-updated', + events: ['users.*.update'], + tls: true +); +``` +```server-python +from appwrite.client import Client +from appwrite.services.webhooks import Webhooks + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_key('') + +webhooks = Webhooks(client) + +result = webhooks.update( + webhook_id='', + name='Updated Webhook', + url='https://example.com/webhook-updated', + events=['users.*.update'], + tls=True +) +``` +```server-ruby +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_key('') + +webhooks = Webhooks.new(client) + +response = webhooks.update( + webhook_id: '', + name: 'Updated Webhook', + url: 'https://example.com/webhook-updated', + events: ['users.*.update'], + tls: true +) +``` +```server-dotnet +using Appwrite; +using Appwrite.Services; +using Appwrite.Models; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetKey(""); + +Webhooks webhooks = new Webhooks(client); + +var result = await webhooks.Update( + webhookId: "", + name: "Updated Webhook", + url: "https://example.com/webhook-updated", + events: new List {"users.*.update"}, + tls: true +); +``` +```server-dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +Webhooks webhooks = Webhooks(client); + +final result = await webhooks.update( + webhookId: '', + name: 'Updated Webhook', + url: 'https://example.com/webhook-updated', + events: ['users.*.update'], + tls: true, +); +``` +```server-kotlin +import io.appwrite.Client +import io.appwrite.services.Webhooks + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") + +val webhooks = Webhooks(client) + +val result = webhooks.update( + webhookId = "", + name = "Updated Webhook", + url = "https://example.com/webhook-updated", + events = listOf("users.*.update"), + tls = true +) +``` +```server-java +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Webhooks; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey(""); + +Webhooks webhooks = new Webhooks(client); + +webhooks.update( + "", // webhookId + "Updated Webhook", // name + "https://example.com/webhook-updated", // url + List.of("users.*.update"), // events + true, // enabled + true, // tls + null, // authUsername + null, // authPassword + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + System.out.println(result); + }) +); +``` +```server-swift +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") + +let webhooks = Webhooks(client) + +let result = try await webhooks.update( + webhookId: "", + name: "Updated Webhook", + url: "https://example.com/webhook-updated", + events: ["users.*.update"], + tls: true +) +``` +```server-go +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/appwrite" +) + +func main() { + client := appwrite.NewClient( + appwrite.WithEndpoint("https://.cloud.appwrite.io/v1"), + appwrite.WithProject(""), + appwrite.WithKey(""), + ) + + webhooks := appwrite.NewWebhooks(client) + result, err := webhooks.Update( + "", + "Updated Webhook", + "https://example.com/webhook-updated", + []string{"users.*.update"}, + ) + + if err != nil { + panic(err) + } + + fmt.Println(result) +} +``` +```server-rust +use appwrite::Client; +use appwrite::services::Webhooks; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new() + .set_endpoint("https://.cloud.appwrite.io/v1") + .set_project("") + .set_key(""); + + let webhooks = Webhooks::new(&client); + + let result = webhooks.update( + "", // webhook_id + "Updated Webhook", // name + "https://example.com/webhook-updated", // url + &["users.*.update"], // events + Some(true), // enabled + Some(true), // tls + None, // auth_username + None, // auth_password + ).await?; + + println!("{:?}", result); + Ok(()) +} +``` +{% /multicode %} + +## Delete a webhook {% #delete-a-webhook %} + +{% multicode %} +```server-nodejs +import { Client, Webhooks } from 'node-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const webhooks = new Webhooks(client); + +await webhooks.delete({ + webhookId: '' +}); +``` +```server-deno +import { Client, Webhooks } from "npm:node-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const webhooks = new Webhooks(client); + +await webhooks.delete({ + webhookId: '' +}); +``` +```server-php +setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setKey(''); + +$webhooks = new Webhooks($client); + +$webhooks->delete( + webhookId: '' +); +``` +```server-python +from appwrite.client import Client +from appwrite.services.webhooks import Webhooks + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_key('') + +webhooks = Webhooks(client) + +webhooks.delete( + webhook_id='' +) +``` +```server-ruby +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_key('') + +webhooks = Webhooks.new(client) + +webhooks.delete( + webhook_id: '' +) +``` +```server-dotnet +using Appwrite; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetKey(""); + +Webhooks webhooks = new Webhooks(client); + +await webhooks.Delete( + webhookId: "" +); +``` +```server-dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +Webhooks webhooks = Webhooks(client); + +await webhooks.delete( + webhookId: '', +); +``` +```server-kotlin +import io.appwrite.Client +import io.appwrite.services.Webhooks + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") + +val webhooks = Webhooks(client) + +webhooks.delete( + webhookId = "" +) +``` +```server-java +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Webhooks; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey(""); + +Webhooks webhooks = new Webhooks(client); + +webhooks.delete( + "", // webhookId + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + System.out.println(result); + }) +); +``` +```server-swift +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") + +let webhooks = Webhooks(client) + +try await webhooks.delete( + webhookId: "" +) +``` +```server-go +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/appwrite" +) + +func main() { + client := appwrite.NewClient( + appwrite.WithEndpoint("https://.cloud.appwrite.io/v1"), + appwrite.WithProject(""), + appwrite.WithKey(""), + ) + + webhooks := appwrite.NewWebhooks(client) + _, err := webhooks.Delete( + "", + ) + + if err != nil { + panic(err) + } + + fmt.Println("Webhook deleted") +} +``` +```server-rust +use appwrite::Client; +use appwrite::services::Webhooks; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new() + .set_endpoint("https://.cloud.appwrite.io/v1") + .set_project("") + .set_key(""); + + let webhooks = Webhooks::new(&client); + + webhooks.delete( + "", + ).await?; + + Ok(()) +} +``` +{% /multicode %} + +## Rotate signing key {% #rotate-signing-key %} + +{% multicode %} +```server-nodejs +import { Client, Webhooks } from 'node-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const webhooks = new Webhooks(client); + +const result = await webhooks.updateSecret({ + webhookId: '' +}); +``` +```server-deno +import { Client, Webhooks } from "npm:node-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const webhooks = new Webhooks(client); + +const result = await webhooks.updateSecret({ + webhookId: '' +}); +``` +```server-php +setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setKey(''); + +$webhooks = new Webhooks($client); + +$result = $webhooks->updateSecret( + webhookId: '' +); +``` +```server-python +from appwrite.client import Client +from appwrite.services.webhooks import Webhooks + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_key('') + +webhooks = Webhooks(client) + +result = webhooks.update_secret( + webhook_id='' +) +``` +```server-ruby +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_key('') + +webhooks = Webhooks.new(client) + +response = webhooks.update_secret( + webhook_id: '' +) +``` +```server-dotnet +using Appwrite; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetKey(""); + +Webhooks webhooks = new Webhooks(client); + +var result = await webhooks.UpdateSecret( + webhookId: "" +); +``` +```server-dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +Webhooks webhooks = Webhooks(client); + +final result = await webhooks.updateSecret( + webhookId: '', +); +``` +```server-kotlin +import io.appwrite.Client +import io.appwrite.services.Webhooks + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") + +val webhooks = Webhooks(client) + +val result = webhooks.updateSecret( + webhookId = "" +) +``` +```server-java +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Webhooks; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey(""); + +Webhooks webhooks = new Webhooks(client); + +webhooks.updateSecret( + "", // webhookId + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + System.out.println(result); + }) +); +``` +```server-swift +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") + +let webhooks = Webhooks(client) + +let result = try await webhooks.updateSecret( + webhookId: "" +) +``` +```server-go +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/appwrite" +) + +func main() { + client := appwrite.NewClient( + appwrite.WithEndpoint("https://.cloud.appwrite.io/v1"), + appwrite.WithProject(""), + appwrite.WithKey(""), + ) + + webhooks := appwrite.NewWebhooks(client) + result, err := webhooks.UpdateSecret( + "", + ) + + if err != nil { + panic(err) + } + + fmt.Println(result) +} +``` +```server-rust +use appwrite::Client; +use appwrite::services::Webhooks; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new() + .set_endpoint("https://.cloud.appwrite.io/v1") + .set_project("") + .set_key(""); + + let webhooks = Webhooks::new(&client); + + let result = webhooks.update_secret( + "", // webhook_id + ).await?; + + println!("{:?}", result); + Ok(()) +} +``` +{% /multicode %} # Payload {% #payload %} @@ -30,11 +1445,189 @@ HTTP requests made to your webhook's configured URL endpoint will contain severa # Verification {% #verification %} -Webhooks can be verified by using the [X-Appwrite-Webhook-Signature](/docs/advanced/platform/webhooks#headers) header. -This is the HMAC-SHA1 signature of the payload. You can find the signature key in your webhooks properties in the dashboard. -To generate this hash you append the payload to the end of webhook URL (make sure there are no spaces in between) and then use the HMAC-SHA1 algorithm to generate the signature. -After you've generated the signature, compare it to the `X-Appwrite-Webhook-Signature` header value. -If they match, the payload is valid and you can trust it came from your Appwrite instance. +You can verify that a webhook request genuinely came from your Appwrite instance using the `X-Appwrite-Webhook-Signature` header. The signature key can be found in your webhook's properties in the Appwrite Console. + +To verify the signature: + +1. Concatenate the **webhook URL** and the **request body** (no spaces in between). +2. Generate an HMAC-SHA1 hash of the concatenated string using your webhook's **signature key**. +3. Base64 encode the resulting hash. +4. Compare the result to the `X-Appwrite-Webhook-Signature` header value. If they match, the payload is authentic. + +{% multicode %} +```server-nodejs +import crypto from 'crypto'; + +function verifyWebhook(req, signatureKey) { + const url = 'https://example.com/webhook'; // Your webhook URL + const payload = req.body; // Raw request body as string + const signature = req.headers['x-appwrite-webhook-signature']; + + const generated = crypto + .createHmac('sha1', signatureKey) + .update(url + payload) + .digest('base64'); + + return crypto.timingSafeEqual( + Buffer.from(generated), + Buffer.from(signature) + ); +} +``` +```server-python +import hmac +import hashlib +import base64 + +def verify_webhook(url, payload, signature_key, signature): + generated = base64.b64encode( + hmac.new( + signature_key.encode(), + (url + payload).encode(), + hashlib.sha1 + ).digest() + ).decode() + + return hmac.compare_digest(generated, signature) +``` +```server-php + Bool { + let key = SymmetricKey(data: Data(signatureKey.utf8)) + let data = Data((url + payload).utf8) + let mac = HMAC.authenticationCode(for: data, using: key) + let generated = Data(mac).base64EncodedString() + + guard generated.count == signature.count else { return false } + return generated.utf8.elementsEqual(signature.utf8, by: { $0 &^ $1 == 0 }) + && generated.count == signature.count +} +``` +```server-dotnet +using System.Security.Cryptography; +using System.Text; + +bool VerifyWebhook( + string url, string payload, string signatureKey, string signature) +{ + using var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(signatureKey)); + var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(url + payload)); + var generated = Convert.ToBase64String(hash); + + return CryptographicOperations.FixedTimeEquals( + Encoding.UTF8.GetBytes(generated), + Encoding.UTF8.GetBytes(signature) + ); +} +``` +```server-rust +use hmac::{Hmac, Mac}; +use sha1::Sha1; +use base64::Engine; +use base64::engine::general_purpose::STANDARD; +use subtle::ConstantTimeEq; + +fn verify_webhook( + url: &str, payload: &str, signature_key: &str, signature: &str +) -> bool { + let mut mac = Hmac::::new_from_slice( + signature_key.as_bytes() + ).expect("HMAC key error"); + mac.update(format!("{}{}", url, payload).as_bytes()); + let generated = STANDARD.encode(mac.finalize().into_bytes()); + + generated.as_bytes().ct_eq(signature.as_bytes()).into() +} +``` +{% /multicode %} # Events {% #events %} diff --git a/static/images/blog/announcing-webhooks-api/cover.png b/static/images/blog/announcing-webhooks-api/cover.png new file mode 100644 index 0000000000..8656f16a44 Binary files /dev/null and b/static/images/blog/announcing-webhooks-api/cover.png differ diff --git a/static/images/docs/platform/create-webhook.png b/static/images/docs/platform/create-webhook.png new file mode 100644 index 0000000000..03c33428a2 Binary files /dev/null and b/static/images/docs/platform/create-webhook.png differ diff --git a/static/images/docs/platform/dark/create-webhook.png b/static/images/docs/platform/dark/create-webhook.png new file mode 100644 index 0000000000..3d38945a6b Binary files /dev/null and b/static/images/docs/platform/dark/create-webhook.png differ