← API Key JS Staff

API Documentation

Send notifications to employees from other systems — scripts, other servers’ cron jobs, n8n, webhooks, WhatsApp bots.

Get an API key

Owner: go to Manage → 🔑 API Key, give it a name, press Create key.

The key looks like jss_ + 64 characters and is shown only once. The server only stores its fingerprint, so if you lose it, nobody can show it to you again — including us. Revoke the old one, create a new one.

Store it in an env var or a config file outside the docroot. Never in code that gets committed to git.

2 · Send the key

Either of these two headers — pick whichever is easier for your tool:

Authorization: Bearer jss_xxxxxxxx...
X-API-Key: jss_xxxxxxxx...

The key determines the company. No endpoint has a tenant parameter — if it did, one leaked key would be enough to send to every company on this platform.

There is no CSRF protection here, and that is correct. CSRF guards against requests triggered by someone else’s browser riding on their cookies. Key-based requests carry nobody’s cookies — there is nothing to ride on.

What this key can do

Only sends notifications to the owning company’s employees, plus reads the list of employee names + ids (so you have a way to get user_id without guessing).

It cannot read anyone’s pay, attendance, email, bank account, or notifications; cannot change anything; cannot touch other companies. Deliberately this narrow — credentials living on another server will leak sooner or later, and how little damage that does depends on how little it can do.

3 · Check the key is alive

GET/api/v1/notif.php?aksi=ping

curl "https://staff.jidanshoppu.com/api/v1/notif.php?aksi=ping" \
  -H "Authorization: Bearer jss_xxx"
{ "ok": true, "key": "n8n produksi", "perusahaan": "Perusahaan Kamu" }

4 · Get the employee list

GET/api/v1/notif.php?aksi=karyawan — to get user_id.

curl "https://staff.jidanshoppu.com/api/v1/notif.php?aksi=karyawan" \
  -H "Authorization: Bearer jss_xxx"
{
  "ok": true,
  "karyawan": [
    { "user_id": 3, "nama": "Sela",   "peran": "staff" },
    { "user_id": 5, "nama": "Syauqi", "peran": "staff" }
  ]
}

Only id, name, and role. No email/rate/bank account — this key is for sending notifications, not for reading people’s data.

5 · Send a notification

POST/api/v1/notif.php — accepts JSON or form data.

FieldRequiredDescription
judulyesmax 120 characters
isiyesmax 255 characters
user_idno employee id. 0 or omitted = all active employees
jenisno info (default), kasbon, gaji, bonus, timer, absen. Only decides the icon; unknown values fall back to info
urlno must be an internal path starting with /

To all employees

curl -X POST https://staff.jidanshoppu.com/api/v1/notif.php \
  -H "Authorization: Bearer jss_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "judul": "Besok libur",
    "isi": "Tanggal 17 libur, timer tidak perlu dinyalakan."
  }'
{ "ok": true, "terkirim": 5 }

To one person, with a link

curl -X POST https://staff.jidanshoppu.com/api/v1/notif.php \
  -H "X-API-Key: jss_xxx" \
  -d "user_id=3" \
  -d "judul=Cek transaksi kamu" \
  -d "isi=Ada penyesuaian baru bulan ini." \
  -d "jenis=gaji" \
  -d "url=/staff/transaksi.php"
url must be an internal path. https://… and //evil.com are rejected with 400.

Reason: an official-looking notification that can point anywhere is the perfect phishing tool — employees click without suspicion precisely because the portal is what delivered it. One leaked key is enough to broadcast "Click to check your pay" to a fake login page.

//evil.com is rejected too, even though it starts with a slash: browsers read it as an address to another host.

What happens after sending

Employees with the portal open hear a sound and see a popup within ≤20 seconds. Those not currently on it will be greeted next time they log in. Everything lands in their Notifications page, and every API send is recorded in the audit log with the key’s name.

Rate limit

60 requests per minute per key. Every response carries:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 57

Over the limit → 429 + Retry-After: 60.

Status codes

CodeMeaning
200Success
400Wrong parameter, broken JSON body, or url is not an internal path
401Key missing, wrong, or already revoked
404user_id is not an employee of this company
405Wrong method
429Rate limit exceeded

If it fails

SymptomUsually caused by
401 even though the key is correct Some servers strip the Authorization header before it reaches PHP. Try X-API-Key — an ordinary header the server never touches. If that works but Bearer doesn’t, let us know.
401 out of nowhere, even though it was working before The owner revoked the key. Check the API Key page.
400 "url must be an internal path" You sent https://…. Use /staff/transaksi.php instead.
200 but "terkirim": 0 The employee is not active, or the company has no active employees yet.

Honest limitations

Need employees to log in to your app with their JS Staff account (not just receive notifications)? That’s SSO — the setup guide is here.