Jawaban singkat: Developer API OngkirHub adalah REST API di api.ongkirhub.com untuk cek tarif, membuat pengiriman, dan melacak resi dari sistem tokomu. Buat kunci test di menu Developer, minta tarif lewat POST /v1/rates, buat pengiriman dengan POST /v1/shipments plus header Idempotency-Key, lalu lacak resi. Setelah alurnya stabil, ganti ke kunci live.

Panduan ini untuk developer toko online, agensi web, atau pembuat sistem kasir yang ingin menghubungkan API ongkir, pembuatan resi, dan pelacakan ke aplikasinya sendiri. Kita mulai dari nol sampai resi pertama di mode test, memakai Developer API OngkirHub yang tersedia sejak OngkirHub 1.0 diluncurkan pada 6 Oktober 2026. Semua contoh memakai kunci placeholder dan data alamat contoh.

Yang kamu butuhkan

Kamu cukup butuh akun OngkirHub App, satu kunci test, dan alat untuk mengirim permintaan HTTP seperti curl.

  • Akun OngkirHub App dengan akses ke menu Developer.
  • Kunci test berformat ohk_test_ diikuti 32 karakter heksadesimal. Teks kunci hanya tampil sekali saat dibuat, jadi langsung simpan di variabel lingkungan server, bukan di kode sumber.
  • Base URL https://api.ongkirhub.com/v1.
  • Referensi API di Docs. Referensi ini disusun dari dokumen OpenAPI 3.1 dan menjadi acuan resmi untuk setiap endpoint, parameter, dan field.

Setiap kunci punya scope, misalnya rates:read untuk cek tarif dan shipments:write untuk membuat serta membatalkan pengiriman. Daftar lengkapnya: locations:read, couriers:read, rates:read, shipments:read, shipments:write, tracking:read, dan wallet:read. Bila scope kunci tidak mencakup endpoint yang dipanggil, API menjawab dengan error forbidden (403).

Langkah integrasi

Alur dasarnya adalah: cari lokasi, minta tarif, buat pengiriman, lalu pantau resinya.

  1. Buat kunci test di menu Developer dan simpan sebagai variabel lingkungan.
  2. Uji kunci dengan memanggil GET /v1/couriers untuk melihat daftar kurir.
  3. Cari data lokasi asal dan tujuan dengan GET /v1/locations, sampai tingkat kelurahan dan kode pos.
  4. Minta tarif dengan POST /v1/rates, berisi pengirim, penerima, barang, jenis serah terima, dan pilihan asuransi.
  5. Pilih satu tarif, lalu buat pengiriman dengan POST /v1/shipments dan header Idempotency-Key.
  6. Ambil detail lewat GET /v1/shipments/{id} sampai field awb terisi.
  7. Lacak resi dengan GET /v1/tracking/{awb}.

Field status pada pengiriman bergerak dari submitting ke booked, awb_ready, picked_up, in_transit, out_for_delivery, sampai delivered. Di luar jalur utama ada failed bila pemesanan ditolak sebelum resi terbit, unknown bila hasil pemesanan ke kurir sedang dicek ulang oleh sistem (berakhir sebagai booked atau failed), problem bila pelacakan menunjukkan kendala, returned, cancel_requested, cancelled, dan expired. Petakan semua status ini ke status pesanan di sistemmu sejak awal, termasuk jalur sampingnya, supaya tidak ada pesanan yang menggantung tanpa keterangan.

Respons POST /v1/rates memuat id penawaran, expiresAt, dan daftar rates. Setiap tarif berisi rateCode, nama kurir dan layanan, etd, serta rincian price. Penawaran berlaku sekitar 30 menit, jadi selalu baca expiresAt dari respons, jangan menebak sendiri.

Contoh permintaan ke API OngkirHub

Contoh berikut memakai curl. Mulai dengan memeriksa kunci.

# Simpan kunci di variabel lingkungan, jangan di kode sumber
export ONGKIRHUB_KEY="ohk_test_xxx"

curl https://api.ongkirhub.com/v1/couriers \
  -H "Authorization: Bearer $ONGKIRHUB_KEY"

