HaloMail Documentation
An open-source platform for meeting scheduling and portfolio contact forms — one typed API, a generated SDK, webhooks, and a polished dashboard.
Raw docs page — the seed of HaloMail's own docs engine. Real content, no external framework.
Introduction
HaloMail gives every user a public booking page and an embeddable contact form, behind a clean ConnectRPC API that speaks gRPC, gRPC-Web, and plain JSON from a single definition. The dashboard, the SDK, and your own integrations all share one contract.
Scheduling
Booking pages, availability, Google & Outlook sync, confirmations, reschedule/cancel.
Contact forms
Embeddable widget, REST + SDK, spam protection, rate limiting, storage, forwarding.
Email designer
Themes — Minimal, Apple, Notion, Glass, Terminal — plus custom HTML and live preview.
Developer
API keys, signed webhooks, generated SDK, OpenAPI, audit logs.
Quickstart
Run the whole stack locally. Full guide: docs/DEVELOPMENT.md.
# 1. infra: postgres, redis, mailpit, jaeger
task up
# 2. generate code from the proto contracts
task proto
# 3. deps + database schema
task bootstrap && task migrate
# 4. run the API (all services in one process)
task api:run # http://localhost:8080
:8025 and traces by Jaeger at :16686 — nothing leaves your machine.Authentication
Two credential types, both via the Authorization header:
| Caller | Header | Obtained from |
|---|---|---|
| Dashboard / user | Bearer <jwt> | AuthService/Login |
| Developer / server | ApiKey hl_live_… | Dashboard → API Keys |
The gateway verifies the credential, then injects the caller's
user_id, org_id, and scopes into the request.
Services
The backend is a set of modular Go microservices behind one gateway.
| Service | Owns |
|---|---|
gateway | Public edge: auth, rate limiting, REST/OpenAPI, aggregation |
identity | Users, orgs, auth/JWT, API keys, audit log |
scheduling | Event types, availability, bookings, calendar sync |
contact | Forms, messages, spam protection, forwarding |
template | Email themes, render, preview |
notification | Email delivery, webhook dispatch |
Scheduling
Availability is weekly rules in the owner's timezone, minus date overrides, existing bookings (plus buffers), and busy time from connected calendars — sliced into the event type's duration and projected into the invitee's timezone.
List open slots (public)
POST /halomail.scheduling.v1.BookingService/ListSlots
# body
{ "event_type_id": "evt_…",
"from_date": "2026-06-15",
"to_date": "2026-06-20",
"invitee_timezone": "Europe/Berlin" }
Contact forms
Drop the widget on any site; submissions are rate-limited, spam-scored, stored, forwarded to your inbox, and pushed to webhooks.
<!-- embed -->
<script src="https://<api-host>/widget.js"
data-form="contact" defer></script>
Email themes
Minimal
Clean, system font, whitespace.
Apple
SF-style, soft shadows, rounded.
Notion
Document-like, neutral dividers.
Glass
Translucent panels, gradients.
Terminal
Monospace, dark, developer.
Custom
Your own HTML with {{variables}}.
Calling the API
ConnectRPC speaks plain JSON over HTTP, so any HTTP client works — no gRPC tooling required.
# Log in and get a session
curl https://<api-host>/halomail.identity.v1.AuthService/Login \
-H 'Content-Type: application/json' \
-d '{"email":"me@example.com","password":"••••••"}'
# Use the access token
curl https://<api-host>/halomail.identity.v1.UserService/GetCurrentUser \
-H 'Authorization: Bearer <access_token>' \
-H 'Content-Type: application/json' -d '{}'
Endpoints
All procedures are POST /halomail.<service>.v1.<Service>/<Method>.
| Procedure | Purpose | Auth |
|---|---|---|
identity.v1.AuthService/Register | Create account + session | none |
identity.v1.AuthService/Login | Email + password → session | none |
identity.v1.ApiKeyService/CreateApiKey | Issue an API key | Bearer |
scheduling.v1.BookingService/ListSlots | Open slots for an event type | none |
scheduling.v1.BookingService/CreateBooking | Book a slot | none |
contact.v1.MessageService/SubmitMessage | Submit a contact form | none |
template.v1.TemplateService/RenderPreview | Render an email theme | Bearer/ApiKey |
notification.v1.WebhookService/CreateWebhook | Subscribe to events | Bearer/ApiKey |
Webhooks
Subscribe to booking.created, booking.cancelled,
booking.rescheduled, and message.received. Each delivery is
signed with HMAC-SHA256 in an X-HaloMail-Signature header; verify it before trusting the payload.
Errors
Errors use Connect status codes mapped from the domain:
| Domain | Connect code | HTTP |
|---|---|---|
| invalid input | invalid_argument | 400 |
| not found | not_found | 404 |
| conflict | already_exists | 409 |
| unauthorized | unauthenticated | 401 |
| rate limited | resource_exhausted | 429 |
Self-hosting
HaloMail is open source — run it wherever you can run a container. In monolith mode it's a single binary backed by PostgreSQL and, optionally, Redis.
# build one image, run it anywhere
docker build -f deploy/Dockerfile --build-arg SERVICE=gateway -t halomail .
docker run -p 8080:8080 --env-file .env halomail
All configuration is via environment variables — see
.env.example in the repository.
HaloMail is MIT-licensed and open to contributions — see CONTRIBUTING.md.