Send it — unless they said no.
Receipts, codes, alerts, "your export is ready." One call sends it by email, by SMS, or into an in-app inbox — and the opt-out your settings screen renders is the same row we check at send time, so your UI can never say "off" while your code keeps sending.
$15/mo or $150/yr — a year costs two months less.
14-day free trial, cancel any time. One subscription covers every Parsons product, on unlimited projects.
Every app builds the same four things.
None of them is the reason your product exists, and you will build all four again in your next app. Only one of them can silently email someone who asked you to stop.
a log · a toggle
UPDATE users SET marketing_ok = 0cron.js → sendMarketing(user)The toggle lives in your database. The send lives somewhere else. The day someone adds a code path that forgets the check, your settings screen says "off" and your app keeps sending — and the customer finds out before you do.
One row. Three things read it.
Your settings screen writes it. Your backend reads it before every send. Your AI flips it when a customer emails you. There is no second copy to drift out of sync, because there is no second copy.
category:"marketing" })
| user_id | channel | category | state |
|---|---|---|---|
| u_123 | marketing | opted_out |
Two layers — channel, and channel + category. The more restrictive one wins. A channel opt-out silences everything on that channel; a category opt-out silences only sends that declare it, so a customer can kill your marketing mail and still get "your order shipped." That is the whole model, and it fits in this paragraph on purpose.
The check is inside the send, not around it.
"We enforce preferences" is a claim. Here is where the enforcement happens: between resolving your template and choosing a transport, before any provider is contacted. There is no way to call the sender and step past it, because the check is not a line in your code that someone could forget to write.
one gate
Email, SMS, or the bell. Same call.
One argument changes. The template, the preference check, the delivery log and the tools are identical across all three. Pick a channel below and watch what the recipient actually gets.
"sms" · "inapp"
send {
channel: "email",
to: "ana@example.com",
templateKey: "order_ready",
vars: { name: "Ana", orderId: "4172" },
category: "orders"
}Sent via Parsons Cloud · unsubscribe
SMS is not turnkey and we will not pretend it is: it needs a number bound for SMS on your project and completed A2P registration. Those are real one-time and monthly fees, itemised in pricing. Once they are done, delivery is a real carrier send with STOP handling and a daily segment cap applied for you.
Your domain. Our signing. One rule about when we won't try again.
Attach a domain to your project and your mail goes out under it, DKIM-signed on our own send path, with a second transport behind it. No domain yet? You still send under your own sender name — our wordmark is never on your mail, only one footer line.
three outcomes
Sender identity is scoped to the project, so several apps in one project share one identity. If you need two identities, make two projects — they are free.
Your name on it. Ours in the footnote.
Four fields, four tool arguments, all on the $15 Base plan. A project that never sets a sender name gets no wordmark at all — not ours. One footer line stays, at footer size, on every send.
set_sender_name.set_sender_identity({ mailingAddress }).List-Unsubscribe header; leave it and we don't — silently unsubscribing someone from their own receipts is a worse bug than not having the header.Variables that can't inject. A missing one that can't break the send.
Write the message once per channel with {{var}} placeholders and send it by key. Two failure classes are closed for you, and both are demonstrable right here — type anything you like into the field.
forgiving on the way out
Hi {{name}} — your order {{orderId}} is ready for pickup.
Try <b>hi</b> or <script>alert(1)</script>. Every substituted value in an email body is HTML-escaped, so a customer whose display name contains markup renders as literal text. A variable is content. It is never markup, and it cannot become markup by being unusual.
Now drop the second variable. The body renders with a gap and the send still goes. Transactional mail should not be lost to a blank field — a receipt with a missing order number is worth more than no receipt at all.
Templates are per project and per channel, so the same key can carry a long email body and a one-segment SMS. Change the wording once and every send that uses the key changes with it.
Four statuses. None of them lie to you.
The console holds a filterable delivery log — the same rows your code reads back over list_messages. It is not a paid tier and not an upgrade — the same log for every subscriber. The filters below are the console's real filters; use them.
status · detail
| When | Channel | To | Status | Detail |
|---|---|---|---|---|
| 09:12:04 | ana@example.com | sent | order_ready | |
| 09:12:04 | inapp | u_123 | delivered | order_ready |
| 08:47:51 | ana@example.com | skipped | opted_out:marketing | |
| 08:31:19 | sms | +15550142210 | sent | 1 segment |
| 08:02:38 | typo@exmaple.com | failed | suppressed · provider-managed | |
| 07:55:02 | sam@example.com | sent | receipt | |
| 07:41:20 | sam@example.com | skipped | opted_out | |
| 07:12:47 | inapp | u_881 | delivered | export_ready |
502, so a plain if (!res.ok) catches a non-delivery.// the 502 rule — a non-delivery fails your own error check, not silently const r = await fetch("https://notifications.parsons.ai/v1/notifications/send", opts) if (!r.ok) { /* 502 — it did not go out. Decide what to do. */ }
One attempt, one log row. There is no retry queue — if a send fails, you decide whether to retry, and you can see the row that says so. And sent is the honest ceiling: post-send delivery events are not built, so nothing on this page will ever tell you a message was read.
Some addresses you can rescue. Some you can't.
An address that hard-bounces or reports spam gets suppressed, and every later send to it fails. What matters is knowing which kind you are looking at — because for one of them a retry is a plan, and for the other it is a way to damage your own sending reputation.
never pretend about the rest
Every suppression tool is scoped to addresses your project has actually sent to — so nobody can use this surface to probe somebody else's list. And a practical warning worth more than any feature: test with real addresses you control. A made-up address burns permanently on its first bounce, and that is the receiving world's rule, not ours.
A sk_live_ key, five endpoints, no SDK.
Your running app sends over plain HTTP. There is no client library to install, no version to keep up with, and nothing to import — a fetch and a bearer token is the whole integration.
from the key
curl -X POST \ https://notifications.parsons.ai/v1/notifications/send \ -H "Authorization: Bearer sk_live_…" \ -H "Content-Type: application/json" \ -d '{"channel":"email", "to":"ana@example.com", "templateKey":"order_ready", "vars":{"name":"Ana","orderId":"4172"}, "category":"orders"}'
| Endpoint | What it is for |
|---|---|
| POST /send | One notification, any channel. 200 on sent or skipped, 502 on failed. |
| GET /messages | The delivery log, filterable by channel and status. |
| GET /prefs | Render your own settings screen from platform state. |
| PUT /prefs | Write the toggle to the same row the sender reads. |
| GET /inbox | Build your notification bell against the in-app inbox. |
The console's own API runs its own 240/240 budget, so a busy dashboard never eats your app's send capacity.
GET and PUT /prefs are what your own settings screen should call. That is how the row stays single: your React toggle and our sender are reading the same state, not two copies of it.
The key is the door for your code, wherever it runs — your own servers, a container, or an app you deployed on Parsons. Our own platform services reach the same sender over an internal binding instead, which is how the scheduler's watchdog pages its owner. Either way it is the same send path and the same gate.
Eighteen tools. Not a chat box bolted to a dashboard.
Every operation on this page is a named tool your own Claude or ChatGPT can call. The support question you would normally answer by grepping production logs becomes a question you just ask.
Every admin action — a send, a template change, a sender identity edit, a preference flip, a suppression clear — is written to an audit log, whether a human, your app or an AI did it.
Nine things we don't do.
Scope is a feature, and a page that sends you elsewhere is a page you can believe. If one of these is the thing you actually need, we would rather you found out now than after you paid.
are "coming soon"
One more, stated plainly because it affects your bill: plan allowances are metered, not enforced. We show you the counts and we do not cut you off mid-month. Do not read that as a promise you cannot exceed your plan — read it as a promise your receipts won't stop at 4pm on the 28th.
Every rival meters the message. We meter the account.
Because they charge per message, everything adjacent to the message has to be sold back to you: the in-app bell is billed, log retention is a tier, your own branding is an upgrade, and a second project is a second workspace. Three rows here go against us. They land first.
at full weight
| Parsons | Knock | Novu (cloud) | Courier | |
|---|---|---|---|---|
| Push notifications | not built | yes | yes | yes |
| Multi-step workflow builder | not built | yes | yes | yes |
| Drop-in inbox UI component | API only | yes, React-first | yes | yes |
| Entry paid price | $15/mo or $150/yr | free tier, then $250/mo | free tier, then $30/mo | free tier, then $0.005/send |
| What the meter counts | the account | messages, guide users, AI credits | workflow runs, across all environments | sends |
| In-app messages | uncapped | metered | counted as workflow runs | counted as sends |
| Delivery log retention | not tiered | 30 days; 90 on Enterprise | 24 hours on free; 7 days on Pro; 90 days on Team | not published |
| Your branding on your mail | included | their branding until Starter; per-customer branding is Enterprise | — | — |
| A second project | free, unlimited | plan- and environment-scoped | 2 environments free, 10 on Team | — |
| Driven by named AI tools | 18 MCP tools | dashboard-first | dashboard-first | dashboard-first |
Competitor rows read from each vendor's own published pricing and docs pages on 2026-08-22. Prices change; check theirs before you decide.
If you need push or a workflow canvas, buy Knock — it is a good product and it does those things. What we will not do is let you find out in month three. And one more honest row that will not fit in the table: if notifications are all you will ever send, and you send few of them, a per-send vendor can bill you less than $15 a month. We are not the cheapest way to send two hundred emails. The rest of the table is structural — none of them can bundle a whole platform of products into one $15 account, because notifications is the only product they have.
$15 buys the account, not the notification layer.
Monthly or yearly — a year costs exactly two months less than twelve months bought one at a time. There is no per-seat price, no enterprise tier and no free tier. One subscription covers every Parsons product on every project you own. Going past an included allowance is welcome: it draws from prepaid credits at roughly cost-and-a-half, so the bill starts when real usage does.
cancel any time
- 200 emails a day, sent under your own sender name and your own domain if you have one
- In-app messages are uncapped — no plan limit, because they never leave your project's store
- Templates, the delivery log, the preference API, suppression tools
- All 18 MCP tools and the sk_live_ HTTP API — tool calls are uncapped and unbilled
- Every other Parsons product, on unlimited projects
14-day free trial on your first subscription · pick monthly or yearly at checkout, switch later · cancel any time · checkout collects a payment method, because it opens the subscription that starts on day 15.
| US local number | $3.99 / mo |
| US toll-free number | $4.99 / mo |
| A2P brand registration | $49.99 once |
| A2P campaign vetting | $18.99 once |
| A2P campaign — low volume | $3.99 / mo |
| A2P campaign — standard use cases | $11.99 / mo |
| Outbound SMS segment | $0.02 |
| Inbound SMS | not billed |
A project is a boundary, not a line item — its own sender identity, its own preference rows, its own delivery log, its own members and its own spend cap. Rivals in this category gate a second environment or a second workspace behind an upgrade. Add as many as you like.
The ones people actually ask.
Every answer is open. An accordion in a section about honesty is a small dishonesty.
behind a click
- Does this replace Resend or Postmark?
- For app-to-user messaging, yes — and it adds the preference layer, the in-app channel and the delivery log you would otherwise build. For newsletters and campaigns, no: that is a different product and we do not have it.
- Can my own settings screen read these preferences?
- Yes —
GETandPUT /v1/notifications/prefswith yoursk_live_key. It is the same row the sender checks, which is the entire point. - Does a skipped send cost me an email?
- No. Metering fires only on sends that were not skipped, so an opted-out user costs you nothing.
- What happens at email number 201?
- It sends. Going past the included allowance simply draws from your prepaid credits at roughly cost-and-a-half — we show you the counts, and the bill starts when real usage does.
- Is SMS really live?
- Yes — a real carrier send with STOP handling and a daily segment cap. It needs a number bound for SMS on your project and completed A2P registration first, and every one of those fees is printed in the pricing section above.
- Can I send from my own domain?
- Yes, once the domain is attached and verified — DKIM-signed on our own send path, with a second transport behind it. Without a domain you still send under your own sender name.
- Do you put your branding on my email?
- No. A project with no sender name gets no wordmark at all — not ours. The only mention of us is one
Sent via Parsons Cloudline in the footer. - Do I get push notifications?
- No. Mobile and web push are not built and are not in V1. The in-app inbox is a stored row you render yourself, not a device push.
- Is there a free tier?
- No. A 14-day free trial on your first subscription, then $15/mo — or $150 for the year. Checkout collects a card because it opens the subscription — we would rather say that here than surprise you at the form.
- Is there an annual plan?
- Yes. $150/yr — exactly two months less than the same plan paid monthly ($180). Pick the interval at checkout; the plan, the allowances and the 14-day trial are identical either way.
- What if I need a second project?
- Make as many as you like. They are free, and each gets its own sender identity, preference rows, delivery log, members and spend cap.
- Can I retry a failed send automatically?
- Not here. One attempt, one log row, and a
502so your own error handling sees it. You decide what a retry means for that message. - How do I know it delivered?
- For in-app,
deliveredmeans the row is in the store. For email and SMS the honest ceiling issent— the provider accepted it. We do not have post-send delivery events and will not invent them.
Send it — unless they said no.
14-day free trial, then $15/mo — or $150 for the year — covering every Parsons product, on unlimited projects.