diff --git a/apps/reference/docs/guides/database/database-webhooks.mdx b/apps/reference/docs/guides/database/database-webhooks.mdx
new file mode 100644
index 00000000000..6828a0bbc7b
--- /dev/null
+++ b/apps/reference/docs/guides/database/database-webhooks.mdx
@@ -0,0 +1,74 @@
+---
+id: webhooks
+title: 'Database Webhooks'
+description: Trigger external payloads on database events.
+---
+
+import Tabs from '@theme/Tabs'
+import TabItem from '@theme/TabItem'
+
+Database Webhooks allow you to send real-time data from your database to another system whenever a table event occurs.
+
+
+You can hook into three table events: `INSERT`, `UPDATE`, and `DELETE`. All events are fired _after_ a database row is changed.
+
+Database Webhooks are very similar to triggers, and that's because Database Webhooks are just a convenience wrapper around triggers
+using the [pg_net](/docs/guides/database/extensions/pgnet) extension. This extension is asynchronous, and therefore will not block your database changes for long-running network requests.
+
+This video demonstrates how you can create a new customer in Stripe each time a row is inserted into a `profiles` table:
+
+:::note
+
+Database Webhooks were previously known as Function Hooks.
+
+:::
+
+
+
+
+
+## Creating a webhook
+
+1. Create a new [Database Webhook](https://app.supabase.com/project/_/database/hooks) in the Dashboard.
+1. Give your Webhook a name.
+1. Select the table you want to hook into.
+1. Select one or more events (table inserts, updates, or deletes) you want to hook into.
+
+We currently support HTTP webhooks. These are sent as a `POST` request with a JSON payload.
+
+## Payload
+
+The payload is automatically generated from the underlying table record:
+
+```typescript
+type InsertPayload = {
+ type: 'INSERT'
+ table: string
+ schema: string
+ record: TableRecord
+ old_record: null
+}
+type UpdatePayload = {
+ type: 'UPDATE'
+ table: string
+ schema: string
+ record: TableRecord
+ old_record: TableRecord
+}
+type DeletePayload = {
+ type: 'DELETE'
+ table: string
+ schema: string
+ record: null
+ old_record: TableRecord
+}
+```
+
+## Resources
+
+- [pg_net](/docs/guides/database/extensions/pgnet): an async networking extension for PostgreSQL
diff --git a/apps/reference/docs/guides/database/extensions/pgcron.mdx b/apps/reference/docs/guides/database/extensions/pgcron.mdx
new file mode 100644
index 00000000000..e7032bf9bd9
--- /dev/null
+++ b/apps/reference/docs/guides/database/extensions/pgcron.mdx
@@ -0,0 +1,99 @@
+---
+id: pgcron
+title: 'pg_cron: Job Scheduling'
+description: 'pgnet: a simple cron-based job scheduler for PostgreSQL that runs inside the database.'
+---
+
+import Tabs from '@theme/Tabs'
+import TabItem from '@theme/TabItem'
+
+The `pg_cron` extension is a simple cron-based job scheduler for PostgreSQL that runs inside the database.
+
+## Usage
+
+### Enable the extension
+
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "pg_cron" and enable the extension.
+
+
+
+
+```sql
+-- Example: enable the "pg_cron" extension
+create extension pg_cron with schema extensions;
+
+-- If you're planning to use a non-superuser role to schedule jobs,
+-- ensure that they are granted access to the cron schema and its underlying objects beforehand.
+-- Failure to do so would result in jobs by these roles to not run at all.
+
+grant usage on schema cron to {{DB user}};
+grant all privileges on all tables in schema cron to {{DB user}};
+
+
+-- Example: disable the "pg_cron" extension
+drop extension if exists pg_cron;
+```
+
+
+
+
+### Syntax
+
+The schedule uses the standard cron syntax, in which \* means "run every time period", and a specific number means "but only at this time":
+
+```bash
+ ┌───────────── min (0 - 59)
+ │ ┌────────────── hour (0 - 23)
+ │ │ ┌─────────────── day of month (1 - 31)
+ │ │ │ ┌──────────────── month (1 - 12)
+ │ │ │ │ ┌───────────────── day of week (0 - 6) (0 to 6 are Sunday to
+ │ │ │ │ │ Saturday, or use names; 7 is also Sunday)
+ │ │ │ │ │
+ │ │ │ │ │
+ * * * * *
+```
+
+## Examples
+
+### Delete data every week
+
+Delete old data on Saturday at 3:30am (GMT):
+
+```sql
+select cron.schedule (
+ 'webhook-every-minute', -- name of the cron job
+ '* * * * *', -- every minute
+ $$ delete from events where event_time < now() - interval '1 week' $$
+);
+```
+
+### Run a vacuum every day
+
+Vacuum every day at 3:00am (GMT)
+
+```sql
+SELECT cron.schedule('nightly-vacuum', '0 3 * * *', 'VACUUM');
+```
+
+### Unschedule a job
+
+Unschedules a job called `'nightly-vacuum'`
+
+```sql
+SELECT cron.unschedule('nightly-vacuum');
+```
+
+## Resources
+
+- [pg_cron GitHub Repository](https://github.com/citusdata/pg_cron)
diff --git a/apps/reference/docs/guides/database/extensions/pgnet.mdx b/apps/reference/docs/guides/database/extensions/pgnet.mdx
new file mode 100644
index 00000000000..341af926e44
--- /dev/null
+++ b/apps/reference/docs/guides/database/extensions/pgnet.mdx
@@ -0,0 +1,254 @@
+---
+id: pgnet
+title: 'pg_net: Async Networking'
+description: 'pg_net: an async networking extension for PostgreSQL.'
+---
+
+import Tabs from '@theme/Tabs'
+import TabItem from '@theme/TabItem'
+
+:::caution
+
+The pg_net API is in beta. Functions signatures may change.
+
+:::
+
+[pg_net](https://github.com/supabase/pg_net/) is a PostgreSQL extension exposing a SQL interface for async networking with a focus on scalability and UX.
+
+It differs from the `http` extension in that it is asynchronous by default. This makes it useful in blocking functions (like triggers).
+
+## Usage
+
+### Enable the extension
+
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "pg_net" and enable the extension.
+
+
+
+
+```sql
+-- Example: enable the "pg_net" extension
+create schema if not exists net;
+create extension pg_net with schema net;
+
+-- Example: disable the "plv8" extension
+drop extension if exists pg_net;
+drop schema net;
+```
+
+Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
+To disable an extension, call `drop extension`.
+
+Procedural languages are automatically installed within `pg_catalog`, so you don't need to specify a schema.
+
+
+
+
+## `http_get` {#http_get}
+
+Creates an HTTP GET request returning the request's ID. HTTP requests are not started until the transaction is committed.
+
+### Signature
+
+:::caution
+
+This is a Postgres SECURITY DEFINER function.
+
+:::
+
+```sql
+net.http_get(
+ -- url for the request
+ url text,
+ -- key/value pairs to be url encoded and appended to the `url`
+ params jsonb default '{}'::jsonb,
+ -- key/values to be included in request headers
+ headers jsonb default '{}'::jsonb,
+ -- WARNING: this is currently ignored, so there is no timeout
+ -- the maximum number of milliseconds the request may take before being cancelled
+ timeout_milliseconds int default 1000
+)
+ -- request_id reference
+ returns bigint
+
+ strict
+ volatile
+ parallel safe
+ language plpgsql
+```
+
+### Usage
+
+```sql
+select net.http_get('https://news.ycombinator.com') as request_id;
+request_id
+----------
+ 1
+(1 row)
+```
+
+After triggering `http_get`, use [`http_get_result`](#http_get_result) to get the result of the request.
+
+## `http_post` {#http_post}
+
+Creates an HTTP POST request with a JSON body, returning the request's ID. HTTP requests are not started until the transaction is committed.
+
+The body's character set encoding matches the database's `server_encoding` setting.
+
+### Signature
+
+:::caution
+
+This is a Postgres SECURITY DEFINER function
+
+:::
+
+```sql
+net.http_post(
+ -- url for the request
+ url text,
+ -- body of the POST request
+ body jsonb default '{}'::jsonb,
+ -- key/value pairs to be url encoded and appended to the `url`
+ params jsonb default '{}'::jsonb,
+ -- key/values to be included in request headers
+ headers jsonb default '{"Content-Type": "application/json"}'::jsonb,
+ -- WARNING: this is currently ignored, so there is no timeout
+ -- the maximum number of milliseconds the request may take before being cancelled
+ timeout_milliseconds int default 1000
+)
+ -- request_id reference
+ returns bigint
+
+ volatile
+ parallel safe
+ language plpgsql
+```
+
+### Usage
+
+```sql
+select
+ net.http_post(
+ url:='https://httpbin.org/post',
+ body:='{"hello": "world"}'::jsonb
+ ) as request_id;
+request_id
+----------
+ 1
+(1 row)
+```
+
+After triggering `http_post`, use [`http_get_result`](#http_get_result) to get the result of the request.
+
+## `http_collect_response` {#http_collect_response}
+
+Given a `request_id` reference, retrieves the response.
+
+When `async:=false` is set it is recommended that [statement_timeout](https://www.postgresql.org/docs/13/runtime-config-client.html) is set for the maximum amount of time the caller is willing to wait in case the response is slow to populate.
+
+### Signature
+
+:::caution
+
+This is a Postgres SECURITY DEFINER function
+
+:::
+
+```sql
+net.http_collect_response(
+ -- request_id reference
+ request_id bigint,
+ -- when `true`, return immediately. when `false` wait for the request to complete before returning
+ async bool default true
+)
+ -- http response composite wrapped in a result type
+ returns net.http_response_result
+
+ strict
+ volatile
+ parallel safe
+```
+
+### Usage
+
+:::caution
+
+`net.http_collect_response` must be in a separate transaction from the calls to `net.http_`
+
+:::
+
+```sql
+select
+ net.http_post(
+ url:='https://httpbin.org/post',
+ body:='{"hello": "world"}'::jsonb
+ ) as request_id;
+request_id
+----------
+ 1
+(1 row)
+
+select * from net.http_collect_response(1, async:=false);
+status | message | response
+--------+---------+----------
+SUCCESS ok (
+ status_code := 200,
+ headers := '{"date": ...}',
+ body := '{"args": ...}'
+ )::net.http_response_result
+
+
+select
+ (response).body::json
+from
+ net.http_collect_response(request_id:=1);
+ body
+-------------------------------------------------------------------
+ {
+ "args": {},
+ "data": "{\"hello\": \"world\"}",
+ "files": {},
+ "form": {},
+ "headers": {
+ "Accept": "*/*",
+ "Content-Length": "18",
+ "Content-Type": "application/json",
+ "Host": "httpbin.org",
+ "User-Agent": "pg_net/0.2",
+ "X-Amzn-Trace-Id": "Root=1-61031a5c-7e1afeae69bffa8614d8e48e"
+ },
+ "json": {
+ "hello": "world"
+ },
+ "origin": "135.63.38.488",
+ "url": "https://httpbin.org/post"
+ }
+(1 row)
+```
+
+Where `response` is a composite:
+
+```sql
+status_code integer
+headers jsonb
+body text
+```
+
+Possible values for `net.http_response_result.status` are `('PENDING', 'SUCCESS', 'ERROR')`
+
+## Resources
+
+- Source code: [github.com/supabase/pg_net](https://github.com/supabase/pg_net/)
+- Official Docs: [supabase.github.io/pg_net](https://supabase.github.io/pg_net/)
diff --git a/apps/reference/nav/_referenceSidebars.js b/apps/reference/nav/_referenceSidebars.js
index f88427304a1..304f9347d96 100644
--- a/apps/reference/nav/_referenceSidebars.js
+++ b/apps/reference/nav/_referenceSidebars.js
@@ -124,6 +124,7 @@ const sidebars = {
'guides/database/connecting-to-postgres',
'guides/database/tables',
'guides/database/functions',
+ 'guides/database/webhooks',
'guides/database/full-text-search',
'guides/database/migrating-between-projects',
// 'guides/database/json',
@@ -135,9 +136,11 @@ const sidebars = {
collapsed: true,
items: [
'guides/database/extensions',
- 'guides/database/extensions/plv8',
'guides/database/extensions/http',
+ 'guides/database/extensions/pgcron',
+ 'guides/database/extensions/pgnet',
'guides/database/extensions/pgtap',
+ 'guides/database/extensions/plv8',
'guides/database/extensions/uuid-ossp',
],
},