API Overview
DockWallet provides a REST API that exposes all backend functionality programmatically — from authentication to pass management.
Base URL: https://<your-server>:8080/api
Version: 0.2.0
Auth: Bearer Token (JWT), valid for 7 days
Authentication
All protected endpoints require a JWT token in the Authorization header:
Authorization: Bearer <token>
The token is returned upon successful login.
Health & Version
GET /api/health
Returns the current backend status.
Response
{
"status": "ok",
"version": "0.2.0"
}
GET /api/version
Detailed version information.
Response
{
"version": "0.2.0",
"name": "dockwallet-backend",
"nodeVersion": "v20.x.x",
"uptime": 3600
}
GET /api/dockerhub/latest
Returns the latest published versions of backend and frontend on Docker Hub. Used for update checks in the admin interface.
Response
{
"backend": "0.2.0",
"frontend": "0.2.0"
}
Auth – /api/auth
GET /api/auth/config
Returns whether public registration is enabled.
Response
{
"registrationEnabled": true
}
POST /api/auth/register
Registers a new user. Requires USER_REGISTRATION=true in the configuration. A verification email is sent after registration.
Request Body
{
"username": "string",
"email": "string",
"password": "string (min. 8 characters)"
}
Response 201
{
"message": "Registration successful. Please confirm your email."
}
Errors
| Status | Description |
|---|---|
400 | Required fields missing or password too short |
403 | Registration is disabled |
409 | Email or username already taken |
GET /api/auth/verify-email?token=<token>
Confirms the email address using the token sent by mail. The account is activated upon success.
Query Parameter
token– Verification token from the email
Response 200
{
"message": "Email confirmed. You can now log in."
}
POST /api/auth/login
Logs in a user. Accepts either username or email in the username field. If MFA is enabled, the MFA code must be included in a second step.
Request Body
{
"username": "string (username or email)",
"password": "string",
"mfaToken": "string (optional, 6 digits)"
}
Response 200 – Without MFA or after successful MFA
{
"token": "eyJ...",
"user": {
"id": 1,
"email": "user@example.com",
"username": "alice",
"is_admin": false
}
}
Response 200 – MFA required (no token provided)
{
"mfaRequired": true
}
Errors
| Status | Description |
|---|---|
400 | Required fields missing |
401 | Invalid credentials or MFA code |
403 | Account not active |
GET /api/auth/me
Returns the profile of the currently logged-in user.
Response
{
"id": 1,
"username": "alice",
"first_name": "Alice",
"last_name": "Smith",
"email": "alice@example.com",
"is_admin": false,
"is_active": true,
"mfa_enabled": false,
"created_at": "2024-01-01T00:00:00.000Z"
}
PUT /api/auth/profile
Updates the current user's profile.
Request Body
{
"username": "string",
"email": "string",
"first_name": "string (optional)",
"last_name": "string (optional)"
}
Response – Updated user profile
Errors
| Status | Description |
|---|---|
400 | Required fields missing |
409 | Email or username already taken |
POST /api/auth/forgot-password
Initiates the password reset flow. Sends an email with a reset link (token valid for 15 minutes). The response is intentionally generic to prevent user enumeration.
Request Body
{
"email": "string"
}
Response
{
"message": "If the email exists, a message has been sent."
}
POST /api/auth/reset-password
Resets the password using a valid reset token.
Request Body
{
"token": "string",
"password": "string (min. 8 characters)"
}
Response
{
"message": "Password changed successfully."
}
MFA (TOTP) – /api/auth/mfa
POST /api/auth/mfa/setup
Generates a new TOTP secret and returns a QR code that can be scanned with an authenticator app (e.g. Google Authenticator).
Response
{
"secret": "JBSWY3DPEHPK3PXP",
"qrCode": "data:image/png;base64,..."
}
POST /api/auth/mfa/enable
Enables MFA for the account. Must be called with a valid TOTP code after setup.
Request Body
{
"token": "string (6-digit TOTP code)"
}
Response
{
"message": "MFA enabled"
}
POST /api/auth/mfa/disable
Disables MFA and deletes the stored secret. Requires one final confirmation via TOTP code.
Request Body
{
"token": "string (6-digit TOTP code)"
}
Response
{
"message": "MFA disabled"
}
GET /api/auth/mfa/smtp-status
Returns whether the SMTP server is configured (required for MFA via email).
Response
{
"smtpConfigured": true
}
POST /api/auth/mfa/send-code
Sends the current TOTP code via email. Only available if SMTP is configured.
Response
{
"message": "Code sent"
}
Passkeys (WebAuthn) – /api/auth/passkey
Passkeys enable passwordless login using biometric methods or hardware keys.
POST /api/auth/passkey/register/options
Starts the passkey registration. Returns WebAuthn options for the browser.
Response – WebAuthn PublicKeyCredentialCreationOptions
POST /api/auth/passkey/register/verify
Verifies and saves the newly registered passkey.
Request Body
{
"credential": { "...": "WebAuthn AuthenticatorAttestationResponse" },
"deviceName": "string (optional, e.g. 'MacBook Pro')"
}
Response
{
"message": "Passkey registered"
}
POST /api/auth/passkey/login/options
Returns WebAuthn authentication options. No token required.
Response – WebAuthn PublicKeyCredentialRequestOptions
POST /api/auth/passkey/login/verify
Verifies the WebAuthn response and returns a JWT token on success.
Request Body
{
"credential": { "...": "WebAuthn AuthenticatorAssertionResponse" }
}
Response
{
"token": "eyJ...",
"user": {
"id": 1,
"username": "alice",
"email": "alice@example.com",
"is_admin": false
}
}
GET /api/auth/passkeys
Lists all registered passkeys for the logged-in user.
Response
[
{
"id": 1,
"device_name": "MacBook Pro",
"created_at": "2024-01-01T00:00:00.000Z"
}
]
DELETE /api/auth/passkeys/:id
Deletes a passkey by its ID.
Response
{
"message": "Passkey deleted"
}
Delete Account – /api/auth/delete-account
POST /api/auth/delete-account/request
Sends a confirmation email to delete the account. Admin accounts cannot be deleted.
Response
{
"message": "Confirmation email has been sent."
}
GET /api/auth/delete-account/confirm?token=<token>
Permanently deletes the account. All associated passes are also deleted.
Query Parameter
token– Token from the confirmation email
Response
{
"message": "Account deleted."
}
Passes – /api/passes
All endpoints require authentication. Users can only access their own passes.
GET /api/passes
Returns all passes belonging to the logged-in user.
Response
[
{
"id": 1,
"user_id": 1,
"type": "boardingPass",
"description": "LH 123 · FRA → JFK",
"airline": "Lufthansa",
"flight_number": "LH123",
"origin": "FRA",
"destination": "JFK",
"barcode_value": "M1MUSTERMANN/MAX ABCDEF FRAJFKLH 0123 123Y00B00000 100",
"barcode_format": "PKBarcodeFormatAztec",
"background_color": "#003366",
"label_color": "#ffffff",
"foreground_color": "#ffffff",
"is_favorite": false,
"created_at": "2024-01-01T00:00:00.000Z",
"updated_at": "2024-01-01T00:00:00.000Z"
}
]
POST /api/passes
Creates a pass manually (without a file).
Request Body – Pass fields directly as JSON
Response 201 – The created pass
POST /api/passes/upload
Imports a pass from a .pkpass file (as a Base64-encoded string). The file is unpacked and parsed server-side.
Request Body
{
"file": "string (Base64-encoded content of the .pkpass file)"
}
Response 201 – The imported pass
Errors
| Status | Description |
|---|---|
400 | No file provided or parsing error |
POST /api/passes/import-url
Downloads a .pkpass file from a URL and imports it. Useful for reading QR code links.
Request Body
{
"url": "https://example.com/ticket.pkpass"
}
Response 201 – The imported pass
Errors
| Status | Description |
|---|---|
400 | No URL, invalid URL, or download failed |
PATCH /api/passes/:id/favorite
Marks or unmarks a pass as a favorite. Favorited passes are sorted to the top of the pass list.
Request Body
{
"is_favorite": true
}
Response
{
"success": true,
"is_favorite": true
}
Errors
| Status | Description |
|---|---|
400 | is_favorite is not a boolean |
404 | Pass not found |
DELETE /api/passes/:id
Deletes a pass belonging to the logged-in user.
Response
{
"success": true
}
Errors
| Status | Description |
|---|---|
404 | Pass not found or belongs to another user |
Sync – /api/sync
The sync API is used by the Android app to synchronize passes across devices. All endpoints require authentication.
POST /api/sync/register-device
Registers a new device and returns a sync token for that device. The token is stored alongside the device name and used to track the last sync time.
Request Body
{
"device_name": "string (optional, e.g. 'Pixel 9')"
}
Response
{
"sync_token": "abc123..."
}
GET /api/sync/devices
Lists all registered devices for the logged-in user.
Response
[
{
"id": 1,
"device_name": "Pixel 9",
"last_sync": "2024-06-01T12:00:00.000Z",
"created_at": "2024-01-01T00:00:00.000Z"
}
]
DELETE /api/sync/devices/:id
Removes a registered device.
Response
{
"success": true
}
Errors
| Status | Description |
|---|---|
404 | Device not found |
GET /api/sync/passes
Returns passes for the logged-in user. Supports incremental sync via the optional since query parameter. After fetching, the device's last_sync timestamp is updated automatically.
Query Parameter
since(optional) – ISO 8601 timestamp. If provided, only passes updated after this time are returned.
Response
{
"passes": [...],
"server_time": "2024-06-01T12:00:00.000Z",
"count": 5
}
POST /api/sync/passes
Uploads a new pass from a device. Accepts either a Base64-encoded .pkpass file or pre-parsed pass data.
Request Body
{
"file": "string (Base64-encoded .pkpass, mutually exclusive with pass_data)"
}
or
{
"pass_data": { "...": "pre-parsed pass fields" }
}
Response 201 – The created pass
Errors
| Status | Description |
|---|---|
400 | Neither file nor pass_data provided, or parsing error |
DELETE /api/sync/passes/:id
Deletes a pass via the sync API.
Response
{
"success": true
}
Errors
| Status | Description |
|---|---|
404 | Pass not found |
PATCH /api/sync/passes/:id/favorite
Marks or unmarks a pass as a favorite via the sync API.
Request Body
{
"is_favorite": true
}
Response
{
"success": true,
"is_favorite": true
}
GET /api/sync/status
Returns a summary of the user's sync state: total pass count and all registered devices with their last sync times.
Response
{
"pass_count": 12,
"devices": [
{
"id": 1,
"device_name": "Pixel 9",
"last_sync": "2024-06-01T12:00:00.000Z"
}
],
"server_time": "2024-06-01T12:05:00.000Z"
}
Admin – /api/admin
All admin endpoints require a valid JWT token and is_admin: true.
GET /api/admin/users
Lists all registered users with statistics.
Response
[
{
"id": 1,
"username": "alice",
"first_name": "Alice",
"last_name": "Smith",
"email": "alice@example.com",
"is_admin": true,
"is_active": true,
"mfa_enabled": false,
"created_at": "2024-01-01T00:00:00.000Z",
"pass_count": 5,
"passkey_count": 1,
"has_mfa_secret": false
}
]
GET /api/admin/users/:id
Returns a single user.
POST /api/admin/users
Creates a new user directly (email confirmation can be skipped by setting is_active: true).
Request Body
{
"username": "string",
"email": "string",
"password": "string (min. 8 characters)",
"first_name": "string (optional)",
"last_name": "string (optional)",
"is_admin": false,
"is_active": true
}
Response 201 – The created user
PUT /api/admin/users/:id
Updates a user's profile, roles, and status.
Request Body
{
"username": "string",
"email": "string",
"first_name": "string (optional)",
"last_name": "string (optional)",
"is_admin": false,
"is_active": true
}
POST /api/admin/users/:id/reset-password
Resets a user's password.
Request Body
{
"password": "string (min. 8 characters)"
}
Response
{
"message": "Password reset"
}
POST /api/admin/users/:id/reset-mfa
Resets a user's MFA (deletes the secret and disables MFA).
Response
{
"message": "MFA reset"
}
DELETE /api/admin/users/:id/passkeys
Deletes all passkeys for a user.
Response
{
"message": "All passkeys deleted"
}
DELETE /api/admin/users/:id
Deletes a user and all their passes (CASCADE). Admins cannot delete themselves.
Response
{
"message": "User deleted"
}
Errors
| Status | Description |
|---|---|
400 | Cannot delete own account |
404 | User not found |