API Keys

Endpoint server-to-server untuk sinkronisasi data produk OtoSwitch ke sistem sendiri (cron job, ETL, mirror DB, app/web reseller).

Fitur ini tersedia di plan Basic dan Pro. Plan Trial tidak.


Kapan butuh

  • Punya app/web reseller sendiri yang harus sinkron dengan harga/stok terkini di Digiflazz.
  • ETL job yang mirror produk ke data warehouse internal.
  • Sistem tagihan otomatis yang butuh master data produk.

Kalau cuma akses dashboard via browser, tidak perlu API key — login biasa cukup.


Membuat API key

  1. Login dashboard → menu API Keys di sidebar.
  2. Klik Generate Kunci Baru.
  3. Beri nama (mis. production-server, staging-cron). Nama untuk identifikasi sendiri.
  4. Klik Generate.
  5. Salin kunci yang muncul SEKARANG — setelah modal ditutup, plaintext tidak bisa dilihat lagi (hanya hash yang tersimpan).
  6. Tempel di tempat aman (env var, secret manager).

⚠️ Jangan commit API key ke git. Format kunci otsk_<43 chars> terdaftar di GitHub secret scanner — kalau accidentally pushed ke repo public, GitHub akan auto-detect dan kirim alert.


Format

otsk_AbCdEf...43-char-string

Prefix otsk_ membuat kunci mudah dikenali di log/repo.


Revoke

Di tab API Keys, klik Revoke di baris kunci. Kunci langsung mati — request berikutnya return 401.

Revoke tidak bisa di-undo. Generate baru kalau perlu.


Endpoint: Export Produk

Endpoint utama untuk sync. Mengembalikan semua produk beserta supplier yang dipasang, harga, status, dan max price.

GET /api/v1/products/export
Headers:
  X-Api-Key: otsk_<plaintext>
  Accept: application/x-ndjson    # atau application/json
  If-None-Match: "<etag>"          # opsional, untuk cache

Response

Code Arti
200 Body streaming. Header ETag berisi tag untuk request berikutnya.
304 If-None-Match cocok — payload tidak berubah, hemat bandwidth.
401 Header X-Api-Key hilang/invalid/revoked.
403 Subscription expired atau plan tidak include api_access.
409 Sesi Digiflazz user expired — user perlu rebind.
429 Rate limit terlampaui. Header Retry-After berisi detik tunggu.

Format payload

NDJSON (recommended untuk streaming)

Satu produk per baris. Cocok untuk \COPY ke Postgres atau processing baris-per-baris.

{"id": "AXIS5K", "name": "Axis 5K", "price": 4900, "max_price": 5500, "seller": "Supplier-A", "status": "active"}
{"id": "AXIS10K", "name": "Axis 10K", "price": 9800, ...}
...

JSON Array

Standar JSON. Cocok untuk client yang load semua ke memory.

[
  {"id": "AXIS5K", ...},
  {"id": "AXIS10K", ...}
]

Rate limit

6 request per menit per kunci (sliding window).

Cocok untuk sinkronisasi tiap 15 detik dengan margin. Cache produk di sisi server TTL 10 menit, jadi sync lebih cepat dari itu hampir selalu mendapatkan payload yang sama.

Selalu pakai If-None-Match untuk hemat bandwidth (~4 MB → 304 200 byte).


Contoh — Sinkronisasi ke PostgreSQL

#!/bin/bash
KEY="otsk_xxx"
URL="https://otoswitch.majopay.id/api/v1/products/export"

# Pertama kali: ambil ETag + payload
ETAG=$(curl -sI -H "X-Api-Key: $KEY" "$URL" \
  | awk -F'[: ]' '/^etag/i {print $3}' | tr -d '\r"')

curl -s -H "X-Api-Key: $KEY" \
     -H "Accept: application/x-ndjson" \
     --compressed \
     "$URL" \
  | psql -c "\copy products_staging FROM STDIN"

echo $ETAG > /var/cache/otoswitch.etag

