API and webhooks
jah.dk runs on the jajob.dk platform, so the API and webhooks are the same. The full API description is in the jajob.dk developer docs.
Basics
- Base URL:
https://jajob.dk/api/v1 - Authentication:
Authorization: Bearer <token>. Get a token withPOST /api/v1/auth/login. - Errors:
{"error": {"code", "message", "details"}}.
Partner API keys (jjk_…) are created by a company owner with POST /api/v1/apikeys; the key is shown once. Access to the external partner API is opened on request: write to info@jajob.dk.
Webhooks
Register an HTTPS endpoint with POST /api/v1/webhooks/endpoints and receive signed events:
| Event | When |
|---|---|
application.created |
A new application arrives |
application.status_changed |
A candidate moves to another stage |
vacancy.expired |
A job reaches its deadline |
candidate.matched |
A matching candidate is found for a job |
Every delivery has the headers X-Jajob-Event, X-Jajob-Delivery and X-Jajob-Signature. Failed deliveries are retried; private and loopback hosts are blocked.
Verify the signature
The signature is HMAC-SHA256 of the canonical JSON body (sorted keys, no spaces) with your endpoint secret.
import hashlib, hmac, json
def verify(secret: str, body: bytes, signature: str) -> bool:
canonical = json.dumps(json.loads(body), sort_keys=True, separators=(",", ":")).encode()
expected = hmac.new(secret.encode(), canonical, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
import crypto from "node:crypto";
function canonical(v) {
if (Array.isArray(v)) return "[" + v.map(canonical).join(",") + "]";
if (v && typeof v === "object")
return "{" + Object.keys(v).sort().map(k => JSON.stringify(k) + ":" + canonical(v[k])).join(",") + "}";
return JSON.stringify(v);
}
export function verify(secret, rawBody, signature) {
const expected = crypto.createHmac("sha256", secret).update(canonical(JSON.parse(rawBody))).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
Answer with a 2xx status quickly and do the work afterwards.
Something unclear or missing? Write to info@jajob.dk.