notifything Get in touch

Every channel · one API · one key

One API for every message.

Email, SMS, Slack, Discord: every project signs up for a provider per channel, keeps the keys and writes its own retries. notifything is one API in front of all of them, with one key and the scopes you set. No dashboards.

Act I

Every project, every provider.

A shop wants to email receipts, text a code and ping a channel when an order fails. Here's what that takes, before the next project needs the same.

01 · Email

Sign up for email.

An account, a verified sending domain, three DNS records you copy by hand, and an API key you'll paste into a project.

email-provider.example/domains/verify
Verify shop.example Email provider
Add these records at your DNS provider, then press verify.
TypeNameValue
TXT@v=spf1 include:_spf.email…
CNAMEem1._domainkeyem1.dkim.email-provider…
CNAMEem2._domainkeyem2.dkim.email-provider…
Verify
Verified. API key created: em_live_9f2c…

02 · SMS

Then a second provider for SMS.

Another account, a phone number to buy, a business to verify, and a trial mode that only texts numbers you've verified yourself.

sms-provider.example/console
Messaging SMS provider
Number +45 12 34 56 78
Use case Order notifications
Buy number
Account SID and auth token created.
Trial account: messages can only go to verified numbers.

03 · Chat

Then Slack, then Discord.

An app or a webhook in each, and the URLs pasted into an .env file, because a webhook URL is a key nobody treats as one.

~/shop
$ echo "SLACK_WEBHOOK=https://hooks.slack.com/services/T0…" >> .env
$ echo "DISCORD_WEBHOOK=https://discord.com/api/webhooks/…" >> .env
$ cat .env | wc -l
9

04 · The code

Then glue it together.

Two SDKs, two raw webhooks, four error shapes and a retry loop that resends everything when one fails. Every project writes this again.

notify.ts lines 1–20
import sgMail from "@sendgrid/mail";
import twilio from "twilio";
sgMail.setApiKey(process.env.SENDGRID_KEY!);
const sms = twilio(process.env.TWILIO_SID, process.env.TWILIO_TOKEN);
export async function notify(user: User, text: string) {
for (let attempt = 1; attempt <= 3; attempt++) {
try {
await sgMail.send({ to: user.email, from: "no-reply@shop.example", subject: "Update", text });
if (user.phone) await sms.messages.create({ to: user.phone, from: "+4512345678", body: text });
await fetch(process.env.SLACK_WEBHOOK!, { method: "POST", body: JSON.stringify({ text }) });
await fetch(process.env.DISCORD_WEBHOOK!, { method: "POST", body: JSON.stringify({ content: text }) });
return;
} catch (e) {
// which one failed? the others already sent. retry them all anyway.
await new Promise((r) => setTimeout(r, 2 ** attempt * 1000));
}
}
}

05 · And then it changes

A provider changes its prices.

Now the SDK, the keys and the DNS records change in every project that copied this code, one pull request at a time.

~/src
$ grep -rl "@sendgrid/mail" ~/src --include=*.ts | cut -d/ -f5 | sort -u
billing-portal
booking
shop
… and four more
# seven projects, seven sets of keys, seven retry loops

That was one project.

providers signed up
4
keys to keep
4
ways it fails
3
projects that copy it
all of them

Here's the same message, with notifything.

Act II

The notifything way.

Providers, keys, retries and templates live in one place. A project gets one key and makes one call.

01 · One key

One key, scoped to what it may do.

A project gets a key that can send, say, email and Slack to its own groups, and nothing else. You decide the scopes; revoking it is one command.

~
$ notifything keys create shop --scopes send:email,send:slack
nt_live_4c1e… · scopes: send:email send:slack · groups: customers
✓ key created · it's the only one shop will ever hold

02 · One call

One call, any channel.

The project names who it's for and what to say. notifything decides how: which channels, which provider, and what to retry if one fails.

~/shop
curl https://notifything.com/v1/send \
-H "Authorization: Bearer $NOTIFYTHING_KEY" \
-d '{ "to": "ops", "text": "deploy finished on prod01" }'
{
"id": "msg_01J8Z3",
"to": "ops",
"delivered": ["email", "discord"],
"skipped": { "sms": "not urgent" }
}

03 · Channels are config

Who gets what is written down.

Groups and channels live in one file, so moving the ops alerts from Slack to Discord is an edit, and no project knows it happened.

channels.toml lines 1–14
# who a message goes to, and how. Projects only ever name the group.
[groups.ops]
email = ["you@example.com"]
discord = "#alerts"
sms = ["+45 12 34 56 78"] # only when it's urgent
[groups.customers]
email = "from the project" # the project passes the address
slack = false
[providers]
email = "whichever notifything picked" # swapped here, never in a project
sms = "whichever notifything picked"

04 · The provider changes

And a provider swap is one line.

Change the provider in one place and every project sends through the new one. Zero pull requests, zero keys handed out again.

~/notifything
$ notifything providers set email another-provider
✓ 7 projects now send email through another-provider
0 code changes · 0 new keys

Act III

Why one place.

Sending a message is boring until it fails. notifything is where the boring failures get handled, once, for everything that sends.

  1. 1

    One API

    Email, SMS, Slack, Discord and whatever comes next are channels behind the same call. A project never learns a provider's SDK.

  2. 2

    One key, your scopes

    Each project holds one key that can do exactly what you allowed: which channels, which groups. No dashboard to remember it in.

  3. 3

    The hard parts, once

    Retries that don't resend what already went out, rate limits, templates and delivery status. Fix it here and every project gets the fix.

Sending from seven projects, both ways
Measure Per project notifything
Provider accounts one per channel none in any project
Keys in each project one per provider one, scoped
Retries and failures a loop in every codebase handled once
Changing a provider a pull request per project one line
Dashboards one per provider none

Tired of wiring up email again?

Tell me which channels your projects send to. notifything is the seam every project here will send through.

Get in touch