WeeChat API Relay
WeeChat API Relay
Sébastien Helleu
flashcode@flashtux.org
version 4.9.0-dev
Table of Contents
1. Introduction
1.1. Terminology
1.2. Network diagram
2. Protocol generalities
2.1. API versioning
2.2. API schema
2.3. Response codes
2.4. Date format
3. Authentication
3.1. Sec-WebSocket-Protocol
4. Compression
5. Resources
5.1. Preflight request
5.2. Handshake
5.3. Version
5.4. Buffers
Lines
Nicks
5.5. Hotlist
5.6. Input
5.7. Completion
5.8. Ping
5.9. Sync
6. Websocket
6.1. Websocket handshake
6.2. Frames
1. Introduction
This document is the specification of
api
relay protocol: the protocol used
to relay WeeChat data to clients using an HTTP REST API.
1.1. Terminology
The following terms are used in this document:
relay
: this is the WeeChat with relay plugin, which acts as "server" and
allows
clients
to connect
client
: this is a software connected to
relay
via a network connection
(WeeChat itself or a remote interface).
1.2. Network diagram
The
clients
are connected to
relay
like shown in this diagram:
┌──────────┐ Workstation
┌────────┐ ┌───┤ client 1 │ (Linux, Windows,
│ irc │◄──┐ ╔═══════════╤═══════╗ │ └──────────┘ BSD, macOS, …)
└────────┘ └──╢ │ ║◄───┘ ┌──────────┐
...... ║ WeeChat │ Relay ║◄───────┤ client 2 │ Mobile device
┌────────┐ ┌──╢ │ ║◄───┐ └──────────┘ (Android, iPhone, …)
│ jabber │◄──┘ ╚═══════════╧═══════╝ │ ......
└────────┘ │ ┌──────────┐
...... └───┤ client N │ Other devices
└──────────┘

└────────────┘ └───────────────────┘╘══════╛└────────────────────────────────┘
network servers ncurses interface relay remote interfaces
All clients here are clients using
api
protocol in
relay
plugin.
The
relay
plugin also allows
irc
and
weechat
protocols (not described in this document).
2. Protocol generalities
Connections from
client
to
relay
are made using TCP sockets on IP/port
used by
relay
plugin to listen to new connections.
Number of
clients
is limited by the option
relay.network.max_clients
Each
client
is independent from other clients.
The
api
relay is an HTTP REST API using JSON format for input/output.
Messages are automatically compressed (deflate, gzip, zstd and permessage-deflate
for websocket protocol).
WeeChat can be used as client of this relay.
2.1. API versioning
The API is versioned using a "practical"
semantic Versioning
like WeeChat, on three digits
X.Y.Z
, where:
is the major version
is the minor version
is the patch version.
Example: version
2.0.0
brings breaking changes vs version
1.2.3
The API version is returned by the
version
resource.
2.2. API schema
You can browse and test the API online:
WeeChat Relay API
2.3. Response codes
The following HTTP response codes can be sent back to the client:
200 OK
: response OK with a body (JSON)
204 No Content
: response OK without body
400 Bad Request
: invalid request received
401 Unauthorized
: missing or invalid credentials
403 Forbidden
: insufficient permissions
404 Not Found
: resource not found
500 Internal Server Error
: internal server error
503 Service Unavailable
: service unavailable
When connected via websocket protocol, an extra response code is sent when WeeChat
pushes data to the client on events:
0 Event
: event pushed to the client, if synchronization is enabled with
sync
resource.
2.4. Date format
The date format is
ISO 8601
using UTC timezone (this is then different from the display by WeeChat which
uses the local timezone).
The dates are returned with maximum precision: up to microseconds if possible,
or milliseconds, or just seconds.
Examples:
2023-12-05T19:46:03.847625Z
2023-12-05T19:46:03.847Z
2023-12-05T19:46:03Z
3. Authentication
The password must be sent in the header
Authorization
with
Basic
authentication schema or in the header
Sec-WebSocket-Protocol
(see details
below).
The password can be sent as plain text or hashed, with one of these formats
for user and password:
plain:
hash:sha256::
hash:sha512::
hash:pbkdf2+sha256:::
hash:pbkdf2+sha512:::
Where:

is the password as plain text

is the current timestamp as integer (number of seconds since
the Unix Epoch); it is used to prevent replay attacks

is the number of iterations (for PBKDF2 algorithm only)

