Tempik API

REST API untuk integrasi temp mail: buat mailbox, baca email yang masuk, dan ambil kode OTP secara otomatis. Semua respons berupa JSON, cocok untuk bot, script testing, dan automasi.

Base URL https://YOUR_DOMAIN
🔑
API Key Auth
Header x-api-key. Key disimpan sebagai hash SHA-256.
🪝
Webhooks
Push event email.received ke server kamu, ditandatangani HMAC.
🔢
OTP Extractor
Endpoint khusus untuk ambil kode verifikasi 4-8 digit.
🌐
Auto Multi-Domain
Domain baru terdeteksi otomatis dari email masuk, tanpa deploy ulang.

Getting Started

  1. Minta API key ke admin, atau buat sendiri lewat admin API jika kamu pemilik server. Key berawalan tmp_.
  2. Kirim header x-api-key pada setiap request ke /api/mailbox, /api/email, /api/otp, dan /api/domains.
  3. Buat mailbox, kirim email ke alamat itu, lalu poll /api/otp/:address atau /api/mailbox/:address sampai email masuk.
curl -X POST https://YOUR_DOMAIN/api/mailbox \
  -H "x-api-key: tmp_xxxxxxxxxxxxxxxxxxxxxxxx"
const res = await fetch('https://YOUR_DOMAIN/api/mailbox', {
  method: 'POST',
  headers: { 'x-api-key': 'tmp_xxxxxxxxxxxxxxxxxxxxxxxx' },
});
const { data } = await res.json();
console.log(data.email); // kopihujan42@example.com
import requests

r = requests.post('https://YOUR_DOMAIN/api/mailbox',
                  headers={'x-api-key': 'tmp_xxxxxxxxxxxxxxxxxxxxxxxx'})
print(r.json()['data']['email'])

Authentication

Semua endpoint di bawah /api/mailbox, /api/email, /api/otp, dan /api/domains wajib menyertakan header x-api-key. Hanya /health dan /api/config yang terbuka tanpa key.

x-api-key: tmp_98cd12b2bfe772cd3d39b3b001aa7cbc2ffb356802d71830
StatusErrorPenyebab
401Missing or malformed x-api-key headerHeader tidak ada atau tidak berawalan tmp_
401Invalid API keyKey tidak terdaftar atau sudah dihapus
Catatan keamanan. Server hanya menyimpan hash SHA-256 dari key. Plaintext key hanya ditampilkan sekali saat dibuat. Jika hilang, buat key baru dan hapus yang lama.

Responses & Errors

Setiap respons memakai amplop JSON yang sama. Cek field success dulu, lalu baca data atau error.

// Sukses
{ "success": true,  "data": { ... }, "ts": "2026-10-15T01:49:37.471Z" }

// Gagal
{ "success": false, "error": "Email not found", "ts": "2026-10-15T01:49:39.210Z" }
StatusArti
200OK
201Resource dibuat (mailbox atau API key)
400Input tidak valid: domain tidak dikenal, username tidak valid, alamat salah format
401API key atau admin token tidak valid
403Admin API nonaktif karena ADMIN_TOKEN belum di-set
404Email atau key tidak ditemukan

Retention

Email disimpan selama 24 jam sejak diterima, lalu dihapus otomatis oleh cron tiap jam. Field expires_at pada setiap email menunjukkan kapan email itu akan hilang. Mailbox (alamatnya) tetap ada dan bisa dipakai lagi.

Create Mailbox

POST /api/mailbox API KEY

Membuat mailbox baru, atau mengklaim alamat yang sudah ada. Tanpa body, alamat dibuat acak dengan gaya nama Indonesia (contoh kopihujan42).

Body fieldWajibKeterangan
usernameTidakLocal-part kustom. 1-64 karakter: a-z, 0-9, titik, underscore, strip.
domainTidakSalah satu domain dari GET /api/domains. Default: domain pertama.
curl -X POST https://YOUR_DOMAIN/api/mailbox \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"username":"myinbox"}'
const res = await fetch('https://YOUR_DOMAIN/api/mailbox', {
  method: 'POST',
  headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({ username: 'myinbox' }),
});
const { data } = await res.json();
r = requests.post(f'{BASE}/api/mailbox', headers=H, json={'username': 'myinbox'})
data = r.json()['data']
Response 201
{
  "success": true,
  "data": {
    "email": "myinbox@example.com",
    "username": "myinbox",
    "domain": "example.com",
    "expires_in_hours": 24
  },
  "ts": "2026-10-15T01:49:37.966Z"
}

List Emails

GET /api/mailbox/:address API KEY

Mengambil maksimal 50 email terbaru untuk alamat tersebut, terbaru di atas. Body email tidak disertakan di sini; pakai GET /api/email/:id untuk isi lengkapnya. Alamat harus di-URL-encode (@ menjadi %40).

curl https://YOUR_DOMAIN/api/mailbox/myinbox%40example.com \
  -H "x-api-key: $API_KEY"
const addr = encodeURIComponent('myinbox@example.com');
const res = await fetch(`https://YOUR_DOMAIN/api/mailbox/${addr}`, {
  headers: { 'x-api-key': API_KEY },
});
const { data } = await res.json();
console.log(data.emails);
from urllib.parse import quote
r = requests.get(f'{BASE}/api/mailbox/{quote("myinbox@example.com")}', headers=H)
emails = r.json()['data']['emails']
Response 200
{
  "success": true,
  "data": {
    "address": "myinbox@example.com",
    "count": 1,
    "emails": [
      {
        "id": "msg_1782461413912_0956a83c",
        "from_address": "noreply@openai.com",
        "subject": "Your ChatGPT code is 482913",
        "received_at": "2026-10-15 01:49:33",
        "expires_at": "2026-10-16T01:49:33.000Z",
        "size_bytes": 38,
        "otp_code": "482913"
      }
    ]
  },
  "ts": "2026-10-15T01:49:38.464Z"
}

Clear Mailbox

DELETE /api/mailbox/:address API KEY

Menghapus semua email di mailbox. Alamatnya tetap ada dan masih bisa menerima email baru.

curl -X DELETE https://YOUR_DOMAIN/api/mailbox/myinbox%40example.com \
  -H "x-api-key: $API_KEY"
Response 200
{ "success": true, "data": { "address": "myinbox@example.com", "cleared": 3 }, "ts": "..." }

Get Email

GET /api/email/:id API KEY

Mengambil satu email lengkap beserta body. Body berisi teks polos jika tersedia, atau HTML mentah jika email hanya punya bagian HTML.

curl https://YOUR_DOMAIN/api/email/msg_1782461413912_0956a83c \
  -H "x-api-key: $API_KEY"
Response 200
{
  "success": true,
  "data": {
    "id": "msg_1782461413912_0956a83c",
    "inbox_address": "myinbox@example.com",
    "from_address": "noreply@openai.com",
    "subject": "Your ChatGPT code is 482913",
    "body": "Enter 482913 to continue",
    "received_at": "2026-10-15 01:49:33",
    "expires_at": "2026-10-16T01:49:33.000Z",
    "size_bytes": 38,
    "otp_code": "482913"
  },
  "ts": "..."
}

Contoh ekstraksi kode sendiri dengan regex, jika kamu tidak mau memakai endpoint OTP:

const match = data.body.match(/\b\d{6}\b/);
const otp = match ? match[0] : null;

Delete Email

DELETE /api/email/:id API KEY
curl -X DELETE https://YOUR_DOMAIN/api/email/msg_1782461413912_0956a83c \
  -H "x-api-key: $API_KEY"
Response 200
{ "success": true, "data": { "deleted": true, "id": "msg_1782461413912_0956a83c" }, "ts": "..." }

Get OTP

GET /api/otp/:address API KEYOTP READY

Memindai email di mailbox, terbaru lebih dulu, dan mengembalikan kode verifikasi pertama yang ditemukan. Prioritas deteksi:

#SumberPola
1Subject6 digit berdiri sendiri
2Body (HTML dibersihkan)6 digit berdiri sendiri
3Subject lalu body4-8 digit berdiri sendiri
curl https://YOUR_DOMAIN/api/otp/myinbox%40example.com \
  -H "x-api-key: $API_KEY"
// Poll setiap 3 detik, maksimal 2 menit
async function waitForOtp(address, timeoutMs = 120000) {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    const res = await fetch(`https://YOUR_DOMAIN/api/otp/${encodeURIComponent(address)}`, {
      headers: { 'x-api-key': API_KEY },
    });
    const { data } = await res.json();
    if (data.found) return data.otp;
    await new Promise((r) => setTimeout(r, 3000));
  }
  throw new Error('OTP timeout');
}
import time

def wait_for_otp(address, timeout=120):
    deadline = time.time() + timeout
    while time.time() < deadline:
        r = requests.get(f'{BASE}/api/otp/{quote(address)}', headers=H)
        data = r.json()['data']
        if data['found']:
            return data['otp']
        time.sleep(3)
    raise TimeoutError('OTP timeout')
Response 200 (ditemukan)
{
  "success": true,
  "data": {
    "found": true,
    "otp": "482913",
    "from": "noreply@openai.com",
    "subject": "Your ChatGPT code is 482913",
    "email_id": "msg_1782461413912_0956a83c",
    "received_at": "2026-10-15 01:49:33"
  },
  "ts": "..."
}
Response 200 (belum ada)
{ "success": true, "data": { "found": false, "otp": null, "checked": 0 }, "ts": "..." }

Webhooks

Daripada polling, daftarkan URL HTTPS dan Tempik akan mengirim POST setiap ada email masuk. Payload ditandatangani HMAC-SHA256 memakai secret yang diberikan saat webhook dibuat. Maksimal 5 webhook per API key. Pengiriman dilakukan sekali tanpa retry, jadi balas cepat dengan status 2xx dan gunakan polling sebagai cadangan.

Create webhook

POST /api/webhooks API KEY
Body fieldWajibKeterangan
urlYaURL https://. http://localhost diizinkan untuk pengembangan.
eventsTidakArray. Default ["email.received"]. Tersedia: email.received, webhook.test.
filter_addressTidakKirim hanya untuk email ke alamat ini. Kosongkan untuk semua mailbox.
is_activeTidakDefault true.
curl -X POST https://YOUR_DOMAIN/api/webhooks \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://bot.example.com/tempik","filter_address":"myinbox@example.com"}'
Response 201 (secret hanya ditampilkan sekali)
{
  "success": true,
  "data": {
    "id": "wh_3f9a1c2b7d10",
    "url": "https://bot.example.com/tempik",
    "events": ["email.received"],
    "filter_address": "myinbox@example.com",
    "is_active": true,
    "created_at": "2026-10-15 02:10:00",
    "stats": { "delivery_count": 0, "failure_count": 0, "last_status": null, "last_delivered_at": null },
    "secret": "whsec_6f1c0e9a4b2d8e7f5a3c1b9d7e5f3a1c0b8d6e4f2a1c3e5b"
  },
  "ts": "..."
}

List, update, test, delete

GET /api/webhooks API KEY
PATCH /api/webhooks/:id API KEY
POST /api/webhooks/:id/test API KEY
DELETE /api/webhooks/:id API KEY
# Nonaktifkan sementara
curl -X PATCH https://YOUR_DOMAIN/api/webhooks/wh_3f9a1c2b7d10 \
  -H "x-api-key: $API_KEY" -H "Content-Type: application/json" \
  -d '{"is_active":false}'

# Kirim event uji (webhook.test) dan lihat status balasan server kamu
curl -X POST https://YOUR_DOMAIN/api/webhooks/wh_3f9a1c2b7d10/test -H "x-api-key: $API_KEY"
# → { "success": true, "data": { "delivered": true, "status": 200, "event": "webhook.test" }, "ts": "..." }

Payload yang kamu terima

Tempik mengirim POST ke URL-mu dengan header berikut:

HeaderIsi
X-Webhook-Signaturesha256=<hex HMAC-SHA256 dari raw body, kunci = secret>
X-Tempik-Eventemail.received atau webhook.test
X-Tempik-Webhook-IdID webhook
{
  "event": "email.received",
  "webhook_id": "wh_3f9a1c2b7d10",
  "ts": "2026-10-15T02:11:03.120Z",
  "data": {
    "id": "msg_1782461413912_0956a83c",
    "inbox_address": "myinbox@example.com",
    "from_address": "noreply@openai.com",
    "subject": "Your ChatGPT code is 482913",
    "body": "Enter 482913 to continue",
    "received_at": "2026-10-15 02:11:03",
    "expires_at": "2026-10-16T02:11:03.000Z",
    "otp_code": "482913"
  }
}

Verifikasi tanda tangan dengan raw body persis seperti diterima, bukan hasil JSON.stringify ulang:

import express from 'express';
import crypto from 'node:crypto';

const app = express();
const SECRET = process.env.TEMPIK_WEBHOOK_SECRET;

app.post('/tempik', express.raw({ type: '*/*' }), (req, res) => {
  const expected = 'sha256=' + crypto.createHmac('sha256', SECRET).update(req.body).digest('hex');
  const given = req.get('X-Webhook-Signature') || '';
  if (given.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected))) {
    return res.status(401).end();
  }
  const { event, data } = JSON.parse(req.body.toString('utf8'));
  if (event === 'email.received') console.log('OTP:', data.otp_code, 'for', data.inbox_address);
  res.status(200).end();
});

