{"owner":"tinode","repo":"chat","hasSkills":true,"totalSkillsCount":1,"totalTokensCount":11602,"categories":["project-spec"],"hasMcp":false,"mcpConfig":null,"found":["docs/API.md"],"skills":{"docs/API.md":"<!-- TOC depthfrom:1 depthto:6 withlinks:true updateonsave:true orderedlist:false -->\n\n- [Server API](#server-api)\n  - [How it Works?](#how-it-works)\n  - [General Considerations](#general-considerations)\n  - [Connecting to the Server](#connecting-to-the-server)\n    - [gRPC](#grpc)\n    - [WebSocket](#websocket)\n    - [Long Polling](#long-polling)\n    - [Out of Band Large Files](#out-of-band-large-files)\n    - [Running Behind a Reverse Proxy](#running-behind-a-reverse-proxy)\n  - [Users](#users)\n    - [Authentication](#authentication)\n      - [Creating an Account](#creating-an-account)\n      - [Logging in](#logging-in)\n      - [Changing Authentication Parameters](#changing-authentication-parameters)\n      - [Resetting a Password, i.e. \"Forgot Password\"](#resetting-a-password-ie-forgot-password)\n    - [Suspending a User](#suspending-a-user)\n    - [Credential Validation](#credential-validation)\n    - [Access Control](#access-control)\n  - [Topics](#topics)\n    - [me Topic](#me-topic)\n    - [fnd and Tags: Finding Users and Topics](#fnd-and-tags-finding-users-and-topics)\n      - [Query Language](#query-language)\n      - [Incremental Updates to Queries](#incremental-updates-to-queries)\n      - [Query Rewrite](#query-rewrite)\n      - [Possible Use Cases](#possible-use-cases)\n    - [Peer to Peer Topics](#peer-to-peer-topics)\n    - [Group Topics](#group-topics)\n    - [sys Topic](#sys-topic)\n  - [Using Server-Issued Message IDs](#using-server-issued-message-ids)\n  - [User Agent and Presence Notifications](#user-agent-and-presence-notifications)\n  - [Trusted, Public, Private, Auxiliary Fields](#trusted-public-private-auxiliary-fields)\n    - [Trusted](#trusted)\n    - [Public](#public)\n    - [Private](#private)\n    - [Auxiliary](#auxiliary)\n  - [Format of Content](#format-of-content)\n  - [Out-of-Band Handling of Large Files](#out-of-band-handling-of-large-files)\n    - [Uploading](#uploading)\n    - [Downloading](#downloading)\n  - [Push Notifications](#push-notifications)\n    - [Tinode Push Gateway](#tinode-push-gateway)\n    - [Google FCM](#google-fcm)\n    - [Stdout](#stdout)\n  - [Video Calls](#video-calls)\n  - [Link Previews](#link-previews)\n  - [Messages](#messages)\n    - [Client to Server Messages](#client-to-server-messages)\n      - [{hi}](#hi)\n      - [{acc}](#acc)\n      - [{login}](#login)\n      - [{sub}](#sub)\n      - [{leave}](#leave)\n      - [{pub}](#pub)\n      - [{get}](#get)\n      - [{set}](#set)\n      - [{del}](#del)\n      - [{note}](#note)\n    - [Server to Client Messages](#server-to-client-messages)\n      - [{data}](#data)\n      - [{ctrl}](#ctrl)\n      - [{meta}](#meta)\n      - [{pres}](#pres)\n      - [{info}](#info)\n\n<!-- /TOC -->\n\n# Server API\n\n## How it Works?\n\nTinode is an IM router and a store. Conceptually it loosely follows a [publish-subscribe](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) model.\n\nServer connects sessions, users, and topics. Session is a network connection between a client application and the server. User represents a human being who connects to the server with a session. Topic is a named communication channel which routes content between sessions.\n\nUsers and topics are assigned unique IDs. User ID is a string with 'usr' prefix followed by base64-URL-encoded pseudo-random 64-bit number, e.g. `usr2il9suCbuko`. Topic IDs are described below.\n\nClients such as mobile or web applications create sessions by connecting to the server over a websocket or through long polling. Client authentication is required in order to perform most operations. Client authenticates the session by sending a `{login}` packet. See [Authentication](#authentication) section for details. Once authenticated, the client receives a token which is used for authentication later. Multiple simultaneous sessions may be established by the same user. Logging out is not supported (and not needed).\n\nOnce the session is established, the user can start interacting with other users through topics. The following\ntopic types are available:\n\n* `me` is a topic for managing one's profile and receiving notifications about other topics; `me` topic exists for every user.\n* `fnd` topic is used for finding other users and topics; `fnd` topic also exists for every user.\n* Peer to peer topic is a communication channel strictly between two users. Each participant sees topic name as the ID of the other participant: 'usr' prefix followed by a base64-URL-encoded numeric part of user ID, e.g. `usr2il9suCbuko`.\n* Group topic is a channel for multi-user communication. It's named as 'grp' followed by 11 pseudo-random characters, i.e. `grpYiqEXb4QY6s`. Group topics must be explicitly created.\n\nSession joins a topic by sending a `{sub}` packet. Packet `{sub}` serves three functions: creating a new topic, subscribing user to a topic, and attaching session to a topic. See [`{sub}`](#sub) section below for details.\n\nOnce the session has joined the topic, the user may start generating content by sending `{pub}` packets. The content is delivered to other attached sessions as `{data}` packets.\n\nThe user may query or update topic metadata by sending `{get}` and `{set}` packets.\n\nChanges to topic metadata, such as changes in topic description, or when other users join or leave the topic, is reported to live sessions with `{pres}` (presence) packet. The `{pres}` packet is sent either to the topic being affected or to the `me` topic.\n\nWhen user's `me` topic comes online (i.e. an authenticated session attaches to `me` topic), a `{pres}` packet is sent to `me` topics of all other users, who have peer to peer subscriptions with the first user.\n\n## General Considerations\n\nTimestamps are always represented as [RFC 3339](http://tools.ietf.org/html/rfc3339)-formatted string with precision up to milliseconds and timezone always set to UTC, e.g. `\"2015-10-06T18:07:29.841Z\"`.\n\nWhenever base64 encoding is mentioned, it means base64 URL encoding with padding characters stripped, see [RFC 4648](http://tools.ietf.org/html/rfc4648).\n\nThe `{data}` packets have server-issued sequential IDs: base-10 numbers starting at 1 and incrementing by one with every message. They are guaranteed to be unique per topic.\n\nIn order to connect requests to responses, client may assign message IDs to all packets set to the server. These IDs are strings defined by the client. Client should make them unique at least per session. The client-assigned IDs are not interpreted by the server, they are returned to the client as is.\n\n## Connecting to the Server\n\nThere are three ways to access the server over the network: websocket, long polling, and [gRPC](https://grpc.io/).\n\nWhen the client establishes a connection to the server over HTTP(S), such as over a websocket or long polling, the server offers the following endpoints:\n * `/v0/channels` for websocket connections\n * `/v0/channels/lp` for long polling\n * `/v0/file/u` for file uploads\n * `/v0/file/s` for serving files (downloads)\n\n`v0` denotes API version (currently zero). Every HTTP(S) request must include the API key. The server checks for the API key in the following order:\n* HTTP header `X-Tinode-APIKey`\n* URL query parameter `apikey` (/v0/file/s/abcdefg.jpeg?apikey=...)\n* Form value `apikey`\n* Cookie `apikey`\n\nA default API key is included with every demo app for convenience. Generate your own key for production using [`keygen` utility](../keygen).\n\nOnce the connection is opened, the client must issue a `{hi}` message to the server. Server responds with a `{ctrl}` message which indicates either success or an error. The `params` field of the response contains server's protocol version `\"params\":{\"ver\":\"0.15\"}` and may include other values.\n\n### gRPC\n\nSee definition of the gRPC API in the [proto file](../pbx/model.proto). gRPC API has slightly more functionality than the API described in this document: it allows the `root` user to send messages on behalf of other users as well as delete users.\n\nThe `bytes` fields in protobuf messages expect JSON-encoded UTF-8 content. For example, a string should be quoted before being converted to bytes as UTF-8: `[]byte(\"\\\"some string\\\"\")` (Go), `'\"another string\"'.encode('utf-8')` (Python 3).\n\n### WebSocket\n\nMessages are sent in text frames, one message per frame. Binary frames are reserved for future use. By default server allows connections with any value in the `Origin` header.\n\n### Long Polling\n\nLong polling works over `HTTP POST` (preferred) or `GET`. In response to client's very first request server sends a `{ctrl}` message containing `sid` (session ID) in `params`. Long polling client must include `sid` in every subsequent request either in the URL or in the request body.\n\nServer allows connections from all origins, i.e. `Access-Control-Allow-Origin: *`\n\n### Out of Band Large Files\n\nLarge files are sent out of band using `HTTP POST` as `Content-Type: multipart/form-data`. See [below](#out-of-band-handling-of-large-files) for details.\n\n### Running Behind a Reverse Proxy\n\nTinode server can be set up to run behind a reverse proxy, such as NGINX. For efficiency it can accept client connections from Unix sockets by setting `listen` and/or `grpc_listen` config parameters to the path of the Unix socket file, e.g. `unix:/run/tinode.sock`. The server may also be configured to read peer's IP address from `X-Forwarded-For` HTTP header by setting `use_x_forwarded_for` config parameter to `true`.\n\n## Users\n\nUser is meant to represent a person, an end-user: producer and consumer of messages.\n\nUsers are generally assigned one of the two authentication levels: authenticated `auth` or anonymous `anon`. The third level `root` is only accessible over `gRPC` where it permits the `root` to send messages on behalf of other users.\n\nWhen a connection is first established, the client application can send either an `{acc}` or a `{login}` message which authenticates the user at one the levels.\n\nEach user is assigned a unique ID. The IDs are composed as `usr` followed by base64-encoded 64-bit numeric value, e.g. `usr2il9suCbuko`. Users also have the following properties:\n\n* `created`: timestamp when the user record was created\n* `updated`: timestamp of when user's `public` or `trusted` was last updated\n* `status`: state of the account\n* `username`: unique string used in `basic` authentication; username is not accessible to other users\n* `defacs`: object describing user's default access mode for peer to peer conversations with authenticated and anonymous users; see [Access control](#access-control) for details\n  * `auth`: default access mode for authenticated `auth` users\n  * `anon`: default access for anonymous `anon` users\n* `trusted`: an application-defined object issued by the system administration. Anyone can read it but only system administrators can change it.\n* `public`: an application-defined object that describes the user. Anyone can query user for `public` data.\n* `private`: an application-defined object that is unique to the current user and accessible only by the user.\n* `tags`: [discovery](#fnd-and-tags-finding-users-and-topics) and credentials.\n\nUser's account has a state. The following states are defined:\n * `ok` (normal): the default state which means the account is not restricted in any way and can be used normally;\n * `susp` (suspended): the user is prevented from accessing the account as well as not found through [search](#fnd-and-tags-finding-users-and-topics); the state can be assigned by the administrator and fully reversible.\n * `del` (soft-deleted): user is marked as deleted but user's data is retained; un-deleting the user is not currenly supported.\n * `undef` (undefined): used internally by authenticators; should not be used elsewhere.\n\nA user may maintain multiple simultaneous connections (sessions) with the server. Each session is tagged with a client-provided `User Agent` string intended to differentiate client software.\n\nLogging out is not supported by design. If an application needs to change the user, it should open a new connection and authenticate it with the new user credentials.\n\n\n### Authentication\n\nAuthentication is conceptually similar to [SASL](https://en.wikipedia.org/wiki/Simple_Authentication_and_Security_Layer): it's provided as a set of adapters each implementing a different authentication method. Authenticators are used during account registration [`{acc}`](#acc) and during [`{login}`](#login). The server comes with the following authentication methods out of the box:\n\n * `token` provides authentication by a cryptographic token.\n * `basic` provides authentication by a login-password pair.\n * `anonymous` is designed for cases where users are temporary, such as handling customer support requests through chat.\n * `rest` is a [meta-method](../server/auth/rest/) which allows use of external authentication systems by means of JSON RPC.\n\nAny other authentication method can be implemented using adapters.\n\nThe `token` is intended to be the primary means of authentication. Tokens are designed in such a way that token authentication is light weight. For instance, token authenticator generally does not make any database calls, all processing is done in-memory. All other authentication methods are intended to be used only to obtain or refresh the token. Once the token is obtained, subsequent logins should use it.\n\nThe `basic` authentication scheme expects `secret` to be a base64-encoded string of a string composed of a user name followed by a colon `:` followed by a plan text password. User name in the `basic` scheme must not contain the colon character `:` (ASCII 0x3A).\n\nThe `anonymous` scheme can be used to create accounts, it cannot be used for logging in: a user creates an account using `anonymous` scheme and obtains a cryptographic token which it uses for subsequent `token` logins. If the token is lost or expired, the user is no longer able to access the account.\n\nCompiled-in authenticator names may be changed by using `logical_names` configuration feature. For example, a custom `rest` authenticator may be exposed as `basic` instead of default one or `token` authenticator could be hidden from users. The feature is activated by providing an array of mappings in the config file: `logical_name:actual_name` to rename or `actual_name:` to hide. For instance, to use a `rest` service for basic authentication use `\"logical_names\": [\"basic:rest\"]`.\n\n\n#### Creating an Account\n\nWhen a new account is created, the user must inform the server which authentication method will be later used to gain access to this account as well as provide shared secret, if appropriate. Only `basic` and `anonymous` can be used during account creation. The `basic` requires the user to generate and send a unique login and password to the server. The `anonymous` does not exchange secrets.\n\nUser may optionally set `{acc login=true}` to use the new account for immediate authentication. When `login=false` (or not set), the new account is created but the authentication status of the session which created the account remains unchanged. When `login=true` the server will attempt to authenticate the session with the new account, the `{ctrl}` response to the `{acc}` request will contain the authentication token on success. This is particularly important for the `anonymous` authentication because that's the only time when the authentication token can be retrieved.\n\n#### Logging in\n\nLogging in is performed by issuing a `{login}` request. Logging in is possible with `basic` and `token` only. Response to any login is a `{ctrl}` message with either a code 200 and a token which can be used in subsequent logins with `token` authentication, or a code 300 request for additional information, such as verifying credentials or responding to a method-dependent challenge in multi-step authentication, or a code 4xx error.\n\nToken has server-configured expiration time so it needs to be periodically refreshed.\n\n#### Changing Authentication Parameters\n\nUser may change authentication parameters, such as changing login and password, by issuing an `{acc}` request. Only `basic` authentication currently supports changing parameters:\n```js\nacc: {\n  id: \"1a2b3\", // string, client-provided message id, optional\n  user: \"usr2il9suCbuko\", // user being affected by the change, optional\n  token: \"XMg...g1Gp8+BO0=\", // authentication token if the session\n                             // is not yet authenticated, optional.\n  scheme: \"basic\", // authentication scheme being updated.\n  secret: base64encode(\"new_username:new_password\") // new parameters\n}\n```\nIn order to change just the password, `username` should be left empty, i.e. `secret: base64encode(\":new_password\")`.\n\nIf the session is not authenticated, the request must include a `token`. It can be a regular authentication token obtained during login, or a restricted token received through [Resetting a Password](#resetting-a-password) process. If the session is authenticated, the token must not be included. If the request is authenticated for access level `ROOT`, then the `user` may be set to a valid ID of another user. Otherwise it must be blank (defaulting to the current user) or equal to the ID of the current user.\n\n\n#### Resetting a Password, i.e. \"Forgot Password\"\n\nTo reset login or password, (or any other authentication secret, if such action is supported by the authenticator), one sends a `{login}` message with the `scheme` set to `reset` and the `secret` containing a base64-encoded string \"`authentication scheme to reset secret for`:`reset method`:`reset method value`\". Most basic case of resetting a password by email is\n```js\nlogin: {\n  id: \"1a2b3\",\n  scheme: \"reset\",\n  secret: base64encode(\"basic:email:jdoe@example.com\")\n}\n```\nwhere `jdoe@example.com` is an earlier validated user's email.\n\nIf the email matches the registration, the server will send a message using specified method and address with instructions for resetting the secret. The email contains a restricted security token which the user can include into an `{acc}` request with the new secret as described in [Changing Authentication Parameters](#changing-authentication-parameters).\n\n### Suspending a User\n\nUser's account can be suspended by service administrator. Once the account is suspended, the user is no longer able to login and use the service.\n\nOnly the `root` user may suspend the account. To suspend the account the root user sends the following message:\n```js\nacc: {\n  id: \"1a2b3\", // string, client-provided message id, optional\n  user: \"usr2il9suCbuko\", // user being affected by the change\n  status: \"susp\"\n}\n```\nSending the same message with `status: \"ok\"` un-suspends the account. A root user may check account status by executing `{get what=\"desc\"}` command against user's `me` topic.\n\n\n### Credential Validation\n\nServer may be optionally configured to require validation of certain credentials associated with the user accounts and authentication scheme. For instance, it's possible to require user to provide a unique email or a phone number, or to solve a captcha as a condition of account registration.\n\nThe server supports verification of email out of the box with just a configuration change. is mostly functional, verification of phone numbers is not functional because a commercial subscription is needed in order to be able to send text messages (SMS).\n\nIf certain credentials are required, then user must maintain them in validated state at all times. It means if a required credential has to be changed, the user must first add and validate the new credential and only then remove the old one.\n\nCredentials are initially assigned at registration time by sending an `{acc}` message, added using `{set topic=\"me\"}`, deleted using `{del topic=\"me\"}`, and queries by `{get topic=\"me\"}` messages. Credentials are verified by the client by sending either a `{login}` or an `{acc}` message.\n\n\n### Access Control\n\nAccess control manages user's access to topics through access control lists (ACLs). The access is assigned individually to each user-topic pair (subscription).\n\nAccess control is mostly usable for group topics. Its usability for `me` and P2P topics is limited to managing presence notifications and banning uses from initiating or continuing P2P conversations. All channel readers are given the same permissions.\n\nUser's access to a topic is defined by two sets of permissions: user's desired permissions \"want\", and permissions granted to user by topic's manager(s) \"given\". Each permission is represented by a bit in a bitmap. It can be either present or absent. The actual access is determined as a bitwise AND of wanted and given permissions. The permissions are communicated in messages as a set of ASCII characters, where presence of a character means a set permission bit:\n\n* No access: `N` is not a permission per se but an indicator that permissions are explicitly cleared/not set. It usually indicates that the default permissions should *not* be applied.\n* Join: `J`, permission to subscribe to a topic\n* Read: `R`, permission to receive `{data}` packets\n* Write: `W`, permission to `{pub}` to topic\n* Presence: `P`, permission to receive presence updates `{pres}`\n* Approve: `A`, permission to approve requests to join a topic, remove and ban members; a user with such permission is topic's administrator\n* Sharing: `S`, permission to invite other people to join the topic\n* Delete: `D`, permission to hard-delete messages; only owners can completely delete topics\n* Owner: `O`, user is the topic owner; the owner can assign any other permission to any topic member, change topic description, delete topic; topic may have a single owner only; some topics have no owner\n\nWhen a user subscribes to a topic or starts a chat with another user, the access permissions are either set explicitly or assigned by default `defacs`. Access permissions can be modified by sending `{set}` messages.\n\nA client may set explicit permissions in `{sub}` and `{set}` messages. If the permissions are missing or set to an empty string (not `N`!), Tinode will use default permissions `defacs` assigned earlir. If no default permissions are found, the authenticated users in group topics will receive a `JRWPS` access, in P2P topics will get `JRWPA`; anonymous users will receive `N` (no access) which means every subscription request must be explicitly approved by the topic manager.\n\nDefault access is defined for two categories of users: authenticated and anonymous. The default access value is applied as a \"given\" permission to all new subscriptions. Topic's default access is established at the topic creation time by `{sub.desc.defacs}` and can be subsequently modified by the owner by sending `{set}` messages. Likewise, user's default access is established at the account creation time by `{acc.desc.defacs}` and can be modified by the user by sending a `{set}` message to `me` topic.\n\n\n## Topics\n\nTopic is a named communication channel for one or more people. Topics have persistent properties. These topic properties can be queried by `{get what=\"desc\"}` message.\n\nTopic properties independent of the user making the query:\n* `created`: timestamp of topic creation time\n* `updated`: timestamp of when topic's `trusted`, `public`, or `private` was last updated\n* `touched`: timestamp of the last message sent to the topic\n* `defacs`: object describing topic's default access mode for authenticated and anonymous users; see [Access control](#access-control) for details\n * `auth`: default access mode for authenticated users\n * `anon`: default access for anonymous users\n* `seq`: integer server-issued sequential ID of the latest `{data}` message sent through the topic\n* `trusted`: an application-defined object issued by the system administrators. Anyone can read it but only administrators can change it.\n* `public`: an application-defined object that describes the topic. Anyone who can subscribe to topic can receive topic's `public` data, only topic `owner` can change it.\n\nUser-dependent topic properties:\n* `acs`: object describing given user's current access permissions; see [Access control](#access-control) for details\n * `want`: access permission requested by this user\n * `given`: access permissions given to this user\n* `private`: an application-defined object that is unique to the current user (topic subscriber).\n\nTopic usually have subscribers. One of the subscribers may be designated as topic owner (`O` access permission) with full access permissions. The list of subscribers can be queries with a `{get what=\"sub\"}` message. The list of subscribers is returned in a `sub` section of a `{meta}` message.\n\n### `me` Topic\n\nTopic `me` is automatically created for every user at the account creation time. It serves as means of managing account information, receiving presence notification from people and topics of interest. Topic `me` has no owner. The topic cannot be deleted or unsubscribed from. One can `leave` the topic which will stop all relevant communication and indicate that the user is offline (although the user may still be logged in and may continue to use other topics).\n\nJoining or leaving `me` generates a `{pres}` presence update sent to all users who have peer to peer topics with the given user and `P` permissions set.\n\nTopic `me` is read-only. `{pub}` messages to `me` are rejected.\n\nMessage `{get what=\"desc\"}` to `me` is automatically replied with a `{meta}` message containing `desc` section with the topic parameters (see intro to [Topics](#topics) section). The `public` parameters of `me` topic is data that the user wants to show to his/her connections. Changing it changes `public` not just for the `me` topic, but also everywhere where user's `public` is shown, such as `public` of all user's peer to peer topics.\n\nMessage `{get what=\"sub\"}` to `me` is different from any other topic as it returns the list of topics that the current user is subscribed to as opposite to the expected user's subscription to `me`.\n* seq: server-issued numeric id of the last message in the topic\n* recv: seq value self-reported by the current user as received\n* read: seq value self-reported by the current user as read\n* seen: for P2P subscriptions, timestamp of user's last presence and User Agent string are reported\n * when: timestamp when the user was last online\n * ua: user agent string of the user's client software last used\n\nMessage `{get what=\"data\"}` to `me` is rejected.\n\nInternally the `me` topics are not persisted separately from the users. The `me` topics don't exist in the `topics` table or collection, they are created in memory from the `users` database record.\n\n### `slf` Topic\n\nTopic `slf` (self) provides mechanism for storing information, like bookmarks or saved messages. Messages sent to `slf` are accessible only by the user who sent them.\n\nThis topic is created automatically when the user subscribes to it for the first time.\n\n### `fnd` and Tags: Finding Users and Topics\n\nTopic `fnd` is automatically created for every user at the account creation time. It serves as an endpoint for discovering other users and group topics. Users and group topics can be discovered by `tags`. Tags are optionally assigned at the topic or user creation time then can be updated by using `{set what=\"tags\"}` against a `me` or a group topic.\n\nA tag is an arbitrary case-insensitive Unicode string (forced to lowercase on the server) up to 96 characters long which may contain characters from `Letter` and `Number` Unicode [classes/categories](https://en.wikipedia.org/wiki/Unicode_character_property#General_Category) as well as any of the following ASCII characters: `_`, `.`, `+`, `-`, `@`, `#`, `!`, `?`.\n\nTag may have a prefix which serves as a namespace. The prefix is a 2-16 character string which starts with a letter [a-z] and may contain lowercase ASCII letters and numbers followed by a colon `:`, ex. prefixed phone tag `tel:+14155551212` or prefixed email tag `email:alice@example.com`. Some prefixed tags are optionally enforced to be unique. In that case only one user or topic may have such a tag. Certain tags may be forced to be immutable to the user, i.e. user's attempts to add or remove an immutable tag will be rejected by the server.\n\nThe tags are indexed server-side and used in user and topic discovery. Search returns users and topics sorted by the number of matched tags in descending order.\n\nIn order to find users or topics, a user sets either `public` or `private` parameter of the `fnd` topic to a search query (see [Query language](#query-language)) then issues a `{get topic=\"fnd\" what=\"sub\"}` request. If both `public` and `private` are set, the `public` query is used. The `private` query is persisted across sessions and devices, i.e. all user's sessions see the same `private` query. The value of the `public` query is ephemeral, i.e. it's not saved to database and not shared between user's sessions. The `private` query is intended for large queries which do not change often, such as finding matches for everyone in user's contact list on a mobile phone. The `public` query is intended to be short and specific, such as finding some topic or a user who is not in the contact list.\n\nThe system responds with a `{meta}` message with the `sub` section listing details of the found users or topics formatted as subscriptions.\n\nTopic `fnd` is read-only. `{pub}` messages to `fnd` are rejected.\n\n_CURRENTLY UNSUPPORTED_ When a new user registers with tags matching the given query, the `fnd` topic will receive `{pres}` notification for the new user.\n\n[Plugins](../pbx) support `Find` service which can be used to replace default search with a custom one.\n\nInternally the `fnd` topics are not persisted separately from the users. The `fnd` topics don't exist in the `topics` table or collection, they are created in memory from the `users` database record.\n\n#### Query Language\n\nTinode query language is used to define search queries for finding users and topics. The query is a string containing atomic terms separated by spaces or commas. The individual query terms are matched against user's or topic's tags. The individual terms may be written in an RTL language but the query as a whole is parsed left to right. Spaces are treated as the `AND` operator, commas (as well as commas preceded and/or followed by a space) as the `OR` operator. The order of operators is ignored: all `AND` tags are grouped together, all `OR` tags are grouped together. `OR` takes precedence over `AND`: if a tag is preceded of followed by a comma, it's an `OR` tag, otherwise an `AND`. For example, `aaa bbb, ccc` (`aaa AND bbb OR ccc`) is interpreted as `(bbb OR ccc) AND aaa`.\n\nQuery terms containing spaces must convert spaces to underscores ` ` -> `_`, e.g. `new york` -> `new_york`.\n\n**Some examples:**\n* `flowers`: find topics or users which contain tag `flowers`.\n* `flowers travel`: find topics or users which contain both tags `flowers` and `travel`.\n* `flowers, travel`: find topics or users which contain either tag `flowers` or `travel` (or both).\n* `flowers travel, puppies`: find topics or users which contain `flowers` and either `travel` or `puppies`, i.e. `(travel OR puppies) AND flowers`.\n* `flowers, travel puppies, kittens`: find topics or users which contain either one of `flowers`, `travel`, `puppies`, or `kittens`, i.e. `flowers OR travel OR puppies OR kittens`. The space between `travel` and `puppies` is treated as `OR` due to `OR` taking precedence over `AND`.\n\n#### Incremental Updates to Queries\n\n_CURRENTLY UNSUPPORTED_ Queries, particularly `fnd.private` could be arbitrarily large, limited only by the limits on the message size, and by the limits on the query size in the underlying database. Instead of rewriting the entire query to add or remove a term, terms can be added or removed incrementally.\n\nThe incremental update request is processed left to right. It may contain the same term multiple times, i.e. `-a_tag+a_tag` is a valid request.\n\n#### Query Rewrite\n\nFinding users by login, phone or email requires query terms to be written with prefixes, i.e. `email:alice@example.com` instead of `alice@example.com`. This may present a problem to end users because it requires them to learn the query language. Tinode solves this problem by implementing _query rewrite_ on the server: if query term (tag) does not contain a prefix, server rewrites it by adding the appropriate prefix. In queries to `fnd.public` the original term is also kept (query `alice@example.com` is rewritten as `email:alice@example.com OR alice@example.com`), in queries to `fnd.private` only the rewritten term is kept (`alice@example.com` is rewritten as `email:alice@example.com`). All terms that look like email, for instance, `alice@example.com` are rewritten to `email:alice@example.com OR alice@example.com`. Terms which look like phone numbers are converted to [E.164](https://en.wikipedia.org/wiki/E.164) and also rewritten as `tel:+14155551212 OR +14155551212`. In addition, in queries to `fnd.public` all other unprefixed terms which look like logins are rewritten as logins: `alice` -> `basic:alice OR alice`.\n\nAs described above, tags which look like phone numbers are converted to E.164 format. Such conversion requires an ISO 3166-1 alpha-2 country code. The following logic is used when converting phone number tags to E.164:\n* If the tag already contains a country calling code, it's used as is: `+1(415)555-1212` -> `+14155551212`.\n* If the tag has no prefix, country code is taken from the locale value set by the client in `lang` field of the `{hi}` message.\n* If client has not provided the code in the `hi.lang`, the country code is taken from `default_country_code` field of the `tinode.conf`.\n* If no `default_country_code` is set in `tinode.conf`, `US` country code is used.\n\n#### Possible Use Cases\n* Restricting users to organisations.\n  An immutable tag(s) may be assigned to the user which denotes the organisation the user belongs to. When the user searches for other users or topics, the search can be restricted to always contain the tag. This approach can be used to segment users into organisations with limited visibility into each other.\n\n* Search by geographical location.\n  Client software may periodically assign a [geohash](https://en.wikipedia.org/wiki/Geohash) tag to the user based on current location. Searching for users in a given area would mean matching on geohash tags.\n\n* Search by numerical range, such as age range.\n  The approach is similar to geohashing. The entire range of numbers is covered by the smallest possible power of 2, for instance the range of human ages is covered by 2<sup>7</sup>=128 years. The entire range is split in two halves: the range 0-63 is denoted by 0, 64-127 by 1. The operation is repeated with each subrange, i.e. 0-31 is 00, 32-63 is 01, 0-15 is 000, 32-47 is 010. Once completed, the age 30 will belong to the following ranges: 0 (0-63), 00 (0-31), 001 (16-31), 0011 (24-31), 00111 (28-31), 001111 (30-31), 0011110 (30). A 30 y.o. user is assigned a few tags to indicate the age, i.e. `age:00111`, `age:001111`,  and `age:0011110`. Technically, all 7 tags may be assigned but usually it's impractical. To query for anyone in the age range 28-35 convert the range into a minimal number of tags: `age:00111` (28-31), `age:01000` (32-35). This query will match the 30 y.o. user by tag `age:00111`.\n\n\n### Peer to Peer Topics\n\nPeer to peer (P2P) topics represent communication channels between strictly two users. The name of the topic is different for each of the two participants. Each of them sees the name of the topic as the user ID of the other participant: `usr` followed by base64 URL-encoded ID of the user. For example, if two users `usrOj0B3-gSBSs` and `usrIU_LOVwRNsc` start a P2P topic, the first one will see it as `usrIU_LOVwRNsc`, the second as `usrOj0B3-gSBSs`. The P2P topic has no owner.\n\nA P2P topic is created by one user subscribing to topic with the name equal to the ID of the other user. For instance, user `usrOj0B3-gSBSs` can establish a P2P topic with user `usrIU_LOVwRNsc` by sending a `{sub topic=\"usrIU_LOVwRNsc\"}`. Tinode will respond with a `{ctrl}` packet with the name of the newly created topic as described above. The other user will receive a `{pres}` message on `me` topic with updated access permissions.\n\nInternally, P2P topics are stored as `p2p` followed by base64 URL encoded concatenation of two 64-bit user IDs, with the lower numeric value ID first: `p2pm7PvMGmdcx_uVkDRaSTbwA`.\n\nThe 'public' parameter of P2P topics is user-dependent. For instance a P2P topic between users A and B would show user A's 'public' to user B and vice versa. If a user updates 'public', all user's P2P topics will automatically update 'public' too.\n\nThe 'private' parameter of a P2P topic is defined by each participant individually as with any other topic type.\n\n### Group Topics\n\nGroup topic represents a communication channel between multiple users. The name of a group topic is `grp` or `chn` followed by a string of characters from base64 URL-encoding set. No other assumptions can be made about internal structure or length of the group name.\n\nGroup topics support limited number of subscribers (controlled by a `max_subscriber_count` parameter in configuration file) with access permissions of each subscriber managed individually. Group topics may also be enabled to support any number of read-only users - `readers`. All `readers` have the same access permissions. Group topics with enabled `readers` are called `channels`.\n\nA group topic is created by sending a `{sub}` message with the topic field set to string `new` or `nch` optionally followed by any characters, e.g. `new` or `newAbC123` are equivalent. Tinode will respond with a `{ctrl}` message with the name of the newly created topic, i.e. `{sub topic=\"new\"}` is replied with `{ctrl topic=\"grpmiKBkQVXnm3P\"}`. If topic creation fails, the error is reported on the original topic name, i.e. `new` or `newAbC123`. The user who created the topic becomes topic owner. Ownership can be transferred to another user with a `{set}` message but one user must remain the owner at all times.\n\nA `channel` topic is different from the non-channel group topic in the following ways:\n\n * Channel topic is created by sending `{sub topic=\"nch\"}`. Sending `{sub topic=\"new\"}` will create a group topic without enabling channel functionality.\n * Sending `{sub topic=\"chnAbC123\"}` will create a `reader` subscription to a channel. A non-channel topic will reject such subscription request.\n * When searching for topics using [`fnd`](#fnd-and-tags-finding-users-and-topics), channels will show addresses with `chn` prefixes, non-channel topic will show with `grp` prefixes.\n * Messages received by readers on channels have no `From` field. Normal subscribers will receive messages with `From` containing ID of the sender.\n * Default permissions for a channel and non-channel group topics are different: channel group topic grants no permissions at all.\n * A subscriber joining or leaving the topic (regular or channel-enabled) generates a `{pres}` message to all other subscribers who are currently in the joined state with the topic and have appropriate permissions. Reader joining or leaving the channel generates no `{pres}` message.\n\n### `sys` Topic\n\nThe `sys` topic serves as an always available channel of communication with the system administrators. A normal non-root user cannot subscribe to `sys` but can publish to it without subscription. Existing clients use this channel to report abuse by sending a Drafty-formatted `{pub}` message with the report as JSON attachment. A root user can subscribe to `sys` topic. Once subscribed, the root user will receive messages sent to `sys` topic by other users.\n\n## Using Server-Issued Message IDs\n\nTinode provides basic support for client-side caching of `{data}` messages in the form of server-issued sequential message IDs. The client may request the last message id from the topic by issuing a `{get what=\"desc\"}` message. If the returned ID is greater than the ID of the latest received message, the client knows that the topic has unread messages and their count. The client may fetch these messages using `{get what=\"data\"}` message. The client may also paginate history retrieval by using message IDs.\n\n## User Agent and Presence Notifications\n\nA user is reported as being online when one or more of user's sessions are attached to the `me` topic. Client-side software identifies itself to the server using `ua` (user agent) field of the `{login}` message. The _user agent_ is published in `{meta}` and `{pres}` messages in the following way:\n\n * When user's first session attaches to `me`, the _user agent_ from that session is broadcast in the `{pres what=\"on\" ua=\"...\"}` message.\n * When multiple user sessions are attached to `me`, the _user agent_ of the session where the most recent action has happened is reported in `{pres what=\"ua\" ua=\"...\"}`; the 'action' in this context means any message sent by the client. To avoid potentially excessive traffic, user agent changes are broadcast no more frequently than once a minute.\n * When user's last session detaches from `me`, the _user agent_ from that session is recorded together with the timestamp; the user agent is broadcast in the `{pres what=\"off\"  ua=\"...\"}` message and subsequently reported as the last online timestamp and user agent.\n\nAn empty `ua=\"\"` _user agent_ is not reported. I.e. if user attaches to `me` with non-empty _user agent_ then does so with an empty one, the change is not reported. An empty _user agent_ may be disallowed in the future.\n\n## Trusted, Public, Private, Auxiliary Fields\n\nTopics have `trusted`, `public`, `aux` fields, subscriptions have `private` fields. The primary difference between these fields is in access control:\n\n * `trusted`: writable by `ROOT` users, readable by anyone.\n * `public`: writable by the `owner` or the user, readable by anyone.\n * `aux`: writable by topic administrators, readable by subscribers.\n * `private`: readable and writable only by the user who created the subscription.\n\nGenerally, the fields are application-defined. The server does not enforce any particular structure of these fields except for `fnd` topic. At the same time, client software should use the same format for interoperability reasons. The following sections describe the format of these fields as they are implemented by all official clients.\n\nAlthough it's not yet enforced, if a third-party application defines custom keys, the key names should start with an `x-` followed by the application's fully qualified domain name, e.g. `x-example.com-value: \"abc\"`. The fields should contain primitive types only, i.e. `string`, `boolean`, `number`, or `null`.\n\n### Trusted\n\nThe format of the optional `trusted` field in group and peer to peer topics is a set of key-value pairs; `fnd` and `sys` topics do not have the `trusted`. The field is writable by `ROOT` users, readable by anyone who has access to the topic or user. The following optional keys are currently defined:\n```js\ntrusted: {\n  verified: true, // boolean, an indicator of a verified/trustworthy user or topic.\n  staff: true,    // boolean, an indicator that the user or topic\n                  // is a part of/belongs to the server administration.\n  danger: true    // boolean, an indicator that the user or topic are untrustworthy.\n}\n```\n\n### Public\n\nThe format of the `public` field in group, peer to peer, systems topics is expected to be [theCard](./thecard.md). The field is writable by by the user for users, the topic owner for topics. The field is readable by anyone who has access to topic or user.\n\nThe `fnd` topic expects `public` to be a string representing a [search query](#query-language)).\n\n### Private\n\nThe format of the `private` field in group and peer to peer topics is a set of key-value pairs. The field is writable and readable by the user only. The following keys are currently defined:\n```js\nprivate: {\n  comment: \"some comment\", // string, optional user comment about a topic or a peer user\n  arch: true, // boolean, indicator that the topic is archived by the user, i.e.\n              // should not be shown in the UI with other non-archived topics.\n  accepted: \"JRWS\", // string, 'given' mode accepted by the user.\n  tpins: [\"grpmiKBkQVXnm3P\", \"usrIU_LOVwRNsc\"] // array of topic IDs to pin to the top of\n              // the contacts list; 'me' topic only.\n}\n```\n\nThe `fnd` topic expects `private` to be a string representing a [search query](#query-language)).\n\n### Auxiliary\n\nThe format of the `aux` field is a set of key-value pairs. The `aux` is writable by topic admins and readable by all topic subscribers. The following keys are currently defined:\n\n```js\naux: {\n  pins: [1001, 23456] // array of integer message IDs to pin to the top of the message list.\n}\n```\n\n## Format of Content\n\nFormat of `content` field in `{pub}` and `{data}` is application-defined and as such the server does not enforce any particular structure of the field. At the same time, client software should use the same format for interoperability reasons. Currently the following two types of `content` are supported:\n * Plain text\n * [Drafty](./drafty.md)\n\nIf Drafty is used, a message header `\"head\": {\"mime\": \"text/x-drafty\"}` must be set.\n\n\n## Out-of-Band Handling of Large Files\n\nLarge files create problems when sent in-band for multiple reasons:\n * limits on database storage as in-band messages are stored in database fields\n * in-band messages must be downloaded completely as a part of downloading chat history\n\nTinode provides two endpoints for handling large files: `/v0/file/u` for uploading files and `v0/file/s` for downloading. The endpoints require the client to provide both [API key](#connecting-to-the-server) and login credentials. The server checks credentials in the following order:\n\n**Login credentials**\n * HTTP header `Authorization` (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization)\n * URL query parameters `auth` and `secret` (/v0/file/s/abcdefg.jpeg?auth=...&secret=...)\n * Form values `auth` and `secret`\n * Cookies `auth` and `secret`\n\n### Uploading\n\nTo upload a file first create an RFC 2388 multipart request then send it to the server using HTTP POST. The server responds to the request either with a `307 Temporary Redirect` with the new upload URL, or with a `200 OK` and a `{ctrl}` message in response body:\n\n```js\nctrl: {\n  params: {\n    url: \"/v0/file/s/mfHLxDWFhfU.pdf\"\n  },\n  code: 200,\n  text: \"ok\",\n  ts: \"2018-07-06T18:47:51.265Z\"\n}\n```\nIf `307 Temporary Redirect` is returned, the client must retry the upload at the provided URL. The URL returned in `307` response should be used for just this one upload. All subsequent uploads should try the default URL first.\n\nThe `ctrl.params.url` contains the path to the uploaded file at the current server. It could be either the full path like `/v0/file/s/mfHLxDWFhfU.pdf`, a relative path like `./mfHLxDWFhfU.pdf`, or just the file name `mfHLxDWFhfU.pdf`. Anything but the full path is interpreted against the default *download* endpoint `/v0/file/s/`. For instance, if `mfHLxDWFhfU.pdf` is returned then the file is located at `http(s)://current-tinode-server/v0/file/s/mfHLxDWFhfU.pdf`.\n\nOnce the URL of the file is received, either immediately or after following the redirect, the client may use the URL to send a `{pub}` message with the uploaded file as an attachment, or, if the file is an image, as an avatar image for a topic or user profile (see [theCard](./thecard.md)). For example, the URL can be used in a [Drafty](./drafty.md)-formatted `pub.content` field:\n\n```js\n{\n  pub: {\n    id: \"121103\",\n    topic: \"grpnG99YhENiQU\",\n    head: {\n      mime: \"text/x-drafty\"\n    },\n    content: {\n      ent: [\n      {\n        data: {\n        mime: \"image/jpeg\",\n        name: \"roses-are-red.jpg\",\n        ref:  \"/v0/file/s/sJOD_tZDPz0.jpg\",\n        size: 437265\n      },\n        tp: \"EX\"\n      }\n    ],\n    fmt: [\n      {\n        at: -1,\n      key:0,\n      len:1\n      }\n    ]\n    }\n  },\n  extra: {\n    attachments: [\"/v0/file/s/sJOD_tZDPz0.jpg\"]\n  }\n}\n```\n\nIt's important to list the used URLs in the `extra: attachments[...]` field. Tinode server uses this field to maintain the uploaded file's use counter. Once the counter drops to zero for the given file (for instance, because a message with the shared URL was deleted or because the client failed to include the URL in the `extra.attachments` field), the server will garbage collect the file. Only relative URLs should be used. Absolute URLs in the `extra.attachments` field are ignored. The URL value is expected to be the `ctrl.params.url` returned in response to upload.\n\n### Downloading\n\nThe serving endpoint `/v0/file/s` serves files in response to HTTP GET requests. The client must evaluate relative URLs against this endpoint, i.e. if it receives a URL `mfHLxDWFhfU.pdf` or `./mfHLxDWFhfU.pdf` it should interpret it as a path `/v0/file/s/mfHLxDWFhfU.pdf` at the current Tinode HTTP server.\n\n_Important!_ As a security measure, the client should not send security credentials if the download URL is absolute and leads to another server.\n\n## Push Notifications\n\nTinode uses compile-time adapters for handling push notifications. The server comes with [Tinode Push Gateway](../server/push/tnpg/), [Google FCM](https://firebase.google.com/docs/cloud-messaging/), and `stdout` adapters. Tinode Push Gateway and Google FCM support Android with [Play Services](https://developers.google.com/android/guides/overview) (may not be supported by some Chinese phones), iOS devices and all major web browsers excluding Safari. The `stdout` adapter does not actually send push notifications. It's mostly useful for debugging, testing and logging. Other types of push notifications such as [TPNS](https://intl.cloud.tencent.com/product/tpns) can be handled by writing appropriate adapters.\n\nIf you are writing a custom plugin, the notification payload is the following:\n```js\n{\n  topic: \"grpnG99YhENiQU\", // Topic which"},"files":{"docs/API.md":"<!-- TOC depthfrom:1 depthto:6 withlinks:true updateonsave:true orderedlist:false -->\n\n- [Server API](#server-api)\n  - [How it Works?](#how-it-works)\n  - [General Considerations](#general-considerations)\n  - [Connecting to the Server](#connecting-to-the-server)\n    - [gRPC](#grpc)\n    - [WebSocket](#websocket)\n    - [Long Polling](#long-polling)\n    - [Out of Band Large Files](#out-of-band-large-files)\n    - [Running Behind a Reverse Proxy](#running-behind-a-reverse-proxy)\n  - [Users](#users)\n    - [Authentication](#authentication)\n      - [Creating an Account](#creating-an-account)\n      - [Logging in](#logging-in)\n      - [Changing Authentication Parameters](#changing-authentication-parameters)\n      - [Resetting a Password, i.e. \"Forgot Password\"](#resetting-a-password-ie-forgot-password)\n    - [Suspending a User](#suspending-a-user)\n    - [Credential Validation](#credential-validation)\n    - [Access Control](#access-control)\n  - [Topics](#topics)\n    - [me Topic](#me-topic)\n    - [fnd and Tags: Finding Users and Topics](#fnd-and-tags-finding-users-and-topics)\n      - [Query Language](#query-language)\n      - [Incremental Updates to Queries](#incremental-updates-to-queries)\n      - [Query Rewrite](#query-rewrite)\n      - [Possible Use Cases](#possible-use-cases)\n    - [Peer to Peer Topics](#peer-to-peer-topics)\n    - [Group Topics](#group-topics)\n    - [sys Topic](#sys-topic)\n  - [Using Server-Issued Message IDs](#using-server-issued-message-ids)\n  - [User Agent and Presence Notifications](#user-agent-and-presence-notifications)\n  - [Trusted, Public, Private, Auxiliary Fields](#trusted-public-private-auxiliary-fields)\n    - [Trusted](#trusted)\n    - [Public](#public)\n    - [Private](#private)\n    - [Auxiliary](#auxiliary)\n  - [Format of Content](#format-of-content)\n  - [Out-of-Band Handling of Large Files](#out-of-band-handling-of-large-files)\n    - [Uploading](#uploading)\n    - [Downloading](#downloading)\n  - [Push Notifications](#push-notifications)\n    - [Tinode Push Gateway](#tinode-push-gateway)\n    - [Google FCM](#google-fcm)\n    - [Stdout](#stdout)\n  - [Video Calls](#video-calls)\n  - [Link Previews](#link-previews)\n  - [Messages](#messages)\n    - [Client to Server Messages](#client-to-server-messages)\n      - [{hi}](#hi)\n      - [{acc}](#acc)\n      - [{login}](#login)\n      - [{sub}](#sub)\n      - [{leave}](#leave)\n      - [{pub}](#pub)\n      - [{get}](#get)\n      - [{set}](#set)\n      - [{del}](#del)\n      - [{note}](#note)\n    - [Server to Client Messages](#server-to-client-messages)\n      - [{data}](#data)\n      - [{ctrl}](#ctrl)\n      - [{meta}](#meta)\n      - [{pres}](#pres)\n      - [{info}](#info)\n\n<!-- /TOC -->\n\n# Server API\n\n## How it Works?\n\nTinode is an IM router and a store. Conceptually it loosely follows a [publish-subscribe](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) model.\n\nServer connects sessions, users, and topics. Session is a network connection between a client application and the server. User represents a human being who connects to the server with a session. Topic is a named communication channel which routes content between sessions.\n\nUsers and topics are assigned unique IDs. User ID is a string with 'usr' prefix followed by base64-URL-encoded pseudo-random 64-bit number, e.g. `usr2il9suCbuko`. Topic IDs are described below.\n\nClients such as mobile or web applications create sessions by connecting to the server over a websocket or through long polling. Client authentication is required in order to perform most operations. Client authenticates the session by sending a `{login}` packet. See [Authentication](#authentication) section for details. Once authenticated, the client receives a token which is used for authentication later. Multiple simultaneous sessions may be established by the same user. Logging out is not supported (and not needed).\n\nOnce the session is established, the user can start interacting with other users through topics. The following\ntopic types are available:\n\n* `me` is a topic for managing one's profile and receiving notifications about other topics; `me` topic exists for every user.\n* `fnd` topic is used for finding other users and topics; `fnd` topic also exists for every user.\n* Peer to peer topic is a communication channel strictly between two users. Each participant sees topic name as the ID of the other participant: 'usr' prefix followed by a base64-URL-encoded numeric part of user ID, e.g. `usr2il9suCbuko`.\n* Group topic is a channel for multi-user communication. It's named as 'grp' followed by 11 pseudo-random characters, i.e. `grpYiqEXb4QY6s`. Group topics must be explicitly created.\n\nSession joins a topic by sending a `{sub}` packet. Packet `{sub}` serves three functions: creating a new topic, subscribing user to a topic, and attaching session to a topic. See [`{sub}`](#sub) section below for details.\n\nOnce the session has joined the topic, the user may start generating content by sending `{pub}` packets. The content is delivered to other attached sessions as `{data}` packets.\n\nThe user may query or update topic metadata by sending `{get}` and `{set}` packets.\n\nChanges to topic metadata, such as changes in topic description, or when other users join or leave the topic, is reported to live sessions with `{pres}` (presence) packet. The `{pres}` packet is sent either to the topic being affected or to the `me` topic.\n\nWhen user's `me` topic comes online (i.e. an authenticated session attaches to `me` topic), a `{pres}` packet is sent to `me` topics of all other users, who have peer to peer subscriptions with the first user.\n\n## General Considerations\n\nTimestamps are always represented as [RFC 3339](http://tools.ietf.org/html/rfc3339)-formatted string with precision up to milliseconds and timezone always set to UTC, e.g. `\"2015-10-06T18:07:29.841Z\"`.\n\nWhenever base64 encoding is mentioned, it means base64 URL encoding with padding characters stripped, see [RFC 4648](http://tools.ietf.org/html/rfc4648).\n\nThe `{data}` packets have server-issued sequential IDs: base-10 numbers starting at 1 and incrementing by one with every message. They are guaranteed to be unique per topic.\n\nIn order to connect requests to responses, client may assign message IDs to all packets set to the server. These IDs are strings defined by the client. Client should make them unique at least per session. The client-assigned IDs are not interpreted by the server, they are returned to the client as is.\n\n## Connecting to the Server\n\nThere are three ways to access the server over the network: websocket, long polling, and [gRPC](https://grpc.io/).\n\nWhen the client establishes a connection to the server over HTTP(S), such as over a websocket or long polling, the server offers the following endpoints:\n * `/v0/channels` for websocket connections\n * `/v0/channels/lp` for long polling\n * `/v0/file/u` for file uploads\n * `/v0/file/s` for serving files (downloads)\n\n`v0` denotes API version (currently zero). Every HTTP(S) request must include the API key. The server checks for the API key in the following order:\n* HTTP header `X-Tinode-APIKey`\n* URL query parameter `apikey` (/v0/file/s/abcdefg.jpeg?apikey=...)\n* Form value `apikey`\n* Cookie `apikey`\n\nA default API key is included with every demo app for convenience. Generate your own key for production using [`keygen` utility](../keygen).\n\nOnce the connection is opened, the client must issue a `{hi}` message to the server. Server responds with a `{ctrl}` message which indicates either success or an error. The `params` field of the response contains server's protocol version `\"params\":{\"ver\":\"0.15\"}` and may include other values.\n\n### gRPC\n\nSee definition of the gRPC API in the [proto file](../pbx/model.proto). gRPC API has slightly more functionality than the API described in this document: it allows the `root` user to send messages on behalf of other users as well as delete users.\n\nThe `bytes` fields in protobuf messages expect JSON-encoded UTF-8 content. For example, a string should be quoted before being converted to bytes as UTF-8: `[]byte(\"\\\"some string\\\"\")` (Go), `'\"another string\"'.encode('utf-8')` (Python 3).\n\n### WebSocket\n\nMessages are sent in text frames, one message per frame. Binary frames are reserved for future use. By default server allows connections with any value in the `Origin` header.\n\n### Long Polling\n\nLong polling works over `HTTP POST` (preferred) or `GET`. In response to client's very first request server sends a `{ctrl}` message containing `sid` (session ID) in `params`. Long polling client must include `sid` in every subsequent request either in the URL or in the request body.\n\nServer allows connections from all origins, i.e. `Access-Control-Allow-Origin: *`\n\n### Out of Band Large Files\n\nLarge files are sent out of band using `HTTP POST` as `Content-Type: multipart/form-data`. See [below](#out-of-band-handling-of-large-files) for details.\n\n### Running Behind a Reverse Proxy\n\nTinode server can be set up to run behind a reverse proxy, such as NGINX. For efficiency it can accept client connections from Unix sockets by setting `listen` and/or `grpc_listen` config parameters to the path of the Unix socket file, e.g. `unix:/run/tinode.sock`. The server may also be configured to read peer's IP address from `X-Forwarded-For` HTTP header by setting `use_x_forwarded_for` config parameter to `true`.\n\n## Users\n\nUser is meant to represent a person, an end-user: producer and consumer of messages.\n\nUsers are generally assigned one of the two authentication levels: authenticated `auth` or anonymous `anon`. The third level `root` is only accessible over `gRPC` where it permits the `root` to send messages on behalf of other users.\n\nWhen a connection is first established, the client application can send either an `{acc}` or a `{login}` message which authenticates the user at one the levels.\n\nEach user is assigned a unique ID. The IDs are composed as `usr` followed by base64-encoded 64-bit numeric value, e.g. `usr2il9suCbuko`. Users also have the following properties:\n\n* `created`: timestamp when the user record was created\n* `updated`: timestamp of when user's `public` or `trusted` was last updated\n* `status`: state of the account\n* `username`: unique string used in `basic` authentication; username is not accessible to other users\n* `defacs`: object describing user's default access mode for peer to peer conversations with authenticated and anonymous users; see [Access control](#access-control) for details\n  * `auth`: default access mode for authenticated `auth` users\n  * `anon`: default access for anonymous `anon` users\n* `trusted`: an application-defined object issued by the system administration. Anyone can read it but only system administrators can change it.\n* `public`: an application-defined object that describes the user. Anyone can query user for `public` data.\n* `private`: an application-defined object that is unique to the current user and accessible only by the user.\n* `tags`: [discovery](#fnd-and-tags-finding-users-and-topics) and credentials.\n\nUser's account has a state. The following states are defined:\n * `ok` (normal): the default state which means the account is not restricted in any way and can be used normally;\n * `susp` (suspended): the user is prevented from accessing the account as well as not found through [search](#fnd-and-tags-finding-users-and-topics); the state can be assigned by the administrator and fully reversible.\n * `del` (soft-deleted): user is marked as deleted but user's data is retained; un-deleting the user is not currenly supported.\n * `undef` (undefined): used internally by authenticators; should not be used elsewhere.\n\nA user may maintain multiple simultaneous connections (sessions) with the server. Each session is tagged with a client-provided `User Agent` string intended to differentiate client software.\n\nLogging out is not supported by design. If an application needs to change the user, it should open a new connection and authenticate it with the new user credentials.\n\n\n### Authentication\n\nAuthentication is conceptually similar to [SASL](https://en.wikipedia.org/wiki/Simple_Authentication_and_Security_Layer): it's provided as a set of adapters each implementing a different authentication method. Authenticators are used during account registration [`{acc}`](#acc) and during [`{login}`](#login). The server comes with the following authentication methods out of the box:\n\n * `token` provides authentication by a cryptographic token.\n * `basic` provides authentication by a login-password pair.\n * `anonymous` is designed for cases where users are temporary, such as handling customer support requests through chat.\n * `rest` is a [meta-method](../server/auth/rest/) which allows use of external authentication systems by means of JSON RPC.\n\nAny other authentication method can be implemented using adapters.\n\nThe `token` is intended to be the primary means of authentication. Tokens are designed in such a way that token authentication is light weight. For instance, token authenticator generally does not make any database calls, all processing is done in-memory. All other authentication methods are intended to be used only to obtain or refresh the token. Once the token is obtained, subsequent logins should use it.\n\nThe `basic` authentication scheme expects `secret` to be a base64-encoded string of a string composed of a user name followed by a colon `:` followed by a plan text password. User name in the `basic` scheme must not contain the colon character `:` (ASCII 0x3A).\n\nThe `anonymous` scheme can be used to create accounts, it cannot be used for logging in: a user creates an account using `anonymous` scheme and obtains a cryptographic token which it uses for subsequent `token` logins. If the token is lost or expired, the user is no longer able to access the account.\n\nCompiled-in authenticator names may be changed by using `logical_names` configuration feature. For example, a custom `rest` authenticator may be exposed as `basic` instead of default one or `token` authenticator could be hidden from users. The feature is activated by providing an array of mappings in the config file: `logical_name:actual_name` to rename or `actual_name:` to hide. For instance, to use a `rest` service for basic authentication use `\"logical_names\": [\"basic:rest\"]`.\n\n\n#### Creating an Account\n\nWhen a new account is created, the user must inform the server which authentication method will be later used to gain access to this account as well as provide shared secret, if appropriate. Only `basic` and `anonymous` can be used during account creation. The `basic` requires the user to generate and send a unique login and password to the server. The `anonymous` does not exchange secrets.\n\nUser may optionally set `{acc login=true}` to use the new account for immediate authentication. When `login=false` (or not set), the new account is created but the authentication status of the session which created the account remains unchanged. When `login=true` the server will attempt to authenticate the session with the new account, the `{ctrl}` response to the `{acc}` request will contain the authentication token on success. This is particularly important for the `anonymous` authentication because that's the only time when the authentication token can be retrieved.\n\n#### Logging in\n\nLogging in is performed by issuing a `{login}` request. Logging in is possible with `basic` and `token` only. Response to any login is a `{ctrl}` message with either a code 200 and a token which can be used in subsequent logins with `token` authentication, or a code 300 request for additional information, such as verifying credentials or responding to a method-dependent challenge in multi-step authentication, or a code 4xx error.\n\nToken has server-configured expiration time so it needs to be periodically refreshed.\n\n#### Changing Authentication Parameters\n\nUser may change authentication parameters, such as changing login and password, by issuing an `{acc}` request. Only `basic` authentication currently supports changing parameters:\n```js\nacc: {\n  id: \"1a2b3\", // string, client-provided message id, optional\n  user: \"usr2il9suCbuko\", // user being affected by the change, optional\n  token: \"XMg...g1Gp8+BO0=\", // authentication token if the session\n                             // is not yet authenticated, optional.\n  scheme: \"basic\", // authentication scheme being updated.\n  secret: base64encode(\"new_username:new_password\") // new parameters\n}\n```\nIn order to change just the password, `username` should be left empty, i.e. `secret: base64encode(\":new_password\")`.\n\nIf the session is not authenticated, the request must include a `token`. It can be a regular authentication token obtained during login, or a restricted token received through [Resetting a Password](#resetting-a-password) process. If the session is authenticated, the token must not be included. If the request is authenticated for access level `ROOT`, then the `user` may be set to a valid ID of another user. Otherwise it must be blank (defaulting to the current user) or equal to the ID of the current user.\n\n\n#### Resetting a Password, i.e. \"Forgot Password\"\n\nTo reset login or password, (or any other authentication secret, if such action is supported by the authenticator), one sends a `{login}` message with the `scheme` set to `reset` and the `secret` containing a base64-encoded string \"`authentication scheme to reset secret for`:`reset method`:`reset method value`\". Most basic case of resetting a password by email is\n```js\nlogin: {\n  id: \"1a2b3\",\n  scheme: \"reset\",\n  secret: base64encode(\"basic:email:jdoe@example.com\")\n}\n```\nwhere `jdoe@example.com` is an earlier validated user's email.\n\nIf the email matches the registration, the server will send a message using specified method and address with instructions for resetting the secret. The email contains a restricted security token which the user can include into an `{acc}` request with the new secret as described in [Changing Authentication Parameters](#changing-authentication-parameters).\n\n### Suspending a User\n\nUser's account can be suspended by service administrator. Once the account is suspended, the user is no longer able to login and use the service.\n\nOnly the `root` user may suspend the account. To suspend the account the root user sends the following message:\n```js\nacc: {\n  id: \"1a2b3\", // string, client-provided message id, optional\n  user: \"usr2il9suCbuko\", // user being affected by the change\n  status: \"susp\"\n}\n```\nSending the same message with `status: \"ok\"` un-suspends the account. A root user may check account status by executing `{get what=\"desc\"}` command against user's `me` topic.\n\n\n### Credential Validation\n\nServer may be optionally configured to require validation of certain credentials associated with the user accounts and authentication scheme. For instance, it's possible to require user to provide a unique email or a phone number, or to solve a captcha as a condition of account registration.\n\nThe server supports verification of email out of the box with just a configuration change. is mostly functional, verification of phone numbers is not functional because a commercial subscription is needed in order to be able to send text messages (SMS).\n\nIf certain credentials are required, then user must maintain them in validated state at all times. It means if a required credential has to be changed, the user must first add and validate the new credential and only then remove the old one.\n\nCredentials are initially assigned at registration time by sending an `{acc}` message, added using `{set topic=\"me\"}`, deleted using `{del topic=\"me\"}`, and queries by `{get topic=\"me\"}` messages. Credentials are verified by the client by sending either a `{login}` or an `{acc}` message.\n\n\n### Access Control\n\nAccess control manages user's access to topics through access control lists (ACLs). The access is assigned individually to each user-topic pair (subscription).\n\nAccess control is mostly usable for group topics. Its usability for `me` and P2P topics is limited to managing presence notifications and banning uses from initiating or continuing P2P conversations. All channel readers are given the same permissions.\n\nUser's access to a topic is defined by two sets of permissions: user's desired permissions \"want\", and permissions granted to user by topic's manager(s) \"given\". Each permission is represented by a bit in a bitmap. It can be either present or absent. The actual access is determined as a bitwise AND of wanted and given permissions. The permissions are communicated in messages as a set of ASCII characters, where presence of a character means a set permission bit:\n\n* No access: `N` is not a permission per se but an indicator that permissions are explicitly cleared/not set. It usually indicates that the default permissions should *not* be applied.\n* Join: `J`, permission to subscribe to a topic\n* Read: `R`, permission to receive `{data}` packets\n* Write: `W`, permission to `{pub}` to topic\n* Presence: `P`, permission to receive presence updates `{pres}`\n* Approve: `A`, permission to approve requests to join a topic, remove and ban members; a user with such permission is topic's administrator\n* Sharing: `S`, permission to invite other people to join the topic\n* Delete: `D`, permission to hard-delete messages; only owners can completely delete topics\n* Owner: `O`, user is the topic owner; the owner can assign any other permission to any topic member, change topic description, delete topic; topic may have a single owner only; some topics have no owner\n\nWhen a user subscribes to a topic or starts a chat with another user, the access permissions are either set explicitly or assigned by default `defacs`. Access permissions can be modified by sending `{set}` messages.\n\nA client may set explicit permissions in `{sub}` and `{set}` messages. If the permissions are missing or set to an empty string (not `N`!), Tinode will use default permissions `defacs` assigned earlir. If no default permissions are found, the authenticated users in group topics will receive a `JRWPS` access, in P2P topics will get `JRWPA`; anonymous users will receive `N` (no access) which means every subscription request must be explicitly approved by the topic manager.\n\nDefault access is defined for two categories of users: authenticated and anonymous. The default access value is applied as a \"given\" permission to all new subscriptions. Topic's default access is established at the topic creation time by `{sub.desc.defacs}` and can be subsequently modified by the owner by sending `{set}` messages. Likewise, user's default access is established at the account creation time by `{acc.desc.defacs}` and can be modified by the user by sending a `{set}` message to `me` topic.\n\n\n## Topics\n\nTopic is a named communication channel for one or more people. Topics have persistent properties. These topic properties can be queried by `{get what=\"desc\"}` message.\n\nTopic properties independent of the user making the query:\n* `created`: timestamp of topic creation time\n* `updated`: timestamp of when topic's `trusted`, `public`, or `private` was last updated\n* `touched`: timestamp of the last message sent to the topic\n* `defacs`: object describing topic's default access mode for authenticated and anonymous users; see [Access control](#access-control) for details\n * `auth`: default access mode for authenticated users\n * `anon`: default access for anonymous users\n* `seq`: integer server-issued sequential ID of the latest `{data}` message sent through the topic\n* `trusted`: an application-defined object issued by the system administrators. Anyone can read it but only administrators can change it.\n* `public`: an application-defined object that describes the topic. Anyone who can subscribe to topic can receive topic's `public` data, only topic `owner` can change it.\n\nUser-dependent topic properties:\n* `acs`: object describing given user's current access permissions; see [Access control](#access-control) for details\n * `want`: access permission requested by this user\n * `given`: access permissions given to this user\n* `private`: an application-defined object that is unique to the current user (topic subscriber).\n\nTopic usually have subscribers. One of the subscribers may be designated as topic owner (`O` access permission) with full access permissions. The list of subscribers can be queries with a `{get what=\"sub\"}` message. The list of subscribers is returned in a `sub` section of a `{meta}` message.\n\n### `me` Topic\n\nTopic `me` is automatically created for every user at the account creation time. It serves as means of managing account information, receiving presence notification from people and topics of interest. Topic `me` has no owner. The topic cannot be deleted or unsubscribed from. One can `leave` the topic which will stop all relevant communication and indicate that the user is offline (although the user may still be logged in and may continue to use other topics).\n\nJoining or leaving `me` generates a `{pres}` presence update sent to all users who have peer to peer topics with the given user and `P` permissions set.\n\nTopic `me` is read-only. `{pub}` messages to `me` are rejected.\n\nMessage `{get what=\"desc\"}` to `me` is automatically replied with a `{meta}` message containing `desc` section with the topic parameters (see intro to [Topics](#topics) section). The `public` parameters of `me` topic is data that the user wants to show to his/her connections. Changing it changes `public` not just for the `me` topic, but also everywhere where user's `public` is shown, such as `public` of all user's peer to peer topics.\n\nMessage `{get what=\"sub\"}` to `me` is different from any other topic as it returns the list of topics that the current user is subscribed to as opposite to the expected user's subscription to `me`.\n* seq: server-issued numeric id of the last message in the topic\n* recv: seq value self-reported by the current user as received\n* read: seq value self-reported by the current user as read\n* seen: for P2P subscriptions, timestamp of user's last presence and User Agent string are reported\n * when: timestamp when the user was last online\n * ua: user agent string of the user's client software last used\n\nMessage `{get what=\"data\"}` to `me` is rejected.\n\nInternally the `me` topics are not persisted separately from the users. The `me` topics don't exist in the `topics` table or collection, they are created in memory from the `users` database record.\n\n### `slf` Topic\n\nTopic `slf` (self) provides mechanism for storing information, like bookmarks or saved messages. Messages sent to `slf` are accessible only by the user who sent them.\n\nThis topic is created automatically when the user subscribes to it for the first time.\n\n### `fnd` and Tags: Finding Users and Topics\n\nTopic `fnd` is automatically created for every user at the account creation time. It serves as an endpoint for discovering other users and group topics. Users and group topics can be discovered by `tags`. Tags are optionally assigned at the topic or user creation time then can be updated by using `{set what=\"tags\"}` against a `me` or a group topic.\n\nA tag is an arbitrary case-insensitive Unicode string (forced to lowercase on the server) up to 96 characters long which may contain characters from `Letter` and `Number` Unicode [classes/categories](https://en.wikipedia.org/wiki/Unicode_character_property#General_Category) as well as any of the following ASCII characters: `_`, `.`, `+`, `-`, `@`, `#`, `!`, `?`.\n\nTag may have a prefix which serves as a namespace. The prefix is a 2-16 character string which starts with a letter [a-z] and may contain lowercase ASCII letters and numbers followed by a colon `:`, ex. prefixed phone tag `tel:+14155551212` or prefixed email tag `email:alice@example.com`. Some prefixed tags are optionally enforced to be unique. In that case only one user or topic may have such a tag. Certain tags may be forced to be immutable to the user, i.e. user's attempts to add or remove an immutable tag will be rejected by the server.\n\nThe tags are indexed server-side and used in user and topic discovery. Search returns users and topics sorted by the number of matched tags in descending order.\n\nIn order to find users or topics, a user sets either `public` or `private` parameter of the `fnd` topic to a search query (see [Query language](#query-language)) then issues a `{get topic=\"fnd\" what=\"sub\"}` request. If both `public` and `private` are set, the `public` query is used. The `private` query is persisted across sessions and devices, i.e. all user's sessions see the same `private` query. The value of the `public` query is ephemeral, i.e. it's not saved to database and not shared between user's sessions. The `private` query is intended for large queries which do not change often, such as finding matches for everyone in user's contact list on a mobile phone. The `public` query is intended to be short and specific, such as finding some topic or a user who is not in the contact list.\n\nThe system responds with a `{meta}` message with the `sub` section listing details of the found users or topics formatted as subscriptions.\n\nTopic `fnd` is read-only. `{pub}` messages to `fnd` are rejected.\n\n_CURRENTLY UNSUPPORTED_ When a new user registers with tags matching the given query, the `fnd` topic will receive `{pres}` notification for the new user.\n\n[Plugins](../pbx) support `Find` service which can be used to replace default search with a custom one.\n\nInternally the `fnd` topics are not persisted separately from the users. The `fnd` topics don't exist in the `topics` table or collection, they are created in memory from the `users` database record.\n\n#### Query Language\n\nTinode query language is used to define search queries for finding users and topics. The query is a string containing atomic terms separated by spaces or commas. The individual query terms are matched against user's or topic's tags. The individual terms may be written in an RTL language but the query as a whole is parsed left to right. Spaces are treated as the `AND` operator, commas (as well as commas preceded and/or followed by a space) as the `OR` operator. The order of operators is ignored: all `AND` tags are grouped together, all `OR` tags are grouped together. `OR` takes precedence over `AND`: if a tag is preceded of followed by a comma, it's an `OR` tag, otherwise an `AND`. For example, `aaa bbb, ccc` (`aaa AND bbb OR ccc`) is interpreted as `(bbb OR ccc) AND aaa`.\n\nQuery terms containing spaces must convert spaces to underscores ` ` -> `_`, e.g. `new york` -> `new_york`.\n\n**Some examples:**\n* `flowers`: find topics or users which contain tag `flowers`.\n* `flowers travel`: find topics or users which contain both tags `flowers` and `travel`.\n* `flowers, travel`: find topics or users which contain either tag `flowers` or `travel` (or both).\n* `flowers travel, puppies`: find topics or users which contain `flowers` and either `travel` or `puppies`, i.e. `(travel OR puppies) AND flowers`.\n* `flowers, travel puppies, kittens`: find topics or users which contain either one of `flowers`, `travel`, `puppies`, or `kittens`, i.e. `flowers OR travel OR puppies OR kittens`. The space between `travel` and `puppies` is treated as `OR` due to `OR` taking precedence over `AND`.\n\n#### Incremental Updates to Queries\n\n_CURRENTLY UNSUPPORTED_ Queries, particularly `fnd.private` could be arbitrarily large, limited only by the limits on the message size, and by the limits on the query size in the underlying database. Instead of rewriting the entire query to add or remove a term, terms can be added or removed incrementally.\n\nThe incremental update request is processed left to right. It may contain the same term multiple times, i.e. `-a_tag+a_tag` is a valid request.\n\n#### Query Rewrite\n\nFinding users by login, phone or email requires query terms to be written with prefixes, i.e. `email:alice@example.com` instead of `alice@example.com`. This may present a problem to end users because it requires them to learn the query language. Tinode solves this problem by implementing _query rewrite_ on the server: if query term (tag) does not contain a prefix, server rewrites it by adding the appropriate prefix. In queries to `fnd.public` the original term is also kept (query `alice@example.com` is rewritten as `email:alice@example.com OR alice@example.com`), in queries to `fnd.private` only the rewritten term is kept (`alice@example.com` is rewritten as `email:alice@example.com`). All terms that look like email, for instance, `alice@example.com` are rewritten to `email:alice@example.com OR alice@example.com`. Terms which look like phone numbers are converted to [E.164](https://en.wikipedia.org/wiki/E.164) and also rewritten as `tel:+14155551212 OR +14155551212`. In addition, in queries to `fnd.public` all other unprefixed terms which look like logins are rewritten as logins: `alice` -> `basic:alice OR alice`.\n\nAs described above, tags which look like phone numbers are converted to E.164 format. Such conversion requires an ISO 3166-1 alpha-2 country code. The following logic is used when converting phone number tags to E.164:\n* If the tag already contains a country calling code, it's used as is: `+1(415)555-1212` -> `+14155551212`.\n* If the tag has no prefix, country code is taken from the locale value set by the client in `lang` field of the `{hi}` message.\n* If client has not provided the code in the `hi.lang`, the country code is taken from `default_country_code` field of the `tinode.conf`.\n* If no `default_country_code` is set in `tinode.conf`, `US` country code is used.\n\n#### Possible Use Cases\n* Restricting users to organisations.\n  An immutable tag(s) may be assigned to the user which denotes the organisation the user belongs to. When the user searches for other users or topics, the search can be restricted to always contain the tag. This approach can be used to segment users into organisations with limited visibility into each other.\n\n* Search by geographical location.\n  Client software may periodically assign a [geohash](https://en.wikipedia.org/wiki/Geohash) tag to the user based on current location. Searching for users in a given area would mean matching on geohash tags.\n\n* Search by numerical range, such as age range.\n  The approach is similar to geohashing. The entire range of numbers is covered by the smallest possible power of 2, for instance the range of human ages is covered by 2<sup>7</sup>=128 years. The entire range is split in two halves: the range 0-63 is denoted by 0, 64-127 by 1. The operation is repeated with each subrange, i.e. 0-31 is 00, 32-63 is 01, 0-15 is 000, 32-47 is 010. Once completed, the age 30 will belong to the following ranges: 0 (0-63), 00 (0-31), 001 (16-31), 0011 (24-31), 00111 (28-31), 001111 (30-31), 0011110 (30). A 30 y.o. user is assigned a few tags to indicate the age, i.e. `age:00111`, `age:001111`,  and `age:0011110`. Technically, all 7 tags may be assigned but usually it's impractical. To query for anyone in the age range 28-35 convert the range into a minimal number of tags: `age:00111` (28-31), `age:01000` (32-35). This query will match the 30 y.o. user by tag `age:00111`.\n\n\n### Peer to Peer Topics\n\nPeer to peer (P2P) topics represent communication channels between strictly two users. The name of the topic is different for each of the two participants. Each of them sees the name of the topic as the user ID of the other participant: `usr` followed by base64 URL-encoded ID of the user. For example, if two users `usrOj0B3-gSBSs` and `usrIU_LOVwRNsc` start a P2P topic, the first one will see it as `usrIU_LOVwRNsc`, the second as `usrOj0B3-gSBSs`. The P2P topic has no owner.\n\nA P2P topic is created by one user subscribing to topic with the name equal to the ID of the other user. For instance, user `usrOj0B3-gSBSs` can establish a P2P topic with user `usrIU_LOVwRNsc` by sending a `{sub topic=\"usrIU_LOVwRNsc\"}`. Tinode will respond with a `{ctrl}` packet with the name of the newly created topic as described above. The other user will receive a `{pres}` message on `me` topic with updated access permissions.\n\nInternally, P2P topics are stored as `p2p` followed by base64 URL encoded concatenation of two 64-bit user IDs, with the lower numeric value ID first: `p2pm7PvMGmdcx_uVkDRaSTbwA`.\n\nThe 'public' parameter of P2P topics is user-dependent. For instance a P2P topic between users A and B would show user A's 'public' to user B and vice versa. If a user updates 'public', all user's P2P topics will automatically update 'public' too.\n\nThe 'private' parameter of a P2P topic is defined by each participant individually as with any other topic type.\n\n### Group Topics\n\nGroup topic represents a communication channel between multiple users. The name of a group topic is `grp` or `chn` followed by a string of characters from base64 URL-encoding set. No other assumptions can be made about internal structure or length of the group name.\n\nGroup topics support limited number of subscribers (controlled by a `max_subscriber_count` parameter in configuration file) with access permissions of each subscriber managed individually. Group topics may also be enabled to support any number of read-only users - `readers`. All `readers` have the same access permissions. Group topics with enabled `readers` are called `channels`.\n\nA group topic is created by sending a `{sub}` message with the topic field set to string `new` or `nch` optionally followed by any characters, e.g. `new` or `newAbC123` are equivalent. Tinode will respond with a `{ctrl}` message with the name of the newly created topic, i.e. `{sub topic=\"new\"}` is replied with `{ctrl topic=\"grpmiKBkQVXnm3P\"}`. If topic creation fails, the error is reported on the original topic name, i.e. `new` or `newAbC123`. The user who created the topic becomes topic owner. Ownership can be transferred to another user with a `{set}` message but one user must remain the owner at all times.\n\nA `channel` topic is different from the non-channel group topic in the following ways:\n\n * Channel topic is created by sending `{sub topic=\"nch\"}`. Sending `{sub topic=\"new\"}` will create a group topic without enabling channel functionality.\n * Sending `{sub topic=\"chnAbC123\"}` will create a `reader` subscription to a channel. A non-channel topic will reject such subscription request.\n * When searching for topics using [`fnd`](#fnd-and-tags-finding-users-and-topics), channels will show addresses with `chn` prefixes, non-channel topic will show with `grp` prefixes.\n * Messages received by readers on channels have no `From` field. Normal subscribers will receive messages with `From` containing ID of the sender.\n * Default permissions for a channel and non-channel group topics are different: channel group topic grants no permissions at all.\n * A subscriber joining or leaving the topic (regular or channel-enabled) generates a `{pres}` message to all other subscribers who are currently in the joined state with the topic and have appropriate permissions. Reader joining or leaving the channel generates no `{pres}` message.\n\n### `sys` Topic\n\nThe `sys` topic serves as an always available channel of communication with the system administrators. A normal non-root user cannot subscribe to `sys` but can publish to it without subscription. Existing clients use this channel to report abuse by sending a Drafty-formatted `{pub}` message with the report as JSON attachment. A root user can subscribe to `sys` topic. Once subscribed, the root user will receive messages sent to `sys` topic by other users.\n\n## Using Server-Issued Message IDs\n\nTinode provides basic support for client-side caching of `{data}` messages in the form of server-issued sequential message IDs. The client may request the last message id from the topic by issuing a `{get what=\"desc\"}` message. If the returned ID is greater than the ID of the latest received message, the client knows that the topic has unread messages and their count. The client may fetch these messages using `{get what=\"data\"}` message. The client may also paginate history retrieval by using message IDs.\n\n## User Agent and Presence Notifications\n\nA user is reported as being online when one or more of user's sessions are attached to the `me` topic. Client-side software identifies itself to the server using `ua` (user agent) field of the `{login}` message. The _user agent_ is published in `{meta}` and `{pres}` messages in the following way:\n\n * When user's first session attaches to `me`, the _user agent_ from that session is broadcast in the `{pres what=\"on\" ua=\"...\"}` message.\n * When multiple user sessions are attached to `me`, the _user agent_ of the session where the most recent action has happened is reported in `{pres what=\"ua\" ua=\"...\"}`; the 'action' in this context means any message sent by the client. To avoid potentially excessive traffic, user agent changes are broadcast no more frequently than once a minute.\n * When user's last session detaches from `me`, the _user agent_ from that session is recorded together with the timestamp; the user agent is broadcast in the `{pres what=\"off\"  ua=\"...\"}` message and subsequently reported as the last online timestamp and user agent.\n\nAn empty `ua=\"\"` _user agent_ is not reported. I.e. if user attaches to `me` with non-empty _user agent_ then does so with an empty one, the change is not reported. An empty _user agent_ may be disallowed in the future.\n\n## Trusted, Public, Private, Auxiliary Fields\n\nTopics have `trusted`, `public`, `aux` fields, subscriptions have `private` fields. The primary difference between these fields is in access control:\n\n * `trusted`: writable by `ROOT` users, readable by anyone.\n * `public`: writable by the `owner` or the user, readable by anyone.\n * `aux`: writable by topic administrators, readable by subscribers.\n * `private`: readable and writable only by the user who created the subscription.\n\nGenerally, the fields are application-defined. The server does not enforce any particular structure of these fields except for `fnd` topic. At the same time, client software should use the same format for interoperability reasons. The following sections describe the format of these fields as they are implemented by all official clients.\n\nAlthough it's not yet enforced, if a third-party application defines custom keys, the key names should start with an `x-` followed by the application's fully qualified domain name, e.g. `x-example.com-value: \"abc\"`. The fields should contain primitive types only, i.e. `string`, `boolean`, `number`, or `null`.\n\n### Trusted\n\nThe format of the optional `trusted` field in group and peer to peer topics is a set of key-value pairs; `fnd` and `sys` topics do not have the `trusted`. The field is writable by `ROOT` users, readable by anyone who has access to the topic or user. The following optional keys are currently defined:\n```js\ntrusted: {\n  verified: true, // boolean, an indicator of a verified/trustworthy user or topic.\n  staff: true,    // boolean, an indicator that the user or topic\n                  // is a part of/belongs to the server administration.\n  danger: true    // boolean, an indicator that the user or topic are untrustworthy.\n}\n```\n\n### Public\n\nThe format of the `public` field in group, peer to peer, systems topics is expected to be [theCard](./thecard.md). The field is writable by by the user for users, the topic owner for topics. The field is readable by anyone who has access to topic or user.\n\nThe `fnd` topic expects `public` to be a string representing a [search query](#query-language)).\n\n### Private\n\nThe format of the `private` field in group and peer to peer topics is a set of key-value pairs. The field is writable and readable by the user only. The following keys are currently defined:\n```js\nprivate: {\n  comment: \"some comment\", // string, optional user comment about a topic or a peer user\n  arch: true, // boolean, indicator that the topic is archived by the user, i.e.\n              // should not be shown in the UI with other non-archived topics.\n  accepted: \"JRWS\", // string, 'given' mode accepted by the user.\n  tpins: [\"grpmiKBkQVXnm3P\", \"usrIU_LOVwRNsc\"] // array of topic IDs to pin to the top of\n              // the contacts list; 'me' topic only.\n}\n```\n\nThe `fnd` topic expects `private` to be a string representing a [search query](#query-language)).\n\n### Auxiliary\n\nThe format of the `aux` field is a set of key-value pairs. The `aux` is writable by topic admins and readable by all topic subscribers. The following keys are currently defined:\n\n```js\naux: {\n  pins: [1001, 23456] // array of integer message IDs to pin to the top of the message list.\n}\n```\n\n## Format of Content\n\nFormat of `content` field in `{pub}` and `{data}` is application-defined and as such the server does not enforce any particular structure of the field. At the same time, client software should use the same format for interoperability reasons. Currently the following two types of `content` are supported:\n * Plain text\n * [Drafty](./drafty.md)\n\nIf Drafty is used, a message header `\"head\": {\"mime\": \"text/x-drafty\"}` must be set.\n\n\n## Out-of-Band Handling of Large Files\n\nLarge files create problems when sent in-band for multiple reasons:\n * limits on database storage as in-band messages are stored in database fields\n * in-band messages must be downloaded completely as a part of downloading chat history\n\nTinode provides two endpoints for handling large files: `/v0/file/u` for uploading files and `v0/file/s` for downloading. The endpoints require the client to provide both [API key](#connecting-to-the-server) and login credentials. The server checks credentials in the following order:\n\n**Login credentials**\n * HTTP header `Authorization` (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization)\n * URL query parameters `auth` and `secret` (/v0/file/s/abcdefg.jpeg?auth=...&secret=...)\n * Form values `auth` and `secret`\n * Cookies `auth` and `secret`\n\n### Uploading\n\nTo upload a file first create an RFC 2388 multipart request then send it to the server using HTTP POST. The server responds to the request either with a `307 Temporary Redirect` with the new upload URL, or with a `200 OK` and a `{ctrl}` message in response body:\n\n```js\nctrl: {\n  params: {\n    url: \"/v0/file/s/mfHLxDWFhfU.pdf\"\n  },\n  code: 200,\n  text: \"ok\",\n  ts: \"2018-07-06T18:47:51.265Z\"\n}\n```\nIf `307 Temporary Redirect` is returned, the client must retry the upload at the provided URL. The URL returned in `307` response should be used for just this one upload. All subsequent uploads should try the default URL first.\n\nThe `ctrl.params.url` contains the path to the uploaded file at the current server. It could be either the full path like `/v0/file/s/mfHLxDWFhfU.pdf`, a relative path like `./mfHLxDWFhfU.pdf`, or just the file name `mfHLxDWFhfU.pdf`. Anything but the full path is interpreted against the default *download* endpoint `/v0/file/s/`. For instance, if `mfHLxDWFhfU.pdf` is returned then the file is located at `http(s)://current-tinode-server/v0/file/s/mfHLxDWFhfU.pdf`.\n\nOnce the URL of the file is received, either immediately or after following the redirect, the client may use the URL to send a `{pub}` message with the uploaded file as an attachment, or, if the file is an image, as an avatar image for a topic or user profile (see [theCard](./thecard.md)). For example, the URL can be used in a [Drafty](./drafty.md)-formatted `pub.content` field:\n\n```js\n{\n  pub: {\n    id: \"121103\",\n    topic: \"grpnG99YhENiQU\",\n    head: {\n      mime: \"text/x-drafty\"\n    },\n    content: {\n      ent: [\n      {\n        data: {\n        mime: \"image/jpeg\",\n        name: \"roses-are-red.jpg\",\n        ref:  \"/v0/file/s/sJOD_tZDPz0.jpg\",\n        size: 437265\n      },\n        tp: \"EX\"\n      }\n    ],\n    fmt: [\n      {\n        at: -1,\n      key:0,\n      len:1\n      }\n    ]\n    }\n  },\n  extra: {\n    attachments: [\"/v0/file/s/sJOD_tZDPz0.jpg\"]\n  }\n}\n```\n\nIt's important to list the used URLs in the `extra: attachments[...]` field. Tinode server uses this field to maintain the uploaded file's use counter. Once the counter drops to zero for the given file (for instance, because a message with the shared URL was deleted or because the client failed to include the URL in the `extra.attachments` field), the server will garbage collect the file. Only relative URLs should be used. Absolute URLs in the `extra.attachments` field are ignored. The URL value is expected to be the `ctrl.params.url` returned in response to upload.\n\n### Downloading\n\nThe serving endpoint `/v0/file/s` serves files in response to HTTP GET requests. The client must evaluate relative URLs against this endpoint, i.e. if it receives a URL `mfHLxDWFhfU.pdf` or `./mfHLxDWFhfU.pdf` it should interpret it as a path `/v0/file/s/mfHLxDWFhfU.pdf` at the current Tinode HTTP server.\n\n_Important!_ As a security measure, the client should not send security credentials if the download URL is absolute and leads to another server.\n\n## Push Notifications\n\nTinode uses compile-time adapters for handling push notifications. The server comes with [Tinode Push Gateway](../server/push/tnpg/), [Google FCM](https://firebase.google.com/docs/cloud-messaging/), and `stdout` adapters. Tinode Push Gateway and Google FCM support Android with [Play Services](https://developers.google.com/android/guides/overview) (may not be supported by some Chinese phones), iOS devices and all major web browsers excluding Safari. The `stdout` adapter does not actually send push notifications. It's mostly useful for debugging, testing and logging. Other types of push notifications such as [TPNS](https://intl.cloud.tencent.com/product/tpns) can be handled by writing appropriate adapters.\n\nIf you are writing a custom plugin, the notification payload is the following:\n```js\n{\n  topic: \"grpnG99YhENiQU\", // Topic which"},"items":[{"name":"API.md","path":"docs/API.md","rawUrl":"https://raw.githubusercontent.com/tinode/chat/HEAD/docs/API.md","title":"docs - API Interface Contract & Specifications","category":"project-spec","format":"markdown","content":"<!-- TOC depthfrom:1 depthto:6 withlinks:true updateonsave:true orderedlist:false -->\n\n- [Server API](#server-api)\n  - [How it Works?](#how-it-works)\n  - [General Considerations](#general-considerations)\n  - [Connecting to the Server](#connecting-to-the-server)\n    - [gRPC](#grpc)\n    - [WebSocket](#websocket)\n    - [Long Polling](#long-polling)\n    - [Out of Band Large Files](#out-of-band-large-files)\n    - [Running Behind a Reverse Proxy](#running-behind-a-reverse-proxy)\n  - [Users](#users)\n    - [Authentication](#authentication)\n      - [Creating an Account](#creating-an-account)\n      - [Logging in](#logging-in)\n      - [Changing Authentication Parameters](#changing-authentication-parameters)\n      - [Resetting a Password, i.e. \"Forgot Password\"](#resetting-a-password-ie-forgot-password)\n    - [Suspending a User](#suspending-a-user)\n    - [Credential Validation](#credential-validation)\n    - [Access Control](#access-control)\n  - [Topics](#topics)\n    - [me Topic](#me-topic)\n    - [fnd and Tags: Finding Users and Topics](#fnd-and-tags-finding-users-and-topics)\n      - [Query Language](#query-language)\n      - [Incremental Updates to Queries](#incremental-updates-to-queries)\n      - [Query Rewrite](#query-rewrite)\n      - [Possible Use Cases](#possible-use-cases)\n    - [Peer to Peer Topics](#peer-to-peer-topics)\n    - [Group Topics](#group-topics)\n    - [sys Topic](#sys-topic)\n  - [Using Server-Issued Message IDs](#using-server-issued-message-ids)\n  - [User Agent and Presence Notifications](#user-agent-and-presence-notifications)\n  - [Trusted, Public, Private, Auxiliary Fields](#trusted-public-private-auxiliary-fields)\n    - [Trusted](#trusted)\n    - [Public](#public)\n    - [Private](#private)\n    - [Auxiliary](#auxiliary)\n  - [Format of Content](#format-of-content)\n  - [Out-of-Band Handling of Large Files](#out-of-band-handling-of-large-files)\n    - [Uploading](#uploading)\n    - [Downloading](#downloading)\n  - [Push Notifications](#push-notifications)\n    - [Tinode Push Gateway](#tinode-push-gateway)\n    - [Google FCM](#google-fcm)\n    - [Stdout](#stdout)\n  - [Video Calls](#video-calls)\n  - [Link Previews](#link-previews)\n  - [Messages](#messages)\n    - [Client to Server Messages](#client-to-server-messages)\n      - [{hi}](#hi)\n      - [{acc}](#acc)\n      - [{login}](#login)\n      - [{sub}](#sub)\n      - [{leave}](#leave)\n      - [{pub}](#pub)\n      - [{get}](#get)\n      - [{set}](#set)\n      - [{del}](#del)\n      - [{note}](#note)\n    - [Server to Client Messages](#server-to-client-messages)\n      - [{data}](#data)\n      - [{ctrl}](#ctrl)\n      - [{meta}](#meta)\n      - [{pres}](#pres)\n      - [{info}](#info)\n\n<!-- /TOC -->\n\n# Server API\n\n## How it Works?\n\nTinode is an IM router and a store. Conceptually it loosely follows a [publish-subscribe](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) model.\n\nServer connects sessions, users, and topics. Session is a network connection between a client application and the server. User represents a human being who connects to the server with a session. Topic is a named communication channel which routes content between sessions.\n\nUsers and topics are assigned unique IDs. User ID is a string with 'usr' prefix followed by base64-URL-encoded pseudo-random 64-bit number, e.g. `usr2il9suCbuko`. Topic IDs are described below.\n\nClients such as mobile or web applications create sessions by connecting to the server over a websocket or through long polling. Client authentication is required in order to perform most operations. Client authenticates the session by sending a `{login}` packet. See [Authentication](#authentication) section for details. Once authenticated, the client receives a token which is used for authentication later. Multiple simultaneous sessions may be established by the same user. Logging out is not supported (and not needed).\n\nOnce the session is established, the user can start interacting with other users through topics. The following\ntopic types are available:\n\n* `me` is a topic for managing one's profile and receiving notifications about other topics; `me` topic exists for every user.\n* `fnd` topic is used for finding other users and topics; `fnd` topic also exists for every user.\n* Peer to peer topic is a communication channel strictly between two users. Each participant sees topic name as the ID of the other participant: 'usr' prefix followed by a base64-URL-encoded numeric part of user ID, e.g. `usr2il9suCbuko`.\n* Group topic is a channel for multi-user communication. It's named as 'grp' followed by 11 pseudo-random characters, i.e. `grpYiqEXb4QY6s`. Group topics must be explicitly created.\n\nSession joins a topic by sending a `{sub}` packet. Packet `{sub}` serves three functions: creating a new topic, subscribing user to a topic, and attaching session to a topic. See [`{sub}`](#sub) section below for details.\n\nOnce the session has joined the topic, the user may start generating content by sending `{pub}` packets. The content is delivered to other attached sessions as `{data}` packets.\n\nThe user may query or update topic metadata by sending `{get}` and `{set}` packets.\n\nChanges to topic metadata, such as changes in topic description, or when other users join or leave the topic, is reported to live sessions with `{pres}` (presence) packet. The `{pres}` packet is sent either to the topic being affected or to the `me` topic.\n\nWhen user's `me` topic comes online (i.e. an authenticated session attaches to `me` topic), a `{pres}` packet is sent to `me` topics of all other users, who have peer to peer subscriptions with the first user.\n\n## General Considerations\n\nTimestamps are always represented as [RFC 3339](http://tools.ietf.org/html/rfc3339)-formatted string with precision up to milliseconds and timezone always set to UTC, e.g. `\"2015-10-06T18:07:29.841Z\"`.\n\nWhenever base64 encoding is mentioned, it means base64 URL encoding with padding characters stripped, see [RFC 4648](http://tools.ietf.org/html/rfc4648).\n\nThe `{data}` packets have server-issued sequential IDs: base-10 numbers starting at 1 and incrementing by one with every message. They are guaranteed to be unique per topic.\n\nIn order to connect requests to responses, client may assign message IDs to all packets set to the server. These IDs are strings defined by the client. Client should make them unique at least per session. The client-assigned IDs are not interpreted by the server, they are returned to the client as is.\n\n## Connecting to the Server\n\nThere are three ways to access the server over the network: websocket, long polling, and [gRPC](https://grpc.io/).\n\nWhen the client establishes a connection to the server over HTTP(S), such as over a websocket or long polling, the server offers the following endpoints:\n * `/v0/channels` for websocket connections\n * `/v0/channels/lp` for long polling\n * `/v0/file/u` for file uploads\n * `/v0/file/s` for serving files (downloads)\n\n`v0` denotes API version (currently zero). Every HTTP(S) request must include the API key. The server checks for the API key in the following order:\n* HTTP header `X-Tinode-APIKey`\n* URL query parameter `apikey` (/v0/file/s/abcdefg.jpeg?apikey=...)\n* Form value `apikey`\n* Cookie `apikey`\n\nA default API key is included with every demo app for convenience. Generate your own key for production using [`keygen` utility](../keygen).\n\nOnce the connection is opened, the client must issue a `{hi}` message to the server. Server responds with a `{ctrl}` message which indicates either success or an error. The `params` field of the response contains server's protocol version `\"params\":{\"ver\":\"0.15\"}` and may include other values.\n\n### gRPC\n\nSee definition of the gRPC API in the [proto file](../pbx/model.proto). gRPC API has slightly more functionality than the API described in this document: it allows the `root` user to send messages on behalf of other users as well as delete users.\n\nThe `bytes` fields in protobuf messages expect JSON-encoded UTF-8 content. For example, a string should be quoted before being converted to bytes as UTF-8: `[]byte(\"\\\"some string\\\"\")` (Go), `'\"another string\"'.encode('utf-8')` (Python 3).\n\n### WebSocket\n\nMessages are sent in text frames, one message per frame. Binary frames are reserved for future use. By default server allows connections with any value in the `Origin` header.\n\n### Long Polling\n\nLong polling works over `HTTP POST` (preferred) or `GET`. In response to client's very first request server sends a `{ctrl}` message containing `sid` (session ID) in `params`. Long polling client must include `sid` in every subsequent request either in the URL or in the request body.\n\nServer allows connections from all origins, i.e. `Access-Control-Allow-Origin: *`\n\n### Out of Band Large Files\n\nLarge files are sent out of band using `HTTP POST` as `Content-Type: multipart/form-data`. See [below](#out-of-band-handling-of-large-files) for details.\n\n### Running Behind a Reverse Proxy\n\nTinode server can be set up to run behind a reverse proxy, such as NGINX. For efficiency it can accept client connections from Unix sockets by setting `listen` and/or `grpc_listen` config parameters to the path of the Unix socket file, e.g. `unix:/run/tinode.sock`. The server may also be configured to read peer's IP address from `X-Forwarded-For` HTTP header by setting `use_x_forwarded_for` config parameter to `true`.\n\n## Users\n\nUser is meant to represent a person, an end-user: producer and consumer of messages.\n\nUsers are generally assigned one of the two authentication levels: authenticated `auth` or anonymous `anon`. The third level `root` is only accessible over `gRPC` where it permits the `root` to send messages on behalf of other users.\n\nWhen a connection is first established, the client application can send either an `{acc}` or a `{login}` message which authenticates the user at one the levels.\n\nEach user is assigned a unique ID. The IDs are composed as `usr` followed by base64-encoded 64-bit numeric value, e.g. `usr2il9suCbuko`. Users also have the following properties:\n\n* `created`: timestamp when the user record was created\n* `updated`: timestamp of when user's `public` or `trusted` was last updated\n* `status`: state of the account\n* `username`: unique string used in `basic` authentication; username is not accessible to other users\n* `defacs`: object describing user's default access mode for peer to peer conversations with authenticated and anonymous users; see [Access control](#access-control) for details\n  * `auth`: default access mode for authenticated `auth` users\n  * `anon`: default access for anonymous `anon` users\n* `trusted`: an application-defined object issued by the system administration. Anyone can read it but only system administrators can change it.\n* `public`: an application-defined object that describes the user. Anyone can query user for `public` data.\n* `private`: an application-defined object that is unique to the current user and accessible only by the user.\n* `tags`: [discovery](#fnd-and-tags-finding-users-and-topics) and credentials.\n\nUser's account has a state. The following states are defined:\n * `ok` (normal): the default state which means the account is not restricted in any way and can be used normally;\n * `susp` (suspended): the user is prevented from accessing the account as well as not found through [search](#fnd-and-tags-finding-users-and-topics); the state can be assigned by the administrator and fully reversible.\n * `del` (soft-deleted): user is marked as deleted but user's data is retained; un-deleting the user is not currenly supported.\n * `undef` (undefined): used internally by authenticators; should not be used elsewhere.\n\nA user may maintain multiple simultaneous connections (sessions) with the server. Each session is tagged with a client-provided `User Agent` string intended to differentiate client software.\n\nLogging out is not supported by design. If an application needs to change the user, it should open a new connection and authenticate it with the new user credentials.\n\n\n### Authentication\n\nAuthentication is conceptually similar to [SASL](https://en.wikipedia.org/wiki/Simple_Authentication_and_Security_Layer): it's provided as a set of adapters each implementing a different authentication method. Authenticators are used during account registration [`{acc}`](#acc) and during [`{login}`](#login). The server comes with the following authentication methods out of the box:\n\n * `token` provides authentication by a cryptographic token.\n * `basic` provides authentication by a login-password pair.\n * `anonymous` is designed for cases where users are temporary, such as handling customer support requests through chat.\n * `rest` is a [meta-method](../server/auth/rest/) which allows use of external authentication systems by means of JSON RPC.\n\nAny other authentication method can be implemented using adapters.\n\nThe `token` is intended to be the primary means of authentication. Tokens are designed in such a way that token authentication is light weight. For instance, token authenticator generally does not make any database calls, all processing is done in-memory. All other authentication methods are intended to be used only to obtain or refresh the token. Once the token is obtained, subsequent logins should use it.\n\nThe `basic` authentication scheme expects `secret` to be a base64-encoded string of a string composed of a user name followed by a colon `:` followed by a plan text password. User name in the `basic` scheme must not contain the colon character `:` (ASCII 0x3A).\n\nThe `anonymous` scheme can be used to create accounts, it cannot be used for logging in: a user creates an account using `anonymous` scheme and obtains a cryptographic token which it uses for subsequent `token` logins. If the token is lost or expired, the user is no longer able to access the account.\n\nCompiled-in authenticator names may be changed by using `logical_names` configuration feature. For example, a custom `rest` authenticator may be exposed as `basic` instead of default one or `token` authenticator could be hidden from users. The feature is activated by providing an array of mappings in the config file: `logical_name:actual_name` to rename or `actual_name:` to hide. For instance, to use a `rest` service for basic authentication use `\"logical_names\": [\"basic:rest\"]`.\n\n\n#### Creating an Account\n\nWhen a new account is created, the user must inform the server which authentication method will be later used to gain access to this account as well as provide shared secret, if appropriate. Only `basic` and `anonymous` can be used during account creation. The `basic` requires the user to generate and send a unique login and password to the server. The `anonymous` does not exchange secrets.\n\nUser may optionally set `{acc login=true}` to use the new account for immediate authentication. When `login=false` (or not set), the new account is created but the authentication status of the session which created the account remains unchanged. When `login=true` the server will attempt to authenticate the session with the new account, the `{ctrl}` response to the `{acc}` request will contain the authentication token on success. This is particularly important for the `anonymous` authentication because that's the only time when the authentication token can be retrieved.\n\n#### Logging in\n\nLogging in is performed by issuing a `{login}` request. Logging in is possible with `basic` and `token` only. Response to any login is a `{ctrl}` message with either a code 200 and a token which can be used in subsequent logins with `token` authentication, or a code 300 request for additional information, such as verifying credentials or responding to a method-dependent challenge in multi-step authentication, or a code 4xx error.\n\nToken has server-configured expiration time so it needs to be periodically refreshed.\n\n#### Changing Authentication Parameters\n\nUser may change authentication parameters, such as changing login and password, by issuing an `{acc}` request. Only `basic` authentication currently supports changing parameters:\n```js\nacc: {\n  id: \"1a2b3\", // string, client-provided message id, optional\n  user: \"usr2il9suCbuko\", // user being affected by the change, optional\n  token: \"XMg...g1Gp8+BO0=\", // authentication token if the session\n                             // is not yet authenticated, optional.\n  scheme: \"basic\", // authentication scheme being updated.\n  secret: base64encode(\"new_username:new_password\") // new parameters\n}\n```\nIn order to change just the password, `username` should be left empty, i.e. `secret: base64encode(\":new_password\")`.\n\nIf the session is not authenticated, the request must include a `token`. It can be a regular authentication token obtained during login, or a restricted token received through [Resetting a Password](#resetting-a-password) process. If the session is authenticated, the token must not be included. If the request is authenticated for access level `ROOT`, then the `user` may be set to a valid ID of another user. Otherwise it must be blank (defaulting to the current user) or equal to the ID of the current user.\n\n\n#### Resetting a Password, i.e. \"Forgot Password\"\n\nTo reset login or password, (or any other authentication secret, if such action is supported by the authenticator), one sends a `{login}` message with the `scheme` set to `reset` and the `secret` containing a base64-encoded string \"`authentication scheme to reset secret for`:`reset method`:`reset method value`\". Most basic case of resetting a password by email is\n```js\nlogin: {\n  id: \"1a2b3\",\n  scheme: \"reset\",\n  secret: base64encode(\"basic:email:jdoe@example.com\")\n}\n```\nwhere `jdoe@example.com` is an earlier validated user's email.\n\nIf the email matches the registration, the server will send a message using specified method and address with instructions for resetting the secret. The email contains a restricted security token which the user can include into an `{acc}` request with the new secret as described in [Changing Authentication Parameters](#changing-authentication-parameters).\n\n### Suspending a User\n\nUser's account can be suspended by service administrator. Once the account is suspended, the user is no longer able to login and use the service.\n\nOnly the `root` user may suspend the account. To suspend the account the root user sends the following message:\n```js\nacc: {\n  id: \"1a2b3\", // string, client-provided message id, optional\n  user: \"usr2il9suCbuko\", // user being affected by the change\n  status: \"susp\"\n}\n```\nSending the same message with `status: \"ok\"` un-suspends the account. A root user may check account status by executing `{get what=\"desc\"}` command against user's `me` topic.\n\n\n### Credential Validation\n\nServer may be optionally configured to require validation of certain credentials associated with the user accounts and authentication scheme. For instance, it's possible to require user to provide a unique email or a phone number, or to solve a captcha as a condition of account registration.\n\nThe server supports verification of email out of the box with just a configuration change. is mostly functional, verification of phone numbers is not functional because a commercial subscription is needed in order to be able to send text messages (SMS).\n\nIf certain credentials are required, then user must maintain them in validated state at all times. It means if a required credential has to be changed, the user must first add and validate the new credential and only then remove the old one.\n\nCredentials are initially assigned at registration time by sending an `{acc}` message, added using `{set topic=\"me\"}`, deleted using `{del topic=\"me\"}`, and queries by `{get topic=\"me\"}` messages. Credentials are verified by the client by sending either a `{login}` or an `{acc}` message.\n\n\n### Access Control\n\nAccess control manages user's access to topics through access control lists (ACLs). The access is assigned individually to each user-topic pair (subscription).\n\nAccess control is mostly usable for group topics. Its usability for `me` and P2P topics is limited to managing presence notifications and banning uses from initiating or continuing P2P conversations. All channel readers are given the same permissions.\n\nUser's access to a topic is defined by two sets of permissions: user's desired permissions \"want\", and permissions granted to user by topic's manager(s) \"given\". Each permission is represented by a bit in a bitmap. It can be either present or absent. The actual access is determined as a bitwise AND of wanted and given permissions. The permissions are communicated in messages as a set of ASCII characters, where presence of a character means a set permission bit:\n\n* No access: `N` is not a permission per se but an indicator that permissions are explicitly cleared/not set. It usually indicates that the default permissions should *not* be applied.\n* Join: `J`, permission to subscribe to a topic\n* Read: `R`, permission to receive `{data}` packets\n* Write: `W`, permission to `{pub}` to topic\n* Presence: `P`, permission to receive presence updates `{pres}`\n* Approve: `A`, permission to approve requests to join a topic, remove and ban members; a user with such permission is topic's administrator\n* Sharing: `S`, permission to invite other people to join the topic\n* Delete: `D`, permission to hard-delete messages; only owners can completely delete topics\n* Owner: `O`, user is the topic owner; the owner can assign any other permission to any topic member, change topic description, delete topic; topic may have a single owner only; some topics have no owner\n\nWhen a user subscribes to a topic or starts a chat with another user, the access permissions are either set explicitly or assigned by default `defacs`. Access permissions can be modified by sending `{set}` messages.\n\nA client may set explicit permissions in `{sub}` and `{set}` messages. If the permissions are missing or set to an empty string (not `N`!), Tinode will use default permissions `defacs` assigned earlir. If no default permissions are found, the authenticated users in group topics will receive a `JRWPS` access, in P2P topics will get `JRWPA`; anonymous users will receive `N` (no access) which means every subscription request must be explicitly approved by the topic manager.\n\nDefault access is defined for two categories of users: authenticated and anonymous. The default access value is applied as a \"given\" permission to all new subscriptions. Topic's default access is established at the topic creation time by `{sub.desc.defacs}` and can be subsequently modified by the owner by sending `{set}` messages. Likewise, user's default access is established at the account creation time by `{acc.desc.defacs}` and can be modified by the user by sending a `{set}` message to `me` topic.\n\n\n## Topics\n\nTopic is a named communication channel for one or more people. Topics have persistent properties. These topic properties can be queried by `{get what=\"desc\"}` message.\n\nTopic properties independent of the user making the query:\n* `created`: timestamp of topic creation time\n* `updated`: timestamp of when topic's `trusted`, `public`, or `private` was last updated\n* `touched`: timestamp of the last message sent to the topic\n* `defacs`: object describing topic's default access mode for authenticated and anonymous users; see [Access control](#access-control) for details\n * `auth`: default access mode for authenticated users\n * `anon`: default access for anonymous users\n* `seq`: integer server-issued sequential ID of the latest `{data}` message sent through the topic\n* `trusted`: an application-defined object issued by the system administrators. Anyone can read it but only administrators can change it.\n* `public`: an application-defined object that describes the topic. Anyone who can subscribe to topic can receive topic's `public` data, only topic `owner` can change it.\n\nUser-dependent topic properties:\n* `acs`: object describing given user's current access permissions; see [Access control](#access-control) for details\n * `want`: access permission requested by this user\n * `given`: access permissions given to this user\n* `private`: an application-defined object that is unique to the current user (topic subscriber).\n\nTopic usually have subscribers. One of the subscribers may be designated as topic owner (`O` access permission) with full access permissions. The list of subscribers can be queries with a `{get what=\"sub\"}` message. The list of subscribers is returned in a `sub` section of a `{meta}` message.\n\n### `me` Topic\n\nTopic `me` is automatically created for every user at the account creation time. It serves as means of managing account information, receiving presence notification from people and topics of interest. Topic `me` has no owner. The topic cannot be deleted or unsubscribed from. One can `leave` the topic which will stop all relevant communication and indicate that the user is offline (although the user may still be logged in and may continue to use other topics).\n\nJoining or leaving `me` generates a `{pres}` presence update sent to all users who have peer to peer topics with the given user and `P` permissions set.\n\nTopic `me` is read-only. `{pub}` messages to `me` are rejected.\n\nMessage `{get what=\"desc\"}` to `me` is automatically replied with a `{meta}` message containing `desc` section with the topic parameters (see intro to [Topics](#topics) section). The `public` parameters of `me` topic is data that the user wants to show to his/her connections. Changing it changes `public` not just for the `me` topic, but also everywhere where user's `public` is shown, such as `public` of all user's peer to peer topics.\n\nMessage `{get what=\"sub\"}` to `me` is different from any other topic as it returns the list of topics that the current user is subscribed to as opposite to the expected user's subscription to `me`.\n* seq: server-issued numeric id of the last message in the topic\n* recv: seq value self-reported by the current user as received\n* read: seq value self-reported by the current user as read\n* seen: for P2P subscriptions, timestamp of user's last presence and User Agent string are reported\n * when: timestamp when the user was last online\n * ua: user agent string of the user's client software last used\n\nMessage `{get what=\"data\"}` to `me` is rejected.\n\nInternally the `me` topics are not persisted separately from the users. The `me` topics don't exist in the `topics` table or collection, they are created in memory from the `users` database record.\n\n### `slf` Topic\n\nTopic `slf` (self) provides mechanism for storing information, like bookmarks or saved messages. Messages sent to `slf` are accessible only by the user who sent them.\n\nThis topic is created automatically when the user subscribes to it for the first time.\n\n### `fnd` and Tags: Finding Users and Topics\n\nTopic `fnd` is automatically created for every user at the account creation time. It serves as an endpoint for discovering other users and group topics. Users and group topics can be discovered by `tags`. Tags are optionally assigned at the topic or user creation time then can be updated by using `{set what=\"tags\"}` against a `me` or a group topic.\n\nA tag is an arbitrary case-insensitive Unicode string (forced to lowercase on the server) up to 96 characters long which may contain characters from `Letter` and `Number` Unicode [classes/categories](https://en.wikipedia.org/wiki/Unicode_character_property#General_Category) as well as any of the following ASCII characters: `_`, `.`, `+`, `-`, `@`, `#`, `!`, `?`.\n\nTag may have a prefix which serves as a namespace. The prefix is a 2-16 character string which starts with a letter [a-z] and may contain lowercase ASCII letters and numbers followed by a colon `:`, ex. prefixed phone tag `tel:+14155551212` or prefixed email tag `email:alice@example.com`. Some prefixed tags are optionally enforced to be unique. In that case only one user or topic may have such a tag. Certain tags may be forced to be immutable to the user, i.e. user's attempts to add or remove an immutable tag will be rejected by the server.\n\nThe tags are indexed server-side and used in user and topic discovery. Search returns users and topics sorted by the number of matched tags in descending order.\n\nIn order to find users or topics, a user sets either `public` or `private` parameter of the `fnd` topic to a search query (see [Query language](#query-language)) then issues a `{get topic=\"fnd\" what=\"sub\"}` request. If both `public` and `private` are set, the `public` query is used. The `private` query is persisted across sessions and devices, i.e. all user's sessions see the same `private` query. The value of the `public` query is ephemeral, i.e. it's not saved to database and not shared between user's sessions. The `private` query is intended for large queries which do not change often, such as finding matches for everyone in user's contact list on a mobile phone. The `public` query is intended to be short and specific, such as finding some topic or a user who is not in the contact list.\n\nThe system responds with a `{meta}` message with the `sub` section listing details of the found users or topics formatted as subscriptions.\n\nTopic `fnd` is read-only. `{pub}` messages to `fnd` are rejected.\n\n_CURRENTLY UNSUPPORTED_ When a new user registers with tags matching the given query, the `fnd` topic will receive `{pres}` notification for the new user.\n\n[Plugins](../pbx) support `Find` service which can be used to replace default search with a custom one.\n\nInternally the `fnd` topics are not persisted separately from the users. The `fnd` topics don't exist in the `topics` table or collection, they are created in memory from the `users` database record.\n\n#### Query Language\n\nTinode query language is used to define search queries for finding users and topics. The query is a string containing atomic terms separated by spaces or commas. The individual query terms are matched against user's or topic's tags. The individual terms may be written in an RTL language but the query as a whole is parsed left to right. Spaces are treated as the `AND` operator, commas (as well as commas preceded and/or followed by a space) as the `OR` operator. The order of operators is ignored: all `AND` tags are grouped together, all `OR` tags are grouped together. `OR` takes precedence over `AND`: if a tag is preceded of followed by a comma, it's an `OR` tag, otherwise an `AND`. For example, `aaa bbb, ccc` (`aaa AND bbb OR ccc`) is interpreted as `(bbb OR ccc) AND aaa`.\n\nQuery terms containing spaces must convert spaces to underscores ` ` -> `_`, e.g. `new york` -> `new_york`.\n\n**Some examples:**\n* `flowers`: find topics or users which contain tag `flowers`.\n* `flowers travel`: find topics or users which contain both tags `flowers` and `travel`.\n* `flowers, travel`: find topics or users which contain either tag `flowers` or `travel` (or both).\n* `flowers travel, puppies`: find topics or users which contain `flowers` and either `travel` or `puppies`, i.e. `(travel OR puppies) AND flowers`.\n* `flowers, travel puppies, kittens`: find topics or users which contain either one of `flowers`, `travel`, `puppies`, or `kittens`, i.e. `flowers OR travel OR puppies OR kittens`. The space between `travel` and `puppies` is treated as `OR` due to `OR` taking precedence over `AND`.\n\n#### Incremental Updates to Queries\n\n_CURRENTLY UNSUPPORTED_ Queries, particularly `fnd.private` could be arbitrarily large, limited only by the limits on the message size, and by the limits on the query size in the underlying database. Instead of rewriting the entire query to add or remove a term, terms can be added or removed incrementally.\n\nThe incremental update request is processed left to right. It may contain the same term multiple times, i.e. `-a_tag+a_tag` is a valid request.\n\n#### Query Rewrite\n\nFinding users by login, phone or email requires query terms to be written with prefixes, i.e. `email:alice@example.com` instead of `alice@example.com`. This may present a problem to end users because it requires them to learn the query language. Tinode solves this problem by implementing _query rewrite_ on the server: if query term (tag) does not contain a prefix, server rewrites it by adding the appropriate prefix. In queries to `fnd.public` the original term is also kept (query `alice@example.com` is rewritten as `email:alice@example.com OR alice@example.com`), in queries to `fnd.private` only the rewritten term is kept (`alice@example.com` is rewritten as `email:alice@example.com`). All terms that look like email, for instance, `alice@example.com` are rewritten to `email:alice@example.com OR alice@example.com`. Terms which look like phone numbers are converted to [E.164](https://en.wikipedia.org/wiki/E.164) and also rewritten as `tel:+14155551212 OR +14155551212`. In addition, in queries to `fnd.public` all other unprefixed terms which look like logins are rewritten as logins: `alice` -> `basic:alice OR alice`.\n\nAs described above, tags which look like phone numbers are converted to E.164 format. Such conversion requires an ISO 3166-1 alpha-2 country code. The following logic is used when converting phone number tags to E.164:\n* If the tag already contains a country calling code, it's used as is: `+1(415)555-1212` -> `+14155551212`.\n* If the tag has no prefix, country code is taken from the locale value set by the client in `lang` field of the `{hi}` message.\n* If client has not provided the code in the `hi.lang`, the country code is taken from `default_country_code` field of the `tinode.conf`.\n* If no `default_country_code` is set in `tinode.conf`, `US` country code is used.\n\n#### Possible Use Cases\n* Restricting users to organisations.\n  An immutable tag(s) may be assigned to the user which denotes the organisation the user belongs to. When the user searches for other users or topics, the search can be restricted to always contain the tag. This approach can be used to segment users into organisations with limited visibility into each other.\n\n* Search by geographical location.\n  Client software may periodically assign a [geohash](https://en.wikipedia.org/wiki/Geohash) tag to the user based on current location. Searching for users in a given area would mean matching on geohash tags.\n\n* Search by numerical range, such as age range.\n  The approach is similar to geohashing. The entire range of numbers is covered by the smallest possible power of 2, for instance the range of human ages is covered by 2<sup>7</sup>=128 years. The entire range is split in two halves: the range 0-63 is denoted by 0, 64-127 by 1. The operation is repeated with each subrange, i.e. 0-31 is 00, 32-63 is 01, 0-15 is 000, 32-47 is 010. Once completed, the age 30 will belong to the following ranges: 0 (0-63), 00 (0-31), 001 (16-31), 0011 (24-31), 00111 (28-31), 001111 (30-31), 0011110 (30). A 30 y.o. user is assigned a few tags to indicate the age, i.e. `age:00111`, `age:001111`,  and `age:0011110`. Technically, all 7 tags may be assigned but usually it's impractical. To query for anyone in the age range 28-35 convert the range into a minimal number of tags: `age:00111` (28-31), `age:01000` (32-35). This query will match the 30 y.o. user by tag `age:00111`.\n\n\n### Peer to Peer Topics\n\nPeer to peer (P2P) topics represent communication channels between strictly two users. The name of the topic is different for each of the two participants. Each of them sees the name of the topic as the user ID of the other participant: `usr` followed by base64 URL-encoded ID of the user. For example, if two users `usrOj0B3-gSBSs` and `usrIU_LOVwRNsc` start a P2P topic, the first one will see it as `usrIU_LOVwRNsc`, the second as `usrOj0B3-gSBSs`. The P2P topic has no owner.\n\nA P2P topic is created by one user subscribing to topic with the name equal to the ID of the other user. For instance, user `usrOj0B3-gSBSs` can establish a P2P topic with user `usrIU_LOVwRNsc` by sending a `{sub topic=\"usrIU_LOVwRNsc\"}`. Tinode will respond with a `{ctrl}` packet with the name of the newly created topic as described above. The other user will receive a `{pres}` message on `me` topic with updated access permissions.\n\nInternally, P2P topics are stored as `p2p` followed by base64 URL encoded concatenation of two 64-bit user IDs, with the lower numeric value ID first: `p2pm7PvMGmdcx_uVkDRaSTbwA`.\n\nThe 'public' parameter of P2P topics is user-dependent. For instance a P2P topic between users A and B would show user A's 'public' to user B and vice versa. If a user updates 'public', all user's P2P topics will automatically update 'public' too.\n\nThe 'private' parameter of a P2P topic is defined by each participant individually as with any other topic type.\n\n### Group Topics\n\nGroup topic represents a communication channel between multiple users. The name of a group topic is `grp` or `chn` followed by a string of characters from base64 URL-encoding set. No other assumptions can be made about internal structure or length of the group name.\n\nGroup topics support limited number of subscribers (controlled by a `max_subscriber_count` parameter in configuration file) with access permissions of each subscriber managed individually. Group topics may also be enabled to support any number of read-only users - `readers`. All `readers` have the same access permissions. Group topics with enabled `readers` are called `channels`.\n\nA group topic is created by sending a `{sub}` message with the topic field set to string `new` or `nch` optionally followed by any characters, e.g. `new` or `newAbC123` are equivalent. Tinode will respond with a `{ctrl}` message with the name of the newly created topic, i.e. `{sub topic=\"new\"}` is replied with `{ctrl topic=\"grpmiKBkQVXnm3P\"}`. If topic creation fails, the error is reported on the original topic name, i.e. `new` or `newAbC123`. The user who created the topic becomes topic owner. Ownership can be transferred to another user with a `{set}` message but one user must remain the owner at all times.\n\nA `channel` topic is different from the non-channel group topic in the following ways:\n\n * Channel topic is created by sending `{sub topic=\"nch\"}`. Sending `{sub topic=\"new\"}` will create a group topic without enabling channel functionality.\n * Sending `{sub topic=\"chnAbC123\"}` will create a `reader` subscription to a channel. A non-channel topic will reject such subscription request.\n * When searching for topics using [`fnd`](#fnd-and-tags-finding-users-and-topics), channels will show addresses with `chn` prefixes, non-channel topic will show with `grp` prefixes.\n * Messages received by readers on channels have no `From` field. Normal subscribers will receive messages with `From` containing ID of the sender.\n * Default permissions for a channel and non-channel group topics are different: channel group topic grants no permissions at all.\n * A subscriber joining or leaving the topic (regular or channel-enabled) generates a `{pres}` message to all other subscribers who are currently in the joined state with the topic and have appropriate permissions. Reader joining or leaving the channel generates no `{pres}` message.\n\n### `sys` Topic\n\nThe `sys` topic serves as an always available channel of communication with the system administrators. A normal non-root user cannot subscribe to `sys` but can publish to it without subscription. Existing clients use this channel to report abuse by sending a Drafty-formatted `{pub}` message with the report as JSON attachment. A root user can subscribe to `sys` topic. Once subscribed, the root user will receive messages sent to `sys` topic by other users.\n\n## Using Server-Issued Message IDs\n\nTinode provides basic support for client-side caching of `{data}` messages in the form of server-issued sequential message IDs. The client may request the last message id from the topic by issuing a `{get what=\"desc\"}` message. If the returned ID is greater than the ID of the latest received message, the client knows that the topic has unread messages and their count. The client may fetch these messages using `{get what=\"data\"}` message. The client may also paginate history retrieval by using message IDs.\n\n## User Agent and Presence Notifications\n\nA user is reported as being online when one or more of user's sessions are attached to the `me` topic. Client-side software identifies itself to the server using `ua` (user agent) field of the `{login}` message. The _user agent_ is published in `{meta}` and `{pres}` messages in the following way:\n\n * When user's first session attaches to `me`, the _user agent_ from that session is broadcast in the `{pres what=\"on\" ua=\"...\"}` message.\n * When multiple user sessions are attached to `me`, the _user agent_ of the session where the most recent action has happened is reported in `{pres what=\"ua\" ua=\"...\"}`; the 'action' in this context means any message sent by the client. To avoid potentially excessive traffic, user agent changes are broadcast no more frequently than once a minute.\n * When user's last session detaches from `me`, the _user agent_ from that session is recorded together with the timestamp; the user agent is broadcast in the `{pres what=\"off\"  ua=\"...\"}` message and subsequently reported as the last online timestamp and user agent.\n\nAn empty `ua=\"\"` _user agent_ is not reported. I.e. if user attaches to `me` with non-empty _user agent_ then does so with an empty one, the change is not reported. An empty _user agent_ may be disallowed in the future.\n\n## Trusted, Public, Private, Auxiliary Fields\n\nTopics have `trusted`, `public`, `aux` fields, subscriptions have `private` fields. The primary difference between these fields is in access control:\n\n * `trusted`: writable by `ROOT` users, readable by anyone.\n * `public`: writable by the `owner` or the user, readable by anyone.\n * `aux`: writable by topic administrators, readable by subscribers.\n * `private`: readable and writable only by the user who created the subscription.\n\nGenerally, the fields are application-defined. The server does not enforce any particular structure of these fields except for `fnd` topic. At the same time, client software should use the same format for interoperability reasons. The following sections describe the format of these fields as they are implemented by all official clients.\n\nAlthough it's not yet enforced, if a third-party application defines custom keys, the key names should start with an `x-` followed by the application's fully qualified domain name, e.g. `x-example.com-value: \"abc\"`. The fields should contain primitive types only, i.e. `string`, `boolean`, `number`, or `null`.\n\n### Trusted\n\nThe format of the optional `trusted` field in group and peer to peer topics is a set of key-value pairs; `fnd` and `sys` topics do not have the `trusted`. The field is writable by `ROOT` users, readable by anyone who has access to the topic or user. The following optional keys are currently defined:\n```js\ntrusted: {\n  verified: true, // boolean, an indicator of a verified/trustworthy user or topic.\n  staff: true,    // boolean, an indicator that the user or topic\n                  // is a part of/belongs to the server administration.\n  danger: true    // boolean, an indicator that the user or topic are untrustworthy.\n}\n```\n\n### Public\n\nThe format of the `public` field in group, peer to peer, systems topics is expected to be [theCard](./thecard.md). The field is writable by by the user for users, the topic owner for topics. The field is readable by anyone who has access to topic or user.\n\nThe `fnd` topic expects `public` to be a string representing a [search query](#query-language)).\n\n### Private\n\nThe format of the `private` field in group and peer to peer topics is a set of key-value pairs. The field is writable and readable by the user only. The following keys are currently defined:\n```js\nprivate: {\n  comment: \"some comment\", // string, optional user comment about a topic or a peer user\n  arch: true, // boolean, indicator that the topic is archived by the user, i.e.\n              // should not be shown in the UI with other non-archived topics.\n  accepted: \"JRWS\", // string, 'given' mode accepted by the user.\n  tpins: [\"grpmiKBkQVXnm3P\", \"usrIU_LOVwRNsc\"] // array of topic IDs to pin to the top of\n              // the contacts list; 'me' topic only.\n}\n```\n\nThe `fnd` topic expects `private` to be a string representing a [search query](#query-language)).\n\n### Auxiliary\n\nThe format of the `aux` field is a set of key-value pairs. The `aux` is writable by topic admins and readable by all topic subscribers. The following keys are currently defined:\n\n```js\naux: {\n  pins: [1001, 23456] // array of integer message IDs to pin to the top of the message list.\n}\n```\n\n## Format of Content\n\nFormat of `content` field in `{pub}` and `{data}` is application-defined and as such the server does not enforce any particular structure of the field. At the same time, client software should use the same format for interoperability reasons. Currently the following two types of `content` are supported:\n * Plain text\n * [Drafty](./drafty.md)\n\nIf Drafty is used, a message header `\"head\": {\"mime\": \"text/x-drafty\"}` must be set.\n\n\n## Out-of-Band Handling of Large Files\n\nLarge files create problems when sent in-band for multiple reasons:\n * limits on database storage as in-band messages are stored in database fields\n * in-band messages must be downloaded completely as a part of downloading chat history\n\nTinode provides two endpoints for handling large files: `/v0/file/u` for uploading files and `v0/file/s` for downloading. The endpoints require the client to provide both [API key](#connecting-to-the-server) and login credentials. The server checks credentials in the following order:\n\n**Login credentials**\n * HTTP header `Authorization` (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization)\n * URL query parameters `auth` and `secret` (/v0/file/s/abcdefg.jpeg?auth=...&secret=...)\n * Form values `auth` and `secret`\n * Cookies `auth` and `secret`\n\n### Uploading\n\nTo upload a file first create an RFC 2388 multipart request then send it to the server using HTTP POST. The server responds to the request either with a `307 Temporary Redirect` with the new upload URL, or with a `200 OK` and a `{ctrl}` message in response body:\n\n```js\nctrl: {\n  params: {\n    url: \"/v0/file/s/mfHLxDWFhfU.pdf\"\n  },\n  code: 200,\n  text: \"ok\",\n  ts: \"2018-07-06T18:47:51.265Z\"\n}\n```\nIf `307 Temporary Redirect` is returned, the client must retry the upload at the provided URL. The URL returned in `307` response should be used for just this one upload. All subsequent uploads should try the default URL first.\n\nThe `ctrl.params.url` contains the path to the uploaded file at the current server. It could be either the full path like `/v0/file/s/mfHLxDWFhfU.pdf`, a relative path like `./mfHLxDWFhfU.pdf`, or just the file name `mfHLxDWFhfU.pdf`. Anything but the full path is interpreted against the default *download* endpoint `/v0/file/s/`. For instance, if `mfHLxDWFhfU.pdf` is returned then the file is located at `http(s)://current-tinode-server/v0/file/s/mfHLxDWFhfU.pdf`.\n\nOnce the URL of the file is received, either immediately or after following the redirect, the client may use the URL to send a `{pub}` message with the uploaded file as an attachment, or, if the file is an image, as an avatar image for a topic or user profile (see [theCard](./thecard.md)). For example, the URL can be used in a [Drafty](./drafty.md)-formatted `pub.content` field:\n\n```js\n{\n  pub: {\n    id: \"121103\",\n    topic: \"grpnG99YhENiQU\",\n    head: {\n      mime: \"text/x-drafty\"\n    },\n    content: {\n      ent: [\n      {\n        data: {\n        mime: \"image/jpeg\",\n        name: \"roses-are-red.jpg\",\n        ref:  \"/v0/file/s/sJOD_tZDPz0.jpg\",\n        size: 437265\n      },\n        tp: \"EX\"\n      }\n    ],\n    fmt: [\n      {\n        at: -1,\n      key:0,\n      len:1\n      }\n    ]\n    }\n  },\n  extra: {\n    attachments: [\"/v0/file/s/sJOD_tZDPz0.jpg\"]\n  }\n}\n```\n\nIt's important to list the used URLs in the `extra: attachments[...]` field. Tinode server uses this field to maintain the uploaded file's use counter. Once the counter drops to zero for the given file (for instance, because a message with the shared URL was deleted or because the client failed to include the URL in the `extra.attachments` field), the server will garbage collect the file. Only relative URLs should be used. Absolute URLs in the `extra.attachments` field are ignored. The URL value is expected to be the `ctrl.params.url` returned in response to upload.\n\n### Downloading\n\nThe serving endpoint `/v0/file/s` serves files in response to HTTP GET requests. The client must evaluate relative URLs against this endpoint, i.e. if it receives a URL `mfHLxDWFhfU.pdf` or `./mfHLxDWFhfU.pdf` it should interpret it as a path `/v0/file/s/mfHLxDWFhfU.pdf` at the current Tinode HTTP server.\n\n_Important!_ As a security measure, the client should not send security credentials if the download URL is absolute and leads to another server.\n\n## Push Notifications\n\nTinode uses compile-time adapters for handling push notifications. The server comes with [Tinode Push Gateway](../server/push/tnpg/), [Google FCM](https://firebase.google.com/docs/cloud-messaging/), and `stdout` adapters. Tinode Push Gateway and Google FCM support Android with [Play Services](https://developers.google.com/android/guides/overview) (may not be supported by some Chinese phones), iOS devices and all major web browsers excluding Safari. The `stdout` adapter does not actually send push notifications. It's mostly useful for debugging, testing and logging. Other types of push notifications such as [TPNS](https://intl.cloud.tencent.com/product/tpns) can be handled by writing appropriate adapters.\n\nIf you are writing a custom plugin, the notification payload is the following:\n```js\n{\n  topic: \"grpnG99YhENiQU\", // Topic which","isInternal":false,"tokens":11602,"sizeBytes":50000}],"systemPromptSnippet":"<yakaai_skills repo=\"tinode/chat\">\n<!-- File: docs/API.md (Tokens: ~11602 | Category: project-spec) -->\n<!-- TOC depthfrom:1 depthto:6 withlinks:true updateonsave:true orderedlist:false -->\n\n- [Server API](#server-api)\n  - [How it Works?](#how-it-works)\n  - [General Considerations](#general-considerations)\n  - [Connecting to the Server](#connecting-to-the-server)\n    - [gRPC](#grpc)\n    - [WebSocket](#websocket)\n    - [Long Polling](#long-polling)\n    - [Out of Band Large Files](#out-of-band-large-files)\n    - [Running Behind a Reverse Proxy](#running-behind-a-reverse-proxy)\n  - [Users](#users)\n    - [Authentication](#authentication)\n      - [Creating an Account](#creating-an-account)\n      - [Logging in](#logging-in)\n      - [Changing Authentication Parameters](#changing-authentication-parameters)\n      - [Resetting a Password, i.e. \"Forgot Password\"](#resetting-a-password-ie-forgot-password)\n    - [Suspending a User](#suspending-a-user)\n    - [Credential Validation](#credential-validation)\n    - [Access Control](#access-control)\n  - [Topics](#topics)\n    - [me Topic](#me-topic)\n    - [fnd and Tags: Finding Users and Topics](#fnd-and-tags-finding-users-and-topics)\n      - [Query Language](#query-language)\n      - [Incremental Updates to Queries](#incremental-updates-to-queries)\n      - [Query Rewrite](#query-rewrite)\n      - [Possible Use Cases](#possible-use-cases)\n    - [Peer to Peer Topics](#peer-to-peer-topics)\n    - [Group Topics](#group-topics)\n    - [sys Topic](#sys-topic)\n  - [Using Server-Issued Message IDs](#using-server-issued-message-ids)\n  - [User Agent and Presence Notifications](#user-agent-and-presence-notifications)\n  - [Trusted, Public, Private, Auxiliary Fields](#trusted-public-private-auxiliary-fields)\n    - [Trusted](#trusted)\n    - [Public](#public)\n    - [Private](#private)\n    - [Auxiliary](#auxiliary)\n  - [Format of Content](#format-of-content)\n  - [Out-of-Band Handling of Large Files](#out-of-band-handling-of-large-files)\n    - [Uploading](#uploading)\n    - [Downloading](#downloading)\n  - [Push Notifications](#push-notifications)\n    - [Tinode Push Gateway](#tinode-push-gateway)\n    - [Google FCM](#google-fcm)\n    - [Stdout](#stdout)\n  - [Video Calls](#video-calls)\n  - [Link Previews](#link-previews)\n  - [Messages](#messages)\n    - [Client to Server Messages](#client-to-server-messages)\n      - [{hi}](#hi)\n      - [{acc}](#acc)\n      - [{login}](#login)\n      - [{sub}](#sub)\n      - [{leave}](#leave)\n      - [{pub}](#pub)\n      - [{get}](#get)\n      - [{set}](#set)\n      - [{del}](#del)\n      - [{note}](#note)\n    - [Server to Client Messages](#server-to-client-messages)\n      - [{data}](#data)\n      - [{ctrl}](#ctrl)\n      - [{meta}](#meta)\n      - [{pres}](#pres)\n      - [{info}](#info)\n\n<!-- /TOC -->\n\n# Server API\n\n## How it Works?\n\nTinode is an IM router and a store. Conceptually it loosely follows a [publish-subscribe](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) model.\n\nServer connects sessions, users, and topics. Session is a network connection between a client application and the server. User represents a human being who connects to the server with a session. Topic is a named communication channel which routes content between sessions.\n\nUsers and topics are assigned unique IDs. User ID is a string with 'usr' prefix followed by base64-URL-encoded pseudo-random 64-bit number, e.g. `usr2il9suCbuko`. Topic IDs are described below.\n\nClients such as mobile or web applications create sessions by connecting to the server over a websocket or through long polling. Client authentication is required in order to perform most operations. Client authenticates the session by sending a `{login}` packet. See [Authentication](#authentication) section for details. Once authenticated, the client receives a token which is used for authentication later. Multiple simultaneous sessions may be established by the same user. Logging out is not supported (and not needed).\n\nOnce the session is established, the user can start interacting with other users through topics. The following\ntopic types are available:\n\n* `me` is a topic for managing one's profile and receiving notifications about other topics; `me` topic exists for every user.\n* `fnd` topic is used for finding other users and topics; `fnd` topic also exists for every user.\n* Peer to peer topic is a communication channel strictly between two users. Each participant sees topic name as the ID of the other participant: 'usr' prefix followed by a base64-URL-encoded numeric part of user ID, e.g. `usr2il9suCbuko`.\n* Group topic is a channel for multi-user communication. It's named as 'grp' followed by 11 pseudo-random characters, i.e. `grpYiqEXb4QY6s`. Group topics must be explicitly created.\n\nSession joins a topic by sending a `{sub}` packet. Packet `{sub}` serves three functions: creating a new topic, subscribing user to a topic, and attaching session to a topic. See [`{sub}`](#sub) section below for details.\n\nOnce the session has joined the topic, the user may start generating content by sending `{pub}` packets. The content is delivered to other attached sessions as `{data}` packets.\n\nThe user may query or update topic metadata by sending `{get}` and `{set}` packets.\n\nChanges to topic metadata, such as changes in topic description, or when other users join or leave the topic, is reported to live sessions with `{pres}` (presence) packet. The `{pres}` packet is sent either to the topic being affected or to the `me` topic.\n\nWhen user's `me` topic comes online (i.e. an authenticated session attaches to `me` topic), a `{pres}` packet is sent to `me` topics of all other users, who have peer to peer subscriptions with the first user.\n\n## General Considerations\n\nTimestamps are always represented as [RFC 3339](http://tools.ietf.org/html/rfc3339)-formatted string with precision up to milliseconds and timezone always set to UTC, e.g. `\"2015-10-06T18:07:29.841Z\"`.\n\nWhenever base64 encoding is mentioned, it means base64 URL encoding with padding characters stripped, see [RFC 4648](http://tools.ietf.org/html/rfc4648).\n\nThe `{data}` packets have server-issued sequential IDs: base-10 numbers starting at 1 and incrementing by one with every message. They are guaranteed to be unique per topic.\n\nIn order to connect requests to responses, client may assign message IDs to all packets set to the server. These IDs are strings defined by the client. Client should make them unique at least per session. The client-assigned IDs are not interpreted by the server, they are returned to the client as is.\n\n## Connecting to the Server\n\nThere are three ways to access the server over the network: websocket, long polling, and [gRPC](https://grpc.io/).\n\nWhen the client establishes a connection to the server over HTTP(S), such as over a websocket or long polling, the server offers the following endpoints:\n * `/v0/channels` for websocket connections\n * `/v0/channels/lp` for long polling\n * `/v0/file/u` for file uploads\n * `/v0/file/s` for serving files (downloads)\n\n`v0` denotes API version (currently zero). Every HTTP(S) request must include the API key. The server checks for the API key in the following order:\n* HTTP header `X-Tinode-APIKey`\n* URL query parameter `apikey` (/v0/file/s/abcdefg.jpeg?apikey=...)\n* Form value `apikey`\n* Cookie `apikey`\n\nA default API key is included with every demo app for convenience. Generate your own key for production using [`keygen` utility](../keygen).\n\nOnce the connection is opened, the client must issue a `{hi}` message to the server. Server responds with a `{ctrl}` message which indicates either success or an error. The `params` field of the response contains server's protocol version `\"params\":{\"ver\":\"0.15\"}` and may include other values.\n\n### gRPC\n\nSee definition of the gRPC API in the [proto file](../pbx/model.proto). gRPC API has slightly more functionality than the API described in this document: it allows the `root` user to send messages on behalf of other users as well as delete users.\n\nThe `bytes` fields in protobuf messages expect JSON-encoded UTF-8 content. For example, a string should be quoted before being converted to bytes as UTF-8: `[]byte(\"\\\"some string\\\"\")` (Go), `'\"another string\"'.encode('utf-8')` (Python 3).\n\n### WebSocket\n\nMessages are sent in text frames, one message per frame. Binary frames are reserved for future use. By default server allows connections with any value in the `Origin` header.\n\n### Long Polling\n\nLong polling works over `HTTP POST` (preferred) or `GET`. In response to client's very first request server sends a `{ctrl}` message containing `sid` (session ID) in `params`. Long polling client must include `sid` in every subsequent request either in the URL or in the request body.\n\nServer allows connections from all origins, i.e. `Access-Control-Allow-Origin: *`\n\n### Out of Band Large Files\n\nLarge files are sent out of band using `HTTP POST` as `Content-Type: multipart/form-data`. See [below](#out-of-band-handling-of-large-files) for details.\n\n### Running Behind a Reverse Proxy\n\nTinode server can be set up to run behind a reverse proxy, such as NGINX. For efficiency it can accept client connections from Unix sockets by setting `listen` and/or `grpc_listen` config parameters to the path of the Unix socket file, e.g. `unix:/run/tinode.sock`. The server may also be configured to read peer's IP address from `X-Forwarded-For` HTTP header by setting `use_x_forwarded_for` config parameter to `true`.\n\n## Users\n\nUser is meant to represent a person, an end-user: producer and consumer of messages.\n\nUsers are generally assigned one of the two authentication levels: authenticated `auth` or anonymous `anon`. The third level `root` is only accessible over `gRPC` where it permits the `root` to send messages on behalf of other users.\n\nWhen a connection is first established, the client application can send either an `{acc}` or a `{login}` message which authenticates the user at one the levels.\n\nEach user is assigned a unique ID. The IDs are composed as `usr` followed by base64-encoded 64-bit numeric value, e.g. `usr2il9suCbuko`. Users also have the following properties:\n\n* `created`: timestamp when the user record was created\n* `updated`: timestamp of when user's `public` or `trusted` was last updated\n* `status`: state of the account\n* `username`: unique string used in `basic` authentication; username is not accessible to other users\n* `defacs`: object describing user's default access mode for peer to peer conversations with authenticated and anonymous users; see [Access control](#access-control) for details\n  * `auth`: default access mode for authenticated `auth` users\n  * `anon`: default access for anonymous `anon` users\n* `trusted`: an application-defined object issued by the system administration. Anyone can read it but only system administrators can change it.\n* `public`: an application-defined object that describes the user. Anyone can query user for `public` data.\n* `private`: an application-defined object that is unique to the current user and accessible only by the user.\n* `tags`: [discovery](#fnd-and-tags-finding-users-and-topics) and credentials.\n\nUser's account has a state. The following states are defined:\n * `ok` (normal): the default state which means the account is not restricted in any way and can be used normally;\n * `susp` (suspended): the user is prevented from accessing the account as well as not found through [search](#fnd-and-tags-finding-users-and-topics); the state can be assigned by the administrator and fully reversible.\n * `del` (soft-deleted): user is marked as deleted but user's data is retained; un-deleting the user is not currenly supported.\n * `undef` (undefined): used internally by authenticators; should not be used elsewhere.\n\nA user may maintain multiple simultaneous connections (sessions) with the server. Each session is tagged with a client-provided `User Agent` string intended to differentiate client software.\n\nLogging out is not supported by design. If an application needs to change the user, it should open a new connection and authenticate it with the new user credentials.\n\n\n### Authentication\n\nAuthentication is conceptually similar to [SASL](https://en.wikipedia.org/wiki/Simple_Authentication_and_Security_Layer): it's provided as a set of adapters each implementing a different authentication method. Authenticators are used during account registration [`{acc}`](#acc) and during [`{login}`](#login). The server comes with the following authentication methods out of the box:\n\n * `token` provides authentication by a cryptographic token.\n * `basic` provides authentication by a login-password pair.\n * `anonymous` is designed for cases where users are temporary, such as handling customer support requests through chat.\n * `rest` is a [meta-method](../server/auth/rest/) which allows use of external authentication systems by means of JSON RPC.\n\nAny other authentication method can be implemented using adapters.\n\nThe `token` is intended to be the primary means of authentication. Tokens are designed in such a way that token authentication is light weight. For instance, token authenticator generally does not make any database calls, all processing is done in-memory. All other authentication methods are intended to be used only to obtain or refresh the token. Once the token is obtained, subsequent logins should use it.\n\nThe `basic` authentication scheme expects `secret` to be a base64-encoded string of a string composed of a user name followed by a colon `:` followed by a plan text password. User name in the `basic` scheme must not contain the colon character `:` (ASCII 0x3A).\n\nThe `anonymous` scheme can be used to create accounts, it cannot be used for logging in: a user creates an account using `anonymous` scheme and obtains a cryptographic token which it uses for subsequent `token` logins. If the token is lost or expired, the user is no longer able to access the account.\n\nCompiled-in authenticator names may be changed by using `logical_names` configuration feature. For example, a custom `rest` authenticator may be exposed as `basic` instead of default one or `token` authenticator could be hidden from users. The feature is activated by providing an array of mappings in the config file: `logical_name:actual_name` to rename or `actual_name:` to hide. For instance, to use a `rest` service for basic authentication use `\"logical_names\": [\"basic:rest\"]`.\n\n\n#### Creating an Account\n\nWhen a new account is created, the user must inform the server which authentication method will be later used to gain access to this account as well as provide shared secret, if appropriate. Only `basic` and `anonymous` can be used during account creation. The `basic` requires the user to generate and send a unique login and password to the server. The `anonymous` does not exchange secrets.\n\nUser may optionally set `{acc login=true}` to use the new account for immediate authentication. When `login=false` (or not set), the new account is created but the authentication status of the session which created the account remains unchanged. When `login=true` the server will attempt to authenticate the session with the new account, the `{ctrl}` response to the `{acc}` request will contain the authentication token on success. This is particularly important for the `anonymous` authentication because that's the only time when the authentication token can be retrieved.\n\n#### Logging in\n\nLogging in is performed by issuing a `{login}` request. Logging in is possible with `basic` and `token` only. Response to any login is a `{ctrl}` message with either a code 200 and a token which can be used in subsequent logins with `token` authentication, or a code 300 request for additional information, such as verifying credentials or responding to a method-dependent challenge in multi-step authentication, or a code 4xx error.\n\nToken has server-configured expiration time so it needs to be periodically refreshed.\n\n#### Changing Authentication Parameters\n\nUser may change authentication parameters, such as changing login and password, by issuing an `{acc}` request. Only `basic` authentication currently supports changing parameters:\n```js\nacc: {\n  id: \"1a2b3\", // string, client-provided message id, optional\n  user: \"usr2il9suCbuko\", // user being affected by the change, optional\n  token: \"XMg...g1Gp8+BO0=\", // authentication token if the session\n                             // is not yet authenticated, optional.\n  scheme: \"basic\", // authentication scheme being updated.\n  secret: base64encode(\"new_username:new_password\") // new parameters\n}\n```\nIn order to change just the password, `username` should be left empty, i.e. `secret: base64encode(\":new_password\")`.\n\nIf the session is not authenticated, the request must include a `token`. It can be a regular authentication token obtained during login, or a restricted token received through [Resetting a Password](#resetting-a-password) process. If the session is authenticated, the token must not be included. If the request is authenticated for access level `ROOT`, then the `user` may be set to a valid ID of another user. Otherwise it must be blank (defaulting to the current user) or equal to the ID of the current user.\n\n\n#### Resetting a Password, i.e. \"Forgot Password\"\n\nTo reset login or password, (or any other authentication secret, if such action is supported by the authenticator), one sends a `{login}` message with the `scheme` set to `reset` and the `secret` containing a base64-encoded string \"`authentication scheme to reset secret for`:`reset method`:`reset method value`\". Most basic case of resetting a password by email is\n```js\nlogin: {\n  id: \"1a2b3\",\n  scheme: \"reset\",\n  secret: base64encode(\"basic:email:jdoe@example.com\")\n}\n```\nwhere `jdoe@example.com` is an earlier validated user's email.\n\nIf the email matches the registration, the server will send a message using specified method and address with instructions for resetting the secret. The email contains a restricted security token which the user can include into an `{acc}` request with the new secret as described in [Changing Authentication Parameters](#changing-authentication-parameters).\n\n### Suspending a User\n\nUser's account can be suspended by service administrator. Once the account is suspended, the user is no longer able to login and use the service.\n\nOnly the `root` user may suspend the account. To suspend the account the root user sends the following message:\n```js\nacc: {\n  id: \"1a2b3\", // string, client-provided message id, optional\n  user: \"usr2il9suCbuko\", // user being affected by the change\n  status: \"susp\"\n}\n```\nSending the same message with `status: \"ok\"` un-suspends the account. A root user may check account status by executing `{get what=\"desc\"}` command against user's `me` topic.\n\n\n### Credential Validation\n\nServer may be optionally configured to require validation of certain credentials associated with the user accounts and authentication scheme. For instance, it's possible to require user to provide a unique email or a phone number, or to solve a captcha as a condition of account registration.\n\nThe server supports verification of email out of the box with just a configuration change. is mostly functional, verification of phone numbers is not functional because a commercial subscription is needed in order to be able to send text messages (SMS).\n\nIf certain credentials are required, then user must maintain them in validated state at all times. It means if a required credential has to be changed, the user must first add and validate the new credential and only then remove the old one.\n\nCredentials are initially assigned at registration time by sending an `{acc}` message, added using `{set topic=\"me\"}`, deleted using `{del topic=\"me\"}`, and queries by `{get topic=\"me\"}` messages. Credentials are verified by the client by sending either a `{login}` or an `{acc}` message.\n\n\n### Access Control\n\nAccess control manages user's access to topics through access control lists (ACLs). The access is assigned individually to each user-topic pair (subscription).\n\nAccess control is mostly usable for group topics. Its usability for `me` and P2P topics is limited to managing presence notifications and banning uses from initiating or continuing P2P conversations. All channel readers are given the same permissions.\n\nUser's access to a topic is defined by two sets of permissions: user's desired permissions \"want\", and permissions granted to user by topic's manager(s) \"given\". Each permission is represented by a bit in a bitmap. It can be either present or absent. The actual access is determined as a bitwise AND of wanted and given permissions. The permissions are communicated in messages as a set of ASCII characters, where presence of a character means a set permission bit:\n\n* No access: `N` is not a permission per se but an indicator that permissions are explicitly cleared/not set. It usually indicates that the default permissions should *not* be applied.\n* Join: `J`, permission to subscribe to a topic\n* Read: `R`, permission to receive `{data}` packets\n* Write: `W`, permission to `{pub}` to topic\n* Presence: `P`, permission to receive presence updates `{pres}`\n* Approve: `A`, permission to approve requests to join a topic, remove and ban members; a user with such permission is topic's administrator\n* Sharing: `S`, permission to invite other people to join the topic\n* Delete: `D`, permission to hard-delete messages; only owners can completely delete topics\n* Owner: `O`, user is the topic owner; the owner can assign any other permission to any topic member, change topic description, delete topic; topic may have a single owner only; some topics have no owner\n\nWhen a user subscribes to a topic or starts a chat with another user, the access permissions are either set explicitly or assigned by default `defacs`. Access permissions can be modified by sending `{set}` messages.\n\nA client may set explicit permissions in `{sub}` and `{set}` messages. If the permissions are missing or set to an empty string (not `N`!), Tinode will use default permissions `defacs` assigned earlir. If no default permissions are found, the authenticated users in group topics will receive a `JRWPS` access, in P2P topics will get `JRWPA`; anonymous users will receive `N` (no access) which means every subscription request must be explicitly approved by the topic manager.\n\nDefault access is defined for two categories of users: authenticated and anonymous. The default access value is applied as a \"given\" permission to all new subscriptions. Topic's default access is established at the topic creation time by `{sub.desc.defacs}` and can be subsequently modified by the owner by sending `{set}` messages. Likewise, user's default access is established at the account creation time by `{acc.desc.defacs}` and can be modified by the user by sending a `{set}` message to `me` topic.\n\n\n## Topics\n\nTopic is a named communication channel for one or more people. Topics have persistent properties. These topic properties can be queried by `{get what=\"desc\"}` message.\n\nTopic properties independent of the user making the query:\n* `created`: timestamp of topic creation time\n* `updated`: timestamp of when topic's `trusted`, `public`, or `private` was last updated\n* `touched`: timestamp of the last message sent to the topic\n* `defacs`: object describing topic's default access mode for authenticated and anonymous users; see [Access control](#access-control) for details\n * `auth`: default access mode for authenticated users\n * `anon`: default access for anonymous users\n* `seq`: integer server-issued sequential ID of the latest `{data}` message sent through the topic\n* `trusted`: an application-defined object issued by the system administrators. Anyone can read it but only administrators can change it.\n* `public`: an application-defined object that describes the topic. Anyone who can subscribe to topic can receive topic's `public` data, only topic `owner` can change it.\n\nUser-dependent topic properties:\n* `acs`: object describing given user's current access permissions; see [Access control](#access-control) for details\n * `want`: access permission requested by this user\n * `given`: access permissions given to this user\n* `private`: an application-defined object that is unique to the current user (topic subscriber).\n\nTopic usually have subscribers. One of the subscribers may be designated as topic owner (`O` access permission) with full access permissions. The list of subscribers can be queries with a `{get what=\"sub\"}` message. The list of subscribers is returned in a `sub` section of a `{meta}` message.\n\n### `me` Topic\n\nTopic `me` is automatically created for every user at the account creation time. It serves as means of managing account information, receiving presence notification from people and topics of interest. Topic `me` has no owner. The topic cannot be deleted or unsubscribed from. One can `leave` the topic which will stop all relevant communication and indicate that the user is offline (although the user may still be logged in and may continue to use other topics).\n\nJoining or leaving `me` generates a `{pres}` presence update sent to all users who have peer to peer topics with the given user and `P` permissions set.\n\nTopic `me` is read-only. `{pub}` messages to `me` are rejected.\n\nMessage `{get what=\"desc\"}` to `me` is automatically replied with a `{meta}` message containing `desc` section with the topic parameters (see intro to [Topics](#topics) section). The `public` parameters of `me` topic is data that the user wants to show to his/her connections. Changing it changes `public` not just for the `me` topic, but also everywhere where user's `public` is shown, such as `public` of all user's peer to peer topics.\n\nMessage `{get what=\"sub\"}` to `me` is different from any other topic as it returns the list of topics that the current user is subscribed to as opposite to the expected user's subscription to `me`.\n* seq: server-issued numeric id of the last message in the topic\n* recv: seq value self-reported by the current user as received\n* read: seq value self-reported by the current user as read\n* seen: for P2P subscriptions, timestamp of user's last presence and User Agent string are reported\n * when: timestamp when the user was last online\n * ua: user agent string of the user's client software last used\n\nMessage `{get what=\"data\"}` to `me` is rejected.\n\nInternally the `me` topics are not persisted separately from the users. The `me` topics don't exist in the `topics` table or collection, they are created in memory from the `users` database record.\n\n### `slf` Topic\n\nTopic `slf` (self) provides mechanism for storing information, like bookmarks or saved messages. Messages sent to `slf` are accessible only by the user who sent them.\n\nThis topic is created automatically when the user subscribes to it for the first time.\n\n### `fnd` and Tags: Finding Users and Topics\n\nTopic `fnd` is automatically created for every user at the account creation time. It serves as an endpoint for discovering other users and group topics. Users and group topics can be discovered by `tags`. Tags are optionally assigned at the topic or user creation time then can be updated by using `{set what=\"tags\"}` against a `me` or a group topic.\n\nA tag is an arbitrary case-insensitive Unicode string (forced to lowercase on the server) up to 96 characters long which may contain characters from `Letter` and `Number` Unicode [classes/categories](https://en.wikipedia.org/wiki/Unicode_character_property#General_Category) as well as any of the following ASCII characters: `_`, `.`, `+`, `-`, `@`, `#`, `!`, `?`.\n\nTag may have a prefix which serves as a namespace. The prefix is a 2-16 character string which starts with a letter [a-z] and may contain lowercase ASCII letters and numbers followed by a colon `:`, ex. prefixed phone tag `tel:+14155551212` or prefixed email tag `email:alice@example.com`. Some prefixed tags are optionally enforced to be unique. In that case only one user or topic may have such a tag. Certain tags may be forced to be immutable to the user, i.e. user's attempts to add or remove an immutable tag will be rejected by the server.\n\nThe tags are indexed server-side and used in user and topic discovery. Search returns users and topics sorted by the number of matched tags in descending order.\n\nIn order to find users or topics, a user sets either `public` or `private` parameter of the `fnd` topic to a search query (see [Query language](#query-language)) then issues a `{get topic=\"fnd\" what=\"sub\"}` request. If both `public` and `private` are set, the `public` query is used. The `private` query is persisted across sessions and devices, i.e. all user's sessions see the same `private` query. The value of the `public` query is ephemeral, i.e. it's not saved to database and not shared between user's sessions. The `private` query is intended for large queries which do not change often, such as finding matches for everyone in user's contact list on a mobile phone. The `public` query is intended to be short and specific, such as finding some topic or a user who is not in the contact list.\n\nThe system responds with a `{meta}` message with the `sub` section listing details of the found users or topics formatted as subscriptions.\n\nTopic `fnd` is read-only. `{pub}` messages to `fnd` are rejected.\n\n_CURRENTLY UNSUPPORTED_ When a new user registers with tags matching the given query, the `fnd` topic will receive `{pres}` notification for the new user.\n\n[Plugins](../pbx) support `Find` service which can be used to replace default search with a custom one.\n\nInternally the `fnd` topics are not persisted separately from the users. The `fnd` topics don't exist in the `topics` table or collection, they are created in memory from the `users` database record.\n\n#### Query Language\n\nTinode query language is used to define search queries for finding users and topics. The query is a string containing atomic terms separated by spaces or commas. The individual query terms are matched against user's or topic's tags. The individual terms may be written in an RTL language but the query as a whole is parsed left to right. Spaces are treated as the `AND` operator, commas (as well as commas preceded and/or followed by a space) as the `OR` operator. The order of operators is ignored: all `AND` tags are grouped together, all `OR` tags are grouped together. `OR` takes precedence over `AND`: if a tag is preceded of followed by a comma, it's an `OR` tag, otherwise an `AND`. For example, `aaa bbb, ccc` (`aaa AND bbb OR ccc`) is interpreted as `(bbb OR ccc) AND aaa`.\n\nQuery terms containing spaces must convert spaces to underscores ` ` -> `_`, e.g. `new york` -> `new_york`.\n\n**Some examples:**\n* `flowers`: find topics or users which contain tag `flowers`.\n* `flowers travel`: find topics or users which contain both tags `flowers` and `travel`.\n* `flowers, travel`: find topics or users which contain either tag `flowers` or `travel` (or both).\n* `flowers travel, puppies`: find topics or users which contain `flowers` and either `travel` or `puppies`, i.e. `(travel OR puppies) AND flowers`.\n* `flowers, travel puppies, kittens`: find topics or users which contain either one of `flowers`, `travel`, `puppies`, or `kittens`, i.e. `flowers OR travel OR puppies OR kittens`. The space between `travel` and `puppies` is treated as `OR` due to `OR` taking precedence over `AND`.\n\n#### Incremental Updates to Queries\n\n_CURRENTLY UNSUPPORTED_ Queries, particularly `fnd.private` could be arbitrarily large, limited only by the limits on the message size, and by the limits on the query size in the underlying database. Instead of rewriting the entire query to add or remove a term, terms can be added or removed incrementally.\n\nThe incremental update request is processed left to right. It may contain the same term multiple times, i.e. `-a_tag+a_tag` is a valid request.\n\n#### Query Rewrite\n\nFinding users by login, phone or email requires query terms to be written with prefixes, i.e. `email:alice@example.com` instead of `alice@example.com`. This may present a problem to end users because it requires them to learn the query language. Tinode solves this problem by implementing _query rewrite_ on the server: if query term (tag) does not contain a prefix, server rewrites it by adding the appropriate prefix. In queries to `fnd.public` the original term is also kept (query `alice@example.com` is rewritten as `email:alice@example.com OR alice@example.com`), in queries to `fnd.private` only the rewritten term is kept (`alice@example.com` is rewritten as `email:alice@example.com`). All terms that look like email, for instance, `alice@example.com` are rewritten to `email:alice@example.com OR alice@example.com`. Terms which look like phone numbers are converted to [E.164](https://en.wikipedia.org/wiki/E.164) and also rewritten as `tel:+14155551212 OR +14155551212`. In addition, in queries to `fnd.public` all other unprefixed terms which look like logins are rewritten as logins: `alice` -> `basic:alice OR alice`.\n\nAs described above, tags which look like phone numbers are converted to E.164 format. Such conversion requires an ISO 3166-1 alpha-2 country code. The following logic is used when converting phone number tags to E.164:\n* If the tag already contains a country calling code, it's used as is: `+1(415)555-1212` -> `+14155551212`.\n* If the tag has no prefix, country code is taken from the locale value set by the client in `lang` field of the `{hi}` message.\n* If client has not provided the code in the `hi.lang`, the country code is taken from `default_country_code` field of the `tinode.conf`.\n* If no `default_country_code` is set in `tinode.conf`, `US` country code is used.\n\n#### Possible Use Cases\n* Restricting users to organisations.\n  An immutable tag(s) may be assigned to the user which denotes the organisation the user belongs to. When the user searches for other users or topics, the search can be restricted to always contain the tag. This approach can be used to segment users into organisations with limited visibility into each other.\n\n* Search by geographical location.\n  Client software may periodically assign a [geohash](https://en.wikipedia.org/wiki/Geohash) tag to the user based on current location. Searching for users in a given area would mean matching on geohash tags.\n\n* Search by numerical range, such as age range.\n  The approach is similar to geohashing. The entire range of numbers is covered by the smallest possible power of 2, for instance the range of human ages is covered by 2<sup>7</sup>=128 years. The entire range is split in two halves: the range 0-63 is denoted by 0, 64-127 by 1. The operation is repeated with each subrange, i.e. 0-31 is 00, 32-63 is 01, 0-15 is 000, 32-47 is 010. Once completed, the age 30 will belong to the following ranges: 0 (0-63), 00 (0-31), 001 (16-31), 0011 (24-31), 00111 (28-31), 001111 (30-31), 0011110 (30). A 30 y.o. user is assigned a few tags to indicate the age, i.e. `age:00111`, `age:001111`,  and `age:0011110`. Technically, all 7 tags may be assigned but usually it's impractical. To query for anyone in the age range 28-35 convert the range into a minimal number of tags: `age:00111` (28-31), `age:01000` (32-35). This query will match the 30 y.o. user by tag `age:00111`.\n\n\n### Peer to Peer Topics\n\nPeer to peer (P2P) topics represent communication channels between strictly two users. The name of the topic is different for each of the two participants. Each of them sees the name of the topic as the user ID of the other participant: `usr` followed by base64 URL-encoded ID of the user. For example, if two users `usrOj0B3-gSBSs` and `usrIU_LOVwRNsc` start a P2P topic, the first one will see it as `usrIU_LOVwRNsc`, the second as `usrOj0B3-gSBSs`. The P2P topic has no owner.\n\nA P2P topic is created by one user subscribing to topic with the name equal to the ID of the other user. For instance, user `usrOj0B3-gSBSs` can establish a P2P topic with user `usrIU_LOVwRNsc` by sending a `{sub topic=\"usrIU_LOVwRNsc\"}`. Tinode will respond with a `{ctrl}` packet with the name of the newly created topic as described above. The other user will receive a `{pres}` message on `me` topic with updated access permissions.\n\nInternally, P2P topics are stored as `p2p` followed by base64 URL encoded concatenation of two 64-bit user IDs, with the lower numeric value ID first: `p2pm7PvMGmdcx_uVkDRaSTbwA`.\n\nThe 'public' parameter of P2P topics is user-dependent. For instance a P2P topic between users A and B would show user A's 'public' to user B and vice versa. If a user updates 'public', all user's P2P topics will automatically update 'public' too.\n\nThe 'private' parameter of a P2P topic is defined by each participant individually as with any other topic type.\n\n### Group Topics\n\nGroup topic represents a communication channel between multiple users. The name of a group topic is `grp` or `chn` followed by a string of characters from base64 URL-encoding set. No other assumptions can be made about internal structure or length of the group name.\n\nGroup topics support limited number of subscribers (controlled by a `max_subscriber_count` parameter in configuration file) with access permissions of each subscriber managed individually. Group topics may also be enabled to support any number of read-only users - `readers`. All `readers` have the same access permissions. Group topics with enabled `readers` are called `channels`.\n\nA group topic is created by sending a `{sub}` message with the topic field set to string `new` or `nch` optionally followed by any characters, e.g. `new` or `newAbC123` are equivalent. Tinode will respond with a `{ctrl}` message with the name of the newly created topic, i.e. `{sub topic=\"new\"}` is replied with `{ctrl topic=\"grpmiKBkQVXnm3P\"}`. If topic creation fails, the error is reported on the original topic name, i.e. `new` or `newAbC123`. The user who created the topic becomes topic owner. Ownership can be transferred to another user with a `{set}` message but one user must remain the owner at all times.\n\nA `channel` topic is different from the non-channel group topic in the following ways:\n\n * Channel topic is created by sending `{sub topic=\"nch\"}`. Sending `{sub topic=\"new\"}` will create a group topic without enabling channel functionality.\n * Sending `{sub topic=\"chnAbC123\"}` will create a `reader` subscription to a channel. A non-channel topic will reject such subscription request.\n * When searching for topics using [`fnd`](#fnd-and-tags-finding-users-and-topics), channels will show addresses with `chn` prefixes, non-channel topic will show with `grp` prefixes.\n * Messages received by readers on channels have no `From` field. Normal subscribers will receive messages with `From` containing ID of the sender.\n * Default permissions for a channel and non-channel group topics are different: channel group topic grants no permissions at all.\n * A subscriber joining or leaving the topic (regular or channel-enabled) generates a `{pres}` message to all other subscribers who are currently in the joined state with the topic and have appropriate permissions. Reader joining or leaving the channel generates no `{pres}` message.\n\n### `sys` Topic\n\nThe `sys` topic serves as an always available channel of communication with the system administrators. A normal non-root user cannot subscribe to `sys` but can publish to it without subscription. Existing clients use this channel to report abuse by sending a Drafty-formatted `{pub}` message with the report as JSON attachment. A root user can subscribe to `sys` topic. Once subscribed, the root user will receive messages sent to `sys` topic by other users.\n\n## Using Server-Issued Message IDs\n\nTinode provides basic support for client-side caching of `{data}` messages in the form of server-issued sequential message IDs. The client may request the last message id from the topic by issuing a `{get what=\"desc\"}` message. If the returned ID is greater than the ID of the latest received message, the client knows that the topic has unread messages and their count. The client may fetch these messages using `{get what=\"data\"}` message. The client may also paginate history retrieval by using message IDs.\n\n## User Agent and Presence Notifications\n\nA user is reported as being online when one or more of user's sessions are attached to the `me` topic. Client-side software identifies itself to the server using `ua` (user agent) field of the `{login}` message. The _user agent_ is published in `{meta}` and `{pres}` messages in the following way:\n\n * When user's first session attaches to `me`, the _user agent_ from that session is broadcast in the `{pres what=\"on\" ua=\"...\"}` message.\n * When multiple user sessions are attached to `me`, the _user agent_ of the session where the most recent action has happened is reported in `{pres what=\"ua\" ua=\"...\"}`; the 'action' in this context means any message sent by the client. To avoid potentially excessive traffic, user agent changes are broadcast no more frequently than once a minute.\n * When user's last session detaches from `me`, the _user agent_ from that session is recorded together with the timestamp; the user agent is broadcast in the `{pres what=\"off\"  ua=\"...\"}` message and subsequently reported as the last online timestamp and user agent.\n\nAn empty `ua=\"\"` _user agent_ is not reported. I.e. if user attaches to `me` with non-empty _user agent_ then does so with an empty one, the change is not reported. An empty _user agent_ may be disallowed in the future.\n\n## Trusted, Public, Private, Auxiliary Fields\n\nTopics have `trusted`, `public`, `aux` fields, subscriptions have `private` fields. The primary difference between these fields is in access control:\n\n * `trusted`: writable by `ROOT` users, readable by anyone.\n * `public`: writable by the `owner` or the user, readable by anyone.\n * `aux`: writable by topic administrators, readable by subscribers.\n * `private`: readable and writable only by the user who created the subscription.\n\nGenerally, the fields are application-defined. The server does not enforce any particular structure of these fields except for `fnd` topic. At the same time, client software should use the same format for interoperability reasons. The following sections describe the format of these fields as they are implemented by all official clients.\n\nAlthough it's not yet enforced, if a third-party application defines custom keys, the key names should start with an `x-` followed by the application's fully qualified domain name, e.g. `x-example.com-value: \"abc\"`. The fields should contain primitive types only, i.e. `string`, `boolean`, `number`, or `null`.\n\n### Trusted\n\nThe format of the optional `trusted` field in group and peer to peer topics is a set of key-value pairs; `fnd` and `sys` topics do not have the `trusted`. The field is writable by `ROOT` users, readable by anyone who has access to the topic or user. The following optional keys are currently defined:\n```js\ntrusted: {\n  verified: true, // boolean, an indicator of a verified/trustworthy user or topic.\n  staff: true,    // boolean, an indicator that the user or topic\n                  // is a part of/belongs to the server administration.\n  danger: true    // boolean, an indicator that the user or topic are untrustworthy.\n}\n```\n\n### Public\n\nThe format of the `public` field in group, peer to peer, systems topics is expected to be [theCard](./thecard.md). The field is writable by by the user for users, the topic owner for topics. The field is readable by anyone who has access to topic or user.\n\nThe `fnd` topic expects `public` to be a string representing a [search query](#query-language)).\n\n### Private\n\nThe format of the `private` field in group and peer to peer topics is a set of key-value pairs. The field is writable and readable by the user only. The following keys are currently defined:\n```js\nprivate: {\n  comment: \"some comment\", // string, optional user comment about a topic or a peer user\n  arch: true, // boolean, indicator that the topic is archived by the user, i.e.\n              // should not be shown in the UI with other non-archived topics.\n  accepted: \"JRWS\", // string, 'given' mode accepted by the user.\n  tpins: [\"grpmiKBkQVXnm3P\", \"usrIU_LOVwRNsc\"] // array of topic IDs to pin to the top of\n              // the contacts list; 'me' topic only.\n}\n```\n\nThe `fnd` topic expects `private` to be a string representing a [search query](#query-language)).\n\n### Auxiliary\n\nThe format of the `aux` field is a set of key-value pairs. The `aux` is writable by topic admins and readable by all topic subscribers. The following keys are currently defined:\n\n```js\naux: {\n  pins: [1001, 23456] // array of integer message IDs to pin to the top of the message list.\n}\n```\n\n## Format of Content\n\nFormat of `content` field in `{pub}` and `{data}` is application-defined and as such the server does not enforce any particular structure of the field. At the same time, client software should use the same format for interoperability reasons. Currently the following two types of `content` are supported:\n * Plain text\n * [Drafty](./drafty.md)\n\nIf Drafty is used, a message header `\"head\": {\"mime\": \"text/x-drafty\"}` must be set.\n\n\n## Out-of-Band Handling of Large Files\n\nLarge files create problems when sent in-band for multiple reasons:\n * limits on database storage as in-band messages are stored in database fields\n * in-band messages must be downloaded completely as a part of downloading chat history\n\nTinode provides two endpoints for handling large files: `/v0/file/u` for uploading files and `v0/file/s` for downloading. The endpoints require the client to provide both [API key](#connecting-to-the-server) and login credentials. The server checks credentials in the following order:\n\n**Login credentials**\n * HTTP header `Authorization` (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization)\n * URL query parameters `auth` and `secret` (/v0/file/s/abcdefg.jpeg?auth=...&secret=...)\n * Form values `auth` and `secret`\n * Cookies `auth` and `secret`\n\n### Uploading\n\nTo upload a file first create an RFC 2388 multipart request then send it to the server using HTTP POST. The server responds to the request either with a `307 Temporary Redirect` with the new upload URL, or with a `200 OK` and a `{ctrl}` message in response body:\n\n```js\nctrl: {\n  params: {\n    url: \"/v0/file/s/mfHLxDWFhfU.pdf\"\n  },\n  code: 200,\n  text: \"ok\",\n  ts: \"2018-07-06T18:47:51.265Z\"\n}\n```\nIf `307 Temporary Redirect` is returned, the client must retry the upload at the provided URL. The URL returned in `307` response should be used for just this one upload. All subsequent uploads should try the default URL first.\n\nThe `ctrl.params.url` contains the path to the uploaded file at the current server. It could be either the full path like `/v0/file/s/mfHLxDWFhfU.pdf`, a relative path like `./mfHLxDWFhfU.pdf`, or just the file name `mfHLxDWFhfU.pdf`. Anything but the full path is interpreted against the default *download* endpoint `/v0/file/s/`. For instance, if `mfHLxDWFhfU.pdf` is returned then the file is located at `http(s)://current-tinode-server/v0/file/s/mfHLxDWFhfU.pdf`.\n\nOnce the URL of the file is received, either immediately or after following the redirect, the client may use the URL to send a `{pub}` message with the uploaded file as an attachment, or, if the file is an image, as an avatar image for a topic or user profile (see [theCard](./thecard.md)). For example, the URL can be used in a [Drafty](./drafty.md)-formatted `pub.content` field:\n\n```js\n{\n  pub: {\n    id: \"121103\",\n    topic: \"grpnG99YhENiQU\",\n    head: {\n      mime: \"text/x-drafty\"\n    },\n    content: {\n      ent: [\n      {\n        data: {\n        mime: \"image/jpeg\",\n        name: \"roses-are-red.jpg\",\n        ref:  \"/v0/file/s/sJOD_tZDPz0.jpg\",\n        size: 437265\n      },\n        tp: \"EX\"\n      }\n    ],\n    fmt: [\n      {\n        at: -1,\n      key:0,\n      len:1\n      }\n    ]\n    }\n  },\n  extra: {\n    attachments: [\"/v0/file/s/sJOD_tZDPz0.jpg\"]\n  }\n}\n```\n\nIt's important to list the used URLs in the `extra: attachments[...]` field. Tinode server uses this field to maintain the uploaded file's use counter. Once the counter drops to zero for the given file (for instance, because a message with the shared URL was deleted or because the client failed to include the URL in the `extra.attachments` field), the server will garbage collect the file. Only relative URLs should be used. Absolute URLs in the `extra.attachments` field are ignored. The URL value is expected to be the `ctrl.params.url` returned in response to upload.\n\n### Downloading\n\nThe serving endpoint `/v0/file/s` serves files in response to HTTP GET requests. The client must evaluate relative URLs against this endpoint, i.e. if it receives a URL `mfHLxDWFhfU.pdf` or `./mfHLxDWFhfU.pdf` it should interpret it as a path `/v0/file/s/mfHLxDWFhfU.pdf` at the current Tinode HTTP server.\n\n_Important!_ As a security measure, the client should not send security credentials if the download URL is absolute and leads to another server.\n\n## Push Notifications\n\nTinode uses compile-time adapters for handling push notifications. The server comes with [Tinode Push Gateway](../server/push/tnpg/), [Google FCM](https://firebase.google.com/docs/cloud-messaging/), and `stdout` adapters. Tinode Push Gateway and Google FCM support Android with [Play Services](https://developers.google.com/android/guides/overview) (may not be supported by some Chinese phones), iOS devices and all major web browsers excluding Safari. The `stdout` adapter does not actually send push notifications. It's mostly useful for debugging, testing and logging. Other types of push notifications such as [TPNS](https://intl.cloud.tencent.com/product/tpns) can be handled by writing appropriate adapters.\n\nIf you are writing a custom plugin, the notification payload is the following:\n```js\n{\n  topic: \"grpnG99YhENiQU\", // Topic which\n</yakaai_skills>"}