morgenruf.dev

API Reference

Complete reference for every HTTP endpoint exposed by Morgenruf. All API paths are relative to your APP_URL (e.g. https://app.morgenruf.dev).

Authentication

Morgenruf uses two authentication mechanisms depending on the context:

Session Authentication (Dashboard API)

The /dashboard/api/* endpoints are protected by a server-side session. Sessions are established via the OAuth install flow or by visiting /dashboard/login. The session cookie (mrn_session) is set as HttpOnly; SameSite=Lax; Secure and must be present on every request.

To authenticate programmatically, obtain a short-lived login token from the OAuth callback and exchange it at login:

GET /dashboard/login?t=<login_token>
http

The login token is a base64-encoded HMAC-signed payload (team_id + user_id + timestamp) valid for 5 minutes. It is issued automatically at the end of the OAuth flow.

Bearer Token Authentication (MCP API)

The /mcp endpoint uses Bearer token auth. Generate an API key in Dashboard β†’ Integrations β†’ MCP and pass it in the Authorization header:

curl -X POST https://app.morgenruf.dev/mcp \
  -H "Authorization: Bearer mrn_a1b2c3d4..." \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"tools/list","params":{},"id":1}'
bash

API keys are stored as SHA-256 hashes. Only the mrn_-prefixed key shown at creation time can be used for auth β€” it cannot be retrieved again.


System Endpoints

GET /

Returns basic application info. No authentication required.

curl https://app.morgenruf.dev/
bash
{
  "name": "morgenruf",
  "version": "1.0.0",
  "status": "ok"
}
json response

GET /healthz

Health check endpoint used by load balancers and Kubernetes liveness/readiness probes. Returns 200 OK when the app is healthy.

curl https://app.morgenruf.dev/healthz
bash
{"status": "ok"}
json response

OAuth Endpoints

GET /install

Redirects the browser to the Slack OAuth authorisation page. Generates an HMAC-signed state parameter automatically. No authentication required.

GET /install
β†’ 302 Redirect to https://slack.com/oauth/v2/authorize?...
http

GET /oauth/callback

Handles the OAuth redirect from Slack. Exchanges the authorization code for a bot token, persists the installation, and redirects to the dashboard. No authentication required.

Query paramTypeDescription
codestringAuthorization code from Slack
statestringHMAC-signed state token from /install
errorstringPresent if the user denied the install

On success: 302 redirect to /dashboard?t=<login_token>.
On error: 400 with an HTML error page.


Slack Endpoints

POST /slack/events

Receives all Slack event subscriptions (messages, app mentions, app_uninstalled, etc.) and slash command payloads. Requests are verified with the SLACK_SIGNING_SECRET. This endpoint is called by Slack β€” you do not call it directly.

POST /slack/interactions

Receives Slack Block Kit interactive component payloads (button clicks, modal submissions, etc.). Also verified with SLACK_SIGNING_SECRET and called by Slack.


Dashboard API

All /dashboard/api/* endpoints require session authentication. They return JSON. On auth failure the response is 401 {"error": "Unauthorized"}; on missing admin role it is 403 {"error": "Admin required"}.

GET /dashboard/api/me

Returns the currently authenticated user's profile.

{
  "user_id": "U012AB3CD",
  "team_id": "T012AB3CD",
  "team_name": "Acme Corp",
  "role": "admin"
}
json response

GET /dashboard/api/standups

Lists all standup schedules configured for the workspace.

[
  {
    "id": "T012AB3CD",
    "channel_id": "C012AB3CD",
    "schedule_time": "09:00",
    "schedule_tz": "America/New_York",
    "schedule_days": "mon,tue,wed,thu,fri",
    "questions": [
      "What did you complete yesterday?",
      "What are you working on today?",
      "Any blockers?"
    ],
    "active": true
  }
]
json response

POST /dashboard/api/standups

Creates a new standup schedule. Admin required.

{
  "channel_id": "C012AB3CD",
  "schedule_time": "09:30",
  "schedule_tz": "Europe/London",
  "schedule_days": "mon,tue,wed,thu,fri",
  "questions": ["What are you shipping today?", "Any blockers?"],
  "active": true
}
json request body

Returns 201 with the created standup object.

PUT /dashboard/api/standups/<standup_id>

Updates a standup schedule. Admin required. Accepts the same fields as POST; omitted fields are left unchanged.

DELETE /dashboard/api/standups/<standup_id>

Deletes a standup schedule. Admin required. Returns {"ok": true}.

GET /dashboard/api/members

Lists workspace members. Admin required.

[
  {
    "user_id": "U012AB3CD",
    "real_name": "Alice Smith",
    "email": "alice@example.com",
    "tz": "America/Chicago",
    "active": true,
    "role": "admin"
  }
]
json response

POST /dashboard/api/members/invite

Adds a Slack user to the workspace member list. Admin required.

{ "user_id": "U012AB3CD" }
json request body

Returns 201 with the new member object. Returns 400 if user_id is missing, or 500 if the Slack API cannot be reached.

PUT /dashboard/api/members/<user_id>/role

Updates a member's role. Admin required.

{ "role": "admin" }
json request body

Valid values for role: "admin", "member". Returns {"ok": true, "user_id": "...", "role": "..."}.

GET /dashboard/api/channels

Returns a list of public Slack channels the bot has access to. Used to populate the channel selector in the dashboard.

[
  { "id": "C012AB3CD", "name": "engineering" },
  { "id": "C012AB3CE", "name": "design" }
]
json response

GET /dashboard/api/stats

Returns high-level participation statistics for the workspace.

{
  "total_members": 12,
  "submitted_today": 9,
  "participation_7d_pct": 84,
  "active_blockers": 2
}
json response

GET /dashboard/api/reports

Returns standup responses and participation data for a date range.

Query paramDefaultDescription
days7Number of days to include
fromβ€”Start date YYYY-MM-DD
toβ€”End date YYYY-MM-DD
{
  "standups": [
    {
      "user_id": "U012AB3CD",
      "standup_date": "2026-04-01",
      "yesterday": "Shipped the login page.",
      "today": "Working on password reset.",
      "blockers": "None",
      "mood": "😊"
    }
  ],
  "participation": [
    { "date": "2026-04-01", "submitted": 9, "total": 12, "pct": 75 }
  ],
  "total_days": 7
}
json response

GET /dashboard/api/analytics

Returns extended analytics including mood trends and per-member participation breakdown. Accepts the same days query param as /reports.

GET /dashboard/api/export/csv

Downloads standup data as a CSV file. Accepts optional from and to query params (YYYY-MM-DD). Returns Content-Type: text/csv with a Content-Disposition: attachment header.

Columns: standup_date, user_id, real_name, yesterday, today, blockers, mood, submitted_at.

GET /dashboard/api/webhooks

Lists registered webhook endpoints. Admin required.

[
  {
    "id": 1,
    "url": "https://example.com/hooks/standup",
    "created_at": "2026-01-15T10:23:00Z"
  }
]
json response

POST /dashboard/api/webhooks

Registers a new webhook endpoint. Admin required.

{ "url": "https://example.com/hooks/standup" }
json request body

Returns 201 with the created hook object. The URL must be an https:// address; private/internal IP ranges are rejected with 400.

DELETE /dashboard/api/webhooks/<hook_id>

Removes a webhook endpoint. Admin required. Returns {"ok": true}.

GET /dashboard/api/kudos

Returns recent kudos given in the workspace.

[
  {
    "from_user": "U012AB3CD",
    "to_user": "U012AB3CE",
    "message": "Great work on the release!",
    "created_at": "2026-04-01T14:00:00Z"
  }
]
json response

GET /dashboard/api/kudos/leaderboard

Returns a ranked list of members by kudos received. Accepts a days query param (default: 30).

[
  { "user_id": "U012AB3CE", "real_name": "Bob Jones", "kudos_received": 7 },
  { "user_id": "U012AB3CD", "real_name": "Alice Smith", "kudos_received": 4 }
]
json response

GET /dashboard/api/templates

Returns the 25 built-in question template sets that can be applied to a standup schedule.

[
  {
    "name": "Classic Standup",
    "questions": [
      "What did you complete yesterday?",
      "What are you working on today?",
      "Any blockers?"
    ]
  }
]
json response

GET /dashboard/api/schedules

Lists all standup schedules (same as /dashboard/api/standups, used by multi-schedule UI).

POST /dashboard/api/schedules

Creates a new standup schedule. Admin required. Accepts same body as POST /dashboard/api/standups.

PUT /dashboard/api/schedules/<schedule_id>

Updates a schedule. Admin required.

DELETE /dashboard/api/schedules/<schedule_id>

Deletes a schedule. Admin required. Returns {"ok": true}.

GET /dashboard/api/rules

Lists workflow automation rules for the workspace. Admin required.

[
  {
    "id": 1,
    "trigger": "blocker_reported",
    "action": "post_to_channel",
    "action_target": "C012AB3CD",
    "active": true
  }
]
json response

POST /dashboard/api/rules

Creates a workflow automation rule. Admin required.

{
  "trigger": "participation_below",
  "trigger_threshold": 50,
  "action": "dm_admin",
  "action_target": "U012AB3CD",
  "active": true
}
json request body

DELETE /dashboard/api/rules/<rule_id>

Deletes a workflow rule. Admin required. Returns {"ok": true}.

POST /dashboard/api/feed-token

Generates (or regenerates) the public standup feed token for the workspace. Admin required. Returns {"token": "abc123..."}.

DELETE /dashboard/api/feed-token

Revokes the public standup feed token. Admin required. Returns {"ok": true}.

GET /feed/<token>

Public read-only standup feed page. No authentication required β€” access is controlled by the token. Returns an HTML page with the most recent standup responses.

GET /dashboard/api/profile

Returns the signed-in member's own profile. Any signed-in member. The user comes from the session only.

{
  "user_id": "U012AB3CD",
  "birth_month": 3,
  "birth_day": 14,
  "start_date": "2023-03-01",
  "role": "Backend engineer",
  "location": "Berlin",
  "ask_me_about": "Rust, bouldering",
  "celebrate": true,
  "set_by_admin": false,
  "updated_at": "2026-09-27T10:00:00Z"
}
json response

PUT /dashboard/api/profile

Updates the signed-in member's own profile. Any signed-in member. Only the fields sent change. birth_month and birth_day go together; there is no year field, and an unknown key such as birth_year is dropped. role and location take up to 80 characters, ask_me_about up to 200.

GET /dashboard/api/profiles

Lists every current member's profile. Workspace admin required.

PUT /dashboard/api/profiles/<user_id>

Updates one member's profile. Workspace admin required. Same fields as PUT /dashboard/api/profile.

POST /dashboard/api/profiles/import

Imports birthdays and start dates from email,birthday,start_date CSV text. Workspace admin required. preview defaults to true and writes nothing; send false to save. overwrite replaces profiles members wrote themselves. See CSV import.

{ "csv": "email,birthday,start_date\npriya@example.com,03-14,2023-03-01", "preview": true, "overwrite": false }
json request body

The response counts rows as ready, unchanged, kept, unmatched and invalid, with written and a per-row list.

Celebrations endpoints

Changes need a workspace admin or a Celebrations admin. See Celebrations.

EndpointWhoWhat it does
GET /dashboard/api/celebrations/settingsany memberChannel, timezone, post time, birthdays, anniversaries, working days, and whether the πŸŽ‰ can be added (can_react).
PUT /dashboard/api/celebrations/settingsadminsSaves the settings. channel_id, timezone and working_days (for example ["mon","tue","wed","thu","fri"]) are required.
GET /dashboard/api/celebrations/holidaysany memberThe holiday list.
POST /dashboard/api/celebrations/holidaysadminsAdds {"date", "name"}, or renames the holiday on that date.
DELETE /dashboard/api/celebrations/holidays/<date>adminsRemoves the holiday on a YYYY-MM-DD date.
POST /dashboard/api/celebrations/holidays/importadminsImports date,name CSV text. preview defaults to true.
GET /dashboard/api/celebrations/upcomingadminsEvery celebration in the next 30 days and the day it is posted.
GET /dashboard/api/celebrations/ask-datesadminsHow many people Ask for dates would DM, and the message. Sends nothing.
POST /dashboard/api/celebrations/ask-datesadminsSends the DM to people still missing dates, at most once per person per 30 days. Returns 409 until a channel and a timezone are saved.

MCP API

The MCP (Model Context Protocol) endpoint exposes standup data to AI assistants (Claude, Cursor, GitHub Copilot). It uses JSON-RPC 2.0 over HTTP and requires a Bearer API key. See the MCP integration guide for full setup instructions.

GET /mcp

Public info endpoint. No authentication required. Returns connection metadata and the list of available tool names.

{
  "name": "Morgenruf MCP Server",
  "version": "1.0.0",
  "transport": "http",
  "endpoint": "https://app.morgenruf.dev/mcp",
  "auth": "Bearer token β€” generate from your Morgenruf dashboard",
  "docs": "https://docs.morgenruf.dev/mcp.html",
  "tools": [
    "get_standups",
    "get_today_standups",
    "get_blockers",
    "get_participation",
    "get_members",
    "search_standups",
    "get_workspace_summary",
    "get_mood_summary"
  ]
}
json response

POST /mcp

MCP JSON-RPC 2.0 endpoint. Requires Bearer authentication.

Supported methods:

MethodDescription
initializeMCP handshake β€” returns protocol version and server capabilities
tools/listReturns all available tools with their input schemas
tools/callExecutes a tool with the given arguments
pingLiveness check β€” returns empty result

Available tools via tools/call:

Tool nameDescriptionKey params
get_standupsFetch standup responses for a date rangefrom_date, to_date, user_id
get_today_standupsGet all submissions from todayβ€”
get_blockersGet active blockers reported by the teamdays
get_participationParticipation statisticsdays
get_membersList all workspace membersβ€”
search_standupsFull-text search across standup responsesquery, days
get_workspace_summaryHigh-level workspace health snapshotβ€”
get_mood_summaryTeam mood trends over timedays

Example tools/call request:

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "get_blockers",
    "arguments": { "days": 7 }
  },
  "id": 1
}
json request body
{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "[{\"user_id\": \"U012AB3CD\", \"date\": \"2026-04-01\", \"blockers\": \"Waiting on PR review\"}]"
      }
    ]
  },
  "id": 1
}
json response

GET /dashboard/api/mcp/keys

Lists MCP API keys for the workspace (prefixes only β€” full keys are never returned after creation). Admin required.

[
  {
    "id": 1,
    "name": "Claude Desktop",
    "key_prefix": "mrn_a1b2",
    "created_at": "2026-03-10T09:00:00Z",
    "last_used_at": "2026-04-01T08:45:00Z",
    "active": true
  }
]
json response

POST /dashboard/api/mcp/keys

Generates a new MCP API key. Admin required. Optionally accepts {"name": "My Key"} in the request body.

{
  "id": 2,
  "name": "Cursor",
  "key": "mrn_a1b2c3d4e5f6g7h8",
  "key_prefix": "mrn_a1b2",
  "created_at": "2026-04-05T12:00:00Z"
}
json response

The full key is only returned once at creation time. Store it securely.

DELETE /dashboard/api/mcp/keys/<key_id>

Revokes an MCP API key. Admin required. Returns {"ok": true}.

GET /dashboard/api/mcp-config

Returns ready-to-use MCP configuration snippets for Claude Desktop and Cursor.

{
  "claude_desktop": {
    "mcpServers": {
      "morgenruf": {
        "command": "npx",
        "args": ["mcp-remote", "https://app.morgenruf.dev/mcp"],
        "env": { "MORGENRUF_API_KEY": "mrn_..." }
      }
    }
  }
}
json response

Error Codes

All REST endpoints use standard HTTP status codes:

StatusMeaning
200Success
201Resource created
302Redirect (OAuth flows)
400Bad request β€” missing or invalid parameter
401Unauthorized β€” no session or invalid Bearer token
403Forbidden β€” authenticated but not an admin
500Internal server error β€” check application logs
503Service unavailable β€” database unreachable

Error responses from the Dashboard API always include a JSON body:

{ "error": "Human-readable description of the problem" }
json

MCP JSON-RPC errors follow the JSON-RPC 2.0 error object format:

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32001,
    "message": "Unauthorized β€” provide a valid Bearer API key from your Morgenruf dashboard"
  },
  "id": null
}
json

Common MCP error codes:

CodeMeaning
-32001Unauthorized β€” invalid or missing Bearer token
-32601Method not found
-32603Internal error β€” tool execution failed