DEVELOPER · API

Integrate your event stack.

Signed webhooks for every photo, event, milestone, and guest signal. Bearer-auth callables for tenant-admin actions and GDPR exports. Anonymous token-gated callables for guest self-service. All real, all verified against the source tree.

  • 8webhook events
  • 7callable endpoints
  • 4auth modes
  • 1region (us-central1)
AUTH

Four auth modes, one mental model.

Every endpoint picks one auth path based on what calls it. Webhook deliveries are signed outbound. Browser callables present a Firebase ID token. Anonymous flows (unsubscribe, gallery password) carry a short-lived HMAC token. Guest self-service (photo deletion) ties integrity to the upload session.

HMAC-SHA256 signed

Outbound webhook delivery. Verify the `Fotowall-Signature` header against your event's webhook secret before trusting the body.

Public (no auth)

No authentication. Integrity is gated on a domain-specific check (uploader sessionId, gallery password, etc.). Rate-limited per IP.

Public token (HMAC)

No Firebase Auth required. Gated on a short-lived HMAC-signed token that the platform embeds in the relevant link (unsubscribe email, etc.).

Tenant admin (bearer)

Requires a Firebase Auth ID token with role=admin (or superadmin) custom claim. Tenant-scoped unless invoked by a superadmin with an explicit tenantId.

QUICKSTART

First webhook in 3 steps.

  1. 01

    Stand up an endpoint

    Anywhere that accepts POST + JSON over HTTPS. Zapier Catch Hook, Make Custom Webhook, Slack Incoming Webhook, a Cloud Run service, a tiny Lambda — all work as-is. No SDK required.

  2. 02

    Add the URL to your event

    In the admin dashboard: event settings → Integrations → Webhooks. Paste the URL, pick which events fire it (photo.uploaded, photo.approved, etc.), and copy the generated webhook secret somewhere safe — you'll need it to verify the signature.

  3. 03

    Verify and ship

    Hit "Send test event" in the admin. A signed photo.uploaded payload lands at your URL within a couple of seconds. Verify the Fotowall-Signature header (HMAC-SHA256 over the raw body), return 200, and you're live.

REFERENCE

Every endpoint, grouped.

15 surfaces total — 8 outbound webhook events and 7 callable functions. Each link below opens the per-endpoint page with request and response schemas, curl + fetch examples, error cases, and a link to the source file in functions/src/.

Webhooks

8
RATE LIMITS

Per-endpoint caps, fail-open defaults.

We tune limits per endpoint based on what realistic usage looks like. Most fail-open — a Firestore blip on the rate-limit counter won't block a legitimate call. Exceptions are the recap and export endpoints, where the cooldown is enforced atomically against state writes.

Endpoint Limit Behavior on breach
webhooks (per event) 20 URLs configured / event, 5s timeout / delivery, 4 attempts total Excess URLs ignored. Delivery marked failed after 4 attempts.
requestPhotoDeletion 30 calls / IP / hour HttpsError resource-exhausted.
getEmailPreferences 20 calls / IP / hour HttpsError resource-exhausted. Fails open on counter error.
setEmailPreferences 20 calls / IP / hour AND 10 writes / token / 24h HttpsError resource-exhausted. Fails open on counter error.
verifyGalleryPassword No hard cap (yet); App Check planned
sendEventRecap 1 send / event / 24h, up to 5000 recipients HttpsError resource-exhausted. Cooldown enforced atomically.
claimSubdomain No hard cap (auth-gated)
accountDataExport 1 export / tenant / 6h, max 5000 signed photo URLs HttpsError resource-exhausted. Cooldown enforced.
ERRORS

Standard error shape for callables.

Callable functions throw Firebase HttpsError values that serialize over the wire as a structured JSON object. Webhook deliveries have no error envelope — your endpoint's response code dictates retry behavior.

HttpsError JSON shape

{
  "error": {
    "code":    "permission-denied",
    "message": "Admin role required to export tenant data.",
    "status":  "PERMISSION_DENIED"
  }
}

Common codes

CodeMeaning
unauthenticatedMissing or invalid Firebase Auth ID token.
permission-deniedToken is valid but the caller lacks the required role / tenant scope.
invalid-argumentRequest body failed validation (missing field, wrong type, regex mismatch).
not-foundReferenced doc (event, tenant, photo) does not exist.
abortedConflict on an atomic operation (e.g., recap-in-progress lock held).
resource-exhaustedRate limit or cooldown was hit.
internalServer-side error (Firestore, Storage, Resend). Sentry-reported. Usually safe to retry.
CHANGELOG · SUPPORT

Stay current. Ask for help.

API changelog

Breaking changes, new endpoints, deprecations. See the platform changelog for everything — API-affecting entries are tagged.

Open changelog

Integration support

API questions, schema requests, partner integrations. Founder reads every ticket; usual reply window is one business day.

Open a ticket

Security disclosure

Found a vulnerability? Coordinated disclosure path lives on the trust page. Don't post it publicly.

Trust page
FAQ

Questions integrators ask.

Is there a JS / Python / Go SDK?

Not yet. The callable endpoints work with the Firebase Functions SDK (recommended — it handles ID tokens and payload framing). The webhook delivery format is a plain signed HTTPS POST, so any HTTPS-capable stack consumes it. A first-party JS SDK is on the roadmap; meanwhile, plain fetch is the path.

What is the base URL for callable endpoints?

https://us-central1-freedomgrc-photowall.cloudfunctions.net/{functionName}. We host in us-central1 only — single region keeps latency predictable and rules simple. Multi-region failover is a Premier/Enterprise conversation.

Are there rate limits?

Per-endpoint, per-IP, and per-tenant — see each endpoint page for the specific cap. Most fail-open (a Firestore blip won't block a legitimate request). sendEventRecap enforces a hard 24h cooldown per event. accountDataExport enforces 6h per tenant.

How do I get my Fotowall event ID?

Open the admin dashboard, pick the event, and copy the URL slug after ?event=. Or look up events/{slug} in Firestore directly if you have console access.

How do I report a bug or request an endpoint?

Open a ticket at /contact with `topic=api` — the API surface is small and we prioritize integrator feedback. Security issues: see /trust for the disclosure path.

START_BUILDING

Wire one URL. Watch the events fly.

Or hand this page to your integrations engineer and let them ship.

Webhook reference Pre-built integrations