Entwickler-API
Erstelle Wegwerf-Adressen und lies eingehende Mails und Bestätigungscodes aus deinem eigenen Code.
Erste Schritte
Die API spricht JSON über HTTPS. Jede Antwort enthält ein success-Feld und entweder data oder ein error-Objekt mit einem maschinenlesbaren Code.
Basis-URL: https://edumail.to/api/v1
curl -X POST https://edumail.to/api/v1/inbox \
-H "X-API-Key: $POSTINBOX_KEY"
# wait for mail, then:
curl "https://edumail.to/api/v1/messages?email=ADDRESS" \
-H "X-API-Key: $POSTINBOX_KEY"Authentifizierung
Erstelle in deinem Dashboard unter API einen Schlüssel und sende ihn bei jeder Anfrage im Header X-API-Key. Bearer-Authentifizierung funktioniert ebenfalls. Behandle den Schlüssel wie ein Passwort; du kannst ihn jederzeit ersetzen oder widerrufen.
X-API-Key: pim_live_3f9c…
# or
Authorization: Bearer pim_live_3f9c…Zugang
Die API wird mit deiner ersten Aufladung von mindestens $2.00 freigeschaltet. Bis dahin antwortet jeder Endpunkt mit 403:
{
"success": false,
"error": {
"code": "API_LOCKED",
"message": "First deposit of minimum $2.00 required to activate API access"
}
}Abrechnung
Eine Adresse auf einer kostenlosen Domain kostet nichts. Auf einer Premium-Domain, etwa diplo.edu.pl für $0.20, wird der Preis im selben Schritt, in dem die Adresse entsteht, von deinem Guthaben abgezogen. Die Antwort enthält den Betrag und dein neues Guthaben. Reicht das Guthaben nicht, schlägt die Anfrage mit 402 fehl und es wird nichts abgebucht.
Ratenlimits
Jedes Konto hat ein Kontingent von 120 Anfragen, das sich mit 60 Anfragen pro Minute auffüllt. Jede Antwort enthält die Header X-RateLimit-Limit, X-RateLimit-Remaining und X-RateLimit-Reset. Ist das Kontingent leer, erhältst du 429 mit einem Retry-After-Header.
Fehler
Fehler verwenden die üblichen HTTP-Statuscodes plus einen error.code, auf den du reagieren kannst.
| Parameter | Typ | Beschreibung |
|---|---|---|
| 401 MISSING_API_KEY / INVALID_API_KEY | auth | Der Schlüssel fehlt, ist falsch oder widerrufen. |
| 402 INSUFFICIENT_FUNDS | billing | Dein Guthaben deckt den Domainpreis nicht. |
| 403 API_LOCKED | access | Die API ist noch gesperrt; lade zum ersten Mal auf. |
| 403 ACCOUNT_SUSPENDED | access | Das Konto ist gesperrt. |
| 404 *_NOT_FOUND | lookup | Postfach, Nachricht oder Datei existiert nicht, ist abgelaufen oder gehört nicht dir. |
| 409 ADDRESS_TAKEN | create | Die Adresse ist bereits vergeben. |
| 422 INVALID_ALIAS | create | Der Adressname verstößt gegen die Regeln der Domain. |
| 429 RATE_LIMITED | limits | Zu viele Anfragen; warte so viele Sekunden wie in Retry-After angegeben. |
Postfächer
POST/inbox
Erstellt eine Adresse. Ohne Domain wird die kostenlose Domain mit der höchsten Priorität verwendet, sodass ein fehlender Parameter nie Geld kostet.
| Parameter | Typ | Beschreibung |
|---|---|---|
| domain | string | Zu verwendende Domain aus GET /domains. |
| domain_id | integer | Alternative zu domain. |
| alias | string | Adressname. Weglassen für einen zufälligen Namen. |
curl -X POST https://edumail.to/api/v1/inbox \
-H "X-API-Key: $POSTINBOX_KEY" \
-H "Content-Type: application/json" \
-d '{"domain":"diplo.edu.pl","alias":"signup.test"}'{
"success": true,
"data": {
"id": 1842,
"email": "signup.test@diplo.edu.pl",
"domain": "diplo.edu.pl",
"pool": "pro",
"is_custom": true,
"source": "api",
"message_count": 0,
"last_message_at": null,
"created_at": "2026-10-21T09:12:44Z",
"expires_at": "2026-10-28T09:12:44Z",
"billing": {
"charged": true,
"price": "0.20",
"balance_before": "2.00",
"balance_after": "1.80"
},
"balance": "1.80"
}
}GET/inbox?email=…
Gibt eines deiner aktiven Postfächer anhand von email (oder id) zurück.
DELETE/inbox?email=…
Löscht das Postfach mit allen Nachrichten und Anhängen.
GET/inboxes
Listet deine aktiven Postfächer, die neuesten zuerst.
| Parameter | Typ | Beschreibung |
|---|---|---|
| page | integer | Seitennummer ab 1. |
| per_page | integer | Einträge pro Seite, bis zu 100. |
GET/domains
Listet die Domains, die du nutzen kannst, mit Preis pro Adresse und Namensregeln. Das meta-Objekt enthält dein Guthaben.
{
"success": true,
"data": [
{
"id": 2,
"domain": "diplo.edu.pl",
"pool": "pro",
"price": "0.20",
"tag": "pro",
"custom_alias": true,
"alias_min_length": 4,
"alias_max_length": 32
},
{
"id": 1,
"domain": "mailfus.de",
"pool": "free",
"price": "0.00",
"tag": null,
"custom_alias": true,
"alias_min_length": 4,
"alias_max_length": 32
}
],
"meta": {
"balance": "1.80"
}
}GET/me
Gibt dein Guthaben, Aufladungen und Ausgaben insgesamt, die Zahl deiner erstellten Adressen und dein Ratenlimit zurück.
Nachrichten
GET/messages?email=…
Listet die Nachrichten eines Postfachs, die neuesten zuerst. Frage mit since_id auf die zuletzt gesehene ID ab, um nur neue Mails zu erhalten.
| Parameter | Typ | Beschreibung |
|---|---|---|
| string | Die Postfachadresse. | |
| since_id | integer | Nur Nachrichten mit höherer ID zurückgeben. |
| limit | integer | Bis zu 100 Nachrichten; Standard 50. |
| unread | boolean | Auf 1 setzen für nur ungelesene Nachrichten. |
{
"success": true,
"data": [
{
"id": 99812,
"inbox": "signup.test@diplo.edu.pl",
"from": {
"email": "no-reply@acme.example",
"name": "Acme"
},
"subject": "Your Acme verification code",
"snippet": "Your code is 482913. It expires in 10 minutes.",
"otp_code": "482913",
"magic_link": null,
"attachment_count": 0,
"size": 4812,
"is_read": false,
"received_at": "2026-10-21T09:13:02Z"
}
],
"meta": {
"inbox": "signup.test@diplo.edu.pl",
"expires_at": "2026-10-28T09:12:44Z",
"count": 1,
"latest_id": 99812
}
}GET/message?id=…
Gibt eine Nachricht mit reinem Text, bereinigtem HTML mit maskierten Links, erkanntem OTP-Code und Anmeldelink, Anhängen und signierten Download-Links zurück. Beim Lesen wird die Nachricht als gelesen markiert.
DELETE/message?id=…
Löscht eine Nachricht und ihre Anhänge.
Downloads
Download-Links in Nachrichtenantworten sind signiert und funktionieren eine Stunde lang ohne API-Schlüssel, sodass du sie an andere Tools weitergeben kannst. Mit deinem Schlüssel kannst du diese Endpunkte auch direkt aufrufen.
| Parameter | Typ | Beschreibung |
|---|---|---|
| GET /attachment?id=… | file | Ein Anhang, anhand der Anhang-ID. |
| GET /raw?id=… | .eml | Die Originalnachricht, anhand der Nachrichten-ID. |
| GET /attachments-zip?id=… | .zip | Alle Anhänge einer Nachricht als ZIP-Datei. |
Webhooks
Schalte Webhooks im Dashboard ein, um bei jeder neuen Nachricht einen JSON-POST zu erhalten. Die Nutzdaten enthalten Postfach, Absender, Betreff, Vorschau, OTP-Code, Anmeldelink, Text und Anhangsliste.
POST /your/endpoint
Content-Type: application/json
X-PostInbox-Event: message.received
X-PostInbox-Delivery: 5521
X-PostInbox-Timestamp: 1792662782
X-PostInbox-Signature: sha256=8f3c…Jede Anfrage ist mit deinem Signaturschlüssel signiert. Berechne einen HMAC-SHA256 aus Zeitstempel, einem Punkt und dem unveränderten Body, vergleiche ihn mit dem Signatur-Header und lehne Anfragen ab, die älter als fünf Minuten sind:
<?php
$body = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_POSTINBOX_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_POSTINBOX_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, getenv('POSTINBOX_WEBHOOK_SECRET'));
if (!hash_equals($expected, $signature) || abs(time() - (int) $timestamp) > 300) {
http_response_code(401);
exit;
}
$event = json_decode($body, true);
// $event['otp_code'], $event['inbox'], $event['subject'] ...
http_response_code(204);import crypto from "node:crypto";
export function verify(rawBody, headers, secret) {
const ts = headers["x-postinbox-timestamp"];
const expected = "sha256=" + crypto.createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex");
const given = headers["x-postinbox-signature"] || "";
return given.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected)) &&
Math.abs(Date.now() / 1000 - Number(ts)) < 300;
}Antworte mit einem beliebigen 2xx-Status. Fehlgeschlagene Zustellungen werden nach 1, 5 und 15 Minuten, 1 Stunde und 6 Stunden wiederholt. Eine 4xx-Antwort (außer 408 oder 429) beendet die Wiederholungen.