# Sync berikutnya: kirim ETag, lewati kalau tidak berubah
ETAG=$(cat /var/cache/otoswitch.etag)
STATUS=$(curl -s -o out.ndjson -w "%{http_code}" \
  -H "X-Api-Key: $KEY" \
  -H "Accept: application/x-ndjson" \
  -H "If-None-Match: \"$ETAG\"" \
  --compressed \
  "$URL")

if [ "$STATUS" = "304" ]; then
  echo "no change"
elif [ "$STATUS" = "200" ]; then
  psql -c "\copy products_staging FROM STDIN" < out.ndjson
  curl -sI -H "X-Api-Key: $KEY" "$URL" \
    | awk -F'[: ]' '/^etag/i {print $3}' | tr -d '\r"' \
    > /var/cache/otoswitch.etag
fi

Contoh — Python

import os
import httpx

KEY = os.environ["OTOSWITCH_API_KEY"]
URL = "https://otoswitch.majopay.id/api/v1/products/export"
ETAG_FILE = "/var/cache/otoswitch.etag"

def load_etag():
    try:
        return open(ETAG_FILE).read().strip()
    except FileNotFoundError:
        return None

def save_etag(etag):
    with open(ETAG_FILE, "w") as f:
        f.write(etag)

def sync():
    headers = {"X-Api-Key": KEY, "Accept": "application/x-ndjson"}
    if etag := load_etag():
        headers["If-None-Match"] = f'"{etag}"'

    with httpx.stream("GET", URL, headers=headers, timeout=60.0) as r:
        if r.status_code == 304:
            print("no change")
            return
        r.raise_for_status()
        for line in r.iter_lines():
            if line:
                process_product(line)  # parse JSON dan upsert ke DB
        save_etag(r.headers["etag"].strip('"'))

def process_product(line):
    import json
    p = json.loads(line)
    # ... upsert ke database

if __name__ == "__main__":
    sync()

Contoh — Node.js

import fs from "fs";
import fetch from "node-fetch";
import readline from "readline";

const KEY = process.env.OTOSWITCH_API_KEY;
const URL = "https://otoswitch.majopay.id/api/v1/products/export";
const ETAG_FILE = "/var/cache/otoswitch.etag";

async function sync() {
  let etag = "";
  try { etag = fs.readFileSync(ETAG_FILE, "utf-8").trim(); } catch {}

  const headers = {
    "X-Api-Key": KEY,
    "Accept": "application/x-ndjson",
  };
  if (etag) headers["If-None-Match"] = `"${etag}"`;

  const res = await fetch(URL, { headers });
  if (res.status === 304) { console.log("no change"); return; }
  if (!res.ok) throw new Error(`status ${res.status}`);

  const rl = readline.createInterface({ input: res.body });
  for await (const line of rl) {
    if (line) await processProduct(JSON.parse(line));
  }
  const newEtag = res.headers.get("etag")?.replace(/"/g, "");
  if (newEtag) fs.writeFileSync(ETAG_FILE, newEtag);
}

async function processProduct(p) {
  // upsert ke database
}

sync().catch(console.error);

Auth UI alternatif (browser/extension)

Browser/extension yang sudah login (cookie/JWT) bisa pakai endpoint yang setara di /api/s/products/export — payload identik, tanpa generate API key. Cocok untuk integration internal yang berjalan di browser user.


FAQ

API key bocor ke publik, apa yang harus dilakukan?

  1. Segera Revoke kunci di dashboard.
  2. Generate kunci baru.
  3. Update aplikasi/server yang pakai dengan kunci baru.
  4. Audit log untuk lihat aktivitas mencurigakan (lihat tab API Keys → Activity).

Bisa pakai satu kunci untuk multiple server? Bisa, tapi tidak disarankan — kalau bocor, harus revoke semua. Generate kunci terpisah untuk tiap server agar revoke bisa selektif.

Berapa banyak kunci bisa dibuat? Tidak ada batas keras saat ini. Tapi disarankan minimal — 1-3 kunci aktif sudah cukup untuk kebanyakan use case.

Endpoint lain selain export produk? Saat ini hanya /api/v1/products/export. Endpoint lain (orders, billing) menyusul.

ETag unik per request atau per data? Per data — kalau payload sama, ETag sama. Jadi cocok dipakai cache long-term.