Berkas Form BPMN untuk Integrasi Pihak Ketiga

Apa yang diterima sistem luar (LOS, core banking, dan sejenisnya) ketika sebuah proses mengirim berkas dari form BPMN — isi placeholder variabel berkas sebelum dan sesudah rilis 2026.9.4, format metadata, dan API lampiran form.

Gambaran Umum

Field File Upload pada form BPMN menghasilkan sebuah berkas lampiran yang disimpan di object storage. Ketika Service Task (API Call atau addon) mengirim berkas itu ke sistem luar, yang dikirim berubah mulai rilis 2026.9.4:

Sebelum 2026.9.4Sesudah 2026.9.4
Isi placeholder ${namaVariabelBerkas} di body integrasiIsi berkas dalam base64URL unduh bertanda tangan, sekali pakai
Yang disimpan di variabel prosesBerkas biner (tipe File)String JSON metadata (attachment_id, file_name, mime_type, size)
Penerima perluMen-decode base64Mengunduh berkas dari URL, segera

Ini adalah breaking change bagi penerima yang selama ini men-decode base64 dari field tersebut. Kode penerima yang tidak diubah akan mencoba men-decode sebuah URL sebagai base64. Baca bagian Membedakan URL dan base64 untuk cara membuat penerima menerima keduanya selama masa transisi.

Isi Placeholder Variabel Berkas di Body Integrasi

Di konfigurasi API Call maupun payload addon, berkas dirujuk lewat placeholder ${namaVariabelBerkas} — namaVariabelBerkas adalah Key dari field File Upload di form. Contoh body:

{
  "nomor_aplikasi": "${nomorAplikasi}",
  "dokumen_ktp": "${ktp}"
}

Saat Service Task berjalan, platform mengganti ${ktp} di posisi yang sama dengan nilai berikut:

Sebelum 2026.9.4 — isi berkas base64:

{
  "nomor_aplikasi": "APP-2026-0001",
  "dokumen_ktp": "JVBERi0xLjQKJcfsj6IK..."
}

Sesudah 2026.9.4 — URL unduh bertanda tangan:

{
  "nomor_aplikasi": "APP-2026-0001",
  "dokumen_ktp": "https://app.contoh.com/api/v1/probis/public/form-attachments/6f1c2a4e-8b7d-4c39-9a51-0e2d3f4a5b6c/download?token=eyJ...abc.def..."
}

Format URL-nya:

GET /api/v1/probis/public/form-attachments/{attachment_id}/download?token=<token>

Host pada URL mengikuti alamat publik gateway instance, sehingga bisa dijangkau dari luar jaringan aplikasi.

Aturan URL unduh

AturanKeterangan
Sekali pakaiToken hanya berlaku untuk satu permintaan unduh. Permintaan kedua dengan URL yang sama ditolak 401. Tidak bisa di-retry dengan URL yang sama.
Masa berlaku 15 menitDihitung sejak URL diterbitkan, yaitu saat Service Task berjalan. Bisa diubah administrator lewat FORM_ATTACHMENT_URL_TTL_SECONDS (batas atas 1 jam).
Tanpa autentikasi lainTidak perlu header Authorization; token di query string adalah kredensialnya. Jangan menaruh header autentikasi platform pada permintaan ini.
Terikat ke satu berkasToken yang diterbitkan untuk satu berkas tidak bisa membuka berkas lain.
Satu URL per kemunculan placeholderKalau ${ktp} muncul dua kali di body, penerima mendapat dua URL berbeda, masing-masing sekali pakai.

Konsekuensi untuk penerima: unduh berkas segera saat pesan diterima, di dalam alur pemrosesan yang sama. URL tidak boleh disimpan untuk diunduh nanti (antrean, batch malam hari), dan tidak boleh diakses dua kali — termasuk oleh pemindai tautan atau health check yang ikut membuka URL.

Token dianggap terpakai begitu permintaan diterima dan divalidasi, sebelum isi berkas dikirim. Unduhan yang terputus di tengah jalan, atau kegagalan storage pada saat itu, tetap menghabiskan token. Penerima yang gagal mengunduh tidak bisa mencoba lagi dengan URL yang sama; pemicunya harus dijalankan ulang dari sisi AlurKerja supaya URL baru diterbitkan.

Response unduh yang berhasil adalah isi berkas dengan Content-Type sesuai mime_type berkas dan header Content-Disposition: attachment yang membawa nama file asli.

StatusArti
200Berkas dikirim
400attachment_id di path bukan UUID yang valid
401Token tidak ada, salah tanda tangan, kedaluwarsa, sudah dipakai, atau bukan untuk berkas ini. Kelima kasus ini sengaja tidak dibedakan di response
404Berkas tidak ditemukan, termasuk bila objeknya sudah hilang dari storage
501Instance belum dikonfigurasi untuk menerbitkan URL unduh

