Trigger Events from a Web Page
Bitcaster can be triggered directly from a web page (browser JavaScript). Because browser code is visible to any visitor, API keys must never be embedded in pages. Instead, a web API key stays on your backend, which derives a browser-safe signing secret from it. Pages use that secret to sign requests with an HMAC-SHA256 signature that the Bitcaster API verifies.
The signed requests are:
- scoped to a single application;
- restricted to triggering events (no reads, no writes, no auto-create);
- bound to an explicit list of allowed origins: requests without a matching
Originheader are rejected; - validated against a timestamp skew window (see
HMAC_SIGNATURE_MAX_SKEW); - throttled per key and client IP.
1. Set up a web API key
Create an API key with:
- Kind:
Web Key - Grant:
Web Event Triggeronly - Scope: an application (required)
- Allowed origins: the origins of the pages that will use the key
- Expires at: optional but recommended
The master key itself is only used server side. Derive the signing secret that can be safely embedded in (or served to) the page:
from bitcaster.models import ApiKey
key = ApiKey.objects.get(name="my-web-key")
secret = key.get_web_signing_secret()
The derived secret cannot be used as a bearer token and does not reveal the master key: it only allows signing trigger requests.
2. Sign requests from the browser
Sign the request with an HMAC-SHA256 over the canonical representation:
METHOD\nPATH\nTIMESTAMP\nSHA256_HEX(BODY)
where:
METHODis the HTTP method, e.g.POST;PATHis the request path (no scheme, no host, no query string), e.g./api/o/{org}/p/{prj}/a/{app}/e/{event}/trigger/;TIMESTAMPis the Unix timestamp (seconds) used in theX-Timestampheader;SHA256_HEX(BODY)is the hex-encoded SHA-256 digest of the raw request body.
Send two extra headers:
Authorization: HMAC-SHA256 <key_id>:<signature>
X-Timestamp: <unix timestamp in seconds>
where key_id is the first 16 characters of the master API key and
signature is the hex-encoded HMAC-SHA256 of the canonical string, keyed with
the derived signing secret. Requests whose timestamp differs from server time
by more than HMAC_SIGNATURE_MAX_SKEW seconds are rejected.
Example (web key; the Origin header must match the allowed origins):
async function trigger(url, body) {
const timestamp = Math.floor(Date.now() / 1000);
const canonical = [
"POST",
new URL(url).pathname,
timestamp,
sha256Hex(JSON.stringify(body)),
].join("\n");
const signature = hmacSha256Hex(SIGNING_SECRET, canonical);
const res = await fetch(url, {
method: "POST",
headers: {
"Authorization": `HMAC-SHA256 ${KEY_ID}:${signature}`,
"X-Timestamp": String(timestamp),
"Origin": window.location.origin,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
return res;
}
A request is rejected (403) when the Origin header is missing or not in the
key's allowed origins, and (401) when the signature or timestamp is invalid.
3. Browser client library
The standalone @bitcaster/js package wraps this flow with a single API —
see its README for usage and examples.
Configuration
| Setting | Default | Description |
|---|---|---|
CORS_ALLOWED_ORIGINS |
[] |
Origins allowed to call the API from a browser |
HMAC_SIGNATURE_MAX_SKEW |
300 |
Max allowed skew (seconds) between X-Timestamp and server time |
TRIGGER_CONTEXT_MAX_SIZE |
32768 |
Max size in bytes of the context payload from web credentials |