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.
https://YOUR_DOMAIN
x-api-key. Key disimpan sebagai hash SHA-256.email.received ke server kamu, ditandatangani HMAC.Getting Started
- Minta API key ke admin, atau buat sendiri lewat admin API jika kamu pemilik server. Key berawalan
tmp_. - Kirim header
x-api-keypada setiap request ke/api/mailbox,/api/email,/api/otp, dan/api/domains. - Buat mailbox, kirim email ke alamat itu, lalu poll
/api/otp/:addressatau/api/mailbox/:addresssampai 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
| Status | Error | Penyebab |
|---|---|---|
401 | Missing or malformed x-api-key header | Header tidak ada atau tidak berawalan tmp_ |
401 | Invalid API key | Key tidak terdaftar atau sudah dihapus |
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" }
| Status | Arti |
|---|---|
200 | OK |
201 | Resource dibuat (mailbox atau API key) |
400 | Input tidak valid: domain tidak dikenal, username tidak valid, alamat salah format |
401 | API key atau admin token tidak valid |
403 | Admin API nonaktif karena ADMIN_TOKEN belum di-set |
404 | Email 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
Membuat mailbox baru, atau mengklaim alamat yang sudah ada. Tanpa body, alamat dibuat acak dengan gaya nama Indonesia (contoh kopihujan42).
| Body field | Wajib | Keterangan |
|---|---|---|
username | Tidak | Local-part kustom. 1-64 karakter: a-z, 0-9, titik, underscore, strip. |
domain | Tidak | Salah 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']
{
"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
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']
{
"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
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"
{ "success": true, "data": { "address": "myinbox@example.com", "cleared": 3 }, "ts": "..." }
Get Email
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"
{
"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
curl -X DELETE https://YOUR_DOMAIN/api/email/msg_1782461413912_0956a83c \
-H "x-api-key: $API_KEY"
{ "success": true, "data": { "deleted": true, "id": "msg_1782461413912_0956a83c" }, "ts": "..." }
Get OTP
Memindai email di mailbox, terbaru lebih dulu, dan mengembalikan kode verifikasi pertama yang ditemukan. Prioritas deteksi:
| # | Sumber | Pola |
|---|---|---|
| 1 | Subject | 6 digit berdiri sendiri |
| 2 | Body (HTML dibersihkan) | 6 digit berdiri sendiri |
| 3 | Subject lalu body | 4-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')
{
"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": "..."
}
{ "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
| Body field | Wajib | Keterangan |
|---|---|---|
url | Ya | URL https://. http://localhost diizinkan untuk pengembangan. |
events | Tidak | Array. Default ["email.received"]. Tersedia: email.received, webhook.test. |
filter_address | Tidak | Kirim hanya untuk email ke alamat ini. Kosongkan untuk semua mailbox. |
is_active | Tidak | Default 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"}'
{
"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
# 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:
| Header | Isi |
|---|---|
X-Webhook-Signature | sha256=<hex HMAC-SHA256 dari raw body, kunci = secret> |
X-Tempik-Event | email.received atau webhook.test |
X-Tempik-Webhook-Id | ID 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
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"
{ "success": true, "data": { "domains": ["example.com", "another.my.id"] }, "ts": "..." }
Health
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
wrangler.toml.
Create key
curl -X POST https://YOUR_DOMAIN/api/keys \
-H "x-admin-token: $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"bot-telegram"}'
{
"success": true,
"data": { "id": "key_35bc1c8ec6c5", "name": "bot-telegram", "key": "tmp_98cd12b2bfe772cd3d39b3b001aa7cbc2ffb356802d71830" },
"ts": "..."
}
List keys
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
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.
// Response akan tampil di sini