API ve otomasyon

Geçici e-posta API'si: betikle gelen kutusu otomasyonu

Üç uç nokta, anahtar yok, hesap yok: bir betiğin bir adres açması, üzerine düşeni okuması ve arkasını toplaması için yeterli. İşte döngünün tamamı — ne kadar hızlı gidebileceğinizi belirleyen iki hız bütçesi ve oynatamayacağınız tek son tarih.

  • Orta düzey
  • 22 dk okuma
Mavi bir dişli tarafından çalıştırılan gri bir konveyör bandı, iki mavi zarfı açık gri bir tepsiye doğru taşıyor

Üç çağrı, ve kurulacak hiçbir şey yok

Arayüzün tamamı https://grabmail.io/api/v1 altındaki üç uç nokta, artı diğerlerinin size hazır olarak verdiği ekler için tek bir adrestir. Posta kutusu oluştur diye bir çağrı yoktur, ve bu yokluk bir eksiklik değildir: bir adres, posta ona ulaştığında var olmaya başlar, bu yüzden böyle bir çağrının yapacağı hiçbir şey yoktur.

ÇağrıNeyi yanıtlarNe geçirirsiniz
GET /mailboxBir adreste bekleyen her şey, en yeniden en eskiye.address, isteğe bağlı olarak da limit ve before
GET /message/{id}Bir mesajın tamamı: düz metin bölümü, HTML bölümü ve URL'si zaten hazırlanmış her ek.mailbox
DELETE /message/{id}Saklama süresinin dolmasını beklemek yerine onu hemen kaldırır.mailbox
GET /attachment/{id}Bir dosyanın baytları, tam olarak geldiği haliyle.mailbox

Her yanıt, her hata dahil, JSON'dur. Her zaman damgası RFC 3339 biçiminde UTC'dir. Mesaj kimlikleri opaktır: onları olduğu gibi geri verin, asla parçalarına ayırmayın.

İlk çağrı, ve boş bir adresin verdiği yanıt

Bir isim seçin, arkasına genel alan adlarından birini koyun ve okuyun. Önce var olması gereken hiçbir şey yoktur, ve sormakla da hiçbir şey oluşturulmaz.

shell
$ curl -sG https://grabmail.io/api/v1/mailbox \
  --data-urlencode "address=k7fq2m@grabmail.io"
yanıt
{
  "address": "k7fq2m@grabmail.io",
  "alias": "q4v8n2mt7xkd@example.net",
  "count": 1,
  "next": null,
  "messages": [
    {
      "id": "3QK7ZB5M9WVXR2HD4TNFJ0PC6A",
      "from": "no-reply@example.com",
      "from_name": "Example",
      "subject": "Your verification code",
      "preview": "Your code is 481920. It expires in 10 minutes.",
      "has_html": false,
      "date": "2026-08-29T09:14:02Z",
      "seen": false,
      "attachments": 0,
      "expires_at": "2026-09-03T09:14:02Z"
    }
  ]
}

Beş alan, ve bunlardan ikisi göründüğünden daha ilginçtir:

count
Bu yanıtta kaç mesaj olduğu — posta kutusunun kaç tane tuttuğu değil. limit geçirdiğiniz an, bunlar iki farklı sayı olur.
next
Bundan sonraki sayfanın imleci, ya da sonrasında hiçbir şey yoksa null. Az önce size verilen son mesajın kimliğidir, bu yüzden sayfalamayı keşfetmenin ekstra bir çağrıya mal olmamasının nedeni de budur.
messages
Listenin kendisi, en yeniden en eskiye. Her girdi zaten şunları taşır: subject, from, date, seen, metnin kısa bir preview'i, bir HTML bölümü olup olmadığı ve kaç ek olduğu.
alias
Buraya teslimat yapan ama bu adres hakkında hiçbir şey ele vermeyen ikinci bir adres. Gerçek adres yerine bunu bir forma verin; sonunda onu bu hizmete yazan kim olursa olsun, boş bir posta kutusu bulur.
address
Adresin anlaşıldığı hâli, küçük harfe çevrilmiş ve kırpılmış olarak. Adresi parçalardan oluşturuyorsanız, gönderdiğinizle karşılaştırın.