Lalu minta tarif. Isi objek location persis seperti hasil GET /v1/locations, karena nama provinsi, kota, kecamatan, dan kelurahan harus cocok dengan data lokasi OngkirHub. Berat ditulis per unit barang dalam kilogram, dan nilai barang dalam rupiah bulat.

curl https://api.ongkirhub.com/v1/rates \
  -H "Authorization: Bearer $ONGKIRHUB_KEY" \
  -H "Content-Type: application/json" \
  -d @rate.json
{
  "sender": {
    "name": "Toko Contoh",
    "phone": "081200000001",
    "email": "toko@example.com",
    "address": "Jl. Contoh Raya No. 1, RT 01 RW 02",
    "location": {
      "province": "DKI Jakarta",
      "city": "Jakarta Selatan",
      "district": "Tebet",
      "subdistrict": "Tebet Barat",
      "zipcode": "12810"
    }
  },
  "receiver": {
    "name": "Pembeli Contoh",
    "phone": "081200000002",
    "address": "Jl. Contoh Dalam No. 2, RT 03 RW 04",
    "location": {
      "province": "Jawa Barat",
      "city": "Bandung",
      "district": "Coblong",
      "subdistrict": "Dago",
      "zipcode": "40135"
    }
  },
  "items": [
    { "name": "Kaos katun", "qty": 2, "weightKg": 0.25, "value": 75000 }
  ],
  "pickupType": "DROP",
  "insured": false
}

Contoh ini memakai DROP, artinya paket diantar ke gerai. Untuk PICKUP, permintaan pembuatan pengiriman wajib menyertakan pickupTime, yaitu waktu mulai slot penjemputan dalam format ISO 8601 dengan offset zona waktu, dan waktunya tidak boleh sudah lewat. Cara mendapatkan slot yang tersedia mengikuti referensi API.

Setelah memilih tarif, buat pengiriman. shipment.json berisi seluruh field rate.json di atas (pengirim, penerima, barang, pickupType, insured) ditambah tiga field berikut: id penawaran, kode tarif yang dipilih, dan nomor pesananmu sendiri. Potongan di bawah hanya menampilkan tiga field tambahannya.

curl https://api.ongkirhub.com/v1/shipments \
  -H "Authorization: Bearer $ONGKIRHUB_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pesanan-1042" \
  -d @shipment.json
{
  "quoteId": "ID_PENAWARAN_DARI_V1_RATES",
  "rateCode": "RATE_CODE_YANG_DIPILIH",
  "externalRef": "INV-1042"
}

Field externalRef menyimpan nomor pesanan dari sistemmu, maksimal 64 karakter berupa huruf, angka, titik, garis miring, garis bawah, atau tanda hubung. Dengan begitu, setiap pengiriman mudah dicocokkan kembali ke pesanan asalnya saat rekap.

Respons awal langsung memuat id pengiriman berawalan OH dan status submitting. Pemesanan ke kurir selesai beberapa saat kemudian, jadi awb bisa masih null. Ambil detailnya, lalu lacak setelah resi terbit.

curl https://api.ongkirhub.com/v1/shipments/OHxxxxxxxx \
  -H "Authorization: Bearer $ONGKIRHUB_KEY"

curl https://api.ongkirhub.com/v1/tracking/NOMORRESIKAMU \
  -H "Authorization: Bearer $ONGKIRHUB_KEY"

Cakupan pelacakan sama dengan cek resi OngkirHub: resi J&T Express dan AnterAja dari mana saja, JNE dengan 5 digit terakhir nomor HP penerima, serta resi yang dibuat lewat OngkirHub untuk kurir lain. Status di endpoint pelacakan diperbarui berkala mengikuti data kurir. Jangan memanggil endpoint detail atau pelacakan terlalu rapat dalam satu putaran. Bila API menjawab rate_limited, beri jeda sebelum mencoba lagi.

Menangani error dan idempotensi

Semua error memakai amplop JSON yang sama, berisi code, message, dan request_id. Simpan request_id di log karena nilai itulah yang diminta tim saat kamu melaporkan masalah.

{
  "error": {
    "code": "quote_expired",
    "message": "Harga ongkir sudah diperbarui. Cek ongkir lagi sebelum membuat resi.",
    "request_id": "..."
  }
}

Tabel berikut merangkum kode yang paling sering kamu temui di alur cek tarif sampai resi.

