# Scalar API (Element) Scalar and Element communicate using cross-origin messages in a defined format (described in this document). The full source for the messaging layer in Element can be seen [here](https://github.com/matrix-org/matrix-react-sdk/blob/develop/src/ScalarMessaging.js). With this API, the integrations manager is able to invite users, get some basic state information, and interact with the room in a limited capacity. The API is intentionally restricted to ensure that misbehaving domains don't have full control over Element. ## Setting up communications Element will automatically open a channel for receiving messages. The integrations manager needs to do the same so it can speak to Element. Here's some sample JavaScript that will do this for us: ``` window.addEventListener("message", function(event) { if (!event.data || !event.data.response) return; // invalid if (event.data.error) throw new Error(event.data.error); // TODO: Better error handling // TODO: Process response }); function sendMessage(action, roomId, userId, otherFields) { if (!otherFields) otherFields = {}; var request = otherFields; request["user_id"] = userId; request["room_id"] = roomId; request["action"] = action; window.opener.postMessage(request, "*"); // "*" posts to all origins } ``` ## Response format For all requests, the request is returned with the response. As an example, if we made a request like this: ``` { "action": "invite", "room_id": "!curbf:matrix.org", "user_id": "@voyager:t2bot.io" } ``` then we can expect a response object that looks like this: ``` { "action": "invite", "room_id": "!curbf:matrix.org", "user_id": "@voyager:t2bot.io", "response": { "success": true } } ``` ### Errors An error response will always have the following structure under `response`: ``` { "error": { "message": "Something went wrong", "_error": } } ``` ## Actions The examples in this section assume the helper function in "Setting up communication" is present. ### Inviting users **Action**: `"invite"` **Required params**: * `room_id` - where to invite the user to * `user_id` - the user to invite **Sample call**: ``` sendMessage("invite", "!curbf:matrix.org", "@voyager:t2bot.io"); ``` **Success Response**: ``` { "action": "invite", "room_id": "!curbf:matrix.org", "user_id": "@voyager:t2bot.io", "response": { "success": true } } ``` ### Get membership state for a user **Action**: `"membership_state"` **Required params**: * `room_id` - where to try and find the user * `user_id` - the user to lookup **Sample call**: ``` sendMessage("membership_state", "!curbf:matrix.org", "@voyager:t2bot.io"); ``` **Success Response**: ``` { "action": "membership_state", "room_id": "!curbf:matrix.org", "user_id": "@voyager:t2bot.io", "response": { "membership": "join", "avatar_url": "mxc://t2bot.io/hcSELkhLCNMRxLLTXKffPPSn", "displayname": "Matrix Traveler" } } ``` *Note*: The `response` will be the content of the `m.room.member` state event. An error may be returned if the user is not in the room. ### Get defined options for a bot **Action**: `"bot_options"` **Required params**: * `room_id` - where to try and find the options * `user_id` - the bot (user) to get options for **Sample call**: ``` sendMessage("bot_options", "!curbf:matrix.org", "@voyager:t2bot.io"); ``` **Success Response**: ``` { "action": "bot_options", "room_id": "!curbf:matrix.org", "user_id": "@voyager:t2bot.io", "response": { "botOption": "hello world" } } ``` *Note*: The `response` will be the content of the `m.room.bot.options` state event, which is defined using `set_bot_options`. ### Setting options for a bot **Action**: `"set_bot_options"` **Required params**: * `room_id` - where to try and set the options * `user_id` - the bot (user) to set options for * `content` - the options to specify (extra data) **Sample call**: ``` sendMessage("set_bot_options", "!curbf:matrix.org", "@voyager:t2bot.io", {content: {botOption: 'hello world'}}); ``` **Success Response**: ``` { "action": "set_bot_options", "room_id": "!curbf:matrix.org", "user_id": "@voyager:t2bot.io", "content": { "botOption": "hello world" }, "response": { "success": true } } ``` *Note*: The options will be set to the state event `m.room.bot.options`, keyed to the bot/user. ### Setting powerlevel for a bot **Action**: `"set_bot_power"` **Required params**: * `room_id` - where to try and set the options * `user_id` - the bot (user) to set options for * `level` - the powerlevel for the bot (integer) **Sample call**: ``` sendMessage("set_bot_power", "!curbf:matrix.org", "@voyager:t2bot.io", {level: 50}); ``` **Success Response**: ``` { "action": "set_bot_power", "room_id": "!curbf:matrix.org", "user_id": "@voyager:t2bot.io", "level": 50, "response": { "success": true } } ``` ### Getting join rules for the room **Action**: `"join_rules_state"` **Required params**: * `room_id` - where to try and set the options **Sample call**: ``` sendMessage("join_rules_state", "!curbf:matrix.org"); ``` **Success Response**: ``` { "action": "join_rules_state", "room_id": "!curbf:matrix.org", "response": { "join_rule": "public" } } ``` *Note*: This returns the content of the state event `m.room.join_rules` in the room. ### Setting the plumbing state for a room **Action**: `"set_plumbing_state"` **Required params**: * `room_id` - where to try and set the options * `status` - the plumbing state (string) **Sample call**: ``` sendMessage("set_plumbing_state", "!curbf:matrix.org", null, {status:"whatever"}); ``` **Success Response**: ``` { "action": "set_plumbing_state", "room_id": "!curbf:matrix.org", "status": "whatever", "response": { "success": true } } ``` *Note*: This sets the `m.room.plumbing` state event for the room. ### Getting the number of people in a room **Action**: `"get_membership_count"` **Required params**: * `room_id` - where to query the membership count **Sample call**: ``` sendMessage("get_membership_count", "!curbf:matrix.org"); ``` **Success Response**: ``` { "action": "get_membership_count", "room_id": "!curbf:matrix.org", "response": 78 } ``` ### Determining if the user can send a particular event **Action**: `"can_send_event"` **Required params**: * `room_id` - where to query the membership count * `event_type` - the event type wishing to be sent * `is_state` - true if the event being sent will be a state event **Sample call**: ``` sendMessage("can_send_event", "!curbf:matrix.org", null, {event_type: "m.room.message", is_state: false}); ``` **Success Response**: ``` { "action": "get_membership_count", "room_id": "!curbf:matrix.org", "event_type": "m.room.message", "is_state": false, "response": true } ``` ### Get widgets in a room **Action**: `"get_widgets"` **Required params**: * `room_id` - where to find widgets **Sample call**: ``` sendMessage("get_widgets", "!curbf:matrix.org"); ``` **Success Response**: ``` { "action": "get_widgets", "room_id": "!curbf:matrix.org", "response: [{ "type": "im.vector.modular.widgets", "state_key": "widget1", "content": { "type": "grafana", "url": "https://somewhere.com", "name": "Dashboard", "data": {"key": "val"} }, "room_id": "!curbf:matrix.org", "sender": "@travis:t2l.io" }] } ``` ### Adding, updating, or deleting a widget in a room **Action**: `"set_widget"` **Required params**: * `"room_id"` - the room to affect * `"widget_id"` - an arbitrary utf8 unique identifier for the widget (used to distinguish update from add) * `"url"` - the url to put in the iframe (null to delete the widget). Some values are replaced: * `$matrix_user_id` - current user ID (not a way to authorize a user for the widget) * `$matrix_room_id` - current room ID * `$matrix_display_name` - current user's display name * `$matrix_avatar_url` - current user's avatar url (http, not mxc) * `$key` - the value of `key` in the `data` object * `"type"` - the widget type. Widgets supported in Scalar are: * `grafana` * `youtube` * `jitsi` * `googledocs` * `etherpad` * `customwidget` * `"name"` - optional human-readable string for the widget (eg: "Dashboard") * `"data"` - optional key-value pairs for the widget. Appended to the iframe. Some values are magic: **Sample call**: ``` // Add sendMessage("set_widget", "!curbf:matrix.org", null, { widget_id: "my_cool_widget", type: "grafana", url: "https://somewhere.com", name: "Dashboard", data: { "userId": "$mxUserId" } }); // Update // All fields must be supplied. If the field is optional, it must be present or undefined sendMessage("set_widget", "!curbf:matrix.org", null, { widget_id: "my_cool_widget", type: "grafana", url: "https://somewhere.else.com", name: "New Dashboard", data: { "userId": "$mxUserId" } }); // Delete sendMessage("set_widget", "!curbf:matrix.org", null, { widget_id: "my_cool_widget", type: "grafana", url: null // other fields not required if deleting }); ``` **Success Response**: ``` { "action": "set_widget", "room_id": "!curbf:matrix.org", "url": "https://somewhere.com", "type": "example", "response": { "success": true } } ``` *Note*: Widgets are documented by the matrix.org team [on this Google Doc](https://docs.google.com/document/d/1TiWNDcEOULeRYQpkJHQDjgIW32ohIJSi5MKv9oRdzCo/edit). That document is the source of truth for the event structure and usage. *Note*: `scalar_token` will be appended to the query string if the widget's url matches the API URL of the integration manager (in Element) ### Getting the room's encryption status **Action**: `"get_room_enc_state"` **Required params**: * `room_id` - the room to check **Sample call**: ``` sendMessage("get_room_enc_state", "!curbf:matrix.org"); ``` **Success Response**: ``` { "action": "get_room_enc_state", "room_id": "!curbf:matrix.org", "response: false } ``` ### Closing the integrations manager (scalar) **Action**: `"close_scalar"` **Required params**: * *None* **Sample call**: ``` sendMessage("close_scalar"); ``` **Success Response**: ``` { "action": "close_scalar", "response": null } ```