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 Hagency 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 Hagency 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: hagency-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\.hagency\.com' aliases: - exclusive: true regex: '#weather_.*:your-name\.hagency\.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 Hagency homeserver will POST events there.
From your Hagency dashboard:
Hagency rejects a registration whose exclusive namespace regex exactly matches one already claimed by another appservice. The registration is stored in the homeserver database and takes effect immediately — no restart required.
You can re-open any installed appservice (click the row to expand it) to copy its tokens, download its registration.yaml, or Edit it in place — editing preserves the enabled/disabled state and keeps the same ID.
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.
Appservices are currently gated by an enable/disable flag, not a per-count quota. Hagency-managed bridges (Telegram, Slack, Discord, etc.) count against the separate bridge quota, not the appservice flag.
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.