An appservice (short for "application service") is Matrix's built-in extension mechanism. An appservice is an external program that the homeserver trusts to:
@slack_*:your-server)#_discord_*:your-server)Every bridge you read about in the Bridges section is, under the hood, an appservice. But you can also write your own — for custom bots, integrations, analytics, admin tooling or bespoke bridges to networks Meldry doesn't ship by default.
This page explains the concept, shows you when you'd use an appservice vs a simple bot, and walks through registering one on a Meldry workspace.
| Plain bot | Appservice | |
|---|---|---|
| Identity | One Matrix user account | An entire namespace of virtual users (e.g., @myint_*) |
| Authentication | Access token, same as any user | Two shared-secret tokens (as_token, hs_token) |
| Receives events | Only events the bot user sees | All events matching the AS namespaces, pushed via HTTP |
| Creates users | No — bot is a single user | Yes — on-demand, no registration flow |
| Creates rooms | Yes, like a user | Yes, and it can create "alias-only" namespaced rooms |
| Typical use | !weather bot, reminder bot, GitHub notifier | Bridge, bulk-integration, bot farm, admin dashboard |
Rule of thumb: if your integration only needs one identity and reacts to messages directed at it, use a bot. If you need many identities (one per external user) or need to see every message in a room without being in it, you need an appservice.
registration.yaml file #An appservice is registered with the homeserver via a registration file — a short YAML document that declares:
as_token (appservice → homeserver auth) and hs_token (homeserver → appservice auth)id: meldry-weatherbot url: https://weather.example.com/_matrix as_token: as_7f3a... # a long random string you generate hs_token: hs_b1c9... # another long random string sender_localpart: weatherbot rate_limited: false namespaces: users: - exclusive: true regex: '@weather_.*:your-name\.meldry\.com' aliases: - exclusive: true regex: '#weather_.*:your-name\.meldry\.com' rooms: []
Exclusive namespaces mean nobody else on the homeserver can register a user or alias matching the regex. Be specific — don't claim @.*:your-server or you'll block normal user registration.
Use any random-secret generator. A common pattern:
openssl rand -hex 32 # run twice, once for as_token, once for hs_token
Save both tokens. They never get regenerated by themselves — if you lose them, you have to re-register.
registration.yaml #Based on the example above, adapt the fields to your integration. Make the id something human-readable and the url a reachable HTTPS endpoint — the Meldry homeserver will POST events there.
From your Meldry dashboard:
Meldry validates the YAML (namespaces must not clash with existing bridges, the URL must resolve), writes it to the homeserver config, and restarts the homeserver to pick it up. Takes about 10 seconds.
Your external program needs to:
?access_token=<hs_token> in the query string. Reject anything without the right token.Authorization: Bearer <as_token>. With this token you can create any user in your namespace (POST /_matrix/client/v3/register?kind=user with type: m.login.application_service), send messages as any of those users, create rooms, etc.The reference SDK most people use is matrix-appservice-bridge for Node.js, or matrix-nio for Python. There's also a Rust crate: matrix-sdk-appservice.
If you no longer want the appservice:
Any virtual users the appservice created stay in the database (and can still appear in room histories), but the appservice can no longer push events or act as them.
Meldry-managed bridges (Telegram, Slack, Discord, etc.) don't count against this quota — only appservices you register yourself.
registration.yaml to public git — it contains both tokens.rate_limited: false field disables rate limiting for traffic from the appservice. Only set this for high-volume integrations; leave it true (or omit it) by default.hs_token.exclusive: true flag is required for the homeserver to allow your AS to create users with that pattern.