Tutorial n8n #11: Membuat Workflow Content Approval dari Brief hingga Publikasi
Content automation yang baik bukan hanya menghasilkan teks lebih cepat. Menurut saya, bagian paling penting justru ada di sistem editorialnya: brief harus rapi, status jelas, reviewer punya ownership, perubahan tercatat, dan keputusan publikasi tidak dilepas begitu saja ke AI.
Di tutorial n8n #11 ini, kita akan membangun workflow content approval dari awal: menerima content brief melalui n8n Form Trigger, menghasilkan draft terstruktur dengan bantuan AI, menyimpan draft dan status review ke Notion atau Supabase, mengirim notifikasi ke editor, lalu memisahkan jalur approved dan revision. Workflow ini sengaja tidak mempublikasikan artikel secara otomatis dari output AI. Publikasi hanya berjalan setelah ada approval manusia.
Tutorial ini cocok untuk pemilik bisnis, tim marketing kecil, atau software engineer yang ingin membuat sistem content operations yang lebih rapi tanpa langsung membangun aplikasi internal dari nol.
Masalah content operations manual
Sebelum masuk ke workflow, kita perlu memahami masalah yang sering muncul ketika produksi konten mulai meningkat:
- Brief tercecer di chat, email, spreadsheet, atau dokumen yang berbeda.
- Draft tidak punya status yang konsisten, misalnya mana yang baru dibuat, sedang direview, butuh revisi, sudah approved, atau sudah published.
- Revisi sulit dilacak karena feedback ada di banyak tempat.
- Approval tidak terdokumentasi, sehingga tim sulit tahu siapa yang menyetujui artikel dan kapan keputusan dibuat.
- AI dipakai untuk mempercepat drafting, tetapi belum ada guardrail agar outputnya tetap diperiksa manusia.
Workflow yang akan kita buat menyelesaikan masalah tersebut dengan pendekatan sederhana: satu form untuk brief, satu tempat penyimpanan status, satu alur approval, dan satu jalur publish yang hanya aktif setelah approval.
Arsitektur workflow
Alur utama yang akan kita bangun:
n8n Form Trigger -> Set/Edit Fields -> OpenAI -> Notion atau Supabase Create -> Telegram/Email notification -> Wait/Webhook approval -> IF status -> HTTP Request publish atau update status revision
Fungsi setiap node:
| Node | Fungsi |
|---|---|
| n8n Form Trigger | Menerima brief dari user melalui form yang dibuat oleh n8n. |
| Set/Edit Fields | Membersihkan dan menormalkan input, misalnya trim judul, format keyword, dan status awal. |
| OpenAI | Membantu menghasilkan draft terstruktur dari brief. |
| Notion atau Supabase | Menyimpan draft, status editorial, reviewer, deadline, dan metadata. |
| Telegram atau Email | Mengirim ringkasan draft dan link review ke reviewer. |
| Wait atau Webhook | Menunggu keputusan manusia, misalnya approve atau request revision. |
| IF atau Switch | Memisahkan jalur berdasarkan status approval. |
| HTTP Request | Mengirim artikel ke CMS hanya jika status sudah approved. |
| Error Workflow atau dead-letter table | Menangani kegagalan publish, timeout, atau data bermasalah. |
Saat membangun dan mengetes workflow, gunakan Test URL dari Form Trigger agar data masuk terlihat di editor n8n. Setelah workflow siap, simpan, aktifkan, lalu gunakan Production URL. Untuk production, jangan berasumsi data submission akan selalu terlihat di editor seperti saat testing.
Prasyarat
Sebelum mengikuti tutorial ini, siapkan:
- Instance n8n yang sudah berjalan.
- Credential OpenAI di n8n Credentials.
- Salah satu penyimpanan editorial: Notion database atau Supabase table.
- Channel notifikasi, misalnya Telegram atau email.
- Endpoint CMS untuk publish, jika sudah tersedia. Di artikel ini kita gunakan placeholder
[CMS_API_URL]dan[REDACTED].
Penting: jangan pernah menaruh API key, password, JWT, atau token nyata di Set node, form, workflow JSON, artikel, atau pesan notifikasi. Simpan credential di n8n Credentials.
1. Buat Form Brief di n8n Form Trigger
Tambahkan node n8n Form Trigger sebagai trigger utama. Buat form dengan field berikut:
| Field | Tipe | Wajib | Catatan |
|---|---|---|---|
| working_title | Text | Ya | Judul kerja artikel. |
| audience | Text | Ya | Target pembaca, misalnya pemilik bisnis kecil atau tim marketing. |
| business_goal | Textarea | Ya | Tujuan bisnis dari artikel. |
| primary_keyword | Text | Ya | Keyword utama. |
| supporting_keywords | Textarea | Tidak | Satu keyword per baris atau dipisah koma. |
| content_angle | Textarea | Ya | Sudut pandang konten. |
| reference_urls | Textarea | Tidak | Daftar URL referensi, satu URL per baris. |
| CTA | Text | Tidak | Ajakan tindakan di akhir artikel. |
| tone | Dropdown | Tidak | Contoh: praktis, profesional, edukatif, santai. |
| deadline | Date | Ya | Deadline review atau publikasi. |
| reviewer | Text atau Email | Ya | Identitas reviewer. |
Gunakan label yang jelas agar user tidak memasukkan informasi sensitif. Tambahkan helper text seperti: Jangan masukkan API key, password, token, credential, data pelanggan sensitif, atau informasi rahasia ke form ini.
2. Validasi input sebelum masuk ke AI
Jangan langsung mengirim semua submission ke AI. Minimal validasi berikut perlu diterapkan:
working_titlewajib ada dan tidak hanya berisi spasi.business_goalwajib ada karena AI butuh konteks tujuan.reference_urlsharus valid jika diisi. Saya menyarankan hanya menerima URLhttps://.deadlineharus masuk akal, misalnya tidak berada di masa lalu dan tidak terlalu dekat untuk proses review.- Form tidak boleh menerima secret, API key, password, token, private key, atau credential lain.
Contoh expression aman di Set/Edit Fields:
clean_working_title:{{ $json.working_title.trim() }}clean_primary_keyword:{{ $json.primary_keyword.toLowerCase().trim() }}initial_status:draftcontent_slug:{{ $json.working_title.toLowerCase().replace(/[^a-z0-9]+/g, '-') }}
Untuk validasi URL, Anda bisa menambahkan Code node atau IF node setelah Set/Edit Fields. Prinsipnya sederhana: pecah reference_urls per baris, buang baris kosong, lalu pastikan setiap URL diawali https:// dan dapat diparse sebagai URL. Jika gagal, hentikan workflow dengan status invalid_brief atau kirim notifikasi ke requester.
Untuk deadline, gunakan IF node. Jika deadline sudah lewat, arahkan ke jalur error ringan dan minta user mengirim ulang brief.
3. Generate draft dengan OpenAI node
Tambahkan OpenAI node setelah data bersih. Peran AI di sini adalah membantu membuat draft awal, bukan mengambil keputusan publikasi.
Output yang kita minta dari AI:
- Outline artikel.
- Search intent.
- Rekomendasi H1 dan H2.
- Pembuka artikel.
- Isi artikel terstruktur.
- Checklist praktis.
- Meta description.
- Internal-link suggestions.
- CTA.
- Daftar klaim yang perlu diverifikasi.
Contoh instruksi prompt yang aman:
Anda membantu membuat draft artikel berdasarkan content brief. Gunakan hanya informasi dari brief dan referensi yang diberikan. Jangan menyalin sumber. Jangan mengarang fakta. Jangan membuat klaim ranking SEO, angka performa, harga, SLA, kapasitas, atau fitur vendor yang tidak terverifikasi.
Jika ada informasi yang belum bisa diverifikasi, tandai di bagian claims_to_verify. Output harus terstruktur dengan bagian: search_intent, outline, h1, h2_sections, opening, body, practical_checklist, meta_description, internal_link_suggestions, CTA, claims_to_verify.
AI hanya membuat draft. Editor manusia tetap bertanggung jawab atas fakta, sumber, kualitas, dan keputusan publikasi.
Masukkan data brief ke prompt menggunakan expression, misalnya:
Working title: {{$json.clean_working_title}}
Audience: {{$json.audience}}
Business goal: {{$json.business_goal}}
Primary keyword: {{$json.clean_primary_keyword}}
Supporting keywords: {{$json.supporting_keywords}}
Content angle: {{$json.content_angle}}
Reference URLs: {{$json.reference_urls}}
CTA: {{$json.CTA}}
Tone: {{$json.tone}}
Deadline: {{$json.deadline}}
Reviewer: {{$json.reviewer}}
Saya biasanya meminta AI menghasilkan output JSON terstruktur agar mudah disimpan ke database. Namun, tetap siapkan fallback jika output tidak valid, misalnya arahkan ke status revision dengan catatan bahwa draft perlu diperiksa manual.
4. Simpan draft dan status editorial
Setelah draft dihasilkan, simpan ke Notion atau Supabase dengan status awal draft. Status yang digunakan dalam workflow ini:
| Status | Makna |
|---|---|
| draft | Draft baru dibuat oleh AI dan belum dikirim untuk review. |
| review | Draft sudah dikirim ke reviewer. |
| revision | Reviewer meminta revisi. |
| approved | Reviewer menyetujui draft untuk dipublikasikan. |
| published | Artikel sudah berhasil dikirim ke CMS. |
Opsi A: struktur tabel Supabase
Contoh struktur minimal content_pipeline:
create table content_pipeline (
id uuid primary key default gen_random_uuid(),
working_title text not null,
audience text,
business_goal text not null,
primary_keyword text,
draft_content text,
status text not null check (status in ('draft', 'review', 'revision', 'approved', 'published')),
reviewer text,
feedback text,
source_urls text[],
deadline timestamptz,
approved_at timestamptz,
published_at timestamptz,
created_at timestamptz default now(),
updated_at timestamptz default now()
);
Tambahkan tabel audit jika memungkinkan:
create table content_pipeline_audit (
id uuid primary key default gen_random_uuid(),
content_id uuid not null,
previous_status text,
new_status text not null,
actor text,
action text,
feedback text,
created_at timestamptz default now()
);
Di Supabase node, gunakan operasi create row untuk menyimpan data awal. Jangan masukkan service role key ke node biasa. Simpan credential di n8n Credentials dan gunakan prinsip least privilege.
Opsi B: property database Notion
Jika menggunakan Notion, buat database editorial dengan property seperti berikut:
| Property | Tipe | Catatan |
|---|---|---|
| id | Text | ID dari n8n atau database. |
| working_title | Title | Judul kerja. |
| audience | Text | Target pembaca. |
| business_goal | Text | Tujuan bisnis. |
| primary_keyword | Text | Keyword utama. |
| draft_content | Page content atau Text | Draft artikel. |
| status | Select | draft, review, revision, approved, published. |
| reviewer | Person, Email, atau Text | Penanggung jawab review. |
| feedback | Text | Catatan revisi. |
| source_urls | URL atau Text | Daftar sumber. |
| deadline | Date | Deadline review. |
| approved_at | Date | Waktu approval. |
| published_at | Date | Waktu publish. |
| created_at | Date | Waktu dibuat. |
| updated_at | Date | Waktu diperbarui. |
Notion lebih nyaman untuk proses editorial karena reviewer bisa membaca dan memberi catatan di halaman yang familiar. Supabase lebih kuat untuk schema, query, audit trail, dan skala.
5. Kirim notifikasi review ke Telegram atau email
Setelah draft disimpan, update status menjadi review, lalu kirim notifikasi ke reviewer.
Isi notifikasi sebaiknya ringkas:
- Judul kerja.
- Primary keyword.
- Deadline.
- Link review ke Notion page, internal dashboard, atau approval form.
- Ringkasan pendek dari draft.
- Instruksi approve atau request revision.
Jangan mengirim credential, token, API key, full request header, atau data sensitif di notifikasi. Jika menggunakan approval link, gunakan approval request ID yang random, batasi masa berlaku, dan simpan hash token di database. Jangan menampilkan token mentah di log.
Contoh pesan:
Draft siap direview.
Judul: {{$json.working_title}}
Keyword: {{$json.primary_keyword}}
Deadline: {{$json.deadline}}
Reviewer: {{$json.reviewer}}
Link review: [REVIEW_LINK]
Pilih approve jika fakta, sumber, struktur, dan CTA sudah sesuai. Pilih revision jika masih perlu perbaikan.
6. Tambahkan approval human-in-the-loop
Bagian ini adalah guardrail utama. Workflow tidak boleh publish langsung dari output AI.
Ada beberapa opsi implementasi:
- Wait node, workflow berhenti sementara sampai menerima resume URL.
- Webhook approval, reviewer mengirim decision melalui form internal.
- Telegram command, misalnya approve atau revision, tetapi harus memverifikasi identitas reviewer.
- Email approval link, reviewer membuka halaman approval lalu mengisi keputusan.
Data approval minimal yang perlu disimpan:
| Field | Fungsi |
|---|---|
| content_id | Menghubungkan approval ke draft. |
| reviewer_identity | Email, user ID, atau chat ID reviewer. |
| decision | approved atau revision. |
| feedback | Catatan reviewer. |
| approved_at | Waktu approval jika approved. |
| action_at | Waktu action terjadi. |
| audit_id | ID catatan audit trail. |
Jangan hanya mengandalkan tombol approve tanpa identitas. Jika ada artikel finansial, kesehatan, hukum, atau artikel yang mengandung klaim sensitif, wajib gunakan approval manual yang lebih ketat dan pastikan klaim diverifikasi oleh orang yang kompeten.
7. Pisahkan jalur approved dan revision dengan IF atau Switch
Setelah keputusan reviewer masuk, gunakan IF atau Switch node.
Logika sederhana:
- Jika
decisionadalahapproved, update status menjadiapproved, simpanapproved_at, lalu lanjut ke HTTP Request publish. - Jika
decisionadalahrevision, update status menjadirevision, simpan feedback, lalu kirim notifikasi kembali ke editor. - Jika decision tidak valid, jangan publish. Simpan sebagai error atau manual review.
Contoh kondisi IF:
{{$json.decision === 'approved'}}
Untuk jalur revision, notifikasi ke editor bisa berisi:
Draft butuh revisi.
Judul: {{$json.working_title}}
Reviewer: {{$json.reviewer}}
Feedback: {{$json.feedback}}
Link draft: [REVIEW_LINK]
8. Publish ke CMS dengan HTTP Request setelah approved
HTTP Request node hanya boleh dijalankan setelah status approved tersimpan. Gunakan endpoint CMS Anda dengan placeholder berikut:
| Parameter | Contoh aman |
|---|---|
| Method | POST |
| URL | [CMS_API_URL]/posts |
| Authentication | Credential dari n8n Credentials |
| Authorization header | Bearer [REDACTED] |
| Idempotency-Key | {{$json.id}}-publish-v1 |
| Timeout | Sesuaikan dengan batas aman workflow Anda. |
Body request dapat berisi title, slug, content, meta description, status, dan source content ID. Jangan kirim field internal yang tidak diperlukan. Jangan kirim token approval, credential, atau audit log lengkap ke CMS.
Setelah CMS mengembalikan respons sukses, update row menjadi published dan isi published_at. Jika gagal, jangan mengulang publish tanpa kontrol. Masukkan ke jalur retry atau dead-letter.
9. Retry, duplicate prevention, dan manual recovery
Workflow approval perlu dirancang untuk kondisi gagal, bukan hanya kondisi ideal.
Rekomendasi praktis:
| Risiko | Solusi |
|---|---|
| Request CMS timeout | Atur timeout dan retry terbatas. |
| Publish terkirim dua kali | Gunakan idempotency key, misalnya content_id + publish-v1. |
| Reviewer klik approve dua kali | Cek status terakhir sebelum publish. Jika sudah published, abaikan event kedua. |
| Rate limit API | Tambahkan retry dengan jeda dan batasi concurrency. |
| Output AI tidak valid | Simpan sebagai revision atau draft_error, lalu minta editor memperbaiki. |
| CMS error | Simpan ke dead-letter table atau status publish_failed. |
| Notifikasi gagal | Retry notifikasi, tetapi jangan publish karena notifikasi gagal. |
| Data duplikat dari form | Buat duplicate prevention berdasarkan working_title, reviewer, deadline, dan idempotency key. |
Untuk error workflow, buat alur terpisah yang menerima error dari workflow utama, lalu menyimpan ringkasan error ke database dan mengirim notifikasi ke admin. Redact request header, token, dan payload sensitif sebelum masuk log.
Notion vs Supabase untuk content pipeline
| Aspek | Notion | Supabase |
|---|---|---|
| Kemudahan review | Sangat nyaman untuk editor non-teknis. | Perlu dashboard atau UI tambahan. |
| Struktur data | Fleksibel, tetapi bisa longgar. | Schema jelas dan konsisten. |
| Query dan filter | Cukup untuk editorial ringan. | Lebih kuat untuk reporting dan automation. |
| Audit trail | Bisa dibuat, tetapi tidak seketat database. | Lebih cocok untuk audit table dan constraint. |
| Skala workflow | Cocok untuk tim kecil dan proses editorial sederhana. | Lebih cocok untuk pipeline yang makin kompleks. |
| Integrasi teknis | Mudah dibaca manusia. | Lebih enak untuk aplikasi internal dan API. |
Jika tim Anda banyak bekerja di dokumen, mulai dari Notion lebih praktis. Jika sejak awal Anda butuh audit, query, dan integrasi ke dashboard, Supabase lebih tepat.
Expected result
Setelah workflow selesai, hasil yang diharapkan:
- User mengisi content brief melalui form.
- n8n membersihkan dan memvalidasi input.
- AI membuat draft terstruktur beserta daftar klaim yang perlu diverifikasi.
- Draft tersimpan dengan status
draft, lalu berubah menjadireview. - Reviewer menerima notifikasi berisi link review dan ringkasan.
- Reviewer memilih approved atau revision.
- Jika revision, editor menerima feedback dan artikel tidak dipublish.
- Jika approved, workflow memanggil CMS API melalui HTTP Request.
- Setelah sukses, status berubah menjadi
publisheddanpublished_atterisi. - Semua perubahan status tercatat di audit trail.
Troubleshooting
| Masalah | Penyebab umum | Solusi |
|---|---|---|
| Form submission tidak terlihat saat production | Production URL berbeda dari Test URL. | Saat debugging gunakan Test URL. Untuk production, cek execution logs dan penyimpanan data. |
| AI menghasilkan output tidak sesuai format | Prompt kurang tegas atau output terlalu panjang. | Minta output JSON terstruktur dan tambahkan fallback manual review. |
| Reviewer tidak menerima notifikasi | Credential Telegram/email salah atau channel ID tidak sesuai. | Cek credential di n8n Credentials dan kirim test message. |
| Approval tidak memicu workflow | Webhook atau Wait URL salah. | Pastikan URL aktif, workflow tersimpan, dan method request sesuai. |
| Artikel terpublish dua kali | Tidak ada idempotency key atau status check. | Cek status sebelum publish dan gunakan idempotency key. |
| CMS menolak request | Payload tidak sesuai atau auth gagal. | Validasi schema body dan credential, jangan hardcode token. |
| Draft berisi klaim tidak akurat | AI mengisi informasi yang belum diverifikasi. | Perkuat prompt guardrail dan wajibkan editor memeriksa claims_to_verify. |
Security checklist
Sebelum menjalankan workflow di production, pastikan:
- AI tidak pernah mempublish langsung ke CMS.
- Credential disimpan di n8n Credentials, bukan Set node, form, workflow JSON, atau artikel.
- Semua endpoint approval dan CMS menggunakan HTTPS.
- Input form divalidasi, terutama URL, deadline, dan field teks bebas.
- Notifikasi tidak berisi API key, password, token, JWT, private key, atau credential lain.
- Log dan error message meredact data sensitif.
- Reviewer identity diverifikasi sebelum approval diterima.
- Status change disimpan di audit trail.
- HTTP Request publish memakai least privilege.
- Artikel finansial, kesehatan, hukum, atau klaim sensitif selalu melewati approval manual.
- Workflow memiliki retry terbatas, timeout, duplicate prevention, dan manual recovery.
Batasan workflow
Workflow ini membantu mengatur proses editorial, tetapi tidak menggantikan editor. AI bisa membantu membuat draft, outline, meta description, dan checklist. Namun, editor tetap bertanggung jawab atas fakta, sumber, kualitas, gaya bahasa, konteks bisnis, legal risk, dan keputusan publikasi.
Workflow ini juga belum mencakup content calendar lengkap, assignment multi-editor, versioning detail per paragraf, plagiarism checking, atau fact-checking otomatis. Semua itu bisa ditambahkan bertahap setelah fondasi status dan approval rapi.
Checklist sebelum publikasi
Gunakan checklist ini sebelum mengaktifkan jalur publish:
- Form brief sudah memiliki field wajib dan helper text keamanan.
- Validasi judul, tujuan, URL referensi, dan deadline sudah aktif.
- Prompt AI sudah melarang fabrikasi fakta, klaim SEO yang tidak terverifikasi, dan penyalinan sumber.
- Draft tersimpan dengan status
draftdan berpindah kereviewsetelah notifikasi dikirim. - Reviewer identity, waktu approval, feedback, dan audit trail tersimpan.
- IF atau Switch node hanya mengizinkan publish jika status
approved. - HTTP Request CMS memakai
[CMS_API_URL], credential di n8n Credentials, dan idempotency key. - Error workflow, retry, timeout, dan dead-letter sudah disiapkan.
- Notifikasi sudah diuji tanpa membocorkan secret.
- Editor memahami bahwa AI hanya membantu draft, bukan pemilik keputusan publikasi.
Pengembangan berikutnya: content calendar
Setelah workflow approval ini stabil, langkah berikutnya adalah mengembangkannya menjadi content calendar. Anda bisa menambahkan field campaign, publish date, channel, owner, topic cluster, internal link target, dan performance review. Dari sana, n8n dapat membantu mengatur antrian konten mingguan, mengingatkan reviewer, dan membuat dashboard status produksi konten.
Bagi saya, inilah titik penting dari content automation: bukan sekadar membuat teks lebih cepat, tetapi membuat sistem yang menjaga kualitas tetap terkendali saat volume konten meningkat.
Referensi
- n8n Form, n8n Docs, https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.formtrigger/. Tanggal publish sumber: tidak terverifikasi dari materi rujukan yang tersedia.
- OpenAI node, n8n Docs, https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-langchain.openai/. Tanggal publish sumber: tidak terverifikasi dari materi rujukan yang tersedia.
- Notion node, n8n Docs, https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.notion/. Tanggal publish sumber: tidak terverifikasi dari materi rujukan yang tersedia.
- Supabase node, n8n Docs, https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.supabase/. Tanggal publish sumber: tidak terverifikasi dari materi rujukan yang tersedia.
- HTTP Request node, n8n Docs, https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/. Tanggal publish sumber: tidak terverifikasi dari materi rujukan yang tersedia.