API-Design - Good Deeds Nachbarschaftshilfe-App
Version: 1.0
Datum: 2026-06-05
Autor: CTO
Status: Initial
Base URL: https://api.gooddeeds.app/api/v1
Protocol: REST/JSON over HTTPS
Inhaltsverzeichnis
Überblick
Die Good Deeds API ist eine RESTful JSON API für die Nachbarschaftshilfe-App. Sie nutzt JWT für Authentication und folgt REST-Best-Practices.
Design-Prinzipien
- REST-konform: Nutzt HTTP-Verben semantisch (GET, POST, PATCH, DELETE)
- JSON-First: Alle Requests/Responses in JSON
- Versioniert: URL-basiertes Versioning (
/api/v1) - Idempotent: POST/PUT/DELETE können sicher wiederholt werden
- Filterable: GET-Endpoints unterstützen Query-Parameter
- Paginiert: Collections nutzen Cursor-Pagination
Common Headers
Content-Type: application/json
Authorization: Bearer <JWT_ACCESS_TOKEN>
Accept: application/json
X-API-Version: 1.0Authentication
JWT-based Authentication
Die API nutzt JWT (JSON Web Tokens) mit Access + Refresh Tokens.
Access Token: 15 Minuten Lifetime
Refresh Token: 7 Tage Lifetime
Auth Flow
1. User → POST /auth/register (Email/Password oder OAuth)
2. API → {accessToken, refreshToken, user}
3. User → GET /tasks (Header: Authorization: Bearer <accessToken>)
4. API → 401 (wenn expired)
5. User → POST /auth/refresh {refreshToken}
6. API → {accessToken, refreshToken}API-Endpoints
1. Authentication
1.1 Register (Email/Password)
POST /api/v1/auth/register
Content-Type: application/json
{
"email": "user@example.com",
"password": "SecurePass123!",
"name": "Max Mustermann",
"location": {
"type": "Point",
"coordinates": [13.405, 52.52] // [longitude, latitude]
}
}Response (201 Created):
{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"name": "Max Mustermann",
"location": {
"type": "Point",
"coordinates": [13.405, 52.52]
},
"points_balance": 0,
"created_at": "2026-06-05T10:30:00Z"
}
}1.2 Login (Email/Password)
POST /api/v1/auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "SecurePass123!"
}Response (200 OK):
{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"name": "Max Mustermann",
"points_balance": 150
}
}1.3 OAuth Login (Google)
POST /api/v1/auth/oauth/google
Content-Type: application/json
{
"token": "google-oauth-token-here"
}Response (200 OK): Same as Login
1.4 Refresh Token
POST /api/v1/auth/refresh
Content-Type: application/json
{
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}Response (200 OK):
{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}1.5 Logout
POST /api/v1/auth/logout
Authorization: Bearer <accessToken>
Content-Type: application/json
{
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}Response (204 No Content)
2. Users
2.1 Get Current User
GET /api/v1/users/me
Authorization: Bearer <accessToken>Response (200 OK):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"name": "Max Mustermann",
"avatar_url": "https://storage.gooddeeds.app/avatars/123.jpg",
"bio": "Ich helfe gerne in der Nachbarschaft!",
"location": {
"type": "Point",
"coordinates": [13.405, 52.52]
},
"address": "Berlin, Deutschland",
"points_balance": 150,
"trust_score": 4.5,
"tasks_completed": 12,
"tasks_created": 5,
"created_at": "2026-05-01T10:00:00Z",
"last_login_at": "2026-06-05T14:30:00Z"
}2.2 Update User Profile
PATCH /api/v1/users/me
Authorization: Bearer <accessToken>
Content-Type: application/json
{
"name": "Max Mustermann",
"bio": "Handwerker mit 10 Jahren Erfahrung",
"location": {
"type": "Point",
"coordinates": [13.405, 52.52]
},
"address": "Berlin, Deutschland"
}Response (200 OK): Updated User Object
2.3 Get User Profile (Public)
GET /api/v1/users/:id
Authorization: Bearer <accessToken>Response (200 OK):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Max Mustermann",
"avatar_url": "https://storage.gooddeeds.app/avatars/123.jpg",
"bio": "Ich helfe gerne!",
"trust_score": 4.5,
"tasks_completed": 12,
"member_since": "2026-05-01T10:00:00Z"
}2.4 Upload Avatar
POST /api/v1/users/me/avatar
Authorization: Bearer <accessToken>
Content-Type: multipart/form-data
file: (binary image data)Response (200 OK):
{
"avatar_url": "https://storage.gooddeeds.app/avatars/123.jpg"
}3. Tasks (Aufgaben)
3.1 Get Tasks (List)
GET /api/v1/tasks
Authorization: Bearer <accessToken>
Query Parameters:
?lat=52.52&lng=13.405&radius=5000 # Geo-Filter (Radius in Metern)
&category=handwerk # Kategorie-Filter
&status=open # Status-Filter
&limit=20 # Pagination Limit
&cursor=<cursor_token> # Pagination CursorResponse (200 OK):
{
"data": [
{
"id": "task-uuid-1",
"title": "Lampe im Wohnzimmer installieren",
"description": "Ich brauche Hilfe beim Anbringen einer Deckenlampe. Leiter vorhanden.",
"category": "handwerk",
"location": {
"type": "Point",
"coordinates": [13.405, 52.52]
},
"address": "Berlin Mitte",
"distance": 1250, // Meter
"points_reward": 50,
"status": "open",
"image_urls": ["https://storage.gooddeeds.app/tasks/img1.jpg"],
"creator": {
"id": "user-uuid",
"name": "Anna Schmidt",
"avatar_url": "https://storage.gooddeeds.app/avatars/456.jpg",
"trust_score": 4.8
},
"created_at": "2026-06-05T10:00:00Z",
"expires_at": "2026-06-12T10:00:00Z"
}
],
"pagination": {
"next_cursor": "eyJpZCI6InRhc2stdXVpZC0yIn0=",
"has_more": true
}
}3.2 Create Task
POST /api/v1/tasks
Authorization: Bearer <accessToken>
Content-Type: application/json
{
"title": "Lampe im Wohnzimmer installieren",
"description": "Ich brauche Hilfe beim Anbringen einer Deckenlampe.",
"category": "handwerk",
"location": {
"type": "Point",
"coordinates": [13.405, 52.52]
},
"address": "Berlin Mitte, Invalidenstraße 120",
"points_reward": 50,
"image_urls": ["https://storage.gooddeeds.app/tasks/img1.jpg"]
}Response (201 Created):
{
"id": "task-uuid-1",
"title": "Lampe im Wohnzimmer installieren",
"description": "Ich brauche Hilfe beim Anbringen einer Deckenlampe.",
"category": "handwerk",
"location": {
"type": "Point",
"coordinates": [13.405, 52.52]
},
"address": "Berlin Mitte, Invalidenstraße 120",
"points_reward": 50,
"status": "open",
"creator_id": "user-uuid",
"helper_id": null,
"image_urls": ["https://storage.gooddeeds.app/tasks/img1.jpg"],
"created_at": "2026-06-05T10:00:00Z",
"expires_at": "2026-06-12T10:00:00Z"
}3.3 Get Task by ID
GET /api/v1/tasks/:id
Authorization: Bearer <accessToken>Response (200 OK):
{
"id": "task-uuid-1",
"title": "Lampe im Wohnzimmer installieren",
"description": "Ich brauche Hilfe beim Anbringen einer Deckenlampe. Leiter vorhanden.",
"category": "handwerk",
"location": {
"type": "Point",
"coordinates": [13.405, 52.52]
},
"address": "Berlin Mitte, Invalidenstraße 120",
"points_reward": 50,
"status": "open",
"image_urls": ["https://storage.gooddeeds.app/tasks/img1.jpg"],
"creator": {
"id": "user-uuid-creator",
"name": "Anna Schmidt",
"avatar_url": "https://storage.gooddeeds.app/avatars/456.jpg",
"trust_score": 4.8
},
"helper": null,
"created_at": "2026-06-05T10:00:00Z",
"expires_at": "2026-06-12T10:00:00Z"
}3.4 Apply for Task
POST /api/v1/tasks/:id/apply
Authorization: Bearer <accessToken>
Content-Type: application/json
{
"message": "Ich kann dir heute Abend helfen!"
}Response (200 OK):
{
"message": "Application submitted successfully",
"task_id": "task-uuid-1",
"status": "pending_approval"
}3.5 Accept Helper (Creator only)
POST /api/v1/tasks/:id/accept
Authorization: Bearer <accessToken>
Content-Type: application/json
{
"helper_id": "user-uuid-helper"
}Response (200 OK):
{
"id": "task-uuid-1",
"status": "assigned",
"helper": {
"id": "user-uuid-helper",
"name": "Max Mustermann"
}
}3.6 Complete Task (Helper marks as done)
POST /api/v1/tasks/:id/complete
Authorization: Bearer <accessToken>Response (200 OK):
{
"id": "task-uuid-1",
"status": "pending_confirmation",
"message": "Task marked as complete. Waiting for creator confirmation."
}3.7 Confirm Completion (Creator confirms)
POST /api/v1/tasks/:id/confirm
Authorization: Bearer <accessToken>
Content-Type: application/json
{
"rating": 5,
"comment": "Super Arbeit, danke!"
}Response (200 OK):
{
"id": "task-uuid-1",
"status": "completed",
"completed_at": "2026-06-05T18:30:00Z",
"points_transferred": 50,
"message": "Task completed successfully! Points awarded to helper."
}3.8 Cancel Task
DELETE /api/v1/tasks/:id
Authorization: Bearer <accessToken>Response (204 No Content)
4. Points
4.1 Get Points Balance
GET /api/v1/users/me/points
Authorization: Bearer <accessToken>Response (200 OK):
{
"user_id": "user-uuid",
"points_balance": 350,
"total_earned": 500,
"total_spent": 150,
"tasks_completed": 10
}4.2 Get Points History
GET /api/v1/users/me/points/history
Authorization: Bearer <accessToken>
Query Parameters:
?limit=50
&cursor=<cursor_token>Response (200 OK):
{
"data": [
{
"id": "transaction-uuid-1",
"amount": 50,
"type": "task_completed",
"description": "Completed task: Lampe installieren",
"task_id": "task-uuid-1",
"created_at": "2026-06-05T18:30:00Z"
},
{
"id": "transaction-uuid-2",
"amount": -100,
"type": "redemption",
"description": "Redeemed: 10€ REWE Gutschein",
"redemption_id": "redemption-uuid-1",
"created_at": "2026-06-04T12:00:00Z"
}
],
"pagination": {
"next_cursor": "eyJpZCI6InRyYW5zYWN0aW9uLXV1aWQtMyJ9",
"has_more": true
}
}4.3 Get Leaderboard
GET /api/v1/points/leaderboard
Authorization: Bearer <accessToken>
Query Parameters:
?period=month # all, week, month
&limit=100Response (200 OK):
{
"period": "month",
"data": [
{
"rank": 1,
"user": {
"id": "user-uuid-1",
"name": "Max Mustermann",
"avatar_url": "https://storage.gooddeeds.app/avatars/123.jpg"
},
"points": 450,
"tasks_completed": 9
},
{
"rank": 2,
"user": {
"id": "user-uuid-2",
"name": "Anna Schmidt",
"avatar_url": "https://storage.gooddeeds.app/avatars/456.jpg"
},
"points": 380,
"tasks_completed": 8
}
]
}5. Partners & Redemptions
5.1 Get Partners List
GET /api/v1/partners
Authorization: Bearer <accessToken>
Query Parameters:
?is_active=true
&limit=50
&cursor=<cursor_token>Response (200 OK):
{
"data": [
{
"id": "partner-uuid-1",
"name": "REWE",
"logo_url": "https://storage.gooddeeds.app/partners/rewe.png",
"description": "Lebensmittel und mehr",
"website_url": "https://www.rewe.de",
"is_active": true
}
],
"pagination": {
"next_cursor": "eyJpZCI6InBhcnRuZXItdXVpZC0yIn0=",
"has_more": true
}
}5.2 Get Partner Offers
GET /api/v1/partners/:id/offers
Authorization: Bearer <accessToken>Response (200 OK):
{
"partner": {
"id": "partner-uuid-1",
"name": "REWE",
"logo_url": "https://storage.gooddeeds.app/partners/rewe.png"
},
"offers": [
{
"id": "offer-uuid-1",
"title": "5€ Gutschein",
"description": "5€ Rabatt auf deinen Einkauf ab 30€",
"points_cost": 50,
"discount_type": "fixed_amount",
"discount_value": 5.00,
"terms": "Gültig bis 31.12.2026",
"available": true
},
{
"id": "offer-uuid-2",
"title": "10€ Gutschein",
"description": "10€ Rabatt auf deinen Einkauf ab 50€",
"points_cost": 100,
"discount_type": "fixed_amount",
"discount_value": 10.00,
"available": true
}
]
}5.3 Redeem Points
POST /api/v1/redemptions
Authorization: Bearer <accessToken>
Content-Type: application/json
{
"partner_id": "partner-uuid-1",
"offer_id": "offer-uuid-1",
"points_spent": 50
}Response (201 Created):
{
"id": "redemption-uuid-1",
"partner": {
"id": "partner-uuid-1",
"name": "REWE"
},
"points_spent": 50,
"discount_code": "REWE-A1B2C3D4",
"discount_description": "5€ Gutschein",
"discount_value": 5.00,
"status": "active",
"redeemed_at": "2026-06-05T14:30:00Z",
"expires_at": "2026-12-31T23:59:59Z",
"instructions": "Zeige diesen Code an der Kasse vor."
}5.4 Get My Redemptions
GET /api/v1/users/me/redemptions
Authorization: Bearer <accessToken>
Query Parameters:
?status=active # active, used, expired
&limit=50
&cursor=<cursor_token>Response (200 OK):
{
"data": [
{
"id": "redemption-uuid-1",
"partner": {
"id": "partner-uuid-1",
"name": "REWE",
"logo_url": "https://storage.gooddeeds.app/partners/rewe.png"
},
"discount_code": "REWE-A1B2C3D4",
"discount_description": "5€ Gutschein",
"discount_value": 5.00,
"status": "active",
"redeemed_at": "2026-06-05T14:30:00Z",
"expires_at": "2026-12-31T23:59:59Z"
}
],
"pagination": {
"next_cursor": "eyJpZCI6InJlZGVtcHRpb24tdXVpZC0yIn0=",
"has_more": false
}
}6. Ratings & Reviews
6.1 Rate User
POST /api/v1/ratings
Authorization: Bearer <accessToken>
Content-Type: application/json
{
"task_id": "task-uuid-1",
"ratee_id": "user-uuid-helper",
"score": 5,
"comment": "Super Arbeit, sehr zuverlässig!"
}Response (201 Created):
{
"id": "rating-uuid-1",
"task_id": "task-uuid-1",
"rater_id": "user-uuid-creator",
"ratee_id": "user-uuid-helper",
"score": 5,
"comment": "Super Arbeit, sehr zuverlässig!",
"created_at": "2026-06-05T18:35:00Z"
}6.2 Get User Ratings
GET /api/v1/users/:id/ratings
Authorization: Bearer <accessToken>
Query Parameters:
?limit=20
&cursor=<cursor_token>Response (200 OK):
{
"user": {
"id": "user-uuid-helper",
"name": "Max Mustermann",
"trust_score": 4.8
},
"stats": {
"total_ratings": 15,
"average_score": 4.8,
"score_distribution": {
"5": 12,
"4": 2,
"3": 1,
"2": 0,
"1": 0
}
},
"data": [
{
"id": "rating-uuid-1",
"score": 5,
"comment": "Super Arbeit!",
"rater": {
"name": "Anna Schmidt",
"avatar_url": "https://storage.gooddeeds.app/avatars/456.jpg"
},
"created_at": "2026-06-05T18:35:00Z"
}
],
"pagination": {
"next_cursor": "eyJpZCI6InJhdGluZy11dWlkLTIifQ==",
"has_more": true
}
}Error Handling
Standard Error Response
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{
"field": "email",
"message": "Email must be a valid email address"
},
{
"field": "password",
"message": "Password must be at least 8 characters"
}
],
"timestamp": "2026-06-05T10:30:00Z",
"request_id": "req-uuid-123"
}
}Error Codes
| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | VALIDATION_ERROR | Request validation failed |
| 401 | UNAUTHORIZED | Missing or invalid authentication |
| 403 | FORBIDDEN | Insufficient permissions |
| 404 | NOT_FOUND | Resource not found |
| 409 | CONFLICT | Resource already exists |
| 422 | UNPROCESSABLE_ENTITY | Business logic error |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests |
| 500 | INTERNAL_SERVER_ERROR | Server error |
| 503 | SERVICE_UNAVAILABLE | Service temporarily unavailable |
Common Error Examples
401 Unauthorized
{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or expired access token",
"timestamp": "2026-06-05T10:30:00Z"
}
}403 Forbidden
{
"error": {
"code": "FORBIDDEN",
"message": "You do not have permission to perform this action",
"timestamp": "2026-06-05T10:30:00Z"
}
}422 Business Logic Error
{
"error": {
"code": "INSUFFICIENT_POINTS",
"message": "Insufficient points balance for this redemption",
"details": {
"required": 100,
"available": 50
},
"timestamp": "2026-06-05T10:30:00Z"
}
}Rate Limiting
Limits
- Public Endpoints: 60 requests / minute / IP
- Authenticated Endpoints: 1000 requests / hour / user
- Sensitive Endpoints (Login, Register): 5 requests / 15 minutes / IP
Headers
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 987
X-RateLimit-Reset: 1717502400429 Response
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Please try again later.",
"retry_after": 60,
"timestamp": "2026-06-05T10:30:00Z"
}
}Pagination
Wir nutzen Cursor-based Pagination für Performance.
Query Parameters
?limit=20 # Max 100
&cursor=<cursor_token> # Opaque cursor stringResponse Format
{
"data": [...],
"pagination": {
"next_cursor": "eyJpZCI6InV1aWQifQ==",
"prev_cursor": "eyJpZCI6InV1aWQifQ==",
"has_more": true
}
}Example
# First page
GET /api/v1/tasks?limit=20
# Next page
GET /api/v1/tasks?limit=20&cursor=eyJpZCI6InV1aWQifQ==OpenAPI Specification
Siehe openapi.yaml für die vollständige OpenAPI 3.0 Specification.
Swagger UI: http://localhost:3000/api/docs (Development)
Production: https://api.gooddeeds.app/docs
Versioning
- URL-basiert:
/api/v1,/api/v2 - Breaking Changes: Neue Major-Version
- Deprecation: 6 Monate Notice vor Removal
- Header (optional):
Accept-Version: 1.0
Categories (Task Categories)
enum TaskCategory {
HANDWERK = 'handwerk', // Handwerkliche Tätigkeiten
GARTEN = 'garten', // Gartenarbeit
TECHNIK = 'technik', // IT/Computer-Hilfe
TRANSPORT = 'transport', // Fahrdienste/Transport
BETREUUNG = 'betreuung', // Kinderbetreuung/Seniorenbetreuung
HAUSHALT = 'haushalt', // Haushaltshilfe
EINKAUF = 'einkauf', // Einkaufshilfe
SONSTIGES = 'sonstiges' // Andere Aufgaben
}Webhooks (Future)
Für Partner-Integration sind Webhooks geplant:
POST <partner_webhook_url>
Content-Type: application/json
X-Webhook-Signature: sha256=...
{
"event": "redemption.created",
"data": {
"id": "redemption-uuid-1",
"discount_code": "REWE-A1B2C3D4",
"user_id": "user-uuid"
},
"timestamp": "2026-06-05T14:30:00Z"
}Nächste Schritte:
- OpenAPI YAML Spec generieren
- Swagger UI integrieren
- API-Tests schreiben (Postman Collection)
- SDK generieren (TypeScript Client)
