Skip to main content

Verified against ActivityPub REC §3.1/§4.1, AS2 Vocabulary, Mastodon v4.7.3 source (actor_serializer, context_helper, signature_verification, account.rb, routes), Lemmy group.json fixture, FEP-521a/1b12 on 2026-10-15.

Actors

In ActivityPub, an Actor is any entity that can perform activities. Most commonly actors represent users, but they can also represent bots, organizations, services, or groups.

Actor Types​

ActivityStreams defines five actor types (AS2 Vocabulary §3.2):

TypeAS2 definitionTypical use
PersonAn individual personIndividual accounts
ApplicationA software applicationServer/instance actors
OrganizationAn organizationBusiness accounts
ServiceA service of any kindBots, relays, bridges
GroupA formal or informal collective of actorsCommunities, forums

Mastodon publishes bot accounts as Service and its instance actor as Application, and treats incoming Service/Application actors as bots (actor_serializer.rb).

Required Properties​

Every object MUST have an id and a type (AP §3.1), and actors MUST also have an inbox and an outbox (AP §4.1). The security context below isn't required by the spec, but you need it for publicKey:

{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1"
],
"type": "Person",
"id": "https://example.com/users/alice",
"inbox": "https://example.com/users/alice/inbox",
"outbox": "https://example.com/users/alice/outbox"
}
PropertyTypeDescription
typeStringOne of the actor types
idURLUnique identifier (dereferenceable)
inboxURLEndpoint for receiving activities
outboxURLCollection of performed activities

Identity​

{
"preferredUsername": "alice",
"name": "Alice Smith",
"summary": "<p>Software developer and Fediverse enthusiast</p>",
"url": "https://example.com/@alice"
}
PropertyDescription
preferredUsernameHandle/username (used in @mentions)
nameDisplay name
summaryBio/description (HTML allowed)
urlHuman-readable profile page

Media​

{
"icon": {
"type": "Image",
"mediaType": "image/png",
"url": "https://example.com/avatars/alice.png"
},
"image": {
"type": "Image",
"mediaType": "image/jpeg",
"url": "https://example.com/headers/alice.jpg"
}
}
PropertyDescription
iconAvatar/profile picture
imageHeader/banner image

Collections​

followers and following SHOULD be present; liked MAY be (AP §4.1).

{
"followers": "https://example.com/users/alice/followers",
"following": "https://example.com/users/alice/following",
"liked": "https://example.com/users/alice/liked"
}
PropertyDescription
followersCollection of followers
followingCollection of followed actors
likedCollection of liked objects

Endpoints​

endpoints is optional; these are some of the properties AP defines for it (AP §4.1). In practice sharedInbox is the one other servers use.

{
"endpoints": {
"sharedInbox": "https://example.com/inbox",
"proxyUrl": "https://example.com/proxy",
"oauthAuthorizationEndpoint": "https://example.com/oauth/authorize",
"oauthTokenEndpoint": "https://example.com/oauth/token"
}
}
EndpointDescription
sharedInboxServer-wide inbox for delivering public and followers-addressed activities once per server (AP §7.1.3)
proxyUrlProxy for fetching remote objects
oauthAuthorizationEndpointOAuth 2.0 authorization
oauthTokenEndpointOAuth 2.0 token endpoint

Public Key​

ActivityPub itself doesn't require keys, but in practice actors must publish a public key so other servers can verify their HTTP Signatures (Mastodon and most servers won't accept unsigned deliveries):

