Administrasi

Integrasi: token API dan webhook

Menghubungkan sistem lain: membuat dan membaca tiket lewat API, menerima peristiwa lewat webhook bertanda tangan.

Halaman Integrasi menghubungkan RANCAQ dengan sistem lain di organisasi Anda, misalnya alat monitoring, sistem HR, chat tim, atau otomasi seperti n8n. Halaman ini ada di Administrasi → Integrasi dan punya dua bagian:

  • Token API: sistem lain membuat dan membaca tiket lewat REST API, tanpa akun orang.
  • Webhook: RANCAQ mengirim peristiwa tiket ke alamat HTTPS milik Anda begitu terjadi.

Siapa yang dapat mengelola

Mengelola token dan webhook memerlukan izin Kelola integrasi. Secara bawaan izin ini hanya dimiliki Tenant Admin, karena token bisa membuka tiket ke sistem lain. Auditor dapat melihat daftar token, webhook, dan riwayat pengiriman tanpa mengubah apa pun. Lihat Pengguna, tim, dan peran.

Token API

Membuat token

  1. Buka Administrasi → Integrasi, tab Token API, lalu klik Token baru.
  2. Isi Nama yang menjelaskan pemakainya, mis. "Zabbix kantor pusat".
  3. Pilih Izin. Pilih sesempit mungkin:
IzinArtinya
Baca tiket buatan integrasi iniHanya tiket yang dibuat token ini sendiri.
Baca seluruh tiket workspaceSemua tiket, tanpa catatan internal. Pilih hanya bila sistem tujuan memang perlu melihat semua.
Buat tiketTiket baru berkanal API, dengan pelapor identitas integrasi.
Tulis komentar publikKomentar publik pada tiket yang bisa dibaca token. Butuh salah satu izin baca.
Buat atas nama pelapor lainIzin khusus. Tiket dicatat atas nama anggota aktif workspace ini, dan audit mencatat token serta pelapor yang diwakili. Butuh Buat tiket.
  1. Pilih Masa berlaku, lalu klik Buat token.
  2. Salin token yang muncul (rqa_…) ke pengelola rahasia sistem tujuan. Token hanya tampil sekali. Bila hilang, cabut lalu buat token baru.

Satu workspace dapat memiliki paling banyak 10 token aktif.

Identitas integrasi

Setiap token punya identitas sendiri bernama Integrasi: . Nama itulah yang tampil sebagai pelaku di linimasa tiket, komentar, dan Audit, sehingga Anda selalu tahu integrasi mana yang bertindak. Identitas ini:

  • tidak bisa masuk ke aplikasi dan tidak menerima email;
  • tidak tampil di daftar pengguna dan tidak dihitung sebagai kursi agen;
  • tidak bisa diberi peran atau dimasukkan ke tim.

Memakai token

Kirim token di header Authorization:

curl -s https://app.rancaq.com/api/v1/tickets?limit=20 \
  -H "Authorization: Bearer rqa_…"

Endpoint yang menerima token:

EndpointIzin
GET /api/v1/tickets dan GET /api/v1/tickets/{id}Baca
GET /api/v1/tickets/{id}/comments dan …/activitiesBaca
POST /api/v1/ticketsBuat tiket
POST /api/v1/tickets/{id}/commentsTulis komentar publik

Endpoint lain menolak token dengan 401.

Membuat tiket:

curl -s -X POST https://app.rancaq.com/api/v1/tickets \
  -H "Authorization: Bearer rqa_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: zabbix-event-48213" \
  -d '{"kind":"INCIDENT","subject":"Disk server DB 95%","description":"Dari Zabbix","urgency":"HIGH"}'
  • Idempotency-Key: kirim kunci unik per kejadian di sistem Anda. Permintaan ulang dengan kunci yang sama tidak membuat tiket kedua. Kunci berlaku per token, jadi dua integrasi yang kebetulan memakai kunci sama tidak saling mengganggu.
  • Atas nama pelapor: dengan izin khusus, tambahkan "requested_for_email": "[email protected]" (atau requested_for_id). Pelapor itu harus anggota aktif workspace ini. Ia melihat tiketnya di portal dan menerima email seperti biasa.
  • Komentar: POST …/comments dengan {"body": "…"} selalu publik. Token tidak bisa membaca maupun menulis catatan internal. Komentar integrasi tidak dihitung sebagai respons pertama tim dan tidak menutup penanda Pelapor membalas.

