> For the complete documentation index, see [llms.txt](https://docs.opendatadiscovery.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.opendatadiscovery.org/developer-guides/api-reference/data-collaboration.md).

# Data Collaboration

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](/configuration-and-deployment/odd-platform.md#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](/features/active-platform-features/data-collaboration.md). The Slack integration here is a **full Slack app** (OAuth-token-driven; the [Slack Events API](https://docs.slack.dev/apis/events-api/) webhook reads replies back into the platform) — **distinct** from the [outgoing alert webhook](/configuration-and-deployment/odd-platform.md#enable-alert-notifications) 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.

{% hint style="danger" %}
**`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.
{% endhint %}

{% hint style="warning" %}
**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.
{% endhint %}

**Outbound to the provider — read & post**

| Method | Path                                              | Operation ID         | Purpose                                                                                                                                                                                                                                                                                                                                                                                                                 |
| ------ | ------------------------------------------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `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**

| Method | Path                                                       | Operation ID            | Purpose                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| ------ | ---------------------------------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `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**

| Method | Path                | Purpose                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| ------ | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST` | `/api/slack/events` | Slack [Events API](https://docs.slack.dev/apis/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](/developer-guides/api-reference.md) — the full per-feature index.
* [Active platform features → Data Collaboration](/features/active-platform-features/data-collaboration.md) — feature description, Discussions tab, message-lifecycle model.
* [Configure ODD Platform → Enable Data Collaboration](/configuration-and-deployment/odd-platform.md#enable-data-collaboration) — `datacollaboration.*` configuration keys + Slack app manifest.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.opendatadiscovery.org/developer-guides/api-reference/data-collaboration.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
