Skip to content

Add ReactionService for message emoji reactions - #414

Draft
timkpaine wants to merge 1 commit into
finos:mainfrom
timkpaine:feat/message-reactions
Draft

timkpaine wants to merge 1 commit into
finos:mainfrom
timkpaine:feat/message-reactions

Conversation

@timkpaine

@timkpaine timkpaine commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

What this adds

Emoji reactions on messages, as bdk.reactions():

await bdk.reactions().react(message_id, "\N{THUMBS UP SIGN}")
await bdk.reactions().unreact(message_id)
reactions = await bdk.reactions().list_reactions(message_id)

Opened as a draft, because the endpoint it calls is not in the published API specification and maintainers may want a different shape, or may prefer to publish the endpoint first. Happy to rework or close it.

Why it is not generated

Reactions are a Symphony Messaging feature that users rely on, but neither agent-api-public.yaml nor pod-api-public.yaml in finos/symphony-api-spec mentions them, so no generated client covers them and a bot currently has no way to participate.

The capability exists server side. The Symphony web client reacts through POST /maestro/reactions/v1/message on the pod host, and that endpoint accepts a bot's sessionToken as-is. This service calls it directly, following the precedent of MultiAttachmentsMessagesApi, which is hand written for an endpoint the generated code does not reach.

I have also filed a request for a documented endpoint: finos/symphony-api-spec#232. If that lands, the right move is to point this service at the published route and drop the note below.

Wire format

Established by observing the web client and then confirming each behaviour against a pod with a bot session token:

POST /maestro/reactions/v1/message        sessionToken header, JSON body → 204
  react:   {"messageId": "<padded base64>", "emoji": "👍", "timestamp": <ms>}
  unreact: {"messageId": "<padded base64>", "timestamp": <ms>}

GET  /maestro/reactions/v1/message?initialMessageId=<url encoded>   → 200
  {"reactions": {"👍": [{"userId": 349026222366055, "ts": 1791324381966}]}, "sequenceNb": 1}

Three behaviours the endpoint enforces, each handled here:

  • Message ids need padded standard base64. Ids are handed out URL-safe and unpadded elsewhere in the API, and the read side matches only on the standard alphabet. to_padded_base64 converts them, and rejects anything that is not base64 rather than sending it.
  • The emoji must be the character. A shortname such as thumbsup, or an empty string, returns 400 REACTIONS_INVALID_EMOJI, so an empty one is refused before the request is made.
  • Clearing a reaction omits the emoji field. An empty string is rejected, and reacting again with the same emoji is idempotent rather than a toggle, so omitting the field is what clears it.

list_reactions takes the id of the original message, which is what reactions attach to for an edited message, and returns {} rather than raising when there are none.

Reactions can be disabled for a pod and restricted in external or federated rooms — the client gates on isReactionsReadEnabled, isReactionsWriteEnabled, and isReactionsWriteExternalEnabled. This service does not try to pre-empt that; a pod that forbids the operation surfaces its own error.

Conventions and testing

ReactionService is wired through service_factory alongside the other services and reachable as bdk.reactions(). Nothing existing is modified beyond those two wiring points.

Typing stays on Optional/Dict/List for the py39 target. ruff format --check . and ruff check . are clean repo-wide. Eight unit tests cover the id conversion, the empty-emoji refusal, that unreact omits the field, the list mapping, and an empty response.

Full suite: 567 passed. The two failures in tests/core/config/bdk_config_loader_test.py::test_load_from_symphony_directory reproduce on an unmodified checkout in my environment and are unrelated to this change.

Emoji reactions are a Symphony Messaging feature that users rely on, but
they are absent from the published Agent and Pod API specifications, so no
generated client covers them and bots have had no way to participate.

ReactionService calls the same pod endpoint the Symphony clients use,
POST and GET on /maestro/reactions/v1/message, and is reachable as
bdk.reactions(). It follows the precedent of
MultiAttachmentsMessagesApi, which is hand written for an endpoint the
generated code does not reach.

Three behaviours the endpoint enforces, each confirmed against a pod:

- Message ids must be padded standard base64. Ids are handed out URL-safe
  and unpadded elsewhere, so to_padded_base64 converts them and rejects
  anything that is not base64.
- The emoji must be the character itself. Shortnames and empty strings
  come back as REACTIONS_INVALID_EMOJI, so an empty one is refused before
  the request is made.
- Clearing a reaction omits the emoji field rather than sending an empty
  one, since an empty string is rejected and reacting twice with the same
  emoji is idempotent rather than a toggle.

Reactions can be disabled per pod, and restricted in external or
federated rooms, in which case these calls surface the pod's own error.

@thibauult thibauult left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution, @timkpaine, and for the thorough write-up of the wire format and the endpoint's behaviors. That's very helpful context.

Unfortunately, we can't accept this PR as it stands. /maestro/reactions/v1/message is an internal endpoint, not part of the public Symphony REST API (https://rest-api.symphony.com/). The BDKs only support the public API, because that's what we can maintain with backward-compatibility guarantees. Internal endpoints can change or disappear in any pod release without notice, and we'd have no way to protect BDK users from that.

We agree that reactions are a useful capability for bots. We'll raise it internally with the Symphony Messaging team to get these endpoints documented and exposed in the public API. We'll also follow up on finos/symphony-api-spec#232. We can't commit to a timeline.

Once the endpoints are published, we'd be glad to revisit this. At that point the service should be a thin wrapper over the client generated from the updated spec. The react/unreact/list_reactions interface and the id and emoji validation would carry over well.

In the meantime, I'd suggest keeping this as a draft, or closing it and reopening once the public endpoint exists. If you'd like to keep using it in the interim, it can live in your own fork or as a separate package, understanding it falls outside the BDK's compatibility guarantees.

Thanks again for the effort and for the detailed investigation.

@timkpaine timkpaine changed the title Add ReactionService for emoji reactions on messages Add ReactionService for message emoji reactions Oct 8, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants