Microsub-spec - IndieWeb
Jump to content
From IndieWeb
Microsub icon
The
Microsub specification
provides a standardized way for clients to consume and interact with feeds collected by a server.
See the
main Microsub page
for current clients (web, iOS, Android) to start using Microsub today!
The Microsub server is responsible for managing the accounts you follow, retrieving updates from them, and the Microsub endpoint provides the feed entries in a normalized format for easy consumption by clients.
Status
This is an early
Editor's Draft
, and feedback is encouraged.
Participate
Wiki (
Feedback
GitHub:
issues and brainstorming
discuss
Editor
Aaron Parecki
License
Per
CC0
, to the extent possible under law, the editor(s) and contributors have waived all copyright and related or neighboring rights to this work. In addition, as of 2026-04-24, the editor(s) and contributors (2017-04-09 onward) have made this specification available under the
Open Web Foundation Agreement Version 1.0
Design Goals
The goal of Microsub is to simplify the process of building a
reader
, since there are many moving parts when consuming external content.
In general, when subscribing to a feed, a reader should use
WebSub
if the feed is enabled with it, but may need to fall back to polling if not. Depending on the format of the feed, there can be many variations in the actual data available at the feed. For example, there are several different ways an
h-entry
can represent the author of the entry, described at
authorship
. There are also multiple ways a list of
h-entrys
can appear in an
h-feed
The role of the Microsub server is to normalize the data in the wild and turn it into a simpler format for displaying in clients. Clients should never have to second guess or doubt any data they receive from the Microsub server. The assumption is that the server has done all the verification and normalizing of the data, and it is ready to display to the user. The fewer checks and conditionals that clients have to write the better.
Where HTML or Microformats may provide multiple ways of representing content, Microsub instead has just one way of representing data. For example, in Microformats the "author" property may be a string URL or an
h-card
, but in Microsub it will always be a jf2 card.
Endpoints
The Microsub endpoint is where the client will make all API requests. All API requests require authentication with a Bearer access token that the client needs to obtain. If the client does not have a preexisting relationship with the server, then the following method of discovery and authorization should be used to obtain an access token and discover the Microsub endpoint.
It is possible for a client to be pre-configured with a Microsub endpoint, or to use other methods of obtaining an access token if there is a preexisting relationship between the client and the server providing the Microsub endpoint.
Discovery
The client first performs discovery on the user's profile URL to find the Microsub endpoint and authorization endpoint. Given a user's profile URL, perform an HTTP GET request and look for either a
or HTTP
Link
header with a
rel
value of
microsub
. Additionally, look for links with rel values
authorization_endpoint
and
token_endpoint
Link:
Link:
Link:
The Microsub endpoint URL MUST NOT contain a fragment, and MAY contain query string components. If the URL contains a query string, then any GET requests MUST properly append the additional parameters to the query string, and POST requests MUST NOT send the query string properties in the post body. e.g. making a GET request with the additional query string component "action=config" to the endpoint "/endpoints?type=microsub" would result in a URL of "/endpoints?type=microsub&action=config"
(Note: The client will likely want to also find the
Micropub
endpoint for the user so that the client can post replies and other interactions to the user's website.)
Authentication and Authorization
Authorization is handled the same way as
Micropub
, using
IndieAuth
to
obtain an access token
Error Responses
For consistency, look to how
Micropub
suggests how error messages should be structured when reporting errors at
Implementations
This is to track implementations using the same error response used in the Micropub spec.
Servers
Clients
Channels
Channels are described by the following properties:
uid
- a string representation of a user-specific unique ID for the channel. This uid will be unique for each user, but may be duplicated across different users. Some implementations will use constant strings such as "example", while others may use numeric database IDs such as "15029932", a random string like "1NYnmUVYR5qBVXYBzt", or a URL such as "http://user.example.com/channel/foo". The valid characters for a uid are any URL-safe character.
name
- the display name for the channel. This may include any valid UTF-8 sequence. The client should use this name when displaying the name of the channel in the interface.
"uid": "indieweb",
"name": "IndieWeb"
Servers must always have a channel with the uid
notifications
, and must always have at least one other channel for a user.
Some actions may want to apply to every channel, so the uid of
global
is reserved for this purpose. Actions such as
mute
that want to mute a user across every channel use the channel uid of
global
Users
All users are identified by profile URLs, with some constraints. User profile URLs MUST use either the
http
or
https
scheme, and MAY contain path and query string components, and MUST NOT contain fragments.
Actions
All operations in Microsub are considered "actions", and are specified with a query string or form body parameter of
action
channels
preview
follow / unfollow
timeline
mute / unmute
block / unblock
Actions that operate within the context of a channel can accept a query string or form body parameter of
channel
specifying the
uid
of the channel to use.
Timelines
action=timeline
Retrieve Entries in a Channel
GET
Retrieve the entries in a given channel.
Parameters:
action=timeline
channel={uid}
after={cursor}
before={cursor}
The response will include a property
items
with an array of post objects. See
#Posts
for documentation on the format of items.
"items": [
{ ... },
{ ... }
],
"paging": {
"after": "xxxxx",
"before": "xxxxx"
Source Parameter
Issues and Discussion
Proposal is the addition of a 'source' parameter into the query. If added, this will limit the return to the specific source feed in question.
Implementations
This is to track implementations using the source parameter for inclusion into the spec.
Servers
Drupal
Aperture
Clients
Monocle
Indigenous for Android
See
#Paging
for more details on the paging mechanism.
Mark Entries Read
POST
To mark one or more individual entries as read or unread:
Parameters:
action=timeline
method=mark_read (or mark_unread)
channel={uid}
entry={entry-id} or entry[]={entry-id}
To mark an entry read as well as everything before it in the timeline: (does not apply to mark_unread)
action=timeline
method=mark_read
channel={uid}
last_read_entry={entry-id}
These commands only affect the entry within the context of the given channel's timeline, and do not affect other timelines.
Remove Entry from a Channel
POST
Parameters:
action=timeline
method=remove
channel={uid}
entry={entry-id} or entry[]={entry-id}
The client can pass one or more entry IDs (found via the
_id
property on an entry) to remove it from the channel. The server may decide whether to actually delete the entry or just hide it from view. The entry will no longer show up in the given channel's timeline, and this command will not affect the given entry in other channels.
Servers will likely use their own generated IDs for the entry ID, so clients must not make assumptions about the uniqueness of these values.
action=search
The "search" action exists to provide a UI for the server to respond with the full URL of possible things to subscribe to. For example, a user should not be expected to type the exact URL of a feed to subscribe to, but instead should be able to enter partial matches, e.g. entering
aaronparecki.com
should return the full URL of
Using the "search" action, a client can provide a single text field where the user can enter either partial URLs or even arbitrary search terms, and the server can reply with a list of URLs that can actually be subscribed to. This also provides the ability to have a confirmation step where users can see a preview of what they will be subscribing to before they actually do so.
If the search term is a URL or partial URL, the Microsub server SHOULD fetch the URL if not already known, and discover any feeds at that URL that can be subscribed to. The server may also return feeds that are already known that match the search term, for example if a similar URL has already been subscribed to. Note that if the server supports multiple users, it should take care to avoid leaking "private" subscription URLs between users.
POST
Request
action=search
query=
POST /microsub
Content-type: application/x-www-form-urlencoded
action=search&query=aaronparecki.com
Response
HTTP/1.1 200 Ok
Content-type: application/json
"results": [
"type": "feed",
"url": "https://aaronparecki.com/",
"name": "Aaron Parecki",
"photo": "https://aaronparecki.com/images/profile.jpg",
"description": "Aaron Parecki's home page"
},
"type": "feed",
"url": "https://percolator.today/",
"name": "Percolator",
"photo": "https://percolator.today/images/cover.jpg",
"description": "A Microcast by Aaron Parecki",
"author": {
"name": "Aaron Parecki",
"url": "https://aaronparecki.com/",
"photo": "https://aaronparecki.com/images/profile.jpg"
},
{ ... }
The response results are a
jf2
version of the
h-feed
vocabulary. The only required properies are the type and URL.
type - (required)
feed
url - (required) the URL of the result which can be used in a follow action to follow the feed
name - the display name of the feed
photo - a photo or icon representing the feed
description - a description of the feed, either as reported by the feed or generated by the Microsub server
author - a
jf2
h-card
describing the author of the feed. For multi-author feeds this may be an h-card representing the organization, or may be omitted
TBD: It might be nice to show an approximate frequency of posts, e.g.
Feedly
, and
Searching for Content
Search past posts that the Microsub server has indexed.
GET or POST
action=search
channel={uid}
query={term}
author={url}
category={tag}
kind={value}
source={url}
before={cursor}
after={cursor}
limit={n}
The presence of the "channel" parameter indicates to the server that the client wants to search for posts rather than search for feeds. The channel parameter can be set to an individual channel uid, or "global" to search across all channels.
If the server receives a request with the "channel" parameter, it MUST treat the request as a search for content.
The "query" parameter is a free-text search term. The server SHOULD search indexed text content such as the post name, summary, and content text when available.
The "author" parameter filters results to posts with a matching author profile URL.
The "category" parameter filters results to posts with a matching value in the post's
category
property.
The "kind" parameter filters results to posts matching one or more standardized inferred kinds. A post MAY match more than one kind.
Servers SHOULD support the following kind values:
like
repost
reply
checkin
photo
video
audio
A post matches these kinds when the corresponding properties are present:
like
matches posts with a
like-of
property
repost
matches posts with a
repost-of
property
matches posts with a
bookmark-of
property
reply
matches posts with an
in-reply-to
property
checkin
matches posts with a
checkin
property
photo
matches posts with a non-empty
photo
property
video
matches posts with a non-empty
video
property
audio
matches posts with a non-empty
audio
property
Servers MAY support additional kind values.
The "source" parameter filters results to posts from the specified source feed URL.
The server MAY support searching with only the "query" parameter, only structured filter parameters, or a combination of both. If none of "query", "author", "category", "kind", or "source" are provided, the server SHOULD return an error.
If multiple different filter parameters are provided, the server SHOULD combine them with a logical AND. If the same parameter is repeated multiple times, the server SHOULD combine those values with a logical OR.
Search results are returned in the same format as timeline responses.
"items": [
{ ... },
{ ... }
],
"paging": {
"after": "xxxxx",
"before": "xxxxx"
Servers SHOULD support the "before" and "after" parameters using the same paging model as timelines.
Servers SHOULD support the "limit" parameter to allow clients to request a specific number of results. Servers MAY enforce minimum or maximum values for "limit".
Results SHOULD be returned newest-first.
Example request:
POST /microsub
Content-type: application/x-www-form-urlencoded
action=search
&channel=global
&query=webmention
&author=
&category=indieweb
&kind=reply
&source=
&limit=20
Example response:
HTTP/1.1 200 Ok
Content-type: application/json
"items": [
"type": "entry",
"name": "Replying to your post",
"url": "
",
"published": "2026-03-26T14:12:00+00:00",
"content": {
"text": "I finally got webmentions working."
},
"author": {
"type": "card",
"url": "
",
"name": "Alice"
},
"category": [
"indieweb",
"webmention"
],
"in-reply-to": "
",
"_source": {
"url": "
",
"name": "Alice"
},
"_id": "12345"
],
"paging": {
"before": "12345"
Preview
action=preview
POST
action=preview
url={url}
The "preview" action exists so that the client can display a preview of a URL to the user before the user wants to create a subscription for it. The preview should show as much about the URL as the server can determine, such as basic profile information about the user, and a few recent entries by the user. There should be no permanent side effects created by previewing a URL, and as much as possible, the URL being previewed should not be provided with identifying information of the user who is previewing the URL.
The response includes the list of items in the feed if available, in the same format as returned by the
#Timelines
API call.
Following
action=follow
GET
action=follow
channel={uid}
Retrieve the list of feeds being followed in the given channel.
Response
"items": [
"type": "feed",
"url": "http://tantek.com/"
The feed items may contain additional properties that describe the feed, see
POST
Follow a new URL in a channel.
action=follow
channel={uid}
url={url}
POST /microsub
Content-type: application/x-www-form-urlencoded
action=follow&channel=indieweb&url=http://tantek.com/
When a request to the follow endpoint is made, the Microsub server registers the follow action, and begins delivering content at that URL into the channel. The Microsub server can subscribe to the target URL via any mechanism available, but most often will attempt a
WebSub
subscription for its HTML+Microformats, or Atom/RSS feed, and fall back to polling if that fails.
New entries at the followed URL will appear in the channel when fetched from the channel's timeline. The Microsub server may fetch existing entries in the feed and add them to the channel at its discretion.
The response is a JSON representation of the channel, the same as is returned when listing all feeds followed in a channel.
"type": "feed",
"url": "http://tantek.com/"
Unfollowing
action=unfollow
POST
action=unfollow
channel={uid}
url={url}
Unfollow a feed from a channel.
POST /microsub
Content-type: application/x-www-form-urlencoded
action=unfollow&channel=indieweb&url=http://tantek.com/
The Microsub server may remove all of this feed's items from the channel, or may leave them in place, at its discretion. If you are used to treating these channels as an IRC or Slack timeline, it would be more appropriate to leave the old items in the channel, just stop delivering new ones. However if you are more used to treating these channels as a Twitter or Facebook feed, then you may want the server to remove them from the channel.
TODO: Should there be another parameter for the client to specify whether to remove previous entries or leave them?
Muting
action=mute
Clients should provide a "mute" option in the interface. This allows the user to mute someone's profile, hiding all posts with the muted user's profile URL as the author from being displayed.
Muting users will cause all posts by the muted user to be hidden from display. The server MAY still store the posts internally, so that un-muting the user will cause past entries to appear again.
Any side effect at the server SHOULD NOT cause the muted user to know they have been muted. Muting users SHOULD NOT have any externally visible side effects.
For example, in the context of the
Salmention
spec, the server should still behave as if the muted user was not muted.
GET
action=mute
channel={uid}
Retrieve the list of users that are muted in the given channel.
Request
GET /microsub?action=mute&channel=indieweb HTTP/1.1
Response
HTTP/1.1 200 Ok
Content-type: application/json
"items": [
"type": "card",
"url": "http://annoying.example.com/",
"name": "Annoying Person",
"photo": "http://annoying.example.com/photo.jpg"
},
...
POST
action=mute
channel={uid}
url={url}
Mute a user in a channel, or with the uid
global
mutes the user across every channel.
Request
POST /microsub HTTP/1.1
Content-type: application/x-www-form-urlencoded
action=mute&channel=indieweb&url=https://annoying.example.com/
Response
HTTP/1.1 200 Ok
Unmuting
POST
action=unmute
channel={uid}
url={url}
To unmute a user, use
action=unmute
and provide the URL of the account to unmute. Unmuting an account that was previously not muted has no effect and should not be considered an error. Using the uid
global
will unmute them in every channel.
Request
POST /microsub HTTP/1.1
Content-type: application/x-www-form-urlencoded
action=unmute&channel=indieweb&url=https://annoying.example.com/
Response
HTTP/1.1 200 Ok
Blocking
action=block
Blocking users will cause all previous posts by the blocked user to be hidden or deleted, and future posts by that user should not be stored. Additionally, the server SHOULD NOT produce any content or side effect that would notify the blocked user about a post. It is acceptable for the blocked user to know they have been blocked.
For example, in the context of the Webmention spec, the server should not send webmentions even if the user mentions the blocked user in a post. In the context of the Salmention spec, the server should stop sending follow-up webmentions to the blocked user.
GET
action=block
channel={uid}
Retrieve the list of users that are blocked in the given channel.
TODO: document the response format
POST
action=block
channel={uid}
url={url}
Block a user in a channel, or with the uid
global
blocks the user across every channel.
Channels
action=channels
GET
action=channels
Retrieve the list of channels for the user.
The response will contain a
channels
property with the list of channel uids and names. The
uid=notifications
channel must always be the first in the list, as clients are expected to treat it separately and not show it in the channel list.
GET /microsub?action=channels
Authorization: Bearer xxxxxxxxx
HTTP/1.1 200 Ok
Content-type: application/json
"channels": [
"uid": "notifications",
"name": "Notifications",
"unread": 0
},
"uid": "31eccfe322d6c48c50dea2c84efc74ff",
"name": "IndieWeb"
"unread": 9
},
"uid": "1870e67e924856dc7e4c37732b303b45",
"name": "W3C",
"unread": true
Servers
should
support tracking the read state of items in channels, and return the number of unread items, or a boolean, when the channel list is queried. If the server does not support read state tracking, then the server
must not
return the
unread
property in the response.
For channels that support full unread count tracking, the value of the
unread
property will be an integer. For channels that support only boolean unread tracking (whether there are new entries or not), the value will be
true
or
false
Proposal to extend channel method to optionally include all sources.
A new parameter to add the return of the sources in the channel.
The addition of a channel parameter to only return data on a single channel.
Return would include the _id property inside the _source property.
POST
To create, update, or delete channels, the client sends a POST request with the
channels
action.
To create a channel, the client includes the name of the channel to create. The uid of the channel will be assigned by the server.
action=channels
name={channel name}
To update a channel, the client includes the uid of the channel to update in the
channel
parameter, and includes the new name of the channel. Changing the name of the channel MAY change the
uid
of the channel, and if it does, the server MUST return the new
uid
in the response.
action=channels
channel={uid}
name={channel name}
To delete a channel, the client includes an additional parameter
method=delete
. Note that the
notifications
channel cannot be deleted.
action=channels
method=delete
channel={uid}
Both creating and updating a channel MUST return the
uid
and
name
properties for the channel.
Create a Channel
Request
action=channels
name={channel name}
POST /microsub HTTP/1.1
Content-type: application/x-www-form-urlencoded
action=channels&name=Coworkers
Response
HTTP/1.1 200 Ok
Content-type: application/json
"uid": "2c5e5d7c4d57da68cb03c972846e827845af974c9b",
"name": "Coworkers"
Update a Channel
Request
action=channels
channel={uid}
name={channel name}
POST /microsub HTTP/1.1
Content-type: application/x-www-form-urlencoded
action=channels
&channel=2c5e5d7c4d57da68cb03c972846e827845af974c9b
&name=Friends
Response
HTTP/1.1 200 Ok
Content-type: application/json
"uid": "2c5e5d7c4d57da68cb03c972846e827845af974c9b",
"name": "Friends"
Delete a Channel
Request
action=channels
method=delete
channel={uid}
POST /microsub HTTP/1.1
Content-type: application/x-www-form-urlencoded
action=channels
&method=delete
&channel=2c5e5d7c4d57da68cb03c972846e827845af974c9b
Response
HTTP/1.1 200 Ok
Content-type: application/json
Set Channel Order
Channels are ordered according to the user's preference. When returning the list of channels, the
notifications
channel must always be the first in the list, as clients are expected to treat it separately such as showing it as a separate icon, not in the main channel list.
To modify the order of channels, the client sends a POST request with a list of channel IDs in the new ordering.
Only the order of the channel IDs specified will be changed. While this command works equally well specifying two, three or more channels, this provides two primary methods for adjusting channel orders.
To move a channel up or down, the client can include the two IDs of adjacent channels in the new desired order.
The client can specify a new ordering for all the channels by providing the full list of channel IDs in the new order.
Request
action=channels
method=order
channels[]={uid}
channels[]={uid}
...
POST /microsub HTTP/1.1
Content-type: application/x-www-form-urlencoded
action=channels
&method=order
&channels[]=2c5e5d7c4d57da68cb03
&channels[]=c972846e827845af974c
Response
HTTP/1.1 200 Ok
Content-type: application/json
Order Algorithm
From the server's perspective, the below is a description of how to process the order command.
Given a list of channels with IDs:
[a b c d e f g h]
and a command to set the order of the following in the list:
[d a c g]
Assign the items in the initial list a numeric index:
[a b c d e f g h]
[1 2 3 4 5 6 7 8]
Build a new map with the input items and their existing numeric order:
1 => a
3 => c
4 => d
7 => g
For each item in the input list in the given order, set the value in the map:
1 => d
3 => a
4 => c
7 => g
For each item in the new map, change the value at the corresponding numeric index of the list:
[d b a c e f g h]
[1 2 3 4 5 6 7 8]
Types of Feeds
The specific types and formats of feeds that can be followed is out of scope of Microsub. Instead, it's up to the Microsub server to support whichever feed formats it wishes. Typically, Microsub servers will prefer a Microformats 2 feed such as an
h-feed
or list of
h-entry
s, and will then fall back to finding an Atom or RSS feed. Other types of feeds may be supported, but clients should not make any assumptions about which formats are supported, and should make use of the "preview" action so that users have an indication of whether a subscription will succeed.
Objects
Posts
Posts are the basic object used in the API. Posts can be short status updates, photos, videos, podcast episodes, checkins, and many other content types. Post objects returned in the "items" array MUST be valid
jf2
post objects.
(Note that the representations below should be treated as canonical examples, whereas the jf2 spec itself may provide multiple variations of values. For example, as of 2019-02-25, jf2 says that if there is one value of a property it should be returned as a string, but Microsub will always include the "photo" property as an array.)
An example of a simple jf2 post in the context of a channel timeline is below.
"type": "entry",
"published": "2017-04-28T11:58:35-07:00",
"url": "https://aaronparecki.com/2017/04/28/9/p3k-http",
"author": {
"type": "card",
"name": "Aaron Parecki",
"url": "https://aaronparecki.com/",
"photo": "https://aaronparecki.com/images/profile.jpg"
},
"category": [
"http",
"p3k",
"library",
"code",
"indieweb"
],
"photo": [
"https://aaronparecki.com/2017/04/28/9/photo.png"
],
"content": {
"text": "Finally packaged up my HTTP functions into a library! https://github.com/aaronpk/p3k-http Previously I had been copy+pasting these around to quite a few projects. Happy to have consolidated these finally!",
"html": "Finally packaged up my HTTP functions into a library! https://github.com/aaronpk/p3k-http Previously I had been copy+pasting these around to quite a few projects. Happy to have consolidated these finally!"
},
"_id": "abc987",
"_is_read": true
All properties beginning with an underscore
are set by the Microsub server and are not part of the post's contents.
_id
(string) - a unique internal ID for this entry in the given channel, used to mark an entry as read or remove it from a channel
_is_read
(boolean) - indicates whether this entry is read or unread so that clients can visually indicate the state to the user
Neither the Microsub spec nor jf2 defines the vocabularies used in posts. The vocabularies are defined by
Microformats
, and represented as
jf2
posts. Clients may also find the
Post Type Discovery
spec useful when consuming posts. A non-comprehensive list of properties and their expected values is below, based on the most common kinds of posts encountered.
entry
Entries are the base post type for most kinds of posts including
articles
notes
photos
checkins
, and more.
Properties:
type=entry
published
(string) - ISO8601 timestamp
url
(string) - the permalink of the post
uid
(string) - a unique ID for the post, often the same as the url
name
(string) - the name of the post, such as if the post is an
article
content
(object)
text
(string) - a plaintext representation of the post content. If a client displays this text in a browser, it is expected to escape any HTML entities itself.
html
(string) - an HTML representation of the post content. The Microsub server should do its own HTML sanitization, but clients may also wish to sanitize the HTML themselves according to their own security requirements before displaying the HTML.
summary
(string) the plaintext summary of a post, if available. Some Microsub apps will display just the name and summary of articles and let the user click through to read the full version.
author
(card object) - a card representing the author of the post
category
(array of strings) - a list of categories (tags) of the post
photo
(array of strings) - one or more URLs to images associated with the post
video
(array of strings) - one or more URLs to video files associated with the post
audio
(array of strings) - one or more URLs to audio files associated with the post, e.g. podcasts
like-of
repost-of
bookmark-of
in-reply-to
(array of strings) - each of these properties indicates a separate post that this post is in response to. See
like
repost
, and
reply
for more information.
syndication
(array of strings) - one or more URLs where this post has been syndicated to
checkin
(card object) - indicates this post is a checkin, and the value is a card representing the location of the checkin
_id
- a unique ID for this post in this channel. Used to reference this post when doing things like removing it from view. Note that the underscore prefix indicates this is not a property from the main post, but instead is a property generated by the Microsub server.
Indicating Item Source Proposal
This is a proposed additional property to be adopted if sufficient implementations.
url, name, photo
: display values, the client should show the name and photo and click on the URL
_id:
internal identifier for the reason this post is in this channel (e.g. the internal feed ID)
"_source": {
"_id": "9857239874",
"url":
",
"name": "Example Person",
"photo": "
Clients should show an "unfollow" button next to a post, which can make an "unfollow" request passing the _id.
Implementations
Servers
Clients
card
Cards represent people or places and are used as the values of certain properties of entries.
People:
type=card
name
(string) - the full name of this person
url
(string) - the profile URL of this person, used as the identifier for this profile
photo
(string) - the profile photo of this person
Places:
type=card
name
(string) - the full name of the place
url
(string) - the URL of the place, used as the identifier for this profile
latitude
and
longitude
(string) - the GPS coordinates of this location
street-address
locality
region
country
(string) - the location of this place
Channels
Feeds
Paging
Microsub uses a cursor-based pagination model, which provides the most flexibility to server implementations while still being easy to use by clients.
For API requests that paginate their results (e.g.
#Timelines
), there is an additional object returned with the values needed to page through those results.
"items": [ ... ],
"paging": {
"before": "xxxxx",
"after": "xxxxx"
If there are any items returned in the response, the server MUST return a "before" value that will retrieve items before all the returned items in the list.
If there are no items returned in the response, the server MUST NOT return a "before" value.
If there are additional items available that were not returned in the response, the server MUST return an "after" value that will retrieve the next page of items.
If there are no more items available, the server MUST NOT return an "after" value.
As far as the client is concerned, the "before" and "after" values are arbitrary strings. This allows the server to internally use whatever specific implementation is most appropriate for its backend technology. (Often this will be either a timestamp or a unique ID identifying the first and last items in the returned list.)
To make a timeline request for the next page of results, the client adds the "after=xxxxx" parameter to the query string. This allows easy navigation through the whole list of items in the channel.
While the user is reading the timeline, the client will likely also want to poll the timeline to see if new posts have been added since it was originally requested. Since not all Microsub servers will support streaming, the client needs an efficient way to poll for new items. The client adds "before=xxxxx" to the query string to request items that come before the first item returned in the previous request. This way the client can poll that until new items appear, and only the new items will be returned.
Example Paging Workflow
The user loads the client and makes a request for the timeline for the default channel:
/microsub?action=timeline
The server replies with the newest 20 items, and includes
before=5a1713e55a171588
and
after=5a1713e55a17136c
The user scrolls to the bottom and clicks "load more". The client makes a request for the next set of results:
/microsub?action=timeline&after=5a1713e55a17136c
The server replies with 3 more items, and does not include an "after" paging cursor in the response, indicating that there are no more items in the timeline.
Meanwhile, in the background, the client polls the timeline to find newer items by using the first "before" cursor that was returned in the initial request:
/microsub?action=timeline&before=5a1713e55a171588
The server replies with an empty
items
list and an empty
paging
object indicating there are no new items
After some interval, the client polls for new items again
/microsub?action=timeline&before=5a1713e55a171588
In the time between the two polls, there have been 25 new items added to the timeline, more than one page of results. The server replies with the newest 20
items
list, and includes new before and after values,
before=5a1724ad5a171599
and
after=5a1722ea5a171280
Since the client sees there is an "after" cursor, it immediately fetches the next page of results using the original "before" and the new "after" value:
/microsub?action=timeline&before=5a1713e55a171588&after=5a1722ea5a171280
The server replies with the remaining 5 items, and no new cursors since there is no more data missing.
The client then continues polling with the latest "before" cursor it received
See the
tests in Aperture
for an example of the API requests made.
Limiting Results
Microsub servers SHOULD set a default limit on the number of items returned in lists. A reasonable default limit is 20 items. Microsub servers SHOULD support an additional query parameter
limit
which clients can use to indicate the requested limit of number items returned. Microsub servers MAY set an upper or lower bound on the values they accept for the
limit
, and MAY return a different number of items in the list than the client requets, for any reason. Clients should not expect the number of results returned to exactly match the number of results requested.
Authentication and Authorization Details
The client builds an
IndieAuth
authorization request URL at the
authorization endpoint
, and directs the user's browser there. In a native client, the client should use a system-native browser, rather than using a web view embedded in the application. See
OAuth 2.0 for Native Apps
for more details.
Build a URL with the following query parameters:
me={the user's profile URL}
- the URL that the user entered at which the Microsub endpoint was found
response_type=code
client_id={the client's URL, e.g. its home page}
state={random state}
- the client should generate a unique state value, and verify that it matches when the user is redirected
redirect_uri={the client's redirect URI}
- for native apps, this may include a custom URL scheme
scope={requested scope}
- a space-separated list of scopes that the client is requesting
Scopes
Microsub defines the following scopes:
read
- this is the minimum scope clients should request. this allows clients to have read access to channels.
follow
- allows the client to manage the following list
mute
- allows the client to mute and unmute users
block
- allows the client to block and unblock users
channels
- allows the client to manage channels
Additionally, the client may request Micropub scopes, in order for the user to be able to reply or like posts from within the client.
create
update
delete
The recommended set of scopes to request is
read follow mute block create
, which enables a rich set of interaction on the client, while also protecting the security of the user by default.
The user will visit the authorization endpoint, and if they approve the request, their browser will be redirected back to the client's redirect URI with a
code
and
state
in the URL.
HTTP/1.1 302 Found
Location: https://client.example/redirect?code=xxxxxxxx
&state=1234567890
The client verifies the state value matches the state it generated for the initial request, and can then exchange the authorization code for an access token.
The client makes a POST request to the token endpoint initially discovered, with the following parameters:
grant_type=authorization_code
code=xxxxxxxxx
redirect_uri={the client's redirect URI}
client_id={the client's URL}
The response will be a JSON object with the following keys:
"access_token": "XXXXXXXXXXX",
"scope": "read follow mute block create",
"me": "https://aaronpk.example/"
The
me
value returned MAY be different from the original
me
value input, but MUST have a matching host name. This enables support for multi-user websites, and allows the user's server to normalize profile URLs, e.g. it will always return
even if the user initially enters
The scopes returned MAY be different from what the client requested, based on whether the user choose to deny certain scopes, or grant additional scopes during the authorization request.
Design Considerations
Why a single endpoint instead of individual endpoints for each operation
Many similar APIs such as the
Twitter API
or
Wordpress API
use unique URLs for each type of operation: following, muting, fetching posts, etc. Microsub instead takes an
RPC
style approach, where all requests are made against a single endpoint, with the operation is specified with a query or form parameter.
This allows more flexibility in the design of the server, since the spec is not imposing a URL design on the server, each can choose a URL for the Microsub endpoint that makes sense for itself.
This also makes clients easier to write, since all requests are made against one base URL rather than needing to either keep track of a fixed URL pattern, or have a configurable URL pattern.
Feedback
Feel free to leave notes here or open an
issue on GitHub
Client developers should be aware of the potential inclusion of a notifications channel when reordering channels. If it is hidden from the ui it should also be hidden from any potential reordering.
Brainstorming
See
brainstorming issues on GitHub
Retrieved from "