İlk ellinin ötesini okumak

Bir çağrı, varsayılan olarak en fazla elli, en üst sınırda ise iki yüz mesajla yanıt verir. Yoğun bir catch-all adres bir öğleden sonrada ikisini de geçer, ve okuyucuların yanlış tahmin ettiği kısım bundan sonra gelendir — çünkü bu bir sayfa numarası değildir.

limit
Bu çağrıda kaç tanenin döndürüleceği, 1 ile 200 arası. Aralık dışı değerler reddedilmek yerine sınırlanır, bu yüzden limit=5000 size sessizce 200 verir.
before
Zaten elinizde olan en eski mesajın kimliği. Ondan sonrakileri alırsınız. Önceki yanıtın next alanına ne koyduysa onu geri geçirin.
next
null, posta kutusunun sonuna ulaştığınız anlamına gelir. Bu, güvenilir tek liste sonu sinyalidir: kısa bir sayfa bu anlama gelmez, çünkü bir sayfa yalnızca sunucu öyle dediğinde kısadır.
bir çağrının döndürdüğübefore='in geri getirdiğinexten yenien eskiBir sayfa numarası değil — sıralı bir listede konum.
limit bir yanıtı üst sınırlar, next o yanıtın nerede durduğunu adlandırır, ve before ondan sonrakileri ister.
bir posta kutusunun tamamında dolaşmak, en eski sayfa en son
ADDR="k7fq2m@grabmail.io"
CURSOR=""

while :; do
  PAGE=$(curl -fsG https://grabmail.io/api/v1/mailbox \
           --data-urlencode "address=$ADDR" \
           --data-urlencode "limit=200" \
           ${CURSOR:+--data-urlencode "before=$CURSOR"})

  printf '%s' "$PAGE" | jq -c '.messages[]'

  CURSOR=$(printf '%s' "$PAGE" | jq -r '.next // empty')
  [ -n "$CURSOR" ] || break
  sleep 1
done

next null olmadığı sürece döngüye devam edin, ve posta kutusunun tamamına sahip olursunuz, ne kadar büyümüş olursa olsun. Her çağrı bir ofset değil, bir indeks üzerinde aralık okumasıdır, bu yüzden bininci sayfa da ilkinin maliyetine mal olur.

Başka bir posta kutusundan gelen ya da o zamandan beri süresi dolmuş bir imleç bir hata değildir: boş bir sayfa ve next: null alırsınız. Bu doğru yanıttır — bunun yerine en yeni sayfayı tekrarlamak, bir betiğe zaten işlediği postayı verirdi — ama bunun anlamı, bayat bir imlecin tam olarak liste sonu gibi görünmesidir.

Bir mesajı açmak, ve buna gerek olmadığı zaman

Listeden gelen kimlik ile teslim edildiği posta kutusu birlikte size mesajın kendisini verir. İkisi de zorunludur: bir gelen kutusundan sızan bir kimlik başka birini okumak için kullanılamaz, çünkü her arama adrese göre de kapsam sınırlıdır.

shell
$ curl -sG https://grabmail.io/api/v1/message/3QK7ZB5M9WVXR2HD4TNFJ0PC6A \
  --data-urlencode "mailbox=k7fq2m@grabmail.io"
yanıt
{
  "id": "3QK7ZB5M9WVXR2HD4TNFJ0PC6A",
  "from": "no-reply@example.com",
  "to": "k7fq2m@grabmail.io",
  "subject": "Your verification code",
  "date": "2026-08-29T09:14:02Z",
  "expires_at": "2026-09-03T09:14:02Z",
  "text": "Your code is 481920. It expires in 10 minutes.",
  "html": null,
  "attachments": []
}
text
Düz metin bölümü. Varsa bunu ayrıştırın: kararlıdır, hiçbir biçimlendirme taşımaz, ve içindeki altı haneli bir kod, altı haneli bir koddur.
html
HTML bölümü, ya da gönderen bunu göndermediyse null. Onay bağlantıları çoğu zaman yalnızca burada bulunur.
attachments
Dosya başına bir girdi, her birinin çekilecek URL'si zaten hazırlanmış olarak. Hiç yoksa null değil, boş bir liste.
expires_at
Bu mesajın ne zaman silineceği, date ile aynı RFC 3339 biçiminde. Bunu hesaplamak yerine okuyun — saklama süresi, dışarıdan emin olabileceğiniz bir ayar değildir.

Çoğu zaman bu çağrıyı tamamen atlayabilirsiniz. Listeleme zaten konuyu, göndereni, tarihi ve metnin kısa bir önizlemesini döndürür, ki bu, bir mesajın beklediğiniz mesaj olmadığına karar vermek için yeterlidir. Bir posta kutusundaki her mesajı, hiçbirini istemediğinizi öğrenmek için çekmek, bir betiğin yavaşlamasının en yaygın yoludur.

Bir dosyayı dışarı almak

Her ek kendi url'sini taşır, ve döngüyü yazmadan önce bilinmeye değer ayrıntı, bunun mutlak bir adres değil, bu kaynak üzerinde bir yol olduğudur — içinde mailbox parametresi zaten vardır. Önüne kaynağı koyun, çekin; geçirecek ya da yetkilendirecek başka hiçbir şey yoktur.

shell
ADDR="k7fq2m@grabmail.io"
ID="3QK7ZB5M9WVXR2HD4TNFJ0PC6A"

curl -fsG https://grabmail.io/api/v1/message/$ID \
  --data-urlencode "mailbox=$ADDR" \
| jq -r '.attachments[] | "\(.url)\t\(.filename)"' \
| while IFS=$'\t' read -r path name; do
    curl -fs "https://grabmail.io$path" -o "$name"
  done

Gönderen dosyayı ne olarak etiketlemiş olursa olsun, her zaman Content-Disposition: attachment ile application/octet-stream yanıtı verir. Bu bilinçlidir — bir yabancının text/html'ini olduğu gibi yansıtmak, bir ekin bu kaynak üzerinde bir sayfa olarak çalışmasına izin verirdi — bu yüzden türle ilgilenen bir betik bunu, bir talimat değil veri olarak durduğu mesaj JSON'undan okur.

Dosyalar dahil mesajın tamamı 5 MB ile sınırlıdır. Base64 bir ikiliyle işini bitirdikten sonra bu tavanın ne anlama geldiği kendi başına bir konudur, ve bunun için ekler hakkında bir rehber vardır.

Tek değil, iki hız bütçesi

Bilinmeye değer ve gözden kaçması kolay olan kısım şu: bir adresi listelemek ve ondan okumak ayrı ayrı ölçülür, çünkü ikisi aynı risk değildir. Bir adresi bilen herkes onun listesini yoklayabilir; bir mesajı okumak ise bir kimlik gerektirir, ve tahmin edilecek hiçbir şey yoktur.

Ne çağırıyorsunuzBütçePratikte anlamı
GET /mailboxAdres başına saniyede bir istekAmaçlanan yoklama ritmi budur, ve bu hızda asla kısıtlanmaz. Daha hızlısı reddedilir, ve zaten işe yaramazdı.
GET /message/{id}, GET /attachment/{id}, DELETEAdres başına çok daha cömertBir sayfa mesajı, aralarında durmadan tek seferde boşaltın. Bir arayüzün, bir yoklamanın çalıştığı saniyede bir mesaj açabilmesinin nedeni budur.
Her şey, toplamdaİstemci başına dakikada 1200 istekSaniyede bir yoklanan yirmi adres — herhangi bir gerçek otomasyonun rahatlıkla ötesinde, ve on bin adreste dolaşan tek bir sunucuya konan bir dur işareti.

Bunlardan herhangi birini aşarsanız, bekleme süresi saniye cinsinden Retry-After başlığında olacak şekilde 429 alırsınız. Kendi uydurduğunuz bir sayıyla geri çekilmek yerine buna uyun: sunucu size tam olarak ne zaman evet diyeceğini söylüyordur.

istendiği kadar uyuyun, daha fazla değil
read_box() {
  local wait
  while :; do
    BODY=$(curl -s -D /tmp/gm.h -G https://grabmail.io/api/v1/mailbox \
             --data-urlencode "address=$1")
    grep -qi '^HTTP/[0-9.]* 429' /tmp/gm.h || { printf '%s' "$BODY"; return 0; }
    wait=$(awk 'tolower($1) == "retry-after:" { print $2 + 0 }' /tmp/gm.h)
    sleep "${wait:-1}"
  done
}

