# Seaway Forms — Product Requirements Document
**Status:** Discovery / Handoff
**Version:** 0.1
**Date:** February 24, 2026
**Author:** Erik Christensen
**Prepared by:** Blake (Claude / Cowork)

---

## 1. Overview

**Seaway Forms** is a lightweight, embeddable lead capture form system designed to work natively inside WordPress sites and Shopify stores, with first-class integrations to major email marketing and CRM platforms (Omnisend, Klaviyo, and others). It is purpose-built for service businesses, agency clients, and D2C brands that need a consistent, brand-safe form experience across multiple properties without depending on monolithic marketing platforms.

The primary use cases at launch are:
- **Execution Space** — "Let's Discuss Your Project" lead gen forms across the marketing site
- **Seaway** — lead capture pages for client campaigns, landing pages, and promotions

---

## 2. Problem Statement

Most lead capture solutions require either (a) a full marketing platform subscription (Hubspot, ActiveCampaign) with vendor lock-in, or (b) a simple contact form plugin with no CRM routing. There is no lightweight, developer-friendly form layer that:

- Drops into both WordPress and Shopify without friction
- Stores submissions locally as a fallback
- Routes to the operator's CRM of choice via a clean integration layer
- Can be styled to match any brand system

This product fills that gap.

---

## 3. Target Users

| User | Description |
|---|---|
| **Agency operator** | Sets up forms for clients across WordPress + Shopify sites; wants one place to manage integrations |
| **Business owner** | Non-technical; needs submissions to show up somewhere useful (inbox, CRM, spreadsheet) |
| **Developer** | Wants a clean API or shortcode/block to embed forms; doesn't want to fight with someone else's CSS |

---

## 4. Scope — V1

### In Scope
- WordPress Plugin (form builder + submission storage + admin UI)
- Shopify App (form embed + submission routing)
- Integration layer: Omnisend, Klaviyo
- Webhook output (generic — enables Zapier, Make, custom pipelines)
- Spam protection (honeypot + optional hCaptcha)
- Email notification to admin on submission

### Out of Scope (V1)
- Multi-step / conditional logic forms
- File upload fields
- Payment-gated forms
- Native analytics dashboard (use CRM analytics)
- SMS capture (deferred to V2 via Omnisend/Klaviyo SMS)

---

## 5. WordPress Plugin

### 5.1 Core Features
- **Form builder** — admin UI to create and manage forms; fields: text, email, phone, textarea, select, checkbox, hidden
- **Block + Shortcode** — embeddable via Gutenberg block (`<!-- wp:seaway-forms/form /-->`) or classic `[seaway_form id="1"]` shortcode
- **Submissions table** — all submissions stored in custom DB table (`wp_seaway_submissions`); viewable, filterable, and exportable as CSV from WP Admin
- **Admin notifications** — configurable email notification on each submission
- **Spam protection** — honeypot field by default; optional hCaptcha site key config
- **Multi-form support** — unlimited forms per site, each independently configured

### 5.2 Integration Layer (WordPress)
- **Settings page** — API key fields for Omnisend, Klaviyo, webhook URL; per-form integration assignment
- **List/tag mapping** — for each form, admin selects which list/tag in the connected CRM to subscribe the lead to
- **Queue + retry** — submissions queued locally; if CRM push fails, retried up to 3× with exponential backoff; failure logged in admin

### 5.3 Data Model

```
wp_seaway_forms
  id, name, config_json, created_at, updated_at

wp_seaway_submissions
  id, form_id, data_json, source_url, ip_hash, status (pending/sent/failed), created_at, synced_at

wp_seaway_integration_log
  id, submission_id, provider, attempt, status, response_code, created_at
```

### 5.4 Technical Constraints
- No external JS dependencies in frontend embed (vanilla JS only, ~8KB gzipped)
- CSS-free by default — outputs semantic HTML with BEM class names; theme applies styles
- PHP 8.1+ / WordPress 6.4+
- GDPR-compliant: IP stored as SHA-256 hash, consent checkbox optional per form

---

## 6. Shopify App

### 6.1 Core Features
- **Shopify App Bridge** — OAuth app, installs via Shopify App Store or custom install link
- **Theme App Extension** — renders the form as a Liquid block injectable into any theme section without touching theme code
- **Admin UI** — Polaris-based form builder inside Shopify Admin; mirrors WordPress builder feature set
- **Submissions table** — stored in app backend (see 6.3); viewable in admin
- **Integration routing** — same Omnisend / Klaviyo / webhook layer as WP plugin

### 6.2 Shopify-Specific Considerations
- Form embed must work in Online Store 2.0 themes (Dawn, Sense, Refresh, etc.)
- Customer data: if shopper is logged in, pre-fill email from Shopify Customer API (with consent)
- App Proxy — form submissions POST through `/apps/seaway-forms/submit` to avoid CSP/CORS issues
- Metafield storage option for attaching submission data to a Shopify customer record

