SnagTime: a self-hostable scheduling app you run yourself
Free, self-hostable scheduling app with booking links, Google Calendar sync, SMTP notifications, and Stripe test payments.
At a glance
- What is it?
- SnagTime is an MIT-licensed TypeScript scheduling app with booking links, Google Calendar sync, SMTP notifications and Stripe test payments. It runs locally on SQLite in one command, but the production path demands PostgreSQL 18, a long-running worker and your own infrastructure.
- Who is it for?
- Adopt SnagTime if you want to own the scheduling stack and are willing to run PostgreSQL 18, a worker process and your own OAuth and SMTP credentials. Do not adopt it if you need live payment processing: the release deliberately rejects Stripe live-mode keys, so it cannot take real money without work you would have to write and audit.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 21 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 17, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
What SnagTime solves, and who it is actually for
Calendly and its peers solve scheduling by renting you a hosted account. SnagTime takes the opposite position: the README says it "gives you the source code for your own scheduling system," and that framing decides who the project suits. If your organization already has a VPS, a domain and someone comfortable with environment variables, SnagTime turns scheduling into one more service you operate rather than one more subscription you pay for.
The feature list is the standard calendar-product set, not a subset: account registration, sign-in, password recovery and email verification; workspaces with members, invitations and switching; event types with multiple durations and optional test pricing; weekly availability, date overrides, buffers, minimum notice and booking windows; public booking links with time-zone handling and custom questions; and booking confirmation, rescheduling, cancellation and recovery links. That is a real product surface, and it is the reason a self-hosted alternative is worth considering at all.
The audience is narrower than the feature list suggests. The README states plainly that the local SQLite experience is "designed for demos, development, and personal experimentation," and that production is "an advanced self-hosting path, not a one-click managed service." Read that as the project telling you it is for engineers, not for a small business owner who wants a booking page by Friday.
How booking, calendar sync and email actually flow
The architecture is a Next.js application in apps/web with API routes, a Prisma layer over two schemas (SQLite for local work, PostgreSQL for production), and a separate background worker. The worker is not decorative. The .env.example file exposes OUTBOX_WORKER_ENABLED and OUTBOX_POLL_INTERVAL_MS, which tells you the design is an outbox: a booking writes a row, and the worker drains calendar and email jobs on a poll interval. The comment above those keys is explicit that you should "keep the Node process long-lived so calendar and email jobs continue to drain."
That single design choice explains most of the deployment constraints. Because jobs are drained by a process rather than fired inline during a request, the README says Vercel "is not supported out of the box because the current production design requires a long-running worker and PostgreSQL runtime roles." It is a correctness choice with an operational price, and the project pays it deliberately.
Integrations follow the same opt-in pattern. CALENDAR_PROVIDER defaults to "local", and the README says the default local provider makes no network calls. Google Calendar is configured with GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET and GOOGLE_REFRESH_TOKEN, or through environment-managed credentials that the .env.example restricts to a single immutable workspace ID. Email mirrors this: EMAIL_PROVIDER="local" gives you a local inbox, while SMTP_HOST, SMTP_PORT and SMTP_TLS_MODE switch you to real delivery. The credential-free demo therefore exercises the full booking path without touching Google or Stripe, which is a genuinely useful property for evaluating the app.
Installing SnagTime locally and taking a first booking
The README's five-minute path assumes Node.js 20.9 or newer (Node.js 24 is named as the verified runtime), npm and Git. Clone the repository and enter it:
git clone https://github.com/nateherkai/snagtime.git
cd snagtimeThen generate local configuration. The setup script creates an ignored .env.local, generates independent cryptographic secrets, and prints the local demo login once:
npm run setupIf you would rather control the organizer account, the README documents flags for that. Note the quoting, because the password contains a special character:
npm run setup -- --email you@example.com --password "YourStrong!Password7"Finally, install, migrate, seed and run in one command:
npm run demo:freeOpen http://localhost:3000 and sign in with the credentials the setup command printed. What you should see is a working scheduling app backed by SQLite, a local calendar adapter, a local email inbox and stub payments. No Google or Stripe account is involved. From there, the useful first exercise is creating an event type, setting weekly availability, and opening the public booking link in a second browser profile so you can watch a booking land and the confirmation appear in the local inbox.
Before wiring anything external, run the validation script the README lists. It checks the credential-free configuration without needing provider accounts:
npm run setup:checkThe production path is strict on purpose, and that is the main cost
The README describes the production architecture as "intentionally strict": PostgreSQL 18, forced row-level security, separate app and worker credentials, verified database TLS, and authenticated proxy ingress. The environment file backs this up with distinct DATABASE_URL, WORKER_DATABASE_URL and MONITOR_DATABASE_URL values, a DATABASE_ROLE setting, and four separate operator-only passwords for provisioning logins. The package.json scripts reinforce it with a long list of PostgreSQL guard checks, including RLS, authority, health and negative provisioning tests.
This is where a prospective adopter should slow down. Row-level security with separate credentials is a sound multi-tenant design, and the guard scripts suggest the project tests those boundaries rather than assuming them. But it also means the gap between the demo and a real deployment is not a config change. You need a host that supports a web service, a worker service, persistent PostgreSQL, secrets and HTTPS. The README names a Linux VPS or a container platform of that shape as the right fit, and names ChatGPT Sites as incompatible.
There is also a scope boundary worth stating plainly: the release "deliberately rejects Stripe live-mode keys." Stripe Checkout in test mode, webhook confirmation and refund handling are implemented, but taking real payments would require implementing and auditing live-mode support yourself. The README says so directly, and warns against advertising the project as a live payment processor.
Where SnagTime is the wrong tool
The clearest failure case is payments. If your booking flow needs to charge a card today, SnagTime will not do it. Test mode is implemented; live mode is refused by design. You would be writing and auditing that integration, including whatever compliance obligations follow from handling real transactions, and the README explicitly places those responsibilities on you.
The second case is hosting. If your team standardizes on Vercel, adopting SnagTime means running infrastructure outside that platform, because the outbox worker needs a long-lived process. Teams that picked a serverless host precisely to avoid operating a worker will find the architecture works against them.
The third case is operational appetite. The README lists what you are responsible for: infrastructure security, backups, provider configuration, deliverability, compliance and ongoing operations. Email deliverability is the one people underestimate. SMTP_HOST and SMTP_TLS_MODE will connect you to a provider, but whether your confirmations land in inboxes depends on a verified sender domain and your own sending reputation. Nothing in the repository fixes that for you.
Finally, the project status note is unambiguous that the local SQLite path is for demos and experimentation. Treating the SQLite setup as a production deployment because it works on your laptop would skip the entire PostgreSQL, RLS and credential-separation design the project built.
How it differs from Calendly and from rolling your own
Against a hosted scheduler like Calendly, the difference is not features, it is the operating model. Calendly runs the service, holds the calendar tokens and handles deliverability; you get an account and a bill. SnagTime inverts that. You hold the OAuth client secret and refresh token, you own the SMTP credentials, and you run the worker that turns a booking into a calendar event and an email. The trade is control and data location against operational work, and the README is honest that the work does not disappear.
Against writing your own scheduler, the difference is the unglamorous part you would otherwise rebuild: time-zone handling on public booking links, buffers, minimum notice and booking windows, rescheduling and cancellation with recovery links, and the token plumbing those flows need. The .env.example hints at how much of that is security-sensitive. BOOKING_CAPABILITY_KEY_ID, BOOKING_CAPABILITY_SECRET and BOOKING_CAPABILITY_KEYRING implement versioned signing keys for booking bootstrap capabilities, and the comment notes that on rotation you must keep prior id and secret pairs in the keyring until every old capability expires. TOKEN_ENCRYPTION_KEY and TENANT_CONTEXT_SECRET cover the rest. Rebuilding that correctly is weeks of work with real security consequences, which is the strongest argument for adopting SnagTime rather than starting from an empty repository.
Licence, upgrades and what maintenance looks like
SnagTime is MIT licensed, which is the permissive end of the spectrum: you can modify it, host it and ship it commercially, provided you keep the licence notice. The README separates the code from the running costs, and that distinction matters. The source is free under MIT and the local demo can cost nothing, but a public deployment can create third-party costs for hosting, a domain, managed PostgreSQL, transactional email volume and any connected provider fees. Google OAuth credentials can be created without paying SnagTime, and Stripe test mode is free. This is a description of the licence and the cost structure, not legal advice; if you plan to redistribute a modified version, read the LICENSE file and your own obligations.
On maintenance, the repository is not archived and the last push was on 2026-08-28, roughly three weeks before this writing, so the codebase is current. There are no retrieved releases, so the project appears to be consumed from the main branch rather than from versioned tags. That changes your upgrade story: you pull, then run the verification scripts the project ships. The package.json includes ci:generated-drift, ci:postgres-guards, ci:postgres-rls and ci:backup-contract, and the README lists npm run test, npm run typecheck, npm run lint and npm run build. Those commands are the upgrade checklist, and the presence of a generated-drift check suggests the project expects schema and generated artifacts to be regenerated rather than hand-edited.
The upgrade cost is concentrated in the database. Because production relies on forced row-level security and separate app, worker and monitor credentials, a migration that touches tenancy is not a routine deploy. Budget for running the PostgreSQL guard scripts against a staging database before promoting anything. And note that npm run db:reset destroys the local SQLite database, so it is a demo tool, not a recovery tool.
Editorial conclusion
Adopt SnagTime if you want to own the scheduling stack and are willing to run PostgreSQL 18, a worker process and your own OAuth and SMTP credentials. Do not adopt it if you need live payment processing: the release deliberately rejects Stripe live-mode keys, so it cannot take real money without work you would have to write and audit. Before committing, verify that your host can run a long-running Node web service plus a separate worker with persistent PostgreSQL, because Vercel and ChatGPT Sites are both named as unsupported production hosts in the README.
Frequently asked questions
What is SnagTime and who is it for?
SnagTime is a free, self-hostable scheduling app written in TypeScript and released under the MIT License. It covers booking links, availability, Google Calendar sync, SMTP notifications and Stripe test payments, and the README frames it for developers and personal experimentation rather than as a managed service.
How do I install SnagTime locally?
You need Node.js 20.9 or newer, npm and Git. Clone the repository, run npm run setup to create .env.local and print the demo login, then run npm run demo:free, which installs, migrates, seeds and starts the app on http://localhost:3000.
Does SnagTime work with Google Calendar and Stripe?
Both are opt-in. Google Calendar uses CALENDAR_PROVIDER with OAuth client credentials, and payments use Stripe Checkout in test mode with webhook confirmation and refund handling. The default local demo uses a local calendar adapter and stub payments, and the release deliberately rejects Stripe live-mode keys.
Can I deploy SnagTime to Vercel or as a static site?
No. The README states SnagTime is a dynamic application that needs server-side Node.js execution, a persistent database, webhook endpoints and a continuously running background worker. It names Vercel as unsupported out of the box and ChatGPT Sites as an incompatible production host.
Community notes