Appservices #

An appservice (short for "application service") is Matrix's built-in extension mechanism. An appservice is an external program that the homeserver trusts to:

  • Create and act as virtual users inside a namespace it owns (e.g., @slack_*:your-server)
  • Create and act as virtual rooms inside a namespace it owns (e.g., #_discord_*:your-server)
  • Receive a push-stream of all events happening in the homeserver that touch its namespace

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.

Appservice vs bot #

Plain botAppservice
IdentityOne Matrix user accountAn entire namespace of virtual users (e.g., @myint_*)
AuthenticationAccess token, same as any userTwo shared-secret tokens (as_token, hs_token)
Receives eventsOnly events the bot user seesAll events matching the AS namespaces, pushed via HTTP
Creates usersNo — bot is a single userYes — on-demand, no registration flow
Creates roomsYes, like a userYes, and it can create "alias-only" namespaced rooms
Typical use!weather bot, reminder bot, GitHub notifierBridge, 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.

The registration.yaml file #

An appservice is registered with the homeserver via a registration file — a short YAML document that declares:

  • An ID (unique string)
  • A URL where the homeserver should push events
  • Two long random tokens: as_token (appservice → homeserver auth) and hs_token (homeserver → appservice auth)
  • A sender localpart (the "default" user for this appservice)
  • A list of namespace patterns the appservice reserves: users, aliases and rooms

Example #

yaml
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.

Registering an appservice on Hagency #

Step 1 — Generate tokens #

Use any random-secret generator. A common pattern:

bash
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.

Step 2 — Write 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.

Step 3 — Upload to Hagency #

From your Hagency dashboard:

  1. Server → Appservices.
  2. Click Import registration.yaml (or pick the Custom Appservice template), then paste the full YAML / upload the file. The structured fields below are filled in for you — review them.
  3. Click Install.

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.

Step 4 — Start your appservice #

Your external program needs to:

  1. Expose an HTTP server at the URL you declared. Matrix will PUT batches of events there. The endpoint must be reachable from the Hagency cluster (public HTTPS, or a tunneling service).
  2. Authenticate inbound requests. Every PUT from the homeserver has ?access_token=<hs_token> in the query string. Reject anything without the right token.
  3. Talk back to the homeserver using the Matrix client-server API with 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.

Unregistering #

If you no longer want the appservice:

  1. Server → Appservices in the dashboard.
  2. Find the registration by ID and click Delete.
  3. Hagency removes it from the homeserver database; its tokens stop working immediately. (To pause an appservice without losing its config, use Disable instead.)

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.

Plan limits #

  • Free — custom appservices are not allowed.
  • Starter / Pro / Enterprise — custom appservices are enabled.

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.

Security considerations #

  • An appservice is trusted code. It can impersonate any user in its namespace, including admins if the namespace is loose. Treat tokens like root credentials.
  • Never commit registration.yaml to public git — it contains both tokens.
  • If you suspect a token leak, unregister the appservice and re-register with fresh tokens.
  • The 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.

Debugging #

  • Events not arriving at your endpoint: check the Hagency dashboard → Server → Logs → Appservices for HTTP error responses. Common causes: wrong URL, expired TLS, wrong hs_token.
  • Can't create users: check your namespace regex. The exclusive: true flag is required for the homeserver to allow your AS to create users with that pattern.
  • "Namespace conflict": another appservice (probably a built-in bridge) already claims overlapping users/aliases. Narrow your regex.

What's next #