API Documentation


General

Permission Levels

LevelDescription
PublicNo authentication required
Any authenticated userValid Bearer token required
AdminUser with admin flag set to true
SuperadminUser with superadmin flag set to true

Common Response Status Codes

StatusMeaning
200OK — successful GET, PATCH, PUT
201Created — successful POST
204No Content — successful DELETE
401Unauthorized — missing or invalid token
403Forbidden — insufficient permissions
404Not Found — resource does not exist
422Unprocessable Entity — validation errors

Authentication

All API endpoints require authentication via a Bearer token (JWT) issued by PiratenSSO (Keycloak), unless marked as public.

Obtaining a token

Request an access token from the PiratenSSO token endpoint using the OAuth 2.0 client credentials flow:

POST https://sso.piratenpartei.de/realms/Piratenlogin/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET

The response contains an access_token:

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "expires_in": 300,
  "token_type": "Bearer"
}

Using the token

Include the token in the Authorization header of every request:

Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

Authentication errors

StatusBodyCause
401{"error": "Authorization header required"}No Authorization header sent
401{"error": "Invalid token: ..."}Token is expired, malformed, or signed by an untrusted issuer

Example: authenticated request

curl -H "Authorization: Bearer eyJhbG..." \
     -H "Accept: application/json" \
     https://meine-piraten.de/tasks.json

Tasks

A task represents an actionable item assigned to an organizational entity and category.

Endpoints

MethodPathDescriptionPermission
GET/tasks.jsonList all tasksAny authenticated user
GET/tasks/:id.jsonGet a single taskAny authenticated user
POST/tasks.jsonCreate a taskAdmin
PATCH/PUT/tasks/:id.jsonUpdate a taskAny authenticated user (see field restrictions below)
DELETE/tasks/:id.jsonDelete a taskAdmin

Request parameters (create / update)

Wrap parameters in a task key:

{
  "task": {
    "title":               "string (required for create, max 200 characters)",
    "description":         "string (optional, max 2000 characters)",
    "completed":           "boolean",
    "creator_name":        "string",
    "time_needed_in_hours": "integer (optional)",
    "activity_points":     "integer (optional)",
    "due_date":            "date (YYYY-MM-DD, optional)",
    "urgent":              "boolean (optional)",
    "category_id":         "integer (required for create, foreign key)",
    "entity_id":           "integer (required for create, foreign key)",
    "status":              "string (optional, see status transitions below)",
    "assignee":            "string (optional, PiratenSSO username)"
  }
}

Field update restrictions for non-admin users

Non-admin users may only update status and assignee. All other fields require admin privileges.

Status transitions

Task status follows a strict state machine. Invalid transitions return 422 Unprocessable Entity.

FromAllowed transitionsWho can transition
openclaimedAny authenticated user
claimedcompleted, openAny authenticated user
completeddone, claimedAdmin only
done(none — terminal state)

Response example (single task)

{
  "id": 1,
  "title": "Wahlkampfmaterial bestellen",
  "description": "Flyer und Plakate für den Wahlkampf bestellen",
  "completed": false,
  "creator_name": "pirat42",
  "time_needed_in_hours": 2,
  "due_date": "2025-06-01",
  "urgent": true,
  "activity_points": 10,
  "category_id": 1,
  "entity_id": 1,
  "status": "open",
  "assignee": null,
  "created_at": "2025-05-04T12:00:00.000Z",
  "updated_at": "2025-05-04T12:00:00.000Z",
  "url": "https://meine-piraten.de/tasks/1.json"
}

Comments

Comments are nested under tasks. Each comment belongs to a task and is ordered by creation date (ascending).

Endpoints

MethodPathDescriptionPermission
GET/tasks/:task_id/comments.jsonList comments for a taskAny authenticated user
POST/tasks/:task_id/comments.jsonCreate a commentAny authenticated user
DELETE/tasks/:task_id/comments/:id.jsonDelete a commentAdmin

Request parameters (create)

Wrap parameters in a comment key:

{
  "comment": {
    "author_name": "string",
    "text":        "string (required)"
  }
}

Response example (single comment)

{
  "id": 1,
  "task_id": 1,
  "author_name": "pirat42",
  "text": "Flyer sind bestellt.",
  "created_at": "2025-05-04T12:30:00.000Z",
  "updated_at": "2025-05-04T12:30:00.000Z",
  "url": "https://meine-piraten.de/tasks/1/comments/1.json"
}

Messages

Private messages between users. All endpoints require JWT authentication.

Endpoints

MethodPathDescriptionPermission
GET/api/messagesList received messages (inbox)Any authenticated user
POST/api/messagesSend a messageAny authenticated user
PATCH/api/messages/:idMark a message as readAny authenticated user (own messages only)

GET /api/messages

Returns the 50 most recent messages received by the authenticated user, ordered by newest first.

[
  {
    "id": 1,
    "sender_id": 42,
    "body": "Kannst du die Flyer bis Freitag fertig haben?",
    "read": false,
    "created_at": "2026-03-19T10:30:00Z"
  }
]

POST /api/messages

Send a message to another user.

{
  "recipient_id": 42,
  "body": "Ja, kein Problem!"
}

Success response (201 Created):

{ "id": 2 }

Error (422): returned if body is blank or recipient is invalid.

PATCH /api/messages/:id

Marks a received message as read. Only the recipient can mark their own messages. Returns 200 OK with {}. Returns 404 if the message does not belong to the authenticated user.


Entities

Entities represent organizational units (Landes-, Bezirks-, or Kreisverbände). Entities can be self-referentially nested (e.g. a KV belonging to an LV).

Endpoints

