Menu

Webhook peringatan harga

Terima permintaan POST bertanda tangan di URL Anda saat harga emas, perak, atau tembaga melewati level tertentu, berubah sebesar persentase tertentu, atau mencapai penutupan harian. Tambahkan peringatan di dasbor, verifikasi setiap permintaan, lalu proses.

Plus · $39/moPro · $89/mo

Peringatan dan batas

Satu peringatan memantau harga tunai satu logam, yaitu emas, perak, atau tembaga, dengan satu aturan dan mengirim ke satu URL. Plus dan Pro masing-masing menyertakan 5 peringatan. Free dan Basic tidak menyertakan peringatan harga. Jika paket Anda berakhir atau pembayaran gagal, pengiriman berhenti sampai paket aktif kembali.

  • Melintasi level: harga bergerak dari satu sisi level ke sisi lainnya, ke atas, ke bawah, atau ke kedua arah. Setelah terpicu, peringatan tidak mengirim apa pun selama setidaknya 10 menit. Jika harga melintasi level ke arah sebaliknya lalu kembali dalam waktu tersebut, persilangan kedua tidak dikirim. Peringatan baru siap terpicu lagi setelah waktu tersebut berlalu dan harga telah kembali ke sisi level sebelum persilangan, melewati level sejauh 0.1% ke arah sebaliknya. Jadi, persilangan kedua yang nyata tetap dikirim, sedangkan fluktuasi kecil di sekitar level diabaikan.
  • Perubahan persentase: harga berubah sebesar persentase tertentu dalam 1 jam atau 24 jam. Peringatan terpicu sekali, lalu siap terpicu lagi setelah 1 jam jika besar perubahannya sudah kembali di bawah ambang batas. Perubahan 24 jam tepat setelah pembukaan pasar mingguan diukur dari harga saat pasar dibuka kembali. Selisih harga selama akhir pekan tidak pernah dihitung sebagai perubahan.
  • Penutupan harian: harga terakhir pada hari UTC, dikirim tepat setelah tengah malam UTC. Logam yang tidak memiliki harga pada hari tersebut, misalnya pada akhir pekan, tidak memiliki harga penutupan.

Hari dihitung berdasarkan UTC. Peringatan penutupan harian dikirim sekali sehari. Harga yang berfluktuasi di sekitar suatu level tidak menghasilkan permintaan untuk setiap pembaruan harga.

Peristiwa yang dapat diterima

  • price.level_cross: harga melewati level yang Anda tetapkan.
  • price.percent_move: harga berubah sebesar persentase yang Anda tetapkan.
  • price.daily_close: hari UTC telah berakhir.
  • webhook.test: Anda menekan Send test pada suatu peringatan. Nilai data adalah {"alert_id":"<alert id>"}. Anda dapat mengirim satu pengujian per peringatan per menit, dan paling banyak 20 pengujian dapat menunggu pengiriman secara bersamaan.

Membaca permintaan

Anda menerima POST di URL Anda. URL harus menggunakan https://, porta 443, dan nama peladen publik, tanpa nama pengguna atau kata sandi, serta panjangnya tidak boleh melebihi 2048 karakter. Alamat diperiksa sebelum setiap pengiriman. Pengalihan tidak diikuti, dan respons 3xx dihitung sebagai kegagalan. Hanya kode status respons Anda yang dibaca.

Content-Type: application/jsonUser-Agent: webhook-delivery/1Webhook-Signature: t=<unix seconds>,v1=<hex>

Isi permintaan berupa JSON dengan empat kolom tingkat atas: id, type, created_at, dan data. Urutan kunci dan spasi pada isi permintaan sebenarnya dapat berbeda dari contoh di sini.

idstring
Pengenal peristiwa yang unik. Gunakan untuk mengabaikan duplikat.
typestring
Salah satu jenis peristiwa di atas.
created_atstring
Waktu UTC dalam format ISO 8601 yang diakhiri dengan Z. Untuk penutupan harian, nilainya adalah 23:59:59 pada hari yang ditutup.
data.alert_idstring
Peringatan yang terpicu. Nilainya tidak berubah saat Anda merotasi rahasia penandatanganan.
data.kindstring
level_cross, percent_move, atau daily_close.
data.symbolstring
XAU-USD-SPOT, XAG-USD-SPOT, atau HG-USD-SPOT.
data.directionstring
above, below, atau either, sesuai pengaturan peringatan.
data.pricestring
Harga yang memicu peringatan, berupa teks angka desimal. Hanya untuk peristiwa level dan persentase.
data.observed_atstring
Waktu harga diamati. Hanya untuk peristiwa level dan persentase.
data.levelstring
Level yang Anda tetapkan. Hanya untuk peristiwa level.
data.percent, data.window, data.reference_pricestring
Persentase yang Anda tetapkan, periodenya (1h atau 24h), dan harga pada awal periode. Hanya untuk peristiwa persentase.
data.date, data.closestring
Hari UTC dan harga terakhirnya. Hanya untuk peristiwa penutupan harian.

