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 param | Type | Description |
|---|---|---|
code | string | Authorization code from Slack |
state | string | HMAC-signed state token from /install |
error | string | Present 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 param | Default | Description |
|---|---|---|
days | 7 | Number 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.
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:
| Method | Description |
|---|---|
initialize | MCP handshake β returns protocol version and server capabilities |
tools/list | Returns all available tools with their input schemas |
tools/call | Executes a tool with the given arguments |
ping | Liveness check β returns empty result |
Available tools via tools/call:
| Tool name | Description | Key params |
|---|---|---|
get_standups | Fetch standup responses for a date range | from_date, to_date, user_id |
get_today_standups | Get all submissions from today | β |
get_blockers | Get active blockers reported by the team | days |
get_participation | Participation statistics | days |
get_members | List all workspace members | β |
search_standups | Full-text search across standup responses | query, days |
get_workspace_summary | High-level workspace health snapshot | β |
get_mood_summary | Team mood trends over time | days |
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:
| Status | Meaning |
|---|---|
200 | Success |
201 | Resource created |
302 | Redirect (OAuth flows) |
400 | Bad request β missing or invalid parameter |
401 | Unauthorized β no session or invalid Bearer token |
403 | Forbidden β authenticated but not an admin |
500 | Internal server error β check application logs |
503 | Service 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:
| Code | Meaning |
|---|---|
-32001 | Unauthorized β invalid or missing Bearer token |
-32601 | Method not found |
-32603 | Internal error β tool execution failed |