is the hashed value of timestamp + password (as hexadecimal)
Note
The max number of seconds allowed before and after the received time
(when password is sent hashed) can be configured with option
relay.network.time_window
Example:
current timestamp is
1706431066
password is
secret_password
hash algorithm is
sha256
result hash is the SHA256 of string
1706431066secret_password
which is as hexadecimal:
dfa1db3f6bb6445d18d9ec7427c10f6421274e3a4751e6c1ffc7dd28c94eadf6
the
Authorization
header is the base64 encoded string
hash:sha256:1706431066:dfa1db3f6bb6445d18d9ec7427c10f6421274e3a4751e6c1ffc7dd28c94eadf6
aGFzaDpzaGEyNTY6MTcwNjQzMTA2NjpkZmExZGIzZjZiYjY0NDVkMThkOWVjNzQyN2MxMGY2NDIxMjc0ZTNhNDc1MWU2YzFmZmM3ZGQyOGM5NGVhZGY2
The headers
Authorization
and
Sec-WebSocket-Protocol
are allowed in the first
request with the websocket protocol or any HTTP request in the other cases.
Request example with plain text password:
curl
-L
-u
'plain:secret_password'
'https://localhost:9000/api/version'
Request example with hashed password (SHA256):
curl
-L
-u
'hash:sha256:1706431066:dfa1db3f6bb6445d18d9ec7427c10f6421274e3a4751e6c1ffc7dd28c94eadf6'
'https://localhost:9000/api/version'
If TOTP (Time-based One-Time Password) is enabled on WeeChat/relay side
(option
relay.network.totp_secret
is set), you must send the TOTP value
in the
x-weechat-totp
header like this:
curl
-L
-u
'hash:sha256:1706431066:dfa1db3f6bb6445d18d9ec7427c10f6421274e3a4751e6c1ffc7dd28c94eadf6'
-H
"x-weechat-totp: 123456"
'https://localhost:9000/api/version'
In case of error, a response
401 Unauthorized
is returned with a field
error
in JSON data that describes the error.
Response: missing password:
HTTP
1.1
401
Unauthorized
"error"
"Missing password"
Response: wrong password:
HTTP
1.1
401
Unauthorized
"error"
"Invalid password"
Response: invalid hash algorithm:
HTTP
1.1
401
Unauthorized
"error"
"Invalid hash algorithm (not found or not supported)"
Response: invalid timestamp:
HTTP
1.1
401
Unauthorized
"error"
"Invalid timestamp"
Response: invalid number of iterations:
HTTP
1.1
401
Unauthorized
"error"
"Invalid number of iterations"
Response: missing TOTP:
HTTP
1.1
401
Unauthorized
"error"
"Missing TOTP"
Response: wrong TOTP:
HTTP
1.1
401
Unauthorized
"error"
"Invalid TOTP"
3.1. Sec-WebSocket-Protocol
The JavaScript WebSocket API used in current web browsers does not support
specifying the
Authorization
header. Therefore it’s also supported to send
the password in the
Sec-WebSocket-Protocol
header which is the only header
possible to set with this API.
To use this header, you must specify the sub-protocols
api.weechat
and
base64url.bearer.authorization.weechat.
where