Membedakan URL dan base64

Berkas yang di-upload sebelum rilis 2026.9.4 tetap tersimpan sebagai berkas biner dan tetap dikirim sebagai base64. Berkas baru dikirim sebagai URL. Selama proses lama masih berjalan, field yang sama bisa berisi base64 atau URL, tergantung kapan berkasnya di-upload.

Base64 tidak pernah mengandung karakter :, sehingga awalan http:// atau https:// membedakannya dengan pasti:

import re
import base64
import requests

def ambil_berkas(nilai: str) -> bytes:
    # URL bertanda tangan (sesudah 2026.9.4)
    if re.match(r"^https?://", nilai):
        resp = requests.get(nilai, timeout=30)
        resp.raise_for_status()
        return resp.content
    # base64 (berkas lama)
    return base64.b64decode(nilai)

Alternatifnya, cek path /api/v1/probis/public/form-attachments/ pada nilai tersebut. Jangan mengandalkan panjang string atau awalan base64 tertentu.

Kalau penerima Anda menyimpan nilai field ini ke database sendiri lalu memprosesnya belakangan, ubah alurnya: unduh dulu saat pesan masuk, simpan berkasnya, bukan URL-nya.

Kalau URL gagal diterbitkan

Penerbitan URL adalah bagian dari Service Task. Kalau gagal — layanan lampiran tidak terjangkau, menolak permintaan, atau tenant/instance proses tidak bisa ditentukan — Service Task gagal dan membuat incident. Request ke sistem luar tidak pernah dikirim, jadi penerima tidak akan pernah menerima metadata JSON mentah sebagai pengganti berkas. Setelah penyebabnya dibereskan, jalankan ulang Service Task dari incident.

Saklar "alihkan kegagalan ke Error Boundary Event" pada Service Task hanya menerjemahkan penolakan dari integrasi (respons HTTP error, penolakan addon). Kegagalan menerbitkan URL berkas bukan penolakan dari integrasi, sehingga tetap berakhir sebagai incident.

Hanya berlaku di body

Penggantian placeholder berkas menjadi URL (atau base64) hanya dilakukan pada:

  • body konfigurasi API Call dengan metode POST, PUT, atau PATCH; dan
  • payload (parameters) addon.

Placeholder berkas di URL, query parameter, atau header tidak diganti menjadi URL unduh — yang tergantikan di sana adalah nilai variabelnya apa adanya, yaitu string JSON metadata. Jangan menaruh ${namaVariabelBerkas} di tempat-tempat itu.

Objek variables yang ikut dikirim ke addon juga berisi string JSON metadata, bukan URL. Addon yang butuh berkasnya harus mengambilnya dari nilai di parameters.

Isi Variabel Proses: Metadata JSON

Sesudah 2026.9.4, variabel proses untuk field File Upload bertipe String dan berisi JSON berikut:

{
  "attachment_id": "6f1c2a4e-8b7d-4c39-9a51-0e2d3f4a5b6c",
  "file_name": "ktp-budi.pdf",
  "mime_type": "application/pdf",
  "size": 248310
}
FieldKeterangan
attachment_idUUID lampiran. Dipakai di path URL unduh dan endpoint lampiran form
file_nameNama file asli
mime_typeTipe isi hasil deteksi server
sizeUkuran dalam byte

Respons upload dan halaman embed juga membawa type ("file_attachment") dan uploaded_at; kedua field itu tidak dipakai untuk mengenali metadata.

Key id tidak seragam antar pengirim. Form yang dikirim dari aplikasi App (app-new-react) mengisi UUID lampiran pada key id, sedangkan jalur lain (embed start-process, respons upload) memakai attachment_id. Pembaca variabel secara langsung — misalnya lewat engine-rest — wajib menerima keduanya: ambil attachment_id, dan kalau tidak ada, ambil id. Platform sendiri melakukan hal ini; sebuah nilai dikenali sebagai metadata lampiran bila berupa objek JSON dengan file_name dan UUID yang valid pada salah satu key tersebut.

def id_lampiran(meta: dict) -> str:
    return meta.get("attachment_id") or meta["id"]

Berkas biner tidak lagi masuk ke database engine; hanya string metadata ini yang tersimpan di variabel.

API Lampiran Form

Lampiran form mengikuti daur draft → commit:

  1. Saat pengguna memilih berkas di form, berkas langsung di-upload dan tersimpan sebagai draft. Respons upload berisi metadata di atas.
  2. Saat form disubmit, proses berjalan dan variabel berisi metadata tersebut. Engine mengikat (commit) lampiran ke process instance dan task-nya secara otomatis begitu variabel tertulis. Sejak itu lampiran berstatus committed.
  3. Draft yang tidak pernah di-commit (mis. berkas diganti sebelum submit, atau proses gagal start) dibersihkan otomatis oleh server; batas usia draft bawaannya 24 jam.

