Skip to content

Hub API proposal (draft before implementation)


Base URL

Your can find Bearicorn API hosted on yourbearicorndomain.com/api

Authentication model

The current backend uses a cookie/JWT session flow via /api/login and /api/logout, with permission guards on endpoints.

The intended flow:

  1. Client authenticates via POST /api/login.
  2. Server sets an HttpOnly session/JWT cookie.
  3. All subsequent requests are authenticated by that cookie.

Response style

The current code typically returns plain DTO objects or 204 No Content.

  • 201 Created usually returns { "id": number }
  • 204 No Content returns an empty body
  • 200 OK returns DTO objects or arrays

This draft keeps that style on purpose.

Error model (simple, consistent shape)

Example error response:

json
{
  "statusCode": 403,
  "message": "User not authorized for the room",
  "error": "Forbidden"
}

2. Auth & session endpoints

2.1 Login

POST /api/login

Authenticate a user and start a session.

Request example

json
{
  "email": "[email protected]",
  "password": "Sup3rSecret!"
}

Response 200 example

json
{
  "jwtHash": "sha256-hash-of-jwt"
}

2.2 Logout

POST /api/logout

End the current session (clear cookie).

Response 204

No body.


3. Users (existing endpoints)

This section describes what is already in the codebase.

3.1 Create user (invite flow)

POST /api/user

Create a new invited user.

Request example

json
{
  "email": "[email protected]",
  "fullName": "New User",
  "password": "TempPass123!"
}

Response 201

json
{
  "id": 123
}

3.2 Activate invited user

POST /api/user/activate

Activate the invited user (provide keys + final credentials).

Request example

json
{
  "fullName": "New User",
  "password": "FinalStrongPass!",
  "privateKey": "<encrypted-private-key>",
  "publicKey": "<public-key>",
  "privateKeyChecksum": "checksum"
}

Response 201

json
{
  "id": 123
}

3.3 Get current user

GET /api/user/me

Return details about the currently authenticated user.

Response 200 example

json
{
  "id": 7,
  "fullName": "John Doe",
  "email": "[email protected]",
  "privateKey": "<encrypted-private-key>",
  "publicKey": "<public-key>",
  "roles": ["ADMIN"],
  "tokens": ["USER__ME", "CHAT__WRITE"],
  "jwtHash": "sha256-hash-of-jwt",
  "photoId": "42"
}

3.4 Update current user (profile + avatar)

POST /api/user/me

Update the current user profile. This is a multipart/form-data request.

Request example

  • Content-Type: multipart/form-data
  • fields:
    • fullName: string
    • photo: file (optional)

Response 204

No body.


3.5 Search users

GET /api/user?search=...

Search users by a string, or load by IDs.

Query params (aligned with DTO)

  • search: string (or alternatively ids[])
  • omit: string[] (optional)

Response 200 example

json
[
  {
    "id": 7,
    "fullName": "John Doe",
    "email": "[email protected]",
    "publicKey": "<public-key>",
    "photoId": "42"
  }
]

3.6 List all users (admin)

GET /api/user/all

Return all users with admin-facing fields.

Response 200 example

json
[
  {
    "id": 7,
    "fullName": "John Doe",
    "email": "[email protected]",
    "publicKey": "<public-key>",
    "photoId": "42",
    "roles": ["ADMIN"],
    "createdAt": "2026-01-10T12:00:00.000Z",
    "createdBy": 1
  }
]

3.7 Get user detail

GET /api/user/:id

Return a single user's public detail.

Response 200 example

json
{
  "id": 9,
  "fullName": "Jane Smith",
  "email": "[email protected]",
  "publicKey": "<public-key>",
  "photoId": "77"
}

4. Users (proposed missing endpoints)

These are not in controllers yet, but they are the typical operations you explicitly asked for.

4.1 Admin edit user

PUT /api/user/:id

Admin updates basic user fields.

Request example

json
{
  "fullName": "Jane Smith",
  "email": "[email protected]",
  "state": "ACTIVE"
}

Response 204

No body.


4.2 Delete user (soft delete)

DELETE /api/user/:id

Soft-delete a user (for example transition to DELETED).

Response 204

No body.


4.3 Change user roles / permissions

PUT /api/user/:id/roles

Change user roles (ADMIN, USER, INVITEE).

Request example

json
{
  "roles": ["USER"]
}

Response 204

No body.


4.4 Reset password flows

A clean design usually separates request/confirm and supports admin reset.

