Private beta Now onboarding Indian startups

Email for developers, made in India

A clean API for OTPs, receipts and password resets. Billed in rupees with GST invoices, with your users' data stored in India.

Transactional email only. No bulk or marketing mail.

Features

Everything you need. Nothing you don't.

A small, focused product for transactional email, built for teams who want to ship and move on.

1import { Vaartix } from 'vaartix';
2
3const vaartix = new Vaartix('vx_123456789');
4
5await vaartix.emails.send({
6  from: 'Acme <[email protected]>',
7  to: '[email protected]',
8  subject: 'Your login code',
9  html: '<p>Your code is <b>482913</b></p>',
10});
11
12// Delivered, opened and bounced events
13// arrive at your webhook, signed.
1curl -X POST https://api.vaartix.com/emails \
2  -H "Authorization: Bearer $VAARTIX_API_KEY" \
3  -H "Content-Type: application/json" \
4  -d '{
5    "from": "Acme <[email protected]>",
6    "to": "[email protected]",
7    "subject": "Your login code",
8    "html": "<p>Your code is <b>482913</b></p>"
9  }'
1import os
2import vaartix
3
4vaartix.api_key = os.environ["VAARTIX_API_KEY"]
5
6vaartix.Emails.send({
7    "from": "Acme <[email protected]>",
8    "to": "[email protected]",
9    "subject": "Your login code",
10    "html": "<p>Your code is <b>482913</b></p>",
11})
Response · 200
{
  "id": "em_7Hq2kV9xR4",
  "status": "queued"
}
  • Queued0 ms
  • Sent+120 ms
  • Delivered+1.4 s

One call to send

Batch and scheduled sends, attachments, tags and idempotency keys built in.

Stored in India

Your users' data lives in the AWS Mumbai region. Nothing is stored abroad.

Billed in rupees

Pay by UPI or card, with GST invoices your accountant will like.

  • UPI
  • Cards
  • GST invoice

Domains in minutes

DKIM, SPF, DMARC and a custom return path, generated for you.

  • DKIMVerified
  • SPFVerified
  • DMARCVerified

Signed webhooks

Every event, signed and retried until your endpoint says OK.

  • email.delivered200
  • email.opened200
  • email.bounced200

Automatic suppression

Hard bounces and complaints are blocked for you, so your reputation stays clean.

Logs you can search

Every email with its full timeline. Find any message in seconds.

  • Your login codeDelivered
  • Receipt #1042Opened
DPDP.

Ready for India's data law

A Data Processing Agreement written for the DPDP Act, 2023.

Coming next

WhatsApp and OTP verification

Verify users over WhatsApp, with email as the fallback.

WhatsAppEmailif not delivered in time

SDKs and React Email

Start with Node.js and React Email. More languages follow soon after launch.

  • Node.js
  • React Email
  • REST
  • Pythonsoon
  • PHPsoon
  • SMTPsoon
IST support

Real people, in your timezone

Talk to the team building Vaartix, during your working day. UTC+05:30.

Integrate

Live before your chai goes cold.

A small REST API that fits into any stack. Use the Node.js SDK at launch, or call the API from the language you already use.

