API Upload File

Mengunggah file ke object storage (MinIO/S3) lewat endpoint upload umum, dan mengambilnya kembali lewat URL publik.

Gambaran Umum

AlurKerja menyediakan satu endpoint upload umum yang menyimpan file ke object storage instance (MinIO, atau S3/COS pada instalasi cloud) dan mengembalikan path objek yang bisa disimpan sendiri oleh pemanggil — misalnya ke kolom master data, ke field custom, atau ke sistem luar.

Endpointnya sepasang:

KegunaanEndpointAutentikasi
Mengunggah filePOST /api/v1/integration/uploadBearer token + role Admin SOP atau Owner
Mengambil file kembaliGET /api/v1/integration/public/{path}Tidak ada — publik

Seluruh file masuk ke satu bucket yang dikonfigurasi di server (MINIO_BUCKET_NAME). Pemanggil tidak bisa memilih bucket; yang bisa diatur hanya folder di dalam bucket tersebut.

Kapan Memakai Endpoint Ini

Endpoint ini adalah jalur upload umum. Untuk tiga kebutuhan berikut sudah ada endpoint khusus yang menyimpan metadata, memvalidasi tipe file, dan mengikat file ke objek bisnisnya — jangan memakai endpoint umum untuk ketiganya:

KebutuhanPakaiAlasan
Lampiran pada field File Upload di form BPMNPOST /api/v1/probis/form-attachments/uploadMemvalidasi ekstensi (jpg, jpeg, png, pdf, docx, xlsx) dan sniffing MIME, menyimpan baris metadata, serta mengikat lampiran ke process instance lewat mekanisme draft → commit. Lihat File Upload dan Berkas Form BPMN untuk Integrasi Pihak Ketiga.
Media Company Profile / MicrositePOST /api/v1/compro/media/uploadFile tercatat sebagai media dan terikat ke entitasnya, sehingga bisa dikelola ulang dari Studio.
Paket addon (.zip)POST /api/v1/integration/addons/uploadAda proses ekstraksi dan registrasi addon setelah upload.

Endpoint umum ini tidak memvalidasi ekstensi maupun tipe isi file, dan tidak menyimpan baris metadata apa pun. Yang tersimpan hanyalah objeknya di bucket; hubungan file dengan data bisnis sepenuhnya menjadi tanggung jawab pemanggil.

Di dalam produk, endpoint ini dipakai oleh kolom bertipe file pada Master Data: nilai yang disimpan ke record adalah string url hasil upload.

Mengunggah File

POST /api/v1/integration/upload
Content-Type: multipart/form-data
Authorization: Bearer <token>
x-active-tenant: <slug-workspace>
HeaderWajibKeterangan
AuthorizationYaBearer <token> milik pengguna ber-role Admin SOP atau Owner di workspace tersebut
x-active-tenantYaSlug workspace, bukan id numerik. Mengisi angka akan ditolak sebagai bukan anggota workspace
Field form-dataWajibKeterangan
fileYaBerkas yang diunggah. Namanya harus persis file
folderTidakPrefix folder di dalam bucket, mis. master-data/pelanggan. Boleh bertingkat. Kalau kosong, objek ditaruh di root bucket

Contoh:

curl -X POST "https://app.contoh.com/api/v1/integration/upload" \
  -H "Authorization: Bearer $TOKEN" \
  -H "x-active-tenant: workspace-saya" \
  -F "file=@ktp-budi.pdf" \
  -F "folder=master-data/pelanggan"

Response 200 OK:

{
  "status": "success",
  "message": "File uploaded successfully",
  "data": {
    "file_name": "ktp-budi.pdf",
    "object_name": "master-data/pelanggan/ktp-budi_1758790000.pdf",
    "url": "/tenant-uploads/master-data/pelanggan/ktp-budi_1758790000.pdf",
    "size": 248310
  }
}
Field responseKeterangan
file_nameNama file asli seperti yang dikirim pemanggil
object_nameKey objek di dalam bucket, sudah termasuk folder
url/<nama-bucket>/<object_name> — path relatif, bukan URL lengkap. Ini nilai yang biasanya disimpan ke database pemanggil
sizeUkuran objek dalam byte, dibaca dari hasil tulis ke storage

Aturan penamaan objek

Nama objek dibentuk dari nama file asli dengan sisipan unix timestamp (detik) sebelum ekstensi:

laporan-q3.xlsx   →   laporan-q3_1758790000.xlsx

Beberapa konsekuensi yang perlu diketahui:

  • Nama file asli dipertahankan apa adanya. Spasi, tanda kurung, dan karakter non-ASCII ikut masuk ke key objek — URL unduhannya wajib di-URL-encode saat dipakai di href atau di pemanggil lain.
  • Komponen direktori pada nama file dibuang. File bernama ../../etc/ktp.pdf tersimpan sebagai ktp.pdf; nama file tidak bisa dipakai untuk keluar dari folder.
  • Resolusinya per detik, bukan per milidetik. Dua upload dengan nama file yang sama, ke folder yang sama, pada detik yang sama akan menghasilkan key yang sama — yang kedua menimpa yang pertama tanpa pesan error apa pun.

Object key tidak mengandung identitas workspace. Kalau dua workspace pada satu instance memakai nilai folder yang sama, keduanya menulis ke ruang nama yang sama di bucket yang sama. Sertakan pembeda di folder (mis. master-data/<slug-workspace>/pelanggan) kalau instance dipakai lebih dari satu workspace.

Mengambil File Kembali