A) Self-service: request reset

POST /api/user/password/reset/request

Request
json
{
  "email": "[email protected]"
}
Response 204

No body (do not leak whether the user exists).

B) Self-service: confirm reset

POST /api/user/password/reset/confirm

Request
json
{
  "token": "reset-token",
  "newPassword": "NewStrongPass123!"
}
Response 204

No body.

C) Admin reset for a specific user

POST /api/user/:id/password/reset

Request example
json
{
  "newPassword": "TempPass123!",
  "invalidateSessions": true
}
Response 204

No body.


5. Rooms (chat) endpoints

Rooms and messages already have a solid baseline in the code. The contract below mirrors the current controllers.

5.1 Create room

POST /api/room

Create a room and configure access for other users.

Request example

json
{
  "name": "Incident 2026-01-24",
  "messageExpiration": 86400,
  "isPublic": false,
  "encryptionKey": "<room-key-for-creator>",
  "otherUsers": [
    {
      "userId": 9,
      "encryptionKey": "<room-key-for-user-9>"
    }
  ]
}

Response 201

json
{
  "id": 55
}

5.2 List rooms

GET /api/room

List rooms for the current user, including unread counts.

Response 200 example

json
[
  {
    "id": 55,
    "name": "Incident 2026-01-24",
    "lastActivity": "2026-01-24T10:00:00.000Z",
    "unreadCount": 3,
    "users": [{ "userId": 7 }, { "userId": 9 }],
    "blockedBy": null
  }
]

5.3 Get room detail

GET /api/room/:id

Return room detail, including the encryption key for the current user.

Response 200 example

json
{
  "id": 55,
  "name": "Incident 2026-01-24",
  "encryptionKey": "<room-key-for-current-user>",
  "isEditor": true,
  "isOwner": true,
  "yolo": {},
  "messageExpiration": 86400,
  "isPublic": false,
  "users": [
    { "userId": 7, "accessLevel": "OWNER" },
    { "userId": 9, "accessLevel": "EDITOR" }
  ],
  "lastActivity": "2026-01-24T10:00:00.000Z",
  "unreadCount": 3,
  "blockedBy": null
}

5.4 Update room

POST /api/room/:id

Update room fields (name, messageExpiration, yolo).

Request example

json
{
  "name": "Incident 2026-01-24 (resolved)",
  "messageExpiration": 604800,
  "yolo": {
    "color": "red"
  }
}

Response 204

No body.


5.5 Delete room

DELETE /api/room/:id

Delete a room.

Response 204

No body.


5.6 Mark room as read

POST /api/room/:id/read

Mark all messages in the room as read.

Response 204

No body.


6. Room permissions endpoints

Room permissions follow the same pattern used by buckets and two-factor records.

6.1 Add user to room

POST /api/room/:id/user/:userId

Grant room access to another user.

Request example

json
{
  "encryptionKey": "<room-key-for-target-user>",
  "accessLevel": "EDITOR"
}

Response 204

No body.


6.2 Change room access level

PUT /api/room/:id/user/:userId

Change access level for a room member.

Request example

json
{
  "accessLevel": "VIEWER"
}

Response 204

No body.


6.3 Remove user from room

DELETE /api/room/:id/user/:userId

Remove a user's access to a room.

Response 204

No body.


6.4 Leave room (current user)

DELETE /api/room/:id/user/me

Current user removes their own access.

Response 204

No body.


7. Messages endpoints (post, edit, delete)

7.1 Create message

POST /api/room/:roomId/message

Create a message in a room.

Request example

json
{
  "messageType": "TEXT",
  "body": "I need a review on PR #42",
  "replyToMessageId": null,
  "fileId": null,
  "mentionIds": [9]
}

Response 201

json
{
  "id": 9001
}

7.2 Update message

POST /api/room/:roomId/message/:id

Edit an existing message.

Request example

json
{
  "body": "I need a review on PR #42 (urgent)",
  "mentionIds": [9]
}

Response 204

No body.


7.3 Delete message

DELETE /api/room/:roomId/message/:id

Delete a message.

Response 204

No body.


7.4 List messages (pagination via take + until)

GET /api/room/:roomId/message?take=50&until=2026-01-24T10:00:00.000Z&sendEncKey=true

Response 200 example

