Develop

Use Spatial Anchors

Updated: Apr 14, 2026

Set up project

  1. In Edit > Project Settings > Plugins > Meta XR, in the Mobile section, select Passthrough Enabled and Anchor Support.
  2. Follow the typical steps for adding a Passthrough layer to an Unreal project.

Asynchronous anchor events

Most Spatial Anchors functions are asynchronous. That is, the results of the functions can’t be processed immediately as part of the return value and instead the results are broadcast later. In a Blueprint, this is performed using latent actions. There are success and failure execution pins available for every asynchronous Spatial Anchors function. In code, this is performed with a callback delegate passed to the function call.

Call asynchronous Blueprint function

Using one of the latent Spatial Anchors actions, you can respond to success or failure events for an asynchronous call by connecting to the output execution pins. Keep in mind that the success and failure execution pins don’t execute in the same frame as the start of the function call. To execute logic that isn’t dependent on the asynchronous results of Spatial Anchors Blueprints you can continue the execution flow by way of the unnamed immediate execution pin.
example of async logic
After the asynchronous function is completed, the success or failure pins execute. If the request was successful you can use the output data members that are part of the latent action.

Call asynchronous C++ function

Executing an asynchronous function in C++ requires passing a delegate as a parameter to the function that starts the asynchronous work. This delegate is executed in the case of success or failure and has a parameter that determines success as well as other parameters you can use in the success case. For more information, see “Delegates and Lambda Functions” in the Unreal Engine documentation.
For example:
void CreateAnchorCallback(EOculusXRAnchorResult::Type Result, UOculusXRAnchorComponent* Anchor)
{
    UE_LOG(LogTemp, Log, TEXT("Result: %s"), *UEnum::GetValueAsString(Result));
}

// Somewhere else in the code
EOculusXRAnchorResult::Type OutResult;
OculusXRAnchors::FOculusXRAnchors::CreateSpatialAnchor(
    TargetActor->GetActorTransform(),
    TargetActor,
    FOculusXRSpatialAnchorCreateDelegate::CreateRaw(SomePointer, &SomeClass::CreateAnchorCallback),
    OutResult);

Persist anchors

Anchors can be saved to the headset’s local storage or to the cloud using SaveAnchor or SaveAnchorList. You can query saved anchors by providing a list of anchor UUIDs.
Anchors saved locally persist across sessions. Cloud-saved anchors are not persistent indefinitely.
For locally persistent spatial anchors, save anchor UUIDs to an app-specific, persistent save game. For more information, see “Saving and Loading Your Game” in the Unreal Engine documentation.

Use the Blueprint interface

There’s no need to add any mandatory components or managers to a scene to use spatial anchors. You can access the Spatial Anchors system from Blueprints using a set of Blueprint nodes. For more information, see Blueprint API Reference.

Create Spatial Anchor example

  1. Spawn an actor.
    • This actor does not have to have a spatial anchor component attached.
  2. Call the Blueprint method OculusXR Async Create Spatial Anchor and pass in the anchor transform and actor as parameters.
    • If the specified actor does not have a spatial anchor component attached, a new component is constructed for you.
  3. Execute logic using the Success and Failure pins.
    • The resulting Anchor component output is only valid in the case of success.
create spatial anchor

Erase Spatial Anchor example

  1. Call the Blueprint method OculusXR Async Erase Anchor.
  2. Pass in an actor that has a spatial anchor attached.
  3. Execute logic using the Success or Failure pins.
    • The resulting Actor and UUID output is only valid in the case of success.
    • The UUID of the destroyed anchor is returned for your bookkeeping.
Erase can also work as an “unsave” operation. It is up to you to decide whether you destroy the anchor actor representation as part of Erase.
Note: EraseAnchor() only supports locally saved anchors. The implementation explicitly checks that the anchor is stored at EOculusXRSpaceStorageLocation::Local and rejects non-local anchors.
erase spatial anchor