### 6.3 Backend Architecture (Shopify App)
- **Stack:** Node.js / Remix (Shopify-recommended), PostgreSQL, hosted on Fly.io or Railway
- **Auth:** Shopify session tokens via App Bridge
- **Webhooks in:** `app/uninstalled`, `customers/data_request`, `customers/redact` (GDPR compliance mandatory for App Store)
- **API:** REST endpoints consumed by both the Theme Extension and the Admin UI

### 6.4 Technical Constraints
- App must pass Shopify App Store review — GDPR webhooks required
- Theme Extension JS bundle < 15KB gzipped
- No jQuery

---

## 7. Integration Layer

### 7.1 Omnisend
- **API version:** v3
- **Actions:** `POST /contacts` (create/update contact), `POST /events` (custom event on submission)
- **List assignment:** Contact added to specific list; tags applied per form config
- **Double opt-in:** Configurable per form — triggers Omnisend's native double opt-in flow if enabled

### 7.2 Klaviyo
- **API version:** v2024-10-15 (revision-based)
- **Actions:** `POST /profiles` (create/update), `POST /lists/{list_id}/relationships/profiles` (add to list)
- **Custom properties:** All form fields mapped as Klaviyo profile properties
- **Events:** Optional `Seaway Form Submit` event posted to Klaviyo Metrics

### 7.3 Webhook (Generic)
- **Method:** POST JSON to configured endpoint
- **Payload:**
```json
{
  "form_id": "string",
  "form_name": "string",
  "submitted_at": "ISO8601",
  "source_url": "string",
  "fields": {
    "email": "string",
    "name": "string",
    "[field_key]": "value"
  }
}
```
- **Signature:** HMAC-SHA256 header (`X-Seaway-Signature`) for payload verification
- **Use cases:** Zapier, Make.com, custom CRMs, Google Sheets via Apps Script

---

## 8. Form Embed — Frontend Spec

### 8.1 Output (HTML)
```html
<form class="seaway-form" data-form-id="1" method="POST" novalidate>
  <div class="seaway-form__field">
    <label class="seaway-form__label" for="sf-name">Name</label>
    <input class="seaway-form__input" type="text" id="sf-name" name="name" required>
    <span class="seaway-form__error" aria-live="polite"></span>
  </div>
  <!-- honeypot -->
  <div class="seaway-form__hp" aria-hidden="true">
    <input type="text" name="_hp" tabindex="-1" autocomplete="off">
  </div>
  <button class="seaway-form__submit" type="submit">Send</button>
</form>
```

### 8.2 States
- Default, Focus, Error (inline), Submitting (button disabled + spinner), Success (replace form with message or redirect)

### 8.3 Accessibility
- All inputs have associated `<label>` elements
- Error messages use `aria-live="polite"`
- Submit button communicates loading state via `aria-busy`
- Tab order preserved; honeypot field removed from tab order

---

## 9. Admin Notifications

- Configurable To / Subject / Body per form
- Body supports merge tags: `{{name}}`, `{{email}}`, `{{message}}`, `{{source_url}}`, `{{submitted_at}}`
- Sent via `wp_mail()` (WordPress) or Resend/SendGrid transactional API (Shopify app backend)

---

## 10. Spam Protection

| Method | Default | Configurable |
|---|---|---|
| Honeypot field | Yes | No (always on) |
| hCaptcha | No | Yes — site key in settings |
| Submission rate limiting | Yes (5/hr per IP hash) | Yes |
| Blocked domains list | No | V2 |

---

## 11. Phased Roadmap

### Phase 1 — WordPress Plugin MVP
- Form builder (5 field types), submissions table, admin notification, honeypot spam protection
- No CRM integrations yet — submissions stay in WP
- Target: working plugin installable from a zip

### Phase 2 — Integration Layer
- Omnisend + Klaviyo connectors
- Webhook output
- Retry queue + integration log

### Phase 3 — Shopify App
- Theme App Extension (form embed)
- Admin UI (Polaris)
- Submission storage + routing to same integration layer
- App Store submission

### Phase 4 — V2 Features
- Conditional logic (show/hide fields based on values)
- Multi-step forms
- File upload (S3-backed)
- Analytics dashboard
- SMS opt-in via Omnisend/Klaviyo

---

## 12. Open Questions / Decisions Needed

| # | Question | Owner | Status |
|---|---|---|---|
| 1 | Brand name: "Seaway Forms"? Or different product name? | Erik | Open |
| 2 | Shopify App Store distribution vs. private/custom install only? | Erik | Open |
| 3 | Backend hosting for Shopify app — Fly.io, Railway, or WP Engine? | Blake | Open |
| 4 | Klaviyo integration priority vs. Omnisend — which ships first in Phase 2? | Erik | Open |
| 5 | hCaptcha vs. reCAPTCHA — preference? (hCaptcha recommended for privacy) | Erik | Open |
| 6 | Double opt-in — required for all forms or per-form toggle? | Erik | Open |
| 7 | Pricing model if distributed publicly (free plugin + paid integrations?) | Erik | Open |

