new-term-radar
Daily radar for new AI terms: names of new models, products and repos, caught on X, Hugging Face, GitHub and sitemaps before keyword tools list them.
Minimal cold-email campaign manager
Translation pending
MarketingTypeScript · PLpgSQLGitHub @budibucusOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Minimal multi-tenant cold-email campaign manager
Translation pending
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
A minimal, multi-tenant cold-email outreach tool. Each user connects their
own Gmail account, manages their own contacts, and sends their own
campaigns — fully isolated from every other user, enforced at the database
level, not just in application code.
Cold outreach tools either cost more than a small team can justify, or they
route your email through a shared sending service that tanks deliverability
the moment one tenant misbehaves. Coldloop takes a narrower, cheaper path:Read the full README ↗A minimal, multi-tenant cold-email outreach tool. Each user connects their own Gmail account, manages their own contacts, and sends their own campaigns — fully isolated from every other user, enforced at the database level, not just in application code.
Cold outreach tools either cost more than a small team can justify, or they route your email through a shared sending service that tanks deliverability the moment one tenant misbehaves. Coldloop takes a narrower, cheaper path:
It was built for a small number of people (think: a handful of founders or a small sales team, not thousands of tenants) who want control over their own sending without building outreach infrastructure from scratch.
No automated scheduler (sending is always a manual click), no spam-folder tracking (Gmail's API doesn't expose that to senders — this repo doesn't pretend otherwise), no shared/team workspaces (every account is a fully separate tenant, even for people at the same company), no billing.
| Layer | Choice |
|---|---|
| Framework | Next.js 16 (App Router, Server Actions) |
| Database, Auth, Storage | Supabase (Postgres + Row Level Security) |
| Email sending & reply checking | Gmail API, per-user OAuth2 |
| AI email generation | Anthropic API (claude-opus-5) |
| Hosting | Vercel |
Nothing here is interchangeable casually — Row Level Security is what makes multi-tenancy safe, and the Gmail OAuth flow is what keeps sending per-user instead of shared.
Browser
│
▼
Next.js (Vercel)
│ │ │
▼ ▼ ▼
Supabase Gmail API Anthropic API
(auth, DB, (OAuth2 per user, (claude-opus-5,
storage, send + search no tools attached —
RLS) for replies) text-only, can't
produce images)
Every table in Postgres carries a user_id and a Row Level Security policy
that restricts it to auth.uid() = user_id — isolation is enforced by the
database itself, not by application-layer filtering, so a bug in a route
handler can't leak one user's data to another.
All tables live in the public schema. Five migrations, applied in order,
build this incrementally (supabase/migrations/0001–0004).
auth.users (Supabase-managed)
│
├──1:1── sender_profiles (onboarding info, one per user)
├──1:1── gmail_connections (one connected Gmail account per user)
├──1:N── contacts (a user's contact list)
├──1:N── campaigns (a user's named sending plans)
└──1:N── emails (every send attempt, draft or sent)
│ │
│ └──N:1── contacts (who it's to)
└───────────N:1── campaigns (which plan produced it, if any)
allowed_senders — global gate list, not user-owned (see below)
sender_profilesOne row per user — the identity and voice used to personalize every generated email.
| Column | Type | Notes |
|---|---|---|
user_id | uuid (PK) | References auth.users, cascades on delete |
company, sender_name | text | Required |
sender_title, product_description, target_segments, tone | text | Optional |
default_attachment_path, default_attachment_filename | text | Fallback attachment for every send unless overridden |
created_at, updated_at | timestamptz | updated_at auto-maintained by trigger |
contactsA user's contact list. One table, no per-campaign copies — the same contact can be targeted by multiple campaigns over time.
| Column | Type | Notes |
|---|---|---|
id | uuid (PK) | |
user_id | uuid | FK to auth.users |
name, email | text | Required |
company, personalization_notes | text | Optional, fed into templates/AI prompts |
status | enum | not_sent / sent / replied / bounced |
attachment_path, attachment_filename | text | Overrides the profile's default attachment |
status is what the automatic follow-up cadence reads: a contact with
sent status becomes eligible for the next sequence step 3 days after
their last email, unless replied (reply detection sets this and cancels
pending follow-ups) or bounced.
campaignsA named, reusable sending plan.
| Column | Type | Notes |
|---|---|---|
id | uuid (PK) | |
user_id | uuid | FK to auth.users |
name | text | |
objective | enum | initial / followup1 / followup2 — which template/stage this campaign targets |
generation_mode | text | template (free, deterministic) or ai (Claude-generated) |
ai_prompt | text | The instructions Claude writes from, when generation_mode = 'ai' |
attachment_path, attachment_filename | text | Campaign-level attachment |
max_send_count | integer | Cumulative cap across the campaign's whole lifetime, even if prepared/sent more than once |
emailsEvery send attempt — draft, sent, failed, or canceled. This is the audit log and the queue at the same time.
| Column | Type | Notes |
|---|---|---|
id | uuid (PK) | |
user_id | uuid | FK to auth.users |
contact_id | uuid | FK to contacts, cascades on delete |
campaign_id | uuid | FK to campaigns, nullable, on delete set null |
sequence_step | enum | initial / followup1 / followup2 |
subject, body | text | The actual generated content |
status | enum | draft / sent / failed / canceled |
sent_at | timestamptz | Null until actually sent |
attachment_filename | text | What was attached at send time (recorded independently of the source attachment, for history) |
A row is created as draft when a campaign batch is prepared, edited
in place on the Review page, and only becomes sent (or failed) when
the user clicks "Confirm and send." canceled is set automatically if the
contact replies while a draft is still pending.
gmail_connectionsOne connected Gmail account per user.
| Column | Type | Notes |
|---|---|---|
user_id | uuid (PK) | FK to auth.users |
gmail_address | text | The connected account's address |
encrypted_access_token, encrypted_refresh_token | text | AES-256-GCM encrypted at the application layer before storage — never stored in plaintext |
token_expires_at | timestamptz |
allowed_sendersA global gate list — not user-owned data, no user_id column. Until
Google's OAuth verification is complete for this app, only pre-approved
Google test users can actually authorize Gmail sending; this table mirrors
that list so the app can gate the "Connect Gmail" button without a second
Google API round-trip. A user may only query whether their own email is
on the list; only the service role can write to it.
A private Supabase Storage bucket, attachments, with one folder per
user ({user_id}/profile/, {user_id}/contacts/, {user_id}/campaigns/).
Row Level Security on storage.objects restricts each user to their own
folder.
src/
app/
(app)/ # everything behind the sidebar shell (auth required)
layout.tsx # shared shell: sidebar + top bar, one auth check
dashboard/ # onboarding checklist, Gmail connect, reply check
onboarding/ # sender profile form
contacts/ # add contact (form or CSV), contacts table
campaigns/ # list, create, and per-campaign detail/prepare
review/ # edit/discard/regenerate drafts, confirm & send
api/gmail/ # OAuth connect + callback route handlers
auth/callback/ # Supabase Auth magic-link/OAuth callback
login/ # Supabase Auth UI (outside the shell)
components/
app-shell/ # sidebar, top bar, user menu
status-badge.tsx # colored pills for contact/campaign status
file-input-button.tsx
lib/
ai/ # Claude email generation
emails/ # template rendering + follow-up eligibility logic
gmail/ # OAuth client, token encryption, send, reply search
storage/ # attachment upload/download
supabase/ # server/browser clients, generated DB types
ui/ # shared button style constants
supabase/
migrations/ # schema, in order — see Database schema above
supabase login
supabase link --project-ref <your-project-ref>
supabase db push
This applies all four migrations in order, including the Storage bucket
and its RLS policies.gmail.send, gmail.readonly, userinfo.email under Data Access, and
add your own email (and anyone else testing) as a test user under
Audience — this app requests sensitive scopes that need Google
verification for public use, so it stays in Testing mode until that's
submitted separately.localhost during development:
https://<your-domain>/api/gmail/callback and
http://localhost:3001/api/gmail/callback.NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GMAIL_TOKEN_ENCRYPTION_KEY= # openssl rand -base64 32
ANTHROPIC_API_KEY= # optional
npm install
npm run dev
Connect the repo to Vercel and set the same environment variables there
(Production, Preview, and Development). Update the Google OAuth redirect
URI and Supabase's Auth → URL Configuration → Site URL to match your
production domain before inviting real users — both default to
localhost and will silently misroute confirmation emails and OAuth
callbacks otherwise.
Until Google verification is complete, add yourself (and anyone else testing) to both the Google Cloud test users list above and the database gate:
insert into allowed_senders (email) values ('[email protected]');