is the base64url
encoded string of the password in the same format as explained above.
Example with password
secret_password
encoded in plain text. This makes the
string to base64url encode
plain:secret_password
which is
cGxhaW46c2VjcmV0X3Bhc3N3b3Jk
Sec-WebSocket-Protocol: api.weechat, base64url.bearer.authorization.weechat.cGxhaW46c2VjcmV0X3Bhc3N3b3Jk
This can be set with the JavaScript WebSocket API like this:
const
ws
new
WebSocket
"wss://localhost:9000/api"
"api.weechat"
"base64url.bearer.authorization.weechat.cGxhaW46c2VjcmV0X3Bhc3N3b3Jk"
])
4. Compression
Compression of response body is automatic and based on header
Accept-Encoding
sent by the client.
Supported compression formats are:
deflate
(zlib)
gzip
zstd
Request example:
curl
-L
-u
'plain:secret_password'
-H
"Accept-Encoding: gzip"
'https://localhost:9000/api/version'
Response:
HTTP
1.1
200
OK
Content-Type
application/json; charset=utf-8
Content-Encoding
gzip
Content-Length
77
[77 bytes data]
Note: with websocket protocol, the extension "permessage-deflate" allows to
compress messages with zlib.
5. Resources
5.1. Preflight request
The preflight request with HTTP method
OPTIONS
is used by web browsers to check
that the server (WeeChat) will permit the actual request.
Request example: check that request
GET /api/version
is authorized:
OPTIONS
/api/version
HTTP
1.1
Host
localhost:9000
Connection
keep-alive
Accept
*/*
Access-Control-Request-Method
GET
Access-Control-Request-Headers
authorization
Origin
User-Agent
Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
Sec-Fetch-Mode
cors
Sec-Fetch-Site
same-site
Sec-Fetch-Dest
empty
Referer
Accept-Encoding
gzip, deflate, br, zstd
Accept-Language
en-US,en;q=0.9,fr;q=0.8
Response:
HTTP
1.1
204
No Content
Access-Control-Allow-Methods
GET, POST, PUT, DELETE
Access-Control-Allow-Headers
origin, content-type, accept, authorization
Access-Control-Allow-Origin
Content-Type
application/json; charset=utf-8
Content-Length
5.2. Handshake
Perform an handshake between the client and WeeChat.
This resource is accessible without authentication.
Endpoint:
POST /api/handshake
Body parameters:
password_hash_algo
(array of strings, optional): list of hash algorithms
supported by the client, each string can be:
plain
: plain-text password (no hash)
sha256
: hash SHA256
sha512
: hash SHA512
pbkdf2+sha256
: hash PBKDF2 with SHA256
pbkdf2+sha512
: hash PBKDF2 with SHA512
The response has the following fields:
password_hash_algo
(string): the hash algorithm to use
null
if no algorithm is compatible)
password_hash_iterations
(integer): the number of iterations to use if
hash PBKDF2 is used
totp
(boolean):
true
if TOTP is enabled in WeeChat (then the client must
send TOTP in specific header),
false
otherwise
Request example:
curl
-L
-X
POST
-d
'{"password_hash_algo": ["plain", "sha256", "sha512"]}'
'https://localhost:9000/api/handshake'
Response:
HTTP
1.1
200
OK
"password_hash_algo"
"sha512"
"password_hash_iterations"
100000
"totp"
false
5.3. Version
Return the WeeChat and relay API versions.
Endpoint:
GET /api/version
Request example:
curl
-L
-u
'plain:secret_password'
'https://localhost:9000/api/version'
Response:
HTTP
1.1
200
OK
"weechat_version"
"4.2.0-dev"
"weechat_version_git"
"v4.1.0-143-g0b1cda1c4"
"weechat_version_number"
67239936
"relay_api_version"
"0.0.1"
"relay_api_version_number"
5.4. Buffers
Return buffers, lines and nicks.
Endpoints:
GET /api/buffers
GET /api/buffers/{buffer_id}
GET /api/buffers/{buffer_name}
Path parameters:
buffer_id
(integer, optional): buffer unique identifier (not to be confused
with the buffer number, which is different)
buffer_name
(string, optional): buffer name
Query parameters:
lines
(integer, optional, default:
): number of lines to return in buffers
with formatted content:
negative number: return N lines from the end of buffer (newest lines)
: do not return any line
positive number: return N lines from the beginning of buffer (oldest lines)
lines_free
(integer, optional, default:
if
lines
is
, otherwise all lines):
number of lines to return in buffers with free content:
negative number: return N lines from the end of buffer
: do not return any line
positive number: return N lines from the beginning of buffer
nicks
(boolean, optional, default:
false
): return nicks in buffer
colors
(string, optional, default:
ansi
): how to return strings with color codes:
ansi
: return ANSI color codes
weechat
: return WeeChat internal color codes
strip
: strip colors
Request example: get all buffers without lines:
curl
-L
-u
'plain:secret_password'
'https://localhost:9000/api/buffers'
Response:
HTTP
1.1
200
OK
"id"
1709932823238637
"name"
"core.weechat"
"short_name"
"weechat"
"number"
"type"
"formatted"
"hidden"
false
"title"
"WeeChat 4.2.0-dev (C) 2003-2023 - https://weechat.org/"
"modes"
""
"input_prompt"
""
"input"
""
"input_position"
"input_multiline"
false
"nicklist"
false
"nicklist_case_sensitive"
false
"nicklist_display_groups"
true
"time_displayed"
true
"local_variables"
"plugin"
"core"
"name"
"weechat"
},
"keys"
[]
},
"id"
1709932823423765
"name"
"irc.server.libera"
"short_name"
"libera"
"number"
"type"
"formatted"
"hidden"
false
"title"
"IRC: irc.libera.chat/6697 (2001:4b7a:a008::6667)"
"modes"
""
"input_prompt"
""
"input"
""
"input_position"
"input_multiline"
false
"nicklist"
false
"nicklist_case_sensitive"
false
"nicklist_display_groups"
true
"time_displayed"
true
"local_variables"
"plugin"
"irc"
"name"
"server.libera"
"type"
"server"
"server"
"libera"
"channel"
"libera"
"charset_modifier"
"irc.libera"
"nick"
"alice"
"tls_version"
"TLS1.3"
"host"
"~alice@example.com"
},
"keys"
[]
},
"id"
1709932823649069
"name"
"irc.libera.#weechat"
"short_name"
"#weechat"
"number"
"type"
"formatted"
"hidden"
false
"title"
"Welcome to the WeeChat official support channel"
"modes"
"+nt"
"input_prompt"
"\u001b[92m@\u001b[96malice\u001b[48;5;22m(\u001b[39mi\u001b[48;5;22m)"
"input"
""
"input_position"
"input_multiline"
false
"nicklist"
true
"nicklist_case_sensitive"
false
"nicklist_display_groups"
false
"time_displayed"
true
"local_variables"
"plugin"
"irc"
"name"
"libera.#weechat"
"type"
"channel"
"server"
"libera"
"channel"
"#weechat"
"nick"
"alice"
"host"
"~alice@example.com"
},
"keys"
[]
Request example: get WeeChat core buffer with only last line and no color codes:
curl
-L
-u
'plain:secret_password'
'https://localhost:9000/api/buffers/core.weechat?lines=-1&colors=strip'
Response:
HTTP
1.1
200
OK
"id"
1709932823238637
"name"
"core.weechat"
"short_name"
"weechat"
"number"
"type"
"formatted"
"hidden"
false
"title"
"WeeChat 4.2.0-dev (C) 2003-2023 - https://weechat.org/"
"modes"
""
"input_prompt"
""
"input"
""
"input_position"
"input_multiline"
false
"nicklist"
false
"nicklist_case_sensitive"
false
"nicklist_display_groups"
true
"time_displayed"
true
"local_variables"
"plugin"
"core"
"name"
"weechat"
},
"keys"
[],
"lines"
"id"
10
"y"
-1
"date"
"2023-12-24T08:17:20.786538Z"
"date_printed"
"2023-12-24T08:17:20.786538Z"
"displayed"
true
"highlight"
false
"notify_level"
"prefix"
""
"message"
"Plugins loaded: alias, buflist, charset, exec, fifo, fset, guile, irc, javascript, logger, lua, perl, php, python, relay, ruby, script, spell, tcl, trigger, typing, xfer"
"tags"
[]
Request example: get an IRC channel buffers with nicks:
curl
-L
-u
'plain:secret_password'
'https://localhost:9000/api/buffers/irc.libera.%23weechat?nicks=true'
Response:
HTTP
1.1
200
OK
"id"
1709932823649069
"name"
"irc.libera.#weechat"
"short_name"
"#weechat"
"number"
"type"
"formatted"
"hidden"
false
"title"
"Welcome to the WeeChat official support channel"
"modes"
"+nt"
"input_prompt"
"\u001b[92m@\u001b[96malice\u001b[48;5;22m(\u001b[39mi\u001b[48;5;22m)"
"input"
""
"input_position"
"input_multiline"
false
"nicklist"
true
"nicklist_case_sensitive"
false
"nicklist_display_groups"
false
"time_displayed"
true
"local_variables"
"plugin"
"irc"
"name"
"libera.#weechat"
"type"
"channel"
"server"
"libera"
"channel"
"#weechat"
"nick"
"alice"
"host"
"~alice@example.com"
},
"keys"
[],
"nicklist_root"
"id"
"parent_group_id"
-1
"name"
"root"
"color_name"
""
"color"
""
"visible"
false
"groups"
"id"
1709932823649181
"parent_group_id"
"name"
"000|o"
"color_name"
"weechat.color.nicklist_group"
"color"
"\u001b[32m"
"visible"
true
"groups"
[],
"nicks"
"id"
1709932823649184
"parent_group_id"
1709932823649181
"prefix"
"@"
"prefix_color_name"
"lightgreen"
"prefix_color"
"\u001b[92m"
"name"
"alice"
"color_name"
"bar_fg"
"color"
""
"visible"
true
},
"id"
1709932823649189
"parent_group_id"
"name"
"001|h"
"color_name"
"weechat.color.nicklist_group"
"color"
"\u001b[32m"
"visible"
true
"groups"
[],
"nicks"
[]
},
"id"
1709932823649203
"parent_group_id"
"name"
"002|v"
"color_name"
"weechat.color.nicklist_group"
"color"
"\u001b[32m"
"visible"
true
"groups"
[],
"nicks"
[]
},
"id"
1709932823649210
"parent_group_id"
"name"
"999|..."
"color_name"
"weechat.color.nicklist_group"
"color"
"\u001b[32m"
"visible"
true
"groups"
[],
"nicks"
[]
],
"nicks"
[]
Request example: get fset buffer:
curl
-L
-u
'plain:secret_password'
'https://localhost:9000/api/buffers/fset.fset'
Response:
HTTP
1.1
200
OK
"id"
1709932823897200
"name"
"fset.fset"
"short_name"
""
"number"
"type"
"free"
"hidden"
false
"title"
"\u001b[96m1/\u001b[36m3565 | Filter: \u001b[93m* | Sort: \u001b[97m~name | Key(input): alt+space=toggle boolean, alt+'-'(-)=subtract 1 or set, alt+'+'(+)=add 1 or append, alt+f,alt+r(r)=reset, alt+f,alt+u(u)=unset, alt+enter(s)=set, alt+f,alt+n(n)=set new value, alt+f,alt+a(a)=append, alt+','=mark/unmark, shift+down=mark and move down, shift+up=move up and mark, ($)=refresh, ($$)=unmark/refresh, (m)=mark matching options, (u)=unmark matching options, alt+p(p)=toggle plugins desc, alt+v(v)=toggle help bar, ctrl+x(x)=switch format, (q)=close buffer"
"modes"
""
"input_prompt"
""
"input"
""
"input_position"
"input_multiline"
false
"nicklist"
false
"nicklist_case_sensitive"
false
"nicklist_display_groups"
true
"time_displayed"
true
"local_variables"
"plugin"
"fset"
"name"
"fset"
"type"
"option"
"filter"
"*"
},
"keys"
"key"
"ctrl-l"
"command"
"/fset -refresh"
},
"key"
"ctrl-n"
"command"
"/eval ${if:${weechat.bar.buflist.hidden}?/fset -down:/buffer +1}"
},
"key"
"ctrl-x"
"command"
"/fset -format"
},
"key"
"down"
"command"
"/fset -down"
},
"key"
"f11"
"command"
"/fset -left"
},
"key"
"f12"
"command"
"/fset -right"
},
"key"
"meta-+"
"command"
"/fset -add 1"
},
"key"
"meta--"
"command"
"/fset -add -1"
},
"key"
"meta-comma"
"command"
"/fset -mark"
},
"key"
"meta-end"
"command"
"/fset -go end"
},
"key"
"meta-f,meta-a"
"command"
"/fset -append"
},
"key"
"meta-f,meta-n"
"command"
"/fset -setnew"
},
"key"
"meta-f,meta-r"
"command"
"/fset -reset"
},
"key"
"meta-f,meta-u"
"command"
"/fset -unset"
},
"key"
"meta-home"
"command"
"/fset -go 0"
},
"key"
"meta-p"
"command"
"/mute /set fset.look.show_plugins_desc toggle"
},
"key"
"meta-q"
"command"
"/input insert meta-q fset ${property}"
},
"key"
"meta-return"
"command"
"/fset -set"
},
"key"
"meta-space"
"command"
"/fset -toggle"
},
"key"
"meta-v"
"command"
"/bar toggle fset"
},
"key"
"shift-down"
"command"
"/fset -mark; /fset -down"
},
"key"
"shift-up"
"command"
"/fset -up; /fset -mark"
},
"key"
"up"
"command"
"/fset -up"
Lines
Return lines in a buffer.
Endpoints:
GET /api/buffers/{buffer_id}/lines
GET /api/buffers/{buffer_id}/lines/{line_id}
GET /api/buffers/{buffer_name}/lines
GET /api/buffers/{buffer_name}/lines/{line_id}
Path parameters:
buffer_id
(integer,
required
): buffer unique identifier (not to be
confused with the buffer number, which is different)
buffer_name
(string,
required
): buffer name
line_id
(integer, optional): return a single line with this identifier
Query parameters:
lines
(integer, optional, default: all lines): number of lines to return:
negative number: return N lines from the end of buffer (newest lines)
: do not return any line (allowed but doesn’t make sense with this resource)
positive number: return N lines from the beginning of buffer (oldest lines)
colors
(string, optional, default:
ansi
): how to return strings with color codes:
ansi
: return ANSI color codes
weechat
: return WeeChat internal color codes
strip
: strip colors
Request example: get last 1000 lines of a buffer, without color codes:
curl
-L
-u
'plain:secret_password'
'https://localhost:9000/api/buffers/irc.libera.%23weechat/lines?lines=-1000&colors=strip'
Response:
HTTP
1.1
200
OK
"id"
"y"
-1
"date"
"2023-12-05T19:46:03.847625Z"
"date_printed"
"2023-12-05T19:46:03.847625Z"
"displayed"
true
"highlight"
false
"notify_level"
"prefix"
"-->"
"message"
"alice (~alice@example.com) has joined #test"
"tags"
"irc_join"
"irc_tag_account=alice"
"irc_tag_time=2023-12-05T19:46:03.847Z"
"nick_alice"
"host_~alice@example.com"
"log4"
},
"id"
"y"
-1
"date"
"2023-12-05T19:46:03.986543Z"
"date_printed"
"2023-12-05T19:46:03.986543Z"
"displayed"
true
"highlight"
false
"notify_level"
"prefix"
"--"
"message"
"Mode #test [+Cnst] by zirconium.libera.chat"
"tags"
"irc_mode"
"irc_tag_time=2023-12-05T19:46:03.986Z"
"nick_zirconium.libera.chat"
"log3"
},
"id"
"y"
-1
"date"
"2023-12-05T19:46:04.287546Z"
"date_printed"
"2023-12-05T19:46:04.287546Z"
"displayed"
true
"highlight"
false
"notify_level"
"prefix"
"--"
"message"
"Channel #test: 1 nick (1 op, 0 voiced, 0 regular)"
"tags"
"irc_366"
"irc_numeric"
"irc_tag_time=2023-12-05T19:46:04.287Z"
"nick_zirconium.libera.chat"
"log3"
Nicks
Return nicks in a buffer.
Endpoints:
GET /api/buffers/{buffer_id}/nicks
GET /api/buffers/{buffer_name}/nicks
Path parameters:
buffer_id
(integer,
required
): buffer unique identifier (not to be
confused with the buffer number, which is different)
buffer_name
(string,
required
): buffer name
Request example: get nicks of a buffer:
curl
-L
-u
'plain:secret_password'
'https://localhost:9000/api/buffers/irc.libera.%23weechat/nicks'
Response:
HTTP
1.1
200
OK
"id"
"parent_group_id"
-1
"name"
"root"
"color_name"
""
"color"
""
"visible"
false
"groups"
"id"
1709932823649181
"parent_group_id"
"name"
"000|o"
"color_name"
"weechat.color.nicklist_group"
"color"
"\u001b[32m"
"visible"
true
"groups"
[],
"nicks"
"id"
1709932823649184
"parent_group_id"
1709932823649181
"prefix"
"@"
"prefix_color_name"
"lightgreen"
"prefix_color"
"\u001b[92m"
"name"
"alice"
"color_name"
"bar_fg"
"color"
""
"visible"
true
},
"id"
1709932823649189
"parent_group_id"
"name"
"001|h"
"color_name"
"weechat.color.nicklist_group"
"color"
"\u001b[32m"
"visible"
true
"groups"
[],
"nicks"
[]
},
"id"
1709932823649203
"parent_group_id"
"name"
"002|v"
"color_name"
"weechat.color.nicklist_group"
"color"
"\u001b[32m"
"visible"
true
"groups"
[],
"nicks"
[]
},
"id"
1709932823649210
"parent_group_id"
"name"
"999|..."
"color_name"
"weechat.color.nicklist_group"
"color"
"\u001b[32m"
"visible"
true
"groups"
[],
"nicks"
[]
],
"nicks"
[]
5.5. Hotlist
Return hotlist.
Endpoint:
GET /api/hotlist
Request example:
curl
-L
-u
'plain:secret_password'
'https://localhost:9000/api/hotlist'
Response:
HTTP
1.1
200
OK
"priority"
"date"
"2024-03-17T16:38:51.572834Z"
"buffer_id"
1710693531508204
"count"
44
},
"priority"
"date"
"2024-03-17T16:38:51.573028Z"
"buffer_id"
1710693530395959
"count"
14
},
"priority"
"date"
"2024-03-17T16:38:51.611617Z"
"buffer_id"
1710693531529248
"count"
5.6. Input
Send command or text to a buffer.
Endpoint:
POST /api/input
Body parameters:
buffer_id
(integer, optional): buffer unique identifier (not to be confused
with the buffer number, which is different)
buffer_name
(string, optional, default:
core.weechat
): buffer name
command
(string,
required
): command or text to send to the buffer
Request example: say "hello!" on channel #weechat:
curl
-L
-u
'plain:secret_password'
-X
POST
-d
'{"buffer_name": "irc.libera.#weechat", "command": "hello!"}'
'https://localhost:9000/api/input'
Response:
HTTP
1.1
204
No content
Request example: part and close channel #weechat (command executed on WeeChat
core buffer):
curl
-L
-u
'plain:secret_password'
-X
POST
-d
'{"command": "/buffer close irc.libera.#weechat"}'
'https://localhost:9000/api/input'
Response:
HTTP
1.1
204
No content
5.7. Completion
Complete user command or text in a buffer.
Endpoint:
POST /api/completion
Body parameters:
buffer_id
(integer, optional): buffer unique identifier (not to be confused
with the buffer number, which is different)
buffer_name
(string, optional, default:
core.weechat
): buffer name
command
(string,
required
): command or text to complete
position
(integer, optional, default: end of string): position in command
(first position is 0)
Request example: complete command
/qu
on channel #weechat:
curl
-L
-u
'plain:secret_password'
-X
POST
-d
'{"buffer_name": "irc.libera.#weechat", "command": "/qu"}'
'https://localhost:9000/api/completion'
Response:
HTTP
1.1
200
OK
"context"
"command"
"base_word"
"qu"
"position_replace"
"add_space"
true
"list"
"query"
"quiet"
"quit"
"quote"
5.8. Ping
Send a "ping" request.
Endpoint:
POST /api/ping
Body parameters:
data
(string, optional): string that is sent back in the response
Request example: no body:
curl
-L
-u
'plain:secret_password'
-X
POST
'https://localhost:9000/api/ping'
Response:
HTTP
1.1
204
No content
Request example: with data:
curl
-L
-u
'plain:secret_password'
-X
POST
-d
'{"data": "1702835741"}'
'https://localhost:9000/api/ping'
Response:
HTTP
1.1
200
OK
"data"
"1702835741"
5.9. Sync
Start or stop synchronization of data with WeeChat.
This resource can be used only when the client is connected with websocket
protocol, as WeeChat will push messages to the client at any time.
If this resource is used without a websocket connection, an error 403 (Forbidden)
is returned.
Endpoint:
POST /api/sync
Body parameters:
sync
(boolean, optional, default:
true
):
true
to enable synchronization
with WeeChat
nicks
(boolean, optional, default:
true
):
true
to receive nick updates
in buffers (used only if
sync
is
true
input
(boolean, optional, default:
true
):
true
to synchronize buffer input
from remote relay to local client (used only if
sync
is
true
colors
(string, optional, default:
ansi
): how to return strings with
color codes (used only if
sync
is
true
):
ansi
: return ANSI color codes
weechat
: return WeeChat internal color codes
strip
: strip colors
Request example with websocket protocol:
"request"
"POST /api/sync"
"body"
"nicks"
false
Response:
"code"
204
"message"
"No Content"
"request"
"POST /api/sync"
"request_body"
"nicks"
false
},
"body_type"
null
"body"
null
Request example without websocket protocol (not authorized):
curl
-L
-u
'plain:secret_password'
-X
POST
-d
'{"nicks": false}'
'https://localhost:9000/api/sync'
Response:
HTTP
1.1
403
Forbidden
"error"
"Sync resource is available only with a websocket connection"
6. Websocket
Websocket protocol is used to make a persistent connection between the client
and WeeChat and receive events in real-time if synchronization is enabled with
sync
resource.
Authentication must be done only one time when the websocket protocol is used
(see
authentication
).
6.1. Websocket handshake
To establish the connection, a handshake is performed on the
/api
endpoint
and looks like:
GET
/api
HTTP
1.1
Host
localhost:9000
Connection
Upgrade
Pragma
no-cache
Cache-Control
no-cache
User-Agent
Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
Upgrade
websocket
Origin
Sec-WebSocket-Version
13
Accept-Encoding
gzip, deflate, br
Accept-Language
en-US,en;q=0.9,fr;q=0.8
Sec-WebSocket-Key
2XE8VAJktqi3Tpw5QnfxVQ==
Sec-WebSocket-Extensions
permessage-deflate; client_max_window_bits
WeeChat returns its handshake response to confirm that websocket protocol is
properly supported (and authentication was successful):
HTTP
1.1
101
Switching Protocols
Upgrade
websocket
Connection
Upgrade
Sec-WebSocket-Accept
PaY9vRflWeOKuD0/F7e5gD9At9U=
Sec-WebSocket-Extensions
permessage-deflate; client_max_window_bits
Note
The
Sec-WebSocket-Accept
value returned is the SHA-1 hash of the value received,
concatenated to the GUID
258EAFA5-E914-47DA-95CA-C5AB0DC85B11
(the SHA-1 is
encoded in base64).
In the example above, the SHA-1 of
2XE8VAJktqi3Tpw5QnfxVQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11
is
PaY9vRflWeOKuD0/F7e5gD9At9U=
(in base64).
6.2. Frames
When the client is connected via the websocket protocol:
requests and responses are in JSON, inside websocket frames
if synchronization is enabled with
sync
resource, WeeChat
can send JSON frames at any time to the client.
Requests to WeeChat are made with a JSON object containing these fields:
request
(string): the HTTP method and path (example:
GET /api/buffers?lines=-100
body
(object or array): the body (optional, for
POST
and
PUT
methods)
request_id
(string): identifier sent back in the response
Multiple requests can be sent at once using an array of objects, each object
being a separate request.
Requests are executed in the order received (see example below).
Responses to client are made with a JSON object containing these fields:
code
(integer): HTTP response code (example:
200
message
(string): message for the code (example:
OK
request
(string): the request (example:
GET /api/buffers?lines=-100
request_body
(object): the request body, or
null
if the request had no body
request_id
(string): the request id, or
null
if the request had no id
body_type
(string): type of objects returned in body (see below), or
null
if the response has no body
body
(object or array): the body returned, or
null
if the response has no body
Body types that can be returned:
handshake
(object)
version
(object)
buffers
(array)
buffer
(object)
lines
(array)
line
(object)
nick_group
(object)
nick
(object)
hotlist
(object)
ping
(object)
Tip
You can browse these schemas online:
WeeChat Relay API
Request example: get version:
"request"
"GET /api/version"
"request_id"
"get_version"
Response:
"code"
200
"message"
"OK"
"request"
"GET /api/version"
"request_body"
null
"request_id"
"get_version"
"body_type"
"version"
"body"
"weechat_version"
"4.2.0-dev"
"weechat_version_git"
"v4.1.0-143-g0b1cda1c4"
"weechat_version_number"
67239936
"relay_api_version"
"0.0.1"
"relay_api_version_number"
Request example: say "hello!" on channel #weechat:
"request"
"POST /api/input"
"body"
"buffer_name"
"irc.libera.#weechat"
"command"
"hello!"
Response:
"code"
204
"message"
"No Content"
"request"
"POST /api/input"
"request_body"
"buffer_name"
"irc.libera.#weechat"
"command"
"hello!"
},
"request_id"
null
"body_type"
null
"body"
null
Requests example: send two requests at once: get list of all buffers with lines
and nicks, then synchronize with the remote:
"request"
"GET /api/buffers?lines=-1000&nicks=true&colors=weechat"
"request_id"
"initial_sync"
},
"request"
"POST /api/sync"
"body"
"colors"
"weechat"
Note
It is recommended to send the synchronization request together with the first
request that is fetching data, so that no events are missed.
First response (body with buffers is truncated for readability):
"code"
200
"message"
"OK"
"request"
"GET /api/buffers?lines=-1000&nicks=true&colors=weechat"
"request_body"
null
"request_id"
"initial_sync"
"body_type"
"buffers"
"body"
"id"
1709932823238637
"name"
"core.weechat"
"short_name"
"weechat"
"number"
"type"
"formatted"
Second response:
"code"
204
"message"
"No Content"
"request"
"POST /api/sync"
"request_body"
"colors"
"weechat"
},
"request_id"
null
"body_type"
null
"body"
null
WeeChat pushes data to the client at any time on some events: when lines are
displayed, buffers added/removed/changed, nicks added/removed/changed, etc.
The messages sent to client have the following fields:
code
message
Event
event_name
(string): the event name (name of signal or hsignal)
buffer_id
(integer): the buffer unique identifier, set only for sub-objects,
-1 in other cases
The following events are sent to the client, according to synchronization options:
Event name
Buffer id
Body type
Body
buffer_opened
buffer id
buffer
buffer with all lines and nicks
buffer_type_changed
buffer id
buffer
buffer
buffer_moved
buffer id
buffer
buffer
buffer_merged
buffer id
buffer
buffer
buffer_unmerged
buffer id
buffer
buffer
buffer_hidden
buffer id
buffer
buffer
buffer_unhidden
buffer id
buffer
buffer
buffer_renamed
buffer id
buffer
buffer
buffer_title_changed
buffer id
buffer
buffer
buffer_time_for_each_line_changed
buffer id
buffer
buffer
buffer_localvar_added
buffer id
buffer
buffer
buffer_localvar_changed
buffer id
buffer
buffer
buffer_localvar_removed
buffer id
buffer
buffer
buffer_cleared
buffer id
buffer
buffer
buffer_closing
buffer id
buffer
buffer
buffer_closed
buffer id
null
null
buffer_line_added
buffer id
line
buffer line
buffer_line_data_changed
buffer id
line
buffer line
input_text_changed
buffer id
buffer
buffer
input_text_cursor_moved
buffer id
buffer
buffer
nicklist_group_changed
buffer id
nick_group
nick group
nicklist_group_added
buffer id
nick_group
nick group
nicklist_group_removing
buffer id
nick_group
nick group
nicklist_nick_added
buffer id
nick
nick
nicklist_nick_removing
buffer id
nick
nick
nicklist_nick_changed
buffer id
nick
nick
upgrade
(1)
-1
null
null
upgrade_ended
(1)
-1
null
null
quit
-1
null
null
Note
(1)
The events
upgrade
and
upgrade_ended
are sent only if the client is
connected with plain text (no TLS), because with TLS the client is disconnected
before the upgrade is done (upgrade of TLS connections is not supported).
Example: new buffer: channel
#weechat
has been joined:
"code"
"message"
"Event"
"event_name"
"buffer_opened"
"buffer_id"
1709932823649069
"body_type"
"buffer"
"body"
"id"
1709932823649069
"name"
"irc.libera.#test"
"short_name"
""
"number"
"type"
"formatted"
"hidden"
false
"title"
""
"modes"
"+nt"
"input_prompt"
"\u001b[92m@\u001b[96malice\u001b[48;5;22m(\u001b[39mi\u001b[48;5;22m)"
"input"
""
"input_position"
"input_multiline"
false
"nicklist"
true
"nicklist_case_sensitive"
false
"nicklist_display_groups"
false
"time_displayed"
true
"local_variables"
"plugin"
"irc"
"name"
"libera.#test"
"type"
"channel"
"nick"
"alice"
"host"
"~alice@example.com"
"server"
"libera"
"channel"
"#test"
},
"keys"
[],
"lines"
[]
Example: new line displayed on channel
#weechat
"code"
"message"
"Event"
"event_name"
"buffer_line_added"
"buffer_id"
1709932823649069
"body_type"
"line"
"body"
"id"
"index"
-1
"date"
"2024-01-07T08:54:00.179483Z"
"date_printed"
"2024-01-07T08:54:00.179483Z"
"displayed"
true
"highlight"
false
"notify_level"
"prefix"
"alice"
"message"
"hello!"
"tags"
"irc_privmsg"
"self_msg"
"notify_none"
"no_highlight"
"prefix_nick_white"
"nick_alice"
"log1"
Example: nick
bob
added with operator status in channel
#weechat
"code"
"message"
"Event"
"event_name"
"nicklist_nick_added"
"buffer_id"
1709932823649069
"body_type"
"nick"
"body"
"id"
1709932823649902
"parent_group_id"
1709932823649181
"prefix"
"@"
"prefix_color_name"
"lightgreen"
"prefix_color"
"\u001b[92m"
"name"
"bob"
"color_name"
"bar_fg"
"color"
""
"visible"
true
Example: channel buffer
#weechat
has been closed:
"code"
"message"
"Event"
"event_name"
"buffer_closed"
"buffer_id"
1709932823649069
"body_type"
null
"body"
null
Example: WeeChat is upgrading:
"code"
"message"
"Event"
"event_name"
"upgrade"
"buffer_id"
-1
"body_type"
null
"body"
null
Example: upgrade of WeeChat is done:
"code"
"message"
"Event"
"event_name"
"upgrade_ended"
"buffer_id"
-1
"body_type"
null
"body"
null
Version 4.9.0-dev