Addon

AlurKerja Python SDK untuk Script Addon

Memanggil service AlurKerja (mis. master data) dari script addon tanpa konfigurasi manual

Script addon Python bisa memanggil service AlurKerja — misalnya membaca atau menambah master data — tanpa menyimpan token, URL, atau ID tenant di konfigurasi addon. Platform mengisi kredensialnya otomatis lewat ctx.svc, dan AlurKerja Python SDK membungkus pemanggilan HTTP-nya.

Halaman ini melanjutkan Pembuatan Addon pada AlurKerja: struktur addon, index.json, dan kontrak run(ctx) di sana tetap berlaku.

1. Cara Kerja

Service Task BPMN (addon action)
   │
   ▼
Camunda ── POST /api/v1/integration/addons/{addon}/execute/{action}
   │        body: variables, parameters, configuration, process
   ▼
integration-service
   1. Addon opt-in svc?            (index.json → "svc": {"enabled": true})
   2. Pemanggil = Camunda?          (service account INTEGRATION_AUTH_CLIENT_ID)
   3. Versi BPMN yang berjalan      (camunda.act_re_procdef)
   4. Siapa yang men-deploy versi itu (camunda.act_hi_op_log)
   5. Deployer masih admin SOP / owner di workspace?
   6. Buat token atas nama deployer (impersonation Keycloak)
   │
   ▼
python3 scripts/<action>/<action>.py --run <ctx.json>
   ctx.svc = { token, baseurl, tenant, process, actor }
   │
   ▼
AlurkerjaSDK.from_ctx(ctx).get("probis", "masterdata", ...)

Hal yang perlu diingat:

  • Token milik user yang men-deploy versi BPMN yang sedang dijalankan proses tersebut, bukan user yang sedang login. Script mendapat hak akses sebesar hak akses deployer itu.
  • Deployer wajib masih berperan admin SOP atau owner di workspace. Kalau tidak, eksekusi ditolak sebelum script dijalankan (lihat Bagian 9).
  • ctx.svc hanya ada untuk action SCRIPT yang dipanggil dari Service Task BPMN. Jalur API addon (/{tenant_setting_id}/api/{action}) tidak menerimanya.
  • ctx.svc tidak disimpan di riwayat eksekusi addon.
  • Bentuk balasan script juga diurus SDK — lihat Bagian 8.4.

2. Prasyarat

KebutuhanKeterangan
Pythonpython3 ≥ 3.8 di host/container integration-service (script dijalankan dengan python3)
SDKalurkerja-sdk terpasang untuk python3 tersebut (Bagian 3)
Deployer BPMNMen-deploy BPMN dengan role admin SOP atau owner
integration-serviceEnv INTEGRATION_AUTH_CLIENT_ID sama dengan client Camunda, dan PROXY_SERVICE_URL terisi

3. Instalasi SDK

Di host atau image integration-service:

python3 -m pip install --upgrade pip
python3 -m pip install "git+https://github.com/alurkerja-id/alurkerja-addon-python-sdk.git@v0.3.0"
python3 -c "import alurkerja_sdk; print(alurkerja_sdk.__version__)"

Di repository addon, catat dependensinya di requirements.txt:

alurkerja-sdk @ git+https://github.com/alurkerja-id/alurkerja-addon-python-sdk.git@v0.3.0

Untuk pengembangan lokal cukup pip install -r requirements.txt.

4. Opt-in di index.json

Tambahkan blok svc di level atas index.json:

{
  "name": "mockapi",
  "version": "1.0.0",
  "type": "REST",
  "svc": {
    "enabled": true
  }
}

Nilai ini disimpan saat addon di-install atau di-publish. Addon yang sudah terpasang sebelum opt-in ditambahkan harus di-publish ulang; tanpa itu ctx.svc tidak pernah dikirim.

5. Isi ctx yang Diterima Script

