Every request to the Gridlock API must be authenticated. There are two supported methods: JWT bearer
tokens and API keys. Both use the same Authorization HTTP header with the Bearer
scheme, but they serve different use cases and have different security characteristics.
Understanding which method to use — and how to handle it securely — is the most important step before building on the API. This guide covers both methods in detail, including how to obtain credentials, code examples, scope definitions, and security best practices.
POST /api/auth/loginIf a human is actively logged in and your app is acting on their behalf, use JWT tokens. If code is running unattended on a server with no user present, use API keys. API keys are more predictable in automated contexts because their lifecycle is fully under your control.
A JWT (JSON Web Token) is issued when a user successfully logs in. Gridlock uses JWTs for all authenticated dashboard sessions. If you are building a web application that authenticates users through the Gridlock login flow, JWTs are the correct choice.
Post the user's credentials to the login endpoint. On success, the response includes the JWT and its expiry timestamp. The login endpoint is rate limited to 10 requests per minute per IP to prevent brute-force attacks.
POST https://lockthegrid.com/api/auth/login
Content-Type: application/json
{
"email": "[email protected]",
"password": "your_password_here"
}
Successful response:
{
"success": true,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoidXNyX2Ex...",
"expires_at": "2026-06-08T14:30:00Z",
"user": {
"id": "usr_a1b2c3d4",
"email": "[email protected]",
"org": "Premier Networx",
"role": "admin"
}
}
Pass the JWT in the Authorization header on every subsequent request using the
Bearer scheme.
# curl example
curl -s \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
https://lockthegrid.com/api/user/profile
// JavaScript fetch example
const token = getStoredToken(); // retrieve from secure storage
const res = await fetch('https://lockthegrid.com/api/threats', {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
if (res.status === 401) {
// Token expired or invalidated — redirect to login
redirectToLogin();
return;
}
const data = await res.json();
JWT tokens expire 7 days after issuance. After expiry, the API returns 401 Unauthorized
with the message "Token expired. Please log in again." Your application should detect
this response and redirect the user back to the login flow to obtain a fresh token. Do not automatically
retry the original request with a new login — always prompt the user for their credentials.
Every Gridlock user account carries a token_version counter. When any of the following
events occur, the counter is incremented and all previously issued JWTs become immediately invalid —
even if they have not yet reached their 7-day expiry:
This is an intentional security mechanism. If an account is suspected to be compromised, changing the password immediately invalidates every active session across every device and API client using JWTs for that account.
Storing JWT tokens in localStorage exposes them to cross-site scripting (XSS) attacks
— any injected script can read and exfiltrate the token. In production web applications, store
tokens in HttpOnly cookies so JavaScript has no access. Gridlock's own dashboard uses
this pattern. localStorage is acceptable only for local development and demos.
API keys are the preferred authentication method for any code running unattended: server-side integrations, cron jobs, CI/CD pipelines, webhook processors, and third-party automations. Unlike JWT tokens, API keys are not tied to a user session — they authenticate the organization's account directly and remain valid until they expire or are revoked.
All Gridlock API keys use the prefix gl_live_ followed by a 32-character random
alphanumeric string. The prefix serves two purposes: it makes keys instantly recognizable in code
review, and it allows automated secret scanning tools to detect accidentally committed keys.
gl_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
The gl_live_ prefix is registered with GitHub's secret scanning service and is
compatible with GitLeaks and TruffleHog patterns. If a key is accidentally committed to a
repository — public or private — revoke it immediately via Settings → API Keys → Revoke, regardless
of whether the commit has been deleted. Deletion does not remove content from git history or cached
forks.
The full API key is displayed only at the moment of creation. There is no way to retrieve it afterward. If you lose a key, revoke it and generate a replacement. Store keys in a secrets manager (AWS Secrets Manager, HashiCorp Vault, GitHub Encrypted Secrets, 1Password Secrets Automation) immediately after generation.
Pass the key in the Authorization header using the Bearer scheme — the
same pattern as JWT tokens. The API automatically distinguishes between a JWT and an API key by
the gl_live_ prefix.
# curl example — key from environment variable
curl -s \
-H "Authorization: Bearer $GRIDLOCK_API_KEY" \
-H "Content-Type: application/json" \
https://lockthegrid.com/api/threats/summary
// Node.js example — key from environment variable
const API_KEY = process.env.GRIDLOCK_API_KEY; // never hardcode
async function runThreatResearch(domain) {
const res = await fetch('https://lockthegrid.com/api/agents/run', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
agent: 'threat-researcher',
target: domain,
options: { include_cve: true }
})
});
if (res.status === 403) {
throw new Error('API key lacks write scope. Regenerate with write permissions.');
}
if (!res.ok) {
const err = await res.json();
throw new Error(err.error);
}
return res.json();
}
Every API key is assigned a scope that restricts what actions it can take. Follow the principle of least privilege: request only the scope your integration actually needs. Granting admin scope to a routine reporting script is unnecessary and dangerous.
| Scope | What It Allows | Use When |
|---|---|---|
| read | All GET requests. Can retrieve any data but cannot create, modify, or delete anything. | Read-only dashboards, reporting tools, monitoring integrations that pull data from Gridlock. |
| write | All read permissions plus the ability to create and update resources: trigger agent runs, create tickets, update settings, submit data. | Automation scripts that trigger agent runs, integrations that write records to Gridlock from external systems. |
| admin | All write permissions plus account management: invite and remove users, change billing, revoke API keys, modify organization settings. | Internal account provisioning workflows and admin tooling only. Never use for routine automation. |
An admin-scoped key can revoke all other API keys, modify billing, and invite or remove team members. Treat an admin key with the same care as root credentials. Create a purpose-specific admin key for a specific task, use it, then revoke it promptly. Do not leave long-lived admin keys active in production environments.
Authentication endpoints are rate limited more strictly than general API endpoints to prevent credential brute-forcing. All limits are applied per IP address.
| Endpoint or Context | Limit | Window |
|---|---|---|
POST /api/auth/login |
10 attempts | per 60 seconds, per IP |
POST /api/auth/register |
5 attempts | per 60 seconds, per IP |
POST /api/auth/reset-password |
5 attempts | per 60 seconds, per IP |
| All other endpoints (JWT auth) | 100 requests | per minute, per IP |
| All other endpoints (API key auth) | 100 requests | per minute, per account |
Authentication failures return either 401 Unauthorized or 403 Forbidden.
The error field provides a specific, human-readable message. These messages are
intentionally safe to surface in developer tooling — they never leak stack traces, database details,
or internal service names.
| HTTP Status | Error Message | Root Cause |
|---|---|---|
401 |
No authorization token provided |
The Authorization header is absent from the request. |
401 |
Invalid token format |
Header is present but not structured as Bearer <value>. |
401 |
Token expired. Please log in again. |
JWT has passed its 7-day expiry window. |
401 |
Token invalidated. Please log in again. |
User changed password or revoked sessions; token_version mismatch. |
401 |
Invalid API key |
Key does not exist or has been revoked. |
401 |
API key expired |
Key exists but has passed its configured expiry date. |
403 |
Insufficient permissions. Required scope: write |
The key's scope does not permit the requested action. |
401 |
Invalid email or password |
Login attempt failed — no matching account for the supplied credentials. |
This is a cybersecurity product. Authentication security must be impeccable. Follow these practices in every integration you build on the Gridlock API.
HttpOnly cookies in production — never in localStorage
or sessionStorage.401 to interrupt a user action.Secure and SameSite=Strict cookie attributes alongside
HttpOnly to prevent cross-origin cookie exposure.To rotate a key without causing a service interruption: (1) generate the replacement key, (2) deploy the updated secret to all consumers, (3) verify the new key is successfully authenticating, then (4) revoke the old key. Always complete steps 1–3 before step 4. Revoking first causes a window of failed requests that is entirely avoidable.
// auth.js — complete JWT login and authenticated request pattern
const BASE_URL = 'https://lockthegrid.com/api';
async function login(email, password) {
const res = await fetch(`${BASE_URL}/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
if (!res.ok) {
const { error } = await res.json();
throw new Error(error);
}
const { token, expires_at } = await res.json();
// In production: set via HttpOnly cookie from server-side
// In this example: sessionStorage (acceptable for short-lived demos only)
sessionStorage.setItem('gl_token', token);
sessionStorage.setItem('gl_token_expires', expires_at);
return token;
}
function getToken() {
const token = sessionStorage.getItem('gl_token');
const expires = sessionStorage.getItem('gl_token_expires');
if (!token || !expires) return null;
if (new Date(expires) <= new Date()) {
sessionStorage.removeItem('gl_token');
return null; // expired — caller should re-authenticate
}
return token;
}
async function apiRequest(path, options = {}) {
const token = getToken();
if (!token) throw new Error('Not authenticated');
const res = await fetch(`${BASE_URL}${path}`, {
...options,
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
...(options.headers || {})
}
});
if (res.status === 401) {
sessionStorage.removeItem('gl_token');
throw new Error('Session expired. Please log in again.');
}
if (!res.ok) {
const { error } = await res.json();
throw new Error(error);
}
return res.json();
}
Base URL, request and response format, status codes, rate limits, versioning, and getting your first API key.
Configure Gridlock to push events to your endpoints, with signature verification and retry behavior for secure webhook handling.
The full endpoint reference — every route, required parameters, response schemas, and example requests for all Gridlock API resources.