Endpoints
All messages to the Iota are either from the Omikron directly, from a Client or another Iota trying to message a User on the Iota.
All messages will be marked with a sender_id this will be added on the Omikron.
It is important to note that the Iota ID is not shared with others as it is what verifies the Iota users to Tensamin.
In the response the sender_id & receiver_id must be swapped as sender.
When a message comes from an iota without a sender-id that the iota has access to or is designated for the client (33333333-3333-3333-3333-333333333333) the message will not be send.
Messages
Section titled “Messages”Messaging Lifecycle
Section titled “Messaging Lifecycle”The messaging system follows a strict derivation loop to ensure delivery and correct state synchronization:
- The client sends a
message_sendto its Iota. - The Iota sends a response
message_sendas confirmation back to the client. - The Iota sends a
message_other_iotato the chat partner’s Iota.- If it times out:
- The initial Iota sends a
message_stateto the client with the state"sending".
- The initial Iota sends a
- If successful:
- The other Iota receives the
message_other_iotaand handles it. - The other Iota informs its client with a
message_live. - If the client doesn’t answer:
- The other Iota will send a
message_stateto the initial Iota with the state"sent". - The initial Iota will store the state and forward the
message_stateto its client.
- The other Iota will send a
- The other Iota receives the
- If it times out:
Message mutations
Section titled “Message mutations”Iota stores each mutation on the sender and recipient replicas. Content remains encrypted chat content. The sender is taken from the authenticated MTP connection and must own the original message for edits and deletion.
MessageEdit
Section titled “MessageEdit”Changes an existing message. ChatPartnerId and SendTime identify the message.
REQ (C2S):
Section titled “REQ (C2S):”{ "type": "MessageEdit", "sender_id": "<authenticated-user-id>", "data": { "ChatPartnerId": "<chat-partner-id>", "SendTime": "<message-unix-milliseconds>", "Content": "<encrypted-base64>" }}RES (S2C):
Section titled “RES (S2C):”{ "type": "Success"}UPDATE:
Section titled “UPDATE:”{ "type": "MessageEditLive", "sender_id": "<editing-user-id>", "receiver_id": "<chat-partner-id>", "data": { "ChatPartnerId": "<editing-user-id>", "SendTime": "<message-unix-milliseconds>", "Content": "<encrypted-base64>" }}MessageReactionAdd
Section titled “MessageReactionAdd”Adds one reaction for the authenticated user. Repeating the same request does not create a duplicate row.
REQ (C2S):
Section titled “REQ (C2S):”{ "type": "MessageReactionAdd", "sender_id": "<authenticated-user-id>", "data": { "ChatPartnerId": "<chat-partner-id>", "SendTime": "<message-unix-milliseconds>", "Reaction": "<emoji-or-reaction-text>" }}RES (S2C):
Section titled “RES (S2C):”{ "type": "Success"}MessageReactionRemove
Section titled “MessageReactionRemove”Removes the authenticated user’s matching reaction.
REQ (C2S):
Section titled “REQ (C2S):”{ "type": "MessageReactionRemove", "sender_id": "<authenticated-user-id>", "data": { "ChatPartnerId": "<chat-partner-id>", "SendTime": "<message-unix-milliseconds>", "Reaction": "<emoji-or-reaction-text>" }}RES (S2C):
Section titled “RES (S2C):”{ "type": "Success"}UPDATE:
Section titled “UPDATE:”{ "type": "MessageReactionLive", "sender_id": "<reacting-user-id>", "receiver_id": "<chat-partner-id>", "data": { "ChatPartnerId": "<reacting-user-id>", "SendTime": "<message-unix-milliseconds>", "Reaction": "<emoji-or-reaction-text>", "SenderId": "<reacting-user-id>", "Accepted": true }}Accepted is true for an add and false for a removal.
MessageDeleteLive
Section titled “MessageDeleteLive”Deletes a message from both replicas. The existing MessageDeleteLive MTP type is used for both the authenticated delete request and the recipient update because no separate delete request type exists in the client type map.
REQ (C2S):
Section titled “REQ (C2S):”{ "type": "MessageDeleteLive", "sender_id": "<authenticated-user-id>", "data": { "ChatPartnerId": "<chat-partner-id>", "SendTime": "<message-unix-milliseconds>" }}RES (S2C):
Section titled “RES (S2C):”{ "type": "Success"}UPDATE:
Section titled “UPDATE:”{ "type": "MessageDeleteLive", "sender_id": "<deleting-user-id>", "receiver_id": "<chat-partner-id>", "data": { "ChatPartnerId": "<deleting-user-id>", "SendTime": "<message-unix-milliseconds>" }} - **If the client answers with `message_state`:** 1. The other Iota will store the state (either `"received"` or `"read"`) and forward the `message_state` to the initial Iota. 2. The initial Iota will store the state and forward the `message_state` to its client.Client adds someone to their contacts
Section titled “Client adds someone to their contacts”When adding via name, the omikron will intercept and fill the chat_partner_id. The iota will never read, or handle the chat_partner_name.
{ "sender_id": "<user-id>", "receiver_id": "<user-id>",
"type": "add_conversation", "id": "<uuid>",
"data": { "chat_partner_id": id, "chat_partner_name": "name" }}{ "sender_id": "<user-id>", "receiver_id": "<user-id>",
"type": "add_conversation", "id": "<uuid>", "data": {}}Client messages someone
Section titled “Client messages someone”{ "sender_id": "<user-id>", "receiver_id": "<user-id>",
"type": "message_send", "id": "<uuid>", "data": { "receiver_id": "<uuid>", "content": "<markdown (encrypted)>", "files": [ { "name": "<cool name>", "id": "<uuid>", "type": "[ image | image_top_right | file ]" } ] }}{ "sender_id": "<user-id>", "receiver_id": "<user-id>",
"type": "message_send", "id": "<uuid>", "data": {}}Client loads messages
Section titled “Client loads messages”{ "sender_id": "<user-id>",
"type":"messages_get", "id": "<uuid>", "data": { "user_id": "<user-id>", "amount": int, "offset": int }}{ "sender_id": "<iota-id>",
"type": "messages_get", "id": "<uuid>", "data": { "messages": [ { "sent_by_self": true, "timestamp": 0, // UNIX Timestamp for time sent "content": "<markdown (encrypted)>", "Edited": true, "Reactions": [ { "Reaction": "<emoji-or-reaction-text>", "SenderId": "<user-id>" } ], "files": [ { "name": "<cool name>", "id": "<uuid>", "type": "[ image | image_top_right | file ]" } ], "tint": "<hex color>", "avatar": false, // unless false key is removed "display": false // unless false key is removed } ] }}ClientConnected and MessagesGet include Edited: true for messages with at least one edit and a Reactions array. Each reaction contains Reaction and SenderId fields. Clients that do not consume these fields continue to receive the existing message fields.
Send a message to other Iota
Section titled “Send a message to other Iota”{ "type": "message_send", "id": "<message_id>", "data": { "receiver_id": "99999999-8888-7777-6666-555555555555", "content": "Hello, how are you?" }}{ "type": "message", "id": "<message_id>", "receiver": "<user_id>"}Receive live message (message_other_iota)
Section titled “Receive live message (message_other_iota)”UPDATE:
Section titled “UPDATE:”{ "type": "message_live", "id": "<message_id>", "receiver": "<user_id>",
"data": { "send_time": unixstamp, "message": "<content>", "sender_id": "99999999-8888-7777-6666-555555555555" }}Communities
Section titled “Communities”Client storing a community on their Iota
Section titled “Client storing a community on their Iota”{ "type": "add_community", "id": "<message_id>", "receiver": "<user_id>",
"data": { "community_address": "community_address", "community_title": "community_title", "position": "x.y.z" }}{ "type": "add_community", "id": "<message_id>", "receiver": "<user_id>",
"data": {}}Client loading communities from their Iota
Section titled “Client loading communities from their Iota”{ "type": "get_communities", "id": "<message_id>",
"data": {}}{ "type": "get_community", "id": "<message_id>", "receiver": "<user_id>",
"data": { "communities": [ { "community_address": "enc_community_address", "community_title": "enc_community_title", "position": "x.y.z" // Frontend defines folders etc }, { "community_address": "enc_community_address", "community_title": "enc_community_title", "position": "x.y.z" } ] }}Remove a community
Section titled “Remove a community”{ "type": "remove_community", "id": "<message_id>",
"data": { "community_address": "community-uuid-or-address" }}{ "type": "remove_community", "id": "<message_id>", "receiver": "<user_id>"}User Settings
Section titled “User Settings”Settings are scoped to the authenticated device session. The SessionId must match the session assigned by the Omikron. Setting names may contain alphanumeric characters, _, -, and ., but may not contain ...
Settings are not included in ClientStateSync and changes are not pushed to clients. A client receives confirmation of its own change in the SettingsSave response and must send SettingsLoad to retrieve a stored value. Because these settings are scoped by SessionId, saving them does not update another device’s settings.
Save user Settings on Iota
Section titled “Save user Settings on Iota”{ "type": "SettingsSave", "id": "<message_id>",
"data": { "SessionId": 12345, "SettingsName": "<name of category>", "Payload": "<serialized or encrypted settings>" }}{ "type": "SettingsSave", "id": "<message_id>", "receiver": "<user_id>",
"data": { "SessionId": 12345, "SettingsName": "<name of category>" }}Load user Settings
Section titled “Load user Settings”{ "type": "SettingsLoad", "id": "<message_id>",
"data": { "SessionId": 12345, "SettingsName": "<name of category>" }}{ "type": "SettingsLoad", "id": "<message_id>", "receiver": "<user_id>",
"data": { "SessionId": 12345, "SettingsName": "<name of category>", "Payload": "<serialized or encrypted settings>" }}Loading a setting that does not exist returns ErrorNotFound with the requested SessionId and SettingsName.