Henüz gelmemiş bir mesajı nasıl bekleyeceğiniz — bir yeniden deneme sayısı değil bir son tarih, ve o geçtiğinde ne yapılacağı — doğrulama akışlarını test etme rehberinin konusudur. Oradaki döngü, zamanlanmış bir işin de ihtiyaç duyduğu döngüyle aynıdır.

Silmek, ve hepsinin altındaki taban

İşi biten bir mesaj, saklama süresini beklemek yerine hemen gidebilir. Çağrı idempotenttir: aynı kimliği iki kez silmek her iki seferde de 200 yanıtı verir, bu yüzden yeniden denenen bir istek asla bir başarısızlık gibi görünmez.

shell
$ curl -s -X DELETE -G https://grabmail.io/api/v1/message/3QK7ZB5M9WVXR2HD4TNFJ0PC6A \
  --data-urlencode "mailbox=k7fq2m@grabmail.io"
Aradığınızı bulduğunuzda silin
Bir mesajı işleyip yerinde bırakan bir betik, gördüklerinin kendi listesini tutmadığı sürece bir sonraki çalıştırmada onu yeniden işler. Silmek, daha ucuz bir muhasebedir.
Gizlilik için buna güvenmeyin
Varış ile silinme arasında, adresi bilen herkes onu okumuş olabilir. Silmek pencereyi kapatır; onu geri almaz.
Her şey ne olursa olsun 5 günde gider
Okunmuş ya da okunmamış, silinmiş ya da silinmemiş, bir mesaj vardığından 5 gün sonra kaybolur. Bu bir ayar değil, kesin bir sınırdır, ve hiçbir parametre onu uzatmaz.

Dallanılacak kısaltmalar, ve asla okunmaması gereken alan

Her başarısızlık, aynı iki alana sahip JSON'dur. error, kararlı, makine tarafından okunabilir bir kısaltmadır; message ise insanlar içindir ve her an yeniden ifade edilebilir. İkincisine göre dallanmak, bir betiğin hiçbir şeyin değişmediği bir günde bozulmasının yoludur.

Durum ve kısaltmaNe olduBir betiğin ne yapması gerektiği
400 invalid_addressAdres eksik, ya da bir adrese benzemiyor.Hemen başarısız olun. Hiçbir yeniden deneme miktarı bir yazım hatasını düzeltmez.
400 bad_cursorbefore bir mesaj kimliği değil.Hemen başarısız olun, ve kendi oluşturduğunuz bir şey yerine next'i geri geçirdiğinizi kontrol edin.
404 unknown_domainO alan adı burada barındırılmıyor.Hemen başarısız olun. Kendi alan adınızda bu, MX kaydıdır — bkz. bir alan adı bağlamak.
404 not_foundO posta kutusunda böyle bir mesaj yok, ya da saklama süresini geçmiş.Bunu kaybolmuş sayın. Yanlış posta kutusuna karşı okunan geçerli bir kimlik için de aynısını alırsınız.
429 rate_limitedYukarıdaki bütçelerden biri.Retry-After saniye kadar uyuyun ve devam edin. Bunu asla başarısız bir çalıştırma saymayın.

Bir adresi her saat boşaltan bir iş

Parçaları bir araya getirince zamanlanmış bir iş kısa olur. Bu iş, bir adreste bekleyen her mesajı alır, JSON olarak diske yazar ve siler — bu yüzden bir sonraki çalıştırma boş bir posta kutusundan başlar ve aynı mesajı asla iki kez işleyemez.

drain.sh
#!/usr/bin/env bash
set -euo pipefail

ADDR="orders@example.com"
OUT="/var/lib/mailsink"
API="https://grabmail.io/api/v1"

mkdir -p "$OUT"

