API Reference — Membership One
Base URL
http://localhost:8081/api
Authentication
All endpoints require a JWT Bearer token in the Authorization header, except public endpoints marked with [PUBLIC].
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
JWT Claims
{
"sub": "42",
"iss": "membership-one",
"email": "admin@example.com",
"roles": ["CLUB_ADMIN", "ACCOUNTANT"],
"tenantId": 1,
"iat": 1708000000,
"exp": 1708000900
}
Token Lifecycle
- Access token: 15 minutes
- Refresh token: 7 days (SHA-256 hash stored server-side)
- Rotation: Each refresh generates a new token pair
Auth Endpoints (/api/auth)
Register [PUBLIC]
POST /api/auth/register
Content-Type: application/json
{
"email": "admin@myclub.com",
"password": "SecureP@ss123",
"firstName": "Max",
"lastName": "Mustermann",
"entityId": 1,
"locale": "de"
}
Response: 201 Created → UserDto
Login [PUBLIC]
POST /api/auth/login
Content-Type: application/json
{
"email": "admin@myclub.com",
"password": "SecureP@ss123",
"entityId": 1
}
Response: 200 OK
{
"accessToken": "eyJ...",
"refreshToken": "eyJ...",
"expiresIn": 900,
"user": {
"id": 42,
"email": "admin@myclub.com",
"firstName": "Max",
"lastName": "Mustermann",
"roles": ["CLUB_ADMIN"],
"permissions": ["MEMBER_READ", "MEMBER_WRITE", "..."],
"locale": "de",
"entityId": 1
}
}
Verify Email [PUBLIC]
POST /api/auth/verify-email?token={base64-token}
Response: 200 OK
Forgot Password [PUBLIC]
POST /api/auth/forgot-password
Content-Type: application/json
{ "email": "admin@myclub.com" }
Response: 200 OK (always, even if email not found — prevents enumeration)
Reset Password [PUBLIC]
POST /api/auth/reset-password
Content-Type: application/json
{ "token": "...", "newPassword": "NewSecure123!" }
Response: 200 OK
Refresh Token [PUBLIC]
POST /api/auth/refresh
Content-Type: application/json
{ "refreshToken": "eyJ..." }
Response: 200 OK → LoginResponse (new token pair)
Logout
POST /api/auth/logout
Authorization: Bearer {token}
Response: 204 No Content
Change Password
POST /api/auth/change-password
Authorization: Bearer {token}
Content-Type: application/json
{ "currentPassword": "OldPass123", "newPassword": "NewPass456" }
Response: 200 OK
Get Current User
GET /api/auth/me
Authorization: Bearer {token}
Response: 200 OK → UserDto
Update Profile
PUT /api/auth/me
Authorization: Bearer {token}
Content-Type: application/json
{ "firstName": "Maximilian", "locale": "en" }
Response: 200 OK → UserDto
Entity Endpoints (/api/entity)
Create Organization
POST /api/entity
Authorization: Bearer {token}
Content-Type: application/json
{
"name": "Fitness Club Berlin",
"type": "CLUB",
"contactEmail": "info@fitnessclub-berlin.de",
"country": "DE",
"city": "Berlin"
}
Response: 201 Created → OrganizationDto
Get Organization
GET /api/entity/{id}
Search Organizations
GET /api/entity/search?name=fitness&type=CLUB&status=ACTIVE&page=0&size=20
Response: 200 OK → PageResponse<OrganizationDto>
Update Organization
PUT /api/entity/{id}
Get Children (Franchise)
GET /api/entity/{id}/children
Response: 200 OK → List<OrganizationDto>
Get/Update Settings
GET /api/entity/{id}/settings
PUT /api/entity/{id}/settings
Member Endpoints (/api/members)
Create Member
POST /api/members
Authorization: Bearer {token}
Content-Type: application/json
{
"entityId": 1,
"firstName": "Anna",
"lastName": "Schmidt",
"email": "anna@example.com",
"dateOfBirth": "1990-05-15",
"gender": "FEMALE",
"joinDate": "2026-02-22"
}
Response: 201 Created → MemberDto (includes auto-generated memberNumber)
Search Members
GET /api/members/search?query=anna&status=ACTIVE&page=0&size=20&sort=lastName,asc
Response: 200 OK → PageResponse<MemberListItemDto>
Get Member
GET /api/members/{id}
Update Member
PUT /api/members/{id}
Content-Type: application/json
{ "phone": "+4930123456", "notes": "VIP member" }
Delete (Soft)
DELETE /api/members/{id}
Sets status=INACTIVE and exitDate=today.
Audit History
GET /api/members/{id}/audit?page=0&size=20
Response: 200 OK → PageResponse<AuditLog>
Link to User Account
POST /api/members/{id}/link-user?userId=42
Contract Endpoints
Membership Templates (/api/membership-templates)
Create Template
POST /api/membership-templates
Content-Type: application/json
{
"entityId": 1,
"name": "Premium Membership",
"price": 49.90,
"billingCycle": "MONTHLY",
"minimumTermMonths": 12,
"cancellationNoticeDays": 30,
"autoRenew": true,
"ageMin": 16,
"features": "[\"Pool\", \"Sauna\", \"Group Classes\"]"
}
List Templates (Admin)
GET /api/membership-templates?page=0&size=20
Public Catalog [PUBLIC]
GET /api/membership-templates/catalog/{entityId}
Returns visible + active templates only, sorted by sortOrder.
Update / Archive
PUT /api/membership-templates/{id}
DELETE /api/membership-templates/{id}
Contracts (/api/contracts)
Purchase Membership
POST /api/contracts
Content-Type: application/json
{
"memberId": 42,
"templateId": 1,
"startDate": "2026-03-01"
}
Creates contract with status=PENDING_SIGNATURE and locks in price.
Sign Contract
POST /api/contracts/{id}/sign
Content-Type: application/json
{ "signatureIp": "192.168.1.1", "signatureUserAgent": "Mozilla/5.0..." }
Transitions to status=ACTIVE.
Cancel Contract
POST /api/contracts/{id}/cancel
Content-Type: application/json
{ "reason": "Moving to another city" }
Validates cancellation notice period.
Get Contract / List by Member
GET /api/contracts/{id}
GET /api/contracts/member/{memberId}?page=0&size=20
Product Endpoints (/api/products)
Create Product
POST /api/products
X-Entity-Id: 1
Content-Type: application/json
{
"entityId": 1,
"name": "Personal Training Session",
"categoryCd": "PERSONAL_TRAINING",
"typeCd": "PER_SESSION",
"price": 59.00,
"vatRate": 19.00,
"vatIncluded": true,
"durationMinutes": 60
}
List Products (Admin)
GET /api/products?category=COURSE&status=ACTIVE&page=0&size=20
X-Entity-Id: 1
Public Catalog [PUBLIC]
GET /api/products/catalog/{entityId}
Update / Archive
PUT /api/products/{id}
DELETE /api/products/{id}
Document Endpoints (/api/documents)
Documents
| Method | Path | Description |
|---|---|---|
| POST | /api/documents/upload | Upload document (multipart) |
| GET | /api/documents/{id} | Get document metadata |
| GET | /api/documents/{id}/download | Download file |
| DELETE | /api/documents/{id} | Delete document |
| GET | /api/documents?linkedType=X&linkedId=Y | List documents by link |
| GET | /api/documents/pdf/contract/{contractId} | Generate contract PDF |
| GET | /api/documents/pdf/invoice/{transactionId} | Generate invoice PDF |
| GET | /api/documents/pdf/member-card/{memberId} | Generate member card PDF |
Data Import Endpoints (/api/imports)
Data Import
| Method | Path | Description |
|---|---|---|
| POST | /api/imports/upload | Upload CSV + create import job |
| POST | /api/imports/{jobId}/validate | Run dry-run validation |
| GET | /api/imports/{jobId}/preview | Get validation results |
| POST | /api/imports/{jobId}/execute | Execute import |
| GET | /api/imports/{jobId}/status | Get import progress |
| GET | /api/imports/{jobId}/errors | Download error report CSV |
| GET | /api/imports | List import jobs |
| GET | /api/imports/templates | List available templates |
Pagination
All paginated endpoints accept standard Spring Data parameters:
?page=0&size=20&sort=lastName,asc&sort=firstName,asc
Response format (PageResponse<T>):
{
"content": [...],
"page": 0,
"size": 20,
"totalElements": 142,
"totalPages": 8
}
Error Responses
All errors return ErrorResponse:
{
"code": "ENTITY_NOT_FOUND",
"message": "Member not found with id: 999",
"details": null
}
HTTP Status Codes
| Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Created |
| 204 | No Content (successful delete/logout) |
| 400 | Bad Request (validation errors) |
| 401 | Unauthorized (missing/invalid JWT) |
| 403 | Forbidden (insufficient permissions) |
| 404 | Not Found |
| 409 | Conflict (duplicate email, etc.) |
| 429 | Too Many Requests (rate limited) |
| 500 | Internal Server Error |
Bank Account Endpoints (/api/bank-accounts)
Create Bank Account
POST /api/bank-accounts
Authorization: Bearer {token}
Content-Type: application/json
{
"memberId": 1,
"accountHolder": "Max Mustermann",
"iban": "DE89370400440532013000",
"bic": "COBADEFFXXX",
"bankName": "Commerzbank",
"mandateSignDate": "2026-02-22",
"mandateTypeCd": "CORE"
}
Response: 201 Created → BankAccountDto
Get Member Bank Accounts
GET /api/bank-accounts/member/{memberId}
Response: 200 OK → List<BankAccountDto>
Update Bank Account
PUT /api/bank-accounts/{id}
Authorization: Bearer {token}
Content-Type: application/json
{
"accountHolder": "Max Mustermann-Schmidt",
"bic": "COBADEFFXXX",
"bankName": "Commerzbank AG"
}
Response: 200 OK → BankAccountDto
Set Default Bank Account
PUT /api/bank-accounts/{id}/set-default
Authorization: Bearer {token}
Response: 200 OK → BankAccountDto
Revoke Bank Account
DELETE /api/bank-accounts/{id}
Authorization: Bearer {token}
Response: 204 No Content
Transaction Endpoints (/api/transactions)
Get Transaction by ID
GET /api/transactions/{id}
Authorization: Bearer {token}
Response: 200 OK → TransactionDto
Search Transactions
GET /api/transactions?statusCd=PENDING&typeCd=MEMBERSHIP_FEE&memberId=1&page=0&size=20
Authorization: Bearer {token}
Response: 200 OK → PageResponse<TransactionDto>
Get Member Transactions
GET /api/transactions/member/{memberId}?page=0&size=20
Response: 200 OK → PageResponse<TransactionDto>
Get Member Balance
GET /api/transactions/member/{memberId}/balance
Response: 200 OK → MemberBalanceDto
{
"memberId": 1,
"outstandingBalance": 58.0000,
"openTransactionCount": 2
}
Full Storno (Cancel Transaction)
POST /api/transactions/{id}/storno
Authorization: Bearer {token}
Content-Type: application/json
{
"reason": "Customer request — duplicate charge"
}
Response: 200 OK → TransactionDto (credit note)
Partial Storno
POST /api/transactions/{id}/storno-partial
Authorization: Bearer {token}
Content-Type: application/json
{
"amount": 10.0000,
"reason": "Partial refund — service not used"
}
Response: 200 OK → TransactionDto (partial credit note)
Record Manual Payment
POST /api/transactions/{id}/record-payment
Authorization: Bearer {token}
Content-Type: application/json
{
"amount": 29.0000,
"paymentMethod": "CASH"
}
Response: 200 OK → TransactionDto
Billing Endpoints (/api/billing)
Trigger Billing Run
POST /api/billing/trigger
Authorization: Bearer {token}
Response: 200 OK
{
"transactionsCreated": 15,
"entityId": 1
}
Submit Pending to Cash360
POST /api/billing/submit
Authorization: Bearer {token}
Response: 200 OK
{
"transactionsSubmitted": 15,
"entityId": 1
}
Get Billing Report
GET /api/billing/report
Authorization: Bearer {token}
Response: 200 OK → BillingReportDto
{
"totalPayoutAmount": 12500.0000,
"fees": 125.0000,
"directDebitReturnAmount": 87.0000,
"reminderCasesAmount": 350.0000,
"pendingAmount": 2100.0000,
"outstandingBalance": 4500.0000
}
Get SEPA Exports
GET /api/billing/sepa-exports
Authorization: Bearer {token}
Response: 200 OK → List<Cash360SepaExportResponse>
Webhook Endpoints (/api/webhooks) [PUBLIC]
Cash360 Webhook
POST /api/webhooks/cash360
X-Webhook-Signature: {hmac-sha256-hex}
Content-Type: application/json
{
"type": "TRANSACTION_STATUS_CHANGED",
"transactionId": 999,
"statusCd": "PAID",
"collectionTypeCd": "DIRECT_DEBIT",
"amountDue": 0.00
}
Response: 200 OK (always, to prevent retries)
Transaction Status Mapping (Cash360 → Membership)
| Cash360 Status | Membership Status |
|---|---|
| NEW, ACCEPTED, EXPORTED | SUBMITTED |
| PAID | PAID |
| SETTLED | SETTLED |
| RETURNED, REJECTED | FAILED |
| CANCELLED | CANCELLED |
| FOR_DUNNING | OVERDUE |
| SENT_TO_INKASSO | DEBT_COLLECTION |
Check-in & Access Control
Check-in Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /api/checkin | Perform check-in (QR/NFC/BLE/MANUAL) |
| POST | /api/checkin/checkout | Record check-out |
| GET | /api/checkin/logs | Check-in log list (paginated, filterable by zone/date/method) |
| GET | /api/checkin/analytics | Check-in analytics (totals by method, zone, time) |
| GET | /api/checkin/history | Member's own check-in history |
Access Zone Endpoints (Admin)
| Method | Path | Description |
|---|---|---|
| GET | /api/admin/access-zones | List access zones |
| POST | /api/admin/access-zones | Create access zone |
| GET | /api/admin/access-zones/{id} | Get access zone details |
| PUT | /api/admin/access-zones/{id} | Update access zone |
| DELETE | /api/admin/access-zones/{id} | Delete access zone |
Access Rule Endpoints (Admin)
| Method | Path | Description |
|---|---|---|
| GET | /api/admin/access-rules | List access rules |
| POST | /api/admin/access-rules | Create access rule |
| PUT | /api/admin/access-rules/{id} | Update access rule |
| DELETE | /api/admin/access-rules/{id} | Delete access rule |
Credential Endpoints (Admin)
| Method | Path | Description |
|---|---|---|
| GET | /api/admin/credentials | List credentials |
| POST | /api/admin/credentials | Create credential |
| GET | /api/admin/credentials/member/{memberId} | Credentials for a member |
| DELETE | /api/admin/credentials/{id} | Revoke credential |
Resources & Bookings
Resource Endpoints (Admin)
| Method | Path | Description |
|---|---|---|
| GET | /api/admin/resources | List resources (paginated) |
| POST | /api/admin/resources | Create resource |
| GET | /api/admin/resources/{id} | Get resource details |
| PUT | /api/admin/resources/{id} | Update resource |
| DELETE | /api/admin/resources/{id} | Delete resource |
| GET | /api/admin/resources/{id}/availability | Check resource availability |
Booking Endpoints (Admin)
| Method | Path | Description |
|---|---|---|
| GET | /api/admin/bookings | List bookings (filterable by date range) |
| POST | /api/admin/bookings | Create booking |
| GET | /api/admin/bookings/{id} | Get booking details |
| PUT | /api/admin/bookings/{id} | Update booking |
| DELETE | /api/admin/bookings/{id} | Cancel booking |
| GET | /api/admin/resources/{id}/bookings | Bookings for a resource |
Resource & Booking Endpoints (Consumer)
| Method | Path | Description |
|---|---|---|
| GET | /api/resources | Consumer: list bookable resources |
| GET | /api/resources/{id}/slots | Consumer: available time slots for date |
| POST | /api/bookings | Consumer: create booking |
Courses
Course Endpoints (Admin)
| Method | Path | Description |
|---|---|---|
| GET | /api/admin/courses | List courses (paginated) |
| POST | /api/admin/courses | Create course |
| GET | /api/admin/courses/{id} | Get course details |
| PUT | /api/admin/courses/{id} | Update course |
| DELETE | /api/admin/courses/{id} | Delete course |
| GET | /api/admin/courses/{id}/registrations | List course registrations |
| POST | /api/admin/courses/{id}/attendance | Mark attendance |
Course Endpoints (Consumer)
| Method | Path | Description |
|---|---|---|
| GET | /api/courses | Consumer: browse available courses |
| GET | /api/courses/{id} | Consumer: course details |
| POST | /api/courses/{id}/register | Consumer: register for course |
| POST | /api/courses/{id}/cancel | Consumer: cancel registration |
Communication & Notifications
Notification Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /api/communications/send | Send single notification |
| POST | /api/communications/bulk | Bulk send via queue |
| GET | /api/communications | List notifications (paginated) |
Template Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/communication-templates | List templates |
| POST | /api/communication-templates | Create template |
| PUT | /api/communication-templates/{id} | Update template |
| DELETE | /api/communication-templates/{id} | Delete template |
| POST | /api/communication-templates/{id}/preview | Preview with variables |
Events
Event Endpoints (Admin)
| Method | Path | Description |
|---|---|---|
| POST | /api/events | Create event |
| GET | /api/events | List events (admin) |
| GET | /api/events/{id} | Event detail |
| PUT | /api/events/{id} | Update event |
| POST | /api/events/{id}/publish | Publish event |
| POST | /api/events/{id}/cancel | Cancel event |
Event Endpoints (Consumer)
| Method | Path | Description |
|---|---|---|
| POST | /api/events/{id}/register | Register for event |
| DELETE | /api/events/{id}/register | Cancel registration |
| GET | /api/events/upcoming/{entityId} | Public upcoming events |
Homepage [PUBLIC]
| Method | Path | Description |
|---|---|---|
| GET | /p/{slug} | Public entity homepage (Thymeleaf SSR) |
| GET | /p/sitemap.xml | Sitemap |
Onboarding & Provisioning
Provisioning Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /api/onboarding/provision | Start provisioning job |
| GET | /api/onboarding/provision/{jobId} | Get provisioning status |
Start Provisioning
POST /api/onboarding/provision
Authorization: Bearer {token}
Content-Type: application/json
{
"organizationName": "FitClub Munich",
"adminEmail": "admin@fitclub-munich.de",
"adminName": "Max Mustermann",
"tier": "STARTER",
"scenario": "FOUNDER",
"industryTemplate": "FITNESS_STUDIO",
"locale": "de",
"currency": "EUR",
"timezone": "Europe/Berlin"
}
Response: 201 Created -> ProvisioningStatusDto
{
"id": 1,
"status": "IN_PROGRESS",
"currentStep": "CREATE_ORGANIZATION",
"totalSteps": 7,
"completedSteps": 0,
"startedAt": "2026-02-23T10:00:00Z"
}
Get Provisioning Status
GET /api/onboarding/provision/{jobId}
Authorization: Bearer {token}
Response: 200 OK -> ProvisioningStatusDto
Wizard Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/onboarding/wizard | Get wizard state for current entity |
| PUT | /api/onboarding/wizard | Save wizard step |
| GET | /api/onboarding/wizard/templates | List industry templates |
Go-Live Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/onboarding/go-live/{entityId} | Get Go-Live checklist |
Response: 200 OK -> GoLiveChecklistDto
{
"profileComplete": true,
"membershipPlanActive": true,
"paymentConfigured": false,
"teamMemberInvited": false,
"membersAdded": true,
"welcomeEmailCustomized": false,
"testTransactionRun": false,
"brandingSet": false,
"completedCount": 3,
"totalCount": 8,
"completedAt": null
}
Health Scoring
Health Score Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/health-score/{entityId} | Get current health score |
| GET | /api/health-score/at-risk | List at-risk organizations |
| GET | /api/health-score/history/{entityId} | Health score history (12 weeks) |
| GET | /api/health-score/config | Get health score configuration |
Get Health Score
GET /api/health-score/{entityId}
Authorization: Bearer {token}
Response: 200 OK -> HealthScoreDto
{
"entityId": 1,
"overallScore": 72,
"loginFrequencyScore": 85,
"featureAdoptionScore": 60,
"memberActivityScore": 75,
"paymentHealthScore": 80,
"supportSentimentScore": 65,
"calculatedAt": "2026-02-23T02:00:00Z",
"trend": "UP",
"organizationName": "FitClub Munich"
}
Get At-Risk Organizations
GET /api/health-score/at-risk
Authorization: Bearer {token}
Response: 200 OK -> List<HealthScoreDto> (organizations with score < 70)
Swagger UI
Interactive API documentation available at:
http://localhost:8081/api/swagger-ui.html
API spec (JSON):
http://localhost:8081/api/api-docs