Save Spatial Anchor example

You can save a specific spatial anchor or multiple anchors.
To save a specific spatial anchor:
  1. Call the Blueprint method OculusXR Async Save Anchor.
    • Specify the target actor that has a spatial anchor attached.
    • Specify the storage location. This is either Local or Cloud.
  2. Execute logic using the Success or Failure pins.
    • The output Anchor Component is only valid if success is executed.
save spatial anchor
To save multiple anchors:
  1. Call the Blueprint method OculusXR Async Save Anchors.
    • Specify the target actors that have spatial anchors attached.
    • Specify the storage location. This is either Local or Cloud.
  2. Execute logic using the Success or Failure pins.
    • The output Anchor Components array is only valid if success is executed.
save spatial anchor

Load Spatial Anchor example

  1. Get a list of Anchor UUIDs to request loading.
    • This can be from a save game or anywhere else you are storing UUIDs.
  2. Call the Blueprint method OculusXR Async Query Anchors.
    • Specify the storage location to query from. This can be Local or Cloud.
  3. If successful, iterate the query results and then construct new actors using the Spawn Oculus Anchor Actor From Query function.
load spatial anchor

Use the Spatial Anchors C++ API

The Spatial Anchors C++ API is accessed through the OculusXRAnchors header file. Each asynchronous function that is part of the interface takes a delegate parameter and an EOculusXRAnchorResult::Type output parameter. The delegate includes a result parameter as its first argument which represents the success or failure state of that call. The following table lists each delegate and their associated parameters.
DelegateParameters
FOculusXRSpatialAnchorCreateDelegate
EOculusXRAnchorResult::Type Result
UOculusXRAnchorComponent* Anchor
FOculusXRAnchorEraseDelegate
EOculusXRAnchorResult::Type Result
FOculusXRUUID AnchorUUID
FOculusXRAnchorSaveDelegate
EOculusXRAnchorResult::Type Result
UOculusXRAnchorComponent* Anchor
FOculusXRAnchorSaveListDelegate
EOculusXRAnchorResult::Type Result
const TArray<UOculusXRAnchorComponent*>& SavedAnchors
FOculusXRAnchorQueryDelegate
EOculusXRAnchorResult::Type Result
const TArray<FOculusXRSpaceQueryResult>& Results
Note: The example code below makes use of lambda delegate bindings. This isn’t a requirement to use the API and is only used to shorten the code being shown.

Create Spatial Anchor example

  1. Spawn an actor.
    • This actor does not have to have a spatial anchor component attached.
  2. Call the method OculusXRAnchors::FOculusXRAnchors::CreateSpatialAnchor and pass the anchor transform, actor, result delegate, and output result in as parameters.
    • If the specified actor does not have a spatial anchor component attached, a new component is constructed for you.
  3. Execute logic in the results delegate.
    • The resulting spatial anchor component is only valid in the case of success.
AActor* NewActor = GetWorld()->SpawnActor<AActor>();

EOculusXRAnchorResult::Type OutResult;
OculusXRAnchors::FOculusXRAnchors::CreateSpatialAnchor(NewActor->GetActorTransform(),
    NewActor,
    FOculusXRSpatialAnchorCreateDelegate::CreateLambda([](EOculusXRAnchorResult::Type Result,
    UOculusXRAnchorComponent* AnchorComponent)
    {
        // Post-create logic in here
    }),
    OutResult
);

Erase Spatial Anchor example

  1. Retrieve an anchor from an actor.
  2. Call the method OculusXRAnchors::FOculusXRAnchors::EraseAnchor and pass the anchor component, result delegate, and output result in as parameters.
  3. Execute logic in the results delegate.
    • The resulting UUID is only valid in the case of success.
UOculusXRAnchorComponent* AnchorComponent = ...; // Retrieve anchor