json
{
  "messages": [
    {
      "id": 9001,
      "messageType": "TEXT",
      "body": "I need a review on PR #42",
      "fileId": null,
      "createdAt": "2026-01-24T10:01:00.000Z",
      "createdBy": 7,
      "versionedAt": null,
      "replyToMessage": null,
      "replyToMessageDeleted": null,
      "reactions": [],
      "expiresAt": null
    }
  ],
  "timestamp": "2026-01-24T10:02:00.000Z",
  "encryptionKey": "<room-key-for-current-user>"
}

7.5 Poll updates since a timestamp

GET /api/room/:roomId/message/update?since=2026-01-24T10:00:00.000Z

Response 200 example

json
{
  "created": [],
  "updated": [],
  "deleted": [8999],
  "timestamp": "2026-01-24T10:05:00.000Z"
}

7.6 Message info (read receipts / history metadata)

GET /api/room/:roomId/message/:id

Response 200 example

json
{
  "createdAt": "2026-01-24T10:01:00.000Z",
  "createdBy": 7,
  "versionedAt": "2026-01-24T10:03:00.000Z",
  "readBy": [7, 9]
}

8. Buckets & files endpoints

In Hub, files live inside buckets, so bucket remains the parent resource.

8.1 Create bucket

POST /api/bucket

Request example

json
{
  "name": "Legal",
  "treeStructure": "<encrypted-tree-json>",
  "encryptionKey": "<bucket-key-for-creator>"
}

Response 201

json
{
  "id": 12
}

8.2 List buckets

GET /api/bucket

Response 200 example

json
[
  {
    "id": 12,
    "name": "Legal",
    "treeStructure": "<encrypted-tree-json>",
    "encryptionKey": "<bucket-key-for-current-user>",
    "yolo": {},
    "isEditor": true,
    "isOwner": true
  }
]

8.3 Get bucket detail

GET /api/bucket/:id

Response 200 example

json
{
  "id": 12,
  "name": "Legal",
  "treeStructure": "<encrypted-tree-json>",
  "encryptionKey": "<bucket-key-for-current-user>",
  "yolo": {},
  "isEditor": true,
  "isOwner": true,
  "users": [
    { "userId": 7, "accessLevel": "OWNER" },
    { "userId": 9, "accessLevel": "EDITOR" }
  ],
  "createdAt": "2026-01-20T08:00:00.000Z",
  "createdBy": 1,
  "versionedAt": "2026-01-22T09:00:00.000Z"
}

8.4 Update bucket

POST /api/bucket/:id

Request example

json
{
  "name": "Legal (2026)",
  "treeStructure": "<encrypted-tree-json-v2>",
  "yolo": {
    "icon": "scale"
  }
}

Response 204

No body.


9. Bucket permissions endpoints

9.1 Add user to bucket

POST /api/bucket/:id/user/:userId

Request example

json
{
  "encryptionKey": "<bucket-key-for-target-user>",
  "accessLevel": "EDITOR"
}

Response 204

No body.


9.2 Change bucket access level

PUT /api/bucket/:id/user/:userId

Request example

json
{
  "accessLevel": "VIEWER"
}

Response 204

No body.


9.3 Remove user from bucket

DELETE /api/bucket/:id/user/:userId

Response 204

No body.


9.4 Leave bucket (current user)

DELETE /api/bucket/:id/user/me

Response 204

No body.


10. Files: upload, detail, rename, delete

10.1 Upload file (TUS)

POST /api/tus

Uploads follow the TUS protocol. Metadata is required and mirrors the existing DTO shape:

  • bucketId
  • fileName
  • mimeType
  • decryptedSize
  • overrideFileId (optional; used to replace an existing file)

Example TUS metadata header

text
Upload-Metadata: bucketId MTI=,fileName Zml1dmEucGRm,mimeType YXBwbGljYXRpb24vcGRm,decryptedSize MTA0ODU2

Note: values are base64-encoded per the TUS specification.

Response

  • Standard TUS response (201 Created + Location header)

10.2 Get file detail

GET /api/bucket/:bucketId/file/:fileId

Response 200 example

json
{
  "id": 501,
  "name": "contract.pdf",
  "mimeType": "application/pdf",
  "size": 345678,
  "decryptedSize": 345678,
  "createdAt": "2026-01-24T09:00:00.000Z",
  "createdBy": 7,
  "versionedAt": "2026-01-24T09:05:00.000Z",
  "versionedBy": 7
}

10.3 Rename / edit file

POST /api/bucket/:bucketId/file/:fileId

Rename a file.

Request example

json
{
  "name": "contract-final.pdf"
}

Response 204

No body.


10.4 Download file

