Micropub

From IndieWeb
Revision as of 20:06, 31 December 2014 by Aaronparecki.com (talk | contribs) (→‎Simple Example: use single syndication target for the "simple example")

Micropub is an open API standard that is used to create posts on one's own domain using third-party clients. Web apps and native apps (e.g. iPhone, Android) can use Micropub to post short notes, photos, events or other posts to your own site, similar to a Twitter client posting to Twitter.com.

Editor
Aaron Parecki
License
Per CC0, to the extent possible under law, the editors have waived all copyright and related or neighboring rights to this work. In addition, as of 2024-08-03, the editors have made this specification available under the Open Web Foundation Agreement Version 1.0.
 

IndieWeb Examples

Examples of IndieWeb sites that support the micropub API (in order of deployment).

Aaron Parecki

Aaron Parecki supports publishing various post types on aaronparecki.com using micropub since 2013-12-25.

  • 2013-12-25 note micropub publishing support added
  • 2013-12-28 pushup micropub publishing support added
  • 2014-02-24 photo micropub publishing support added

Barnaby Walters

Barnaby Walters posts notes and replies on waterpigs.co.uk (Taproot) since 2014-03-10 using Shrewdness and Taproot as micropub clients

Kartik Prabhu

Kartik posts notes on kartikprabhu.com via micropub using Quill since 2014-05-24

Known

Ben has written a Known plugin that logs in via IndieAuth and publishes content via micropub. It's designed to work so that all indieweb post types - notes etc - are passed to the content plugin registered to handle them. [1]

Postly

Ben Roberts posts to his Postly site via micropub. Photos are posted using OwnYourGram and Notes, Replies, and Articles are posted using his own Postly Client. Deleting entries via micropub added 2014-09-28.

Jeremy Keith

Jeremy Keith supports posting notes and photos via micropub since 2014-10-21.

Clients

Sites and client applications that publish via micropub. Alphabetically sorted.

Jonnybarnes.uk

JonnyBarnes post UI is publicly available at https://jonnybarnes.uk/notes/new

Neonblog

Emma's Neonblog post UI creates notes and articles via micropub.

Postly

Ben Roberts' Postly creates notes and articles via micropub and can add syndication links after posting. synditate-to links are passed as a php readable array field not a comma separated list. Publicly Available UI

OwnYourGram

aaronpk's OwnYourGram is a tool to reverse-syndicate instagram photos to your personal site, using micropub.

  • The signup process contains lots of useful documentation and debugging information to help get new micropub implementations working.

PushupCounter

aaronpk's PushupCounter-iOS is an iOS client for publishing exercise data.

Quill

Quill is a reference Micropub client you can use to post notes to your site.

  • The signup process walks you through configuring your website to accept Micropub requests from apps like this.

Shrewdness

Shrewdness - indieweb reader

Taproot

Barnaby's Taproot publishes notes via micropub. The post UI is publicly accessible, and you can use it to make posts on your own website. As of 2014-06-12 it’s also marked up with h-product and h-x-app markup so that authorization UIs can provide a better experience whilst authorizing, without having to pre-register apps and upload logos

Client implementation requests

Requests for clients to implement Micropub

Macaw

Macaw is an "open source micro-blogging client, with support for ADN (app.net) and Twitter (twitter.com)."

Request for Micropub support:

Open Source

Open source libraries & implementations used to support micropub on the client app side and on the API endpoint side on the server:

How to implement

How to implement the Micropub API, both in a client that can discover an endpoint and publish to it, and on a server to support an endpoint to create/update/delete posts in response.

Endpoint Discovery

It should be possible to configure an API client by authenticating as your domain name using IndieAuth. After signing in, your domain needs a way to specify the API endpoint the client will use to create new posts.

Add a <link> tag in the HTML head of your home page, or send an HTTP Link header.

HTTP Header

Link: <https://example.com/micropub>; rel="micropub"

HTML Head

<link rel="micropub" href="https://example.com/micropub">

Authentication

Authorization should be handled via the IndieAuth protocol (built on top of OAuth 2.0).

An app that wants to post to a user's Micropub endpoint will need to obtain authorization.

