These are CORS-enabled — designed to be called from the browser on your
own domain — and authenticated with your project’s
publishable key.
The management endpoints are what your agent (or backend) operates: the
full loop of reading user data, creating and controlling campaigns, and
logging its decisions — server-to-server:
Customer-owned management calls use your project’s secret key. Galinum’s
hosted evaluator uses a separate scoped
pk_agent_… key for required reads and
the lease-fenced hosted-loop routes. It cannot use ordinary campaign, status,
goal, segment, media, launch-policy, or campaign-activation writes. Those writes
return 403. Launch-policy and campaign-activation GET requests remain available.
Management endpoints are not CORS-enabled, so
call them from an agent or backend, never the browser. See
Authentication for the exact key boundaries. There is
no DELETE in the agent management API: campaigns end, variants retire with weight: 0, and goals
archive.
Prepared campaigns and deployment activation
The product server supports launch policy and activation on self-hosted installations. DeclaresourceChanges alongside content in ordinary campaign
POST/PATCH. Create uses { changes }; replacement PATCH requires
{ expectedRevision, changes }. Campaign detail returns { revision, changes }
in sourceChanges. Omission preserves the declaration and approval.
Content and source replacement commit together. Approval survives edits, but
current source coverage and channel readiness remain required. Source-managed
preparations reject generic association replacement with 409 before changing
copy. See GitHub integration for the complete activation workflow.
Base URL
Quickstart
Identify a user:GET /api/v1/audiences/capabilities catalogs the observed traits, events,
and properties; POST /api/v1/audiences/check evaluates an expression
without saving anything and returns the exact matching count, diagnostics,
and a bounded sample; POST /api/v1/audiences/explain traces one user’s
match. A campaign’s audience is stored as an immutable version the campaign
pins — see Audiences.
Message presentation
Every message states how the built-in renderer presents it.presentation
is required on create and on any message edit:
"toast"— a compact card in the bottom-right corner. The least interruptive option, and the right default for most announcements."modal"— a centered announcement over a full-screen backdrop. Use it for flagship moments that deserve to interrupt.
presentation. Campaigns
authored before the field existed keep their original look: content with
media resolves to modal, content without it resolves to toast.
Message media
A message can carry one optional image. Upload it first (secret key, multipart), then reference the returned managed URL asmessage.media:
alt text or an explicit decorative: true.
Campaign writes verify that the referenced object still exists. Uploaded
objects are immutable and retained even after campaigns stop referencing them.
Referencing a missing object returns 400. Upload the image again and retry with
the new URL. A 503 (Could not verify the media object right now. Try again.) means storage was unreachable; the
same request is safe to retry.
Email content
An email message uses a channel-specific shape:subject and the Markdown body are required. previewText and cta are
optional. Email CTA links must use http:// or https://. In-app fields
such as title, presentation, media, and campaign pages are rejected.
Galinum renders the email on the server, adds the project’s company address,
and includes signed unsubscribe links and one-click headers. It reads each
recipient from the end user’s email trait and skips suppressed addresses.
An accepted campaign email makes the recipient active for MAU billing.
Galinum accepts at most 3 campaign emails per project recipient in any rolling
24-hour window. A send above the limit is recorded as frequency_capped, is
not delayed, and does not accrue MAU. Transactional service email is exempt.
Goals
A goal is an optional construct for the optimized-communication loop: a target event to move, free-formguardrails the agent must respect, and an
approvalMode (require_human = the agent proposes and waits; auto = it
may launch on its own). Direct communications never need one — the
campaign endpoints work without any goal existing. Linking a campaign to a
goal (goalId on create or PATCH) wires up conversion measurement: a
tracked event matching the goal’s target event automatically resolves the
campaign’s exposed deliveries to converted. Exposure is shownAt for web
in-app and deliveredAt for email. When evaluating a different tracked event,
GET /api/v1/campaigns/{id}/conversions?event= computes the exact per-variant
post-exposure aggregate without returning raw event or delivery rows.
Delivery semantics
Web in-app delivery starts when the SDK fetches messages. Galinum evaluates every running campaign inside its delivery window — a campaign can carry an optionaldeliverFrom/deliverUntil window that schedules its start and expires it
automatically — matches its targeting against the user’s traits and events,
assigns a stable A/B variant, and records a delivery. The delivery starts
out queued: the impression is counted only when the client reports
shown through the delivery feedback endpoint — the React SDK does this
automatically when a message actually renders. If you consume /messages
directly, send { "type": "shown" } when you display the message;
otherwise it never counts as shown. Each user gets each campaign at most
once; once feedback resolves a delivery, the message stops coming back.
Email uses a push worker instead. It evaluates the same pinned audience in
bounded batches, reserves the delivery before sending, and keeps enrolling
newly matching users while the campaign runs. The worker records
frequency-capped skips. Provider events update sent, delivered, opened,
clicked, bounced, and complaint state. Unsubscribes,
permanent bounces, and complaints suppress that address across the project.
Conventions
- Timestamps in responses are epoch milliseconds. Time filters accept epoch ms or an ISO-8601 date; campaign delivery windows require an explicit timezone offset on ISO strings.
- Paginated management lists take
?page=&perPage=(default 50, max 100) and return{ …, "total", "page", "pageCount" }.