Contoh isi permintaan

price.level_cross

{
  "id": "evt_3f0c6a52-6a2e-4b8e-9c1d-2f4b7e9a1c03_level_cross:492841",
  "type": "price.level_cross",
  "created_at": "2026-10-23T14:02:11.482113Z",
  "data": {
    "alert_id": "3f0c6a52-6a2e-4b8e-9c1d-2f4b7e9a1c03",
    "kind": "level_cross",
    "symbol": "XAU-USD-SPOT",
    "direction": "above",
    "price": "4203.1",
    "level": "4200",
    "observed_at": "2026-10-23T14:02:11.482113+00:00"
  }
}

price.percent_move

{
  "id": "evt_8a1d52c4-0b7e-4f55-a3d9-61e2c7b8f410_percent_move:492855",
  "type": "price.percent_move",
  "created_at": "2026-10-23T09:15:40.120934Z",
  "data": {
    "alert_id": "8a1d52c4-0b7e-4f55-a3d9-61e2c7b8f410",
    "kind": "percent_move",
    "symbol": "XAU-USD-SPOT",
    "direction": "either",
    "price": "4168.8",
    "percent": "2",
    "window": "24h",
    "reference_price": "4083.05",
    "observed_at": "2026-10-23T09:15:40.120934+00:00"
  }
}

price.daily_close

{
  "id": "evt_c92e0f7b-31a4-4d6a-8e57-a05b3d1c92e8_close:2026-10-22",
  "type": "price.daily_close",
  "created_at": "2026-10-22T23:59:59Z",
  "data": {
    "alert_id": "c92e0f7b-31a4-4d6a-8e57-a05b3d1c92e8",
    "kind": "daily_close",
    "symbol": "XAG-USD-SPOT",
    "direction": "either",
    "date": "2026-10-22",
    "close": "48.62"
  }
}

webhook.test

{
  "id": "evt_test_1792143012345",
  "type": "webhook.test",
  "created_at": "2026-10-23T09:30:12.345000Z",
  "data": {
    "alert_id": "3f0c6a52-6a2e-4b8e-9c1d-2f4b7e9a1c03"
  }
}

Memverifikasi tanda tangan

Periksa Webhook-Signature sebelum menindaklanjuti peristiwa. Gunakan bita mentah isi permintaan persis seperti yang diterima. Jangan pernah mengurai lalu menserialisasi ulang JSON sebelum pemeriksaan.

  1. Pisahkan Webhook-Signature pada tanda koma untuk membaca t dan v1.
  2. Tolak permintaan jika t lebih dari 300 detik di depan atau di belakang waktu pada jam Anda.
  3. Susun pesan dari t, tanda titik, dan isi mentah permintaan: <t>. diikuti isi permintaan.
  4. Hitung HMAC-SHA256 pesan menggunakan rahasia penandatanganan peringatan Anda, yang diawali dengan whsec_, lalu enkode hasilnya dalam heksadesimal.
  5. Bandingkan hasilnya dengan v1 menggunakan perbandingan waktu konstan. Tolak permintaan jika keduanya berbeda.

Node.js

Python

import { createHmac, timingSafeEqual } from "node:crypto";
import { createServer } from "node:http";

const TOLERANCE_SECONDS = 300;

// rawBody: the request body exactly as received (a Buffer), before any JSON parsing.
function verifyWebhook(rawBody, header, secret, now = Math.floor(Date.now() / 1000)) {
  const fields = {};
  for (const part of String(header ?? "").split(",")) {
    const i = part.indexOf("=");
    if (i > 0) fields[part.slice(0, i).trim()] = part.slice(i + 1).trim();
  }
  const t = Number(fields.t);
  if (!Number.isInteger(t) || Math.abs(now - t) > TOLERANCE_SECONDS) return false;
  const expected = createHmac("sha256", secret).update(t + ".").update(rawBody).digest();
  const given = Buffer.from(fields.v1 ?? "", "hex");
  return given.length === expected.length && timingSafeEqual(given, expected);
}

const seen = new Set(); // use your database in production