When making requests to the Micropub endpoint, the access token should be sent in either an HTTP header or in a post body parameter as described in the OAuth Bearer Token RFC.

HTTP Header

Authorization: Bearer XXXXXXXX

Form-Encoded Body Parameter

access_token=XXXXXXXXX


Scope

The client may request one or more scopes during the authorization request. It does this according to standard OAuth 2.0 techniques, by passing a space-separated list of scope names in the authorization request.

The authorization server must indicate to the user any scopes that are recognized. The token that is generated will contain the scopes approved by the user. Since API clients may be requesting scopes that the API server does not recognize, the list of approved and recognized scopes should be returned along with the access token so the client can decide what to do when it does not have the necessary scopes.

Verification

(This section is a stub)

When a Micropub client obtains an access token, it may need to be able to verify the access token is valid and retrieve the URL of the user it belongs to. Micropub endpoints must implement a method that returns basic information about the access token.

Access tokens can be verified by making an empty GET request to the Micropub endpoint with the access token in the header. If the access token is valid, the Micropub endpoint must reply with at least the "me" parameter, and may also return scope, client_id, and expiration date.

Form-encoded Microformats Representation

For the simplicity of writing clients, all requests to a Micropub API must be in the standard form-encoded format. At a most basic level, you should be able to write an HTML form and set the form action to your own endpoint and use it to post to your site.

Response

This section is a stub and probably needs to be thought out more.

Create

A successful creation should return an HTTP 201 Created response and a "Location" header with the full URL to the entry created. The body of the response may be blank, but could optionally include an HTML page rendering the object created or a link to the new object. API clients will most likely ignore the HTML body.

If the post also has a short link, the short link can be indicated as an additional HTTP header:

Link: <http://aaron.pk/xxxxxx>; rel="shortlink"

If the Micropub endpoint returns an HTML body, then the body can contain a <link rel="shortlink" href="http://aaron.pk/xxxxxx"> property or a u-shortlink property with the short URL.

If the post has successfully been syndicated the response should include one or more "Link" headers marked with rel="syndication", e.g.:

Link: <https://twitter.com/aaronpk/status/xxxxxx>; rel="syndication"
  • returning HTML makes sense if the item is added and can be rendered accurately immediately. I'm worried about the use case when a new creation requires further processing before it's truly a post. Would a 202 Accepted with a unique id work in this case? --Bear.im 14:30, 14 January 2014 (PST)
    • returning HTML really only makes sense for user agents that can and want to render HTML. e.g. iPhone apps probably don't care about the HTML response. I see your point about returning 202 Accepted if the post cannot yet be rendered before further processing. I believe HTTP allows a `Location` header to be returned in the 202 example anyway. --Aaronparecki.com 14:34, 14 January 2014 (PST)

Update

Should return HTTP 200 or 204 depending on whether the response contains content.

Delete

Should return HTTP 200 or 204 depending on whether the response contains content.

Creating Objects

Indicating the object being created

To indicate the object being created, use a property called "h", (which would never be the name of a property of a microformats object), followed by the name of the microformats object. Examples:

  • h=entry
  • h=card
  • h=event
  • h=cite

h-entry

The following properties may be included in a request to create a new h-entry:

  • name
  • summary
  • content
  • published
  • updated
  • category = tag1, tag2, tag3 (sent as array syntax: &category[]=tag1&category[]=tag2)
  • slug
  • location
    • as a Geo URI, for example geo:45.51533714,-122.646538633
    • My micropub client currently has a map to mark the specific location in LatLng values, and a text input for an actual place name. So my client is sending a request of the form "&location=1.23,%40-4.56:An%40Address" Jonnybarnes.net 07:57, 10 June 2014 (PDT)
  • in-reply-to
  • repost-of
  • syndication
    • Pass one or more URLs pointing to places where this entry already exists. Can be used for PESOS implementations.
  • syndicate-to = http://twitter.com/aaronpk, http://alpha.app.net/aaronpk, http://facebook.com/aaronpk, etc.
    • This property is slightly different from the others since it is giving a command to the server rather than describing an object.
    • client should identify services by their domain name.
    • are syndication options needed in the client?
New Note

Posting a new note with tags, syndicating to twitter:

  • content
  • category
  • syndicate-to
  • published (optional, defaults to "now" if not present. Useful for writing offline and syncing later)
