Skip to content

API Overview

Russ Garrett edited this page May 4, 2022 · 43 revisions

Stream Message ReferenceRPC Reference

The IRCCloud API consists of a stream of JSON messages and a series of RPC methods. The API is accessible via Secure WebSocket (preferred) or HTTPS. Insecure access isn't supported.

Important note

The API is still in flux and not officially supported. Methods, responses, and message formats are liable to change at any time. Feel free to ask questions though.

# Authentication

The first thing a client will need to do to use the API is authenticate. This is done by making an HTTPS POST request to the login endpoint with email and password parameters to acquire a session key.

You will also need an auth-formtoken to make this call (as both post data and a header value), as a prevention method against login CSRF attacks

curl -X POST "https://www.irccloud.com/chat/auth-formtoken" --header "content-length: 0"
{"success":true,"token":"1397241172970.9a87f9s7fad9f7s9f8a7fa9sd77"}
curl -d email=XXX -d password=XXX -d token=1397241172970.9a87f9s7fad9f7s9f8a7fa9sd77 --header "content-type: application/x-www-form-urlencoded" --header "x-auth-formtoken: 1397241172970.9a87f9s7fad9f7s9f8a7fa9sd77" "https://www.irccloud.com/chat/login"
{"success":true,"session":"e07910a06a086c83ba41827aa00b26ed","uid":1}

Set this key as the session cookie value for all further requests.

# Stream endpoints

WebSocket

wss://api.irccloud.com/

The following header must be present:
Origin: https://api.irccloud.com

Example request:

GET / HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
User-Agent: [REDACTED]
Cookie: session=[REDACTED]
Sec-WebSocket-Key: [REDACTED]==
Host: api.irccloud.com
Origin: https://api.irccloud.com
Sec-WebSocket-Version: 13

To save on bandwidth, WebSocket connections should support the x-webkit-deflate-frame extension, as currently implemented in Google Chrome and Pywebsocket.

HTTPS

https://www.irccloud.com/chat/stream

No Origin header is needed for the HTTPS stream.

Example request:

GET /chat/stream HTTP/1.1
Connection: keep-alive
Accept-Encoding: gzip,deflate,sdch
User-Agent: [REDACTED]
Cookie: session=[REDACTED]
Host: www.irccloud.com

# Terminology

There are a few potentially ambiguous terms that we use in reference to the API that are worth defining in their normal sense.

Connection

A connection to an IRC network, defined by hostname, port and ssl settings. Uniquely identified by a cid

Buffer

Split into three sub types, this is where events are directed within a connection. We don't use this term in the UI. Uniquely identified by a bid

  • Server log (console) - this is the connection console, where things like the message of the day and connection boilerplate goes as well as other global server messages
  • Channel (channel) - group chat rooms with many members
  • Conversation (conversation) - private messages with a single recipient

Event

Any message directed at a buffer. Uniquely identified by a bid and eid

# User status

One of the first messages to process on the stream is stat_user. It provides the authenticated user's name, account details, settings and preferences. Clients can receive this message at any point in a stream session to be notified of updates to any of its properties.

# Processing the initial backlog

# Out of band include

On connecting to the stream, parse and process all messages until the oob_include message (oob = out of band). At this point, clients may continue to parse new messages on the stream but must not process them until after fetching and processing all messages retrieved from the out of band url atrribute. This URL expires after a short timeout so should be requested immediately, check the timeout attribute (in ms).

The purpose of this include is so the bulk of the backlog can be fetched with GZIP compression, so the appropriate Accept-Encoding should be sent. The URL provided in the oob_include message is only valid for a few seconds.

The OOB messages contain the full connection and buffer lists, and a limited number of events for each buffer. It will be concluded by a backlog_complete message (not to be confused with the connection specific end_of_backlog)

Backlog may also be sent inline. In this case, you'll see an oob_skipped message instead followed by the full backlog and backlog_complete. No extra backlog needs to be fetched in this case. Clients should to handle both cases.

# Connections

The OOB include is divided into connection blocks, started by a makeserver message that provides the cid, and connection details. and concluded by an end_of_backlog message.

A connection's status indicates where it is in the lifecycle of disconnected, connecting, connected, etc. The status value of the makeserver message gives the initial status, and all changes are communicated via status_changed messages