EOculusXRAnchorResult::Type OutResult;
OculusXRAnchors::FOculusXRAnchors::EraseAnchor(AnchorComponent,
    FOculusXRAnchorEraseDelegate::CreateLambda([](EOculusXRAnchorResult::Type Result,
    FOculusXRUUID UUID)
    {
        // Post-erase logic here
    }),
    OutResult
);

Save Spatial Anchor example

  1. Retrieve an anchor from an actor.
  2. Call the method OculusXRAnchors::FOculusXRAnchors::SaveAnchor and pass the anchor component, storage location, result delegate, and output result in as parameters.
  3. Execute logic in the results delegate.
    • The resulting AnchorComponent is only valid in the case of success.
UOculusXRAnchorComponent* AnchorComponent = ...; // Retrieve anchor

EOculusXRAnchorResult::Type OutResult;
OculusXRAnchors::FOculusXRAnchors::SaveAnchor(AnchorComponent,
    EOculusXRSpaceStorageLocation::Local,
    FOculusXRAnchorSaveDelegate::CreateLambda([](EOculusXRAnchorResult::Type Result,
    UOculusXRAnchorComponent* AnchorComponent)
    {
        // Post-save logic here
    }),
    OutResult
);

Save Spatial Anchor List example

  1. Retrieve a list of anchors from actors.
  2. Call the method OculusXRAnchors::FOculusXRAnchors::SaveAnchorList and pass the array of anchor components, storage location, result delegate, and output result in as parameters.
  3. Execute logic in the results delegate.
    • The resulting SavedAnchors array is only valid in the case of success.
TArray<UOculusXRAnchorComponent*> AnchorComponents = ...; // Retrieve anchors

EOculusXRAnchorResult::Type OutResult;
OculusXRAnchors::FOculusXRAnchors::SaveAnchorList(AnchorComponents,
    EOculusXRSpaceStorageLocation::Local,
    FOculusXRAnchorSaveListDelegate::CreateLambda([](EOculusXRAnchorResult::Type Result,
    const TArray<UOculusXRAnchorComponent*>& SavedAnchors)
    {
        // Post-save logic here
    }),
    OutResult
);

Query Spatial Anchors example

  1. Generate a list of UUIDs.
  2. Call the method OculusXRAnchors::FOculusXRAnchors::QueryAnchors and pass the list of UUIDs, the query location, the result delegate, and the output result in as parameters.
  3. Execute logic in the results delegate.
    • Spawn an actor of your choice. There is a helper method you can use called UOculusXRAnchorBPFunctionLibrary::SpawnActorWithAnchorQueryResults.
EOculusXRSpaceStorageLocation QueryLocation = EOculusXRSpaceStorageLocation::Local;
TArray<FOculusXRUUID> AnchorUUIDs = ...;

EOculusXRAnchorResult::Type OutResult;
OculusXRAnchors::FOculusXRAnchors::QueryAnchors(AnchorUUIDs, QueryLocation,
    FOculusXRAnchorQueryDelegate::CreateLambda([this](EOculusXRAnchorResult::Type Result,
    const TArray<FOculusXRSpaceQueryResult>& Results)
    {
        if (Result == EOculusXRAnchorResult::Success)
        {
            for (auto& it : Results)
            {
                UOculusXRAnchorBPFunctionLibrary::SpawnActorWithAnchorQueryResults(
                    GetWorld(),
                    it,
                    AActor::StaticClass(), // Your class here
                    Owner,
                    Instigator,
                    CollisionHandlingMethod
                );
            }
        }
    }),
    OutResult
);
Note: DiscoverAnchors is a newer API for finding anchors, providing streaming results as they are discovered. QueryAnchors remains available for UUID-based lookups.

Error codes

Error codes are exposed as the result of Spatial Anchors functions. These error codes are available as an output from the Blueprint API and the C++ API. Error codes are defined in the EOculusXRAnchorResult enum. Most enumeration results are general, but there are specific enumerations available for spatial anchors.
UENUM(BlueprintType)
namespace EOculusXRAnchorResult
{
	enum Type
	{
		Success,

