> ## Documentation Index
> Fetch the complete documentation index at: https://docs.adhdsimple.co.uk/llms.txt
> Use this file to discover all available pages before exploring further.

# Send email hook setup

# Send Email Hook Setup Guide

## Overview

This Send Email Hook replaces Supabase's default email templates with SendGrid's dynamic templates, providing platform-specific email handling for mobile and web applications.

## Features

* Platform detection (mobile vs web)
* SendGrid dynamic templates
* Conditional email content (code-only for mobile, link-only for web)
* Support for all 6 Supabase auth email types: signup, recovery, magic link, email change, invite, and reauthenticate emails

## Setup Steps

### 1. Environment Variables

Add these environment variables to your Supabase project:

```bash theme={null}
# SendGrid API Key
SENDGRID_API_KEY=your_sendgrid_api_key_here

# Hook secret (generate in Supabase Dashboard > Auth > Hooks)
SEND_EMAIL_HOOK_SECRET=v1,whsec_your_base64_secret_here

# Supabase Anonymous Key (already available in your environment)
SUPABASE_ANON_KEY=your_supabase_anon_key_here
```

### 2. SendGrid Template IDs

The current template IDs configured in the system:

```typescript theme={null}
const SENDGRID_TEMPLATES = {
  signup: "d-f616cf918fea42b2ac48870b0953154b", // Confirm signup
  recovery: "d-4264768063af464bb138244a4cfc011e", // Reset password
  magiclink: "d-615be9bd4d34493f80a7c7ba283516a3", // Magic link login
  email_change: "d-your-email-change-template-id", // TODO: Configure email change template
  invite: "d-6f15a805896e468fb8ad74cb69d62c5f", // Invite user
  reauthenticate: "d-your-reauthenticate-template-id", // TODO: Configure reauthenticate template
};
```

**Note:** The `email_change` and `reauthenticate` templates still need to be configured with actual SendGrid template IDs.

### 3. SendGrid Dynamic Template Setup

Create dynamic templates in SendGrid with these variables:

#### Available Variables for Templates

**Common Variables (Available in All Templates):**

```handlebars theme={null}
{{first_name}}
<!-- User's first name (only present if available) -->
{{email}}
<!-- User's email address -->
{{platform}}
<!-- "mobile" or "web" -->
{{token}}
<!-- 6-digit OTP code -->
{{confirmation_url}}
<!-- Full confirmation URL with /auth/v1/verify endpoint -->
{{site_url}}
<!-- Base site URL -->
{{action_type}}
<!-- Email action type -->
{{subject}}
<!-- Email subject line -->
```

**Template-Specific Variables:**

* **Signup Template (`d-f616cf918fea42b2ac48870b0953154b`)**
  * `{{subject}}` → "Confirm Your Signup - ADHD Simple"
* **Recovery Template (`d-4264768063af464bb138244a4cfc011e`)**
  * `{{subject}}` → "Reset Your Password - ADHD Simple"
* **Magic Link Template (`d-615be9bd4d34493f80a7c7ba283516a3`)**
  * `{{subject}}` → "Magic Link Login - ADHD Simple"
* **Invite Template (`d-6f15a805896e468fb8ad74cb69d62c5f`)**
  * `{{subject}}` → "You've Been Invited - ADHD Simple"
* **Email Change Template (TODO)**
  * `{{subject}}` → "Confirm Email Change - ADHD Simple"
  * `{{new_email}}` → New email address (if applicable)
* **Reauthenticate Template (TODO)**
  * `{{subject}}` → "Security Verification Required - ADHD Simple"

**Platform Detection:**

* `{{platform}}` will be "mobile" or "web"
* Use `{{#equals platform "mobile"}}` for conditional content in SendGrid templates
* Use `{{#if first_name}}` for checking if variables exist

#### Template Examples

**Signup Template:**

