Data Collaboration
Data Collaboration HTTP endpoints — outbound to the provider, per-entity threads & history, and the inbound Slack Events webhook. Gated by `@ConditionalOnDataCollaboration`.
The Data Collaboration HTTP surface is split across the data-collaboration controllers (DataCollaborationController, EventApiController) and a single Slack-events webhook. Every route below is gated by @ConditionalOnDataCollaboration (the Slack webhook controller is also conditional) — they return 404 Not Found when datacollaboration.enabled=false. For the configuration keys (datacollaboration.*), the Slack app manifest, and the operator setup flow, see Configure ODD Platform → Enable Data Collaboration; for the user-facing description (the per-entity Discussions tab, how a discussion flows, the message-lifecycle model), see Active platform features → Data Collaboration. The Slack integration here is a full Slack app (OAuth-token-driven; the Slack Events API webhook reads replies back into the platform) — distinct from the outgoing alert webhook used by alert notifications (notifications.receivers.slack.url, one-way write only).
Authentication and authorization
Read these caveats before exposing any Data Collaboration route, especially on a public-facing or auth.type=DISABLED deployment.
POST /api/slack/events is unauthenticated and unsigned — any network caller can forge an event the platform treats as a genuine Slack reply. The path is on the platform's authentication allow-list (SecurityConstants.WHITELIST_PATHS → permitAll() in AuthorizationCustomizer), so it is reachable without a session under every auth.type. The handler reads the request body as a raw Mono<String> and dispatches it to the Slack event parser without reading the X-Slack-Signature / X-Slack-Request-Timestamp headers — a code-side search for X-Slack-Signature, signingSecret, verifySignature, and HmacSHA256 returns zero matches, so Slack's mandated HMAC-SHA256 request-signature check is not performed. A forged event_callback materialises arbitrary messages in the per-entity Discussions tab and runs the same downstream processing as a real delivery. Put a reverse proxy that performs Slack HMAC-SHA256 verification in front of /api/slack/events until the platform-side check lands.
None of the Data Collaboration routes carry an RBAC permission gate — datacollaboration.enabled is the only access control. The platform's security-rule registry has no entry for /api/datacollaboration/..., /api/dataentities/{id}/messages, or /api/messages/..., so every route except the allow-listed webhook falls through to the global "any authenticated user" rule — no TERM_*-style permission applies. Under auth.type=DISABLED the global rule is permitAll(), so enabling Data Collaboration on a DISABLED-auth deployment publishes the entire read-and-post surface to anonymous callers, not just the webhook. Do not enable Data Collaboration on an internet-reachable deployment without a real authentication mode (LOGIN_FORM, OAUTH2, or LDAP) plus the perimeter signature check above.
Outbound to the provider — read & post
GET
/api/datacollaboration/providers/slack/channels
getSlackChannels
List Slack channels the bot can write to, optionally filtered by channel_name. Used by the in-app channel autocomplete. The channel_name filter is a prefix match (startsWith), not a substring match — typing the middle of a channel name returns nothing. Results come from a Caffeine cache with a 60-second write expiry, so a bot freshly added to a channel will not appear until the cache refreshes.
POST
/api/datacollaboration/providers/slack/messages
postMessageInSlack
Queue a message for delivery into Slack. Returns 202 Accepted once the message is enqueued; a background sender (DataCollaborationMessageSenderJob) drains the queue with up to datacollaboration.sending-messages-retry-count retries per message.
Per-entity threads & history
GET
/api/dataentities/{data_entity_id}/messages
getDataEntityMessages
List thread-root messages attached to a data entity, paged by size and (optionally) filtered by channel_id.
GET
/api/dataentities/{data_entity_id}/messages/{message_id}
getMessages
List replies under a specific parent message, paged by size from last_message_id.
GET
/api/dataentities/{data_entity_id}/channels
getChannels
Top channels that already carry messages about the given entity (used to seed the channel picker).
GET
/api/messages/{message_id}/url
redirect
302 Found redirect to the provider's deep-link for the message — runtime returns HttpStatus.FOUND from DataCollaborationController.redirect(...) via DataCollaborationService.resolveMessageUrl(...). Spec / runtime drift: the OpenAPI spec declares 301 Moved Permanently for this route; the platform actually serves 302 Found. Operators relying on cache-cacheability semantics should treat the response as 302 for now (clients that follow redirects unconditionally are unaffected).
Inbound webhook from Slack
POST
/api/slack/events
Slack Events API webhook. Handles three event types in SlackEventParser: url_verification (Slack handshake — returns the challenge token), event_callback (a thread reply — enqueued via DataCollaborationService.enqueueMessageEvent(...) and translated to a platform message by DataCollaborationMessageEventProcessor), and filter / error events (logged, acknowledged with 200 / 400). The path is fixed in EventApiController.java:22, so the manifest line https://<ODD_PLATFORM_BASE_URL>/api/slack/events matches the platform's route exactly.
See also
API Reference hub — the full per-feature index.
Active platform features → Data Collaboration — feature description, Discussions tab, message-lifecycle model.
Configure ODD Platform → Enable Data Collaboration —
datacollaboration.*configuration keys + Slack app manifest.
Last updated