Reinvention for the people who run toward the fire
Trade the night shift for a life you actually built.
OffShift Consulting helps healthcare workers, first responders, and overworked professionals turn lived experience into books, speaking, digital products, and creator income — without burning the bridge before the bridge is built.
Built by an ER nurse, not a guru Healthcare → creator playbooks Speaking · Books · Digital Offers
OFFSHIFT · 001
FocusReinvention
ForBurnt-out pros
Built onLived reps
What OffShift Does
Three doors out. One plan that actually pays.
Pick the door that fits the season you’re in. Each one is built around lived experience — not theory — and engineered to move you from “surviving the shift” to owning your time.
1:1
Consulting Strategy
Private strategy intensives for nurses, medics, and frontline pros pivoting into creator, coaching, or business income.
Audience & niche clarity in one session
Content engine you can run on 2 hours/day
Exit math: when you actually can drop shifts
From $497 · 90-min intensive
Stages
Speaking & Podcast Bookings
Keynotes, panels, and podcast appearances on healthcare burnout, frontline reinvention, and turning trauma into traction.
Conferences, hospital systems, nursing schools
Creator and entrepreneurship podcasts
Custom talks for healthcare orgs
Custom · Speaking fee on inquiry
Self-paced
Books & Digital Products
Frameworks, playbooks, and books that compress a decade of frontline + creator lessons into a weekend you can act on.
The OffShift Exit Blueprint (flagship)
Companion templates & scripts
Reader-only paid community access
From $47 · Instant access
Free · Lead Magnet
The OffShift Exit Roadmap
A 14-page tactical PDF for healthcare workers and overworked pros who are done being the “strong one” at work and invisible at home. Inside: the 5 exit lanes, the 90-day pivot calendar, and the income floor math that tells you exactly when you can drop a shift.
The 5 reinvention lanes ranked by speed & risk
A 90-day calendar you can run on two off-days a week
Exit math: the income floor that makes dropping a shift safe
12 content hooks proven for nurse & first-responder audiences
Speaking · Consulting · Partnerships
Book OffShift for your stage, podcast, or team.
I speak candidly about healthcare burnout, frontline reinvention, and how lived experience becomes an income engine. Talks are practical, story-driven, and built for the people doing the work — not the slide deck.
Currently booking talks for late 2026. Past clients available on request.
Also from the founder
Don't Flinch — a memoir
Foster care to the ER and back to myself. The story behind the framework — published on Amazon.
Before OffShift Consulting, there was the long climb back. Don't Flinch is the story behind the framework — foster care, paramedic shifts, COVID-era ERs, the moments that broke and the ones that rebuilt.
Not a self-help book. Not a victory lap. A memoir for anyone who has carried other people's worst days and is starting to wonder what theirs is supposed to look like.
A no-fluff, weekend-actionable playbook for healthcare workers and overworked pros who want a real plan to build creator income, ship a book, land speaking gigs, and finally drop a shift — without blowing up the income they still depend on.
For nurses, medics, first respondersCreators in healthcare nichesAspiring authors & speakers
If this sounds like you…
You’re tired of trading your nervous system for a paycheck.
You have lived experience worth packaging — and no clear playbook.
You want a real exit math model, not “quit your job” fantasy bait.
You want to film, write, and ship — without losing your weekends.
What’s inside
The 5-lane Reinvention Map (creator, coach, author, speaker, product)
Exit Math Worksheet: your real number to drop a shift
The 2-hour Content Engine for clinicians who hate “hustle” content
The Story-to-Offer framework (turn a single shift into a product)
Book-in-a-Weekend outline template (KDP-ready)
Speaker one-sheet template & cold pitch scripts
Questions
FAQ
Is this just for nurses?
No. It’s built first for healthcare workers because that’s the lived experience behind it — but the frameworks apply to any overworked professional packaging real-world expertise into creator income.
I have zero followers. Will this still work?
Yes. The Content Engine and Story-to-Offer framework are designed for zero-audience starts. The point isn’t to go viral — it’s to build a small, paying audience fast.
How is this different from another “quit your job” course?
This is the opposite. The Exit Math worksheet is built to keep you in your job until your numbers say it’s safe to step out. No fantasy. No bridge-burning.
Do I get future updates?
Yes. Buyers get every revision of the Blueprint for life, plus future bonus templates as they’re added.
What if it isn’t for me?
7-day refund, no questions asked. Just reply to the receipt email.
Customers
Access your purchase
Your Blueprint and bonus templates live in your Gumroad library. Log in with the email you used at checkout to re-download anytime.
Lightweight demo dashboard reading directly from Supabase. Connect your project below, run the included SQL, and your forms will populate these tables in real time.
Supabase configuration
Paste your project URL and anon key. Saved to this browser only.
Supabase SQL setup — tables, RLS, and policies (click to view)
-- ============================================================
-- OffShift Consulting LLC — Supabase setup
-- Paste this entire block into Supabase SQL editor and run.
-- ============================================================
-- extensions
create extension if not exists "pgcrypto";
-- ============ TABLES ============
create table if not exists public.leads (
id uuid primary key default gen_random_uuid(),
created_at timestamptz not null default now(),
first_name text not null,
email text not null,
interest text,
message text,
source text default 'website'
);
create table if not exists public.bookings (
id uuid primary key default gen_random_uuid(),
created_at timestamptz not null default now(),
name text not null,
organization text,
email text not null,
type text not null,
details text not null,
status text not null default 'new' -- new | reviewing | accepted | declined
);
create table if not exists public.customers (
id uuid primary key default gen_random_uuid(),
created_at timestamptz not null default now(),
full_name text,
email text not null unique,
stripe_customer_id text
);
create table if not exists public.orders (
id uuid primary key default gen_random_uuid(),
created_at timestamptz not null default now(),
customer_email text not null,
product_name text not null,
amount integer not null, -- in cents
currency text not null default 'usd',
status text not null default 'pending', -- pending | paid | refunded | failed
stripe_session_id text
);
create table if not exists public.content_access (
id uuid primary key default gen_random_uuid(),
created_at timestamptz not null default now(),
customer_email text not null,
product_name text not null,
access_granted boolean not null default false
);
create index if not exists idx_leads_email on public.leads(email);
create index if not exists idx_bookings_email on public.bookings(email);
create index if not exists idx_orders_email on public.orders(customer_email);
create index if not exists idx_orders_status on public.orders(status);
-- ============ ROW LEVEL SECURITY ============
alter table public.leads enable row level security;
alter table public.bookings enable row level security;
alter table public.customers enable row level security;
alter table public.orders enable row level security;
alter table public.content_access enable row level security;
-- INSERTS from the anon (browser) role: only the forms that should be public
drop policy if exists "anon can insert leads" on public.leads;
create policy "anon can insert leads"
on public.leads for insert
to anon
with check (true);
drop policy if exists "anon can insert bookings" on public.bookings;
create policy "anon can insert bookings"
on public.bookings for insert
to anon
with check (true);
-- Customers + orders should NEVER be inserted by anon directly in production.
-- They are written server-side by your Stripe webhook (service role) which bypasses RLS.
-- SELECT policies for the demo dashboard:
-- WARNING: enabling anon SELECT on these tables exposes lead/booking/order data publicly.
-- Recommended for production: remove these SELECT policies and call a Postgres RPC
-- behind an admin password, or move the dashboard to an authenticated route.
drop policy if exists "demo anon read leads" on public.leads;
create policy "demo anon read leads"
on public.leads for select to anon using (true);
drop policy if exists "demo anon read bookings" on public.bookings;
create policy "demo anon read bookings"
on public.bookings for select to anon using (true);
drop policy if exists "demo anon read customers" on public.customers;
create policy "demo anon read customers"
on public.customers for select to anon using (true);
drop policy if exists "demo anon read orders" on public.orders;
create policy "demo anon read orders"
on public.orders for select to anon using (true);
-- Paywall verification: allow anon to READ the orders for a given email
-- (RLS above already permits anon select). In production, replace with an
-- RPC like public.has_paid_access(p_email text) returning boolean,
-- and revoke direct select.
--
-- Example RPC:
-- create or replace function public.has_paid_access(p_email text, p_product text)
-- returns boolean language sql security definer as $$
-- select exists (
-- select 1 from public.orders
-- where lower(customer_email) = lower(p_email)
-- and product_name = p_product
-- and status = 'paid'
-- );
-- $$;
-- grant execute on function public.has_paid_access(text, text) to anon;
Stripe webhook — minimal serverless verification example (click to view)
// ============================================================
// Stripe → Supabase: minimal Node serverless webhook
// Deploy to Vercel/Netlify/Cloudflare/Render. NEVER ship Stripe
// secret keys to the browser. This endpoint is the source of truth
// for marking orders as "paid".
// ============================================================
//
// ENV VARS required on the server:
// STRIPE_SECRET_KEY
// STRIPE_WEBHOOK_SECRET
// SUPABASE_URL
// SUPABASE_SERVICE_ROLE_KEY (server-only, bypasses RLS)
//
// Stripe dashboard → Developers → Webhooks → add endpoint:
// https://YOUR-DOMAIN/api/stripe-webhook
// Events: checkout.session.completed, charge.refunded
//
// package.json: { "type": "module", "dependencies": { "stripe": "^14", "@supabase/supabase-js": "^2" } }
import Stripe from 'stripe';
import { createClient } from '@supabase/supabase-js';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const supabase = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_SERVICE_ROLE_KEY,
{ auth: { persistSession: false, autoRefreshToken: false } }
);
export const config = { api: { bodyParser: false } };
async function readRaw(req) {
const chunks = [];
for await (const c of req) chunks.push(c);
return Buffer.concat(chunks);
}
export default async function handler(req, res) {
if (req.method !== 'POST') return res.status(405).end();
const sig = req.headers['stripe-signature'];
let event;
try {
const raw = await readRaw(req);
event = stripe.webhooks.constructEvent(raw, sig, process.env.STRIPE_WEBHOOK_SECRET);
} catch (err) {
return res.status(400).send(`Webhook signature failure: ${err.message}`);
}
if (event.type === 'checkout.session.completed') {
const s = event.data.object;
const email = (s.customer_details?.email || s.customer_email || '').toLowerCase();
const product_name = 'OffShift Exit Blueprint';
// Upsert customer
await supabase.from('customers').upsert(
{ email, full_name: s.customer_details?.name || null, stripe_customer_id: s.customer || null },
{ onConflict: 'email' }
);
// Record order as paid
await supabase.from('orders').insert({
customer_email: email,
product_name,
amount: s.amount_total ?? 0,
currency: s.currency ?? 'usd',
status: s.payment_status === 'paid' ? 'paid' : 'pending',
stripe_session_id: s.id
});
// Grant content access
await supabase.from('content_access').insert({
customer_email: email, product_name, access_granted: true
});
}
if (event.type === 'charge.refunded') {
const c = event.data.object;
const email = (c.billing_details?.email || '').toLowerCase();
if (email) {
await supabase.from('orders').update({ status: 'refunded' })
.eq('customer_email', email).eq('status', 'paid');
await supabase.from('content_access').update({ access_granted: false })
.eq('customer_email', email);
}
}
res.status(200).json({ received: true });
}