{
  "variables": {
    "master_data_slug": "customers",
    "harga": 75000
  },
  "parameters": {
    "page": 0,
    "page_size": 10
  },
  "configuration": {},
  "runkey": "exec-1789123456789-getMasterDataRecords",
  "svc": {
    "token": "eyJhbGciOi...",
    "baseurl": "https://onprem.merapi.alurkerja.com",
    "tenant": {
      "id": 1,
      "name": "Tenant A",
      "slug": "tenant-a",
      "uuid": "f8672713-7ec0-4a8a-98ba-f6adf420d105"
    },
    "process": {
      "definitionId": "pengajuan-cuti:7:3f1c",
      "key": "pengajuan-cuti",
      "name": "Pengajuan Cuti",
      "version": 7,
      "deploymentId": "a41b",
      "tenantId": "c3a9…",
      "instanceId": "9c2e",
      "businessKey": "CUTI-2026-001",
      "activityId": "Activity_ambil_md",
      "activityName": "Ambil Master Data"
    },
    "actor": {
      "email": "deployer@example.com",
      "source": "deployer"
    }
  }
}
Field svcKeteranganDi SDK
tokenAccess token Keycloak milik deployer, berumur pendekdipakai otomatis sebagai Authorization: Bearer
baseurlGateway AlurKerjasdk.api_url (selalu diakhiri /api/v1)
tenantWorkspace tempat proses berjalansdk.tenant.id, .name, .slug, .uuid — slug otomatis dikirim sebagai header x-active-tenant
processBPMN yang sedang berjalansdk.process.key, .version, .activity_id, .instance_id, …
actorPemilik tokensdk.actor.email, sdk.actor.source

6. Contoh: Membaca Record Master Data

Struktur file di addon:

mockapi/
├── index.json
├── requirements.txt
└── scripts/
    └── get_masterdata_records/
        ├── get_masterdata_records.py
        └── request.example.json

6.1 Script

scripts/get_masterdata_records/get_masterdata_records.py:

#!/usr/bin/env python3
"""
Get Master Data Records Script
Membaca record master data tenant aktif lewat AlurKerja SDK.
Mengikuti General Action Executor contract.

Butuh paket `alurkerja-sdk` dan `ctx.svc` (token, baseurl, tenant), yang diisi
platform otomatis karena addon ini opt-in `svc.enabled` di index.json.
"""

import sys
import json
import argparse

try:
    from alurkerja_sdk import (
        AlurkerjaSDK,
        AlurkerjaAPIError,
        AlurkerjaConfigError,
        AlurkerjaRequestError,
        exit_code_for,
    )
except ImportError:
    AlurkerjaSDK = None


def run(ctx):
    """
    Entry point untuk action script (wajib bernama 'run')

    Args:
        ctx: Execution context dengan struktur:
            - variables: Data runtime (dapat dimodifikasi), termasuk
              `master_data_slug` (wajib) dan `harga` (opsional, untuk business
              rule batas harga)
            - parameters: page, page_size, search, order, orderBy
            - configuration: Konfigurasi addon/plugin
            - svc: {token, baseurl, tenant, process, actor} dari platform
            - runkey: Unique identifier untuk tracing

    Returns:
        dict: Response dengan format standard
    """

    parameters = ctx.get("parameters", {})
    variables = ctx.get("variables", {})

    slug = variables.get("master_data_slug")
    harga = variables.get("harga")

    sdk = AlurkerjaSDK.from_ctx(ctx)

    if not slug:
        return sdk.error("slug wajib diisi", code="MISSING_PARAMETER")

    if harga is not None and harga > 100000:
        return sdk.bpmn_error("HARGA_MELEBIHI_BATAS", "Harga melebihi batas maksimum")

    if sdk.tenant is None or sdk.tenant.id is None:
        return sdk.error("svc.tenant.id is required", code="MISSING_CONFIGURATION")

    # GET /api/v1/probis/masterdata/{tenantId}/{masterDataSlug}/records
    try:
        records = sdk.get(
            "probis", "masterdata", sdk.tenant.id, slug, "records",
            params={
                "page": parameters.get("page"),
                "page_size": parameters.get("page_size"),
                "search": parameters.get("search"),
                "order": parameters.get("order"),
                "orderBy": parameters.get("orderBy"),
            },
        )
    except AlurkerjaAPIError as exc:
        return sdk.error(exc)

    return sdk.success(records, message="Master data terbaca")


