Creating a Custom Connector for Mastodon in Power Automate

6 Comments

If you're not in the know, a pretty decent amount of people on Twitter have been migrating over to the Fediverse via Mastodon (including me). As this is a new platform to many, maybe you happen to have a Power Automate flow which posts a link to your latest blog post through various social media sites (me). Now you want to be able to throw Mastodon into the mix. How do we accomplish this?

Let's make a Custom Connector in Power Automate!

NOTE: You will need a Power Automate subscription to create custom connectors. You can sign up for a 90-day trial when prompted or use a developer subscription. Yes, I know exactly what you're saying right now...

Prerequisites

Getting Started

The first thing that needs to be done is creating the OAuth client app that Power Automate is going to be connect with and issue authenticated REST API calls on your behalf.

Create Client App in Mastodon

Assuming you've signed into your Mastodon instance, go to the settings page and click on the Development link.

https://{yourmastodoninstance}/settings/applications

Click the New Application button to begin.

Fill out the Application Name with your desired name. This will be displayed to identify the client where this post originated from. For example, "Web" will be the client used if you're posting from the Mastodon instance in a web browser.

Add https://global.consent.azure-apim.net/redirect on the next line in the Redirect URI section. This is the URL used when you configure your OAuth connection within Power Automate.

To post a "toot" on your timeline, the write:statuses scope is required.

Once you've finished creating the application, the Client Key and Client Secret will be available for use.

Add Custom Connector for Mastodon

For this part, we'll be in the Power Automate interface.

I'll let you choose your destiny at this point in time. Walk through the manual steps of creating a custom connector from scratch, or import the result.

From Scratch

Here's the step by step on how to create this from scratch.

Why from scratch? There's no official OpenAPI document for the Mastodon API, and the few unofficial ones that exist require so much extra manual work.

Head over to https://make.powerautomate.com. Expand the Data menu and click on Custom Connectors

Select Create from blank from the New custom connector menu

Choose a name for your connector. In this example, we'll use Mastodon API. Press Continue.

You should see a connector configuration screen with 5 steps: General, Security, Definition, Code (preview), and Test.

1. General

This step is used for configuring the base piece of the connector.

Upload Connector icon

Optional.

Icon background color

Optional.

Description

Optional, but useful to describe what this connector does.

Scheme

HTTPS

Host

Set this to the domain of the Mastodon instance you're joined to, without the https:// in front of it

Base URL

Leave this as / for now

2. Security

The security section is where you will configure authentication to the connector.

The Authentication Type portion contains a dropdown that includes many authentication provider options. Select OAuth 2.0 as the authentication option.

Fill out OAuth 2.0 configuration options:

Identity Provider

Generic OAuth 2.0

Client Id

Client Key generated from Mastodon

Client Secret

Client Secret generated from Mastodon

Authorization Url

The OAuth Authorization Url endpoint for Mastodon instances is a relative path of /oauth/authorize. For example: https://mastodon.instance/oauth/authorize

Token URL

Relative path of /oauth/token

Refresh URL

Use the Token URL for this

Scope

Use write:statuses for the scope.

Redirect URL

The Redirect URL will show once the connector is saved. It's usually the same url you configured for use in the Mastodon client app.

Your security configuration is complete.

3. Definition

You can define actions for your custom connector in the Definition section. These actions can be invoked individually from the custom connector and represent REST API calls.

For writing statuses using the Mastodon API, we will create an action that uses the statuses API. This API can be used by sending the status form-data parameter as JSON as an HTTP POST to /api/v1/statuses

POST https://mastodon.example/api/v1/statuses
Content-Type: application/json

{
    "status": "Some status text" 
}

Press the (+) New Action link under the Action section on the left.

Under General, we can use information from the statuses API to fill in the information needed for the action.

Summary

Publish new status

Description

Post a new status.

Operation ID

Post/api/v1/statuses

Now we're ready to configure the Request. Scroll down to the next section and click on + Import from sample

Verb

POST

URL

/api/v1/statuses

Body

{
    "status": "Some status text"
}

The request is now configured to properly send data to the statuses endpoint, but how do you handle the response data?

According to the API definition, the statuses API for creating new posts must handle 200: OK, 401: Unauthorized, and 422: Unprocessable entity Status Codes.

Starting with the 200 response, click on the default response under the Response section.

Change the Name to 200. Click the + Import from sample button

Headers

You can leave this blank, otherwise add Content-Type application/json. By default, this will assume that you're sending POST body as JSON.

Body

Paste the example of the Status entity used as the return object of this API call.

Click the Import button to complete this section

The 200: OK response is complete.

Press the Back button above the Response section header.

To configure the 401 and 422 responses individually, click + Add Default Response. This will launch the same Import from sample dialog seen from the 200 process. Use either the examples for 401 or 422 as the Body.

Click the Import button. This will create a default response item. Click into it and modify the Name to the proper response code.

Repeat this process until you have all 3.

The Definition section is complete.

Click the button to Create the connector, then click the close button.

After about 15-20 seconds, refresh the Custom Connectors screen.

