HTTP API
Status: implemented sign-in, session management, smoke, and OpenAPI routes. Account, API-key, usage, payment, and redemption routes are planned. Updated: 29 October 2026.
Public routes
| Method | Path | Authentication | Response |
|---|---|---|---|
POST |
/login/v1/google |
None | JSON access/refresh token pair. |
GET |
/hello |
None | text/plain smoke response. |
GET |
/v3/api-docs/** |
None | OpenAPI JSON. |
GET |
/swagger-ui/**, /swagger-ui.html |
None | Swagger UI. |
Protected routes
All other routes require a backend access token:
Authorization: Bearer <accessToken>
The token must carry a single-element role list containing user, validate against a retained signing key, and reference an active, unexpired login_session. Refresh tokens carry ["refresh"] and are rejected on ordinary routes.
Session and API key management are the protected route groups so far. The same boundary will cover the planned profile, usage, and payment endpoints.
Sessions
| Method | Path | Response |
|---|---|---|
GET |
/sessions/v1 |
The caller’s active, unexpired sessions, newest first. |
GET |
/sessions/v1/current |
The session bound to the calling token. |
POST |
/sessions/v1/logout |
Revokes the calling session; add ?all=true to revoke every session of the account. |
DELETE |
/sessions/v1 |
Revokes the sessions listed in the body { "ids": [42, 43] } (1–100 IDs), such as sign-ins on other devices. |
Session items are { "id": 42, "expiresAt": 1793270400, "current": true }. expiresAt is in UTC Unix seconds, and current marks the session of the calling token. The signed sid reference is never returned. Revocation responses are { "revoked": <count> }.
Ownership comes only from the access token. Revocation applies on the next request: the access and refresh tokens of a revoked session fail verification with 401. A repeated logout returns revoked: 0. Bulk revocation skips missing, already revoked, and other users’ IDs, so revoked can be lower than the number of IDs sent. An empty list returns 400 SESSION_IDS_REQUIRED; more than 100 IDs or a non-positive ID returns 400 INVALID_SESSION_IDS.
API keys
| Method | Path | Response |
|---|---|---|
POST |
/api-keys/v1 |
201 with { "apiKey": {...}, "key": "ak_..." } for body { "name": "CI", "description": "optional" }. |
GET |
/api-keys/v1 |
All of the caller’s keys, revoked included, newest first. |
DELETE |
/api-keys/v1/{id} |
Revokes one owned key; returns { "revoked": <count> }. |
Key items are { "id": 7, "name": "CI", "description": null, "revoked": false, "createdAt": 1793270400, "updatedAt": 1793270400 } with UTC Unix-second timestamps. The plaintext key appears only in the create response; the server stores just its digest. A blank name returns 400 API_KEY_NAME_REQUIRED; a name over 100 or description over 500 characters returns 400 INVALID_API_KEY_NAME / INVALID_API_KEY_DESCRIPTION. Revoking a missing, foreign or already revoked key returns revoked: 0; a non-positive ID returns 400 INVALID_API_KEY_ID. Keys are not yet accepted as request credentials.
Google sign-in
Request
POST /login/v1/google
Content-Type: application/json
{
"idToken": "<Google ID token>"
}
The token must be issued for the configured GOOGLE_CLIENT_ID. The backend verifies the signature, issuer, audience, expiry, subject, and verified email before writing account data.
Success
HTTP/1.1 200 OK
Content-Type: application/json
{
"accessToken": "<signed JWT>",
"refreshToken": "<signed JWT>"
}
The response contains only token strings. Each JWT carries standard iat (issued at) and exp (expires at) claims as UTC Unix seconds. The access token expires before the refresh token. Refresh tokens share the session but cannot call ordinary routes; no refresh exchange endpoint exists yet.
Failure
| Condition | Status | Code |
|---|---|---|
Blank idToken |
400 |
GOOGLE_TOKEN_REQUIRED |
| Invalid Google signature, issuer, audience, or time claims | 401 |
INVALID_GOOGLE_TOKEN |
| Missing subject, email, verified email, or expiry | 401 |
UNVERIFIED_GOOGLE_IDENTITY |
| Unexpected server or database failure | 500 |
INTERNAL_ERROR |
Smoke endpoint
GET /hello
Returns Hello, world! as text/plain without authentication, persistence, or network calls.
Error contract
Application and security failures return one JSON shape:
{
"code": "INVALID_ACCESS_TOKEN",
"message": "Invalid access token",
"traceId": "0f1e2d3c-4b5a-6978-8796-a5b4c3d2e1f0",
"status": 401
}
| Field | Meaning |
|---|---|
code |
Stable machine-readable failure code. |
message |
Client-safe text. It never includes SQL, stack traces, or secrets. |
traceId |
Correlation ID written to the server log with the internal failure. |
status |
HTTP status repeated in the body. |
Security filter failures use the same shape through the servlet response extension. MVC protocol failures keep Spring’s status and headers but use INVALID_HTTP_REQUEST and Request could not be processed. Unexpected failures return 500 INTERNAL_ERROR and Something went wrong.
OpenAPI and Swagger UI
The app includes Springdoc Web MVC UI. The generated document is available at /v3/api-docs, and the UI is available at /swagger-ui/index.html (or /swagger-ui.html).
Controller @Operation summaries and Markdown descriptions describe client-visible behavior. They do not add response, parameter, or security annotations that the implementation does not enforce.
Planned routes
These are not implemented and should not be called:
- Refresh exchange.
- Profile read/update.
- API-key creation, listing, and revocation.
- Usage summaries.
- Razorpay order creation, payment verification, and purchase status.
- Redeem-code consumption.
The schema and entity projections for keys, subscriptions, usage, and redemption exist, but no controller or service exposes them.
Related code
| Path | Owns |
|---|---|
hello/HelloController.kt |
Public smoke route. |
account/login/LoginController.kt |
Google sign-in route and OpenAPI description. |
account/session/SessionController.kt |
Session list, current-session, logout, and bulk revocation routes. |
account/token/TokenFunctions.kt |
extractUserId / extractSessionId from the authenticated token. |
platform/config/SecurityConfiguration.kt |
Public-route allowlist, bearer authentication, and security error responses. |
platform/exception/ApiExceptionHandler.kt |
MVC exception normalization and the common error payload. |
platform/exception/ApplicationException.kt |
Error code, status, safe message, trace ID, and JSON conversion. |
platform/exception/ApplicationExceptionExtensions.kt |
Servlet error writing for security filters. |