{
"publicKey": {
"id": "https://example.com/users/alice#main-key",
"owner": "https://example.com/users/alice",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2L...\n-----END PUBLIC KEY-----"
}
}
PropertyDescription
idURL of the key (usually {actor}#main-key)
ownerURL of the actor owning this key
publicKeyPemPEM-encoded public key (RSA is what everyone supports)

RSA via publicKey remains the baseline for interoperability, but it is no longer the only option. FEP-521a (FINAL) lets an actor publish multiple keys, including Ed25519, as Multikey entries in assertionMethod; Mastodon 4.6 accepts multiple keys per remote actor and 4.7 also verifies Ed25519 in RFC 9421 signatures (CHANGELOG). Multiple keys are also what makes key rotation workable: publish the new key alongside the old one, send an Update of the actor to followers, and remove the old key later. Mastodon refetches the key once when a signature fails to verify, so stale caches recover (signature_verification.rb). See Keys and Identity for details.

Complete Actor Example​

{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"toot": "http://joinmastodon.org/ns#",
"discoverable": "toot:discoverable",
"schema": "http://schema.org#",
"PropertyValue": "schema:PropertyValue",
"value": "schema:value"
}
],
"type": "Person",
"id": "https://example.com/users/alice",
"inbox": "https://example.com/users/alice/inbox",
"outbox": "https://example.com/users/alice/outbox",
"followers": "https://example.com/users/alice/followers",
"following": "https://example.com/users/alice/following",
"preferredUsername": "alice",
"name": "Alice Smith",
"summary": "<p>Software developer and Fediverse enthusiast</p>",
"url": "https://example.com/@alice",
"manuallyApprovesFollowers": false,
"discoverable": true,
"published": "2023-01-15T10:00:00Z",
"icon": {
"type": "Image",
"mediaType": "image/png",
"url": "https://example.com/avatars/alice.png"
},
"image": {
"type": "Image",
"mediaType": "image/jpeg",
"url": "https://example.com/headers/alice.jpg"
},
"attachment": [
{
"type": "PropertyValue",
"name": "Website",
"value": "<a href=\"https://alice.example\" rel=\"me\">alice.example</a>"
},
{
"type": "PropertyValue",
"name": "GitHub",
"value": "<a href=\"https://github.com/alice\" rel=\"me\">github.com/alice</a>"
}
],
"endpoints": {
"sharedInbox": "https://example.com/inbox"
},
"publicKey": {
"id": "https://example.com/users/alice#main-key",
"owner": "https://example.com/users/alice",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
}
}

Mastodon Extensions​

Mastodon adds several properties to actors, defined in its JSON-LD context (context_helper.rb):

Profile Fields​

{
"attachment": [
{
"type": "PropertyValue",
"name": "Website",
"value": "<a href=\"https://example.com\" rel=\"me\">example.com</a>"
}
]
}
{
"featured": "https://example.com/users/alice/collections/featured"
}
{
"featuredTags": "https://example.com/users/alice/collections/tags"
}

Account Settings​

{
"manuallyApprovesFollowers": true,
"discoverable": true,
"memorial": false,
"indexable": true
}
PropertyDescription
manuallyApprovesFollowersRequires follow approval
discoverableListed in profile directory
memorialAccount is memorialized
indexablePosts can be indexed by search

Service and Application Actors​

Server-level actors for relays, bridges, and bots. Mastodon's instance actor, for example, is an Application served at /actor with an inbox at /actor/inbox and preferredUsername mastodon.internal (routes.rb, seed):

{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Application",
"id": "https://example.com/actor",
"inbox": "https://example.com/inbox",
"outbox": "https://example.com/outbox",
"preferredUsername": "example.com",
"name": "Example.com Server Actor"
}

Group Actors​

For communities (like Lemmy):

{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"lemmy": "https://join-lemmy.org/ns#"
}
],
"type": "Group",
"id": "https://lemmy.example/c/technology",
"name": "Technology",
"preferredUsername": "technology",
"inbox": "https://lemmy.example/c/technology/inbox",
"outbox": "https://lemmy.example/c/technology/outbox",
"followers": "https://lemmy.example/c/technology/followers",
"attributedTo": "https://lemmy.example/c/technology/moderators"
}

In Lemmy, attributedTo on a community points to its moderators collection, name is the display title and preferredUsername the slug (Lemmy group.json). Group federation in general is described by FEP-1b12 (FINAL).

Actor Verification​

When receiving activities, verify:

  1. The key belongs to the actor: the actor's own document lists the key (publicKey or a FEP-521a assertionMethod entry). A key hosted on another origin is only acceptable if the actor document lists it.
  2. The key owner is activity.actor
  3. The actor (or its domain) is not blocked

:::tip Use the tested verifier Request verification has its own page with tested code: Verifying incoming requests. It covers draft-cavage and RFC 9421, the digest over the raw body, the required signed headers, binding the key owner to activity.actor, and SSRF-safe key fetching. :::

Caching Actors​

Cache remote actors to reduce requests, but refresh them:

  • Periodically. Mastodon treats a remote account as possibly stale after one day and refreshes it in the background (account.rb).
  • On Update. Apply Update activities for the actor (after checking they come from the actor itself).
  • On signature failure. Refetch the key once before rejecting, as above.

The TTL is your choice; one day matches Mastodon.

Next Steps​