def main():
    """Main function untuk handle command line arguments"""

    parser = argparse.ArgumentParser(
        description='Get master data records script - General Action Executor'
    )

    parser.add_argument(
        '--run',
        type=str,
        required=True,
        help='Execution context (JSON file path or JSON string)'
    )

    args = parser.parse_args()
    runkey = ""

    # Dicek sebelum try: tanpa SDK, kelas exception di bawah tidak pernah terimpor
    if AlurkerjaSDK is None:
        print(json.dumps({
            "status": "error",
            "message": "Package alurkerja-sdk is not installed",
            "error": "DEPENDENCY_MISSING",
            "runkey": runkey
        }, indent=2))
        sys.exit(3)

    try:
        # Parse execution context
        if args.run.endswith('.json'):
            with open(args.run, 'r') as f:
                ctx = json.load(f)
        else:
            ctx = json.loads(args.run)
        runkey = ctx.get("runkey", "")

        result = run(ctx)
        print(json.dumps(result, indent=2))
        sys.exit(exit_code_for(result))

    except json.JSONDecodeError as e:
        error = {"message": f"Invalid JSON format: {str(e)}", "error": "INVALID_INPUT"}
        exit_code = 2
    except AlurkerjaConfigError as e:
        error = {"message": str(e), "error": "INVALID_CONFIGURATION"}
        exit_code = 2
    except AlurkerjaRequestError as e:
        error = {"message": str(e), "error": "REQUEST_FAILED"}
        exit_code = 3
    except Exception as e:
        error = {"message": f"Error executing get master data records script: {str(e)}", "error": "EXECUTION_ERROR"}
        exit_code = 1

    print(json.dumps({"status": "error", **error, "runkey": runkey}, indent=2))
    sys.exit(exit_code)


if __name__ == "__main__":
    main()

6.2 Penjelasan Bagian Script

Import dengan fallback. Import SDK dibungkus try/except ImportError. Kalau SDK belum terpasang di host, script tetap bisa mencetak JSON error yang jelas (DEPENDENCY_MISSING, exit code 3) alih-alih traceback Python.

Slug dari process variable. master_data_slug dibaca dari ctx["variables"], yaitu variable proses Camunda yang sudah ada saat Service Task berjalan (mis. diisi dari field form di user task sebelumnya). Paging dan pencarian (page, page_size, search, order, orderBy) tetap dari ctx["parameters"] yang diisi di konfigurasi action pada Service Task. Service Task yang dibuat lewat Studio sudah memanggil putAllProcessVariables(), sehingga seluruh variable proses ikut terkirim ke variables.

Membuat SDK dari ctx. AlurkerjaSDK.from_ctx(ctx) membaca ctx["svc"]. Token, base URL, dan tenant tidak perlu dirakit sendiri. Buat SDK di dalam run(ctx) pada setiap eksekusi — token berumur pendek dan tidak boleh disimpan.

Membentuk response. Balasan script dibuat SDK, bukan dirakit sendiri: sdk.success(...) untuk berhasil, sdk.error(...) untuk gangguan teknis atau validasi, dan sdk.bpmn_error(...) untuk kegagalan bisnis. status, runkey, dan key type diisi otomatis — lihat Bagian 8.4.

Business rule lewat bpmn_error. Batas harga di contoh ini bukan error teknis, melainkan keputusan bisnis: sdk.bpmn_error("HARGA_MELEBIHI_BATAS", ...) membuat Camunda melempar BpmnError, sehingga proses berbelok ke error boundary event berkode HARGA_MELEBIHI_BATAS. Bandingkan dengan sdk.error("slug wajib diisi", ...) di atasnya, yang berakhir sebagai incident di Cockpit.

Error API ditangkap di run. sdk.get(...) melempar AlurkerjaAPIError untuk respons non-2xx; contoh ini menangkapnya dan mengembalikan sdk.error(exc), sehingga error, http_status, dan response terisi sendiri. Kalau tidak ditangkap, exception itu jatuh ke main() dan hasilnya sama.

Membentuk URL. Argumen sdk.get(...) adalah service code diikuti potongan path:

sdk.get("probis", "masterdata", sdk.tenant.id, slug, "records", params={...})
# GET {baseurl}/api/v1/probis/masterdata/1/customers/records?page=0&page_size=10
# Authorization: Bearer <svc.token>
# x-active-tenant: tenant-a