POST /post/new HTTP/1.1
Host: aaronparecki.com
Content-type: application/x-www-form-urlencoded

h=entry
&content=The+%40Jawbone+UP%2C+my+favorite+of+the+%23quantifiedself+trackers%2C+finally+released+their+official+API%21+http%3A%2F%2Fjawbone.com%2Fup%2Fdeveloper%2F
&category[]=jawbone&category[]=quantifiedself&category[]=api
&syndicate-to=http://twitter.com/aaronpk
Minimal Example
POST /micropub HTTP/1.1
Host: example.com
Content-type: application/x-www-form-urlencoded
Authorization: Bearer XXXXXXX

h=entry
&content=Hello+World
curl https://example.com/micropub -d h=entry -d "content=Hello World" -H "Authorization: Bearer XXXXXXX"
New Reply

Posting a new reply, syndicating to twitter

  • content
  • in-reply-to
  • syndicate-to
  • published
POST /post/new HTTP/1.1
Host: aaronparecki.com
Content-type: application/x-www-form-urlencoded

h=entry
&content=%40BarnabyWalters+My+favorite+for+that+use+case+is+Redis.+It%27s+easy+to+set+up+and+use%2C+I+often+use+it+to+move+data+between+apps+written+in+different+languages+too.
&in-reply-to=http://waterpigs.co.uk/notes/4S0LMw/
&syndicate-to=http://twitter.com/aaronpk
New Repost

Posting a new repost, and adding additional tags.

  • repost-of
  • category
POST /post/new HTTP/1.1
Host: aaronparecki.com
Content-type: application/x-www-form-urlencoded

h=entry
&repost-of=http://waterpigs.co.uk/notes/4S0LMw/
&category=realtime
New Article

Posting a new article

  • content
  • name
  • category
  • published
POST /post/new HTTP/1.1
Host: aaronparecki.com
Content-type: application/x-www-form-urlencoded

h=entry
&content=Now+that+I%27ve+been+%5Bhttp%3A%2F%2Faaronparecki.com%2Fevents+creating+a+list+of+events%5D+on+my+site+using+%5Bhttp%3A%2F%2Findiewebcamp.com%2Fp3k+p3k%5D%2C+it+would+be+great+if+I+could+get+a+more+calendar-like+view+of+that+list.+%0A%0ASince+I+live+in+Google+Calendar+every+day+anyway%2C+it+would+be+great+to+use+that+interface+to+browse+my+%23indieweb+events+as+well%21+Since+my+events+page+is+marked+up+with+%5Bhttp%3A%2F%2Fmicroformats.org%2Fwiki%2Fh-event+h-event+microformats%5D%2C+all+it+would+take+is+to+write+an+h-event+to+iCal+converter+script+to+generate+an+iCal+feed+from+my+list+of+events.+Then+I+could+just+subscribe+to+the+iCal+feed+from+within+Google+Calendar.%0A%0A%23%23%23+Bonus%3A+read%2Fwrite+access+to+indieweb+events+via+Google+Calendar%0A%0AEven+better+would+be+to+use+Google+Calendar+to+also+create+events+on+my+site.+Unfortunately+Google+Calendar+doesn%27t+support+CalDAV%2C+so+we+can%27t+do+it+that+way.+%28Of+course+I+could+use+Apple%27s+iCal+to+publish+directly%2C+but+that+also+means+I%27d+have+to+write+some+code+tot+speak+CalDAV%29.+%0A%0AInstead%2C+I+can+create+a+%22write-only%22+calendar+in+Google+Calendar%2C+and+have+p3k+subscribe+to+it.+Any+new+events+in+that+feed+would+be+moved+over+to+the+internal+events+page+and+deleted+from+the+Google+Calendar.
&name=Itching%3A+h-event+to+iCal+converter
&category[]=indieweb&category[]=hevent&category[]=events&category[]=calendar&category[]=p3k
New Bookmark

Posting a new bookmark with name, quote, and tags.

  • bookmark
  • name
  • content
  • category
POST /post/new HTTP/1.1
Host: aaronparecki.com
Content-type: application/x-www-form-urlencoded