send.ts
1import { Vaartix } from 'vaartix';
2
3const vaartix = new Vaartix('vx_xxxxxxxxx');
4
5const { data, error } = await vaartix.emails.send({
6  from: 'Acme <[email protected]>',
7  to: ['[email protected]'],
8  subject: 'Hello from Vaartix',
9  html: '<strong>It works!</strong>',
10});
11
12console.log(data ?? error);
app/api/send/route.ts
1import { Vaartix } from 'vaartix';
2
3const vaartix = new Vaartix(process.env.VAARTIX_API_KEY);
4
5export async function POST() {
6  const { data, error } = await vaartix.emails.send({
7    from: 'Acme <[email protected]>',
8    to: ['[email protected]'],
9    subject: 'Hello from Vaartix',
10    html: '<strong>It works!</strong>',
11  });
12
13  if (error) {
14    return Response.json({ error }, { status: 500 });
15  }
16
17  return Response.json(data);
18}
server.ts
1import express from 'express';
2import { Vaartix } from 'vaartix';
3
4const app = express();
5const vaartix = new Vaartix(process.env.VAARTIX_API_KEY);
6
7app.post('/send', async (req, res) => {
8  const { data, error } = await vaartix.emails.send({
9    from: 'Acme <[email protected]>',
10    to: ['[email protected]'],
11    subject: 'Hello from Vaartix',
12    html: '<strong>It works!</strong>',
13  });
14
15  if (error) return res.status(500).json({ error });
16  res.json(data);
17});
18
19app.listen(3000);
src/index.ts
1import { Hono } from 'hono';
2import { Vaartix } from 'vaartix';
3
4const app = new Hono();
5const vaartix = new Vaartix(process.env.VAARTIX_API_KEY);
6
7app.post('/send', async (c) => {
8  const { data, error } = await vaartix.emails.send({
9    from: 'Acme <[email protected]>',
10    to: ['[email protected]'],
11    subject: 'Hello from Vaartix',
12    html: '<strong>It works!</strong>',
13  });
14
15  return error ? c.json({ error }, 500) : c.json(data);
16});
17
18export default app;
server/api/send.post.ts
1import { Vaartix } from 'vaartix';
2
3const vaartix = new Vaartix(process.env.VAARTIX_API_KEY);
4
5export default defineEventHandler(async () => {
6  const { data, error } = await vaartix.emails.send({
7    from: 'Acme <[email protected]>',
8    to: ['[email protected]'],
9    subject: 'Hello from Vaartix',
10    html: '<strong>It works!</strong>',
11  });
12
13  if (error) throw createError({ statusCode: 500, data: error });
14  return data;
15});
src/pages/api/send.ts
1import type { APIRoute } from 'astro';
2import { Vaartix } from 'vaartix';
3
4const vaartix = new Vaartix(import.meta.env.VAARTIX_API_KEY);
5
6export const POST: APIRoute = async () => {
7  const { data, error } = await vaartix.emails.send({
8    from: 'Acme <[email protected]>',
9    to: ['[email protected]'],
10    subject: 'Hello from Vaartix',
11    html: '<strong>It works!</strong>',
12  });
13
14  return Response.json(error ?? data, { status: error ? 500 : 200 });
15};
index.ts
1import { Vaartix } from 'vaartix';
2
3const vaartix = new Vaartix(Bun.env.VAARTIX_API_KEY);
4
5Bun.serve({
6  port: 3000,
7  async fetch() {
8    const { data, error } = await vaartix.emails.send({
9      from: 'Acme <[email protected]>',
10      to: ['[email protected]'],
11      subject: 'Hello from Vaartix',
12      html: '<strong>It works!</strong>',
13    });
14
15    return Response.json(error ?? data, { status: error ? 500 : 200 });
16  },
17});
app/routes/api.send.ts
1import { json } from '@remix-run/node';
2import { Vaartix } from 'vaartix';
3
4const vaartix = new Vaartix(process.env.VAARTIX_API_KEY);
5
6export async function action() {
7  const { data, error } = await vaartix.emails.send({
8    from: 'Acme <[email protected]>',
9    to: ['[email protected]'],
10    subject: 'Hello from Vaartix',
11    html: '<strong>It works!</strong>',
12  });
13
14  if (error) return json({ error }, { status: 500 });
15  return json(data);
16}
npm install vaartixOfficial SDK
app/api/send/route.ts
1export async function POST() {
2  const res = await fetch('https://api.vaartix.com/emails', {
3    method: 'POST',
4    headers: {
5      'Content-Type': 'application/json',
6      Authorization: `Bearer ${process.env.VAARTIX_API_KEY}`,
7    },
8    body: JSON.stringify({
9      from: 'Acme <[email protected]>',
10      to: ['[email protected]'],
11      subject: 'Hello from Vaartix',
12      html: '<strong>It works!</strong>',
13    }),
14  });
15
16  return Response.json(await res.json(), { status: res.status });
17}
supabase/functions/send/index.ts
1Deno.serve(async () => {
2  const res = await fetch('https://api.vaartix.com/emails', {
3    method: 'POST',
4    headers: {
5      'Content-Type': 'application/json',
6      Authorization: `Bearer ${Deno.env.get('VAARTIX_API_KEY')}`,
7    },
8    body: JSON.stringify({
9      from: 'Acme <[email protected]>',
10      to: ['[email protected]'],
11      subject: 'Hello from Vaartix',
12      html: '<strong>It works!</strong>',
13    }),
14  });
15
16  return new Response(await res.text(), {
17    status: res.status,
18    headers: { 'Content-Type': 'application/json' },
19  });
20});
main.ts
1// deno run --allow-net --allow-env main.ts
2const KEY = Deno.env.get('VAARTIX_API_KEY');
3
4const res = await fetch('https://api.vaartix.com/emails', {
5  method: 'POST',
6  headers: {
7    'Content-Type': 'application/json',
8    Authorization: `Bearer ${KEY}`,
9  },
10  body: JSON.stringify({
11    from: 'Acme <[email protected]>',
12    to: ['[email protected]'],
13    subject: 'Hello from Vaartix',
14    html: '<strong>It works!</strong>',
15  }),
16});
17
18console.log(await res.json());
src/index.ts
1export default {
2  async fetch(request: Request, env: Env) {
3    const res = await fetch('https://api.vaartix.com/emails', {
4      method: 'POST',
5      headers: {
6        'Content-Type': 'application/json',
7        Authorization: `Bearer ${env.VAARTIX_API_KEY}`,
8      },
9      body: JSON.stringify({
10        from: 'Acme <[email protected]>',
11        to: ['[email protected]'],
12        subject: 'Hello from Vaartix',
13        html: '<strong>It works!</strong>',
14      }),
15    });
16
17    return new Response(await res.text(), { status: res.status });
18  },
19};
index.mjs
1export const handler = async () => {
2  const res = await fetch('https://api.vaartix.com/emails', {
3    method: 'POST',
4    headers: {
5      'Content-Type': 'application/json',
6      Authorization: `Bearer ${process.env.VAARTIX_API_KEY}`,
7    },
8    body: JSON.stringify({
9      from: 'Acme <[email protected]>',
10      to: ['[email protected]'],
11      subject: 'Hello from Vaartix',
12      html: '<strong>It works!</strong>',
13    }),
14  });
15
16  return { statusCode: res.status, body: await res.text() };
17};
Plain fetch, no SDK requiredREST API
send.rb
1require 'net/http'
2require 'json'
3
4uri = URI('https://api.vaartix.com/emails')
5req = Net::HTTP::Post.new(uri)
6req['Authorization'] = "Bearer #{ENV['VAARTIX_API_KEY']}"
7req['Content-Type'] = 'application/json'
8req.body = {
9  from: 'Acme <[email protected]>',
10  to: ['[email protected]'],
11  subject: 'Hello from Vaartix',
12  html: '<strong>It works!</strong>'
13}.to_json
14
15res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
16  http.request(req)
17end
18
19puts res.body
POST https://api.vaartix.com/emailsREST API
send.py
1import os
2import requests
3
4res = requests.post(
5    "https://api.vaartix.com/emails",
6    headers={"Authorization": f"Bearer {os.environ['VAARTIX_API_KEY']}"},
7    json={
8        "from": "Acme <[email protected]>",
9        "to": ["[email protected]"],
10        "subject": "Hello from Vaartix",
11        "html": "<strong>It works!</strong>",
12    },
13)
14
15print(res.json())
POST https://api.vaartix.com/emailsREST API · SDK soon
send.php
1<?php
2
3$ch = curl_init('https://api.vaartix.com/emails');
4
5curl_setopt_array($ch, [
6    CURLOPT_POST => true,
7    CURLOPT_RETURNTRANSFER => true,
8    CURLOPT_HTTPHEADER => [
9        'Authorization: Bearer ' . getenv('VAARTIX_API_KEY'),
10        'Content-Type: application/json',
11    ],
12    CURLOPT_POSTFIELDS => json_encode([
13        'from' => 'Acme <[email protected]>',
14        'to' => ['[email protected]'],
15        'subject' => 'Hello from Vaartix',
16        'html' => '<strong>It works!</strong>',
17    ]),
18]);
19
20echo curl_exec($ch);
POST https://api.vaartix.com/emailsREST API · SDK soon
terminal
1curl -X POST 'https://api.vaartix.com/emails' \
2  -H "Authorization: Bearer $VAARTIX_API_KEY" \
3  -H 'Content-Type: application/json' \
4  -d '{
5    "from": "Acme <[email protected]>",
6    "to": ["[email protected]"],
7    "subject": "Hello from Vaartix",
8    "html": "<strong>It works!</strong>"
9  }'
POST https://api.vaartix.com/emailscURL
main.go
1package main
2
3import (
4    "bytes"
5    "encoding/json"
6    "fmt"
7    "io"
8    "net/http"
9    "os"
10)
11
12func main() {
13    body, _ := json.Marshal(map[string]any{
14        "from":    "Acme <[email protected]>",
15        "to":      []string{"[email protected]"},
16        "subject": "Hello from Vaartix",
17        "html":    "<strong>It works!</strong>",
18    })
19
20    req, _ := http.NewRequest("POST", "https://api.vaartix.com/emails", bytes.NewReader(body))
21    req.Header.Set("Authorization", "Bearer "+os.Getenv("VAARTIX_API_KEY"))
22    req.Header.Set("Content-Type", "application/json")
23
24    res, err := http.DefaultClient.Do(req)
25    if err != nil {
26        panic(err)
27    }
28    defer res.Body.Close()
29
30    out, _ := io.ReadAll(res.Body)
31    fmt.Println(string(out))
32}
POST https://api.vaartix.com/emailsREST API
main.rs
1use serde_json::json;
2
3fn main() -> Result<(), reqwest::Error> {
4    let key = std::env::var("VAARTIX_API_KEY").expect("VAARTIX_API_KEY not set");
5
6    let res = reqwest::blocking::Client::new()
7        .post("https://api.vaartix.com/emails")
8        .bearer_auth(key)
9        .json(&json!({
10            "from": "Acme <[email protected]>",
11            "to": ["[email protected]"],
12            "subject": "Hello from Vaartix",
13            "html": "<strong>It works!</strong>"
14        }))
15        .send()?;
16
17    println!("{}", res.text()?);
18    Ok(())
19}
POST https://api.vaartix.com/emailsREST API
SendEmail.java
1import java.net.URI;
2import java.net.http.HttpClient;
3import java.net.http.HttpRequest;
4import java.net.http.HttpResponse;
5
6public class SendEmail {
7    public static void main(String[] args) throws Exception {
8        String json = """
9            {
10              "from": "Acme <[email protected]>",
11              "to": ["[email protected]"],
12              "subject": "Hello from Vaartix",
13              "html": "<strong>It works!</strong>"
14            }
15            """;
16
17        HttpRequest request = HttpRequest.newBuilder()
18            .uri(URI.create("https://api.vaartix.com/emails"))
19            .header("Authorization", "Bearer " + System.getenv("VAARTIX_API_KEY"))
20            .header("Content-Type", "application/json")
21            .POST(HttpRequest.BodyPublishers.ofString(json))
22            .build();
23
24        HttpResponse<String> response = HttpClient.newHttpClient()
25            .send(request, HttpResponse.BodyHandlers.ofString());
26
27        System.out.println(response.body());
28    }
29}
POST https://api.vaartix.com/emailsREST API
send.exs
1Mix.install([{:req, "~> 0.5"}])
2
3response =
4  Req.post!("https://api.vaartix.com/emails",
5    auth: {:bearer, System.fetch_env!("VAARTIX_API_KEY")},
6    json: %{
7      from: "Acme <[email protected]>",
8      to: ["[email protected]"],
9      subject: "Hello from Vaartix",
10      html: "<strong>It works!</strong>"
11    }
12  )
13
14IO.inspect(response.body)
POST https://api.vaartix.com/emailsREST API
Program.cs
1using System.Net.Http.Headers;
2using System.Net.Http.Json;
3
4using var http = new HttpClient();
5http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
6    "Bearer", Environment.GetEnvironmentVariable("VAARTIX_API_KEY"));
7
8var response = await http.PostAsJsonAsync("https://api.vaartix.com/emails", new
9{
10    from = "Acme <[email protected]>",
11    to = new[] { "[email protected]" },
12    subject = "Hello from Vaartix",
13    html = "<strong>It works!</strong>",
14});
15
16Console.WriteLine(await response.Content.ReadAsStringAsync());
POST https://api.vaartix.com/emailsREST API
POST /emails
1POST /emails HTTP/1.1
2Host: api.vaartix.com
3Authorization: Bearer vx_xxxxxxxxx
4Content-Type: application/json
5
6{
7  "from": "Acme <[email protected]>",
8  "to": ["[email protected]"],
9  "subject": "Hello from Vaartix",
10  "html": "<strong>It works!</strong>"
11}
POST https://api.vaartix.com/emailsHTTP
smtp.ts
1// SMTP relay arrives soon after launch.
2import nodemailer from 'nodemailer';
3
4const transporter = nodemailer.createTransport({
5  host: 'smtp.vaartix.com',
6  port: 587,
7  secure: false, // upgraded with STARTTLS
8  auth: {
9    user: 'vaartix',
10    pass: process.env.VAARTIX_API_KEY,
11  },
12});
13
14await transporter.sendMail({
15  from: 'Acme <[email protected]>',
16  to: '[email protected]',
17  subject: 'Hello from Vaartix',
18  html: '<strong>It works!</strong>',
19});
POST https://api.vaartix.com/emailsComing soon

Code samples preview the Vaartix API, which opens with the private beta.

Delivered
  1. HTTP 200 { "id": "em_3f9c2a71d4b8" }
  2. HTTP 200 { "id": "em_8b14e60c92af" }
  3. HTTP 200 { "id": "em_c27d5f03a6e1" }
  4. HTTP 200 { "id": "em_51ae9b7c08d3" }

Test mode

Send to special test addresses to see deliveries, bounces and complaints, without a single real email leaving your account.

  1. Opened

    to [email protected] with subject Your login code

    in Gmail on Android

  2. Complained

    to [email protected] with feedback Spam

    in Yahoo Mail on iOS

Webhooks, your way

Get a signed request on your server the moment an email is delivered, opened, clicked, bounced or marked as spam. Subscribe only to the events you need.

How it works

Your first email, in minutes.

01

Add your domain

Copy the DNS records into your provider and verify. We take care of DKIM signing and bounces.

02

Create an API key

Send-only keys for your apps, full-access keys for automation. Shown once, stored hashed.

03

Send and track

Call the API, then follow every email in the dashboard or through webhooks.

Start sending, in rupees.

We're onboarding a small group of Indian startups and developers. Tell us what you send and roughly how much.