Aplikasi resmi (App, Studio, embed) sudah menjalankan daur ini sendiri. Endpoint di bawah relevan bagi Anda kalau membangun klien sendiri.

Versi login (JWT)

Header: Authorization: Bearer <token> dan x-active-tenant: <slug-workspace> (slug, bukan id numerik).

EndpointFungsi
POST /api/v1/probis/form-attachments/uploadUpload berkas sebagai draft. multipart/form-data: file, process_key, field_name (wajib); process_instance_id, task_id (opsional, kosong untuk form start). Respons 201 berisi metadata. Batas 20 permintaan per menit per pengguna
POST /api/v1/probis/form-attachments/finalizeMeng-commit draft. Body JSON: attachment_ids (wajib), process_instance_id, task_id. 200 bila semua id ter-commit, 409 bila ada yang tidak ter-commit (id salah, tenant lain, atau sudah dibersihkan)
GET /api/v1/probis/form-attachments/allowed-typesDaftar ekstensi yang diizinkan dan max_size_mb
DELETE /api/v1/probis/form-attachments/{id}Menghapus draft milik sendiri. 204; 404 bila bukan miliknya atau tidak ada; 409 bila sudah committed
GET /api/v1/probis/form-attachments/{id}/downloadUnduh dengan sesi login. Hanya pengunggah atau peserta diskusi process instance-nya; selain itu 404

Ekstensi yang diterima: jpg, jpeg, png, pdf, docx, xlsx (tipe isi diperiksa, bukan hanya ekstensi). Ukuran maksimum per berkas bawaan 10 MB, diatur administrator lewat UPLOAD_MAX_SIZE.

Versi public (embed start-process)

Untuk form start-process yang di-embed — pengunjung tanpa login. Identitas pengunjung adalah token sesi form pada header X-Probis-Form-Token yang dibawa halaman embed; tenant dan proses diambil dari token itu, bukan dari request.

EndpointFungsi
POST /api/v1/probis/public/form-attachments/uploadUpload draft. multipart/form-data: file, field_name. Batas 30 permintaan per menit per IP; maksimum 10 berkas dan total ukuran dua kali batas per berkas per sesi form
DELETE /api/v1/probis/public/form-attachments/{id}Menghapus draft yang diunggah oleh sesi form yang sama
GET /api/v1/probis/public/form-attachments/allowed-typesDaftar ekstensi yang diizinkan
GET /api/v1/probis/public/form-attachments/{id}/download?token=…Unduh dengan URL bertanda tangan (lihat bagian atas)

Jalur public tidak punya endpoint finalize: pengikatan lampiran dilakukan engine sendiri setelah proses berhasil berjalan.

Nilai Variabel Berkas di Tasklist dan Report

Bentuk nilai (value) variabel berkas yang dibaca dari tasklist/report ikut berubah:

Berkas lama (tipe File)Berkas baru (metadata lampiran)
Bentuk valuePath relatif /{processKey}/download/{processInstanceId}/{namaVariabel}Path absolut /api/v1/probis/form-attachments/{attachment_id}/download
Field tambahanfilename, sizefilename, size, mime_type
Cara mengunduhRelatif terhadap base tasklist (/api/v1/tasklist)Dipakai apa adanya dari root gateway; butuh sesi login (JWT + x-active-tenant)

Aturannya satu untuk semua konsumen: value yang diawali /api/v1/ adalah path absolut dari root gateway dan dipakai apa adanya; nilai lain relatif terhadap base tasklist. Untuk berkas lama, awalan /{processKey}/ pada laporan berisi process definition key.

Path pada tabel di atas adalah jalur dengan login, untuk pengguna aplikasi. Sistem luar yang tidak punya sesi login memakai URL bertanda tangan dari bagian Isi Placeholder.

Bukan Jalur Ini

Tidak semua "upload file" di AlurKerja memakai mekanisme di halaman ini. Kalau kebutuhan Anda salah satu di bawah, jalur yang berlaku berbeda:

KebutuhanJalurKeterangan
Upload umum ke object storage (MinIO/S3)POST /api/v1/integration/uploadTanpa metadata dan tanpa daur draft → commit. Lihat API Upload File
Lampiran pesan diskusiEndpoint diskusi (/api/v1/probis/discussions/…)Lampiran diskusi punya id dan aturan akses sendiri, tidak lewat form-attachments
Kolom bertipe file pada record master dataUpload umum di atas; nilai record berupa path urlBukan lampiran form. Lihat Master Data