Skip to content

For SaaS and fintech

Transactional email, onboarding journeys and broadcasts for your product.

Transactional email through a Resend-compatible API. Journeys that start when your product sends an event. Broadcasts you say out loud. One domain, one consent model, one log for all of it.

NNorthstarDraft, then approve

What should we send today?

Say it in a sentence. Linnet writes the email, picks who gets it, and sets the time.

    Enter to send · this is a demo, nothing is sent
    # Your app tells Linnet what happened.
    curl https://app.linnet.run/v1/events \
      -H "Authorization: Bearer $LINNET_API_KEY" \
      -d '{"id":"signup-user_123",
           "name":"user.signed_up",
           "contact_id":"user_123",
           "properties":{"plan":"trial"}}'
    
    # The journey you approved in chat starts.

    Same product, two doors. Chat for the people, API and MCP for your code and your AI. An event never grants email permission.

    Reads the public page only. Colors, type, and logo land in the draft above.
    The card is the same one you get in the product. Real product, illustrative brand.

    What a product sends

    What your product can send.

    Transactional email is a request. Everything else is a sentence that comes back as a card.

    Receipts, alerts, reminders, invitesOne request, one id you can follow to delivered, bounced, or held. Approve the template once. Your code sends after that.POST /emails
    Onboarding and trial journeysStarted by the events your product already has. Stops when the user does the thing.When a trial hits day 7 with no project, send the setup email.
    BroadcastsTo a plan, a cohort, or everyone who said yes. Counted live before you approve.Announce the new dashboard to everyone on a paid plan.
    A drop-in APIResend-compatible request and response shapes. Change the base URL and the key.RESEND_BASE_URL=https://app.linnet.run
    MCP for Claude and CodexYour agent drafts, counts, and inspects with the same actions. It cannot approve its own proposal.codex mcp add linnet …
    One consent modelProduct events never grant permission. Unsubscribes and suppressions are enforced on every send.Who can I email right now?

    Moving from Resend or SES

    Move from Resend or SES by changing two settings.

    Bring your audience with it. Contacts are not a line on the bill. Weighing it up? Resend alternatives, honestly compared, or let us move you by hand, free.

    Two settings

    # .env
    RESEND_API_KEY=lnt_live_…
    RESEND_BASE_URL=https://app.linnet.run
    
    # Your code, unchanged
    await resend.emails.send({
      from: "billing@northstar.app",
      to: user.email,
      subject: "Your receipt",
      html
    });

    Same request shape, same response shape. Batch and cancel work the same way.

    Bring your audience

    curl https://app.linnet.run/v1/contact-imports \
      -H "Authorization: Bearer $LINNET_API_KEY" \
      -d '{"source_key":"product",
           "contacts":[{"source_object_id":"user_123",
             "email":"ada@northstar.app",
             "consent":{"purpose":"marketing",
                        "status":"granted"}}]}'

    Or upload the export from Resend, Mailchimp, or a CSV. Rows are reconciled one by one, consent included.

    Start journeys from events

    curl https://app.linnet.run/v1/events \
      -H "Authorization: Bearer $LINNET_API_KEY" \
      -d '{"id":"trial-day7-user_123",
           "name":"trial.day_7_no_project",
           "contact_id":"user_123"}'

    Send events by user id. The journey you approved in chat picks them up. An event never grants email permission.

    Start

    Send your first real email today.

    Connect your domain, change two settings, say the first sentence.

    The linnet wires one event cable into an oversized switchboard.