GET /api/v1/integration/public/{path}

Nilai url dari response upload bisa langsung ditempel di belakang /api/v1/integration/public:

https://app.contoh.com/api/v1/integration/public/tenant-uploads/master-data/pelanggan/ktp-budi_1758790000.pdf

Dua bentuk path diterima dan menunjuk objek yang sama:

BentukContohPerlakuan
Diawali nama bucket yang dikonfigurasi/public/tenant-uploads/master-data/ktp_175....pdfSegmen pertama dianggap nama bucket dan dibuang dari key
Tanpa nama bucket/public/master-data/ktp_175....pdfSeluruh path dianggap key objek

Artinya endpoint ini selalu membaca dari bucket yang dikonfigurasi di server; tidak ada cara meminta objek dari bucket lain lewat URL.

Response-nya adalah isi file dengan Content-Type sesuai yang tersimpan saat upload, dan header Content-Disposition: attachment — browser akan mengunduh, bukan menampilkan inline.

Endpoint unduh ini publik dan tanpa autentikasi, sementara nama objek dibentuk dari nama file asli ditambah timestamp detik. Siapa pun yang tahu (atau menebak) path objeknya bisa mengunduh isinya, termasuk dari luar workspace. Jangan memakai endpoint upload umum ini untuk dokumen yang bersifat rahasia — untuk lampiran yang perlu terkendali, pakai lampiran form BPMN yang aksesnya melewati pemeriksaan hak di sisi server.

Kode Status

StatusKapan munculBody
200Upload berhasil{"status":"success","message":"File uploaded successfully","data":{…}}
401Token tidak ada atau tidak valid{"error":"…"}
403Token valid tapi role di workspace bukan Admin SOP/Owner, atau x-active-tenant menunjuk workspace yang bukan miliknya{"error":"…"}
413Body melebihi batas ukuran (lihat bagian berikut)ditentukan oleh proxy/gateway
500Gagal menulis ke object storage{"status":"error","message":"Failed to upload file"}

Permintaan tanpa field file juga dijawab 500 Failed to upload file, bukan 400. Jadi status 500 pada endpoint ini tidak selalu berarti object storage bermasalah — periksa dulu nama field pada form-data-nya (harus persis file). Perilaku ini berlaku pada versi saat ini.

Untuk endpoint unduh:

StatusKapan muncul
200Objek ditemukan dan dikirim
400Path objek kosong, atau bucket belum dikonfigurasi di server
404Objek tidak ada di bucket

Batas Ukuran File

Tidak ada satu angka tunggal — yang berlaku adalah batas terkecil di sepanjang jalur permintaan:

LapisanBatasPengaturan
Integration Service100 MBTetap, tidak lewat env
Proxy / gateway AlurKerja80 MB (bawaan)MAX_REQUEST_BODY_SIZE, wajib memakai satuan eksplisit seperti 80MB atau 100MiB — angka telanjang ditolak saat start
Reverse proxy di depannya (mis. nginx)tergantung instalasiclient_max_body_size

Jadi pada instalasi standar, batas efektifnya 80 MB.

Pada instalasi yang masih memakai image proxy-service lama (sebelum perbaikan 30 Agustus 2026), batas body terkunci di 4 MiB bawaan framework dan tidak bisa dinaikkan lewat environment variable. Gejalanya: upload file beberapa MB gagal dengan 413 padahal MAX_REQUEST_BODY_SIZE sudah diisi besar. Solusinya memperbarui image proxy, bukan mengubah konfigurasi.

Konfigurasi Server

Endpoint ini membaca konfigurasi object storage dari environment variable berikut pada Integration Service:

VariableBawaanKeterangan
MINIO_ENDPOINT—Host object storage, mis. minio:9000 (tanpa skema)
MINIO_ACCESS_KEY—Access key. Tidak perlu diisi kalau memakai IAM role
MINIO_SECRET_KEY—Secret key. Tidak perlu diisi kalau memakai IAM role
MINIO_BUCKET_NAMEtenant-uploadsBucket tujuan upload sekaligus satu-satunya bucket yang bisa dibaca endpoint unduh
MINIO_USE_SSLfalseIsi true kalau endpoint diakses lewat HTTPS
MINIO_BUCKET_LOOKUPkosong (auto)Isi dns untuk Tencent COS; biarkan kosong untuk MinIO
AWS_IRSA_ENABLEDfalseIsi true untuk memakai IAM role (AWS IRSA) alih-alih access key statis
AWS_REGIONap-southeast-1Hanya dipakai saat AWS_IRSA_ENABLED=true

Kalau bucket belum ada, service akan mencoba membuatnya pada upload pertama. Kredensial yang dipakai karena itu perlu izin membuat bucket, atau bucket-nya disiapkan lebih dulu oleh administrator.

Pola Pemakaian yang Umum

  1. Kirim file ke POST /api/v1/integration/upload bersama folder yang menjelaskan asal datanya.
  2. Simpan nilai url dari response ke record milik Anda (kolom master data, variable proses, atau sistem luar) — bukan object_name, supaya prefix bucket ikut tersimpan.
  3. Saat perlu menampilkan, rangkai tautan "<base-url>/api/v1/integration/public" + url, dan lakukan URL-encode bila nama file mengandung spasi atau karakter khusus.

Kalau file yang diunggah harus mengikuti daur hidup proses (ikut terhapus saat draft dibatalkan, ikut tercatat di histori task), gunakan lampiran form BPMN, bukan pola di atas.