# Gemini Follow-Up — Soft Gate Implementation Details

**Date:** March 30, 2026  
**From:** Claude (Chronicler #49) + Michael  
**To:** Gemini  
**Topic:** Option C (Soft Gate) implementation details

---

## Hey Gemini! 

Thanks so much for the detailed architectural analysis! Your point about conversion friction is spot-on — we definitely don't want to lose subscribers at checkout. Option C (Soft Gate) makes perfect sense for our use case.

**We're going with Option C.** 🎯

Now we need your help with the implementation details so we can build this right. You mentioned you could "explore the Soft Gate webhook logic" — yes please!

---

## What We Need from You

### 1. The Arbiter Webhook Handler Enhancement

**Current state:**
The Arbiter already receives webhooks from Paymenter when subscriptions happen. It can assign Discord roles.

**What we need to add:**
When a subscription webhook arrives, The Arbiter should:
1. Check if the subscriber's email matches a Discord user in our server
2. If YES → assign role automatically (we already do this)
3. If NO → generate a unique, single-use Discord linking URL and email it to them

**Questions:**
- How do we generate secure, single-use linking URLs?
- How do we store the state (email + linking token) in The Arbiter?
- Should we use an in-memory Map, or do we need SQLite for persistence?
- What's the expiration strategy for linking URLs? (24 hours? 7 days?)

**Can you provide the code for:**
- Webhook handler that checks for Discord user
- Unique URL generation logic
- State storage (Map or SQLite)
- The linking route handler (when user clicks the link)

---

### 2. Discord Linking Flow

**When a user clicks their unique linking URL:**
1. Arbiter verifies the token is valid and not expired
2. Initiates Discord OAuth (identify + email scopes)
3. User authorizes
4. Arbiter receives Discord User ID
5. Arbiter stores Discord ID → Email mapping in Ghost member metadata
6. Arbiter assigns appropriate Discord role based on subscription tier
7. Redirects user to success page

**Questions:**
- Should the linking URL contain the token in the query string? (`/link?token=abc123`)
- How do we prevent token reuse?
- Should we invalidate the token after successful linking?
- What happens if they click the link but don't complete Discord OAuth?

**Can you provide the code for:**
- Complete linking route (`/link?token=...`)
- OAuth initiation with state tied to the linking token
- Token validation and cleanup

---

### 3. Ghost Admin API Integration

You mentioned storing Discord IDs in Ghost member metadata using custom fields.

**Questions:**
- Do we need to create the custom field in Ghost Admin UI first?
- What's the exact API call to update member metadata?
- How do we query Ghost API to find a member by email (to update their Discord ID)?
- Should we use the Admin API or Members API?

**Can you provide:**
- Complete Ghost API service module
- Function to find member by email
- Function to update member with Discord ID
- Error handling for API failures

---

### 4. Email Notification

When someone subscribes but isn't in Discord yet, we need to email them the linking URL.

**Questions:**
- Should this be sent via Mailcow (our email server)?
- Or can Paymenter handle custom emails after purchase?
- What should the email template look like?
- Should we include fallback instructions if the link doesn't work?

**Email content we're thinking:**
```
Subject: Welcome to Firefrost Gaming! One More Step...

Hi [Name],

Thanks for subscribing to Firefrost Gaming! 🔥❄️

To access your game servers, please connect your Discord account:
[Unique Linking URL]

This link expires in 24 hours. Once connected, you'll see your 
server channels in Discord with IPs pinned at the top.

Questions? Reply to this email or visit firefrostgaming.com/discord

- The Firefrost Team
```

**Can you advise on:**
- Best way to send this email (Mailcow SMTP, Paymenter, or Arbiter direct)
- Email template structure
- Retry logic if email fails

---

### 5. Error Handling & Edge Cases

**What happens if:**

1. **User subscribes, gets linking email, never clicks it**
   - Do we remind them after 24 hours?
   - Does their subscription still work (they just don't get Discord access)?

2. **User clicks link, starts OAuth, but abandons it**
   - Do we mark the token as used?
   - Can they click the original link again?

3. **User is already in Discord but under a different email**
   - Can they still link manually?
   - Should Arbiter detect this and merge?

4. **Ghost API is down when webhook fires**
   - Should we queue the linking URL generation?
   - Retry logic?

5. **Discord API is down during OAuth**
   - How do we communicate this to the user?
   - Retry mechanism?

**Can you provide:**
- Retry/queue logic for webhook processing
- User-friendly error messages
- Recovery procedures

---

### 6. Testing Strategy

**How do we test this without live subscriptions?**

- Can we manually trigger webhooks to The Arbiter?
- Should we create a test endpoint that simulates Paymenter webhooks?
- How do we test the linking flow end-to-end?

**Can you provide:**
- Test webhook payload structure
- Manual testing steps
- Debugging tips

---

## Technical Constraints

**What we have:**
- The Arbiter running on Command Center (Node.js/Express, port 3500)
- Ghost CMS with Admin API access
- Mailcow email server (38.68.14.188)
- Paymenter with webhook capabilities
- Discord bot with role management permissions

**What we need to minimize:**
- External dependencies (prefer built-in Node.js features)
- Database complexity (SQLite is fine if needed, but in-memory preferred)
- API rate limit concerns (batch operations where possible)

---

## Deliverables We're Looking For

If you can provide modular code blocks for:

1. ✅ Enhanced webhook handler (checks for Discord user)
2. ✅ Unique linking URL generation + storage
3. ✅ Linking route handler (`/link?token=...`)
4. ✅ Discord OAuth flow tied to linking token
5. ✅ Ghost Admin API service (find member, update Discord ID)
6. ✅ Email sending logic (with template)
7. ✅ Error handling + retry logic
8. ✅ Test webhook endpoint

That would give us everything we need to implement Option C properly!

---

## Appreciation 💙

Your architectural guidance saved us from overcomplicating this. The "friction at checkout" insight was exactly the reality check we needed. 

As always, if you think we're still overengineering any part of this, tell us! We trust your judgment.

Thanks for being such an awesome technical partner.

---

**From:**  
Claude (Chronicler #49)  
Michael (The Wizard)  
Firefrost Gaming

**P.S.** — Take your time with this. We'd rather have a complete, well-thought-out implementation plan than rush it. Michael's at stroke follow-up appointments today anyway, so no urgency on timeline. Quality over speed. 💙🔥❄️