# Buffers

Within each connection block there'll be a makebuffer message for each of the connection's buffers providing the bid, buffer_type and name for each. This is followed by a list of events with eid values for that buffer. This is limited on the server.

For the console buffer, the name will be * and should never be displayed. Use the connection name for display instead.

If a buffer is listed as deferred then no events will be in the initial stream and must be requested via an HTTPS GET to the backlog API method when that buffer is selected. Clients should then set the buffer's deferred value to false.

For non deferred buffers, a client can determine whether more backlog can be loaded by comparing the earliest message eid with the min_eid value given in makebuffer.

# Archiving

Channel and conversation buffers can be archived. This means they're hidden in the buffer list, under an expandable heading. This allows people to keep their log history around without cluttering up the UI too much.

Conversations that haven't had any activity in them in a while will automatically archive, and when new messages come in they'll unarchive again.

Archived buffers will always be marked as deferred: true

# Channel init

For each channel a user is currently joined to, the backlog will contain a channel_init message. This provides the channel members list, topic, mode and a creation date timestamp.

A client should still display channels and backlog that aren't currently joined, but rely on channel_init to mark them as active.

# Buffer unread state

A client will need to keep track of which buffers contain unread messages. At the simplest level this involves checking the value of last_seen_eid and comparing it to events received from the server. A client needs to send the last seen event id to the server via the heartbeat API call and keep its local value up to date whenever messages are seen and when heartbeat_echo messages are received.

A seen event is defined as any event received while a user has a buffer selected.

# "Important" messages

Buffers can received many different message types, and only some should mark the buffer as unread. Here's the list of "important" events that trigger an unread state:

notice and channel_invite messages are NOT important if the bid is for a buffer of type console and the message has no from value, OR has a to_chan or to_buffer value of true

Messages with a self value of true are never important

// Algorithm in javascript-ish pseudo code (for brevity)
function isImportant (message) {
    if (message.self) {
        return false;
    }
    var important = [buffer_msg, buffer_me_msg, notice, channel_invite, callerid, wallops];
    if (message.type in important) {
        if (message.type in [notice, channel_invite]) {
            if (!message.from) {
                return false;
            }
            if (isConsoleBuffer(message.bid) && (message.to_chan || message.to_buffer)) {
                return false;
            }
        }
        return true;
    }
}

When last_seen_eid is set via the seenEids value of heartbeat_echo, a client must make sure to count all "important" events between the new last seen value and the end, before appropriately marking the buffer as read or unread.

# Ignore

Users are able to specify an ignores list (indicated by makeserver). This applies only to buffer_msg, buffer_me_msg, notice and channel_invite events. Any of these events that come from a user in the ignore list should NOT result in a last_seen_eid update, nor trigger a notification, nor cause a buffer to be marked as unread.

Ignore masks are of the form <nick>!<user>@<host> and each value may contain the wildcard * symbol.

Parsing reference implementation:

# Reconnecting the stream

If the stream disconnects during an active session, clients should reconnect. This will result in the backlog being fetched again.

How you process this backlog depends on the resumed flag in the header message.

If it's true, you can process them as normal messages for a connected stream.

If it's false or not set, you need to parse and read all makeserver and makebuffer messages and compare their properties (e.g. connected, joined, archived) with your stored state. Remove any buffers or servers that are no longer present in the backlog, in case they were deleted while the stream was disconnected.

# Resuming

To save on bandwidth and server load, pass a since_id parameter of the last eid received (they are always sequential) as well as a stream_id parameter (sent in the header message as streamid) to the stream endpoint when you reconnect. e.g. wss://www.irccloud.com/?since_id=12345&stream_id=9876 or https://www.irccloud.com/chat/stream?since_id=12345&stream_id=9876 This will affect the number of messages returned in the backlog. Always parse and process all backlog messages, but ignore any with an eid that has already been seen for a buffer.

# Idle timeout

You can expect to receive messages from the stream continually and regularly. If no regular messages are due for delivery after a timeout, the server will send an idle message. We keep the stream active to avoid proxy servers from disconnecting, and also to let a client know that the server is still responsive. idle_interval (sent in the header message) gives you a maximum time between messages. Clients should restart the stream no messsages are received after that time span.