API Design
API-First Principle
The Membership platform follows an API-first development approach: every feature is designed as an API endpoint before any frontend screen is built. The API is the product -- the Flutter apps, admin web interface, third-party integrations, and future AI agents all consume the same API.
Phase 1 (v1.0): REST API with JSON payloads. This is the only API style implemented at launch. Phase 2: GraphQL endpoint for flexible querying (admin dashboards, reporting). Runs alongside REST, not as a replacement. Phase 3: MCP (Model Context Protocol) interface for AI agent integration. Webhook system for event-driven third-party integration.
All API styles share the same authentication, authorization, and business logic layer. Only the transport/serialization layer differs.
Current Consumer API (V1)
The existing mb-api module provides 25 endpoints across 9 controllers under the /mobile/v1/ prefix. These serve the consumer-facing mobile app.
Authentication Endpoints
| Method | Path | Auth | Purpose |
|---|---|---|---|
POST |
/mobile/v1/account/login |
None | Authenticate with email + password, returns JWT token |
POST |
/mobile/v1/account/register |
None | Create new consumer account with email + password |
POST |
/mobile/v1/account/verify |
None | Verify email address using verification key |
GET |
/mobile/v1/account/is-verified |
JWT | Check if current user's email is verified, returns consumer data |
POST |
/mobile/v1/account/forgot-password |
None | Request password reset link (sent via email) |
POST |
/mobile/v1/account/reset-password |
None | Reset password using UUID from email link |
POST |
/mobile/v1/account/change-password |
JWT | Change password (requires old password + new + confirm) |
DELETE |
/mobile/v1/account |
JWT | Delete own account (legacy endpoint) |
DELETE |
/mobile/v1/account/delete-account-new |
JWT | Delete own account with password confirmation (secure) |
Consumer Profile Endpoints
| Method | Path | Auth | Purpose |
|---|---|---|---|
POST |
/mobile/v1/consumer/create |
JWT | Create consumer profile (name, address, birthday, emergency contact) |
PUT |
/mobile/v1/consumer/update |
JWT | Update own consumer profile (email cannot be changed) |
GET |
/mobile/v1/consumer |
JWT | Get current consumer profile with open debt calculation |
GET |
/mobile/v1/consumer/country/all |
JWT | List all available countries (for address form) |
Contract and Membership Endpoints
| Method | Path | Auth | Purpose |
|---|---|---|---|
GET |
/mobile/v1/contract-pre-defined/{entityId} |
JWT | List available membership offers for an organization |
POST |
/mobile/v1/contract/create/{predefinedId} |
JWT | Purchase a membership (creates contract, schedules billing) |
GET |
/mobile/v1/contract/{idMbConsumer} |
JWT | List all contracts for a consumer |
Financial Endpoints
| Method | Path | Auth | Purpose |
|---|---|---|---|
GET |
/mobile/v1/transaction/all |
JWT | List all transactions for current user |
GET |
/mobile/v1/transaction/{idPmTransaction} |
JWT | Get transaction detail by ID |
POST |
/mobile/v1/bank-account/create/{idMcEntity} |
JWT | Create bank account (IBAN) for SEPA payments |
Entity and Organization Endpoints
| Method | Path | Auth | Purpose |
|---|---|---|---|
GET |
/mobile/v1/entity/{id} |
JWT | Get organization detail (with bank account if available) |
GET |
/mobile/v1/entity/search |
JWT | Search organizations by parameters or entity hierarchy |
POST |
/mobile/v1/entity/radius |
JWT | Find organizations within geographic radius (lat, lng, radius) |
GET |
/mobile/v1/entity-settings/{entityId} |
JWT | Get organization settings (branding, features, config) |
Device and Access Control Endpoints
| Method | Path | Auth | Purpose |
|---|---|---|---|
POST |
/mobile/v1/device/entrance-control |
JWT | Process QR code check-in (any active contract) |
POST |
/mobile/v1/device/entrance-control/{contractId} |
JWT | Process QR code check-in for specific contract |
Missing Endpoints (Identified in Review)
The following endpoints are required by the mobile app specification but do not exist in the current codebase.
Authentication Gaps
POST /mobile/v1/account/resend-verification
Resend the email verification link. Required when the original email is lost or expired.
Request: (empty body, uses JWT to identify user)
Response: { "status": "OK", "message": "Verification email resent" }
Rate limit: 1 request per 60 seconds per user
POST /mobile/v1/account/refresh-token
Exchange a valid refresh token for a new access token + refresh token pair. Enables silent re-authentication without storing credentials on device.
Request: { "refreshToken": "eyJ..." }
Response: { "accessToken": "eyJ...", "refreshToken": "eyJ...", "expiresIn": 3600 }
Note: Refresh token rotation -- old refresh token is invalidated on use
POST /mobile/v1/account/logout
Invalidate the current session. Revokes the refresh token server-side and clears any server-side session state.
Request: (empty body, uses JWT to identify session)
Response: 204 No Content
Consumer Profile Gaps
GET /mobile/v1/account/export-data
GDPR data export. Returns all personal data associated with the account in a machine-readable format (JSON). Required by EU GDPR Article 20 (right to data portability).
Request: (empty body, uses JWT)
Response: {
"account": { "email": "...", "createdAt": "...", "verifiedAt": "..." },
"profile": { "firstName": "...", "lastName": "...", ... },
"contracts": [ ... ],
"transactions": [ ... ],
"bankAccounts": [ ... ],
"accessLogs": [ ... ]
}
Note: May be asynchronous for large accounts (returns job ID, notifies via email when ready)
Bank Account Gaps
PUT /mobile/v1/bank-account/{id}
Update an existing bank account (e.g., correct IBAN, change account holder name).
Request: { "iban": "DE89...", "accountHolder": "Max Mustermann", "bic": "COBADEFFXXX" }
Response: { "id": 42, "iban": "DE89...", "accountHolder": "...", "statusCd": "ACTIVE" }
Constraint: Cannot update a bank account that has pending SEPA transactions
DELETE /mobile/v1/bank-account/{id}
Delete (archive) a bank account. Soft delete -- the account is marked as archived but retained for historical transaction reference.
Request: (empty)
Response: 204 No Content
Constraint: Cannot delete the last active bank account if active contracts require SEPA payment
New API Areas
Beyond the consumer-facing mobile API, the Membership platform requires four additional API domains for the admin interface, resource management, events, and reporting.
Admin API (/api/v1/admin/)
The admin API provides full CRUD operations for all domain entities, accessible only to users with Club Admin role or higher. All admin endpoints enforce multi-tenant isolation via the authenticated user's entity context.
| Area | Endpoints | Description |
|---|---|---|
| Members | CRUD + search + bulk | Create, read, update, archive members. Bulk import/export. Advanced search with filters. |
| Contracts | CRUD + lifecycle | Create, modify, suspend, cancel, renew contracts. Template management. |
| Billing | CRUD + cycle management | Run billing cycles, create manual transactions, generate SEPA files, manage adjustments. |
| Users & Roles | CRUD + assignment | Create admin users, assign roles, manage invitations, deactivate users. |
| Organization | CRUD + hierarchy | Manage entity settings, branding, payment config, child entities. |
| Communication | CRUD + send | Email templates, push notifications, newsletter campaigns, bulk messaging. |
| Data Import | Upload + map + execute | CSV upload, column mapping, dry run, execution, error reports. |
| Reports | Generate + export | Financial reports, member statistics, attendance analytics, export to PDF/CSV. |
| CRM | CRUD + pipeline | Lead management, deal tracking, activity logging, conversion analytics. |
| Support | CRUD + SLA | Ticket management, agent assignment, knowledge base, satisfaction surveys. |
| Accounting | CRUD + export | Journal entries, cost centers, DATEV export, bank reconciliation. |
| Dashboards | CRUD + config | Dashboard configuration, widget management, alert thresholds. |
| Access Control | CRUD + commands | Zone management, credentials, access rules, door commands, occupancy. |
Resource Management API (/api/v1/resources/)
Designed as a standalone-capable API (see Chapter 06). All endpoints work both within the Membership platform and as an independent resource management service.
| Area | Key Endpoints | Description |
|---|---|---|
| Resources | GET/POST/PUT/DELETE /resources |
CRUD for rooms, areas, equipment, personnel |
| Bookings | GET/POST/PUT/DELETE /bookings |
Single and recurring bookings, conflict detection |
| Calendar | GET /calendar/{resourceId} |
Visual schedule view with availability |
| Inventory | GET/POST /equipment/issue, POST /equipment/return |
Equipment tracking via QR/NFC |
| Access Points | GET/POST /access-points |
Turnstile, reader, sensor configuration |
| Access Logs | GET /access-logs, POST /check-in |
Check-in/check-out recording and querying |
Events and Courses API (/api/v1/events/)
| Area | Key Endpoints | Description |
|---|---|---|
| Events | GET/POST/PUT/DELETE /events |
One-time and recurring events |
| Courses | GET/POST/PUT/DELETE /courses |
Recurring class definitions with schedule |
| Schedule | GET /schedule |
Combined calendar view of all events/courses |
| Registration | POST /events/{id}/register, DELETE /events/{id}/unregister |
Participant sign-up with waitlist |
| Attendance | POST /events/{id}/attendance, GET /events/{id}/attendance |
Mark and query attendance |
| Trainers | GET/POST/PUT /trainers, POST /events/{id}/assign-trainer |
Trainer profile and assignment management |
Communication API (/api/v1/communication/)
| Area | Key Endpoints | Description |
|---|---|---|
POST /email/send, GET/POST /email/templates |
Send emails, manage templates | |
| Push | POST /push/send, POST /push/register-device |
Push notification sending and device registration |
| Newsletter | GET/POST/PUT /newsletters, POST /newsletters/{id}/send |
Newsletter creation and distribution |
| Messages | GET/POST /messages |
In-app messaging between users |
CRM and Sales API (/api/v1/crm/)
| Area | Key Endpoints | Description |
|---|---|---|
| Leads | GET/POST/PUT/DELETE /leads |
Lead CRUD with source tracking and pipeline stage |
| Lead conversion | POST /leads/{id}/convert |
Convert won lead to member (one-click) |
| Deals | GET/POST/PUT/DELETE /deals |
Deal tracking with pipeline stage, value, probability |
| Pipeline | GET /pipeline, PUT /pipeline/stages |
Pipeline view (Kanban) and stage configuration |
| Activities | GET/POST/PUT /activities |
Activity log (calls, emails, meetings, notes, tasks) |
| Trial tracking | GET /trials, GET /trials/conversion-rate |
Trial-to-member conversion analytics |
| Sales reports | GET /reports/pipeline, GET /reports/conversion |
Pipeline value, conversion funnel, win/loss analysis |
Support and Ticketing API (/api/v1/support/)
| Area | Key Endpoints | Description |
|---|---|---|
| Tickets | GET/POST/PUT /tickets |
Ticket CRUD with category, priority, status |
| Ticket comments | GET/POST /tickets/{id}/comments |
Public and internal comments on tickets |
| Ticket assignment | PUT /tickets/{id}/assign |
Assign or reassign tickets to agents |
| KB articles | GET/POST/PUT/DELETE /kb/articles |
Knowledge base article management |
| KB search | GET /kb/search?q=... |
Full-text search across published articles |
| SLA config | GET/PUT /sla/config |
SLA response and resolution time configuration |
| Ticket reports | GET /reports/tickets, GET /reports/sla |
Volume, resolution time, SLA compliance |
| CSAT | POST /tickets/{id}/satisfaction, GET /reports/csat |
Satisfaction survey and reporting |
Accounting API (/api/v1/accounting/)
| Area | Key Endpoints | Description |
|---|---|---|
| Journal entries | GET/POST /entries |
View and create journal entries |
| Posting rules | GET/PUT /posting-rules |
Configure automatic posting rules for billing events |
| Cost centers | GET/POST/PUT/DELETE /cost-centers |
Cost center management |
| Chart of accounts | GET/POST/PUT /chart-of-accounts |
Account definitions (SKR03/SKR04) |
| DATEV export | POST /datev-export, GET /datev-export/{id} |
Generate and download DATEV CSV/XML |
| Bank reconciliation | POST /reconciliation/import, GET /reconciliation/unmatched |
Import bank statements and match entries |
| Financial reports | GET /reports/pl, GET /reports/balance, GET /reports/cashflow |
P&L, balance sheet, cash flow |
Vendor API (/api/v1/vendor/)
Platform-level API for Membership One (vendor) operations. Requires Vendor Admin or Vendor Support role. All endpoints operate cross-tenant.
| Area | Key Endpoints | Description |
|---|---|---|
| Tenants | GET/POST/PUT /tenants |
Tenant provisioning, configuration, status management |
| Customer health | GET /tenants/{id}/health, GET /health/overview |
Health scores, usage metrics, risk indicators |
| System metrics | GET /metrics/system, GET /metrics/usage |
Infrastructure health, API call volumes, error rates |
| SaaS billing | GET /billing/mrr, GET /billing/churn |
MRR, ARR, churn, expansion revenue |
| Impersonation | POST /impersonate/{userId} |
Support impersonation with audit logging |
Access Control API (/api/v1/access/)
| Area | Key Endpoints | Description |
|---|---|---|
| Zones | GET/POST/PUT/DELETE /zones |
Access zone CRUD with hierarchy |
| Access rules | GET/POST/PUT/DELETE /rules |
Access rule configuration per zone |
| Credentials | GET/POST/PUT /credentials |
Credential issuance, suspension, revocation |
| Door commands | POST /doors/{id}/unlock, POST /doors/{id}/lock |
Remote door control |
| Occupancy | GET /zones/{id}/occupancy, GET /occupancy/overview |
Real-time and historical occupancy |
| Access logs | GET /access-logs |
Check-in/check-out event history |
| Hardware | GET/POST/PUT /devices, GET /devices/{id}/health |
Access control hardware management |
API Versioning
URL-Based Versioning
All endpoints include the version in the URL path:
/mobile/v1/account/login (consumer mobile API, version 1)
/api/v1/admin/members (admin API, version 1)
/api/v2/admin/members (admin API, version 2, when needed)
Version Lifecycle
| State | Duration | Behavior |
|---|---|---|
| Current | Indefinite | Actively developed, receives new features and bug fixes |
| Deprecated | 12 months minimum | No new features, receives security fixes. Response includes Sunset header. |
| Retired | After deprecation period | Returns 410 Gone with migration guide URL |
Migration Rules
- Breaking changes (field removal, type change, behavior change) require a new version.
- Additive changes (new optional fields, new endpoints) do not require a new version.
- The consumer mobile API (
/mobile/v1/) and admin API (/api/v1/) are versioned independently.
Authentication
JWT Token Structure
Authentication uses JWT tokens issued by the Membership backend (not an external IdP in v1). The token is included in the Authorization header as a Bearer token.
Access Token Claims:
{
"sub": "user@example.com",
"iss": "membership-api",
"iat": 1740000000,
"exp": 1740003600,
"userId": 12345,
"entityId": 67,
"roles": ["CLUB_ADMIN"],
"type": "access"
}
Token Lifecycle:
| Token | Lifetime | Storage |
|---|---|---|
| Access token | 1 hour | In-memory (mobile: secure storage) |
| Refresh token | 30 days | Secure storage (keychain/keystore) |
Refresh Token Rotation:
When the access token expires, the client sends the refresh token to /mobile/v1/account/refresh-token. The server returns a new access token AND a new refresh token. The old refresh token is immediately invalidated. This limits the damage window if a refresh token is compromised.
Token Revocation: On logout or password change, all refresh tokens for the user are revoked server-side. Active access tokens continue to work until expiration (max 1 hour). For immediate revocation (e.g., account compromise), the server maintains a short-lived blacklist in Redis.
Pagination Standard
All list endpoints support cursor-based or offset-based pagination. The default is offset-based (consistent with Spring Data's Pageable).
Request Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page |
int | 0 | Zero-based page index |
size |
int | 20 | Page size (max 100) |
sort |
string | varies | Sort field + direction (e.g., createdAt,desc) |
Response Envelope
{
"content": [ ... ],
"page": {
"number": 0,
"size": 20,
"totalElements": 150,
"totalPages": 8
},
"sort": {
"field": "createdAt",
"direction": "DESC"
}
}
For endpoints where offset pagination is inefficient (large datasets, real-time feeds), cursor-based pagination is used:
{
"content": [ ... ],
"cursor": {
"next": "eyJpZCI6MTIzNH0=",
"hasMore": true
}
}
Rate Limiting
Rate limits protect the API from abuse and ensure fair usage across tenants.
| Tier | Limit | Scope | Applies To |
|---|---|---|---|
| Anonymous | 10 req/min | Per IP | Login, register, forgot-password |
| Authenticated | 300 req/min | Per user | All authenticated endpoints |
| Admin | 600 req/min | Per user | Admin API endpoints |
| Bulk operations | 5 req/min | Per user | Import, export, batch operations |
| Webhook delivery | 100 req/min | Per tenant | Outbound webhook calls |
Rate limit headers (included in every response):
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 287
X-RateLimit-Reset: 1740003660
When exceeded:
HTTP 429 Too Many Requests
Retry-After: 30
Rate limits are enforced using Redis sliding window counters, which survive server restarts and work correctly across multiple API server instances.
Error Response Format
All errors follow a standardized format, regardless of the endpoint or error type.
Standard Error Object
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The request contains invalid data.",
"details": [
{
"field": "email",
"message": "Email address is already registered.",
"code": "DUPLICATE_EMAIL"
},
{
"field": "iban",
"message": "Invalid IBAN checksum.",
"code": "INVALID_IBAN"
}
],
"timestamp": "2026-02-22T14:30:00Z",
"traceId": "abc-123-def-456"
}
}
Error Codes
| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | VALIDATION_ERROR |
Request body fails validation (missing fields, wrong format) |
| 400 | BUSINESS_RULE_VIOLATION |
Request violates a business rule (e.g., contract start date in past) |
| 401 | UNAUTHORIZED |
Missing or invalid JWT token |
| 401 | TOKEN_EXPIRED |
JWT token has expired (client should refresh) |
| 403 | FORBIDDEN |
User lacks permission for this operation |
| 403 | TENANT_MISMATCH |
User attempting to access data from another tenant |
| 404 | NOT_FOUND |
Resource does not exist or is not visible to the current user |
| 409 | CONFLICT |
Concurrent modification detected (optimistic locking) |
| 409 | DUPLICATE |
Attempted to create a resource that already exists |
| 422 | UNPROCESSABLE |
Request is syntactically valid but semantically wrong |
| 429 | RATE_LIMITED |
Too many requests (see rate limiting) |
| 500 | INTERNAL_ERROR |
Unexpected server error (details logged, not exposed) |
| 503 | SERVICE_UNAVAILABLE |
Dependency is down (Cash360 API, database) |
Error Design Principles
- Error messages are user-friendly in the
messagefield (suitable for display in the UI). - Technical details (stack traces, SQL errors) are never included in the response. They are logged server-side with the
traceIdfor debugging. - Validation errors include per-field detail so the frontend can highlight specific form fields.
- The
traceIdenables correlation between the client error report and server logs.
Cash360 Payment API Integration
The Membership platform delegates all payment processing to Cash360 via its REST API. This is a server-to-server integration -- the Membership backend calls Cash360; the Membership frontend never talks to Cash360 directly.
Integration Architecture
Integration Points
| Operation | Cash360 Endpoint | Direction | Trigger |
|---|---|---|---|
| Create transaction | POST /api/pm-transaction |
Membership -> Cash360 | Contract billing cycle or manual charge |
| Create adjustment | POST /api/pm-adjustment |
Membership -> Cash360 | Refund, credit, manual correction |
| Generate SEPA file | POST /api/eb-sepa-export |
Membership -> Cash360 | Admin initiates SEPA export |
| Submit to bank | POST /api/eb-sepa-export/submit |
Membership -> Cash360 | Admin triggers EBICS submission |
| Transaction status update | Webhook callback | Cash360 -> Membership | Payment settled, returned, or failed |
| Generate invoice PDF | POST /api/pp-invoice/generate |
Membership -> Cash360 | Invoice creation during billing cycle |
| Consumer sync | POST /api/csr-consumer |
Membership -> Cash360 | Member creation (creates Cash360 consumer record) |
Authentication Between Services
Service-to-service authentication uses a dedicated API key (not user JWT). The API key is stored in environment variables and included in the X-API-Key header. Each Membership tenant has a separate API key that maps to a Cash360 entity (idMcEntity), ensuring multi-tenant isolation at the payment layer.
X-API-Key: mb-tenant-67-a1b2c3d4e5f6...
OpenAPI / Swagger Documentation
Documentation Strategy
Every endpoint is documented using SpringDoc OpenAPI annotations directly in the controller code. This ensures that the documentation stays synchronized with the implementation.
Annotations used:
- @Operation(summary, description) on each endpoint method
- @Parameter(description, required, example) on path/query parameters
- @Schema(description, example) on DTO fields
- @ApiResponse(responseCode, description) for each possible response status
Access
| Environment | URL | Auth |
|---|---|---|
| Local dev | http://localhost:8080/api/swagger-ui.html |
None |
| Staging | https://staging.membership.example/api/swagger-ui.html |
Basic auth |
| Production | Not exposed | Internal only (CI/CD extracts spec) |
Generated Artifacts
The build pipeline extracts the OpenAPI spec as:
- openapi.json -- Machine-readable spec, used by code generators (Flutter client, Postman collection)
- openapi.yaml -- Human-readable spec, committed to version control for change tracking
- Swagger UI -- Interactive documentation for developers
Client Code Generation
The Flutter API client is generated from the OpenAPI spec using openapi-generator-cli with the Dart/Dio template. This eliminates manual DTO maintenance in the frontend and guarantees type-safe API calls that match the current backend contract.
openapi-generator-cli generate \
-i openapi.json \
-g dart-dio \
-o frontend/lib/core/api/generated/
The generated client is checked into version control and regenerated whenever the API spec changes.