h=entry
&bookmark=https%3A%2F%2Fplus.google.com%2F%2BKartikPrabhu%2Fposts%2FUzKErSbfmHq
&name=To+everyone+who+is+complaining+about+Popular+Science+shutting+down+comments...
&content=%22Why+is+there+this+expectation+that+every+website+should+be+a+forum%3F+No+website+has+any+obligation+to+provide+a+space+for+your+rants.+Use+your+own+space+on+the+web+to+do+that.%22
&category[]=indieweb&category[]=comments
New Weight Measurement

Aaron currently publishes his weight on his site as a list of h-entry posts (example).

In addition to a human-readable text version of the measurement, the values are marked up with custom properties to make the parsed version machine readable.

HTML:

"Weighed <data class="p-weight">153.2lbs</data> (<data class="p-weight">69.7kg</data>) <data class="p-bodyfat" value="0.1691">16.9%</data> body fat"

Parsed JSON:

"name": [
  "Weighed 153.2lbs (69.7kg) 16.9% body fat"
],
"weight": [
  "153.2lbs",
  "69.7kg"
],
"bodyfat": [
  "0.1691"
],
"published": [
  "2013-10-09T08:19:00-07:00"
],

To be able to create this post from a Micropub API, we would need the following properties:

  • content
  • weight (number with units)
  • bodyfat (percentage in decimal form)
  • published

h-event

The following properties may be included in a request to create a new h-event:

Posting a new event

  • name
  • summary
  • description
  • start
  • end
  • duration
  • category
  • location
POST /post/new HTTP/1.1
Host: aaronparecki.com
Content-type: application/x-www-form-urlencoded

h=event
&name=IndieWeb Dinner at 21st Amendment
&description=In SF Monday evening? Join @caseorganic and I for an #indieweb dinner at 6pm! (Sorry for the short notice!)
&start=2013-09-30T18:00:00-07:00
&category=indieweb
&location=http://21st-amendment.com/


h-cite

The following properties may be included in a request to create a new h-cite:

(The following list is from microformats.org/wiki/h-cite)

  • name
  • published - date of publication of the work (not the date the h-cite was created)
  • author - URL of an h-card
  • url - a URL to access the cited work
  • content - the content or partial content of the work itself, such as when including a blockquote snippet of a work

Nested Microformat Objects

How to handle nested data? For example when the p-location property is an h-geo vs an h-card.

For an h-geo property, just use a Geo URI such as geo:37.786971,-122.399677

For more complicated objects, it may be best to first create an object on the target site, then reference that object's URL in the main request.

For example, creating a checkin post would involve two POST requests:

First create the venue by posting an h-card:

POST /micropub

h=card
&name=Ford+Food+and+Drink
&url=http://www.fordfoodanddrink.com/
&street-address=2505 SE 11th Ave
&locality=Portland
&region=OR
&postal-code=97214
&geo=geo:45.5048473,-122.6549551
&tel=(503) 236-3023

Response:

HTTP/1.1 201 Created
Location: http://example.com/venue/10

Then create the checkin post:

POST /micropub

h=entry
&location=http://example.com/venue/10
&name=Working on Micropub
&category=indieweb
&syndication=https://foursquare.com/aaronpk/checkin/52a9e136498ef2d086e0341e

Response:

HTTP/1.1 201 Created
Location: http://example.com/entry/1001
  • This technique has the advantage of ensuring that each object that is created has its own URL (each piece of Data has its own Link, wink wink)
  • Also gives the server an opportunity to handle each entity separately. E.g., rather than creating a duplicate of an existing venue, it may give back a link to one that was already created, possibly even merging in newly received data first.

Alternative form markup first

An alternative design approach would be form markup first rather than HTTP protocol first.

See related:

If we can figure out how a <form class="h-entry"> should work from a markup / posting UI perspective, then maybe we can derive a protocol from how that uses HTTP accordingly.

(this section is a stub, feel free to expand with a complete form element code example that shows what a form for posting a new h-entry or editing an existing h-entry would look like in terms of minimal HTML markup, then we can figure out the HTTP interaction from that)

Updating Objects

Adding a Syndication URL

Use case: adding a syndication link to a post after it has been published. For example, when a client supports posting first then syndicating to Twitter or Facebook after the fact, the site needs a way to update the original post with the new syndication URL.

To add syndication URLs, the request looks similar to the original "create" request, but includes only the values that are changing.

POST /micropub

url=http://aaronparecki.com/notes/2014/06/01/2/indieweb
&syndication=https://twitter.com/aaronpk/status/473186139172384770

Adding Tags

Use case: adding tags to a post after it's been created.

POST /micropub

url=http://aaronparecki.com/notes/2014/06/01/2/indieweb
&tags=webmention

Question: how to handle removing or replacing tags? One possibility is to have separate parameters for adding, removing and replacing the tags:

  • add_tags=webmention - adds the specified tags
  • remove_tags=indieweb - removes the specified tags
  • tags=webmention,indieweb - replaces the current tag list with the given tag list

Syndicating a Post


After a post is already created, it is sometimes desirable to syndicate a copy of it elsewhere. For example if I first post a photo to my site, then want to syndicate it to Twitter and Facebook after the fact.

Simple Example

A simple micropub request to syndicate an existing post may look like this:

POST /micropub

url=http://aaronparecki.com/notes/2014/06/30/1/indiewebcamp
&syndicate-to=https://twitter.com/aaronpk

The micropub endpoint will retrieve the contents of the original post (including a photo if present) and syndicate it to the targets listed in the "syndicate-to" parameter.

Custom Syndication Content

What if you want to provide custom text to the site you are syndicating to? For example, since Twitter posts are limited to 160 characters, you may want to hand-craft a version of the post that fits within the limit.

This may look like a standard micropub request where there is just no h=entry value since it's not creating a new post on the author's site. This way we can include a "content" field. The limitation of course is that only one content field can be present, so the syndicated text will be the same for all targets.

POST /micropub

url=http://aaronparecki.com/notes/2014/06/30/1/indiewebcamp
&syndicate-to=https://twitter.com/aaronpk
&content=Shorter+version+of+the+post

TODO: Is this a reasonable alternative POST request to specify custom content for multiple silos?

POST /micropub

url=http://aaronparecki.com/notes/2014/06/30/1/indiewebcamp
&syndicate-to[]=https://twitter.com/aaronpk
&syndicate-to[]=https://facebook.com/aaronpk
&content[https://twitter.com/aaronpk]=Shorter+version+of+the+post
&content[https://facebook.com/aaronpk]=Content+to+post+to+Facebook

In PHP and Ruby, this is interpreted as the following object (shown as JSON for convenience):

{
  "u": "http://aaronparecki.com/notes/2014/06/30/1/indiewebcamp",
  "syndicate-to": [
    "https://twitter.com/aaronpk",
    "https://facebook.com/aaronpk"
  ],
  "content": {
    "https://twitter.com/aaronpk": "Shorter version of the post",
    "https://facebook.com/aaronpk": "Content to post to Facebook"
  }
}


Syndicating likes, reposts, etc

If there is already a "like" post on your site, then syndicating this to Twitter or Facebook is just a standard syndicate request. The micropub endpoint should recognize that the post being syndicated is a "like" and handle it appropriately.

POST /micropub

url=http://aaronparecki.com/notes/2014/06/30/1/indiewebcamp
&syndicate-to=https://twitter.com/aaronpk

Discovering Supported Syndication Targets

Since any given micropub endpoint may support syndicating to websites that a client does not know about, the endpoint needs a way to indicate which syndication targets are supported.

The client makes a GET request with q=syndicate-to to query the list of syndication endpoints supported.

GET /micropub?q=syndicate-to
Authorization: Bearer xxxxxxxxx

syndicate-to[]=https://twitter.com/aaronpk&syndicate-to[]=https://twitter.com/pkbot&syndicate-to[]=https://facebook.com/aaronpk

The list can be generated dynamically depending on the client making the request. For example, I may want to authorize an app to syndicate to IndieNews but not Twitter. In this case, when the app makes the request to find supported syndication targets, my site would only return the IndieNews URL.

Scope

A micropub endpoint may wish to restrict syndicating to some or all targets, only explicitly allowing it for specific clients. This can be accomplished with the "scope" parameter when the access token is obtained.

See Also