@yaotoshi/n8n-sdk
SDK TypeScript untuk otomasi n8n yaotoshi — kirim notifikasi WhatsApp,
terjemahkan invoice/packing list China dari Google Drive, cek input
pembelian importir, dan ambil hasilnya lewat view layer (URL capability
tanpa x-api-key). Tipe & klien di-generate dari openapi.json (via
openapi-fetch; satu-satunya dependensi runtime, sangat kecil), dual ESM/CJS.
Endpoint baru di openapi.json otomatis tersedia sebagai
api.POST('/path') — tanpa menulis request helper tangan.
Kontrak v1 (major): POST /webhook/translate-invoice dan
/webhook/cek-importir-pembelian selalu membalas 202 (ViewResult
dengan status: 'processing') — tidak ada lagi respons HTML sinkron.
Hasil diambil via view layer GET /view/<type>/<id> (HTML) / .json
(data) — tanpa x-api-key; id capability-nya sendiri adalah secret-nya.
wait: true di SDK = polling klien ke view .json (tidak ada parameter
wait di wire).
Instalasi
npm install @yaotoshi/n8n-sdk
Penggunaan singkat
import { createN8nClient } from '@yaotoshi/n8n-sdk';
const n8n = createN8nClient({
apiKey: process.env.N8N_WEBHOOK_SECRET, // nilai header x-api-key (webhook saja)
// baseUrl opsional; default https://n8n.yaotoshi.xyz
});
Kirim notifikasi WhatsApp
const res = await n8n.sendWa({ to: 'me', message: 'Halo dari SDK' });
// res: { ok: true, messageId: '...', toJid: '6281...@s.whatsapp.net' }
// dengan media (URL publik yang bisa di-download gateway):
await n8n.sendWa({ to: 'me', message: 'Foto 🌄', mediaUrl: 'https://picsum.photos/300' });
to diisi nama penerima (alias/display_name dari direktori penerima),
bukan nomor/JID — nomor mentah ditolak.
Terjemahkan invoice dari Google Drive (selalu 202)
const res = await n8n.translateInvoice({
link: 'https://drive.google.com/file/d/<id>/view',
// sheetIndex opsional (0-based, default 0) — untuk spreadsheet;
// di luar jangkauan -> error 400
});
// res: ViewResult
// { ok: true, result_id: '<id capability>', view_type: 'translate',
// status: 'processing', view: 'https://n8n.yaotoshi.xyz/view/translate/<id>' }
Hasil diambil lewat view layer — segera (polling manual) atau otomatis
dengan wait: true (polling klien 3 detik, timeout 5 menit, sampai
status: 'done'):
// polling klien: POST 202 -> loop GET view .json sampai done (maks 5 menit)
const done = await n8n.translateInvoice({ link: 'https://drive.google.com/file/d/<id>/view' }, { wait: true });
console.log(done.status, done.verdict, done.markdown); // done, 'TIDAK BERMASALAH', '## ...'
Cek input pembelian importir (selalu 202)
const res = await n8n.checkImportirPembelian({
id: '5', // nota pembelian importir di cahub
link: 'https://docs.google.com/spreadsheets/d/<id>/edit', // invoice/packing list
// sheetIndex opsional (0-based, default 0)
});
// res: ViewResult { ok: true, result_id, view_type: 'cek-importir', status: 'processing', view }
const done = await n8n.checkImportirPembelian({ id: '5', link: '...' }, { wait: true });
Lihat hasil tersimpan (view layer)
URL view dari respons 202 bisa dibuka langsung di browser (HTML, tanpa
x-api-key) atau diambil datanya:
// data row hasil (status processing|done + markdown/verdict/issues/model/dst.)
const row = await n8n.getViewData({ type: 'translate', id: res.result_id });
console.log(row.status, row.verdict, row.issues);
// proses ulang in-place (id & URL tetap; 404/409/429 -> N8nApiError)
const queued = await n8n.resetViewData({ type: 'translate', id: res.result_id });
// queued: { ok: true, status: 'processing', view: <URL sama> }
Biaya: reset memicu ulang pemrosesan (LLM) — rate limit 1x/60 detik per row.
getTranslateResult({ rid }) adalah alias deprecated (jawaban
ViewResult, bukan HTML lama) yang memanggil getViewData. rid numerik
ditolak di sisi klien dengan pesan yang menunjuk method legacy di bawah —
bukan lagi 404 samar dari server:
/** @deprecated pakai n8n.getViewData({ type: 'translate', id: rid }) */
const row = await n8n.getTranslateResult({ rid: '18aJlfjIddkjDvRTrsEolirT' });
// rid numerik -> N8nApiError 400: "...pakai getLegacyTranslateResultHtml..."
Id legacy numerik tidak dilayani view layer — semua id baru adalah
capability random non-numerik (/gen-key). Item lama yang masih ber-id
numerik dibaca lewat endpoint deprecated ber-x-api-key yang membalas
HTML halaman hasil, dibungkus SDK sebagai
getLegacyTranslateResultHtml:
/** @deprecated hanya untuk row ber-id numerik pra-migrasi */
const html = await n8n.getLegacyTranslateResultHtml({ rid: '173' });
Contoh lengkap (dengan error handling)
import { createN8nClient, N8nApiError } from '@yaotoshi/n8n-sdk';
const n8n = createN8nClient({
apiKey: process.env.N8N_WEBHOOK_SECRET, // nilai header x-api-key
});
try {
const res = await n8n.checkImportirPembelian(
{ id: '268', link: 'https://docs.google.com/spreadsheets/d/<id>/edit' },
{ wait: true }, // polling klien sampai done (atau timeout 5 menit -> status terakhir)
);
if (res.status === 'done') {
console.log(res.verdict); // 'BERMASALAH (1 masalah)' | 'TIDAK BERMASALAH' | 'GAGAL'
console.log(res.issues, res.model, res.markdown);
} else {
console.log('masih diproses', res.view); // timeout 5 menit
}
} catch (err) {
if (err instanceof N8nApiError) {
console.error('status', err.status, '| pesan:', err.message);
// status 400 | pesan: Nota pembelian 99999999 tidak ditemukan di cahub
// (pesan sudah dibersihkan dari sufiks internal n8n, siap dipakai mesin)
} else {
throw err;
}
}
Catatan waktu: wait: true memakai polling klien (3 detik, timeout 5
menit — batas lama proses n8n). Cek yang rute reusenya aktif (hasil
terjemahan sudah tersimpan) selesai ~10–30 detik; yang belum pernah
diterjemahkan menjalankan terjemahan penuh ~1–2 menit (memakai workflow
Terjemahkan secara internal). Gunakan fetch dengan timeout longgar
(AbortSignal.timeout(180_000)) bila kamu meneruskan fetch kustom.
Wire selalu 202. wait adalah opsi polling di sisi klien — tidak ada
parameter wait di body request.
Error
Semua kegagalan dilempar sebagai N8nApiError:
import { N8nApiError } from '@yaotoshi/n8n-sdk';
try {
await n8n.sendWa({ to: 'tidak-ada', message: 'x' });
} catch (err) {
if (err instanceof N8nApiError) {
console.log(err.status, err.message); // status 400, message berisi daftar alias valid
}
}
| Skenario | status |
message (contoh) |
|---|---|---|
x-api-key salah / tidak ada |
401 / 403 | x-api-key tidak valid atau tidak diizinkan |
| Alias tak dikenal | 400 | Alias tidak dikenal… + daftar alias valid |
| Alias ambigu (cocok >1 penerima) | 400 | Daftar kandidat |
mediaUrl gagal di-fetch gateway |
400 | Failed to fetch media… |
| Link bukan Google Drive | 400 | Host tidak diizinkan… |
sheetIndex di luar jangkauan |
400 | sheetIndex … di luar jangkauan… |
| Tipe file tidak didukung | 400 | Tipe file tidak didukung… |
| Cek: id/link kosong | 400 | Field id wajib diisi… |
| Cek: nota tidak ditemukan | 400 | Nota pembelian <id> tidak ditemukan di cahub |
| View: id/type tidak dikenal | 404 | not found |
| View: reset saat masih diproses | 409 | processing (body server: {ok:false, error:'processing'}) |
| View: rate limit (reset per-row 60s / 30 req/menit per IP) | 429 | rate limited (body server: {ok:false, error:'rate limited'}) |
getTranslateResult dengan rid numerik |
400 (klien) | rid numerik legacy tidak dilayani view layer - pakai getLegacyTranslateResultHtml… |
| Legacy translate-result: rid tidak ada / key salah | 400 / 403 | pesan error dari body JSON |
| Koneksi/network | — | TypeError: fetch failed (dari fetch) |
Catatan: view layer (getViewData/resetViewData) dipanggil tanpa
x-api-key — id capability adalah secret-nya. apiKey tetap wajib di
createN8nClient untuk jalur webhook.
Kontribusi / sinkron tipe
Tipe di-generate dari openapi.json:
npm run generate:sdk
npm run build
npm test
Aturan menambah endpoint publik: baca .claude/rules/api-sdk-sync.md di repo
n8n-selfhosted — update openapi.json, naikkan version, regenerate, commit
satu paket. Setelah regenerate, panggilan typed untuk endpoint baru tersedia
otomatis (api.POST('/webhook/...')); wrapper ergonomis (sendWa,
translateInvoice) tinggal 3 baris di src/index.ts. CI (publish-n8n-sdk.yml)
menolak bila tipe tidak sinkron atau README tidak menyebut method, dan
menerbitkan npm otomatis bila versi berbeda.
Dokumentasi API (Scalar)
Live: https://n8n.yaotoshi.xyz/docs/ — kontrak dari openapi.json
disajikan server statis kecil (scripts/view-server.mjs di root repo, pm2
n8n-web), di-routing tunnel path /docs* di depan rule n8n biasa. Versi tanpa
server juga tersedia: buka docs.html di browser (kontrak di-embed).
Regenerasi bila openapi.json berubah:
bash scripts/gen-docs.sh
Lisensi
MIT