GET /api/bucket/:bucketId/file/:fileId/download

Download the binary content. (Implementation may rewrite this path to the TUS handler.)

Response

  • 200 OK + binary data

10.5 Delete file

DELETE /api/bucket/:bucketId/file/:fileId

Delete a file.

Response 204

No body.


11. Files: proposed file-level permissions endpoints

Even if permissions are currently bucket-level, file-level ACLs may be useful in the future.

11.1 Add user access to file

POST /api/bucket/:bucketId/file/:fileId/user/:userId

Request example

json
{
  "accessLevel": "VIEWER",
  "encryptionKey": "<file-key-for-target-user>"
}

Response 204

No body.


11.2 Change user access on file

PUT /api/bucket/:bucketId/file/:fileId/user/:userId

Request example

json
{
  "accessLevel": "EDITOR"
}

Response 204

No body.


11.3 Remove user access from file

DELETE /api/bucket/:bucketId/file/:fileId/user/:userId

Response 204

No body.


12. Documents endpoints (editable text files)

Documents are text resources stored inside buckets.

12.1 Create document

POST /api/bucket/:bucketId/document

Request example

json
{
  "name": "Runbook",
  "body": "# Incident procedure\n\n1. Determine the scope"
}

Response 201

json
{
  "id": 801
}

12.2 Get document detail

GET /api/bucket/:bucketId/document/:documentId

Response 200 example

json
{
  "id": 801,
  "name": "Runbook",
  "body": "# Incident procedure\n\n1. Determine the scope",
  "createdAt": "2026-01-24T08:00:00.000Z",
  "createdBy": 7,
  "versionedAt": "2026-01-24T08:30:00.000Z",
  "versionedBy": 9
}

12.3 Update document

POST /api/bucket/:bucketId/document/:documentId

Partial update (name and/or body).

Request example

json
{
  "body": "# Incident procedure\n\n1. Determine the scope\n2. Inform the customer"
}

Response 204

No body.


12.4 Delete document

DELETE /api/bucket/:bucketId/document/:documentId

Response 204

No body.


12.5 Document history

GET /api/bucket/:bucketId/document/:documentId/history

Response 200 example

json
[
  {
    "uid": "550e8400-e29b-41d4-a716-446655440000",
    "state": "ACTIVE",
    "versionedAt": "2026-01-24T08:30:00.000Z",
    "versionedBy": 9,
    "nameChanged": false,
    "bodyChanged": true,
    "name": "Runbook"
  }
]

13. Notifications endpoints

Not explicitly requested, but they are part of the user-facing behavior already present in the app.

13.1 List notifications

GET /api/notification

Response 200 example (simplified)

json
{
  "read": [],
  "unread": [],
  "pinned": []
}

13.2 Overview (counts)

GET /api/notification/overview

Response 200 example

json
{
  "unread": 3,
  "pinned": 1
}

13.3 Mark all as read

POST /api/notification/read

Response 204

No body.


13.4 Mark a single notification state

  • POST /api/notification/:id/read
  • POST /api/notification/:id/unread
  • POST /api/notification/:id/pin
  • POST /api/notification/:id/unpin

Response 204

No body.


Because the goal is “documentation first, implementation second”, a good sequence would be:

  1. Add DTOs for the proposed missing admin endpoints:
    • UpdateUserRequest
    • UpdateUserRolesRequest
    • PasswordResetRequest/Confirm/...
  2. Decide whether file permissions are:
    • inherited from the bucket (simpler), or
    • explicit per-file/per-document ACLs.
  3. Confirm update semantics:
    • current style often uses POST /resource/:id,
    • alternative is PUT/PATCH for stricter REST semantics.
  4. Then implement controllers and services to match this document.

15. Quick index of core endpoints

Users

  • POST /api/user
  • DELETE /api/user/:id (proposed)
  • PUT /api/user/:id/roles (proposed)
  • POST /api/user/password/reset/request (proposed)

Rooms & messages

  • POST /api/room
  • POST /api/room/:roomId/message
  • POST /api/room/:roomId/message/:id
  • DELETE /api/room/:roomId/message/:id

Files & documents

  • POST /api/tus
  • GET /api/bucket/:bucketId/file/:fileId
  • POST /api/bucket/:bucketId/file/:fileId
  • POST /api/bucket/:bucketId/document
  • POST /api/bucket/:bucketId/document/:documentId

This document is intentionally a draft. The purpose is to let FE/BE align on the contract before the implementation details are finalized.