Xitroo API

Nutze Xitroo aus deinem eigenen Code: Bestätigungsmails in End-to-End-Tests empfangen, Registrierungen automatisieren oder E-Mails von jeder @xitroo.de-Adresse senden.

So bekommst du deinen API-Schlüssel

1

Tarif wählen

Wähl unten Developer, Team oder Business und klick auf Abonnieren.

2

Sicher bei Stripe bezahlen

Karte oder andere Zahlarten. Monatlich in US-Dollar, Rechnung per E-Mail, jederzeit kündbar.

3

Schlüssel erhalten

Dein Schlüssel (xit_…) erscheint direkt nach der Zahlung und kommt zusätzlich per E-Mail.

Schlüssel verloren? Neuen anfordern →

Tarife

zzgl. anfallender Umsatzsteuer, monatlich in US-Dollar abgerechnet, jederzeit kündbar

Abo verwalten

Tarif wechseln, Zahlungsdaten ändern, Rechnungen herunterladen oder kündigen. Gib deinen API-Schlüssel ein.

API-Schlüssel verloren?

Gib die E-Mail-Adresse deines Abos ein. Wir schicken dir einen neuen Schlüssel, der alte gilt dann nicht mehr.

Schnellstart

  1. Hol dir deinen API-Schlüssel (siehe oben): Er erscheint direkt nach der Zahlung und kommt zusätzlich per E-Mail. Er beginnt mit xit_.
  2. Schick ihn bei jeder Anfrage in der Kopfzeile X-Api-Key mit.
  3. Jede Adresse @xitroo.de, @xitroo.com oder @xitroo.fr funktioniert sofort. E-Mails werden nach 48 Stunden gelöscht.
curl -H "X-Api-Key: $XITROO_API_KEY" \
  "https://api.xitroo.com/v1/mails?mailAddress=mein-test-42@xitroo.de&mailsPerPage=25&minTimestamp=0&maxTimestamp=$(date +%s)&locale=de"

Basis-URL: https://api.xitroo.com/v1 · alle Antworten sind JSON · der Schlüssel gehört nie in die URL.

Beispiele

Getesteter Code in acht Sprachen. Setz die Umgebungsvariable XITROO_API_KEY auf deinen Schlüssel.

Auf eine Bestätigungsmail warten

ADDRESS="my-test-42@xitroo.com"
SINCE=$(date +%s)
# ... sign up with $ADDRESS in your app ...
while :; do
  sleep 2
  ID=$(curl -s -H "X-Api-Key: $XITROO_API_KEY" \
    "https://api.xitroo.com/v1/mails?mailAddress=$ADDRESS&mailsPerPage=10&minTimestamp=$SINCE&maxTimestamp=$(( $(date +%s) + 60 ))&locale=en" \
    | jq -r '.mails[0]._id // empty')
  [ -n "$ID" ] && break
done
curl -s -H "X-Api-Key: $XITROO_API_KEY" \
  "https://api.xitroo.com/v1/mail?id=$ID&locale=en" | jq -r .subject
<?php
$api = 'https://api.xitroo.com/v1';
$ctx = stream_context_create(['http' => [
    'header' => 'X-Api-Key: ' . getenv('XITROO_API_KEY'),
    'ignore_errors' => true,
]]);
$address = 'my-test-42@xitroo.com';
$since = time();
// ... sign up with $address in your app ...
do {
    sleep(2);
    $query = http_build_query([
        'mailAddress' => $address, 'mailsPerPage' => 10, 'locale' => 'en',
        'minTimestamp' => $since, 'maxTimestamp' => time() + 60,
    ]);
    $list = json_decode(file_get_contents("$api/mails?$query", false, $ctx), true);
} while (empty($list['mails']));

$id = $list['mails'][0]['_id'];
$mail = json_decode(file_get_contents("$api/mail?id=$id&locale=en", false, $ctx), true);
echo $mail['subject'], PHP_EOL;
// Node.js 18+, save as wait.mjs
const API = "https://api.xitroo.com/v1";
const headers = { "X-Api-Key": process.env.XITROO_API_KEY };
const address = "my-test-42@xitroo.com";
const since = Math.floor(Date.now() / 1000);
// ... sign up with address in your app ...
let list;
do {
  await new Promise((r) => setTimeout(r, 2000));
  const q = new URLSearchParams({
    mailAddress: address, mailsPerPage: 10, locale: "en",
    minTimestamp: since, maxTimestamp: Math.floor(Date.now() / 1000) + 60,
  });
  list = await (await fetch(`${API}/mails?${q}`, { headers })).json();
} while (!list.mails);