Setiap segmen di-URL-encode. Segmen kosong, ., .., atau yang mengandung / ditolak dengan ValueError, sehingga nilai dari parameters tidak bisa membelokkan request ke endpoint lain.

Query params. Nilai None otomatis dibuang, jadi parameter opsional yang tidak diisi (mis. search) tidak ikut terkirim. True/False dikirim sebagai true/false.

Hasil. Respons JSON dikembalikan sebagai dict, lalu dimasukkan ke data pada output script lewat sdk.success(records, ...). Endpoint list master data mengembalikan halaman bergaya Spring: record ada di data.content, total di data.totalElements.

Exit code. sys.exit(exit_code_for(result)) menurunkan exit code dari isi balasan: 0 untuk sdk.success dan sdk.bpmn_error, 1 untuk sdk.error biasa. BPMN_ERROR sengaja 0 — exit non-zero membuat platform menjawab 500 dan Camunda membuat incident sebelum sempat membacanya.

Penanganan error di main(). Yang tersisa di main() hanyalah kegagalan di luar run(ctx) atau yang tidak ditangkap di sana:

ExceptionKapanerrorExit code
json.JSONDecodeError--run bukan JSON yang sahINVALID_INPUT2
AlurkerjaConfigErrorctx.svc tidak ada / tidak lengkap (dilempar from_ctx)INVALID_CONFIGURATION2
AlurkerjaRequestErrorRequest tidak sampai (koneksi, DNS, timeout)REQUEST_FAILED3
Exception lainError tak terduga, termasuk ValueError segmen pathEXECUTION_ERROR1

AlurkerjaAPIError tidak lagi sampai ke sini karena sudah ditangkap di run(ctx).

6.3 Daftarkan Script dan Action di index.json

{
  "svc": { "enabled": true },
  "scripts": {
    "getMasterDataRecords": {
      "description": "Membaca record master data tenant aktif via AlurKerja SDK.",
      "scripts": {
        "type": "python",
        "executable": "scripts/get_masterdata_records/get_masterdata_records.py"
      }
    }
  },
  "actions": {
    "getMasterDataRecords": {
      "description": "Read master data records of the active tenant.",
      "type": "SCRIPT",
      "endpoint": "getMasterDataRecords",
      "body": [
        { "key": "page", "value": "0", "type": "number", "required": false },
        { "key": "page_size", "value": "10", "type": "number", "required": false },
        { "key": "search", "value": "", "type": "text", "required": false }
      ]
    }
  }
}

actions.<nama>.endpoint harus sama dengan key di scripts, dan setiap body[].key menjadi field di ctx["parameters"]. master_data_slug sengaja tidak ada di body karena dibaca dari variable proses, bukan dari parameter action.

6.4 Uji Lokal

Saat dijalankan lokal tidak ada platform yang mengisi svc, jadi isi sendiri di request.example.json. Token bisa diambil dari sesi login user admin SOP (header Authorization di browser devtools).

scripts/get_masterdata_records/request.example.json:

{
  "variables": {
    "master_data_slug": "customers",
    "harga": 75000
  },
  "parameters": {
    "page": 0,
    "page_size": 10
  },
  "configuration": {},
  "runkey": "test-get-masterdata-001",
  "svc": {
    "token": "your_token_here",
    "baseurl": "https://onprem.merapi.alurkerja.com",
    "tenant": { "id": 1, "name": "Tenant A", "slug": "tenant-a", "uuid": "f8672713-7ec0-4a8a-98ba-f6adf420d105" }
  }
}
pip install -r requirements.txt
python3 scripts/get_masterdata_records/get_masterdata_records.py \
  --run scripts/get_masterdata_records/request.example.json

Contoh output:

{
  "status": "ok",
  "message": "Master data terbaca",
  "data": {
    "data": {
      "content": [
        { "id": 1, "name": "PT Mitra Sejahtera" }
      ],
      "number": 0,
      "size": 10,
      "numberOfElements": 1,
      "totalElements": 1,
      "totalPages": 1,
      "first": true,
      "last": true,
      "empty": false
    },
    "status": 200,
    "message": "Master data records retrieved successfully"
  },
  "runkey": "test-get-masterdata-001"
}