---

## 13. Execution Space Website — Current Decision

For the Execution Space site build (exe_2025 / Milton), the "Let's Discuss Your Project" form will:

- **Use Phase 1 WordPress only** — form submissions stored directly in WordPress, no CRM routing
- **No Omnisend integration for now** — revisit once Phase 2 integration layer is built
- **Implementation:** Custom Gutenberg block pattern in the theme, POSTing to a lightweight WP REST endpoint or `admin-ajax.php`, storing to a custom table or `wp_posts` (CPT)
- **Admin notification:** Email to Erik on each submission via `wp_mail()`

Milton to implement as part of the homepage and About/Services page patterns.

---

## 14. Customer Account Layer

### 14.1 Concept

Beyond lead capture, clients (Execution Space clients, Seaway clients) need a way to log in and access protected project information and support — without requiring a full-blown SaaS portal. This is a natural extension of the form system: capture the lead → convert to client → provision account access.

### 14.2 WordPress — Customer Portal

**Authentication**
- Native WordPress user accounts (role: `client`)
- Registration triggered on form submission or manually by admin
- Login via standard WP login or a custom branded login page (`/client-login`)
- Password reset via WP native flow

**Protected Content**
- Restricted pages/posts visible only to logged-in `client` role
- Use cases: project status updates, deliverable files, invoice links, shared documents
- Implementation: page-level role restriction via lightweight plugin (Members, PublishPress) or custom `template_redirect` hook in theme
- Files: protected download links (time-limited signed URLs or WP serves via PHP to prevent direct hotlinking)

**Client Dashboard Page**
- Custom page template (`templates/client-dashboard.html`) in the theme
- Displays: active projects, recent support tickets (Freshdesk), uploaded files, contact info
- Data pulled dynamically via WP REST API + Freshdesk API (server-side, keys never exposed to client JS)

### 14.3 Freshdesk Integration

**Goal:** Clients can view and respond to their support tickets directly from the WordPress client portal — no need to log into Freshdesk separately.

**Approach**
- **Freshdesk Contacts API** — on WP user creation, create a corresponding Freshdesk contact (synced by email)
- **Freshdesk Tickets API** — fetch open/closed tickets for the logged-in user's email from the WP backend; render in the dashboard template
- **New ticket form** — simple form on the dashboard POSTs to WP backend → Freshdesk `POST /tickets`; submission fires via WP REST endpoint (server-side, API key protected)
- **Reply to ticket** — client can add a reply to an existing ticket via `POST /tickets/{id}/reply`
- **File attachments** — V2 (multipart form + Freshdesk attachments API)

**Freshdesk API Key Storage**
- Stored in `wp-config.php` as a constant (`FRESHDESK_API_KEY`, `FRESHDESK_DOMAIN`) — never in the database, never exposed to frontend

**Data Flow**
```
Client logs into WP → Dashboard loads
→ WP REST endpoint /wp-json/seaway/v1/tickets
  → PHP fetches tickets from Freshdesk API (server-side)
  → Returns sanitized JSON to dashboard JS
→ Dashboard renders ticket list
→ Client submits reply → WP REST → Freshdesk API
```

### 14.4 Shopify — Customer Account Consideration

For Shopify properties (Seaway client stores), Shopify's native customer accounts (New Customer Accounts, 2024) provide login. The integration path:
- Freshdesk contact synced via Shopify `customers/create` webhook → Freshdesk Contacts API
- Customer-facing support UI embedded via Shopify Theme App Extension (same app as form embed)
- Authenticated via Shopify Customer Account API (requires customer access token scoping)

### 14.5 Phased Plan — Customer Accounts

| Phase | Scope |
|---|---|
| Phase 1 | WordPress `client` role, protected pages, branded login |
| Phase 2 | Client dashboard template, Freshdesk ticket list + new ticket form |
| Phase 3 | Freshdesk replies from dashboard, file download links |
| Phase 4 | Shopify customer → Freshdesk sync, theme app extension support embed |

### 14.6 Open Questions — Customer Accounts

| # | Question | Owner | Status |
|---|---|---|---|
| 8 | Self-serve client registration or admin-provisioned only? | Erik | Open |
| 9 | Should the client portal live on executionspace.com or a subdomain (e.g. `clients.executionspace.com`)? | Erik | Open |
| 10 | Freshdesk plan/tier — does current plan support API access? | Erik | Open |
| 11 | Will Seaway clients use the same portal or a separate instance? | Erik | Open |
| 12 | SSO consideration — Google/Microsoft login for clients instead of WP password? | Erik | Open |

---

*This document is a living discovery spec. Update as decisions are made and scope is confirmed.*