Setiap token dibatasi 120 permintaan per menit. Bila terlampaui, jawabannya 429 dengan header Retry-After. Workspace yang masa trial-nya berakhir hanya menerima permintaan baca.

Mencabut token

Klik Cabut pada baris token. Sejak saat itu setiap permintaan dengan token itu ditolak 401, dan langkah ini tidak bisa dibatalkan. Riwayat tindakan token tetap tersimpan.

Webhook

Membuat webhook

  1. Buka tab Webhook, lalu klik Webhook baru.
  2. Isi Nama dan URL penerima. URL harus https:// dan menuju alamat publik. Alamat jaringan privat, alamat loopback seperti 127.0.0.1, dan alamat khusus ditolak. Pemeriksaan berlangsung dua kali. Saat Simpan, RANCAQ menolak URL yang berbentuk alamat IP privat atau khusus dan nama host internal (misalnya *.local, *.internal). Nama host biasa belum dicek ke DNS, karena alamatnya bisa berubah. Saat mengirim, nama host dicek ke DNS: bila mengarah ke alamat privat, kiriman tidak dibuka dan tercatat Gagal di riwayat dengan pesan ditolak: alamat tujuan tidak diizinkan ….
  3. Pilih Peristiwa:
PeristiwaKapan dikirim
ticket.created.v1Tiket baru dibuat lewat jalur apa pun.
ticket.status_changed.v1Status tiket berubah, termasuk perubahan otomatis.
ticket.assigned.v1Tim atau petugas tiket berubah.
ticket.comment_added.v1Komentar publik ditambahkan. Catatan internal tidak pernah dikirim.
sla.breached.v1Tenggat SLA respons atau penyelesaian terlampaui.

Peristiwa boleh dikosongkan. Webhook tanpa peristiwa hanya menerima kiriman aturan automation yang memilihnya (automation.rule_triggered.v1). Lihat Automation. Kartu webhook menyebut aturan yang mengirim ke sana.

  1. Klik Simpan, lalu salin rahasia penanda tangan (whsec_…). Rahasia hanya tampil sekali. Bila hilang, pakai Ganti rahasia.
  2. Klik Kirim uji pada kartu webhook untuk mengirim ping.v1 ke penerima.

Satu workspace dapat memiliki paling banyak 10 webhook aktif. Webhook hanya menerima peristiwa yang terjadi sesudah ia dibuat.

Isi kiriman

RANCAQ mengirim POST berisi JSON:

{
  "event_id": "5d0b…",
  "event_type": "ticket.status_changed.v1",
  "tenant_id": "…",
  "occurred_at": "2026-10-06T13:20:05Z",
  "actor": { "type": "user", "id": "…", "name": "Agus" },
  "subject": { "type": "ticket", "id": "…", "number": "INC-2026-000123" },
  "data": {
    "ticket": { "number": "INC-2026-000123", "subject": "VPN putus", "status": "TRIAGED", "priority": "P2", "…": "…" },
    "change": { "from": "NEW", "to": "TRIAGED" }
  }
}
  • actor.type: user untuk orang, integration untuk token API, system untuk perubahan otomatis RANCAQ.
  • data.ticket: keadaan tiket saat peristiwa diproses, beberapa detik sesudah kejadian.
  • Data tambahan per peristiwa: change untuk perubahan, comment untuk komentar, sla untuk pelanggaran SLA.

Header yang ikut dikirim:

