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.


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