From here, click the + next to the newly created connector to initiate a Connection.

Login to your Mastodon instance and accept the OAuth consent.

You now have a successfully authenticated Mastodon API connection

Completed OpenAPI/Swagger code

This Swagger YAML can be either copy/pasted into the Swagger Editor from the Custom Connector wizard, or saved and imported by choosing Import from OpenAPI file in the New Custom Connector drop down.

After this is imported, go to the Security section of the connector and enter the Client Id and Client Secret that was generated from Mastodon in the initial steps above. Once you've saved the connector, you can create a connection from it and use it.

swagger: '2.0'
info:
title: Mastodon API
description: Mastodon API
version: '1.0'
host: mastodon.instance
basePath: /
schemes:
- https
consumes: []
produces: []
paths:
/api/v1/statuses:
post:
responses:
'200':
description: default
schema:
type: object
properties:
id:
type: string
description: id
created_at:
type: string
description: created_at
in_reply_to_id:
type: string
description: in_reply_to_id
in_reply_to_account_id:
type: string
description: in_reply_to_account_id
sensitive:
type: boolean
description: sensitive
spoiler_text:
type: string
description: spoiler_text
visibility:
type: string
description: visibility
language:
type: string
description: language
uri:
type: string
description: uri
url:
type: string
description: url
replies_count:
type: integer
format: int32
description: replies_count
reblogs_count:
type: integer
format: int32
description: reblogs_count
favourites_count:
type: integer
format: int32
description: favourites_count
favourited:
type: boolean
description: favourited
reblogged:
type: boolean
description: reblogged
muted:
type: boolean
description: muted
bookmarked:
type: boolean
description: bookmarked
content:
type: string
description: content
reblog:
type: string
description: reblog
application:
type: object
properties:
name:
type: string
description: name
website:
type: string
description: website
description: application
account:
type: object
properties:
id:
type: string
description: id
username:
type: string
description: username
acct:
type: string
description: acct
display_name:
type: string
description: display_name
locked:
type: boolean
description: locked
bot:
type: boolean
description: bot
discoverable:
type: boolean
description: discoverable
group:
type: boolean
description: group
created_at:
type: string
description: created_at
note:
type: string
description: note
url:
type: string
description: url
avatar:
type: string
description: avatar
avatar_static:
type: string
description: avatar_static
header:
type: string
description: header
header_static:
type: string
description: header_static
followers_count:
type: integer
format: int32
description: followers_count
following_count:
type: integer
format: int32
description: following_count
statuses_count:
type: integer
format: int32
description: statuses_count
last_status_at:
type: string
description: last_status_at
emojis:
type: array
items: {}
description: emojis
fields:
type: array
items:
type: object
properties:
name:
type: string
description: name
value:
type: string
description: value
verified_at:
type: string
description: verified_at
description: fields
description: account
media_attachments:
type: array
items: {}
description: media_attachments
mentions:
type: array
items: {}
description: mentions
tags:
type: array
items: {}
description: tags
emojis:
type: array
items: {}
description: emojis
card:
type: object
properties:
url:
type: string
description: url
title:
type: string
description: title
description:
type: string
description: description
type:
type: string
description: type
author_name:
type: string
description: author_name
author_url:
type: string
description: author_url
provider_name:
type: string
description: provider_name
provider_url:
type: string
description: provider_url
html:
type: string
description: html
width:
type: integer
format: int32
description: width
height:
type: integer
format: int32
description: height
image:
type: string
description: image
embed_url:
type: string
description: embed_url
description: card
poll:
type: string
description: poll
'401':
description: default
schema:
type: object
properties:
error:
type: string
description: error
'422':
description: default
schema:
type: object
properties:
error:
type: string
description: error
summary: Publish new status
description: Post a new status.
operationId: Post_api_v1_statuses
parameters:
- name: body
in: body
required: true
schema:
type: object
properties:
status:
type: string
description: status
title: status
definitions: {}
parameters: {}
responses: {}
securityDefinitions:
oauth2-auth:
type: oauth2
flow: accessCode
authorizationUrl: https://mastodon.instance/oauth/authorize
tokenUrl: https://mastodon.instance/oauth/token
refreshUrl: https://mastodon.instance/oauth/token
scopes:
write:statuses: write:statuses
security:
- oauth2-auth:
- write:statuses
tags: []

Let's Test This Baby Out With a Power Automate Flow

I've created a simple Power Automate flow with a manual trigger. Then, I added a new step and selected the Publish new status Action from the Mastodon API custom connector.

I added a simple sentence to the status property of the action: "Sup Mastodon! With love from #PowerAutomate 💯"

Saved the flow and kicked it off manually.

Alright! The flow completed successfully.

Did it actually work, though? 🤔

... ...

IT DID!

We have a successful Mastodon post driven by Power Automate!

Conclusion

This was a simple, fun, and detailed exercise to create a Custom Connector in Power Automate for REST APIs. Anyone can do this as long as the information for an API endpoint is known. Have fun cross-posting to Mastodon and other social media sites!