Trusted Notifications

Integrating Trusted Notifications

Trusted Notifications sends transactional emails — password resets, verifications, magic links, MFA codes and similar — from an authenticated domain, branded as you. You send us a small JSON request describing the email you want; we render it and deliver it. There's no SMTP, no template engine and no DNS to wire up on your side.

This whole page is deliberately self-contained, so you (or an AI assistant) have everything needed to integrate in one place.

Working with an AI coding assistant? You can hand it this page and let it do the integration. Paste the URL https://trustednotifications.com/docs into your assistant along with a prompt like:

Integrate Trusted Notifications into this codebase.
Read the docs at https://trustednotifications.com/docs.

Send transactional emails by POSTing JSON to
  https://trustednotifications.com/v1/notifications
with header:  Authorization: Bearer $TRUSTED_NOTIFICATIONS_API_KEY

Start with the "password-reset" type. Read the API key from an
environment variable and only ever call the API from server-side code.

Everything the assistant needs — the endpoint, authentication, the request shape, the full list of notification types, and the error format — is documented below.

How it works

Every email is described by a type (for example password-reset) plus a recipient and a few fields that the type needs (such as a reset_url). You don't write any HTML — we hold the templates, and you supply the values. All values are treated as plain text and escaped, so you can't inject markup into an email.

Endpoint

There's a single endpoint for sending:

POSThttps://trustednotifications.com/v1/notifications

Requests and responses are JSON. Send Content-Type: application/json.

Authentication

Authenticate with an API key using a bearer token:

Authorization: Bearer tn_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Keys are created per application in your dashboard. Sandbox keys start with tn_test_ and production keys with tn_live_. Treat a key like a password: keep it in an environment variable or secret store, use it only from server-side code, and never expose it in a browser, mobile app, or public repository.

Sending a notification

POST a JSON object with these fields:

FieldTypeDescription
typestringRequired. One of the notification types listed below.
recipientstringRequired. The email address to send to.
…fieldsstringThe fields required by the chosen type, sent as top-level keys alongside type and recipient. Any extra fields are ignored.

A minimal password-reset request looks like this:

{
    "type": "password-reset",
    "recipient": "user@example.com",
    "reset_url": "https://yourapp.com/reset?token=abc123"
}

Notification types

This is the full library of types you can send. It's intentionally focused on functional, transactional messages — there's no arbitrary HTML, no newsletters and no attachments, which is part of what keeps deliverability healthy for everyone.

TypeWhat it's forRequired fields
api-key-created Alert that a new API key was created. key_prefix
deployment-completed Notify that a deployment or pipeline finished. environment
email-changed Confirm a change of email address. new_email
invitation Invite someone to a team or workspace. invite_url
magic-login Send a passwordless sign-in link. login_url
mfa-code Deliver a one-time passcode. code
password-changed Confirm that an account password was changed. none
password-reset Send a secure, single-use password-reset link. reset_url
payment-failed Tell a customer a payment failed and needs attention. update_url
receipt Send a payment receipt. amount currency
verify-email Confirm a new user's email address. verify_url

Example request body for every type

Each block below is a complete, ready-to-send JSON body (swap in a real recipient and your own values):

{
    "type": "api-key-created",
    "recipient": "user@example.com",
    "key_prefix": "tn_live_9f2a"
}

{
    "type": "deployment-completed",
    "recipient": "user@example.com",
    "environment": "production"
}

{
    "type": "email-changed",
    "recipient": "user@example.com",
    "new_email": "new.address@example.com"
}

{
    "type": "invitation",
    "recipient": "user@example.com",
    "invite_url": "https://yourapp.com/invite?token=abc123"
}

{
    "type": "magic-login",
    "recipient": "user@example.com",
    "login_url": "https://yourapp.com/magic?token=abc123"
}

{
    "type": "mfa-code",
    "recipient": "user@example.com",
    "code": "492001"
}

{
    "type": "password-changed",
    "recipient": "user@example.com"
}

{
    "type": "password-reset",
    "recipient": "user@example.com",
    "reset_url": "https://yourapp.com/reset?token=abc123"
}

{
    "type": "payment-failed",
    "recipient": "user@example.com",
    "update_url": "https://yourapp.com/billing"
}

{
    "type": "receipt",
    "recipient": "user@example.com",
    "amount": "49.00",
    "currency": "USD"
}

{
    "type": "verify-email",
    "recipient": "user@example.com",
    "verify_url": "https://yourapp.com/verify?token=abc123"
}

Responses

A successful request returns 202 Accepted with the notification's id and status:

{
  "id": "b2c9f0e1-8a4d-4f7c-9b21-6e3f0a1d2c34",
  "status": "sent"
}

If a recipient can't currently be delivered to (for example they previously complained about mail from your app), the request is still accepted but not delivered. This is a normal, healthy outcome, not an error — and your dashboard shows which of your sends were suppressed and why:

{
  "id": "b2c9f0e1-8a4d-4f7c-9b21-6e3f0a1d2c34",
  "status": "suppressed",
  "reason": "complaint"
}

Some recipients are held back for platform-wide deliverability reasons that aren't specific to your app; in that case the response is simply { "status": "accepted" } with no reason.

Errors

Errors use standard HTTP status codes and a consistent envelope:

{
  "error": {
    "code": "invalid_request",
    "message": "Both \"type\" and \"recipient\" are required."
  }
}
StatusCodeMeaning
400invalid_jsonThe body wasn't a valid JSON object.
401unauthorizedThe API key is missing or invalid.
403application_suspendedThe application has been suspended.
405method_not_allowedUse POST.
422invalid_requestA required field is missing or a value is invalid.
422unknown_typeThe type isn't a recognised notification type.
429rate_limitedYou've hit your rate or daily limit — retry after a short wait.
502delivery_failedThe message couldn't be handed off for delivery. Safe to retry.

Sandbox & production

New applications start in sandbox so you can build and test safely. Two rules are worth knowing up front, because they're the usual reason a send is rejected while you're getting started:

Applications also have a per-minute rate limit and a daily limit; exceeding either returns 429 rate_limited. Moving from sandbox to production is a quick review step in the dashboard once you've verified your domain.

Code examples

The same password-reset request in a few languages. In each, the API key comes from an environment variable rather than being hard-coded.

curl

curl -X POST https://trustednotifications.com/v1/notifications \
  -H "Authorization: Bearer $TRUSTED_NOTIFICATIONS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "password-reset",
    "recipient": "user@example.com",
    "reset_url": "https://yourapp.com/reset?token=abc123"
  }'

Node.js (fetch)

const res = await fetch("https://trustednotifications.com/v1/notifications", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.TRUSTED_NOTIFICATIONS_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    type: "password-reset",
    recipient: "user@example.com",
    reset_url: "https://yourapp.com/reset?token=abc123",
  }),
});

const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? "Send failed");
console.log(data.id, data.status);

PHP

$ch = curl_init("https://trustednotifications.com/v1/notifications");
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        "Authorization: Bearer " . getenv("TRUSTED_NOTIFICATIONS_API_KEY"),
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "type"      => "password-reset",
        "recipient" => "user@example.com",
        "reset_url" => "https://yourapp.com/reset?token=abc123",
    ]),
]);
$response = curl_exec($ch);
$status   = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

Getting an API key

You'll need a free account to create an application and generate a key. It takes a couple of minutes, and you can send to your own verified address straight away in sandbox.

Create an account  ·  Sign in  ·  Ask us a question