Notifications as code · sovereign in the EU

Notifications that
always find their way.

The EU-sovereign notification platform you program, not configure. Write your workflows in TypeScript; palumb runs the managed, durable control plane that delivers them across email, SMS, push and in-app.

Start sending free
Made in the EUEU-owned · data resident
no provider keys in your code open source core
workflow · welcome.ts running
on Signed up trigger
send Welcome day 0
wait 6h12h18h24h30h36h42h48h
if idle Nudge day 2
wait 6h12h18h24h30h36h42h48h
if idle Last call day 4
EU · Paris one workflow · timers survive restarts

In one sentence

What is palumb?

palumb is an EU-sovereign notification platform that you program instead of configure. Rather than building flows in a low-code canvas, you write each notification workflow as plain TypeScript and own, review and test it like any other code. palumb runs the managed control plane and a durable runtime (Restate) that authenticates your app, stores your subscribers and channel credentials, and delivers across email, SMS, push, in-app and webhook. Because the runtime is durable, a workflow that says “wait an hour, batch, then send” survives crashes and deploys without lost or double-sent messages — you write no checkpointing or idempotency code. The control plane, the durable runtime and your data all live in the EU, run by an EU provider, with no transfer to the US. The core is open source and self-hostable.

The wedge

Four things most teams have to trade off. You shouldn't have to.

Each of these exists somewhere. palumb's bet is that one class of EU teams should get all four at once.

01

EU-sovereign

Your notification data stays in the EU, run by an EU provider. Not a US service with a region toggle.

02

Managed

We operate the control plane and the durable runtime. No queues, workers, or Restate cluster for you to run.

03

Durable

Wait, batch, branch and survive a crash mid-send. No lost notifications, no double sends — built in.

04

Code-first

Workflows are plain TypeScript you own, review and test. No low-code canvas to fight.

01 · How it works

You write the workflow.
palumb runs the control plane.

The boundary is the whole point. You never touch a provider's API keys, and palumb never sees your business logic. Each side owns what it's best at.

you write — plain TypeScript

Your workflow code

One workflow per name. The trigger names it directly — no giant switch over event types. Branch, loop, call your own APIs. It's just code.

order-shipped.ts copy
export default workflow("order-shipped",
async ({ payload, step }) => {
const orders = payload.items
if (!orders.length) return
 
await step.send("digest", "email", () => ({
subject: orders.length + " orders shipped",
body: orderSummary(orders),
}))
})
palumb runs — the managed service

The control plane

The only part that holds state and secrets. It does exactly four jobs — and stays deliberately dumb about your logic.

  • 1 Authenticates you Machine-to-machine API keys. No human login, no JWT.
  • 2 Knows your audience Subscribers, topics and subscriptions — target one user or a whole topic.
  • 3 Holds your channels Provider credentials, encrypted at rest, never returned to you.
  • 4 Delivers, durably Receives triggers, makes the real provider calls, records every attempt.
Data flow: your app sends a trigger to palumb's control plane via POST /v1/events. palumb — authentication, audience, channels and a durable runtime, all hosted in the EU — runs your TypeScript workflow and delivers the message to your channels (email, SMS, push, in-app). Your business logic and provider keys never cross the boundary. YOU WRITE Your workflow plain TypeScript POST /v1/events PALUMB RUNS · EU SOVEREIGN Control plane + durable runtime auth · audience channels · delivery delivers DELIVERS TO Channels email · SMS · push · in-app
A trigger in (/v1/events), durable delivery out — your business logic and provider keys never cross the line.

02 · Notifications as code

The hard parts, handled — without leaving your editor.

The single send is easy. palumb's unit is the workflow: waiting, batching, branching, and surviving failure. The runtime underneath is durable — the difference that shows up in production, not in the demo.

  • Durable steps “Wait a minute, then batch and send” survives a process restart. You write no checkpointing code.
  • Idempotent sends Every send is wrapped so a retry after a crash never delivers twice.
  • Building blocks Durable patterns you'd otherwise hand-roll — Digest collapses a burst into one message.
order-shipped.workflow.ts copy
import { workflow } from "@palumb/sdk/bridge"
 
export default workflow("order-shipped", async ({ payload, step }) => {
const orders = payload.items
if (orders.length === 0) return
 
await step.send("digest", "email", () => ({
subject: orders.length + " orders shipped",
body: orderSummary(orders),
})) // idempotent — never double-sends
})

03 · Channels

One workflow. Every channel your user opted into.

Your workflow names a channel kind; palumb resolves the concrete provider, decrypts its credentials, looks up the subscriber's address, and records every attempt.

email

SMTP & providers

sms

global routes

push

iOS · Android · web

in-app

inbox feed

webhook

your endpoints

The same trigger can target one subscriber or fan out to a whole topic — palumb expands it to a deduplicated set and runs your workflow once per subscriber.

Made in the EU

04 · EU sovereignty

Sovereign by design,
not by region toggle.

Most managed notification stacks are US-centric with an EU region bolted on. palumb is built EU-first: the control plane, the durable runtime and your data all live in the EU, run by an EU provider. And because the core is open source, that independence is something you can enforce — not just trust.

No lock-in — your exit is built in →
data residency locked
EU · Paris
region
fr-par
us transfer
none

GDPR by design

Data minimisation, residency and deletion on request are defaults, not add-ons. A DPA and sub-processor list come standard.

Encrypted credentials

Provider secrets are encrypted at rest and never returned — your app never holds a provider key.

Open-source core

The control plane is open source (AGPL-3.0), so your exit is built in: audit it, or self-host the whole thing on your own EU infrastructure.

05 · Durability

No lost notifications.
No double sends.

The durable runtime under your workflows is Restate. It's why “wait an hour, then send” survives a deploy or a crash — without you holding a worker open or writing a single idempotency key.

Durable execution
State & timers persist across restarts.
Idempotent delivery
Retries reconcile — never double-send.
Built-in retries
Provider down? It backs off and resumes.
Digests & waits
Batch a window without a cron or queue.
a delivery through a crash recovered
  1. trigger received /v1/events · order-shipped
  2. wait 1m · batching timer persisted
  3. ⚡ process crash runtime restarts, state intact
  4. resume & send no duplicate — idempotent
  5. recorded attempt logged, EU

06 · Why palumb

Built it yourself? Bought a send API? Here's the trade-off.

palumb does one shape of the problem very well — code-first, durable, managed, EU-sovereign — rather than every shape adequately.

How palumb compares to building it yourself, a generic send API, and a low-code builder, across logic ownership, durable workflows, crash-safety, managed runtime, EU sovereignty and provider-key handling.
Build it yourself Generic send API Low-code builder palumb
Logic in code you own & review partial
Durable workflows (wait, batch, retry) partial
Survives crashes — no double sends partial partial
Fully managed runtime
EU-sovereign by design partial
No provider keys in your app

Not the right fit if your notifications are authored by non-developers, or if EU residency genuinely doesn't matter to you — the sovereignty wedge is a real part of the value. Read the honest version →

Send your first notification
in about five minutes.

Free to start. Open source if you'd rather self-host. EU-sovereign either way.

$ npm i @palumb/sdk