mod_rest

Introduction

This is yet another RESTful API for sending and receiving stanzas via Prosody. It can be used to build bots and components implemented as HTTP services.

Usage

Enabling

Component "rest.example.net" "rest"
rest_credentials = "Bearer dmVyeSBzZWNyZXQgdG9rZW4K"

Sending stanzas

The API endpoint becomes available at the path /rest, so the full URL will be something like https://your-prosody.example:5281/rest.

To try it, simply curl an XML stanza payload:

curl https://prosody.example:5281/rest \
    --oauth2-bearer dmVyeSBzZWNyZXQgdG9rZW4K \
    -H 'Content-Type: application/xmpp+xml' \
    --data-binary '<message type="chat" to="user@example.org">
            <body>Hello!</body>
        </body>'

or a JSON payload:

curl https://prosody.example:5281/rest \
    --oauth2-bearer dmVyeSBzZWNyZXQgdG9rZW4K \
    -H 'Content-Type: application/json' \
    --data-binary '{
           "body" : "Hello!",
           "kind" : "message",
           "to" : "user@example.org",
           "type" : "chat"
        }'

The Content-Type header is important!

Replies

A POST containing an <iq> stanza automatically wait for the reply, long-polling style.

curl https://prosody.example:5281/rest \
    --oauth2-bearer dmVyeSBzZWNyZXQgdG9rZW4K \
    -H 'Content-Type: application/xmpp+xml' \
    --data-binary '<iq type="get" to="example.net">
            <ping xmlns="urn:xmpp:ping"/>
        </iq>'

Replies to other kinds of stanzas that are generated by the same Prosody instance MAY be returned in the HTTP response. Replies from other entities (connected clients or remote servers) will not be returned, but can be forwarded via the callback API described in the next section.

Receiving stanzas

TL;DR: Set this webhook callback URL, get XML POST-ed there.

Component "rest.example.net" "rest"
rest_credentials = "Bearer dmVyeSBzZWNyZXQgdG9rZW4K"
rest_callback_url = "http://my-api.example:9999/stanzas"

To enable JSON payloads set

rest_callback_content_type = "application/json"

Example callback looks like:

POST /stanzas HTTP/1.1
Content-Type: application/xmpp+xml
Content-Length: 102

<message to="bot@rest.example.net" from="user@example.com" type="chat">
<body>Hello</body>
</message>

or as JSON:

POST /stanzas HTTP/1.1
Content-Type: application/json
Content-Length: 133

{
   "body" : "Hello",
   "from" : "user@example.com",
   "kind" : "message",
   "to" : "bot@rest.example.net",
   "type" : "chat"
}

Replying

To accept the stanza without returning a reply, respond with HTTP status code 202 or 204.

HTTP status codes in the 4xx and 5xx range are mapped to an appropriate stanza error.

For full control over the response, set the Content-Type header to application/xmpp+xml and return an XMPP stanza as an XML snippet.

HTTP/1.1 200 Ok
Content-Type: application/xmpp+xml

<message type="chat">
<body>Yes, this is bot</body>
</message>

Payload format

JSON

{
   "body" : "Hello!",
   "kind" : "message",
   "type" : "chat"
}

Further JSON object keys as follows:

Messages
kind
"message"
type
Commonly "chat" for 1-to-1 messages and "groupchat" for group chat messages. Others include "normal", "headline" and "error".
body
Human-readable message text.
subject
Message subject or MUC topic.
html
HTML.
oob_url
URL of an out-of-band resource, often used for images.
Presence
kind
"presence"
type
Empty for online or "unavailable" for offline.
show
Online status, away, dnd etc.
status
Human-readable status message.
join
Boolean. Join a group chat.
Info-Queries

Only one type of payload can be included in an iq.

kind
"iq"
type
"get" or "set" for queries, "response" or "error" for replies.
ping
Send a ping. Get a pong. Maybe.
disco
Retrieve service discovery information about an entity.
items
Discover list of items (other services, groupchats etc).

XML

<message type="" id="" to="" from="" xml:lang="">
...
</message>

An XML declaration (<?xml?>) MUST NOT be included.

The payload MUST contain one (1) message, presence or iq stanza.

The stanzas MUST NOT have an xmlns attribute, and the default/empty namespace is treated as jabber:client.

Examples

Python / Flask

Simple echo bot that responds to messages as XML:

from flask import Flask, Response, request
import xml.etree.ElementTree as ET

app = Flask("echobot")


@app.before_request
def parse():
    request.stanza = ET.fromstring(request.data)


@app.route("/", methods=["POST"])
def hello():
    if request.stanza.tag == "message":
        return Response(
            "<message><body>Yes this is bot</body></message>",
            content_type="application/xmpp+xml",
        )

    return Response(status=501)


if __name__ == "__main__":
    app.run()

And a JSON variant:

from flask import Flask, Response, request, jsonify

app = Flask("echobot")


@app.route("/", methods=["POST"])
def hello():
    print(request.data)
    if request.is_json:
        data = request.get_json()
        if data["kind"] == "message":
            return jsonify({"body": "hello"})

    return Response(status=501)


if __name__ == "__main__":
    app.run()

Remember to set rest_callback_content_type = "application/json" for this to work.

Compatibility

Requires Prosody trunk / 0.12