DripDrop is an Elixir library that stores its domain data in the dripdrop
Postgres schema and uses a host application repo.
Requirements
DripDrop requires Elixir/OTP compatible with the project .tool-versions,
Postgres 18+ (for native uuidv7()), a host Ecto repo, and
DRIPDROP_ENCRYPTION_KEY set to a base64-encoded 32-byte key.
Dependencies
Add DripDrop to the host app:
{:dripdrop, "~> 0.1"}For local path development:
{:dripdrop, path: "../dripdrop"}Optional provider packages such as Swoosh, Finch, MJML, Phoenix PubSub, Oban, AWS SNS, ExGram, and whatsapp_sdk are loaded only when the matching provider is used. Liquex is required for Liquid template rendering.
Configuration
config :dripdrop,
repo: MyApp.Repo,
scheduler: DripDrop.Schedulers.Pgflow,
channels: [],
quiet_hours_default: {8, 21},
sms_max_chars: 1600
config :dripdrop, :pgflow,
jobs: [DripDrop.Jobs.DispatchStep, DripDrop.Jobs.CronTick]Set DRIPDROP_ENCRYPTION_KEY before boot. It must be a base64-encoded key
accepted by DripDrop.Vault.
For host apps that already run Oban, set scheduler: DripDrop.Schedulers.Oban
and configure the host Oban supervision tree with a :dripdrop queue.
Supervision
The PgFlow scheduler only enqueues work; something still has to poll pgmq and
execute it. Add a {PgFlow, ...} child to the host application's supervision
tree, after the host Ecto repo and before the Endpoint:
children = [
MyApp.Repo,
{PgFlow,
repo: MyApp.Repo,
jobs: [DripDrop.Jobs.DispatchStep, DripDrop.Jobs.CronTick],
signal_strategy: :notify,
notify_throttle_ms: 50,
notify_fallback_interval: 250},
MyAppWeb.Endpoint
]List every job module that must run under this worker, not just DripDrop's
own DispatchStep and CronTick — any host job modules (use PgFlow.Job)
belong in the same jobs: list, since PgFlow only starts a poller for job
modules it is told about. The jobs: list under config :dripdrop, :pgflow
is a separate, DripDrop-only setting that DripDrop.startup_check/0 reads to
confirm DispatchStep was wired up; it does not register anything with
PgFlow, so host jobs never need to be listed there.
Without this child, DripDrop.startup_check/0 reports
pgflow_job_not_registered and DripDrop dispatches never execute — enrollment
enqueues rows that sit in pgmq forever.
Omit this child (or gate it behind a config flag) in test environments that
configure scheduler: DripDrop.Schedulers.Test: a live poller would race to
claim and execute pgmq rows that other tests enqueue.
Migrations
PgFlow is the recommended scheduler. Generate PgFlow migrations first:
mix pgflow.gen.postgres_extensions_migration
mix pgflow.gen.pgmq_migration
mix pgflow.setup
mix pgflow.gen.job_migration DripDrop.Jobs.DispatchStep
mix pgflow.gen.job_migration DripDrop.Jobs.CronTick
DispatchStep is the generic worker DripDrop uses for scheduled sends.
CronTick seeds due cron-style DripDrop steps; keep it configured and compiled
when cron timing is enabled. For hosts without pg_cron, generate PgFlow
extensions with --no-cron and keep CronTick in the configured PgFlow job
list.
These are one-time scheduler/job setup migrations. DripDrop sequence authoring is dynamic: creating new sequences, steps, transitions, conditions, hooks, or enrollments does not require new PgFlow migrations.
Generate the DripDrop wrapper migration:
mix dripdrop.setup --repo MyApp.Repo
Run host migrations after scheduler migrations are present:
mix ecto.migrate
Use mix dripdrop.check_schema in CI or deploy checks to verify the installed
schema version matches the library.
Cold Outbound
Cold outbound is optional. No extra migration is required beyond the current V01 schema. Hosts that enable outbound mode should:
- Create mailbox or ESP adapters with health/ramp fields.
- Create an adapter pool and add pool members.
- Set
sequence_versions.mode = "outbound"andconfig["pool_id"]on the outbound version. - Pump inbound mailbox replies from host-owned IMAP, Microsoft Graph, or Gmail
infrastructure into
DripDrop.ingest_inbound_message/2.
See cold_outbound.md for the full operator guide.
Webhooks
In a Phoenix router:
import DripDrop.Web.Router
scope "/" do
dripdrop_webhooks("/webhooks/dripdrop")
endInbound routes are registered for Mailgun, SendGrid, Postmark, MailerSend, SES,
and Twilio. Verification happens in DripDrop.Web.WebhookPlug; supported
providers use their native signature, basic auth, or SNS certificate checks.
Gmail, MS365, SMTP, PubSub, Slack, and Telegram treat a successful send as the
terminal positive signal.