while :; do
  page=$(curl -fsG "$API/mailbox" \
           --data-urlencode "address=$ADDR" \
           --data-urlencode "limit=200")

  ids=$(printf '%s' "$page" | jq -r '.messages[].id')
  [ -n "$ids" ] || break

  for id in $ids; do
    curl -fsG "$API/message/$id" \
      --data-urlencode "mailbox=$ADDR" > "$OUT/$id.json"
    curl -fs -X DELETE -G "$API/message/$id" \
      --data-urlencode "mailbox=$ADDR" > /dev/null
  done

  sleep 1
done
crontab
17 * * * * /usr/local/bin/drain.sh

Adını koymaya değer dört özellik var, çünkü çalışır bırakabileceğiniz bir işi izlemeniz gereken bir işten ayıran şey tam olarak bunlar:

  1. İki kez çalıştırmak güvenlidir. Aynı anda başlatılan iki kopya, aynı işi farklı bir sırayla yapar ve aynı mesajları siler; ikincisi boş bir posta kutusu bulur ve durur.
  2. Silmeden önce yazar. Disk doluysa ya da süreç öldürülürse, mesaj bir sonraki çalıştırmada hâlâ posta kutusundadır. Ters sıra, tam da önemli olduğu gün postayı kaybeder.
  3. Okumaz, boşaltır. Her mesaj diske güvenle yazılır yazılmaz gittiği için, bir sonraki listeleme bir sonraki iki yüzü döndürür — bu yüzden çalıştırmalar arasında dört yüz mesaj alan bir posta kutusu, en yeni elliye kadar değil, tamamen boşaltılır.
  4. Yüksek sesle başarısız olur. Cron'un size çıktıyı göndermesini sağlayan şey, sıfır olmayan bir çıkış kodudur. Kendi hatalarını yutan bir iş, bir aydır bozuk olan bir iştir.

Bu API'nin sizin için yapmayacağı şeyler

Yapmadığı dört şey var, her biri kasıtlı ve hiçbiri daha sonra eklenmeyecek. Bunları, sessizce yarı çalışan bir betikten keşfetmek yerine şimdi tasarımınıza dahil etmek daha iyidir:

Asla göndermez
Yalnızca alır. Bir mesajı hatta koyan bir uç nokta yoktur, bu yüzden burada hiçbir şey sahibi olmadığınız bir adresten göndermek için kullanılamaz.
Asla itmez
Ne webhook ne de geri çağırma vardır: siz sorarsınız, o yanıtlar. Posta gelene kadar bloke olmayı tercih eden bir yapay zeka ajanının bunun yerine MCP üzerinden wait_for_message'ı vardır — bkz. ajanlar için rehber.
Asla aramaz
Bir gönderen ya da konu için sorgu parametresi yoktur. Filtreleme sizin tarafınızda, listeleme üzerinden gerçekleşir — listelemenin bir önizleme taşımasının nedenlerinden biri de budur.
Genel bir alan adında asla kimlik doğrulamaz
Adresi bilen herkes posta kutusunu okur. Adres, sırrın tamamıdır, bu yüzden ona öyle davranın: onu asla bir müşterinin isminden türetmeyin, ve yüksek sesle okunmasından rahatsız olacağınız hiçbir şeyi paylaşılan bir alan adına yönlendirmeyin.

Sonuncusunun yanıtı, sahibi olduğunuz bir alan adıdır. MX'ini smtp.grabmail.io'e yöneltin, üzerindeki her adres öğrenilecek ikinci bir API ve döndürülecek bir anahtar olmadan aynı üç uç nokta üzerinden yanıt verir — ve talep üzerine, yalnızca bir bearer anahtarının açabileceği şekilde kapatılabilir. Bir alan adı bağlamak tek bir DNS kaydı gerektirir.

10 smtp.grabmail.io

Çalışır bırakmadan önce

Siz izlemeden çalışacak bir işte kontrol etmeye değer altı şey:

  1. Adres başına saniyede birden hızlı yoklamayın, ve beklemeniz söylendiğinde Retry-After'a uyun.
  2. next'i sona kadar takip edin, tek bir çağrının posta kutusunun tamamı olduğunu varsaymak yerine.
  3. Durum koduna ve error'a göre dallanın, asla message'a göre değil.
  4. Saklamanız gereken her şeyi silmeden önce yazın, ve 5 günün oynatamayacağınız bir taban olduğunu unutmayın.
  5. Çıkardığınız her şeyi kendi şablonunuza sabitleyin. Yalın bir altı haneli örüntü, önce gelen bir yılı, bir fiyatı ya da bir sipariş numarasını seve seve eşleştirir.
  6. Kontrol ettiğiniz bir alan adında olmadığı sürece adresin genel olduğunu varsayın, ve önemli olan her şeyi öyle olan bir tanesine koyun.

