Skip to content

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

  1. Überblick
  2. Authentication
  3. API-Endpoints
  4. Error Handling
  5. Rate Limiting
  6. Pagination
  7. OpenAPI Specification

Ü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

http
Content-Type: application/json
Authorization: Bearer <JWT_ACCESS_TOKEN>
Accept: application/json
X-API-Version: 1.0

Authentication

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)

http
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):

json
{
  "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)

http
POST /api/v1/auth/login
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "SecurePass123!"
}

Response (200 OK):

json
{
  "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)

http
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

http
POST /api/v1/auth/refresh
Content-Type: application/json

{
  "refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}

Response (200 OK):

json
{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}

1.5 Logout

http
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

http
GET /api/v1/users/me
Authorization: Bearer <accessToken>

Response (200 OK):

json
{
  "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

http
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)

http
GET /api/v1/users/:id
Authorization: Bearer <accessToken>

Response (200 OK):

json
{
  "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

http
POST /api/v1/users/me/avatar
Authorization: Bearer <accessToken>
Content-Type: multipart/form-data

file: (binary image data)

Response (200 OK):

json
{
  "avatar_url": "https://storage.gooddeeds.app/avatars/123.jpg"
}

3. Tasks (Aufgaben)

3.1 Get Tasks (List)

http
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 Cursor

Response (200 OK):

json
{
  "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

http
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):

json
{
  "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

http
GET /api/v1/tasks/:id
Authorization: Bearer <accessToken>

Response (200 OK):

json
{
  "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

http
POST /api/v1/tasks/:id/apply
Authorization: Bearer <accessToken>
Content-Type: application/json

{
  "message": "Ich kann dir heute Abend helfen!"
}

Response (200 OK):

json
{
  "message": "Application submitted successfully",
  "task_id": "task-uuid-1",
  "status": "pending_approval"
}

3.5 Accept Helper (Creator only)

http
POST /api/v1/tasks/:id/accept
Authorization: Bearer <accessToken>
Content-Type: application/json

{
  "helper_id": "user-uuid-helper"
}

Response (200 OK):

json
{
  "id": "task-uuid-1",
  "status": "assigned",
  "helper": {
    "id": "user-uuid-helper",
    "name": "Max Mustermann"
  }
}

3.6 Complete Task (Helper marks as done)

http
POST /api/v1/tasks/:id/complete
Authorization: Bearer <accessToken>

Response (200 OK):

json
{
  "id": "task-uuid-1",
  "status": "pending_confirmation",
  "message": "Task marked as complete. Waiting for creator confirmation."
}

3.7 Confirm Completion (Creator confirms)

http
POST /api/v1/tasks/:id/confirm
Authorization: Bearer <accessToken>
Content-Type: application/json

{
  "rating": 5,
  "comment": "Super Arbeit, danke!"
}

Response (200 OK):

json
{
  "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

http
DELETE /api/v1/tasks/:id
Authorization: Bearer <accessToken>

Response (204 No Content)


4. Points

4.1 Get Points Balance

http
GET /api/v1/users/me/points
Authorization: Bearer <accessToken>

Response (200 OK):

json
{
  "user_id": "user-uuid",
  "points_balance": 350,
  "total_earned": 500,
  "total_spent": 150,
  "tasks_completed": 10
}

4.2 Get Points History

http
GET /api/v1/users/me/points/history
Authorization: Bearer <accessToken>
Query Parameters:
  ?limit=50
  &cursor=<cursor_token>

Response (200 OK):

json
{
  "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

http
GET /api/v1/points/leaderboard
Authorization: Bearer <accessToken>
Query Parameters:
  ?period=month  # all, week, month
  &limit=100

Response (200 OK):

json
{
  "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

http
GET /api/v1/partners
Authorization: Bearer <accessToken>
Query Parameters:
  ?is_active=true
  &limit=50
  &cursor=<cursor_token>

Response (200 OK):

json
{
  "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

http
GET /api/v1/partners/:id/offers
Authorization: Bearer <accessToken>

Response (200 OK):

json
{
  "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

http
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):

json
{
  "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

http
GET /api/v1/users/me/redemptions
Authorization: Bearer <accessToken>
Query Parameters:
  ?status=active  # active, used, expired
  &limit=50
  &cursor=<cursor_token>

Response (200 OK):

json
{
  "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

http
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):

json
{
  "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

http
GET /api/v1/users/:id/ratings
Authorization: Bearer <accessToken>
Query Parameters:
  ?limit=20
  &cursor=<cursor_token>

Response (200 OK):

json
{
  "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

json
{
  "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 StatusError CodeDescription
400VALIDATION_ERRORRequest validation failed
401UNAUTHORIZEDMissing or invalid authentication
403FORBIDDENInsufficient permissions
404NOT_FOUNDResource not found
409CONFLICTResource already exists
422UNPROCESSABLE_ENTITYBusiness logic error
429RATE_LIMIT_EXCEEDEDToo many requests
500INTERNAL_SERVER_ERRORServer error
503SERVICE_UNAVAILABLEService temporarily unavailable

Common Error Examples

401 Unauthorized

json
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or expired access token",
    "timestamp": "2026-06-05T10:30:00Z"
  }
}

403 Forbidden

json
{
  "error": {
    "code": "FORBIDDEN",
    "message": "You do not have permission to perform this action",
    "timestamp": "2026-06-05T10:30:00Z"
  }
}

422 Business Logic Error

json
{
  "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

http
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 987
X-RateLimit-Reset: 1717502400

429 Response

json
{
  "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 string

Response Format

json
{
  "data": [...],
  "pagination": {
    "next_cursor": "eyJpZCI6InV1aWQifQ==",
    "prev_cursor": "eyJpZCI6InV1aWQifQ==",
    "has_more": true
  }
}

Example

http
# 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)

typescript
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:

http
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:

  1. OpenAPI YAML Spec generieren
  2. Swagger UI integrieren
  3. API-Tests schreiben (Postman Collection)
  4. SDK generieren (TypeScript Client)

Good Deeds - Nachbarschaftshilfe-App