const id = list.mails[0]._id;
const mail = await (await fetch(`${API}/mail?id=${id}&locale=en`, { headers })).json();
console.log(mail.subject);
import os, time, requests

API = "https://api.xitroo.com/v1"
HEADERS = {"X-Api-Key": os.environ["XITROO_API_KEY"]}
address = "my-test-42@xitroo.com"
since = int(time.time())
# ... sign up with address in your app ...
while True:
    time.sleep(2)
    data = requests.get(f"{API}/mails", headers=HEADERS, params={
        "mailAddress": address, "mailsPerPage": 10, "locale": "en",
        "minTimestamp": since, "maxTimestamp": int(time.time()) + 60}).json()
    if data.get("mails"):
        break

mail_id = data["mails"][0]["_id"]
mail = requests.get(f"{API}/mail", headers=HEADERS,
                    params={"id": mail_id, "locale": "en"}).json()
print(mail["subject"])
require "net/http"
require "json"

API = "https://api.xitroo.com/v1"

def get(path, params)
  uri = URI("#{API}#{path}")
  uri.query = URI.encode_www_form(params)
  req = Net::HTTP::Get.new(uri)
  req["X-Api-Key"] = ENV.fetch("XITROO_API_KEY")
  res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(req) }
  JSON.parse(res.body)
end

address = "my-test-42@xitroo.com"
since = Time.now.to_i
# ... sign up with address in your app ...
list = {}
until list["mails"]
  sleep 2
  list = get("/mails", mailAddress: address, mailsPerPage: 10, locale: "en",
                       minTimestamp: since, maxTimestamp: Time.now.to_i + 60)
end

mail = get("/mail", id: list["mails"][0]["_id"], locale: "en")
puts mail["subject"]
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"time"
)

const api = "https://api.xitroo.com/v1"

func get(path string, params url.Values, out any) error {
	req, _ := http.NewRequest("GET", api+path+"?"+params.Encode(), nil)
	req.Header.Set("X-Api-Key", os.Getenv("XITROO_API_KEY"))
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	return json.NewDecoder(res.Body).Decode(out)
}

func main() {
	address := "my-test-42@xitroo.com"
	since := time.Now().Unix()
	// ... sign up with address in your app ...
	var list struct {
		Mails []struct {
			ID string `json:"_id"`
		} `json:"mails"`
	}
	for len(list.Mails) == 0 {
		time.Sleep(2 * time.Second)
		get("/mails", url.Values{
			"mailAddress": {address}, "mailsPerPage": {"10"}, "locale": {"en"},
			"minTimestamp": {strconv.FormatInt(since, 10)},
			"maxTimestamp": {strconv.FormatInt(time.Now().Unix()+60, 10)},
		}, &list)
	}

	var mail struct {
		Subject string `json:"subject"`
	}
	get("/mail", url.Values{"id": {list.Mails[0].ID}, "locale": {"en"}}, &mail)
	fmt.Println(mail.Subject)
}
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class WaitForMail {
    static final String API = "https://api.xitroo.com/v1";
    static final HttpClient HTTP = HttpClient.newHttpClient();

    static String get(String pathAndQuery) throws Exception {
        HttpRequest req = HttpRequest.newBuilder(URI.create(API + pathAndQuery))
            .header("X-Api-Key", System.getenv("XITROO_API_KEY")).build();
        return HTTP.send(req, HttpResponse.BodyHandlers.ofString()).body();
    }

    public static void main(String[] args) throws Exception {
        String address = URLEncoder.encode("my-test-42@xitroo.com", StandardCharsets.UTF_8);
        long since = System.currentTimeMillis() / 1000;
        // ... sign up with the address in your app ...
        Matcher id;
        do {
            Thread.sleep(2000);
            long until = System.currentTimeMillis() / 1000 + 60;
            String list = get("/mails?mailAddress=" + address + "&mailsPerPage=10&locale=en"
                + "&minTimestamp=" + since + "&maxTimestamp=" + until);
            id = Pattern.compile("\"_id\":\"([0-9a-f]{24})\"").matcher(list);
        } while (!id.find());

        // JSON with subject, bodyText, bodyHtml, ... (parse it with your JSON library)
        System.out.println(get("/mail?id=" + id.group(1) + "&locale=en"));
    }
}
// .NET 6+ (dotnet new console), Program.cs
using System.Net.Http.Json;
using System.Text.Json;

var api = "https://api.xitroo.com/v1";
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", Environment.GetEnvironmentVariable("XITROO_API_KEY"));

var address = Uri.EscapeDataString("my-test-42@xitroo.com");
var since = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
// ... sign up with the address in your app ...
JsonElement mails;
while (true)
{
    await Task.Delay(2000);
    var until = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + 60;
    var list = await http.GetFromJsonAsync<JsonElement>(
        $"{api}/mails?mailAddress={address}&mailsPerPage=10&locale=en&minTimestamp={since}&maxTimestamp={until}");
    if (list.TryGetProperty("mails", out mails)) break;
}

var id = mails[0].GetProperty("_id").GetString();
var mail = await http.GetFromJsonAsync<JsonElement>($"{api}/mail?id={id}&locale=en");
Console.WriteLine(mail.GetProperty("subject").GetString());

Eine E-Mail senden

curl -X POST https://api.xitroo.com/v1/mail/send/premium \
  -H "X-Api-Key: $XITROO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "my-test-42@xitroo.com",
    "recipient": "you@example.com",
    "subject": "Hello",
    "bodyText": "Sent with the Xitroo API"
  }'
# reply: add "replyMailID": "<_id of a received mail>"
<?php
$ctx = stream_context_create(['http' => [
    'method' => 'POST',
    'header' => "Content-Type: application/json\r\n"
              . 'X-Api-Key: ' . getenv('XITROO_API_KEY'),
    'content' => json_encode([
        'from'      => 'my-test-42@xitroo.com',
        'recipient' => 'you@example.com',
        'subject'   => 'Hello',
        'bodyText'  => 'Sent with the Xitroo API',
        // 'replyMailID' => '<_id of a received mail>',
    ]),
    'ignore_errors' => true,
]]);
echo file_get_contents('https://api.xitroo.com/v1/mail/send/premium', false, $ctx), PHP_EOL;
// Node.js 18+, save as send.mjs
const res = await fetch("https://api.xitroo.com/v1/mail/send/premium", {
  method: "POST",
  headers: {
    "X-Api-Key": process.env.XITROO_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    from: "my-test-42@xitroo.com",
    recipient: "you@example.com",
    subject: "Hello",
    bodyText: "Sent with the Xitroo API",
    // replyMailID: "<_id of a received mail>",
  }),
});
console.log(res.status, await res.json());
import os, requests

r = requests.post(
    "https://api.xitroo.com/v1/mail/send/premium",
    headers={"X-Api-Key": os.environ["XITROO_API_KEY"]},
    json={
        "from": "my-test-42@xitroo.com",
        "recipient": "you@example.com",
        "subject": "Hello",
        "bodyText": "Sent with the Xitroo API",
        # "replyMailID": "<_id of a received mail>",
    },
)
print(r.status_code, r.json())
require "net/http"
require "json"

uri = URI("https://api.xitroo.com/v1/mail/send/premium")
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json",
                               "X-Api-Key" => ENV.fetch("XITROO_API_KEY"))
req.body = {
  from: "my-test-42@xitroo.com",
  recipient: "you@example.com",
  subject: "Hello",
  bodyText: "Sent with the Xitroo API"
  # replyMailID: "<_id of a received mail>"
}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(req) }
puts res.code, res.body
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	body, _ := json.Marshal(map[string]string{
		"from":      "my-test-42@xitroo.com",
		"recipient": "you@example.com",
		"subject":   "Hello",
		"bodyText":  "Sent with the Xitroo API",
		// "replyMailID": "<_id of a received mail>",
	})
	req, _ := http.NewRequest("POST", "https://api.xitroo.com/v1/mail/send/premium", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-Api-Key", os.Getenv("XITROO_API_KEY"))
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(res.Status, string(out))
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class SendMail {
    public static void main(String[] args) throws Exception {
        String json = """
            {"from": "my-test-42@xitroo.com", "recipient": "you@example.com",
             "subject": "Hello", "bodyText": "Sent with the Xitroo API"}""";
        HttpRequest req = HttpRequest.newBuilder(URI.create("https://api.xitroo.com/v1/mail/send/premium"))
            .header("Content-Type", "application/json")
            .header("X-Api-Key", System.getenv("XITROO_API_KEY"))
            .POST(HttpRequest.BodyPublishers.ofString(json))
            .build();
        HttpResponse<String> res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString());
        System.out.println(res.statusCode() + " " + res.body());
    }
}
// .NET 6+ (dotnet new console), Program.cs
using System.Net.Http.Json;