Kode Status HTTP Artinya Yang perlu dilakukan
validation_error 422 Ada field yang kosong atau formatnya salah Perbaiki isian, lalu kirim ulang
unauthenticated 401 Kunci tidak ada atau tidak valid Periksa header Authorization
forbidden 403 Scope kunci tidak mencakup endpoint ini Gunakan kunci dengan scope yang sesuai
not_found 404 Data tidak ditemukan Periksa id yang dipakai
quote_expired 409 Penawaran kedaluwarsa atau harganya berubah Minta tarif lagi
idempotency_conflict 409 Idempotency-Key sudah dipakai untuk isi berbeda, atau penawaran itu sudah dipakai untuk pengiriman lain Pakai kunci baru dan minta tarif baru untuk pesanan baru
no_rates_available 422 Tidak ada layanan untuk rute dan paket ini Ubah alamat, berat, atau jenis serah terima
wallet_insufficient_funds 402 Saldo tidak cukup Isi saldo, lalu ulangi
cancellation_not_allowed 409 Pengiriman sudah tidak bisa dibatalkan Cek status pengiriman
rate_limited 429 Terlalu banyak permintaan Tunggu sebentar, lalu coba lagi

Idempotensi berarti permintaan yang sama boleh dikirim ulang tanpa membuat pengiriman ganda. Header Idempotency-Key berisi 1 sampai 100 karakter dari huruf, angka, titik, garis bawah, titik dua, atau tanda hubung. Nomor pesanan dari tokomu biasanya pilihan yang paling mudah.

Kalau koneksi putus sebelum respons diterima, kirim ulang POST /v1/shipments dengan kunci dan isi yang sama persis. API akan mengembalikan pengiriman yang sudah dibuat, bukan membuat yang baru. Kunci yang sama dengan isi berbeda, atau kunci yang sama dipakai lintas mode test dan live, dijawab dengan idempotency_conflict.

Satu hal lagi soal harga: isi pengirim, penerima, barang, pickupType, dan insured di shipment.json harus sama dengan saat meminta tarif. Bila ada yang berubah, atau penawaran sudah lebih dari 10 menit, harga dicek ulang. Kalau hasilnya berbeda dari harga yang kamu pilih, permintaan ditolak dengan quote_expired supaya kamu tidak pernah ditagih harga yang belum kamu setujui.

Uji dengan kunci test

Kunci test tidak membuat pengiriman sungguhan, jadi kamu bebas mencoba alur dari awal sampai akhir. Gunakan mode ini untuk menguji kasus gagal juga, bukan hanya jalur mulus.

  • Penawaran yang dibuat dengan kunci test hanya bisa dipakai dengan kunci test, begitu pula sebaliknya.
  • Event webhook dari pengiriman test membawa livemode bernilai false, sehingga mudah dipisahkan dari data produksi.
  • Coba sengaja kirim alamat yang tidak lengkap, Idempotency-Key yang sama dengan isi berbeda, dan penawaran yang sudah kedaluwarsa untuk memastikan penanganan error di sistemmu berjalan.

Setelah alurnya stabil, buat kunci live di menu Developer, ganti variabel lingkungan, dan jalankan satu pengiriman nyata berukuran kecil. Resi dari kunci live dibayar dari saldo OngkirHub, jadi pastikan saldo cukup sebelum mulai. Cabut kunci yang tidak lagi dipakai dari menu yang sama.

Langkah berikutnya

Setelah resi pertama berhasil, ada tiga hal yang biasanya dipasang berikutnya.

  • Webhook. Daripada memanggil endpoint detail berulang kali, terima kabar otomatis saat resi terbit atau status berubah. Konsepnya dijelaskan di apa itu webhook status pengiriman, dan panduan teknisnya ada di Docs.
  • Saldo dan mutasi. GET /v1/wallet menampilkan saldo, sedangkan GET /v1/wallet/transactions menampilkan mutasinya untuk rekonsiliasi harian.
  • Pembatalan. POST /v1/shipments/{id}/cancel mengajukan pembatalan dengan alasan, hanya untuk status booked, awb_ready, atau problem. Pengajuan diproses tim bersama kurir dan tidak instan.

Ringkasan fitur integrasi untuk tim non-teknis tersedia di halaman Developer.