Skip to main content
Version: Current (Latest)

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

StatusDescription
400Required fields missing or password too short
403Registration is disabled
409Email 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

StatusDescription
400Required fields missing
401Invalid credentials or MFA code
403Account 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

StatusDescription
400Required fields missing
409Email 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

StatusDescription
400No 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

StatusDescription
400No 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

StatusDescription
400is_favorite is not a boolean
404Pass not found

DELETE /api/passes/:id

Deletes a pass belonging to the logged-in user.

Response

{
"success": true
}

Errors

StatusDescription
404Pass 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

StatusDescription
404Device 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

StatusDescription
400Neither file nor pass_data provided, or parsing error

DELETE /api/sync/passes/:id

Deletes a pass via the sync API.

Response

{
"success": true
}

Errors

StatusDescription
404Pass 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

StatusDescription
400Cannot delete own account
404User not found