using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", Environment.GetEnvironmentVariable("XITROO_API_KEY"));

var res = await http.PostAsJsonAsync("https://api.xitroo.com/v1/mail/send/premium", new
{
    @from = "my-test-42@xitroo.com",
    recipient = "you@example.com",
    subject = "Hello",
    bodyText = "Sent with the Xitroo API",
    // replyMailID = "<_id of a received mail>",
});
Console.WriteLine($"{(int)res.StatusCode} {await res.Content.ReadAsStringAsync()}");

E-Mails empfangen

Postfach auflisten

GET /v1/mails

Parameter Pflicht Bedeutung
mailAddress ja Vollständige Adresse, z. B. mein-test-42@xitroo.de
mailsPerPage ja 1 – 200
minTimestamp ja Unix-Sekunden; nur E-Mails, die danach ankamen
maxTimestamp ja Unix-Sekunden; nur E-Mails, die davor ankamen (höchstens eine Stunde in der Zukunft)
locale ja de oder en (Sprache der Fehlermeldungen)

Antwort, neueste zuerst:

{
  "totalMails": 2,
  "mails": [
    { "_id": "6748f1c2a1b2c3d4e5f60718", "from": "noreply@example.com",
      "subject": "Bestätige dein Konto", "arrivalTimestamp": 1793786065.12 }
  ]
}

Ein leeres Postfach antwortet mit HTTP 200 und {"type": "empty response", ...} statt einer Liste.

Auf eine E-Mail warten: Setz minTimestamp auf den Startzeitpunkt deines Tests, dann siehst du nur neue E-Mails.

Eine E-Mail lesen

GET /v1/mail?id=<_id>&locale=de

Liefert deliveredTo, from, subject, arrivalTimestamp, expireTimestamp, bodyText, bodyHtml, rawHeader, messageID und attachments (Dateinamen).

Anhänge

GET /v1/mail/attachment?id=<_id>&filename=<name>&locale=de liefert die Datei.

E-Mails senden

POST /v1/mail/send/premium mit Kopfzeile X-Api-Key und JSON-Körper:

Feld Pflicht Bedeutung
from ja Deine Absenderadresse, muss auf @xitroo.de, @xitroo.com oder @xitroo.fr enden
recipient ja Genau eine Empfängeradresse
subject ja Bis 200 Zeichen, ohne Zeilenumbruch
bodyText ja Text, bis 100 KB
replyMailID nein _id einer empfangenen E-Mail: deine E-Mail geht als Antwort darauf
curl -X POST https://api.xitroo.com/v1/mail/send/premium \
  -H "X-Api-Key: $XITROO_API_KEY" -H "Content-Type: application/json" \
  -d '{"from":"mein-test-42@xitroo.de","recipient":"support@example.com","subject":"Hallo","bodyText":"Hallo zusammen!"}'
{ "sendSuccess": true, "sendsPerMonth": 1000, "sentThisMonth": 17 }

Antworten an deine Xitroo-Adresse landen in ihrem Postfach wie jede andere E-Mail. Das Monatskontingent beginnt am 1. jedes Monats (UTC) neu; höchstens 10 E-Mails pro Minute.

Grenzen und Fehler

HTTP code Bedeutung
400 PARAMETER_INVALID, PARAMETER_REQUIRED Ein Parameter fehlt oder ist ungültig; siehe Feld parameter
401 INVALID_API_KEY Schlüssel fehlt, ist falsch oder das Abo ist nicht aktiv
429 RATE_LIMITED Zu viele Anfragen; Retry-After Sekunden warten
429 QUOTA_EXCEEDED Monatskontingent für den Versand aufgebraucht
502 SEND_FAILED Der Mailserver hat die E-Mail nicht angenommen; zählt nicht zum Kontingent

Jeder Fehler ist JSON: {"status": 429, "code": "RATE_LIMITED", "message": "...", "retryAfter": 12}.

Halte deinen Schlüssel geheim: nur auf Servern oder in der CI verwenden, nie in einer öffentlichen Webseite oder App.

Abrechnung

Abos laufen monatlich und werden von Stripe in US-Dollar abgerechnet; die Rechnung kommt jeden Monat per E-Mail. Tarif wechseln, Karte ändern, Rechnungen herunterladen oder kündigen kannst du jederzeit im Kundenportal (siehe Abo verwalten oben). Nach der Kündigung gilt dein Schlüssel bis zum Ende des bezahlten Zeitraums.