Lunar Back home

Documentation

Everything you need to import events, build cross-calendar campaigns, and integrate Lunar with your existing booking stack.

Getting started

A typical Lunar setup takes under 5 minutes:

  1. Sign up and pick a default calendar (Lunar, Hijri, or Hebrew) during onboarding.
  2. Import your past customer events — by CSV upload, by webhook from your booking software, or via the API.
  3. Create one or more campaigns. Choose a calendar type and the days-before-anniversary offset for the send.
  4. Test-send a sample to your own inbox, then enable the campaign.

The daily dispatcher checks for due anniversaries every morning and sends the matching email.

Webhook ingestion

Push events from your existing booking platform with a single POST request. The endpoint is idempotent on external_id, so replays do not create duplicates.

POST /api/public/sync-external-event
Authorization: Bearer <YOUR_API_KEY>
Content-Type: application/json

{
  "external_id": "booking-12345",
  "customer_email": "guest@example.com",
  "customer_name": "Mei Chen",
  "event_name": "Wedding reception",
  "event_date_gregorian": "2024-05-15",
  "metadata": { "venue": "Garden Hall" }
}

Successful response (201 Created on first sync, 200 OK on update):

{
  "ok": true,
  "event_id": "f0e1d2c3-...",
  "created": true,
  "converted": true
}

Status codes you may receive:

  • 200 — event updated (already existed)
  • 201 — event created
  • 400 — invalid JSON or schema validation failed (response includes details)
  • 401 — missing, invalid, or revoked API key
  • 413 — payload exceeds 32 KB
  • 429 — rate limit exceeded; honor the Retry-After header
  • 500 — server error; safe to retry with the same external_id

Rate limit: 120 requests per minute per API key. When exceeded, the response includes a Retry-After header (seconds until the next minute).

Retry guidance: Because every write is idempotent on (account, external_id), retries are safe. Recommended policy: exponential backoff (1s, 2s, 4s, 8s, 16s) on 5xx and 429; do not retry 4xx other than 429.

Updating an event: Send the same external_id with the changed fields. Omitted optional fields (customer_name, metadata) preserve the previously stored value rather than nulling it out.

CORS: The endpoint accepts cross-origin requests from any origin (it is intentionally public, secured by Bearer auth).

CSV import

From the Events page, upload a CSV with at least an email column and a date column. Lunar auto-detects header names and shows a column-mapping step before import. Date strings are parsed flexibly (YYYY-MM-DD, MM/DD/YYYY, etc.).

Campaign templates

Campaigns are simple text or HTML templates with merge variables. Available variables:

  • {{ customer_name }} — falls back to the part before @ if name is missing
  • {{ customer_email }}
  • {{ event_name }}
  • {{ event_date }} — original Gregorian date
  • {{ anniversary_date }} — the converted next-occurrence date
  • {{ business_name }}

Set Trigger offset to send before, on, or after the anniversary. 0 sends on the day; -7 sends a week before; 3 sends three days after.

API keys

Create keys from the API Keys page. Keys are shown once at creation; we only store a hash. Revoke compromised keys immediately — revocation takes effect on the next request.

Compliance

Every email Lunar sends includes a one-click unsubscribe link. Unsubscribes are tenant-scoped and applied automatically across all your campaigns. The dispatcher also skips any address you add to the Suppression list on the Activity page.

Need a hand?

Email hello@lunar.app and we'll get back within one business day.