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
Email 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 message field (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 traceId for debugging.
  • Validation errors include per-field detail so the frontend can highlight specific form fields.
  • The traceId enables 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

sequenceDiagram participant MB as Membership Backend participant C3 as Cash360 API participant Bank as Bank (SEPA/EBICS) MB->>C3: POST /api/transaction/create<br/>(amount, consumer, entity, payment method) C3-->>MB: Transaction created (id, status: PENDING) MB->>C3: POST /api/sepa-export/generate<br/>(entity, date range) C3-->>MB: SEPA XML file (Pain.008) MB->>C3: POST /api/sepa-export/submit<br/>(via EBICS) C3->>Bank: Submit SEPA file Bank-->>C3: Acknowledgment Note over C3,Bank: Days later... Bank-->>C3: Settlement report C3->>C3: Reconcile transactions C3-->>MB: Webhook: transaction status update<br/>(SETTLED / RETURNED / FAILED) MB->>MB: Update local transaction status

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.