Jangan commit token asli ke request.example.json.

7. Memakai Action di BPMN

  1. Publish addon yang sudah memuat opt-in svc (lihat Publish Addon).
  2. Pastikan variable proses master_data_slug sudah terisi sebelum Service Task tercapai — misalnya dari field form bernama master_data_slug di user task sebelumnya. Contoh ini juga membaca variable harga untuk business rule batas harga.
  3. Di BPMN editor, pilih Service Task, pilih addon dan action getMasterDataRecords, lalu isi parameter paging/pencarian bila perlu.
  4. Kalau memakai sdk.bpmn_error seperti contoh, pasang error boundary event di Service Task dengan Error Code yang sama persis (HARGA_MELEBIHI_BATAS). Tanpa itu, Camunda membuat incident.
  5. Deploy BPMN dengan akun admin SOP atau owner. Akun inilah yang tokennya dipakai script.
  6. Jalankan proses. Saat Service Task tercapai, script menerima ctx.svc dan memanggil master data.

Kalau Save Result diaktifkan, hasil disimpan ke variable proses (default integration_<addon>_<action>_output). Karena output script berbentuk {"data": ...}, isi variable-nya adalah nilai data tersebut — respons dari endpoint master data. Pada contoh di atas, daftar record ada di data.content dari variable itu.

8. Referensi SDK

8.1 Membuat Instance

from alurkerja_sdk import AlurkerjaSDK

sdk = AlurkerjaSDK.from_ctx(ctx)           # ctx["svc"], fallback ctx["configuration"]["svc"]
sdk = AlurkerjaSDK(ctx["svc"])             # setara
sdk = AlurkerjaSDK(ctx["svc"], timeout=10) # timeout default 30 detik

svc boleh berupa dict atau string JSON. token dan baseurl wajib; tanpa itu SDK langsung melempar AlurkerjaConfigError.

8.2 Method

MethodContoh
get(service, *path, params=, headers=, timeout=)sdk.get("probis", "masterdata", sdk.tenant.id, "customers", "records")
post(service, *path, params=, json=, data=, files=, …)sdk.post("probis", "masterdata", sdk.tenant.id, "customers", "records", json={"name": "PT A"})
put(...) / patch(...) / delete(...)bentuk sama dengan post
request(method, service, *path, ...)method HTTP bebas
build_url(service, *path)URL lengkap tanpa query, untuk logging

params dan json boleh dikirim bersamaan:

# POST /api/v1/probis/masterdata/1/customers/records?source=addon
created = sdk.post(
    "probis", "masterdata", sdk.tenant.id, "customers", "records",
    params={"source": "addon"},
    json={"name": "PT Mitra Sejahtera", "npwp": "01.234.567.8-901.000"},
)

8.3 Nilai Kembalian