MethodPathDescriptionPermission
GET/entities.jsonList all entitiesAny authenticated user
GET/entities/:id.jsonGet a single entityAny authenticated user
POST/entities.jsonCreate an entityAdmin
PATCH/PUT/entities/:id.jsonUpdate an entityAdmin
DELETE/entities/:id.jsonDelete an entityAdmin

Request parameters (create / update)

Wrap parameters in an entity key:

{
  "entity": {
    "name":         "string (required)",
    "entity_level": "string (one of: LV, BZV, KV)",
    "parent_entity_id": "integer (optional, parent entity foreign key)"
  }
}

Entity levels

ValueMeaning
LVLandesverband
BZVBezirksverband
KVKreisverband

Response example (single entity)

{
  "id": 1,
  "name": "Piratenpartei Bayern",
  "entity_level": "LV",
  "parent_entity_id": null,
  "created_at": "2025-01-01T00:00:00.000Z",
  "updated_at": "2025-01-01T00:00:00.000Z",
  "parent_entity_name": null,
  "url": "https://meine-piraten.de/entities/1.json"
}

Categories

Categories are used to classify tasks.

Endpoints

MethodPathDescriptionPermission
GET/categories.jsonList all categoriesAny authenticated user
GET/categories/:id.jsonGet a single categoryAny authenticated user
POST/categories.jsonCreate a categoryAdmin
PATCH/PUT/categories/:id.jsonUpdate a categoryAdmin
DELETE/categories/:id.jsonDelete a categoryAdmin

Request parameters (create / update)

Wrap parameters in a category key:

{
  "category": {
    "name": "string (required)"
  }
}

Response example (single category)

{
  "id": 1,
  "name": "Öffentlichkeitsarbeit",
  "created_at": "2025-01-01T00:00:00.000Z",
  "updated_at": "2025-01-01T00:00:00.000Z",
  "url": "https://meine-piraten.de/categories/1.json"
}

News

News posts are sourced from Telegram channel updates. This endpoint is public and does not require authentication.

Endpoints

MethodPathDescriptionPermission
GET/api/news.jsonList recent news postsPublic (no authentication required)

Query parameters

ParameterTypeDefaultDescription
limitinteger50Number of posts to return (max 200)

Example request

curl -H "Accept: application/json" \
     https://meine-piraten.de/api/news.json?limit=10

Response example (list)

[
  {
    "chat_id": -1001234567890,
    "message_id": 42,
    "posted_at": "2026-03-01T14:30:00Z",
    "text": "Neuer Blogpost: Unsere Positionen zur Digitalpolitik..."
  },
  {
    "chat_id": -1001234567890,
    "message_id": 41,
    "posted_at": "2026-02-28T10:00:00Z",
    "text": "Einladung zum nächsten Stammtisch..."
  }
]

Posts are ordered by posted_at descending (newest first) and limited to posts from the last 30 days.


Push Subscriptions

Register or deregister APNs device tokens for push notifications. All endpoints require JWT authentication.

Endpoints

MethodPathDescriptionPermission
POST/api/push_subscriptionsRegister or update a device tokenAny authenticated user
DELETE/api/push_subscriptions/:tokenDeregister a device tokenAny authenticated user

POST /api/push_subscriptions

Register a device token or update notification preferences for an existing token (upsert).

{
  "token":    "a1b2c3d4e5f6...",
  "platform": "ios",
  "messages": true,
  "todos":    false,
  "forum":    true,
  "news":     false
}

Success response: 200 OK with {}

Error (422): returned if validation fails.

DELETE /api/push_subscriptions/:token

Remove a device token registration. Idempotent — returns 200 OK with {} even if the token is unknown.

Notification categories

CategoryParameterTrigger
MessagesmessagesNew private message received
TodostodosTask assigned to you or task status changed
ForumforumNew forum post (if available)
NewsnewsNew Telegram news item published

Admin Requests

Any authenticated user can request admin privileges. Requests are reviewed by superadmins. Approving a request automatically grants the user admin rights.

Endpoints

MethodPathDescriptionPermission
GET/admin_requests/status.jsonCheck your own admin statusAny authenticated user
POST/admin_requests.jsonSubmit an admin requestAny authenticated user
PATCH/admin_requests/:id/approve.jsonApprove a requestSuperadmin
PATCH/admin_requests/:id/reject.jsonReject a requestSuperadmin
PATCH/admin_requests/:id/demote.jsonRevoke admin rights from a userSuperadmin

Check admin status

GET /admin_requests/status.json
Authorization: Bearer eyJhbG...

Returns:

{ "admin": true }

Submit a request

Send the reason as a top-level parameter:

POST /admin_requests.json
Authorization: Bearer eyJhbG...
Content-Type: application/json

{
  "reason": "Ich möchte Aufgaben für meinen KV verwalten."
}

Success response (201 Created)

{
  "id": 1,
  "status": "pending",
  "reason": "Ich möchte Aufgaben für meinen KV verwalten.",
  "created_at": "2026-02-17T10:00:00.000Z"
}

Validation error (422 Unprocessable Entity)

{
  "errors": ["Reason can't be blank"]
}

Approve / Reject / Demote (superadmin only)

PATCH /admin_requests/1/approve.json
Authorization: Bearer eyJhbG...

Grants admin rights to the request's user. Returns:

{ "status": "approved" }
PATCH /admin_requests/1/reject.json
Authorization: Bearer eyJhbG...

Rejects the request without changing user permissions. Returns:

{ "status": "rejected" }
PATCH /admin_requests/1/demote.json
Authorization: Bearer eyJhbG...

Revokes admin rights from the request's user and marks the request as rejected. Returns:

{ "status": "rejected" }

Non-superadmin users receive 403 Forbidden for approve / reject / demote endpoints.