Loopin serves versioned HTTP endpoints under the /api/v1 context path. Examples in this document omit the host and show paths relative to /api/v1.
Interactive OpenAPI documentation (Swagger UI) is available at /api/swagger-ui.html to inspect and test endpoints. To authenticate requests in Swagger UI:
- Click the Authorize button.
- Enter your JWT Bearer token in the input field.
- Protected endpoints will automatically include the
Authorization: Bearer <token>header.
Use JSON for request and response bodies unless noted otherwise:
Content-Type: application/jsonProtected endpoints require a bearer token:
Authorization: Bearer <token>The examples use placeholder IDs and tokens only. Do not commit real JWTs, Google ID tokens, production hostnames, or credentials.
| Item | Convention |
|---|---|
| Public resource IDs | UUID strings exposed in API paths and DTOs. |
| Pagination | Spring pageable parameters such as page, size, and sort. |
| Date-time values | ISO-8601 local date-time strings, for example 2026-08-15T10:00:00. |
| Auth failures | Missing or invalid bearer tokens return 401 Unauthorized. |
| Authorization failures | Authenticated users without access return 403 Forbidden. |
POST /auth/google
Validates a Google ID token and returns a Loopin JWT.
Request:
{
"idToken": "<google-id-token>"
}Response 200 OK:
{
"token": "<jwt>",
"email": "alex@example.test",
"name": "Alex Smith",
"role": "USER"
}POST /users/register
Creates a local user record. This endpoint is public.
Request:
{
"email": "alex@example.test",
"name": "Alex Smith"
}Response 201 Created:
{
"id": "11111111-1111-4111-8111-111111111111",
"email": "alex@example.test",
"name": "Alex Smith",
"role": "USER"
}GET /users/me
Requires authentication.
Response 200 OK:
{
"id": "11111111-1111-4111-8111-111111111111",
"email": "alex@example.test",
"name": "Alex Smith",
"role": "USER"
}The following endpoints require an administrator role:
| Method | Path | Purpose |
|---|---|---|
GET |
/users |
List users. |
GET |
/users/{id} |
Read one user. |
PUT |
/users/{id}/role |
Update user role. |
DELETE |
/users/{id} |
Delete user. |
GET /me
Response 200 OK:
{
"id": "11111111-1111-4111-8111-111111111111",
"name": "Alex Smith",
"city": "Baku",
"bio": "Enjoys startup events and small-group meetups.",
"interests": []
}PUT /me
Request:
{
"name": "Alex Smith",
"city": "Baku",
"bio": "Enjoys startup events and small-group meetups."
}Response 200 OK returns the updated profile.
| Method | Path | Purpose |
|---|---|---|
GET |
/me/interests |
List current user's interests. |
PUT |
/me/interests |
Replace current user's interests. |
GET |
/me/badges |
List current user's earned badges. |
Update interests request:
{
"interests": [
{
"interestId": "44444444-4444-4444-8444-444444444444",
"weight": 1.0,
"source": "USER"
}
]
}GET /interests
Requires authentication.
Response 200 OK:
[
{
"id": "44444444-4444-4444-8444-444444444444",
"name": "Technology",
"slug": "technology",
"category": "Professional"
}
]Event enum values:
| Field | Values |
|---|---|
type |
EVENT, ACTIVITY |
category |
TECH, STARTUP, HR, EDUCATION, TRAVEL, SPORT, SOCIAL, LANGUAGE, CREATIVE, OTHER |
response status |
DRAFT, PUBLISHED, COMPLETED, CANCELLED |
GET /events
This endpoint is public.
Optional query parameters:
| Parameter | Description |
|---|---|
type |
Filter by EVENT or ACTIVITY. |
category |
Filter by event category. |
city |
Filter by city. |
isFree |
Filter by free or paid events. |
search |
Search title or description. |
startDate |
ISO date lower bound, for example 2026-08-01. |
endDate |
ISO date upper bound, for example 2026-08-31. |
page, size, sort |
Spring pageable controls. |
Response 200 OK is a Spring page containing event items.
GET /events/{id}
This endpoint is public.
GET /events/recommended?limit=10
Requires authentication.
POST /events
Requires authentication.
Request:
{
"title": "Founder Coffee Chat",
"description": "Small-group coffee meetup for early-stage founders.",
"type": "EVENT",
"category": "STARTUP",
"city": "Baku",
"address": "Nizami Street 10",
"startDateTime": "2026-08-15T10:00:00",
"endDateTime": "2026-08-15T12:00:00",
"isFree": true,
"price": 0,
"organizerName": "Loopin Community",
"imageUrl": "https://example.test/images/founder-coffee.jpg",
"interestIds": [
"44444444-4444-4444-8444-444444444444"
]
}Response 201 Created:
{
"id": "55555555-5555-4555-8555-555555555555",
"title": "Founder Coffee Chat",
"description": "Small-group coffee meetup for early-stage founders.",
"type": "EVENT",
"category": "STARTUP",
"city": "Baku",
"address": "Nizami Street 10",
"startDateTime": "2026-08-15T10:00:00",
"endDateTime": "2026-08-15T12:00:00",
"isFree": true,
"price": 0,
"organizerName": "Loopin Community",
"imageUrl": "https://example.test/images/founder-coffee.jpg",
"status": "PUBLISHED",
"interests": [],
"createdAt": "2026-07-07T12:00:00",
"updatedAt": "2026-07-07T12:00:00"
}The server owns the initial lifecycle status. Automatically approved events start as PUBLISHED; events awaiting moderation start as DRAFT. Do not include status in create or update requests. Lifecycle changes are handled separately from normal event editing; moderation approval and rejection continue to use the existing admin moderation endpoints.
PUT /events/{id}
Requires authentication and ownership.
The normal update request uses the same event detail fields as creation and does not accept status.
DELETE /events/{id}
Requires authentication and ownership. Returns 204 No Content.
Group enum values:
| Field | Values |
|---|---|
groupSize |
TWO, THREE, FOUR, FOUR_PLUS |
status |
OPEN, FULL, ARCHIVED, CANCELLED |
POST /groups
Requires authentication.
Request:
{
"eventId": "55555555-5555-4555-8555-555555555555",
"title": "Founder Coffee Table",
"groupSize": "FOUR_PLUS",
"maxMembers": 8,
"groupNote": "Meet near the main entrance five minutes early."
}Response 201 Created:
{
"id": "22222222-2222-4222-8222-222222222222",
"eventId": "55555555-5555-4555-8555-555555555555",
"adminId": "11111111-1111-4111-8111-111111111111",
"adminUsername": "Alex Smith",
"title": "Founder Coffee Table",
"groupSize": "FOUR_PLUS",
"maxMembers": 8,
"status": "OPEN",
"groupNote": "Meet near the main entrance five minutes early.",
"memberCount": 1,
"createdAt": "2026-07-07T12:00:00"
}| Method | Path | Auth | Purpose |
|---|---|---|---|
GET |
/groups/{groupId} |
Public | Read one group. |
PUT |
/groups/{groupId} |
Required | Update title, size, max members, and note. |
PATCH |
/groups/{groupId}/status |
Required | Update group status. |
Update group request:
{
"title": "Founder Coffee Table",
"groupSize": "FOUR_PLUS",
"maxMembers": 10,
"groupNote": "Bring one question for the group."
}Update group status request:
{
"status": "FULL"
}| Method | Path | Purpose |
|---|---|---|
POST |
/groups/{groupId}/members |
Add a member. |
GET |
/groups/{groupId}/members |
List group members. |
GET |
/groups/{groupId}/members/{userId} |
Read a group member by user ID. |
DELETE |
/groups/{groupId}/members/{userId} |
Remove a member. |
Writes require authentication and appropriate group permissions.
Join request status values are PENDING, APPROVED, and REJECTED.
POST /groups/{groupId}/join-requests
Requires authentication.
Request:
{
"message": "I would like to join and can arrive on time."
}Response 201 Created:
{
"id": "33333333-3333-4333-8333-333333333333",
"groupId": "22222222-2222-4222-8222-222222222222",
"userId": "11111111-1111-4111-8111-111111111111",
"status": "PENDING",
"message": "I would like to join and can arrive on time.",
"createdAt": "2026-07-07T12:05:00"
}| Method | Path | Purpose |
|---|---|---|
GET |
/groups/{groupId}/join-requests |
List requests for a group. |
GET |
/groups/{groupId}/join-requests/{requestId} |
Read one request. |
GET |
/me/group-join-requests |
List current user's requests. |
PATCH |
/groups/{groupId}/join-requests/{requestId}/approve |
Approve a request. |
PATCH |
/groups/{groupId}/join-requests/{requestId}/reject |
Reject a request. |
DELETE |
/groups/{groupId}/join-requests/{requestId} |
Delete a request. |
All join request endpoints require authentication. Approval and rejection require group admin permissions.
Loopin supports persisted group chat messages through REST and real-time delivery through WebSocket. See Real-Time Chat for the WebSocket protocol.
The REST chat controller currently uses the internal numeric group ID in the path.
GET /groups/{groupId}/messages
Requires authentication and group membership.
Response 200 OK:
[
{
"id": 1,
"groupId": 1,
"senderId": 1,
"senderName": "Alex Smith",
"messageText": "Looking forward to meeting everyone.",
"createdAt": "2026-07-07T12:10:00"
}
]POST /groups/{groupId}/messages
Requires authentication and group membership.
Request:
{
"messageText": "Looking forward to meeting everyone."
}Response 201 Created returns the persisted message.
Report target values are GROUP and MESSAGE. Report status values are PENDING, REVIEWED, RESOLVED, and DISMISSED.
POST /reports
Requires authentication.
Request:
{
"targetType": "GROUP",
"targetId": "22222222-2222-4222-8222-222222222222",
"reason": "Unsafe coordination",
"details": "The group note asks users to move the conversation to an unsafe channel."
}Response 201 Created:
{
"id": "66666666-6666-4666-8666-666666666666",
"reporterId": "11111111-1111-4111-8111-111111111111",
"targetType": "GROUP",
"targetId": "22222222-2222-4222-8222-222222222222",
"reason": "Unsafe coordination",
"details": "The group note asks users to move the conversation to an unsafe channel.",
"status": "PENDING",
"createdAt": "2026-07-07T12:15:00",
"updatedAt": "2026-07-07T12:15:00"
}GET /admin/reports?status=PENDING&page=0&size=10
Requires the ADMIN role and returns a Spring page of reports.
PATCH /admin/reports/{id}
Requires the ADMIN role.
Request:
{
"status": "REVIEWED"
}GET /admin/dashboard/stats
Requires the ADMIN role.
Response 200 OK:
{
"totalUsers": 125,
"activeUsers": 120,
"totalEvents": 48,
"publishedEvents": 42,
"activeGroups": 18
}All endpoints in this table require the ADMIN role.
| Method | Path | Purpose |
|---|---|---|
GET |
/admin/users |
List users with pagination. |
PUT |
/admin/users/{id}/role |
Update a user role. |
DELETE |
/admin/users/{id} |
Delete a user. |
GET |
/admin/events |
List events, optionally filtered by status. |
DELETE |
/admin/events/{id} |
Delete an event. |
GET |
/admin/moderation/pending |
List content awaiting moderation review. |
PATCH |
/admin/moderation/events/{id}/approve |
Approve a pending event and publish it. |
PATCH |
/admin/moderation/events/{id}/reject |
Reject a pending event; accepts an optional reason. |