createServer((req, res) => {
  const chunks = [];
  req.on("data", (chunk) => chunks.push(chunk));
  req.on("end", () => {
    const rawBody = Buffer.concat(chunks);
    const header = req.headers["webhook-signature"];
    if (!verifyWebhook(rawBody, header, process.env.WEBHOOK_SECRET)) {
      res.writeHead(400).end();
      return;
    }
    const event = JSON.parse(rawBody);
    if (!seen.has(event.id)) {
      seen.add(event.id);
      // Hand the event to a queue or background job here, then answer quickly.
      console.log(event.type, event.data);
    }
    res.writeHead(204).end();
  });
}).listen(Number(process.env.PORT ?? 3000));
import hashlib
import hmac
import time

TOLERANCE_SECONDS = 300


def verify_webhook(raw_body: bytes, header: str, secret: str, now: int | None = None) -> bool:
    """raw_body is the request body exactly as received, before any JSON parsing."""
    fields = dict(p.strip().split("=", 1) for p in (header or "").split(",") if "=" in p)
    try:
        t = int(fields["t"])
    except (KeyError, ValueError):
        return False
    now = int(time.time()) if now is None else now
    if abs(now - t) > TOLERANCE_SECONDS:
        return False
    signed = f"{t}.".encode() + raw_body
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(fields.get("v1", "").encode(), expected.encode())


# Flask:  verify_webhook(request.get_data(), request.headers.get("Webhook-Signature", ""), secret)
# Django: verify_webhook(request.body, request.headers.get("Webhook-Signature", ""), secret)

Merespons dan menangani percobaan ulang

Kembalikan kode 2xx apa pun dalam 10 detik. Respons 3xx, 4xx, 5xx, batas waktu yang terlampaui, atau kesalahan koneksi dihitung sebagai percobaan yang gagal. Simpan peristiwa dan segera kembalikan respons. Kerjakan proses yang lambat di latar belakang.

  • Percobaan ulang pertama: 1 menit setelah kegagalan pertama.
  • Percobaan ulang kedua: 5 menit setelah kegagalan sebelumnya.
  • Percobaan ulang ketiga: 30 menit setelah kegagalan sebelumnya.
  • Percobaan ulang keempat: 2 jam setelah kegagalan sebelumnya.
  • Percobaan ulang kelima dan terakhir: 6 jam setelah kegagalan sebelumnya.

Totalnya 6 percobaan selama sekitar 8.5 jam. Setelah percobaan terakhir gagal, peristiwa tidak dikirim lagi dan tidak ada pengiriman cadangan melalui surel. Setiap percobaan ditandatangani ulang dengan stempel waktu baru. Peristiwa yang sudah berusia lebih dari satu jam saat pertama kali dicoba untuk dikirim akan dibuang. Catatan pengiriman disimpan selama 30 hari.

Merotasi rahasia penandatanganan

Rahasia hanya ditampilkan sekali, saat Anda menambahkan peringatan. Jika rahasia mungkin bocor atau hilang, pilih Rotate secret pada peringatan di dasbor Anda. Rahasia baru ditampilkan sekali. Pengenal peringatan tetap sama, rahasia lama langsung tidak berlaku, dan pengiriman selanjutnya menggunakan rahasia baru. Agar pengiriman tidak gagal, izinkan peladen Anda menerima kedua rahasia selama beberapa menit saat beralih.

Pertanyaan

Paket apa yang diperlukan untuk peringatan harga?

Plus ($39/bulan) dan Pro ($89/bulan) sama-sama menyediakan fitur peringatan harga. Masing-masing paket menyertakan 5 peringatan. Fitur peringatan harga ini tidak disertakan dalam paket Free maupun Basic.

Seberapa sering satu peringatan dapat terpicu?

Setelah terpicu, peringatan level tidak mengirim apa pun selama setidaknya 10 menit; persilangan selama waktu itu tidak dikirim. Peringatan baru siap terpicu lagi setelah waktu tersebut berlalu dan harga telah kembali ke sisi level sebelum persilangan, melewati level sejauh 0.1% ke arah sebaliknya. Peringatan persentase terpicu sekali, lalu siap lagi setelah 1 jam jika perubahannya sudah hilang. Peringatan penutupan harian dikirim sekali per hari UTC.

Apa yang terjadi jika peladen saya tidak aktif?

Pengiriman yang gagal dicoba ulang hingga 5 kali lagi selama sekitar 8.5 jam. Setelah percobaan terakhir gagal, peristiwa tidak dikirim lagi dan tidak ada pengiriman cadangan melalui surel.

Bisakah saya menerima peristiwa yang sama dua kali?

Ya. Pengiriman dapat dicoba ulang meskipun sudah Anda proses, misalnya jika respons Anda tiba setelah 10 detik. Simpan pengenal peristiwa yang sudah diproses dan abaikan duplikatnya.

Lihat juga