Quest Framework Verse API

We shipped the Quest Framework API with 41.10, in experimental. Here’s what you need to know to get a jumpstart on building quests, objectives, and rewards in Verse ahead of the upcoming full release.

One building block powers all of it — the same quest primitive scales to whatever you’re building:

  • Achievements (one-time milestones)

  • Tutorials (chained steps, each unlocking the next)

  • Battle passes (tiered quest rows)

  • Daily/weekly challenges (quests on a timer)

  • Community goals (one shared objective across all players)

  • Collection sets (gather them all + a bonus “set complete” quest)

What’s a quest?

A quest is simply a completable goal. Everything else — objectives, rewards, participants — layers on top of that core building block.

You’ll spend most of your time with basic_quest, which extends adds two core concepts:

  • An objective, which tracks progress toward the goal

  • Rewards, which are granted to participants once it’s completed

Add using { /UnrealEngine.com/Progression } at the top of your Verse file to access basic_quest.

A full (simple) quest in Verse

Here’s a complete example — land 50 headshots, earn a skin:

sharpshooter_quest<public> := class(basic_quest):
    var Objective<override>:quest_objective = progress_quest_objective:
        Icon := MyIcon
        Name := SharpshooterQuestName
        Description := SharpshooterQuestDescription
        ShortDescription := SharpshooterQuestShortDescription
        RequiredCount := 50.0


    var Rewards<override>:[]quest_reward = array:
        entitlement_quest_reward:
            Icon := MyIcon
            Name := SharpshooterRewardName
            Description := SharpshooterRewardDescription
            ShortDescription := SharpshooterRewardShortDescription
            Entitlement := SniperSkin
            Quantity := 1

Call SetProgress() on the objective as headshots land. When Progress reaches RequiredCount, the quest completes and grants the reward automatically to all participants.

Setting progress

SetProgress() is how you manipulate Progress on a progress_quest_objective — you can increase or decrease it at any time.

Once Progress is greater than or equal to RequiredCount, the objective is considered complete. Because basic_quest wires its objective to the quest, a complete objective automatically completes the quest and grants its rewards. You never need to call Complete() yourself.

Self-driving quests

Instead of calling SetProgress() from outside,you can subclass progress_quest_objective and have the objective listen to game events and update itself.

Here’s an elimination counter that watches EliminatedEvent and bumps its own progress:

elimination_quest_objective := class(progress_quest_objective):
    Watch<public>(EliminationManager:elimination_manager_device, Collection:quest_collection, Quest:quest)<suspends>:void =
        loop:
            MaybeEliminator := EliminationManager.EliminationEvent.Await()
            if:
                Eliminator := MaybeEliminator?
                Memberships := Collection.Quests[Quest]
                Contributors := GetContributors(Memberships)
                Eliminator.IsContributor[Contributors]
            then:
                SetProgress(Progress + 1.0)


EliminationQuestName<localizes> : message = "Eliminate 50 enemies"
EliminationQuestDescription<localizes> : message = "Eliminate enemies tracked by the elimination manager"
EliminationQuestShortDescription<localizes> : message = "Eliminations"


elimination_quest<public> := class(basic_quest):
    var Icon<override>:texture = MyIcon
    var Name<override>:message = EliminationQuestName
    var Description<override>:message = EliminationQuestDescription
    var ShortDescription<override>:message = EliminationQuestShortDescription
    var Objective<override>:quest_objective = elimination_quest_objective:
        Icon := MyIcon
        Name := EliminationQuestName
        Description := EliminationQuestDescription
        ShortDescription := EliminationQuestShortDescription
        RequiredCount := 50.0


StartEliminationQuest(Collection:quest_collection, Player:agent, EliminationManager:elimination_manager_device):void =
    Quest := elimination_quest{}
    Participant := agent_quest_participant{ Agent := Player }
    Collection.JoinQuest(Quest, Participant, quest_participant_info{})
    if (Objective := elimination_quest_objective[Quest.Objective]):
        spawn { Objective.Watch(EliminationManager, Collection, Quest) }


(Eliminator:agent).IsContributor<public>(Contributors:[]quest_participant)<decides><reads>:void =
    first:
        Contributor : Contributors
        P := agent_quest_participant[Contributor]
        P.Agent = Eliminator
    do { P }

Because the objective manages its own progress, you don’t need any external code calling SetProgress() — and no polling loop either.

The three participation flags

Once a quest is set up, you control who takes part in it through three flags on quest_participant_info:

  • Contributes — which players can make progress

  • Observes — which players can see the quest UI

  • Receives — which players get the reward

They default to true, so quest_participant_info{} just works. Mix them for shared quests, observer modes, assisted objectives.

Joining players to quests

Join a player to a quest through the quest_collection — it’s the source of truth for who’s participating in what:

Info := quest_participant_info{}

Collection.JoinQuest(Quest, Participant, Info)

Every join returns a quest_membership — an immutable record pairing the quest, participant, and their flags.

React to quest changes instantly with events

Every object in the API — quests, objectives, rewards, collections, and participants — exposes listenable events you can subscribe to directly.

Quest.CompleteEvent.Subscribe(OnQuestComplete)

Quest.JoinEvent.Subscribe(OnPlayerJoined)

ProgressEvent, GrantEvent, AbandonEvent — they’re all there, on quests, objectives, rewards, and collections.

Toasts, banners, HUD counters — wire them directly.

Build today, ship soon

Every type in this API is experimental, so the surface may change as it stabilizes. Build with it, but plan to revisit as updates roll out. And of course, please let us know what you think of it.

For more information on the API, check out the API documentation.

Two imports, one subclass, and you’ve got a working quest. Go build!

/Verse.org/Progression + /UnrealEngine.com/Progression