Content-Type responsHasil
application/jsondict / list hasil parse
text/*str
lainnya (mis. unduhan file)bytes
body kosong (mis. 204)None

Status non-2xx selalu melempar AlurkerjaAPIError dengan atribut status_code, body, method, dan url.

8.4 Membentuk Response Script

Script tidak perlu merakit dict balasan sendiri. SDK mengisi status, runkey, dan — untuk kegagalan — key type yang menentukan apa yang terjadi di BPMN.

def run(ctx):
    sdk = AlurkerjaSDK.from_ctx(ctx)

    slug = ctx.get("parameters", {}).get("slug")
    if not slug:
        return sdk.error("slug wajib diisi", code="MISSING_PARAMETER")

    try:
        records = sdk.get("probis", "masterdata", sdk.tenant.id, slug, "records")
    except AlurkerjaAPIError as err:
        return sdk.error(err)                       # error, http_status, response terisi sendiri

    if records["data"]["totalElements"] == 0:
        return sdk.bpmn_error("DATA_KOSONG", f"Master data '{slug}' belum berisi record")

    return sdk.success(records, message="Master data terbaca", total=records["data"]["totalElements"])
MethodtypeExit codeEfek di BPMN
sdk.success(data=None, message=None, **extra)—0Proses lanjut normal
sdk.error(message_or_exception, code=None, data=None, **extra)RUNTIME_ERROR1 / 2 / 3Incident di Cockpit; operator bisa retry activity
sdk.bpmn_error(code, message=None, data=None, **extra)BPMN_ERROR0Camunda melempar BpmnError dengan errorCode = code → ditangkap error boundary event berkode sama

Bentuk yang dihasilkan:

// sdk.success(records, message="Master data terbaca", total=1)
{ "status": "ok", "message": "Master data terbaca", "data": { "…": "…" }, "total": 1, "runkey": "exec-…" }

// sdk.error("slug wajib diisi", code="MISSING_PARAMETER")
{ "status": "error", "type": "RUNTIME_ERROR", "message": "slug wajib diisi", "error": "MISSING_PARAMETER", "runkey": "exec-…" }

// sdk.error(err) — err: AlurkerjaAPIError 403
{ "status": "error", "type": "RUNTIME_ERROR", "message": "GET … -> HTTP 403: …", "error": "API_ERROR",
  "http_status": 403, "response": { "error": "forbidden" }, "runkey": "exec-…" }

// sdk.bpmn_error("STOK_HABIS", "Stok tidak mencukupi", data={"tersedia": 2})
{ "status": "error", "type": "BPMN_ERROR", "error": "STOK_HABIS", "message": "Stok tidak mencukupi",
  "data": { "tersedia": 2 }, "runkey": "exec-…" }

Catatan:

  • Argumen tambahan (**extra) menambah key di top-level, dan key top-level itulah yang di-merge ke variables proses. Muatan utama tetap di data.
  • runkey diisi otomatis kalau SDK dibuat dengan AlurkerjaSDK.from_ctx(ctx).
  • Tersedia juga sebagai fungsi modul — from alurkerja_sdk import success, error, bpmn_error, exit_code_for — untuk dipakai saat SDK belum sempat dibuat, mis. svc tidak lengkap. Di situ runkey= diisi manual.
  • Di main(), pakai sys.exit(exit_code_for(result)).
  • Alias sdk.bpmnError(...) tersedia supaya penamaannya sama dengan istilah di diagram.

Kapan memakai bpmn_error

sdk.bpmn_error untuk kegagalan bisnis yang alurnya sudah digambar — stok habis, dokumen ditolak, data tidak ditemukan — sehingga proses berbelok lewat error boundary event, bukan berhenti sebagai incident.

┌────────────────────┐
│ Service Task       │
│ (addon action)     │──────▶ lanjut normal (sdk.success)
└─────────┬──────────┘
          │ error boundary event, Error Code: STOK_HABIS
          ▼
   ┌──────────────┐
   │ Task: pesan  │  ← sdk.bpmn_error("STOK_HABIS", ...)
   │ ke supplier  │
   └──────────────┘
  • Error Code di boundary event wajib sama persis dengan code di script. Kalau tidak ada boundary event yang cocok, Camunda tetap membuat incident.
  • sdk.bpmn_error memakai exit code 0 dengan sengaja. Exit non-zero membuat platform menjawab HTTP 500, dan Camunda membuat incident sebelum sempat membaca type-nya.
  • Variabel hasil disimpan lebih dulu, baru BpmnError dilempar. Jadi task di jalur error masih bisa membaca alasan penolakannya dari variable.
  • Di riwayat eksekusi addon, BPMN_ERROR tercatat sebagai eksekusi berhasil — script memang selesai; yang terjadi adalah alur berbelok.

Gunakan sdk.error untuk gangguan teknis (API 5xx, koneksi putus, konfigurasi salah): operator melihatnya sebagai incident dan bisa me-retry setelah penyebabnya beres.

8.5 Endpoint Master Data

Service code probis (bpm-service). {tenantId} = sdk.tenant.id, {slug} = slug master data.

OperasiPemanggilan SDK
List record (page, page_size, search, order, orderBy)sdk.get("probis", "masterdata", tid, slug, "records", params={...})
Detail recordsdk.get("probis", "masterdata", tid, slug, "records", record_id)
Tambah recordsdk.post("probis", "masterdata", tid, slug, "records", json={...})
Tambah banyak recordsdk.post("probis", "masterdata", tid, slug, "records", "bulk", json=[...])
Ubah recordsdk.put("probis", "masterdata", tid, slug, "records", record_id, json={...})
Hapus recordsdk.delete("probis", "masterdata", tid, slug, "records", record_id)

Endpoint master data mensyaratkan role admin SOP, itulah alasan deployer wajib admin SOP atau owner.

9. Penolakan dan Troubleshooting

9.1 Penolakan Sebelum Script Dijalankan

Jika syarat svc tidak terpenuhi, integration-service menolak eksekusi dan Service Task menjadi incident di Cockpit dengan body {"error": "...", "addon": "...", "reason": "..."}:

reasonArtiTindakan
svc_not_configuredINTEGRATION_AUTH_CLIENT_ID atau PROXY_SERVICE_URL kosong di integration-serviceLengkapi env, restart service
svc_caller_not_allowedAction dipanggil bukan dari Service Task (mis. curl dengan token user)Jalankan dari BPMN
svc_process_context_missingService Task tidak mengirim konteks prosesDeploy ulang BPMN dari Studio terbaru
svc_process_definition_not_foundVersi BPMN tidak ditemukanPeriksa deployment proses
svc_process_lookup_failedQuery ke schema camunda gagalPeriksa izin database integration-service
svc_tenant_mismatchProses bukan milik workspace pemanggilPeriksa mapping tenant
svc_deployer_not_foundUser yang men-deploy versi ini tidak tercatatDeploy ulang BPMN oleh admin SOP
svc_deployer_not_authorizedDeployer sudah bukan admin SOP/owner atau keluar dari workspaceDeploy ulang BPMN oleh admin SOP aktif, lalu jalankan ulang proses
svc_token_exchange_failedKeycloak gagal membuat tokenRetry activity; periksa role impersonation service account

Deploy ulang menghasilkan versi baru. Instance yang sudah berjalan di versi lama tetap memakai deployer versi lama, jadi instance tersebut perlu dijalankan ulang di versi baru.

9.2 Masalah Umum

GejalaPenyebabSolusi
Output DEPENDENCY_MISSINGSDK belum terpasang untuk python3 yang menjalankan scriptPasang SDK (Bagian 3) dengan python3 -m pip
INVALID_CONFIGURATION: svc tidak ditemukan …Addon belum opt-in, atau dipanggil lewat jalur API addonTambah "svc": {"enabled": true}, publish ulang, jalankan dari Service Task
Script jalan tetapi svc kosong, tanpa penolakanAddon terpasang sebelum opt-in ditambahkan, atau integration-service belum versi yang mendukung svcPublish ulang addon; pastikan integration-service sudah diperbarui
API_ERROR dengan http_status 403Deployer tidak punya akses ke endpoint yang dipanggilDeploy BPMN dengan akun yang aksesnya sesuai
API_ERROR dengan http_status 404Slug master data atau ID record salahPeriksa nilai variable proses master_data_slug
EXECUTION_ERROR dengan pesan ... segmen path harus str atau int: NoneVariable proses master_data_slug belum terisi saat Service Task berjalanIsi variable tersebut di langkah sebelumnya (mis. field form)
Service task jadi incident padahal ingin berbelok ke jalur errorScript memakai sdk.error, atau kode bpmn_error tidak sama dengan Error Code boundary eventPakai sdk.bpmn_error("KODE", ...) dengan kode yang sama persis
Unable to locate package python3.12 di Ubuntu 20.04Tidak perlu Python 3.12SDK mendukung Python 3.8 bawaan Ubuntu 20.04

10. Aturan Keamanan

LakukanHindari
Buat AlurkerjaSDK di dalam run(ctx) setiap eksekusiMenyimpan token ke file, database, atau variable global
Kirim nilai dari parameters lewat argumen SDK (otomatis di-encode)Merakit URL sendiri dengan f-string dari input user
Kembalikan data bisnis saja di outputMengembalikan ctx["svc"] atau token di output — output tersimpan di riwayat eksekusi dan variable proses
Log sdk.process.key / sdk.actor.email untuk tracingMencetak token ke stdout/stderr