Build plugins for Iris
A plugin teaches Iris something new: a command she can run, a slash command in the chat, a window or a whole screen. Moneybird invoices, the weather, a WhatsApp digest, your own tools. Once it is in the marketplace, every Iris can install it by being asked.
There are two ways to build one:
- Ask your Iris. Tell her what you want ("build a plugin that lists my open Moneybird invoices") and she writes it, tests it and can publish it for you. She follows this same guide.
- Write it yourself. A plugin is a plain folder with a manifest and a few scripts. No SDK, no build step.
If you are a language model: this page is also at /docs.md as plain Markdown, and /llms.txt points to everything you need.
What a plugin is
One flat folder in ~/plugins/<name>/ inside an Iris home: a plugin.json plus the files it names. No subfolders. Your code runs in that Iris's own private workspace (Linux, with Python 3, Node and the usual command line tools), never on anyone else's.
~/plugins/moneybird/
plugin.json
moneybird.py the command `moneybird`
moneybird.jsx a window with the open invoices
icon.svg its icon in the marketplace
README.md its page in the marketplace
plugin.json
{
"name": "moneybird",
"version": "1.0.0",
"author": "Woodst",
"description": "Open invoices, payments and contacts from Moneybird.",
"permissions": ["internet", "secrets"],
"commands": { "moneybird": "moneybird.py" },
"slash": { "moneybird": "open invoices" },
"window": "moneybird.jsx",
"category": "finance",
"icon": "icon.svg"
}
| Field | Required | Meaning |
|---|---|---|
name | yes | The name, equal to the folder name: lowercase letters, digits and -, at most 40 characters. |
version | yes | Three numbers, like 1.0.0. Every published version is final; change anything and raise it. |
author | yes | Who made it, shown in the marketplace. |
description | yes | One sentence: what it does for the person, not how it is built. |
permissions | yes | The permissions it needs, see below. An empty list is fine and trusted most. |
commands | no | Command name to file in the folder. The file starts with a #! line. |
slash | no | Commands that also appear in the / menu of the chat, with a short explanation. |
window | no | A .jsx window shown over the conversation. |
screen | no | A .jsx screen that rearranges the whole page. |
category | no | One of communication, finance, productivity, home, media, knowledge, developer, other. |
icon | no | A square .svg or .png in the folder. Without one the marketplace draws a letter. |
screenshots | no | Up to 6 .png, .jpg or .webp files in the folder, shown on its marketplace page in this order. |
Screenshots show the plugin at work: its window, its screen, or a conversation where Iris uses it. Use made-up demo content, never someone's real messages or data. A phone shot (1170 x 2532) or a window shot (1280 x 800) both work; keep the whole folder under 6 MB.
A README.md in the folder becomes the plugin's page in the marketplace: what it does, what the person needs (an account, an API key), and a few example sentences to say to Iris.
Permissions
Say honestly what the plugin needs. People see this list before they install, and a new version that asks for more is never installed without their yes.
| Permission | Means |
|---|---|
internet | Fetches something from the internet. |
files | Reads or writes files outside its own folder. |
secrets | Uses a key from the owner's vault (an API token, a login). |
phone | Sends something to the owner's phone. |
voice | Speaks or listens. |
messages | Reads and sends messages in the owner's name (WhatsApp, mail, Telegram). |
Commands
A command is any executable file. What it prints is what Iris reads back, so keep output short and readable: it is an answer, not a log.
#!/usr/bin/env python3
import sys
print("3 open invoices, 1.240 euro in total. The oldest is 34 days overdue.")
Commands under slash show up in the chat's / menu once the plugin is on. Choosing /moneybird overdue runs moneybird overdue and shows what it prints. Words after the command arrive as arguments, never through a shell.
A command never replaces an existing one: if web or klus already exists, pick another name.
Keys and logins
A plugin never sees a secret. The owner keeps tokens in the vault; your command asks the vault to make the call and gets back only the response. {g} is replaced by the secret inside the vault.
vault call moneybird GET https://moneybird.com/api/v2/administrations.json --header "Authorization: Bearer {g}"
For HTTP Basic auth use {g:base64}. Missing key? Tell the owner in one sentence which key to add and where to get it: vault ask moneybird --domain moneybird.com "your Moneybird API token" opens a safe window for them to paste it. Such a plugin needs the secrets permission.
Windows and screens
A window is a picture over the conversation; a screen rearranges the whole page. Both are one .jsx file with a default export, built from the same kit Iris uses for her own screens. plugin open <name> shows it.
export default () => (
<Card label="Moneybird" title="3 open invoices" icon="euro">
<Row left="Bakkerij Jansen" right="480 euro" />
<Row left="Studio Noord" right="760 euro" />
<Buttons>
<Button primary say="Remind Studio Noord about their invoice">Send a reminder</Button>
</Buttons>
</Card>
);
| Component | Props |
|---|---|
Screen | title, subtitle, icon: the frame of a whole screen |
Card | label, title, icon |
Row | left, right, icon |
Stats, Stat | value, label, icon: numbers side by side |
Buttons, Button | primary, onClick, or say: the sentence Iris receives when it is tapped |
Text | dim |
List | items |
Icon | name, size |
say("...") sends a sentence to Iris from your own code, as if the owner said it.
Try it out
Inside an Iris home:
plugin check moneybird # is plugin.json right? (changes nothing)
plugin enable moneybird # put its commands on the path
moneybird # run it
plugin open moneybird # show its window or screen
plugin disable moneybird # and off again
Publish
plugin publish moneybird
This sends the version to the marketplace. It waits for a review first; once approved it is listed here and any Iris can install it. The first home that publishes a name owns it: later versions of moneybird can only come from that same home.
Before you publish:
- No keys, passwords, names or personal data of your owner in the files. Other people will run this.
- The permissions match what the code does.
- One flat folder, at most 6 MB in total.
- The version is higher than the one already listed.
A review looks at exactly these points and at what the code does. A rejected version can be fixed and submitted again with a higher version number.
Updates
Every Iris checks the marketplace once an hour. A new version that asks for no more permissions than the one installed goes in by itself. One that asks for more waits: Iris asks her owner, and only on a yes does she run plugin update <name>. Folders you wrote yourself are never replaced; only plugins installed from the marketplace are.
plugin update # everything that can update without asking
plugin update moneybird # this one, also when it asks for more (after a yes)
Install
In any Iris, say "install moneybird", or run:
plugin browse # what is in the marketplace
plugin install moneybird # the newest version, enabled
plugin install moneybird@1.0.0
Price
All plugins are free for now. Paid plugins, with the money going to their maker, come later.
API
Read-only and public. Every published version is immutable and carries one SHA-256 hash over its files.
| Request | Returns |
|---|---|
GET /api/plugins | All plugins, newest version first: the manifest plus versions, hash, published. |
GET /api/plugins/<name> | The newest package: manifest, files (file name to base64), hash, published. |
GET /api/plugins/<name>/<version> | That exact version. |
Iris Apps