Hiçbiri bir hesap gerektirmez. Genel alan adlarını aştığınızda değişen şey adresteki alan adıdır — yukarıdaki üç çağrı tam olarak oldukları gibi kalır.

Sorular

Bir API anahtarına ihtiyacım var mı?

Hayır. Genel alan adlarında hesap, jeton ya da kaydolunacak hiçbir şey yoktur, ve buraya yönlendirdiğiniz bir alan adı da aynı uç noktalarda anahtar olmadan yanıt verir. Tek istisna, talep üzerine kapattığımız ve bir Authorization: Bearer başlığıyla okunan bir alan adıdır.

Ne kadar hızlı yoklayabilirim?

Listeleme için adres başına saniyede bir, ki bu amaçlanan ritimdir ve asla kısıtlanmaz. Bir mesajı ya da eki okumak ayrı ve çok daha cömert ölçülür, bu yüzden bir sayfa mesajı tek seferde boşaltabilirsiniz. Her şey birlikte istemci başına dakikada 1200 istekle sınırlıdır.

Posta kutusunun tamamını okuduğumu nasıl anlarım?

next, null döndüğünde. Bunu kısa bir sayfadan çıkarmayın: bir sayfanın ne olduğuna sunucu karar verir, ve limit'ten daha kısa bir sayfa, tek başına son anlamına gelmez.

Bunu bir tarayıcıdan çağırabilir miyim?

Evet. Yanıtlar Access-Control-Allow-Origin: * taşır, bu yüzden herhangi bir kaynaktaki bir sayfa, arada sizin bir proxy'niz olmadan uç noktaları doğrudan çağırabilir. Buradaki yetkilendirme asla bir çerez değildir, bu yüzden bu kadar açmanın bir bedeli yoktur.

Süresi dolmuş bir mesajı istersem ne olur?

Hiç var olmamış bir kimlik için olduğu gibi, tam olarak not_found ile 404. Okunmuş olsun olmasın, her şey vardığından 5 gün sonra silinir, ve hiçbir parametre bunu uzatmaz.

Posta geldiğinde bir webhook alabilir miyim?

Hayır — REST API sor-ve-yanıtla biçimindedir, geri çağırma yoktur. İstediğiniz şey mesaj gelene kadar bloke olan bir kodsa, MCP sunucusunda tam olarak bunu yapan ve ajanlar için tasarlanmış wait_for_message vardır.

Üretimde genel bir adres kullanmak güvenli mi?

Yalnızca bir yabancının okumasından rahatsız olmayacağınız şeyler için. Adresi bilen herkes, tıpkı site üzerinden olduğu gibi API üzerinden de posta kutusunu okuyabilir. Başka her şey için, sahibi olduğunuz bir alan adını buraya yönlendirin — çağrılar değişmez.

Hiç açmadığım bir mesaj neden okunmuş görünüyor?

Çünkü bir şey onu açtı. Bir mesajı API üzerinden okumak seen bayrağını ayarlar, ve bu bayrak o adrese bakan herkesle paylaşılır. Aynı posta kutusunu izleyen bir betik ile bir kişi birbirini sürekli şaşırtmaya devam eder, bu yüzden seen yerine işlediğiniz kimliklere göre filtreleyin.

Mesajları silmek zorunda mıyım?

Hayır — her şey 5 gün sonra kendiliğinden süresi dolar. Yine de zamanlanmış bir işte silmek yapmaya değer, çünkü boşaltılmış bir posta kutusu, zaten işlediğinizin mümkün olan en basit kaydıdır.

Henüz tazeyken deneyin

Bir adres tek tıkla alınır, hesap ve kart gerekmez. Bu rehberdeki her şey onunla hemen çalışır.

Tekrar hoş geldiniz

Kutularınız ve alan adlarınız tek bir yerde.