Quick Start
Get your app connected to JLS Crewbook in 7 steps.
Before you begin
client_id in OAuth requests), your API key secret (used in the x-api-key header), and a registered redirect URI. Create all of these from the API Keys page in your dashboard.Get your API key
Go to the API Keys page in your dashboard to create a read-scope API key. You’ll receive two values: a Client ID (UUID) used as client_id in OAuth requests, and a secret key used in the x-api-key header. Keep the secret safe — treat it like a password. The Client ID can always be found later via View Details. (Need an admin-scope key? Contact your EDS administrator.)
Register a redirect URI
On the API Keys page, click View Details on your API key and add the exact callback URL your app will use (e.g. https://yourapp.com/auth/callback). The redirect URI must match exactly during the OAuth flow — including scheme, host, path, and trailing slash.
Redirect to authorize
When an employee wants to log in, redirect their browser to the EDS authorize endpoint. Include your Client ID (from View Details), redirect_uri, and a random state value for CSRF protection.
const state = crypto.randomUUID();
sessionStorage.setItem("oauth_state", state);
const params = new URLSearchParams({
// Client ID (UUID) from your API key's View Details — NOT the secret key
client_id: "YOUR_CLIENT_ID",
redirect_uri: "https://yourapp.com/auth/callback",
state,
});
window.location.href = `https://<your-eds-host>/api/v1/oauth/authorize?${params}`;Handle the callback
EDS redirects back to your app with a short-lived code and the state you sent. Verify the state matches, then exchange the code for a session token.
// In your callback route handler
const params = new URLSearchParams(window.location.search);
if (params.get("error") === "access_denied") {
// Employee denied consent or account is inactive
throw new Error(params.get("error_description") ?? "Access denied");
}
const code = params.get("code");
const returnedState = params.get("state");
const savedState = sessionStorage.getItem("oauth_state");
if (!code || returnedState !== savedState) {
throw new Error("Invalid OAuth callback");
}
sessionStorage.removeItem("oauth_state");Exchange code for session token
POST the authorization code to /api/v1/oauth/token within 5 minutes. Include your API key in the x-api-key header. You’ll receive a 24-hour session token and the authenticated employee’s profile.
const response = await fetch(`https://<your-eds-host>/api/v1/oauth/token`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": "YOUR_API_KEY",
},
body: JSON.stringify({
grant_type: "authorization_code",
code,
redirect_uri: "https://yourapp.com/auth/callback",
}),
});
const { session_token, employee, expires_at } = await response.json();
// Store session_token securely (e.g. httpOnly cookie)Use the session token
On subsequent requests, verify the session is still active by calling /api/v1/oauth/introspect. This returns the employee object if the token is valid.
const response = await fetch(`https://<your-eds-host>/api/v1/oauth/introspect`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": "YOUR_API_KEY",
},
body: JSON.stringify({ session_token: "SESSION_TOKEN" }),
});
const data = await response.json();
if (!data.active) {
// Token is expired or revoked — redirect to login
redirectToLogin();
}
const employee = data.employee; // Use employee dataLog the user out
When an employee logs out of your app, revoke their session token so EDS invalidates it. This endpoint is idempotent — calling it on an already-revoked token is safe.
await fetch(`https://<your-eds-host>/api/v1/oauth/revoke`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": "YOUR_API_KEY",
},
body: JSON.stringify({ session_token: "SESSION_TOKEN" }),
});
// Clear local session state
sessionStorage.clear();