Enable Notifications
Use Gmail and Supabase edge functions to send notifications for guestbook events.
When notifications are enabled, emails are sent for:
| Event | Recipients |
|---|---|
| New email added/invited to admin allow-list ("Admin list") | The new address, and existing admin list |
| Invited admin completes sign-in for the first time | The new user, and existing admin list |
| Email removed from the admin allow-list | The removed address, and existing admin list |
| Comment submitted | Admin list |
| Admin edits or deletes a comment | Admin list |
| Guestbook settings saved | Admin list |
Setting Up
Create Webhook Secret
Supabase Edge Functions check a shared secret instead of a user JSON Web Token (JWT).
We'll first generate a shared secret that will be used with our Supabase Database Webhooks.
-
Use a secure password generator like Bitwarden's password generator:
- Choose Password and not Passphrase.
- Set the Characters length to
32or longer. - Leave A–Z, a–z, and 0–9 on, and keep !@#$%^&* off so you can paste the value into
npx supabase secrets setwithout quoting.
-
Copy the generated password. Hang on to this — we'll need to store this as Supabase secret when we connect Google OAuth 2.0 to Supabase under
NOTIFY_WEBHOOK_SECRET:NOTIFY_WEBHOOK_SECRET=<your-webhook-secret>
Create Gmail Google Cloud Project
As sending emails requires more sensitive scopes, which may delay the approval of your OAuth client if you intend to publish the OAuth client for the admin authentication to the general public, let's create a new project for notifications.
Log in to your Google Cloud console:
-
Make sure to select the project you just created by clicking Select your project (or the name of the guestbook project, if that is what you have currently selected).
-
In the left sidebar, click APIs & Services, then Library.
-
Under Google Workspace, select Gmail API.
-
Click Enable.
-
In the left sidebar, click APIs & Services, then select OAuth consent screen.
-
In OAuth Overview, click Get started.
-
Configure your project, then click Create. For example, enter your:
- App Information
- Audience (for personal Google accounts, select
External) - Contact Information
- Finish (to agree to the Google API Services: User Data Policy)
-
Click Create.
-
In the left sidebar, click APIs & Services, then select Credentials.
-
Click + Create credentials, then select OAuth client ID.
-
In the Application type drop-down, select
Web application. -
Give your application a Name.
-
Under Authorized redirect URIs, add
https://developers.google.com/oauthplayground. -
Click Create.
-
On the OAuth client created popup, copy the Client ID and Client secret to your clipboard. Hang on to these — we'll need to store these as Supabase secrets in the next section under
GMAIL_CLIENT_IDandGMAIL_CLIENT_SECRET:GMAIL_CLIENT_ID=<your-client-id>.apps.googleusercontent.com GMAIL_CLIENT_SECRET=<your-client-secret>
-
In the left sidebar, click APIs & Services, then select OAuth consent screen.
-
In the left sidebar, click Audience.
-
Under Test users, click + Add users then add the address you'll use to send notification emails.
-
Click Save.
-
Open OAuth 2.0 Playground, signed in with the test user you just added.
-
Click the icon (OAuth 2.0 configuration). Set:
- Access type to
Offline. - Force prompt to
Consent Screen.
- Access type to
-
Check the Use your own OAuth credentials checkbox.
-
Paste in your
GMAIL_CLIENT_IDandGMAIL_CLIENT_SECRETfrom the OAuth 2.0 client you just created. -
Click Close.
-
From the scope list left sidebar, open Gmail API v1 and select
https://www.googleapis.com/auth/gmail.send.
-
Click Authorize APIs.
-
Sign in as the user that is connected to the OAuth 2.0 client you just created to allow access and follow the prompts to allow access.
-
Click Exchange authorization code for tokens, then copy the Refresh token.
Hang on to this — we'll need to store these as Supabase secrets in the next section under
GMAIL_REFRESH_TOKEN:GMAIL_REFRESH_TOKEN=<your-refresh-token>
Create Supabase Access Token
From within Supabase:
-
Click on your profile icon in the top right corner, then select Account.
-
In the left sidebar, click Access Tokens.
-
Click Generate new token:
- Enter in a Name for the token.
- Set the time the token Expires in.
-
Scope the Resource access to Project, then under Select projects choose your guestbook project.
-
Under Permissions, select
Preset - Read only. -
Click Project, then under Project Settings, select
Read. -
Click Application services:
- Under Edge Functions, select
Read-write. - Under Edge Function Secrets, select
Read-write.
- Under Edge Functions, select
-
Click Review access >.
-
Click Create token.
-
Copy the token to your clipboard. Hang on to this — we'll need to store this as Supabase secret in the next section under
SUPABASE_ACCESS_TOKEN:SUPABASE_ACCESS_TOKEN=<sbp_your_access_token>
Customize Notification Templates
Sender name is hardcoded in supabase/functions/_shared/gmail.ts. Change the FROM_NAME to your desired name:
const FROM_NAME = "y2k Guestbook";
Subject lines and body copy live as Markdown in supabase/functions/_shared/templates. Each email template is plain text with a subject: frontmatter block.
| File | Sends when | Recipients | Placeholders |
|---|---|---|---|
allowlist-added-self.md | Address added to the admin allow-list | The new address | {{email}}, {{admin}} |
allowlist-added.md | Same event | Existing admin list | {{email}}, {{admin}} |
allowlist-removed-self.md | Address removed from the admin allow-list | The removed address | {{email}}, {{admin}} |
allowlist-removed.md | Same event | Remaining admin list | {{email}}, {{admin}} |
comment-insert.md | Comment submitted | Admin list | {{name}}, {{body}}, {{admin}} |
comment-update.md | Comment edited | Admin list | {{name}}, {{body}}, {{admin}} |
comment-delete.md | Comment deleted | Admin list | {{name}}, {{body}}, {{admin}} |
settings-update.md | Guestbook settings saved | Admin list | {{summary}}, {{admin}} |
auth-created-self.md | Invited admin completes sign-in for the first time | The new user | {{email}}, {{admin}} |
auth-created.md | Same event | Existing admin list | {{email}}, {{admin}} |
Configuring Supabase
Deploy Edge Functions
Let's deploy the edge functions in supabase/functions to Supabase that will handle your notifications.
a. First, make sure that your .env.local file contains the correct NEXT_PUBLIC_SUPABASE_URL: https://<your-database-url>.supabase.co.
-
From your project root, call:
export SUPABASE_ACCESS_TOKEN=<sbp_your_access_token> npx y2k-guestbook deploy-notifsYou should receive the following output:
Deploying Function: notify-admins Uploading asset (notify-admins): supabase/functions/_shared/templates/allowlist-added-self.md Uploading asset (notify-admins): supabase/functions/_shared/templates/allowlist-added.md Uploading asset (notify-admins): supabase/functions/_shared/templates/allowlist-removed-self.md Uploading asset (notify-admins): supabase/functions/_shared/templates/allowlist-removed.md Uploading asset (notify-admins): supabase/functions/_shared/templates/auth-created-self.md Uploading asset (notify-admins): supabase/functions/_shared/templates/auth-created.md Uploading asset (notify-admins): supabase/functions/_shared/templates/comment-delete.md Uploading asset (notify-admins): supabase/functions/_shared/templates/comment-insert.md Uploading asset (notify-admins): supabase/functions/_shared/templates/comment-update.md Uploading asset (notify-admins): supabase/functions/_shared/templates/settings-update.md Uploading asset (notify-admins): supabase/functions/notify-admins/index.ts Uploading asset (notify-admins): supabase/functions/_shared/template.ts Uploading asset (notify-admins): supabase/functions/_shared/gmail.ts Uploading asset (notify-admins): supabase/functions/_shared/env.ts Uploading asset (notify-admins): supabase/functions/_shared/allowlist.ts Deploying Function: on-user-created Uploading asset (on-user-created): supabase/functions/_shared/templates/allowlist-added-self.md Uploading asset (on-user-created): supabase/functions/_shared/templates/allowlist-added.md Uploading asset (on-user-created): supabase/functions/_shared/templates/allowlist-removed-self.md Uploading asset (on-user-created): supabase/functions/_shared/templates/allowlist-removed.md Uploading asset (notify-admins): supabase/functions/_shared/templates/settings-update.md Uploading asset (notify-admins): supabase/functions/notify-admins/index.ts Uploading asset (notify-admins): supabase/functions/_shared/template.ts Uploading asset (notify-admins): supabase/functions/_shared/gmail.ts Uploading asset (notify-admins): supabase/functions/_shared/env.ts Uploading asset (notify-admins): supabase/functions/_shared/allowlist.ts Uploading asset (notify-admins): supabase/functions/_shared/templates/settings-update.md Uploading asset (notify-admins): supabase/functions/_shared/templates/settings-update.md Uploading asset (notify-admins): supabase/functions/notify-admins/index.ts Uploading asset (notify-admins): supabase/functions/_shared/template.ts Uploading asset (notify-admins): supabase/functions/_shared/gmail.ts Uploading asset (notify-admins): supabase/functions/_shared/env.ts Uploading asset (notify-admins): supabase/functions/_shared/allowlist.ts Deploying Function: on-user-created Uploading asset (on-user-created): supabase/functions/_shared/templates/allowlist-added-self.md Uploading asset (on-user-created): supabase/functions/_shared/templates/allowlist-added.md Uploading asset (on-user-created): supabase/functions/_shared/templates/allowlist-removed-self.md Uploading asset (on-user-created): supabase/functions/_shared/templates/allowlist-removed.md Uploading asset (on-user-created): supabase/functions/_shared/templates/auth-created-self.md Uploading asset (on-user-created): supabase/functions/_shared/templates/auth-created.md Uploading asset (on-user-created): supabase/functions/_shared/templates/comment-delete.md Uploading asset (on-user-created): supabase/functions/_shared/templates/comment-insert.md Uploading asset (on-user-created): supabase/functions/_shared/templates/comment-update.md Uploading asset (on-user-created): supabase/functions/_shared/templates/settings-update.md Uploading asset (on-user-created): supabase/functions/on-user-created/index.ts Uploading asset (on-user-created): supabase/functions/_shared/template.ts Uploading asset (on-user-created): supabase/functions/_shared/gmail.ts Uploading asset (on-user-created): supabase/functions/_shared/env.ts Uploading asset (on-user-created): supabase/functions/_shared/allowlist.ts You can inspect your deployment in the Dashboard: https://supabase.com/dashboard/project/<your-database-url>/functions
-
Set the following Supabase secrets:
-
From within your Supabase project, click Edge Functions in the left sidebar.
-
Select Secrets.
-
Under add or replace secrets, add the following secrets:
Name Value GMAIL_USERyou@gmail.comGMAIL_CLIENT_ID<your-client-id>.apps.googleusercontent.comGMAIL_CLIENT_SECRET<your-client-secret>GMAIL_REFRESH_TOKEN<your-refresh-token>SITE_URLhttps://<your-project-url>.vercel.appNOTIFY_WEBHOOK_SECRET<your-webhook-secret>FLAG_NOTIFfalse
Call the following commands via your terminal (requires Supabase CLI installed and an active Supabase login):
# Make sure you're logged in to your Supabase project npx supabase login ( # Prevent the `//` in the URL path from being interpreted as a command, wrap "Name=Value" in quotes export MSYS_NO_PATHCONV=1 npx supabase secrets set --project-ref <your-database-url> "GMAIL_REFRESH_TOKEN=<your-refresh-token>" # Populates the root URL for the {{admin}} placeholder in the notification templates npx supabase secrets set --project-ref <your-database-url> "SITE_URL=https://<your-project-url>.vercel.app" ) npx supabase secrets set --project-ref <your-database-url> GMAIL_USER=you@gmail.com npx supabase secrets set --project-ref <your-database-url> GMAIL_CLIENT_ID=<your-client-id>.apps.googleusercontent.com npx supabase secrets set --project-ref <your-database-url> GMAIL_CLIENT_SECRET=<your-client-secret> npx supabase secrets set --project-ref <your-database-url> NOTIFY_WEBHOOK_SECRET=<your-webhook-secret> npx supabase secrets set --project-ref <your-database-url> FLAG_NOTIF=falseIf your admin dashboard is not at
/admin, also set yourGUESTBOOK_ADMIN_PATH.You should receive the following output for each of the set secrets:
Finished supabase secrets set. -
Configure Database Webhooks
-
Within your Supabase project for the guestbook, select Integrations from the left sidebar.
-
From the Explore all landing page, select Database Webhooks.
-
Click Install integration.
-
Click Install integration again to confirm the installation.
-
Select the Webhooks tab. For each hook in the following list:
Table Events public.admin_allowlistINSERT, DELETE public.commentsINSERT, UPDATE, DELETE public.guestbook_settingsUPDATE Table Events auth.usersINSERT -
Click Create a new hook.
-
Enter a Name, such as
admin_added. -
Under Table, select the table you want to add a hook for (you'll need to repeat these steps for each table).
-
Set Type of webhook to HTTP Request.
-
Then set HTTP Request to:
- Method:
POST - URL:
https://<your-database-url>.supabase.co/functions/v1/notify-admins
- Method:
POST - URL:
https://<your-database-url>.supabase.co/functions/v1/on-user-created
- Method:
-
Under HTTP Headers, click Add header and add the following headers:
Name Value Content-Typeapplication/jsonx-notify-webhook-secretThe same string as NOTIFY_WEBHOOK_SECRET -
Click Create webhook.
-
Enable Notifications
Notifications are gated behind the Supabase FLAG_NOTIF flag. To enable notifications, set this flag to true in your Supabase project:
-
From within your Supabase project, click Edge Functions in the left sidebar.
-
Select Secrets.
-
Under add or replace secrets, update
FLAG_NOTIFtotrue.
Call the following command via your terminal:
npx supabase secrets set --project-ref <your-database-url> FLAG_NOTIF=true
You should receive the following output:
Finished supabase secrets set.
Review Webhooks
In your Supabase project, you can review webhook responses by running the following query from the SQL Editor in the left sidebar.
-- Original POST body is not stored after the request is sent
-- Rows older than ~6 hours have null status_code / response
select
c.relname as table_name,
h.hook_name,
r.status_code,
left(r.content, 500) as response,
r.timed_out,
r.error_msg,
split_part(encode(t.tgargs, 'escape'), '\000', 1) as url,
split_part(encode(t.tgargs, 'escape'), '\000', 2) as method,
h.created_at as triggered_at,
r.created as responded_at,
h.request_id
from supabase_functions.hooks as h
left join net._http_response as r
on r.id = h.request_id
left join pg_class as c
on c.oid = h.hook_table_id
left join pg_namespace as n
on n.oid = c.relnamespace
left join pg_trigger as t
on t.tgrelid = h.hook_table_id
and t.tgname = h.hook_name
order by h.created_at desc
-- Limit to the last 50 responses
limit 50;
Publish Notifications Client
Log in to your Google Cloud console and select your project:
-
In the left sidebar, click APIs & Services, then select OAuth consent screen.
-
Click Branding and enter your:
- App name — This will be displayed instead of your generic Supabase project URL.
- User support email — This will be displayed in the "Contact us" link in the Google OAuth consent screen.
-
Under App domain, enter:
-
Application home page — The URL of your guestbook application.
-
Application privacy policy link — The URL of your guestbook's privacy policy.
-
Application terms of service link — The URL of your guestbook's terms of service.
-
-
Under Authorized domains, enter any Authorized domains # where you intend to serve your guestbook application.
-
In Developer contact information, enter your contact email.
-
Click Save.
-
In the left sidebar, click Audience.
-
Under Testing, click Publish app.
-
Click Confirm to publish your client to production.
-
Regenerate your
GMAIL_REFRESH_TOKENwith the instructions in Create Gmail Google Cloud Project: 3. Create Refresh Token. -
Update your
GMAIL_REFRESH_TOKENSupabase secret from the CLI:npx supabase secrets set --project-ref <your-database-url> GMAIL_REFRESH_TOKEN=<your-refresh-token>Or from within Supabase:
-
From within your Supabase project, click Edge Functions in the left sidebar.
-
Select Secrets.
-
Under add or replace secrets, update the
GMAIL_REFRESH_TOKENsecret with your new refresh token.
-