Reusable contract templates with standard clauses for common scenarios.
Template 1: Service Agreement (PKS Jasa)
PERJANJIAN KERJA SAMA (PKS)
Nomor: PKS-{NUMBER}/{MONTH}/{YEAR}
Pada hari ini, {DAY} tanggal {DATE}, bertempat di Jakarta, telah disepakati
Perjanjian Kerja Sama (PKS) antara:
PIHAK PERTAMA:
Nama/Perusahaan : {COMPANY_NAME}
NIK/NPWP : {ID_NUMBER}
Alamat : {ADDRESS}
Selanjutnya disebut "Pihak Pertama"
PIHAK KEDUA:
Nama/Perusahaan : {PARTY_2_NAME}
NIK/NPWP : {PARTY_2_ID}
Alamat : {PARTY_2_ADDRESS}
Selanjutnya disebut "Pihak Kedua"
Pasal 1 – LINGKUP PEKERJAAN
Pihak Pertama menugaskan Pihak Kedua untuk:
{SCOPE_OF_WORK}
Pasal 2 – JANGKA WAKTU
Perjanjian ini berlaku selama {DURATION} terhitung mulai {START_DATE}
hingga {END_DATE}.
Pasal 3 – IMBALAN JASA
Pihak Pertama membayar kepada Pihak Kedua:
- Total nilai: Rp {AMOUNT}
- Pembayaran: {PAYMENT_SCHEDULE}
- Rekening: {BANK_ACCOUNT}
Pasal 4 – HAK DAN KEWAJIBAN
[Standard obligations clause]
Pasal 5 – KERAHASIAAN
Kedua pihak wajib menjaga kerahasiaan informasi selama {NDA_PERIOD}.
Pasal 6 – PENYELESAIAN SENGKETA
Sengketa diselesaikan secara musyawarah. Jika gagal, melalui BANI atau
Pengadilan Negeri {JURISDICTION}.
Pasal 7 – KETENTUAN LAIN
Force majeure, governing law: Hukum Republik Indonesia.
Demikian PKS ini dibuat dalam 2 (dua) rangkap bermaterai cukup.
PIHAK PERTAMA PIHAK KEDUA
___________________ ___________________
{NAME_1} {NAME_2}
Saksi 1: _______________ Saksi 2: _______________
Template 2: Talent Commission Contract
PERJANJIAN MANAJEMEN TALENT
Nomor: TALENT-{NUMBER}/{MONTH}/{YEAR}
PIHAK PERTAMA (Manajemen/Agensi):
BerkahKarya / {LEGAL_ENTITY}
PIHAK KEDUA (Talent):
Nama : {TALENT_NAME}
NIK : {TALENT_NIK}
Platform : {PLATFORMS} # e.g., TikTok: @handle, IG: @handle
Pasal 1 – EKSKLUSIVITAS
Talent setuju bahwa Manajemen memiliki hak EKSKLUSIF untuk:
- Platform: {PLATFORM_LIST}
- Kategori konten: {CONTENT_CATEGORIES}
- Periode eksklusivitas: {DURATION}
Pasal 2 – PEMBAGIAN PENDAPATAN
| Sumber Pendapatan | Talent % | Manajemen % |
|--------------------------|----------|-------------|
| Brand Deal / Endorse | 70% | 30% |
| AdSense / Platform Fund | 85% | 15% |
| Merchandise | 60% | 40% |
| Live Gifting | 80% | 20% |
Pasal 3 – KEWAJIBAN TALENT
- Minimum {MIN_POSTS} konten/bulan
- Response time maks {RESPONSE_TIME} jam untuk brief brand
- Tidak bergabung agensi lain selama kontrak
Pasal 4 – KEWAJIBAN MANAJEMEN
- Pembayaran maksimal {PAYMENT_DAY} setiap bulan
- Laporan revenue transparan setiap bulan
- Tidak menanda-tangani deal tanpa persetujuan talent
Pasal 5 – KEPEMILIKAN KONTEN (IP)
Konten yang dibuat talent TETAP milik talent. Agensi hanya memiliki hak
penggunaan (license) selama kontrak aktif.
Pasal 6 – TERMINASI
- Notice period: {NOTICE_PERIOD} hari
- Early termination fee: {EARLY_TERM_FEE}
- Cooling-off: {COOLING_OFF} hari setelah terminasi
Pasal 7 – MATERAI DAN TANDA TANGAN
Ditandatangani dengan materai Rp 10.000 di atas.
Template 3: NDA (Non-Disclosure Agreement)
PERJANJIAN KERAHASIAAN (NDA)
Nomor: NDA-{NUMBER}/{YEAR}
Antara: {DISCLOSING_PARTY} ("Pihak Pengungkap")
Dan: {RECEIVING_PARTY} ("Pihak Penerima")
Pasal 1 – DEFINISI
"Informasi Rahasia" mencakup: strategi bisnis, data keuangan, data pengguna,
source code, daftar klien, dan informasi yang ditandai RAHASIA.
Pasal 2 – KEWAJIBAN KERAHASIAAN
Pihak Penerima WAJIB:
a) Tidak mengungkapkan Informasi Rahasia kepada pihak ketiga
b) Menggunakan hanya untuk tujuan {PURPOSE}
c) Melindungi dengan standar keamanan yang wajar
Pasal 3 – PENGECUALIAN
NDA tidak berlaku untuk informasi yang:
- Sudah diketahui publik
- Diperoleh dari sumber independen yang sah
- Diwajibkan oleh hukum/pengadilan
Pasal 4 – JANGKA WAKTU KERAHASIAAN
{NDA_DURATION} tahun sejak penandatanganan, atau selama informasi tetap
bersifat rahasia (whichever is longer).
Pasal 5 – SANKSI
Pelanggaran NDA dikenai denda Rp {PENALTY} dan ganti rugi aktual.
Pasal 6 – GOVERNING LAW
Hukum Republik Indonesia. Sengketa di Pengadilan Negeri {JURISDICTION}.
Template 4: SLA (Service Level Agreement)
SERVICE LEVEL AGREEMENT (SLA)
Lampiran dari PKS Nomor: {PKS_NUMBER}
1. UPTIME GUARANTEE
- Minimum uptime: {UPTIME_PERCENTAGE}% per bulan
- Planned maintenance: max {MAINTENANCE_HOURS} jam/bulan, notifikasi 48 jam sebelumnya
2. RESPONSE TIME
| Severity | Response | Resolution |
|----------|----------|------------|
| Critical | 1 jam | 4 jam |
| High | 4 jam | 24 jam |
| Medium | 24 jam | 72 jam |
| Low | 72 jam | 1 minggu |
3. PENALTY CLAUSE
Jika SLA dilanggar: kredit {PENALTY_PERCENTAGE}% dari tagihan bulan itu
Max penalty: {MAX_PENALTY_PERCENTAGE}% per bulan
4. EXCLUSIONS
Force majeure, acts of third-party providers beyond vendor control.
5. REPORTING
Monthly SLA report dikirim maksimal tanggal {REPORT_DAY} tiap bulan.
Key Clauses Library
Essential clauses for liability, termination, IP, and dispute resolution.
Payment Terms
KLAUSUL PEMBAYARAN STANDAR:
- DP: 50% upon signing
- Final: 50% upon delivery/milestone
- Late payment: 2% per month interest
- Payment method: Bank transfer to {ACCOUNT}
- Currency: IDR (or USD if international)
- Invoice: NET-{DAYS} from invoice date
IP Ownership
KLAUSUL HAK KEKAYAAN INTELEKTUAL:
OPSI A (Work-for-hire — client owns):
Seluruh hasil pekerjaan yang dibuat dalam ruang lingkup PKS ini secara
otomatis menjadi milik Pihak Pertama (klien) setelah pembayaran lunas.
OPSI B (Licensor keeps IP — agency/vendor owns):
Pihak Kedua tetap memiliki IP atas tools, metodologi, dan karya sebelumnya.
Pihak Pertama mendapat lisensi non-eksklusif, non-transferable untuk
penggunaan yang disepakati.
OPSI C (Joint ownership):
IP dikembangkan bersama dimiliki bersama 50/50. Penggunaan komersial
memerlukan persetujuan bersama.
Non-Compete
KLAUSUL NON-COMPETE:
Pihak Kedua setuju bahwa selama kontrak DAN {MONTHS} bulan setelah
terminasi, tidak akan:
a) Bekerja untuk kompetitor langsung: {COMPETITOR_LIST}
b) Menghubungi klien Pihak Pertama secara langsung
c) Mendirikan bisnis yang bersaing di bidang {SCOPE}
Geografis: {GEOGRAPHIC_SCOPE}
Catatan: Non-compete >12 bulan sulit ditegakkan di Indonesia.
Termination Clause
KLAUSUL TERMINASI:
TERMINASI DENGAN PEMBERITAHUAN:
Salah satu pihak dapat mengakhiri PKS ini dengan memberikan notice
{NOTICE_DAYS} hari secara tertulis.
TERMINASI SEGERA (Cause):
PKS dapat diakhiri segera tanpa notice jika:
- Material breach yang tidak diperbaiki dalam 14 hari setelah notifikasi
- Kebangkrutan/insolvency
- Pelanggaran hukum yang signifikan
- Force majeure > {FM_DAYS} hari
AKIBAT TERMINASI:
- Pembayaran yang sudah jatuh tempo tetap harus dibayar
- Pengembalian aset/data dalam {RETURN_DAYS} hari
- NDA tetap berlaku {NDA_DURATION} tahun setelah terminasi
Governing Law & Dispute Resolution
KLAUSUL PENYELESAIAN SENGKETA:
1. Musyawarah: Para pihak mengutamakan penyelesaian musyawarah
dalam 30 hari setelah sengketa muncul.
2. Mediasi: Jika musyawarah gagal, mediasi melalui BANI
(Badan Arbitrase Nasional Indonesia) dalam 60 hari.
3. Arbitrase/Pengadilan: Jika mediasi gagal, penyelesaian melalui:
OPSI A: BANI (binding arbitration, lebih cepat)
OPSI B: Pengadilan Negeri {JURISDICTION} (public, slower)
Hukum yang berlaku: Hukum Republik Indonesia.
Tracking System
Track contract status, renewal dates, and obligation deadlines.
Contract Registry (contracts/registry.csv)
id,type,party,start_date,end_date,value_idr,status,renewal_alert,payment_milestone,file_path,notes
TLT-001,TALENT,@jendralbot_handle,2026-01-01,2026-12-31,0,ACTIVE,2026-11-01,monthly,active/talent/TALENT-JENDRALBOT-20260101-v1.pdf,Revenue share deal
CLT-001,CLIENT,Acme Brand,2026-03-01,2026-03-31,15000000,ACTIVE,2026-03-24,on-delivery,active/client/CLIENT-ACME-20260301-v1.pdf,Campaign Feb
VND-001,VENDOR,Hostinger,2026-01-15,2027-01-15,1200000,ACTIVE,2026-12-15,annual,active/vendor/VENDOR-HOSTINGER-20260115-v1.pdf,Hosting renewal
EMP-001,EMPLOYMENT,Sony Ops,2026-03-01,2027-02-28,6000000,ACTIVE,2027-01-01,monthly,active/employment/EMPLOYMENT-PKWT-SONY-20260301-v1.pdf,PKWT 1 tahun
Status Codes
Status
Meaning
DRAFT
Being written, not sent
REVIEW
Under internal review
NEGOTIATE
Sent to counterparty, pending counter
PENDING_SIGN
Both agreed, awaiting signatures
ACTIVE
Fully signed, running
BREACH
Party in breach, remediation active
EXPIRED
Past end date, not renewed
TERMINATED
Ended early
ARCHIVE
Filed away, no action needed
Scripts
Automation scripts for contract generation, tracking, and alerts.
scripts/contract_tracker.py
#!/usr/bin/env python3"""
Contract Tracker — reads registry.csv and shows dashboard.
Usage: python3 scripts/contract_tracker.py [--status active] [--type talent]
"""import csv
import json
import argparse
from datetime import datetime, date, timedelta
from pathlib import Path
REGISTRY = Path(__file__).parent.parent / "contracts" / "registry.csv"
ALERT_DAYS = [30, 7, 1] # Days before expiry to flag
STATUS_EMOJI = {
"DRAFT": "📝",
"REVIEW": "🔍",
"NEGOTIATE": "🤝",
"PENDING_SIGN": "✍️",
"ACTIVE": "✅",
"BREACH": "🚨",
"EXPIRED": "⏰",
"TERMINATED": "❌",
"ARCHIVE": "📦",
}
defload_contracts(status_filter=None, type_filter=None):
contracts = []
ifnot REGISTRY.exists():
print(f"[ERROR] Registry not found: {REGISTRY}")
return []
withopen(REGISTRY) as f:
reader = csv.DictReader(f)
for row in reader:
if status_filter and row["status"].upper() != status_filter.upper():
continueif type_filter and row["type"].upper() != type_filter.upper():
continue
contracts.append(row)
return contracts
defdays_until(date_str):
"""Returns days until a date string (YYYY-MM-DD). Negative = overdue."""try:
target = datetime.strptime(date_str, "%Y-%m-%d").date()
return (target - date.today()).days
except:
returnNonedefget_expiring_soon(contracts, threshold_days=30):
expiring = []
for c in contracts:
if c["status"] != "ACTIVE":
continue
days = days_until(c.get("end_date", ""))
if days isnotNoneand0 <= days <= threshold_days:
expiring.append({**c, "_days_left": days})
returnsorted(expiring, key=lambda x: x["_days_left"])
defget_breaches(contracts):
breaches = []
for c in contracts:
if c["status"] == "BREACH":
breaches.append(c)
elif c["status"] == "ACTIVE":
days = days_until(c.get("end_date", ""))
if days isnotNoneand days < 0:
breaches.append({**c, "_overdue_days": abs(days), "_auto": True})
return breaches
defprint_dashboard(contracts):
today = date.today()
active = [c for c in contracts if c["status"] == "ACTIVE"]
expiring_30 = get_expiring_soon(contracts, 30)
expiring_7 = get_expiring_soon(contracts, 7)
breaches = get_breaches(contracts)
print("\n" + "="*60)
print(f"📋 CONTRACT DASHBOARD — {today}")
print("="*60)
print(f"\n📊 SUMMARY")
print(f" Total contracts: {len(contracts)}")
for status, emoji in STATUS_EMOJI.items():
count = sum(1for c in contracts if c["status"] == status)
if count > 0:
print(f" {emoji}{status}: {count}")
if breaches:
print(f"\n🚨 BREACHES / OVERDUE ({len(breaches)})")
for c in breaches:
print(f" [{c['id']}] {c['party']} — {c['type']}")
if expiring_7:
print(f"\n⚠️ EXPIRING ≤7 DAYS ({len(expiring_7)})")
for c in expiring_7:
print(f" [{c['id']}] {c['party']} — {c['_days_left']}d left — ends {c['end_date']}")
if expiring_30:
print(f"\n📅 EXPIRING ≤30 DAYS ({len(expiring_30)})")
for c in expiring_30:
print(f" [{c['id']}] {c['party']} — {c['_days_left']}d left — ends {c['end_date']}")
if active:
print(f"\n✅ ALL ACTIVE CONTRACTS ({len(active)})")
for c in active:
days = days_until(c.get("end_date", ""))
days_str = f"{days}d"if days isnotNoneelse"ongoing"
value = f"Rp {int(c['value_idr']):,}"if c.get('value_idr') else"—"print(f" [{c['id']}] {c['party']} | {c['type']} | {value} | {days_str} left")
print("\n" + "="*60 + "\n")
defmain():
parser = argparse.ArgumentParser(description="BerkahKarya Contract Tracker")
parser.add_argument("--status", help="Filter by status (active, draft, etc.)")
parser.add_argument("--type", help="Filter by type (talent, client, vendor, employment)")
parser.add_argument("--json", action="store_true", help="Output as JSON")
parser.add_argument("--expiring", type=int, default=None, help="Show contracts expiring in N days")
args = parser.parse_args()
contracts = load_contracts(args.status, args.type)
if args.json:
if args.expiring:
print(json.dumps(get_expiring_soon(contracts, args.expiring), indent=2))
else:
print(json.dumps(contracts, indent=2))
returnif args.expiring:
expiring = get_expiring_soon(contracts, args.expiring)
for c in expiring:
print(f"[{c['id']}] {c['party']} — {c['_days_left']} days left")
return
print_dashboard(contracts)
if __name__ == "__main__":
main()
scripts/renewal_alert.py
#!/usr/bin/env python3"""
Renewal Alert System — sends Telegram alerts for expiring contracts.
Runs via cron daily at 09:00 WIB.
Cron: 0 9 * * * cd /path/to/contracts && python3 scripts/renewal_alert.py
Setup:
export TELEGRAM_BOT_TOKEN="your_bot_token"
export TELEGRAM_CHAT_ID="your_chat_id" # or @channel_name
"""import csv
import os
import json
import urllib.request
import urllib.parse
from datetime import datetime, date, timedelta
from pathlib import Path
REGISTRY = Path(__file__).parent.parent / "contracts" / "registry.csv"
ALERT_THRESHOLDS = [30, 7, 1] # Days before expiry
TELEGRAM_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", "")
TELEGRAM_CHAT_ID = os.environ.get("TELEGRAM_CHAT_ID", "")
# State file to avoid duplicate alerts
STATE_FILE = Path(__file__).parent.parent / "contracts" / ".alert_state.json"defload_state():
if STATE_FILE.exists():
withopen(STATE_FILE) as f:
return json.load(f)
return {}
defsave_state(state):
withopen(STATE_FILE, "w") as f:
json.dump(state, f, indent=2)
defsend_telegram(message: str) -> bool:
ifnot TELEGRAM_TOKEN ornot TELEGRAM_CHAT_ID:
print(f"[TELEGRAM NOT CONFIGURED]\n{message}")
returnFalse
url = f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage"
data = urllib.parse.urlencode({
"chat_id": TELEGRAM_CHAT_ID,
"text": message,
"parse_mode": "Markdown"
}).encode()
try:
req = urllib.request.Request(url, data=data, method="POST")
urllib.request.urlopen(req, timeout=10)
returnTrueexcept Exception as e:
print(f"[TELEGRAM ERROR] {e}")
returnFalsedefdays_until(date_str):
try:
target = datetime.strptime(date_str, "%Y-%m-%d").date()
return (target - date.today()).days
except:
returnNonedefcheck_and_alert():
today = str(date.today())
state = load_state()
alerts_sent = 0ifnot REGISTRY.exists():
print(f"[ERROR] Registry not found: {REGISTRY}")
return
contracts = []
withopen(REGISTRY) as f:
contracts = list(csv.DictReader(f))
for c in contracts:
if c["status"] notin ("ACTIVE", "PENDING_SIGN"):
continue
days = days_until(c.get("end_date", ""))
if days isNone:
continuefor threshold in ALERT_THRESHOLDS:
if days == threshold:
alert_key = f"{c['id']}_{threshold}d_{today}"if alert_key in state.get("sent", []):
continue# Already sent today
urgency = "🚨"if threshold <= 1else"⚠️"if threshold <= 7else"📅"
msg = (
f"{urgency} *CONTRACT EXPIRY ALERT*\n\n"f"📋 Contract: `{c['id']}`\n"f"👤 Party: {c['party']}\n"f"🏷️ Type: {c['type']}\n"f"📅 Expires: {c['end_date']} ({days} days)\n"f"💰 Value: Rp {int(c.get('value_idr', 0) or0):,}\n"f"📁 File: {c.get('file_path', 'N/A')}\n\n"f"{'⚡ ACTION REQUIRED — 1 DAY LEFT!'if threshold <= 1else'⚡ Renew or close out before deadline.'}"
)
sent = send_telegram(msg)
if sent:
state.setdefault("sent", []).append(alert_key)
alerts_sent += 1print(f"[ALERT SENT] {c['id']} — {threshold}d warning")
# Breach detection: active contract past end dateif days < 0and c["status"] == "ACTIVE":
breach_key = f"BREACH_{c['id']}_{today}"if breach_key notin state.get("sent", []):
msg = (
f"🚨 *CONTRACT BREACH / OVERDUE*\n\n"f"📋 Contract: `{c['id']}`\n"f"👤 Party: {c['party']}\n"f"📅 Expired: {c['end_date']} ({abs(days)} days ago)\n"f"❗ Status still ACTIVE — update registry!\n\n"f"Action: Renew, terminate, or update status to EXPIRED."
)
sent = send_telegram(msg)
if sent:
state.setdefault("sent", []).append(breach_key)
alerts_sent += 1
save_state(state)
print(f"[DONE] {alerts_sent} alerts sent | {today}")
if __name__ == "__main__":
check_and_alert()
E-Signature Options
Key aspects of contract-manager relevant to this section.
Free / Low-Cost Options (Recommended for Indonesia)
Tool
Cost
Legal in ID?
Use Case
Privy.id
Freemium
✅ Yes (BSrE certified)
Best for Indonesian contracts
PeruriSign
Paid
✅ Yes (Government)
High-stakes, legal proceedings
DocuSign
Paid
✅ Accepted
International contracts
Adobe Sign
Paid
✅ Accepted
Enterprise use
SignWell
Free (5/mo)
✅ Accepted
Low volume
PDF + WhatsApp
Free
⚠️ Debatable
Small informal deals, add materai
Materai Digital
For Indonesian legal enforceability: use e-Materai from Peruri
Required for contracts > Rp 5,000,000 or legally sensitive
Workflow (Privy.id Recommended)
1. Draft contract in Google Docs / Word
2. Export to PDF
3. Upload to Privy.id
4. Add e-Materai if needed (Peruri)
5. Send signing link to counterparty
6. Download signed PDF
7. File in contracts/active/{type}/
8. Update registry.csv
Indonesian Law Compliance
Key aspects of contract-manager relevant to this section.
PKS Format Requirements
A valid PKS (Perjanjian Kerja Sama) in Indonesia must have:
Nomor PKS (unique reference number)
Tanggal dan tempat penandatanganan
Identitas para pihak (nama, NIK/NPWP, alamat)
Lingkup pekerjaan yang jelas
Jangka waktu
Nilai dan mekanisme pembayaran
Hak dan kewajiban masing-masing pihak
Klausul kerahasiaan (jika diperlukan)
Penyelesaian sengketa
Tanda tangan + materai Rp 10.000
2 orang saksi (strongly recommended)
References
Law
Scope
KUH Perdata Pasal 1320
Syarat sah perjanjian
UU No. 13/2003
Ketenagakerjaan
PP No. 35/2021
PKWT, alih daya
UU No. 11/2008 (ITE)
Kontrak elektronik
PP No. 71/2019
Tanda tangan elektronik
UU No. 28/2014
Hak Cipta
UU No. 20/2016
Merek dan Indikasi Geografis
Quick Commands
# Run contract dashboard
python3 scripts/contract_tracker.py
# Show only active contracts
python3 scripts/contract_tracker.py --status active
# Show talent contracts only
python3 scripts/contract_tracker.py --type talent
# Show contracts expiring in 30 days
python3 scripts/contract_tracker.py --expiring 30
# Run renewal alerts manually (also runs via cron)
python3 scripts/renewal_alert.py
# Export registry as JSON
python3 scripts/contract_tracker.py --json
# Setup Telegram alerts (set env vars first)export TELEGRAM_BOT_TOKEN="bot_token_here"export TELEGRAM_CHAT_ID="@berkahkarya_ops"
python3 scripts/renewal_alert.py
Cron Setup
# Add to crontab: crontab -e# Run daily at 09:00 WIB (UTC+7 = UTC+0 02:00)
0 2 * * * cd /home/openclaw/.openclaw/workspace/contracts && TELEGRAM_BOT_TOKEN=xxx TELEGRAM_CHAT_ID=yyy python3 scripts/renewal_alert.py >> logs/renewal_alert.log 2>&1