AI
Tutorial OpenAI Agents API: Hosted Agent dan Sandbox
Oktober 202612 menit baca

Ini adalah layanan terkelola, public beta sejak 10 September 2026, yang memberi aplikasi Anda akses ke Codex harness lewat API. OpenAI mengelola session, orkestrasi, context compaction, dan recovery, sementara Anda menyediakan tool dan memilih di mana agent berjalan. Anda bekerja dengan empat objek: agent, environment opsional, session yang durable, serta event dan item-nya.
Tidak ada biaya platform terpisah. Anda membayar tarif token model yang dipilih, tarif standar untuk tool OpenAI, dan tarif container untuk sandbox OpenAI-hosted. Halaman pricing mencantumkan container 0,03 USD untuk 1 GB, 0,12 USD untuk 4 GB, dan 0,48 USD untuk 16 GB per session 20 menit.
Kirim event agent.session.input.message ke session yang sama. Jika ada turn aktif, pesan itu melakukan steering pada turn tersebut; jika session idle, pesan itu memulai turn baru dengan percakapan yang ada. Untuk menghentikan pekerjaan, kirim agent.session.input.cancel, yang tetap mempertahankan session dan pekerjaan sebelumnya.
Tidak. Idle hanya berarti session siap menerima input berikutnya. Cek agent.session.turn.completed, turn.failed, atau turn.cancelled pada root turn, dan periksa output agent, karena turn yang completed masih bisa berisi tool call yang gagal.
Tidak. Agents API saat ini hanya mendukung data residency di Amerika Serikat dan tidak mendukung Zero Data Retention. Memilih sandbox self-hosted tidak mengubah hal itu, karena harness dan state session tetap berjalan di sisi OpenAI.