```handlebars theme={null}
<h2>{{subject}}</h2>

<p>Hi {{#if first_name}}{{first_name}}{{else}}there{{/if}},</p>

{{#equals platform "mobile"}}
  <!-- Mobile: Show code only -->
  <p style="font-size: 16px; margin-bottom: 10px; color: #333;">Enter this code
    in the app:</p>
  <div
    style="font-size: 32px; font-weight: bold; text-align: center; color: #3C93CD; letter-spacing: 4px; font-family: 'Courier New', monospace; padding: 20px; background: #f8f9fa; border: 2px solid #3C93CD; border-radius: 8px;"
  >
    {{token}}
  </div>
  <p style="font-size: 14px; color: #666; margin-top: 10px;">This code expires
    in 1 hour.</p>
{{else}}
  <!-- Web: Show button with link -->
  <p>Follow this link to confirm your signup:</p>
  <div style="text-align: center; margin: 20px 0;">
    <a
      href="{{confirmation_url}}"
      style="background-color: #3C93CD; color: white; padding: 12px 44px; text-decoration: none; border-radius: 100px; font-weight: bold; letter-spacing: 1px; display: inline-block;"
    >
      VERIFY EMAIL
    </a>
  </div>
{{/equals}}

<p>Welcome to ADHD Simple!</p>
```

**Recovery Template:**

```handlebars theme={null}
<h2>{{subject}}</h2>

<p>Hi {{#if first_name}}{{first_name}}{{else}}there{{/if}},</p>

{{#equals platform "mobile"}}
  <!-- Mobile: Show code only -->
  <p style="font-size: 16px; margin-bottom: 10px; color: #333;">Enter this code
    in the app to reset your password:</p>
  <div
    style="font-size: 32px; font-weight: bold; text-align: center; color: #ff9800; letter-spacing: 4px; font-family: 'Courier New', monospace; padding: 20px; background: #f8f9fa; border: 2px solid #ff9800; border-radius: 8px;"
  >
    {{token}}
  </div>
  <p style="font-size: 14px; color: #666; margin-top: 10px;">This code expires
    in 1 hour.</p>
{{else}}
  <!-- Web: Show button with link -->
  <p>Click this link to reset your password:</p>
  <div style="text-align: center; margin: 20px 0;">
    <a
      href="{{confirmation_url}}"
      style="background-color: #ff9800; color: white; padding: 12px 44px; text-decoration: none; border-radius: 100px; font-weight: bold; letter-spacing: 1px; display: inline-block;"
    >
      RESET PASSWORD
    </a>
  </div>
{{/equals}}
```

**Magic Link Template:**

```handlebars theme={null}
<h2>{{subject}}</h2>

<p>Hi {{#if first_name}}{{first_name}}{{else}}there{{/if}},</p>

{{#equals platform "mobile"}}
  <!-- Mobile: Show code only -->
  <p style="font-size: 16px; margin-bottom: 10px; color: #333;">Use this code to
    sign in to the app:</p>
  <div
    style="font-size: 32px; font-weight: bold; text-align: center; color: #2196f3; letter-spacing: 4px; font-family: 'Courier New', monospace; padding: 20px; background: #f8f9fa; border: 2px solid #2196f3; border-radius: 8px;"
  >
    {{token}}
  </div>
  <p style="font-size: 14px; color: #666; margin-top: 10px;">This code expires
    in 1 hour.</p>
{{else}}
  <!-- Web: Show button with link -->
  <p>Click this magic link to sign in:</p>
  <div style="text-align: center; margin: 20px 0;">
    <a
      href="{{confirmation_url}}"
      style="background-color: #2196f3; color: white; padding: 12px 44px; text-decoration: none; border-radius: 100px; font-weight: bold; letter-spacing: 1px; display: inline-block;"
    >
      SIGN IN
    </a>
  </div>
{{/equals}}
```

**Invite Template:**

```handlebars theme={null}
<h2>{{subject}}</h2>

<p>Hi {{#if first_name}}{{first_name}}{{else}}there{{/if}},</p>

{{#equals platform "mobile"}}
  <!-- Mobile: Show code only -->
  <p style="font-size: 16px; margin-bottom: 10px; color: #333;">You've been
    invited to join ADHD Simple! Use this code in the mobile app:</p>
  <div
    style="font-size: 32px; font-weight: bold; text-align: center; color: #9c27b0; letter-spacing: 4px; font-family: 'Courier New', monospace; padding: 20px; background: #f8f9fa; border: 2px solid #9c27b0; border-radius: 8px;"
  >
    {{token}}
  </div>
  <p style="font-size: 14px; color: #666; margin-top: 10px;">This code expires
    in 1 hour.</p>
{{else}}
  <!-- Web: Show button with link -->
  <p>You've been invited to join ADHD Simple! Click to accept:</p>
  <div style="text-align: center; margin: 20px 0;">
    <a
      href="{{confirmation_url}}"
      style="background-color: #9c27b0; color: white; padding: 12px 44px; text-decoration: none; border-radius: 100px; font-weight: bold; letter-spacing: 1px; display: inline-block;"
    >
      ACCEPT INVITATION
    </a>
  </div>
{{/equals}}
```

