diff --git a/README.md b/README.md
index 9bda959..20823a3 100644
--- a/README.md
+++ b/README.md
@@ -62,3 +62,118 @@ git tag vX.X.X && git push origin vX.X.X
> [!CAUTION]
> The workflow only tells if a deployment started on portainer. It can not detect if the API container or any other container fails on start as long as the container gets marked as "running".
+---
+
+## Shipping flow
+
+```mermaid
+flowchart TD
+ A[Customer views product] --> B[Add to cart]
+ B --> C[Start checkout]
+ C --> D[Create Order: DRAFT]
+ D --> E["Reserve inventory (StockReservation)"]
+ E -->|insufficient stock| E1[Reject / show out of stock]
+ E -->|ok| F["Create Payment Session/Intent (PSP)"]
+ F --> G[Customer completes payment on PSP]
+ G --> H[PSP sends Webhook: payment_succeeded / payment_failed]
+
+ H --> I{"Webhook verified + idempotent?"}
+ I -->|duplicate| I1["Return 200 OK (no-op)"]
+ I -->|new| J[DB txn: Payment=SUCCEEDED]
+ J --> K[Order status -> PAID]
+ K --> L[Commit inventory: decrement on_hand + release reservation]
+ L --> M[Enqueue fulfillment job: CREATE_LABEL]
+
+ M --> N["Worker calls Carrier API (e.g. DHL)"]
+ N --> O{"Label created?"}
+ O -->|no| O1[Retry/backoff + alert admin]
+ O -->|yes| P[Shipment: tracking + label stored]
+ P --> Q[Order status -> READY_TO_SHIP]
+ Q --> R["Admin pick & pack with packing slip"]
+ R --> S[Hand over to carrier / pickup scan]
+ S --> T[Order status -> SHIPPED]
+ T --> U[Send tracking email to customer]
+
+ H -->|payment_failed| V[Payment=FAILED]
+ V --> W[Order status -> CANCELLED]
+ W --> X[Release reservation]
+```
+
+### Phase 0 — Foundations (do this first)
+
+1. Core domain + DB schema
+- Product/SKU, Inventory, Order, OrderItem, Customer, Address
+
+2. Order state machine
+- DRAFT → PENDING_PAYMENT → PAID → READY_TO_SHIP → SHIPPED
+- PENDING_PAYMENT → CANCELLED (timeout / failed)
+
+3. Idempotency & constraints
+- Unique constraints for “one shipment label per order”
+- Event inbox table (dedupe webhook events)
+
+### Phase 1 — Checkout (money + stock safety)
+
+1. Cart + Checkout draft API
+- Create draft order and line items with price snapshots
+
+2. Inventory reservations
+- Reserve on checkout start; expiry + cleanup job
+
+3. Payment provider integration (PSP)
+- Create payment session/intent; store provider reference
+
+4. Webhook endpoint (authoritative payment confirmation)
+- Signature verification
+- Idempotent processing
+- Transactional transition to PAID + commit inventory
+
+### Phase 2 — Admin fulfillment without carrier integration (ship manually first)
+
+1. Admin order list + detail
+- Filter by status: PAID / READY_TO_SHIP / SHIPPED
+
+2. Pick/pack documents
+- Packing slip + pick list (PDF optional, HTML fine initially)
+
+3. Manual shipment marking
+- Admin enters tracking number manually
+- Customer notification email
+
+(At this point you have a working webshop that can get paid, manage stock correctly, and ship manually.)
+
+### Phase 3 — Automation: DHL label generation
+
+1. Background job system
+- Worker + queue; retry/backoff; dead-letter + alerts
+
+2. Carrier abstraction layer
+- CarrierAdapter.create_label(order) interface
+
+3. DHL adapter
+- Create label, store PDF/ZPL, tracking number
+
+4. Admin label printing + shipment workflow
+- “Create label” / “Recreate label” rules
+- Print label + packing slip from admin
+
+### Phase 4 — Operational hardening (what makes it production-grade)
+
+1. Outbox pattern for reliable job enqueueing
+
+2. Observability
+- Structured logs, correlation IDs, metrics
+
+3. Fraud/edge-case handling
+- Payment reversals/refunds → inventory & order adjustments
+
+4. Returns + refunds (basic RMA)
+
+5. Partial fulfillment / split shipments (optional, later)
+
+### Practical MVP scope (fastest path that still won’t bite you)
+
+- Webhook-driven payment confirmation
+- Reservation-based inventory
+- Admin pick/pack + manual tracking
+- Then add DHL labels
\ No newline at end of file
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..0dac574
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,267 @@
+# TODO — OpenTaberna API Implementation Roadmap
+
+Ordered by dependency. Each phase builds on the previous.
+CRUD for items (`crud-item-store`) is handled by a partner and not listed here.
+
+---
+
+## Shared Infrastructure (already done ✅)
+
+- [x] Config module (`shared/config/`)
+- [x] Logger module (`shared/logger/`)
+- [x] Exceptions module (`shared/exceptions/`)
+- [x] Responses module (`shared/responses/`)
+- [x] Database module (`shared/database/`) — async SQLAlchemy 2.0, BaseRepository, migrations, health
+- [x] Keycloak auth (`authorize/keycloak.py`)
+- [x] FastAPI app skeleton (`main.py`)
+
+---
+
+## Model Convention
+
+> **All models throughout this project are pure Pydantic models.**
+> SQLAlchemy's `Base` / `DeclarativeBase` is **not used** for domain entities.
+> Database interaction happens via the `BaseRepository` with raw SQL or query builders — models are validated and serialized exclusively through Pydantic.
+
+Pattern per entity:
+
+```
+models/
+├── customer.py
+│ ├── CustomerBase(BaseModel) # shared fields
+│ ├── CustomerCreate(CustomerBase) # input / write schema
+│ ├── CustomerUpdate(BaseModel) # partial update (all Optional)
+│ └── CustomerResponse(CustomerBase) # output / read schema, incl. id + timestamps
+```
+
+- Use `model_config = ConfigDict(from_attributes=True)` on response models if mapping from DB rows
+- Timestamps (`created_at`, `updated_at`) are read-only response fields, never in Create/Update schemas
+- Primary keys are always in response models only, never in Create schemas
+
+---
+
+## Phase 0 — Domain Models & DB Schema
+
+> Prerequisite for everything. No service can be built without these.
+
+### 0.1 Customer & Address
+
+- [ ] `CustomerBase`, `CustomerCreate`, `CustomerUpdate`, `CustomerResponse` — fields: id, keycloak_user_id, email, name, created_at, updated_at
+- [ ] `AddressBase`, `AddressCreate`, `AddressUpdate`, `AddressResponse` — fields: id, customer_id, street, city, zip, country, is_default, created_at, updated_at
+- [ ] Alembic migration for `customers` + `addresses` (schema defined separately from Pydantic models)
+- [ ] `CustomerRepository(BaseRepository)` — typed to `CustomerResponse`
+- [ ] `AddressRepository(BaseRepository)` — typed to `AddressResponse`
+
+### 0.2 Inventory
+
+> Depends on partner's `Product`/`SKU` models being accessible.
+
+- [ ] `InventoryItemBase`, `InventoryItemCreate`, `InventoryItemUpdate`, `InventoryItemResponse` — fields: id, sku_id, on_hand, reserved, created_at, updated_at
+- [ ] `StockReservationBase`, `StockReservationCreate`, `StockReservationResponse` — fields: id, inventory_item_id, order_id, quantity, expires_at, status (`ReservationStatus` enum: ACTIVE / COMMITTED / EXPIRED / RELEASED), created_at, updated_at
+- [ ] DB constraint (in migration): `on_hand >= 0`, `reserved >= 0`, `on_hand >= reserved`
+- [ ] Alembic migration for `inventory_items` + `stock_reservations`
+- [ ] `InventoryRepository(BaseRepository)`
+- [ ] `StockReservationRepository(BaseRepository)`
+
+### 0.3 Order & OrderItem
+
+- [ ] `OrderStatus` enum: `DRAFT` → `PENDING_PAYMENT` → `PAID` → `READY_TO_SHIP` → `SHIPPED` → `CANCELLED`
+- [ ] `OrderBase`, `OrderCreate`, `OrderUpdate`, `OrderResponse` — fields: id, customer_id, status, total_amount, currency, deleted_at, created_at, updated_at
+- [ ] `OrderItemBase`, `OrderItemCreate`, `OrderItemResponse` — fields: id, order_id, sku_id, quantity, unit_price (price snapshot at order time), created_at, updated_at
+- [ ] Alembic migration for `orders` + `order_items`
+- [ ] `OrderRepository(BaseRepository)`
+- [ ] `OrderItemRepository(BaseRepository)`
+
+### 0.4 Payment
+
+- [ ] `PaymentStatus` enum: PENDING / SUCCEEDED / FAILED / REFUNDED
+- [ ] `PaymentProvider` enum: STRIPE / …
+- [ ] `PaymentBase`, `PaymentCreate`, `PaymentUpdate`, `PaymentResponse` — fields: id, order_id, provider, provider_reference, amount, currency, status, created_at, updated_at
+- [ ] Alembic migration for `payments` (unique constraint on `order_id`, unique on `provider_reference`)
+- [ ] `PaymentRepository(BaseRepository)`
+
+### 0.5 Webhook Event Inbox (idempotency)
+
+- [ ] `WebhookEventCreate`, `WebhookEventResponse` — fields: id, provider, event_id, payload (dict), processed_at, created_at
+- [ ] Alembic migration for `webhook_events` (unique constraint on `(provider, event_id)`)
+- [ ] `WebhookEventRepository(BaseRepository)`
+
+### 0.6 Shipment
+
+- [ ] `Carrier` enum: DHL / MANUAL
+- [ ] `ShipmentStatus` enum: PENDING / LABEL_CREATED / HANDED_OVER
+- [ ] `ShipmentBase`, `ShipmentCreate`, `ShipmentUpdate`, `ShipmentResponse` — fields: id, order_id, carrier, tracking_number, label_url, label_format (PDF / ZPL), status, created_at, updated_at
+- [ ] Alembic migration for `shipments` (unique constraint on `order_id`)
+- [ ] `ShipmentRepository(BaseRepository)`
+
+---
+
+## Phase 1 — Checkout & Payment (`services/order-processing/`)
+
+> Create service: `src/app/services/order-processing/`
+
+### 1.1 Cart / Draft Order API
+
+- [ ] `POST /orders` — create draft order with line items (price snapshot from SKU)
+- [ ] `GET /orders/{id}` — retrieve order (customer-scoped via Keycloak token)
+- [ ] `DELETE /orders/{id}` — cancel draft order
+- [ ] Pydantic models: `OrderCreate`, `OrderItemCreate`, `OrderResponse`, `OrderDetailResponse`
+- [ ] Business logic: validate SKUs exist, calculate totals, create `Order` in `DRAFT` status
+- [ ] Register router in `main.py`
+
+### 1.2 Inventory Reservation
+
+- [ ] `functions/reserve_inventory.py` — atomic check-and-reserve (single DB transaction)
+ - Check `on_hand - reserved >= requested quantity`
+ - Insert `StockReservation` (status=ACTIVE, expires_at = now + configurable TTL)
+ - Increment `reserved` on `InventoryItem`
+- [ ] `functions/release_reservation.py` — set reservation to RELEASED, decrement `reserved`
+- [ ] `functions/commit_reservation.py` — set reservation to COMMITTED, decrement `on_hand` + `reserved`
+- [ ] `functions/expire_reservations.py` — background cleanup: expire stale reservations and release stock
+- [ ] Add `RESERVATION_TTL_MINUTES` to `Settings`
+
+### 1.3 Checkout Endpoint
+
+- [ ] `POST /orders/{id}/checkout` — transition `DRAFT` → `PENDING_PAYMENT`
+ - Reserve inventory (reject with 409 if insufficient stock, include which SKUs)
+ - Create PSP payment session/intent (see 1.4)
+ - Return PSP client secret / redirect URL
+
+### 1.4 PSP Integration (Stripe recommended as first adapter)
+
+- [ ] `services/payment_provider/` subfolder inside `order-processing`
+- [ ] `PaymentProviderAdapter` interface (abstract base): `create_session(order) → ProviderSession`, `verify_webhook(headers, body) → WebhookPayload`
+- [ ] `StripeAdapter` implementing the interface
+- [ ] Add `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET` to `Settings`
+
+### 1.5 Webhook Endpoint
+
+- [ ] `POST /webhooks/stripe` — raw body endpoint (do NOT parse body as JSON before signature check)
+- [ ] Signature verification via `StripeAdapter.verify_webhook()`
+- [ ] Idempotency check: lookup `(provider="stripe", event_id=stripe_event_id)` in `webhook_events` — return 200 if already processed
+- [ ] On `payment_intent.succeeded`:
+ - DB transaction: insert `WebhookEvent`, update `Payment` → SUCCEEDED, update `Order` → PAID, call `commit_reservation()`
+ - Enqueue fulfillment job (Phase 3; use a no-op stub for now)
+- [ ] On `payment_intent.payment_failed`:
+ - DB transaction: insert `WebhookEvent`, update `Payment` → FAILED, update `Order` → CANCELLED, call `release_reservation()`
+- [ ] Register webhook router in `main.py`
+
+---
+
+## Phase 2 — Admin Fulfillment (`services/admin/`)
+
+> Requires Phase 1 complete. No carrier integration yet — ship manually.
+
+- [ ] Create service: `src/app/services/admin/`
+- [ ] Keycloak role guard for all admin routes (e.g. `role="admin"`)
+
+### 2.1 Admin Order Management
+
+- [ ] `GET /admin/orders` — list orders, filter by status, paginated (`PaginatedResponse`)
+- [ ] `GET /admin/orders/{id}` — order detail with items, customer, address, payment, shipment
+- [ ] `PATCH /admin/orders/{id}/status` — manual status override with audit log
+
+### 2.2 Pick & Pack Documents
+
+- [ ] `GET /admin/orders/{id}/packing-slip` — HTML response (print-friendly) listing items, quantities, customer address
+- [ ] `GET /admin/orders/{id}/pick-list` — aggregate pick list across multiple orders (batch picking)
+
+### 2.3 Manual Shipment Marking
+
+- [ ] `POST /admin/orders/{id}/shipments` — create `Shipment` with manual tracking number; transition `Order` → `READY_TO_SHIP`
+- [ ] `POST /admin/orders/{id}/ship` — mark as handed over to carrier; transition `Order` → `SHIPPED`
+- [ ] Trigger customer notification email on `SHIPPED` (see 2.4)
+
+### 2.4 Customer Notification Email
+
+- [ ] `functions/send_tracking_email.py` — send tracking number + carrier link to customer
+- [ ] Add `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASSWORD`, `EMAIL_FROM` to `Settings`
+- [ ] Use async SMTP (e.g. `aiosmtplib`); templated HTML email
+
+---
+
+## Phase 3 — Automated Label Generation (`services/fulfillment/`)
+
+> Requires Phase 2 complete. Replaces the manual shipping step with automation.
+
+### 3.1 Background Job System
+
+- [ ] Evaluate and add queue backend: **ARQ** (Redis-based, async, fits FastAPI well) recommended
+- [ ] Add `REDIS_URL` to `Settings`
+- [ ] Worker entry point: `src/app/worker.py`
+- [ ] Job: `create_label_job(order_id: int)` — retries (max 5), exponential backoff, dead-letter logging
+
+### 3.2 Carrier Abstraction Layer
+
+- [ ] `services/fulfillment/carrier/interface.py` — `CarrierAdapter` abstract base
+ ```
+ create_label(order: OrderResponse, shipment: ShipmentResponse) → LabelResult
+ LabelResult(BaseModel): tracking_number, label_url, label_format
+ ```
+- [ ] `ManualCarrierAdapter` — no-op adapter (used in Phase 2, keeps interface consistent)
+
+### 3.3 DHL Adapter
+
+- [ ] `services/fulfillment/carrier/dhl.py` — `DhlAdapter(CarrierAdapter)`
+- [ ] DHL Parcel DE Shipping API (REST): create shipment, retrieve label PDF/ZPL
+- [ ] Add `DHL_API_KEY`, `DHL_ACCOUNT_NUMBER`, `DHL_PRODUCT` to `Settings`
+- [ ] Store label binary in object storage (S3 / MinIO) — add `STORAGE_*` settings
+- [ ] Handle DHL error responses with proper `AppException` subclass
+
+### 3.4 Admin Label Workflow
+
+- [ ] `POST /admin/orders/{id}/label` — trigger `create_label_job` manually (or re-trigger on failure)
+- [ ] `GET /admin/orders/{id}/label` — download label PDF/ZPL (proxy from storage)
+- [ ] Rules: only allowed when `Order.status == PAID` and no committed label exists
+
+### 3.5 Outbox Pattern (reliable job enqueueing)
+
+- [ ] `OutboxEventCreate`, `OutboxEventResponse` Pydantic models — fields: id, event_type, payload (dict), enqueued_at, created_at
+- [ ] Replace direct ARQ enqueue in webhook handler with outbox insert (same transaction as order update)
+- [ ] Background poller: read un-enqueued outbox events, push to ARQ, mark enqueued
+
+---
+
+## Phase 4 — Operational Hardening
+
+### 4.1 Observability
+
+- [ ] Correlation ID middleware — inject `X-Request-ID` into every request's log context (already supported by `shared/logger/context.py`)
+- [ ] Structured log fields: `order_id`, `payment_id`, `user_id` on all relevant log statements
+- [ ] Health endpoints: `GET /health` (liveness) + `GET /health/ready` (DB + Redis checks via `shared/database/health.py`)
+- [ ] Prometheus metrics endpoint (optional; add `prometheus-fastapi-instrumentator`)
+
+### 4.2 Reservation Expiry Job (production-grade)
+
+- [ ] ARQ scheduled job: run `expire_reservations()` every N minutes
+- [ ] Alert admin on repeated expiry failures
+
+### 4.3 Payment Reversals / Refunds
+
+- [ ] Handle `charge.refunded` / `payment_intent.canceled` Stripe webhooks
+- [ ] `Payment` → REFUNDED, `Order` → CANCELLED, `release_reservation()` if not yet committed
+- [ ] If already committed/shipped: create `Refund` record (separate model), flag for manual review
+
+### 4.4 Returns & RMA (basic)
+
+- [ ] `ReturnStatus` enum: REQUESTED / APPROVED / RECEIVED / REFUNDED
+- [ ] `ReturnCreate`, `ReturnUpdate`, `ReturnResponse` Pydantic models — fields: id, order_id, reason, status, created_at, updated_at
+- [ ] `POST /orders/{id}/returns` — customer requests return
+- [ ] `PATCH /admin/returns/{id}` — admin approves and processes
+
+### 4.5 Security Hardening
+
+- [ ] Restrict CORS `origins` in `main.py` (currently `["*"]`)
+- [ ] Rate limiting on webhook endpoint (e.g. `slowapi`)
+- [ ] Replace `secret_key` default `"CHANGE_ME_IN_PRODUCTION"` with startup validation
+
+---
+
+## Cross-Cutting Tasks (do as you go)
+
+- [ ] Write pytest tests for every new service module (mirror `tests/` structure)
+- [ ] Add each new model to Alembic `env.py` imports so auto-generate works
+- [ ] Register every new service router in `main.py`
+- [ ] Keep `Settings` as the single source of truth for all env vars — no hardcoded values
+- [ ] Use `shared/exceptions/` for all error cases — never return raw HTTP exceptions from business logic
+- [ ] Use `shared/responses/` factory helpers (`success()`, `paginated()`, `error_from_exception()`) in all routers
diff --git a/docs/roadmap.md b/docs/roadmap.md
new file mode 100644
index 0000000..7209a7e
--- /dev/null
+++ b/docs/roadmap.md
@@ -0,0 +1,208 @@
+# Roadmap — OpenTaberna API
+
+Implementierungsplan für den vollständigen Kunden-Kaufprozess:
+**Kunde betritt den Shop → sieht ein Item → klickt → kauft.**
+
+Basis: `crud-item-store` (Items) ist fertig. Alle anderen Services werden hier geplant.
+
+---
+
+## Shipping Flow (Referenz)
+
+```mermaid
+flowchart TD
+ A[Customer views product] --> B[Add to cart]
+ B --> C[Start checkout]
+ C --> D[Create Order: DRAFT]
+ D --> E["Reserve inventory (StockReservation)"]
+ E -->|insufficient stock| E1[Reject / show out of stock]
+ E -->|ok| F["Create Payment Session/Intent (PSP)"]
+ F --> G[Customer completes payment on PSP]
+ G --> H[PSP sends Webhook: payment_succeeded / payment_failed]
+
+ H --> I{"Webhook verified + idempotent?"}
+ I -->|duplicate| I1["Return 200 OK (no-op)"]
+ I -->|new| J[DB txn: Payment=SUCCEEDED]
+ J --> K[Order status -> PAID]
+ K --> L[Commit inventory: decrement on_hand + release reservation]
+ L --> M[Enqueue fulfillment job: CREATE_LABEL]
+
+ M --> N["Worker calls Carrier API (e.g. DHL)"]
+ N --> O{"Label created?"}
+ O -->|no| O1[Retry/backoff + alert admin]
+ O -->|yes| P[Shipment: tracking + label stored]
+ P --> Q[Order status -> READY_TO_SHIP]
+ Q --> R["Admin pick & pack with packing slip"]
+ R --> S[Hand over to carrier / pickup scan]
+ S --> T[Order status -> SHIPPED]
+ T --> U[Send tracking email to customer]
+
+ H -->|payment_failed| V[Payment=FAILED]
+ V --> W[Order status -> CANCELLED]
+ W --> X[Release reservation]
+```
+
+---
+
+## Alle Endpunkte
+
+### Bereits vorhanden — `crud-item-store`
+
+| Methode | Pfad | Was sie tut |
+|---|---|---|
+| `GET` | `/items` | Item-Liste mit Paginierung |
+| `GET` | `/items/{sku}` | Ein Item per SKU |
+| `GET` | `/items/{uuid}` | Ein Item per UUID |
+| `POST` | `/items` | Item anlegen |
+| `PATCH` | `/items/{uuid}` | Item aktualisieren |
+| `DELETE` | `/items/{uuid}` | Item löschen |
+
+---
+
+### `customers` — Phase 0/1
+
+| Methode | Pfad | Was sie tut |
+|---|---|---|
+| `GET` | `/customers/me` | Eigenes Kundenprofil abrufen (legt bei erstem Aufruf automatisch an) |
+| `PATCH` | `/customers/me` | Eigenes Profil aktualisieren |
+| `GET` | `/customers/me/addresses` | Alle Adressen des Kunden |
+| `POST` | `/customers/me/addresses` | Neue Adresse anlegen |
+| `PATCH` | `/customers/me/addresses/{id}` | Adresse aktualisieren |
+| `DELETE` | `/customers/me/addresses/{id}` | Adresse löschen |
+
+---
+
+### `order-processing` — Phase 1
+
+| Methode | Pfad | Was sie tut |
+|---|---|---|
+| `POST` | `/orders` | Draft-Order anlegen mit Line Items + Preissnapshot |
+| `GET` | `/orders/{id}` | Order abrufen (nur eigene, Keycloak-gesichert) |
+| `DELETE` | `/orders/{id}` | Draft-Order stornieren (nur DRAFT erlaubt) |
+| `POST` | `/orders/{id}/checkout` | DRAFT → PENDING_PAYMENT: Inventar reservieren + PSP-Session erstellen, gibt PSP-Client-Secret zurück |
+| `POST` | `/webhooks/stripe` | Stripe-Webhook: Signaturprüfung, Idempotenz, PAID oder CANCELLED je nach Event |
+
+---
+
+### `admin` — Phase 2
+
+| Methode | Pfad | Was sie tut |
+|---|---|---|
+| `GET` | `/admin/orders` | Alle Orders, filterbar nach Status, paginiert |
+| `GET` | `/admin/orders/{id}` | Order-Detail mit Items, Kunde, Adresse, Payment, Shipment |
+| `PATCH` | `/admin/orders/{id}/status` | Manueller Status-Override mit Audit-Log |
+| `GET` | `/admin/orders/{id}/packing-slip` | HTML-Packing-Slip (druckbar) für eine Order |
+| `GET` | `/admin/orders/pick-list` | Aggregierte Pick-Liste über mehrere Orders (Batch-Picking) |
+| `POST` | `/admin/orders/{id}/shipments` | Shipment anlegen mit manueller Tracking-Nummer → Order → READY_TO_SHIP |
+| `POST` | `/admin/orders/{id}/ship` | Order → SHIPPED, löst Tracking-E-Mail aus |
+
+---
+
+### `fulfillment` — Phase 3
+
+| Methode | Pfad | Was sie tut |
+|---|---|---|
+| `POST` | `/admin/orders/{id}/label` | DHL-Label-Job manuell anstoßen (oder bei Fehler neu triggern) |
+| `GET` | `/admin/orders/{id}/label` | Label-PDF/ZPL herunterladen (Proxy aus Storage) |
+
+---
+
+### `returns` — Phase 4
+
+| Methode | Pfad | Was sie tut |
+|---|---|---|
+| `POST` | `/orders/{id}/returns` | Kunde beantragt Rücksendung |
+| `PATCH` | `/admin/returns/{id}` | Admin genehmigt/verarbeitet die Rücksendung |
+
+---
+
+### Infra / Health — Phase 4
+
+| Methode | Pfad | Was sie tut |
+|---|---|---|
+| `GET` | `/health` | Liveness-Check (API läuft) |
+| `GET` | `/health/ready` | Readiness-Check (DB + Redis erreichbar) |
+
+---
+
+## Implementierungsreihenfolge
+
+### Phase 0 — Domain Models & DB Schema *(Blockiert alles andere)*
+
+Kein Feature-Endpunkt ohne diese Grundlage. Beide können parallel arbeiten.
+
+| Du | Kollege |
+|---|---|
+| **0.1** Customer + Address — Pydantic-Modelle, Alembic-Migration, Repository | **0.2** Inventory: `InventoryItem`, `StockReservation`, DB-Constraints (`on_hand >= reserved`), Repository |
+| **0.5** Webhook Event Inbox — Modell + Migration (kurz, unabhängig) | *(parallel zu 0.2)* |
+| → dann: **0.3** Order + OrderItem — abhängig von 0.1 + 0.2 | |
+| **0.4** Payment-Modell + Migration | **0.6** Shipment-Modell + Migration |
+
+**Fortschritt:** `[ ] 0.1` `[ ] 0.2` `[ ] 0.3` `[ ] 0.4` `[ ] 0.5` `[ ] 0.6`
+
+---
+
+### Phase 1 — Checkout & Payment (`services/order-processing/`)
+
+| Du | Kollege |
+|---|---|
+| **1.1** `POST /orders` — Draft-Order + Business-Logik (Preissnapshot, Validierung, DRAFT-Status) | **1.4** PSP-Integration — `PaymentProviderAdapter` Interface + `StripeAdapter` (völlig unabhängig) |
+| **1.2** Inventar-Funktionen — `reserve_inventory`, `release_reservation`, `commit_reservation`, `expire_reservations` | *(parallel zu 1.1)* |
+| → dann: **1.3** `POST /orders/{id}/checkout` — braucht 1.1 + 1.2 + 1.4 | |
+| → dann: **1.5** `POST /webhooks/stripe` — Signaturprüfung, Idempotenz, DB-Transaktion | |
+
+> Nach Phase 1: funktionierender Webshop der Geld einnehmen und Stock sicher reservieren kann.
+
+**Fortschritt:** `[ ] 1.1` `[ ] 1.2` `[ ] 1.3` `[ ] 1.4` `[ ] 1.5`
+
+---
+
+### Phase 2 — Admin-Fulfillment (`services/admin/`) *(kein Carrier nötig)*
+
+| Du | Kollege |
+|---|---|
+| **2.1** `GET /admin/orders` + `GET /admin/orders/{id}` — Liste + Detail, paginiert, Status-Filter | **2.4** `send_tracking_email.py` + SMTP-Settings + HTML-E-Mail-Template |
+| **2.2** `GET /admin/orders/{id}/packing-slip` + `pick-list` — HTML-Dokumente | *(parallel zu 2.1)* |
+| → dann: **2.3** `POST /admin/orders/{id}/shipments` + `POST /admin/orders/{id}/ship` | |
+
+> Nach Phase 2: **vollständiges lauffähiges MVP** — zahlender Kunde, korrekter Lagerbestand, manuell versendbar.
+
+**Fortschritt:** `[ ] 2.1` `[ ] 2.2` `[ ] 2.3` `[ ] 2.4`
+
+---
+
+### Phase 3 — DHL-Label-Generierung (`services/fulfillment/`)
+
+| Du | Kollege |
+|---|---|
+| **3.1** ARQ Job-System — Redis-Queue, `worker.py`, Retry/Backoff, Dead-Letter-Logging | **3.2** `CarrierAdapter` Interface + `ManualCarrierAdapter` (no-op, hält Interface konsistent) |
+| **3.3** `DhlAdapter` — DHL Parcel DE REST API, Label PDF/ZPL, Storage (S3/MinIO) | **3.5** Outbox-Pattern — Outbox-Tabelle, Poller, zuverlässiges Job-Enqueuing in gleicher DB-Transaktion |
+| **3.4** Admin-Label-Endpunkte `POST/GET /admin/orders/{id}/label` | |
+
+**Fortschritt:** `[ ] 3.1` `[ ] 3.2` `[ ] 3.3` `[ ] 3.4` `[ ] 3.5`
+
+---
+
+### Phase 4 — Operational Hardening *(laufend parallel möglich)*
+
+| Task | Was |
+|---|---|
+| **4.1** Observability | Correlation-ID-Middleware, strukturierte Logs mit `order_id`/`user_id`, `/health` + `/health/ready` |
+| **4.2** Reservation-Expiry-Job | ARQ Scheduled Job — `expire_reservations()` alle N Minuten |
+| **4.3** Payment Reversals | Stripe `charge.refunded` / `payment_intent.canceled` Webhooks → REFUNDED/CANCELLED |
+| **4.4** Returns / RMA | `POST /orders/{id}/returns`, `PATCH /admin/returns/{id}` |
+| **4.5** Security Hardening | CORS einschränken, Rate-Limiting auf Webhook (`slowapi`), `CHANGE_ME`-Startup-Validation |
+
+**Fortschritt:** `[ ] 4.1` `[ ] 4.2` `[ ] 4.3` `[ ] 4.4` `[ ] 4.5`
+
+---
+
+## Kritische Reihenfolge
+
+```
+Phase 0 (parallel) → Phase 1 (parallel) → Phase 2 (parallel) → MVP ✓ → Phase 3 → Phase 4
+```
+
+**Kritischer Pfad für den ersten zahlenden Kunden:** `GET /items` → `POST /orders` → `POST /orders/{id}/checkout`
+
+**Gesamt: ~26 Endpunkte** (6 vorhanden, 20 zu bauen)
diff --git a/docs/taberna board.md b/docs/taberna board.md
new file mode 100644
index 0000000..a3ad163
--- /dev/null
+++ b/docs/taberna board.md
@@ -0,0 +1,57 @@
+---
+kanban-plugin: board
+---
+
+## Backlog
+
+- [ ] **Database Layer Setup** 📦 #database #infrastructure
Setup PostgreSQL with SQLAlchemy/Tortoise ORM, connection pooling, migrations (Alembic), and health checks
+- [ ] **Authentication & Authorization** 🔐 #security #auth
Implement Keycloak integration, JWT token handling, role-based access control (RBAC), and permission middleware
+- [ ] **Item Store CRUD** 📝 #feature #crud
Complete CRUD operations for item-store service using shared modules (exceptions, responses, logger, config)
+- [ ] **API Documentation** 📚 #docs #api
Setup Swagger/OpenAPI documentation with examples, schemas, and authentication flows
+- [ ] **Caching Layer** ⚡ #performance #cache
Implement Redis caching for frequently accessed data with TTL management
+- [ ] **Background Jobs** ⏰ #jobs #async
Setup Celery/ARQ for async tasks (email notifications, data processing, scheduled jobs)
+- [ ] **File Upload Service** 📤 #feature #files
Handle file uploads with S3/MinIO integration, validation, and thumbnail generation
+- [ ] **Search & Filtering** 🔍 #feature #search
Implement full-text search with Elasticsearch/PostgreSQL and advanced filtering
+- [ ] **Rate Limiting** 🚦 #security #middleware
Add rate limiting middleware to prevent abuse and DDoS attacks
+- [ ] **Monitoring & Metrics** 📊 #observability #metrics
Setup Prometheus metrics, Grafana dashboards, and alerting
+
+
+## Next
+
+- [ ] **Shared Modules Integration** 🔧 #refactor #shared
Ensure all services use config, logger, exceptions, and responses modules correctly
+- [ ] **Testing Strategy** 🧪 #testing #quality
Write unit tests, integration tests, and E2E tests for all features. Target 80% coverage
+- [ ] **Docker Compose Setup** 🐳 #devops #docker
Create docker-compose.yml with all services (API, PostgreSQL, Redis, Keycloak) for local development
+- [ ] **CI/CD Pipeline** 🚀 #devops #automation
Setup GitHub Actions for testing, linting, building, and deploying to staging/production
+- [ ] **Error Handling Middleware** ⚠️ #middleware #errors
Create global exception handler using shared exception module for consistent error responses
+
+
+## In Progress
+
+- [ ] **Shared Modules Documentation** 📖 #docs #shared
Document how to use config, logger, exceptions, and responses in features (✅ completed docs/shared-modules.md)
+
+
+## Wait
+
+- [ ] **API Versioning** 🔢 #api #architecture
Design and implement API versioning strategy (URL-based vs header-based)
+- [ ] **WebSocket Support** 🔌 #feature #realtime
Add WebSocket support for real-time notifications and updates (waiting for use case clarification)
+- [ ] **Multi-tenancy** 🏢 #architecture #multitenancy
Implement tenant isolation and data segregation (waiting for requirements)
+- [ ] **Payment Integration** 💳 #feature #payments
Integrate payment gateway (Stripe/PayPal) for transactions (waiting for business logic)
+
+
+## Done
+
+**Complete**
+- [x] **Config Module** ⚙️ #shared #config
✅ Environment-based settings, secrets management, singleton pattern
+- [x] **Logger Module** 📝 #shared #logging
✅ Structured logging, context management, sensitive data filtering
+- [x] **Exception Module** ❌ #shared #exceptions
✅ Custom exception hierarchy, auto-logging, helper functions
+- [x] **Response Module** ✅ #shared #responses
✅ Generic success/error responses, pagination, exception integration
+- [x] **Project Structure** 🏗️ #architecture #setup
✅ Modular FastAPI structure with services, shared modules, and documentation
+
+
+
+
+%% kanban:settings
+```
+{"kanban-plugin":"board","hide-tags-in-title":true,"tag-colors":[],"hide-card-count":false,"show-checkboxes":false,"show-relative-date":true,"hide-date-display":false,"hide-date-in-title":true,"hide-tags-display":false,"date-colors":[{"isToday":false,"distance":3,"unit":"days","direction":"after","color":"rgba(255, 218, 0, 1)"},{"isToday":false,"distance":1,"unit":"days","direction":"after","color":"rgba(255, 80, 0, 1)"},{"distance":1,"unit":"days","direction":"after","color":"rgba(255, 0, 0, 1)","isBefore":true}],"move-dates":false,"append-archive-date":true,"archive-with-date":true,"date-picker-week-start":1,"list-collapse":[false,null,false,null,false],"new-note-folder":"Uni","max-archive-size":20}
+```
+%%
\ No newline at end of file