Ringkasan Utama
OpenAI Agents API, public beta sejak 10 September 2026, menjalankan Codex harness untuk Anda: buat session berisi agent dan environment, kirim task, pantau lewat event atau webhook, lakukan steering atau cancel di tengah turn, lalu unduh artifact dan hapus session. Tidak ada biaya platform, tetapi data residency hanya di AS dan Zero Data Retention tidak didukung.
Pekerjaan yang saya bayangkan membosankan tapi nyata. Setiap tutup buku, seseorang di klien ERP mengekspor invoice per cabang, menjumlahkannya di spreadsheet, lalu mengejar baris yang tidak cocok. Ini persis jenis task yang seharusnya bisa diselesaikan agent dengan sandbox Python tanpa diawasi, dan sampai September 2026, melakukannya di OpenAI berarti saya harus menulis dan meng-host agent loop sendiri.
Tutorial OpenAI Agents API ini menelusuri seluruh siklus hidup session dalam TypeScript, hanya memakai field yang terdokumentasi di panduan resmi OpenAI: membuat session di sandbox OpenAI-hosted, streaming turn dengan benar, steering dan cancel, menangani webhook dan hasil function, memilih sandbox hosted atau self-hosted, serta cleanup. Di akhir ada dua batasan penanganan data yang wajib dicek sebelum siapa pun membawanya ke production.
Agents API adalah layanan terkelola di sekitar Codex harness yang open source. Overview OpenAI menyebutkan bahwa layanan ini mengelola session, orkestrasi, context compaction, dan recovery, sementara aplikasi Anda menyediakan tool dan memilih environment eksekusi. Desainnya bertumpu pada empat objek:
Apa yang ditambahkan harness dibanding loop buatan sendiri tertulis jelas: menjalankan perintah di sandbox, menerapkan skill, terhubung ke tool atau MCP, steering saat agent bekerja, merangkum pekerjaan sebelumnya agar muat di context window, mendelegasikan ke subagent, dan melanjutkan session dari titik terakhir. Yang tetap menjadi tanggung jawab Anda adalah semua hal yang berdampak bisnis: function handler yang menyentuh sistem Anda, kredensial, keputusan di mana kode berjalan, dan pengecekan apakah turn yang selesai benar-benar menyelesaikan pekerjaannya.
Fitur beta ini ada di namespace beta.agents pada SDK resmi. Request HTTP mentah memerlukan header OpenAI-Beta: agents=v1, yang ditambahkan otomatis oleh SDK. Siapkan semuanya dengan urutan berikut:
import OpenAI from "openai";
// Application key: api.agents.read + api.agents.write + api.responses.write.
// It stays in your backend. Nothing in the sandbox should ever see it.
const client = new OpenAI();
const session = await client.beta.agents.sessions.create({
agent: {
model: "gpt-6-astra",
instructions:
"You reconcile ERP exports. Use Python, show the figures you used, " +
"and write every result file to /workspace/outputs.",
},
environment: {
type: "openai_hosted",
container_size: "small", // 1 vCPU / 1 GB. Default is "medium" (2 vCPU / 4 GB)
network: { access: "disabled" }, // default is "enabled" — opt out unless the task needs it
packages: { python: ["pandas==2.2.3"] },
files: [
// Inline files: 5 MiB each, 10 MiB per request, measured before base64.
{ type: "inline", path: "/workspace/invoices.csv", data: invoicesCsvBase64 },
],
},
// No input yet: an OpenAI-hosted session may start empty. The task is sent
// after the event stream is open, so no early event is missed.
});
// Store this next to your own job record — it is the handle for everything else.
console.log(session.id, session.environment.id);Dua pilihan dalam request itu disengaja. Akses network di sandbox OpenAI-hosted default-nya enabled, jadi job yang hanya mengolah CSV sebaiknya memakai disabled, atau restricted dengan daftar allowed_domains berisi 1 sampai 100 nama host persis. Selain itu, session dibuat tanpa input: panduannya meminta Anda subscribe ke event stream sebelum mengirim pekerjaan, karena stream tidak memutar ulang event yang terlewat. Ukuran yang terdokumentasi adalah small dengan 1 vCPU dan 1 GB, medium dengan 2 vCPU dan 4 GB sebagai default, dan large dengan 4 vCPU dan 16 GB.
Cek setup sebelum memercayai sandbox. Panggilan create yang sukses hanya berarti setup sudah dimulai. Mengambil /v1/agents/environments/ diikuti environment.id milik session akan mengembalikan provisioning selama package dan setup_commands berjalan, dan connected setelah berhasil. Exit status nonzero dari setup command membuat agent sama sekali tidak dijalankan, sehingga setup command adalah tempat termurah untuk memastikan file atau package yang dibutuhkan memang ada.
Turn adalah satu siklus kerja di dalam session. Helper di bawah membuka stream, mengirim task, mencetak text delta, dan baru return ketika root turn selesai. Helper ini diadaptasi dari helper send-and-stream di panduan Events and items OpenAI, dan setiap case di switch-nya punya alasan yang dijelaskan di dokumentasi.
async function runTask(client: OpenAI, sessionId: string, text: string) {
// Subscribe BEFORE sending input — streams do not replay missed events.
const events = await client.beta.agents.sessions.events.stream(sessionId);
try {
await client.beta.agents.sessions.events.create(sessionId, {
events: [
{
type: "agent.session.input.message",
input: [{ role: "user", content: [{ type: "input_text", text }] }],
},
],
});
for await (const event of events) {
switch (event.type) {
case "agent.session.turn.output_text.delta":
process.stdout.write(event.delta); // deltas may be absent; .done carries full text
break;
case "agent.session.idle":
continue; // Wrong to treat as success: idle only means "ready for input"
case "error":
throw new Error(event.error.message);
case "agent.session.failed":
case "agent.session.environment.failed":
throw new Error(`Agent lifecycle failure: ${event.type}`);
case "agent.session.turn.failed":
case "agent.session.turn.cancelled":
if (event.turn.subagent_id === null) throw new Error(event.type);
break; // a subagent's turn failing does not end the root turn
case "agent.session.turn.completed":
if (event.turn.subagent_id === null) return; // still inspect tool results
break;
}
}
throw new Error("Stream closed before the turn ended. Retrieve saved items.");
} finally {
events.controller.abort(); // closing the stream does NOT cancel the turn
}
}
await runTask(
client,
session.id,
"Sum the amount column per branch in /workspace/invoices.csv and " +
"write /workspace/outputs/totals.json. Read it back to verify it.",
);Jebakan yang hampir pasti saya injak adalah agent.session.idle. Event ini muncul ketika session siap menerima input berikutnya, bukan ketika pekerjaan berhasil, sehingga kode yang menganggap idle sebagai selesai akan melaporkan rekonsiliasi yang gagal sebagai beres. Event hasilnya adalah turn.completed, turn.failed, dan turn.cancelled, dan bahkan turn yang completed tidak menjamin setiap tool call berhasil, jadi baca juga output agent. Dua detail lain penting di production. Turn event dari subagent membawa subagent_id dan tidak boleh mengakhiri loop. Dan ketika stream putus, event yang terlewat hilang: recovery dilakukan dengan membuka stream baru, mengambil session beserta item tersimpannya, lalu menggabungkan berdasarkan item_id.
Tidak ada endpoint steering terpisah. Event agent.session.input.message yang sama memulai turn baru ketika session idle, dan melakukan steering pada turn aktif ketika agent sedang bekerja, sehingga koreksi seperti mengecualikan cabang uji langsung diterima saat agent masih di tengah task. Cancel adalah input event berbeda di endpoint yang sama, dan menghentikan turn tanpa membuang session maupun pekerjaan sebelumnya.
// One event type does two jobs. Sent to an idle session it starts a new turn;
// sent while a turn is running it steers that turn.
async function sendMessage(client: OpenAI, sessionId: string, text: string) {
await client.beta.agents.sessions.events.create(sessionId, {
events: [
{
type: "agent.session.input.message",
input: [{ role: "user", content: [{ type: "input_text", text }] }],
},
],
});
}
// Mid-turn correction: the agent is already working on the wrong branch set.
await sendMessage(client, session.id, "Exclude branch JKT-99, it is a test branch.");
// Stop the current turn. The session and its previous work survive.
await client.beta.agents.sessions.events.create(session.id, {
events: [{ type: "agent.session.input.cancel" }],
});Sebagian setting bisa diubah pada session yang sedang berjalan, sebagian tidak. POST ke session itu sendiri bisa mengubah model, reasoning effort, atau service tier untuk turn yang dimulai setelah update, sementara turn aktif tetap memakai setting lamanya meskipun Anda melakukan steering. Instruksi, tool, dan multi_agent tetap selama umur session, jadi mengubah apa yang boleh dilakukan agent berarti membuat session baru. Sifat ini berguna ketika auditor bertanya apa saja yang diizinkan untuk agent pada satu run tertentu.
# Change model, reasoning effort or service tier for LATER turns of one session.
# instructions, tools and multi_agent cannot change here: create a new session.
curl -sS -X POST "https://api.openai.com/v1/agents/sessions/$SESSION_ID" \
-H "OpenAI-Beta: agents=v1" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "agent": { "reasoning": { "effort": "low" }, "service_tier": null } }'Job tutup buku tidak seharusnya menahan HTTP stream terbuka selama agent bekerja. Session webhook mencakup perubahan state, yaitu agent.session.created, action_required, in_progress, idle, dan failed. Webhook ini ditandatangani, jadi handler memverifikasi signature dengan webhook secret Anda sebelum mem-parse apa pun. Yang paling penting adalah action_required, yang muncul ketika session membutuhkan hasil function, koneksi environment, atau approval computer use.
import express from "express";
import OpenAI from "openai";
const app = express();
const client = new OpenAI({ webhookSecret: process.env.OPENAI_WEBHOOK_SECRET });
app.post("/webhooks/openai", express.raw({ type: "application/json" }), async (req, res) => {
const payload = req.body.toString("utf8");
try {
await client.webhooks.verifySignature(payload, req.headers);
} catch {
return res.status(400).send("Invalid signature");
}
const event = JSON.parse(payload);
res.sendStatus(200); // acknowledge fast; queue the slow part in production
// Webhook name: agent.session.action_required.
// The stream event for the same situation is agent.session.requires_action.
if (event.type !== "agent.session.action_required") return;
// The webhook omits call IDs and arguments — read them from the session.
const session = await client.beta.agents.sessions.retrieve(event.data.id);
for (const action of session.required_actions ?? []) {
if (action.type !== "function_call" || action.name !== "get_stock_level") continue;
const output = JSON.stringify(await getStockLevel(action.arguments));
await client.beta.agents.sessions.events.create(session.id, {
events: [{
type: "agent.session.input.tool_result",
turn_id: action.turn_id,
call_id: action.call_id,
success: true,
output, // a string; serialize objects yourself
}],
});
}
});
app.listen(Number(process.env.PORT ?? 8000));Dua detail di handler itu diambil langsung dari dokumentasi dan mudah terlewat. Webhook sengaja tidak menyertakan call ID dan argumen, jadi Anda mengambil session dan membaca required_actions alih-alih memercayai payload. Selain itu, webhook-nya bernama agent.session.action_required sementara stream event yang setara bernama agent.session.requires_action, perbedaan satu kata yang diam-diam merusak handler yang ditulis berdasarkan halaman yang salah. Untuk function yang punya side effect, misalnya posting jurnal, simpan setiap hasil berdasarkan session, turn, dan call ID, lalu kirim ulang hasil tersimpan setelah koneksi putus, bukan menjalankan function dua kali.
Tipe environment menentukan siapa yang menyediakan compute dan bagaimana file dikembalikan. Hanya sandbox OpenAI-hosted yang mempublikasikan file di /workspace/outputs sebagai artifact yang bisa diunduh. Environment self-hosted mengembalikan file lewat provider Anda sendiri, meskipun dari path yang sama.
| environment.type | Siapa yang menyediakan | Cara mengambil file | Pakai ketika |
|---|---|---|---|
| none | Tidak ada, karena tidak ada sandbox | Baca output dari session item | Agent hanya menjawab pertanyaan atau memanggil function dan tool eksternal Anda |
| openai_hosted | OpenAI, dengan ukuran, package, file, dan kebijakan network yang Anda atur | Artifacts API untuk file di /workspace/outputs, tetap bisa diunduh setelah sandbox kedaluwarsa | Anda butuh workspace Linux sekali pakai tanpa mengelola infrastruktur |
| self_hosted | Anda sendiri, di laptop, container, atau provider seperti Modal, E2B, atau Daytona | File API milik provider Anda atau filesystem yang di-mount | Anda butuh image sendiri, compute sendiri, atau private network |
Self-hosting bekerja dengan menjalankan Codex executor, codex exec-server, di dalam environment Anda. Executor mendaftar dengan environment ID dan environment key yang terbatas, lalu menjaga WebSocket keluar untuk menerima perintah, sehingga tidak ada port masuk yang dibuka. Environment key hanya bisa menghubungkan environment dan tidak bisa mengotorisasi aksi API lain, itulah sebabnya hanya kredensial OpenAI ini yang boleh ada di dalam sandbox.
# Inside YOUR sandbox (a container, a Modal/E2B/Daytona box, a laptop).
# CODEX_API_KEY is the restricted environment key: it can only connect environments.
# The application OPENAI_API_KEY never enters this machine.
export CODEX_API_KEY="$OPENAI_EXECUTOR_API_KEY"
# Outbound only: api.openai.com (register) + wss://codex-cloud-environments.chatgpt.com
codex exec-server \
--remote "<session.environment.remote_url>" \
--environment-id "<session.environment.id>"Pilihan sandbox saat peluncuran: selain sandbox OpenAI-hosted, pengumumannya mencantumkan integrasi kelas satu dengan Blaxel, Cloudflare, Daytona, DigitalOcean, E2B, Modal, Oracle Cloud, Runloop, dan Vercel, dan panduan self-hosted juga mendokumentasikan AWS Lambda MicroVMs. Sandbox OpenAI-hosted menawarkan CPU, GPU, dan memori yang bisa dikonfigurasi, baik fully managed maupun di-deploy ke VPC Anda sendiri.
Agents API sendiri tidak memungut biaya tambahan; pengumumannya menyebut Anda membayar token dan tool yang dipakai. Di halaman pricing OpenAI, gpt-6-astra, model yang dipakai di semua contoh Agents API, tercantum dengan tarif standar short-context 10 USD per sejuta input token, 1 USD per sejuta cached input token, dan 50 USD per sejuta output token. Baris containers mencantumkan 0,03 USD untuk 1 GB, 0,12 USD untuk 4 GB, dan 0,48 USD untuk 16 GB per session 20 menit, dengan session yang memenuhi syarat ditagih per menit dan minimum lima menit. Tingkatan memori itu sama dengan ukuran sandbox small, medium, dan large.
Cleanup punya urutan. Artifact adalah salinan immutable yang dipublikasikan ketika turn selesai, dan API mengunduh satu artifact per request tanpa endpoint batch, jadi lakukan loop atau minta agent meng-zip output-nya menjadi satu file. Unduh dulu yang Anda perlukan, baru hapus session. Respons 409 saat penghapusan berarti setup atau eksekusi masih berjalan, jadi retry dengan jumlah percobaan yang dibatasi.
# Python — copy outputs out BEFORE deleting the session.
def download_artifact(client, session_id, turn_id, path, destination):
for artifact in client.beta.agents.sessions.artifacts.list(session_id):
if artifact.turn_id != turn_id or artifact.path != path:
continue
with client.beta.agents.sessions.artifacts.with_streaming_response.content(
artifact.id, session_id=session_id
) as response:
response.stream_to_file(destination)
return
raise FileNotFoundError(f"No artifact for {path!r} in turn {turn_id}")
download_artifact(client, session_id, turn_id, "/workspace/outputs/totals.json", "totals.json")
# Then delete. A 409 means setup or execution is still finishing:
# wait and retry a bounded number of times.
client.beta.agents.sessions.delete(session_id)Jangan mengandalkan kedaluwarsa sebagai mekanisme cleanup. Sandbox yang terhubung menerima keep-alive, termasuk di antara turn, dan baru bisa dihapus setelah satu jam tanpa aktivitas maupun keep-alive, timeout yang tidak bisa dikonfigurasi. Menutup event stream juga tidak membatalkan task. Untuk session self-hosted peringatannya lebih tegas: menghapus session tidak memicu webhook dan tidak menghentikan compute di provider, jadi kode provisioning Anda yang bertanggung jawab mematikannya.
Overview menyatakan kedua batasan ini dengan jelas. Agents API saat ini hanya mendukung data residency di Amerika Serikat, dan tidak mendukung Zero Data Retention. Session memang menyimpan state, itulah yang membuat follow-up berfungsi, dan Anda bisa menghapus session serta artifact yang dipublikasikan setelah selesai, tetapi penghapusan adalah langkah cleanup, bukan jaminan retensi.
Memilih sandbox self-hosted tidak membuat Agents API memenuhi syarat ZDR. Menjalankan executor di hardware sendiri memang menjaga file dan perintah di mesin Anda, tetapi harness, percakapan, dan state session tetap berada di OpenAI di AS. Jika kontrak klien mewajibkan pemrosesan di dalam region atau zero retention, Agents API dalam beta saat ini adalah runtime yang salah, sandbox apa pun yang Anda pilih.
Untuk contoh tutup buku saya, ini membagi pekerjaan dengan rapi. Ekspor yang sudah diagregasi dan dianonimkan layak dijadikan pilot untuk hosted session dengan network dimatikan. Buku besar mentah yang memuat nama pelanggan, atau apa pun yang secara kontrak harus tetap di dalam region bagi klien Indonesia, tetap berada di runtime tempat saya mengendalikan loop dan storage-nya. Secret mengikuti logika yang sama: panduan hosted sandbox memperingatkan bahwa kode buatan agent bisa membaca nilai env, dan menyarankan vault credential untuk apa pun yang sensitif.
Agents API menghapus bagian rekayasa agent yang tidak ingin dipegang siapa pun, yaitu loop, compaction, recovery, dan plumbing subagent, lalu menyisakan bagian yang mengandung risiko untuk Anda. Perlakukan sebagai protokol session: buka stream sebelum mengirim pekerjaan, nilai keberhasilan dari hasil turn dan output, bukan dari idle, jauhkan application key dari sandbox, unduh artifact sebelum menghapus, dan cek residency serta retensi sebelum record nyata pertama masuk.
Sumber