Jawaban singkat untuk pertanyaan yang paling sering diajukan pembaca tentang topik ini.
01Apa itu OpenAI Skills API?
Ini adalah kumpulan endpoint di bawah /v1/skills untuk menyimpan bundle skill yang bisa dipakai ulang: folder dengan manifest SKILL.md berisi name dan description di YAML front matter, plus scripts, references, dan assets opsional. Skill diberi versi di server lalu dipasang ke shell tool di Responses API, atau ditemukan oleh Agents API, sehingga model mengikuti prosedur terkemas hanya saat dibutuhkan.
02Bagaimana cara upload skill ke OpenAI?
Kirim POST ke /v1/skills dalam bentuk multipart, dengan satu form part per file dan path relatif di filename, atau sebagai satu file zip. Batas yang terdokumentasi adalah 50 MB untuk zip, 25 MB setelah diekstrak, dan 500 file per versi, dengan tepat satu SKILL.md per bundle. Versi baru dari skill yang sudah ada dikirim ke endpoint versions milik skill tersebut.
03Apa bedanya default_version dan latest_version?
latest_version selalu menunjuk upload terbaru, sedangkan default_version adalah versi yang didapat request yang tidak menyebut versi, dan hanya berubah saat Anda mengaturnya secara eksplisit. Dengan begitu versi baru bisa di-upload dan dievaluasi tanpa memengaruhi production. OpenAI menyarankan pin versi secara eksplisit di request production.
04Apa saja isi container hosted shell OpenAI?
OpenAI mendokumentasikan hosted shell saat ini berbasis Debian 12 dengan Python 3.11, Node.js 22.16, Java 17, PHP 8.2, Ruby 3.1, dan Go 1.23, dan menyebut basisnya bisa berubah. Perintah berjalan tanpa sudo dan tanpa TTY interaktif, dan /mnt/data adalah lokasi resmi untuk output yang bisa diunduh. Akses network keluar mati kecuali Anda mengaktifkannya.
05Bagaimana domain_secrets menjauhkan API key dari model?
Setiap entri domain_secrets di network_policy mengikat nama dan nilai secret ke satu domain yang diizinkan. Model dan container hanya melihat placeholder seperti $API_KEY, dan auth-translation sidecar milik OpenAI menyisipkan nilai asli hanya pada request ke domain itu. Ini melindungi credential-nya, tetapi Anda tetap butuh allowlist yang ketat dan approval karena konten yang diambil bisa membawa prompt injection.
OpenAI Skills API dan Hosted Shell: SKILL.md di Container
Cara kerja OpenAI Skills API dari awal sampai akhir: upload bundle SKILL.md ke /v1/skills, beri versi dan pin, mount ke container hosted shell, dan jauhkan credential dari model dengan allowlist dan domain_secrets.
OpenAI Skills API menyimpan bundle SKILL.md berversi di /v1/skills dan me-mount-nya ke hosted shell milik Responses API, yaitu container Debian 12 yang secara default tidak punya akses network. Pin versi skill di production, perlakukan perubahan default_version seperti deploy, dan kirim API key lewat domain_secrets supaya model hanya melihat placeholder.
OpenAI Skills API menjawab pertanyaan yang terus muncul di pekerjaan laporan ERP: di mana sebuah prosedur panjang yang berulang seharusnya disimpan? Laporan aging piutang bukan satu tool call. Isinya script, aturan pengelompokan, format output, dan daftar hal yang tidak boleh dilakukan. Menempelkan semua itu ke system prompt di setiap request itu boros dan mustahil diberi versi. Jawaban OpenAI, yang masuk ke Responses API pada Februari 2026, adalah skill: sebuah folder dengan file SKILL.md, di-upload sekali, diberi versi di server, lalu di-mount ke container tempat model bisa menjalankan perintah.
Tulisan ini menelusuri seluruh alurnya dengan dokumentasi resmi OpenAI sebagai acuan: format manifest, endpoint upload beserta batasnya, hubungan default_version dan latest_version, empat cara skill sampai ke runtime, isi sebenarnya dari hosted shell, dan kontrol network yang menentukan apakah skill boleh memanggil ERP Anda tanpa model pernah memegang key-nya. Skill di Claude Code memakai format file yang sama, dan post lain tentang Agents API membahas sisi session; tulisan ini fokus pada model upload, versioning, dan container.
Apa itu skill OpenAI: folder dengan manifest SKILL.md
Skill adalah direktori dengan tepat satu SKILL.md di root-nya. File itu diawali YAML front matter berisi dua field wajib, name dan description, lalu diikuti instruksi dalam Markdown. File pendukung diletakkan di sebelahnya sesuai konvensi: scripts untuk aksi berulang, references untuk materi latar, assets untuk template dan data contoh. OpenAI mengikuti spesifikasi terbuka Agent Skills, yang membatasi name maksimal 64 karakter berupa huruf kecil, angka, dan tanda hubung tunggal, mewajibkannya sama dengan nama folder, serta mengizinkan description hingga 1024 karakter.
ar-aging-report/
├── SKILL.md
├── requirements.txt
├── scripts/
│ └── aging.py
└── references/
└── bucket-rules.md
# ar-aging-report/SKILL.md
---
name: ar-aging-report
description: Build an accounts-receivable aging report (0-30, 31-60, 61-90, 90+ days)
from an exported invoices CSV. Use when the user uploads open invoices and asks
for aging, overdue buckets or collection priorities. Do not use for AP or GL data.
---
# AR aging report
## How to run
python scripts/aging.py --input /mnt/data/invoices.csv --outdir /mnt/data/aging
## Rules
- Bucket by due_date, not invoice_date. See references/bucket-rules.md.
- If a dependency is missing, report it. Do not attempt a network install.
## Outputs
- /mnt/data/aging/report.md
- /mnt/data/aging/aging.csv
Bagian yang paling menentukan adalah description. Saat skill dipasang, platform menambahkan name, description, dan path setiap skill ke konteks prompt, dan model baru membaca SKILL.md lengkap lewat path itu setelah memutuskan skill tersebut relevan. Jadi description harus menjelaskan kapan skill dipakai dan, sama pentingnya, kapan tidak dipakai. Cookbook OpenAI menyarankan contoh negatif justru karena alasan ini, dan spesifikasinya menyarankan isi SKILL.md di bawah 500 baris, dengan detail dipindah ke file reference yang dibuka model saat dibutuhkan.
Upload ke /v1/skills: multipart atau zip
Ada dua bentuk upload untuk POST /v1/skills. Multipart mengirim satu form part per file, dengan field filename berisi path relatif supaya struktur folder tetap utuh. Zip mengirim seluruh bundle sebagai satu part. Saya akan memakai zip di CI, karena hasilnya satu artifact yang bisa di-hash, disimpan, dan di-upload ulang, dan karena batas ukuran yang terdokumentasi memang dinyatakan untuk bentuk ini.
# Option 1: multipart, one part per file. The filename= carries the folder path,
# so the bundle keeps its layout on the server.
curl --fail-with-body 'https://api.openai.com/v1/skills' \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-F 'files[]=@./ar-aging-report/SKILL.md;filename=ar-aging-report/SKILL.md;type=text/markdown' \
-F 'files[]=@./ar-aging-report/scripts/aging.py;filename=ar-aging-report/scripts/aging.py;type=text/plain'
# Option 2: one zip. Easier in CI, and it is what the 50 MB limit applies to.
(cd ar-aging-report/.. && zip -r ar-aging-report.zip ar-aging-report)
curl -X POST 'https://api.openai.com/v1/skills' \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-F 'files=@./ar-aging-report.zip;type=application/zip'
# The response carries the skill id plus default_version and latest_version.
# Store the id in config; it is what every request references.
Ukuran zip maksimal: 50 MB.
Ukuran maksimal setelah diekstrak: 25 MB.
Jumlah file maksimal per versi: 500, dengan tepat satu SKILL.md per bundle, dicocokkan tanpa membedakan huruf besar-kecil.
Batas 25 MB setelah diekstrak inilah yang paling sering menjebak. Skill yang menyertakan Python wheel atau dataset contoh bisa jauh di bawah 50 MB saat di-zip tetapi tetap gagal. Jangan masukkan input besar ke bundle; kirim sebagai file saat request. Skill seharusnya menjelaskan cara memproses data, bukan membawa datanya.
Versioning: default_version, latest_version, dan pinning
Setiap skill punya dua pointer. latest_version selalu mengikuti upload terbaru, yang dibuat lewat POST /v1/skills/skill_id/versions. default_version adalah versi yang didapat request yang tidak menyebut versi, dan hanya berubah kalau Anda mengubahnya secara eksplisit di skill. Pemisahan inilah inti model rilisnya: upload sebebasnya, evaluasi versi baru dengan pin di request uji, lalu promosikan.
# Ship a new version. This moves latest_version, NOT default_version.
curl -X POST "https://api.openai.com/v1/skills/$SKILL_ID/versions" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-F 'files=@./ar-aging-report.zip;type=application/zip'
# Promote it once the eval run passes. Requests that omit "version"
# switch over at this moment, so treat it like a deploy.
curl -X POST "https://api.openai.com/v1/skills/$SKILL_ID" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{"default_version": 3}'
# Rollback is the same call with the old number: {"default_version": 2}
skill_reference menerima versi berupa integer atau string latest. Cookbook OpenAI menyarankan pin versi di production alih-alih mengikuti latest, serta pin model bersama skill agar sebuah run bisa direproduksi. Saya akan melangkah lebih jauh: perlakukan perubahan default_version sebagai deploy yang dicatat di changelog, karena setiap pemanggil yang tidak menyebut versi langsung berubah perilakunya saat itu juga, tanpa ada perubahan code yang bisa ditunjuk.
Ada dua aturan penghapusan yang perlu diketahui sebelum Anda menulis script cleanup. Versi yang sedang menjadi default_version tidak bisa dihapus sebelum default_version dipindah ke versi lain, dan menghapus versi terakhir yang tersisa akan menghapus skill itu sendiri, termasuk id-nya. Loop yang asal memangkas versi lama bisa ikut menghapus skill_id production.
Empat cara skill sampai ke runtime
Upload sebenarnya opsional. Bundle SKILL.md yang sama bisa sampai ke model lewat empat jalur, dan pilihan itu menentukan di mana file berada dan berapa lama bertahan.
Jalur
Cara skill dipasang
Masa hidup
Pakai saat
Hosted shell, container_auto
skill_reference dengan skill_id dan version opsional di tools.environment.skills
Container baru per request, disediakan OpenAI
Job sekali jalan, misalnya membuat laporan dari file upload
Hosted shell, container_reference
Skill di-mount ke container yang dibuat lewat Containers API, lalu dirujuk dengan container_id
Sampai dihapus atau kedaluwarsa, 20 menit setelah aktivitas terakhir di contoh dokumentasi
Pekerjaan multi-turn yang butuh file dan package terpasang tetap ada
Bundle inline
Zip berenkode base64 di request, type inline, dengan name dan description
Hanya container tempat ia dikirim
Prototyping, atau skill yang dibuat per tenant dan tidak ingin disimpan
Local shell
name, description, dan path di filesystem; skill_reference tidak didukung
Mesin atau server Anda sendiri
Data yang wajib tetap on-premise; Anda sendiri yang menjalankan perintahnya
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
tools=[
{
"type": "shell",
"environment": {
"type": "container_auto",
"skills": [
# Wrong in production: no version means default_version,
# which someone can move without touching this code.
# {"type": "skill_reference", "skill_id": AGING_SKILL_ID},
# Right: pin the version you evaluated.
{"type": "skill_reference", "skill_id": AGING_SKILL_ID, "version": 3},
# A curated first-party skill, referenced by its id.
{"type": "skill_reference", "skill_id": "openai-spreadsheets", "version": "latest"},
],
},
}
],
input="Build the AR aging report from the attached invoices export.",
)
print(response.output_text)
Baris yang di-comment di request itu adalah kesalahan yang paling mungkin dibuat tim di awal: berjalan normal, lolos review, lalu diam-diam mengikuti apa pun default_version bulan depan. Skill kurasi openai-spreadsheets menunjukkan sisi lain dari trade-off ini, di mana mengikuti latest masuk akal karena OpenAI yang memeliharanya dan bukan Anda yang mengevaluasi setiap rilis. Ingat juga bahwa skill di hosted shell dibuang saat container kedaluwarsa, jadi apa pun yang di-mount bukan penyimpanan permanen.
Isi container hosted shell
OpenAI mendokumentasikan runtime hosted saat ini berbasis Debian 12, dengan Python 3.11, Node.js 22.16, Java 17, PHP 8.2, Ruby 3.1, dan Go 1.23 terpasang, dan menyebut basisnya bisa berubah seiring waktu. Perintah berjalan tanpa sudo dan tanpa TTY interaktif, jadi script skill yang meminta input atau memasang package sistem akan macet atau gagal. /mnt/data selalu tersedia dan merupakan lokasi resmi untuk apa pun yang perlu bisa diunduh pengguna; artifact di sana diambil dengan container files API yang sama dengan code interpreter.
// What the model emits: a batch of commands with its own limits.
{
"type": "shell_call",
"call_id": "call_9d14...",
"action": {
"commands": ["python scripts/aging.py --input /mnt/data/invoices.csv --outdir /mnt/data/aging"],
"timeout_ms": 120000,
"max_output_length": 4096
},
"status": "in_progress"
}
// What comes back. Log the exit code, not just stdout: a non-zero exit
// with a confident final answer is the failure you want an alert on.
{
"type": "shell_call_output",
"call_id": "call_9d14...",
"output": [
{ "stdout": "...", "stderr": "...", "outcome": { "type": "exit", "exit_code": 0 } }
]
}
Setiap shell_call bisa membawa beberapa perintah dengan timeout_ms dan max_output_length per call, dan setiap hasil melaporkan stdout, stderr, serta outcome exit. Saat membuat container sendiri, Anda juga menentukan memory_limit dan expires_after, misalnya satu gigabyte dan 20 menit sejak last_active_at, dan Anda bisa melakukan DELETE begitu job selesai alih-alih membayar waktu idle. Shell tool hanya tersedia lewat Responses API, tidak lewat Chat Completions.
Tulis script skill agar mencetak satu baris ringkasan terstruktur saat sukses dan error yang jelas saat gagal. Output dipotong di max_output_length, dan model memutuskan langkah berikutnya dari bagian yang tersisa. Script yang menumpahkan sepuluh ribu baris ke stdout tidak mengajari model apa pun dan membuang satu turn.
Allowlist network dan domain_secrets
Container hosted secara default tidak punya akses network keluar. Untuk mengaktifkannya ada dua lapis: admin mengatur allowlist organisasi di dashboard, lalu setiap request menambahkan network_policy bertipe allowlist di dalam environment shell. Daftar di request hanya bisa mempersempit daftar organisasi; request yang menyebut domain di luar daftar itu akan gagal, bukan diam-diam memperluas akses, dan memang begitulah perilaku yang diharapkan dari sebuah kontrol keamanan.
"tools": [
{
"type": "shell",
"environment": {
"type": "container_auto",
"network_policy": {
"type": "allowlist",
// Must be a subset of the org allowlist set in the dashboard,
// or the request fails rather than silently widening access.
"allowed_domains": ["pypi.org", "files.pythonhosted.org", "erp.example.co.id"],
"domain_secrets": [
{
"domain": "erp.example.co.id",
"name": "ERP_API_KEY",
// The model and the container only ever see $ERP_API_KEY.
// The real value is applied by OpenAI's auth sidecar on the way
// out, and only for this domain.
"value": "<read from your secret manager at request time>"
}
]
}
}
}
]
domain_secrets membuat skill yang memanggil API privat jadi bisa diterima. Setiap entri mengikat name dan value ke satu domain. Model dan runtime hanya melihat placeholder, misalnya $ERP_API_KEY, dan auth-translation sidecar milik OpenAI mengganti dengan nilai asli hanya pada request ke tujuan yang disetujui. Script skill tetap bisa autentikasi ke ERP Anda, sementara model yang terkena prompt injection lalu mencetak environment-nya hanya mencetak placeholder, dan request ke host lain tidak membawa apa pun yang berguna.
domain_secrets melindungi key, bukan data. Panduan OpenAI memperingatkan bahwa konten apa pun yang diambil lewat network bisa berisi instruksi tersembunyi yang ditujukan ke model, dan panduan skill-nya menyebut prompt injection dan exfiltration data sebagai risiko utama. Batasi allowlist hanya ke domain yang benar-benar dibutuhkan skill, utamakan credential read-only, dan pasang approval eksplisit untuk aksi tulis.
Skill di Agents API: capability directories
Agents API menemukan skill dengan cara berbeda. Alih-alih daftar referensi, environment sebuah session menyebut capability_directories, lalu platform mencari file SKILL.md di dalamnya. Path harus absolut tanpa segmen titik, harus sudah ada di sandbox, dan satu session bisa mendaftarkan paling banyak 32 direktori.
{
"environment": {
"type": "self_hosted",
"workspace_directory": "/workspace",
"capability_directories": [
"/workspace/capabilities/finance",
"/workspace/capabilities/inventory"
]
}
}
// Rules the platform enforces:
// absolute paths only, no "." or ".." segments
// at most 32 directories per session
// each directory must already exist in the sandbox
// Any SKILL.md found under them is registered as a skill.
Batas itu memengaruhi cara Anda menata skill. Tiga puluh dua direktori sangat cukup kalau setiap direktori adalah satu domain, misalnya finance, inventory, atau engineering, yang berisi beberapa skill, tetapi sempit kalau setiap skill punya direktori top-level sendiri. Pengelompokan per domain juga memberi unit permission yang alami: session agent sales cukup tidak me-mount direktori finance, sehingga skill-skill itu sama sekali tidak muncul di prompt-nya.
Kapan skill adalah alat yang tepat, dan cara merilisnya dengan aman
Cookbook OpenAI menarik garisnya dengan jelas: system prompt berisi perilaku global yang selalu aktif; tool melakukan sesuatu di dunia nyata dengan side effect; skill adalah prosedur terkemas yang dipanggil model secara kondisional, dengan script dan template yang dijalankan di sandbox. Laporan aging piutang, checklist rekonsiliasi akhir bulan, atau format export standar adalah skill. Posting jurnal adalah tool, dengan approval gate sendiri. Urutan rilis skill baru versi saya:
Validasi bundle secara lokal dengan validator skills-ref dari proyek Agent Skills, supaya name yang salah atau field front matter yang hilang gagal di CI, bukan di production.
Upload sebagai versi baru dan jalankan eval set terhadap nomor versi itu yang di-pin secara eksplisit, sementara production tetap di default lama.
Promosikan default_version, catat nomornya di changelog, dan perbarui pemanggil yang di-pin secara sengaja.
Tinjau network_policy dan domain_secrets untuk request skill tersebut, dan jaga keduanya seminimal yang dibutuhkan script.
Format SKILL.md yang sama dipakai oleh OpenAI, Claude Code, dan tool lain yang mengikuti spesifikasi Agent Skills, jadi folder skill yang ditulis dengan baik bersifat portabel. Yang tidak ikut pindah adalah infrastruktur di sekitarnya: skill id, pointer versi, mode container, dan network policy adalah fitur khusus API OpenAI.
Model mental yang berguna: skill adalah artifact yang di-deploy, bukan prompt. Ia punya id, versi bernomor, langkah promosi, runtime dengan bahasa dan batas yang diketahui, serta batas network yang Anda konfigurasi. Perlakukan dengan disiplin yang sama seperti deploy lainnya: pin apa yang sudah diuji, promosikan dengan sengaja, dan jangan pernah biarkan credential sampai ke model sebagai teks.