app.listen(3000);
// Cloudflare Worker / Deno / Bun style
async function verify(request, secret) {
  const raw = await request.text();
  const key = await crypto.subtle.importKey('raw', new TextEncoder().encode(secret),
    { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
  const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(raw));
  const hex = [...new Uint8Array(sig)].map((b) => b.toString(16).padStart(2, '0')).join('');
  const ok = request.headers.get('X-Webhook-Signature') === `sha256=${hex}`;
  return ok ? JSON.parse(raw) : null;
}
import hmac, hashlib, os
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = os.environ['TEMPIK_WEBHOOK_SECRET'].encode()

@app.post('/tempik')
def tempik():
    expected = 'sha256=' + hmac.new(SECRET, request.get_data(), hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, request.headers.get('X-Webhook-Signature', '')):
        abort(401)
    payload = request.get_json(force=True)
    if payload['event'] == 'email.received':
        print('OTP:', payload['data']['otp_code'])
    return '', 200

Domains

GET /api/domains API KEY

Daftar domain yang bisa dipakai. Domain baru terdeteksi otomatis begitu email pertama masuk ke domain itu, jadi daftar ini bisa bertambah tanpa deploy ulang. Selalu ambil dari endpoint ini daripada hardcode.

curl https://YOUR_DOMAIN/api/domains -H "x-api-key: $API_KEY"
Response 200
{ "success": true, "data": { "domains": ["example.com", "another.my.id"] }, "ts": "..." }

Health

GET /health NO AUTH

Cek status layanan untuk monitoring. Tidak memerlukan key.

curl https://YOUR_DOMAIN/health
{ "success": true, "data": { "status": "ok", "app": "Tempik", "version": "2.1.0" }, "ts": "..." }

Admin: API Keys

Cara termudah: buka dashboard admin, masukkan admin token, lalu buat key dari sana. Endpoint di bawah ini adalah yang dipakai dashboard tersebut. Dilindungi oleh secret ADMIN_TOKEN lewat header x-admin-token. Set dulu dengan:

npx wrangler secret put ADMIN_TOKEN
Jangan bagikan admin token. Siapa pun yang memegangnya bisa membuat dan menghapus API key. Simpan hanya di secret Cloudflare, bukan di wrangler.toml.

Create key

POST /api/keys ADMIN
curl -X POST https://YOUR_DOMAIN/api/keys \
  -H "x-admin-token: $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"bot-telegram"}'
Response 201 (key hanya ditampilkan sekali)
{
  "success": true,
  "data": { "id": "key_35bc1c8ec6c5", "name": "bot-telegram", "key": "tmp_98cd12b2bfe772cd3d39b3b001aa7cbc2ffb356802d71830" },
  "ts": "..."
}

List keys

GET /api/keys ADMIN
curl https://YOUR_DOMAIN/api/keys -H "x-admin-token: $ADMIN_TOKEN"
{ "success": true, "data": { "keys": [ { "id": "key_35bc1c8ec6c5", "name": "bot-telegram", "created_at": "2026-10-15 01:49:36", "last_used_at": null } ] }, "ts": "..." }

Revoke key

DELETE /api/keys/:id ADMIN
curl -X DELETE https://YOUR_DOMAIN/api/keys/key_35bc1c8ec6c5 -H "x-admin-token: $ADMIN_TOKEN"

Full Flow Example

Buat mailbox, tunggu email verifikasi masuk, ambil OTP-nya, lalu bersihkan mailbox.

BASE="https://YOUR_DOMAIN"
API_KEY="tmp_xxxxxxxxxxxxxxxxxxxxxxxx"

# 1. Buat mailbox acak
EMAIL=$(curl -s -X POST $BASE/api/mailbox -H "x-api-key: $API_KEY" | jq -r '.data.email')
echo "Mailbox: $EMAIL"
ENC=$(printf %s "$EMAIL" | jq -sRr @uri)

# 2. Daftar di layanan lain memakai $EMAIL, lalu poll OTP
for i in $(seq 1 40); do
  OTP=$(curl -s "$BASE/api/otp/$ENC" -H "x-api-key: $API_KEY" | jq -r '.data.otp // empty')
  [ -n "$OTP" ] && break
  sleep 3
done
echo "OTP: $OTP"

# 3. Bersihkan mailbox
curl -s -X DELETE "$BASE/api/mailbox/$ENC" -H "x-api-key: $API_KEY"
const BASE = 'https://YOUR_DOMAIN';
const API_KEY = process.env.TEMPIK_API_KEY;
const H = { 'x-api-key': API_KEY, 'Content-Type': 'application/json' };

async function api(method, path, body) {
  const res = await fetch(BASE + path, { method, headers: H, body: body && JSON.stringify(body) });
  const json = await res.json();
  if (!json.success) throw new Error(json.error);
  return json.data;
}

const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

async function main() {
  // 1. Buat mailbox acak
  const { email } = await api('POST', '/api/mailbox');
  console.log('Mailbox:', email);

  // 2. Daftar di layanan lain memakai `email`, lalu poll OTP
  const enc = encodeURIComponent(email);
  let otp = null;
  for (let i = 0; i < 40 && !otp; i++) {
    const data = await api('GET', `/api/otp/${enc}`);
    if (data.found) otp = data.otp;
    else await sleep(3000);
  }
  console.log('OTP:', otp);

  // 3. Bersihkan mailbox
  await api('DELETE', `/api/mailbox/${enc}`);
}

main().catch(console.error);
import os, time, requests
from urllib.parse import quote

BASE = 'https://YOUR_DOMAIN'
H = {'x-api-key': os.environ['TEMPIK_API_KEY']}

def api(method, path, **kw):
    r = requests.request(method, BASE + path, headers=H, **kw)
    j = r.json()
    if not j['success']:
        raise RuntimeError(j['error'])
    return j['data']

# 1. Buat mailbox acak
email = api('POST', '/api/mailbox')['email']
print('Mailbox:', email)
enc = quote(email)

# 2. Daftar di layanan lain memakai `email`, lalu poll OTP
otp = None
for _ in range(40):
    data = api('GET', f'/api/otp/{enc}')
    if data['found']:
        otp = data['otp']
        break
    time.sleep(3)
print('OTP:', otp)

# 3. Bersihkan mailbox
api('DELETE', f'/api/mailbox/{enc}')

Try It Live

Uji langsung dari browser. API key disimpan di localStorage browser ini saja dan tidak dikirim ke mana pun selain server ini.

Create mailbox
Mailbox actions
// Response akan tampil di sini