		/// Failure
		Failure,
		Failure_InvalidParameter,
		Failure_NotInitialized,
		Failure_InvalidOperation,
		Failure_Unsupported,
		Failure_NotYetImplemented,
		Failure_OperationFailed,
		Failure_InsufficientSize,
		Failure_DataIsInvalid,
		Failure_DeprecatedOperation,
		Failure_ErrorLimitReached,
		Failure_ErrorInitializationFailed,

		/// Space error cases
		Failure_SpaceCloudStorageDisabled,
		Failure_SpaceMappingInsufficient,
		Failure_SpaceLocalizationFailed,
		Failure_SpaceNetworkTimeout,
		Failure_SpaceNetworkRequestFailed,
		Failure_SpaceInsufficientResources,
		Failure_SpaceStorageAtCapacity,
		Failure_SpaceInsufficientView,
		Failure_SpacePermissionInsufficient,
		Failure_SpaceRateLimited,
		Failure_SpaceTooDark,
		Failure_SpaceTooBright,

		/// Warning
		Warning_BoundaryVisibilitySuppressionNotAllowed,
	};
}

Error value details

  • Failure_SpaceCloudStorageDisabled: Saving anchors to cloud storage is not permitted by the user.
  • Failure_SpaceMappingInsufficient: The user did not move around enough for a reliable localization. The user should walk and look around their space before calling save again. For example, the user could walk in a large circle around the center of their playspace.
  • Failure_SpaceLocalizationFailed: The system failed to localize the shared anchor. This could be because the shared anchor is not near the user when calling Query.
  • Failure_SpaceNetworkTimeout: Network operation timed out.
  • Failure_SpaceNetworkRequestFailed: Network operation failed.
  • Failure_SpaceInsufficientResources: System ran out of resources for spatial anchors.
  • Failure_SpaceStorageAtCapacity: Anchor storage has reached its capacity.
  • Failure_SpaceInsufficientView: The system does not have a sufficient view of the environment.
  • Failure_SpacePermissionInsufficient: The app does not have sufficient permissions.
  • Failure_SpaceRateLimited: The spatial anchor request was rate limited.
  • Failure_SpaceTooDark: The environment is too dark for spatial anchors.
  • Failure_SpaceTooBright: The environment is too bright for spatial anchors.
  • Warning_BoundaryVisibilitySuppressionNotAllowed: Boundary visibility suppression is not allowed.

Migrating from legacy Spatial Anchors

The API for Spatial Anchors changed to an improved version in Oculus SDK v43. Any legacy versions of Spatial Anchors in existing projects need to be migrated to this current version.
In general, all legacy Spatial Anchors calls that relied on event listeners have now been changed to latent actions. This means you can fold the event logic into a single execution flow graph rather than handling events in a separate callback.
For example, the legacy Spatial Anchors logic for creating a spatial anchor looked like the following diagram:
legacy spatial anchor
You would:
  1. Bind a callback, usually in begin play, to the Spatial Anchors event you are listening for.
  2. Execute a Spatial Anchors method, in this case “Create Spatial Anchor”.
  3. Wait for the bound callback to execute.
  4. Complete any work necessary in the callback.
In contrast, the current Spatial Anchors logic now resembles the following diagram:
current spatial anchor
  1. Execute a latent action Blueprint node.
  2. Execute Success or Failure pins based on results.
The following table lists the legacy Spatial Anchors calls and their new latent action names. For more information, see Spatial Anchors Blueprint Reference.
BindingLatent Action
Create Spatial Anchor
OculusXR Async Create Spatial Anchor
Erase Spatial Anchor
OculusXR Async Erase Anchor
Save Spatial Anchor
OculusXR Async Save Anchor
Query All Local Spatial Anchors
OculusXR Async Query Anchors
You can use the SpatialAnchorsSample sample project as an example of how to use Spatial Anchors after migrating.