Status: ✅ Accepted — 2026-07-03 by CPTO
CPTO Sign-off: 2026-07-03 — RESTful + OpenAPI ist richtig für Cartly. JWT + tenant_id Isolation aus ADR-002 muss in alle Endpunkte durchdekliniert werden. Rate Limiting nach Plan (Good/Better/Best) ist customer-friendly pricing.
Entscheidung: RESTful API Design mit versionierten Endpunkten und einheitlicher Authentifizierung
Cartly benötigt eine API für:
Anforderungen:
Pattern: URL-Path Versioning (/api/v1/)
/api/v1/products
/api/v1/orders
/api/v1/tenants
Regeln:
Sunset: Sat, 31 Dec 2027 23:59:59 GMT/api/v1/changelog| Methode | Verwendung | Idempotent | Response Code |
|---|---|---|---|
| GET | Lesen | ✅ | 200, 404, 401 |
| POST | Erstellen | ❌ | 201, 400, 401 |
| PUT | Vollständiges Update | ✅ | 200, 400, 404, 401 |
| PATCH | Partielles Update | ❌ | 200, 400, 404, 401 |
| DELETE | Löschen | ✅ | 204, 404, 401 |
| HEAD | Existenz-Prüfung | ✅ | 200, 404, 401 |
| OPTIONS | CORS Preflight | ✅ | 204, 401 |
Request: JSON im Body
POST /api/v1/products
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json
{
"sku": "TSHIRT-BLK-M",
"name": "Classic T-Shirt Schwarz",
"price": 29.99,
"sizes": ["S", "M", "L", "XL"],
"colors": [
{"name": "Schwarz", "hex": "#000000"},
{"name": "Weiß", "hex": "#FFFFFF"}
]
}
Response: JSON mit konsistentem Wrapper
{
"data": { ... },
"meta": {
"requestId": "req_abc123",
"timestamp": "2026-07-03T12:00:00Z",
"tenantId": "tenant_xyz"
}
}
Error Response:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "SKU ist bereits vergeben",
"details": [
{"field": "sku", "message": "Duplicate SKU"}
]
},
"meta": {
"requestId": "req_abc123",
"timestamp": "2026-07-03T12:00:00Z"
}
}
Pattern: Cursor-based Pagination (besser für große Datasets)
GET /api/v1/products?limit=20&cursor=eyJpZCI6MTIzfQ==
{
"data": [...],
"pagination": {
"hasMore": true,
"nextCursor": "eyJpZCI6MTQzfQ==",
"total": 1234
}
}
Alternativen:
?page=2&per_page=20) — nur für Admin-Endpoints mit bekannter GesamtgrößeJWT Structure:
interface JWTPayload {
sub: string; // User ID
tenantId: string; // Tenant ID
role: 'admin' | 'manager' | 'sales';
permissions: string[]; // ['products:read', 'products:write', ...]
iat: number;
exp: number; // Access Token: 15min
}
Token Endpoints:
POST /api/v1/auth/login # Login → {accessToken, refreshToken}
POST /api/v1/auth/refresh # Refresh → {accessToken}
POST /api/v1/auth/logout # Invalidate Refresh Token
POST /api/v1/auth/register # Admin: Neuen User erstellen
Security Header:
Authorization: Bearer <access_token>
X-Tenant-ID: <tenant_id> # Nur für Server→Server
Header:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1719993600
Retry-After: 60 # Bei 429
Limits (per Tenant):
| Plan | Requests/Minute | Requests/Stunde |
|---|---|---|
| Good | 100 | 10,000 |
| Better | 500 | 50,000 |
| Best | 2000 | 200,000 |
Response Header:
Cache-Control: private, max-age=3600
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified: Wed, 03 Jul 2026 12:00:00 GMT
Cache Strategy:
openapi: 3.1.0
info:
title: Cartly API
version: 1.0.0
description: ERP+POS API für Fashion Retail
servers:
- url: https://api.cartly.io/api/v1
paths:
/products:
get:
summary: Liste aller Produkte
parameters:
- $ref: '#/components/parameters/TenantId'
- $ref: '#/components/parameters/Limit'
- $ref: '#/components/parameters/Cursor'
POST /api/v1/auth/login
POST /api/v1/auth/refresh
POST /api/v1/auth/logout
POST /api/v1/auth/register (Admin)
GET /api/v1/auth/me
GET /api/v1/products
POST /api/v1/products
GET /api/v1/products/:id
PUT /api/v1/products/:id
PATCH /api/v1/products/:id
DELETE /api/v1/products/:id
GET /api/v1/orders
POST /api/v1/orders
GET /api/v1/orders/:id
PATCH /api/v1/orders/:id
GET /api/v1/tenants
POST /api/v1/tenants
GET /api/v1/tenants/:id
PATCH /api/v1/tenants/:id
POST /api/v1/ai/completions (OpenRouter Proxy)
GET /api/v1/ai/models
Erstellt: 2026-07-03 von Documentation Agent (a66674bf)