HeaderIsi
X-ITSM-EventJenis peristiwa, sama dengan event_type.
X-ITSM-Event-IDSama dengan event_id.
X-ITSM-Delivery-IDPengenal kiriman, tetap untuk semua percobaan dan kirim ulang.
X-ITSM-Delivery-AttemptNomor percobaan: 1, 2, dan seterusnya.
X-ITSM-TimestampDetik Unix saat percobaan dikirim.
X-ITSM-Signaturesha256= diikuti HMAC-SHA256.

Memverifikasi tanda tangan

Hitung HMAC-SHA256 atas <X-ITSM-Timestamp>.<body mentah> dengan rahasia penanda tangan utuh (termasuk awalan whsec_) sebagai kunci. Lalu:

  1. bandingkan hasilnya dengan X-ITSM-Signature secara constant-time;
  2. tolak bila timestamp berselisih lebih dari 5 menit dari jam Anda.

Hitung atas byte body apa adanya, sebelum diurai JSON.

import crypto from 'node:crypto'

function sah(rahasia, timestamp, bodyMentah, tandaTangan) {
  const hitung = 'sha256=' + crypto.createHmac('sha256', rahasia)
    .update(`${timestamp}.${bodyMentah}`).digest('hex')
  const a = Buffer.from(hitung), b = Buffer.from(tandaTangan)
  const segar = Math.abs(Date.now() / 1000 - Number(timestamp)) < 300
  return segar && a.length === b.length && crypto.timingSafeEqual(a, b)
}
import hmac, hashlib, time

def sah(rahasia: str, timestamp: str, body_mentah: bytes, tanda_tangan: str) -> bool:
    hitung = "sha256=" + hmac.new(rahasia.encode(), timestamp.encode() + b"." + body_mentah,
                                  hashlib.sha256).hexdigest()
    return abs(time.time() - int(timestamp)) < 300 and hmac.compare_digest(hitung, tanda_tangan)

Contoh untuk menguji kode Anda: rahasia whsec_contoh, timestamp 1759750000, dan body {"event_id":"x"} menghasilkan sha256=b65486071794a9af8c19a14c7882303172b0131257324735b6abd3e060ddf454.

Kiriman ganda dan urutan

Pengiriman bersifat at-least-once: satu peristiwa bisa tiba lebih dari sekali, misalnya bila penerima memproses kiriman tetapi jawabannya tidak sampai sebelum batas waktu. Agar aman:

  • simpan event_id yang sudah diproses, lalu abaikan kiriman berikutnya dengan event_id yang sama;
  • jawab secepatnya dengan kode 2xx, lalu kerjakan prosesnya di belakang;
  • jangan mengandalkan urutan tiba. Gunakan occurred_at dan status di data.ticket.

Percobaan ulang dan riwayat

Kiriman dianggap berhasil bila penerima menjawab 2xx dalam 10 detik. Redirect tidak diikuti. Bila gagal, RANCAQ mencoba lagi 1 menit, 5 menit, 30 menit, 2 jam, dan 6 jam kemudian, dengan Delivery-ID dan body yang sama. Sesudah enam percobaan gagal, kiriman berstatus Gagal.

Tab Riwayat pengiriman menampilkan setiap kiriman beserta semua percobaannya: waktu, kode HTTP, pesan galat, dan cuplikan jawaban penerima. Saring per webhook, status, peristiwa, atau nomor tiket. Kirim ulang mengirim kiriman yang sudah selesai sekali lagi, dengan Delivery-ID dan event_id yang sama.

Nonaktif otomatis

Bila 5 kiriman berturut-turut berakhir Gagal (masing-masing sudah dicoba enam kali), webhook dinonaktifkan otomatis. Kartu webhook menampilkan alasan dan waktunya, dan Audit mencatatnya. Perbaiki penerima, lalu Aktifkan kembali. Kiriman yang masih menunggu akan dilanjutkan, dan kiriman yang sudah gagal dapat dikirim ulang dari riwayat.

Kegagalan webhook tidak pernah membatalkan perubahan tiket dan tidak menunda email notifikasi.

Lihat juga

  • Audit log: jejak pembuatan, pencabutan, dan tindakan token.
  • Antrean tiket: tiket berkanal API dapat disaring di antrean.