### 4. Deploy the Function

```bash theme={null}
# Set environment variables in Supabase
supabase secrets set SENDGRID_API_KEY=your_key_here
supabase secrets set SEND_EMAIL_HOOK_SECRET=your_secret_here

# Deploy the function
supabase functions deploy send-email --no-verify-jwt
```

### 5. Configure Auth Hook

1. Go to Supabase Dashboard > Auth > Hooks
2. Create a new "Send Email" hook
3. Set the URL to: `https://your-project-id.supabase.co/functions/v1/send-email`
4. Generate and copy the hook secret to your environment variables (format: `v1,whsec_base64_secret`)

### 6. Platform Detection Logic

The hook determines platform based on (in priority order):

1. **User metadata**: `user.user_metadata.platform === "mobile"`
2. **App metadata**: `user.app_metadata.platform === "mobile"`
3. **Redirect URL patterns**:
   * Mobile indicators: `adhdsimple://`, `exp://`, `/mobile/`, `app.adhdsimple.co.uk`
   * Default: `web`

### 7. URL Construction

The confirmation URLs are built with the correct Supabase auth endpoint:

```
https://your-project-id.supabase.co/auth/v1/verify?token=TOKEN_HASH&type=EMAIL_TYPE&redirect_to=ENCODED_REDIRECT_URL
```

**Important:** No `apikey` parameter is needed - Supabase handles this internally.

### 8. Testing

Test different email types:

```bash theme={null}
# Signup
curl -X POST 'https://your-project-id.supabase.co/auth/v1/signup' \
  -H 'apikey: your-anon-key' \
  -H 'Content-Type: application/json' \
  -d '{"email": "test@example.com", "password": "password123"}'

# Recovery
curl -X POST 'https://your-project-id.supabase.co/auth/v1/recover' \
  -H 'apikey: your-anon-key' \
  -H 'Content-Type: application/json' \
  -d '{"email": "test@example.com"}'

# Magic Link
curl -X POST 'https://your-project-id.supabase.co/auth/v1/magiclink' \
  -H 'apikey: your-anon-key' \
  -H 'Content-Type: application/json' \
  -d '{"email": "test@example.com"}'

# Invite User (requires service role key)
curl -X POST 'https://your-project-id.supabase.co/auth/v1/invite' \
  -H 'apikey: your-service-role-key' \
  -H 'Content-Type: application/json' \
  -d '{"email": "test@example.com"}'
```

## Platform-Specific Behavior

### Mobile Apps

* Shows only the 6-digit verification code
* Code expires in 1 hour
* No clickable links (users enter code manually)
* Platform detected via user metadata or redirect URL patterns

### Web Apps

* Shows only confirmation button/link
* No verification code displayed
* Standard email confirmation flow with clickable links
* Users are redirected after clicking the link

## Troubleshooting

### Common Issues

1. **"No API key found in request" error**:
   * Fixed by ensuring URL uses `/auth/v1/verify` endpoint (not `/auth/v1`)
   * No `apikey` parameter needed in the URL

2. **Hook secret format**: Must start with `v1,whsec_`

3. **Template IDs**: Must start with `d-` and be valid SendGrid dynamic template IDs

4. **Platform detection**: Check user metadata and redirect URL patterns

5. **SendGrid templates**: Ensure Handlebars syntax is correct (use `{{#equals platform "mobile"}}` for comparisons, `{{#if first_name}}` for existence checks)

### Logs

Check function logs:

```bash theme={null}
supabase functions logs send-email --follow
```

### Testing Platform Detection

Set platform explicitly in user metadata:

```javascript theme={null}
// Mobile signup
supabase.auth.signUp({
  email: "test@example.com",
  password: "password123",
  options: {
    data: {
      platform: "mobile",
    },
  },
});

// Web signup (default)
supabase.auth.signUp({
  email: "test@example.com",
  password: "password123",
});
```

## Configuration Status

✅ **Configured and Working:**

* Signup emails
* Recovery emails
* Magic link emails
* Invite emails

⚠️ **Needs Configuration:**

* Email change emails (template ID needed)
* Reauthenticate emails (template ID needed)

## Next Steps

1. Create SendGrid templates for `email_change` and `reauthenticate` actions
2. Update the template IDs in `packages/supabase/functions/send-email/index.ts`
3. Test all email flows in both mobile and web contexts
4. Monitor function logs for any issues
