Notix
Integrations

Send email from Convex actions.

Convex mutations cannot call out to the network, but actions can. Put the send in an action, read the key from the deployment's environment, and schedule the action from the mutation that decides an email is due, so the database write and the send stay separate.

The send

Send an email from Convex.

convex/email.ts
import { v } from "convex/values";
import { internalAction } from "./_generated/server";

export const sendReceipt = internalAction({
  args: { to: v.string(), orderId: v.string() },
  handler: async (_ctx, { to, orderId }) => {
    const response = await fetch("https://app.usenotix.dev/api/v1/emails", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.NOTIX_API_KEY}`,
        "Content-Type": "application/json",
        "Idempotency-Key": `order-${orderId}-receipt`,
      },
      body: JSON.stringify({ from: "Acme <receipts@acme.com>", to, subject: "Your receipt", html: "<p>Thanks.</p>" }),
    });
    if (!response.ok) throw new Error(`Notix answered ${response.status}`);
  },
});

The from address must be on a domain you have verified in Notix. Step by step: the cURL quickstart.

Environment

The key in the deployment's environment.

Set it with npx convex env set NOTIX_API_KEY notix_xxxxxxxxxxxx, or in the dashboard's environment variables. Actions read it from process.env; nothing in the client bundle can.

From a mutation, schedule the send with ctx.scheduler.runAfter(0, internal.email.sendReceipt, { to, orderId }), so it runs only if the mutation commits.

FAQ

Questions, answered.

Why an action and not a mutation?
Convex mutations are transactional and cannot make network calls. An action can, and scheduling it from the mutation sends only once the write has committed.
What if the same request runs twice?
Send an Idempotency-Key header (idempotencyKey in notix-js) with a value that names the thing being sent, such as order-4471-receipt. A repeat with the same key and body returns the first result and sends nothing new; the same key with a different body is refused with 409.
Why is my send refused with a from-address error?
The from address must be on a domain you have verified in Notix. Add the domain in the console, publish the DNS records it shows, and wait for verification; until then, sends from that domain are refused.

The free plan does not ask for a card.

Send your first email in five minutes.

Verify a domain, copy an API key, make one call. The free plan does not ask for a card.