---
title: Cross-behaviour calls in CloudBehaviours
product: Cloud Code
version: 3.0.0
latest_version: 3.0.0
status: This page documents version 3.0.0. The latest version is 3.0.0.
canonical: https://docs.unity.com/en-us/cloud-code/3.0.0/cloudbehaviours/calling-another-behaviour
latest_url: https://docs.unity.com/en-us/cloud-code/3.0.0/cloudbehaviours/calling-another-behaviour.md
last_updated: 2026-10-23
generated_at: 2026-11-04T11:39:42.959Z
locale: en-us
---

# Cross-behaviour calls in CloudBehaviours

> Call an endpoint on another CloudBehaviour from inside your own, in another scope instance or another module.

> **Important:**
>
> This page describes an experimental feature that might change significantly before release. It's not recommended to use or rely on experimental features in production environments due to potential instability.

From inside a CloudBehaviour, you can call an endpoint on another CloudBehaviour. How you make the call depends on where the target runs:

| Target                                                         | How to call it                                                                                                                                            |
| :------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- |
| A CloudBehaviour in another scope instance, in the same module | Use the generated interface and `ForScope`. Refer to [Call a CloudBehaviour in another scope instance](#call-a-cloudbehaviour-in-another-scope-instance). |
| A CloudBehaviour in another scope instance, in another module  | Call it as a Cloud Code module endpoint. Refer to [Call a CloudBehaviour in another module](#call-a-cloudbehaviour-in-another-module).                    |
| The same CloudBehaviour in the current scope instance          | Call the method directly, the same as any other C# method.                                                                                                |
| A different CloudBehaviour in the current scope instance       | You can't call it and wait for its result. Refer to [Call the current scope instance](#call-the-current-scope-instance).                                  |

To run an endpoint on the same CloudBehaviour later instead of now, schedule a timer. Refer to [Timers in CloudBehaviours](./timers.md).

## Call a CloudBehaviour in another scope instance

The Unity Editor generates an `I{Behaviour}` interface for every CloudBehaviour in your module. Inject the interface with `[CloudCodeService]`, then call a member on the interface to get back an `ICall` that hasn't run yet. To run the `ICall`, pass the target's scope ID to `ForScope(scopeId)`.

An `ICall` does nothing until you run it. If you create an `ICall` and never call `ForScope` or `ScheduleAsync` on it, the build fails with `CCSG133`.

The following example shows a `MultiplayerSession`-scoped `MatchRoom` that updates each player's `PlayerRecord` at the end of a match. `PlayerRecord` is `Player`-scoped because it belongs to one player and outlives any single match:

```csharp title="MatchRoom calling PlayerRecord"
[StateScope(Scope.Player)]
public class PlayerRecord : CloudBehaviour
{
    public int MatchesPlayed;
    public int PersonalBest;

    [CloudCodeAccess(Access.Service)]
    public int RecordMatch(int score)
    {
        MatchesPlayed++;
        if (score > PersonalBest) PersonalBest = score;
        return PersonalBest;
    }
}

[StateScope(Scope.MultiplayerSession)]
public class MatchRoom : CloudBehaviour
{
    [CloudCodeSerializeProperty] private Dictionary<string, int> _scores = new();

    public async Task EndMatch([CloudCodeService] IPlayerRecord playerRecord)
    {
        foreach (var entry in _scores)
        {
            var best = await playerRecord.RecordMatch(entry.Value).ForScope(entry.Key);
            Logger.LogInformation("{PlayerId} personal best is now {Best}", entry.Key, best);
        }
    }
}
```

`ForScope` takes the scope ID of the target CloudBehaviour's own `[StateScope]`, not the caller's scope ID. In this example, `entry.Key` is a player ID, which matches the `Scope.Player` scope of `PlayerRecord`.

`ForScope` can't target the scope instance that's already running your code. If you pass `Context.ScopeId`, the build fails with `CCSG138`, because a scope instance can't start a second call while it's still running the current one. Refer to [Call the current scope instance](#call-the-current-scope-instance).

Awaiting the call returns the endpoint's return value directly. The caller doesn't receive the target's synchronized state. If the call changes that state, Cloud Code pushes the new state to the clients in the target's scope, the same as after any other endpoint call, unless the target has turned off `AutoSynchronize`. In this example, each player receives their updated `PlayerRecord`. For more information, refer to [Control synchronization manually](./synchronized-state.md#control-synchronization-manually).

### Get a scope ID

Read the scope ID to pass to `ForScope` from the CloudBehaviour's `Context`:

| Target scope               | Scope ID             | Notes                                                                                                                 |
| :------------------------- | :------------------- | :-------------------------------------------------------------------------------------------------------------------- |
| `Scope.Player`             | `Context.PlayerId`   | The ID of the player making the current call. For the other players in a session, use `Context.Session.PlayerIds`.    |
| `Scope.MultiplayerSession` | `Context.Session.Id` | Only available when the calling CloudBehaviour is `MultiplayerSession`-scoped. `Context.Session` is `null` otherwise. |

When a service such as a timer or another CloudBehaviour calls the endpoint, `Context.PlayerId` is `null`. In those calls, pass the scope ID as an endpoint argument instead.

## Restrict access to the target endpoint

A cross-behaviour call arrives with no player identity. The player ID you pass to `ForScope` selects only which scope instance runs the endpoint. The player ID doesn't make the call on that player's behalf.

The target is still an endpoint that a client can call directly. Mark the target `[CloudCodeAccess(Access.Service)]` so no player can call it directly. Refer to [Access control in CloudBehaviours](./access-control.md).

## Call a CloudBehaviour in another module

The generated interface only reaches CloudBehaviours in the same module. To call a CloudBehaviour in another module, call its endpoint as a Cloud Code module endpoint, and pass the scope ID of the target scope instance. The target scope instance must be different from the one that's running your code. Refer to [Call the current scope instance](#call-the-current-scope-instance).

## Call the current scope instance

A scope instance runs one call at a time. While your endpoint runs, any other call into the same scope instance waits for your endpoint to finish. How a call into the current scope instance fails depends on how you make the call:

* If you call through the generated interface with `ForScope`, the call fails immediately. If you pass `Context.ScopeId`, the build fails with `CCSG138`. If you pass another value that matches the current scope ID, the call throws an `InvalidOperationException` at runtime.
* If you call a Cloud Code module endpoint, including a CloudBehaviour in another module, nothing stops the call. The call waits for your endpoint, and your endpoint waits for the call, until the request fails with a `Gateway Timeout` error.

To run code on the same CloudBehaviour in the current scope instance, call the method directly, the same as any other C# method. You can't call a different CloudBehaviour in the current scope instance and wait for its result.

To share logic between modules in the same scope, refer to [Avoid calls into your own scope](../modules/stateful-cloud-code/state-scopes#avoid-calls-into-your-own-scope).

## Requirements

To call another CloudBehaviour through its generated interface, you must do the following:

* Put both CloudBehaviours in the same module. If the target is in another module, refer to [Call a CloudBehaviour in another module](#call-a-cloudbehaviour-in-another-module).
* Mark the injected parameter with `[CloudCodeService]`. Otherwise, the build fails with `CCSG136`. Refer to [Inject services into an RPC](./rpcs.md#inject-services-into-an-rpc).
* Register the target's interface in your module's `ICloudCodeSetup`. In this example, call `config.AddPlayerRecordClient()`. Refer to [Register the generated interface](./rpcs.md#register-the-generated-interface).
* Pass only the endpoint's own arguments. The Editor removes every `[CloudCodeService]` parameter from `I{Behaviour}`, so the call has no argument for the injected parameter.
* Give each CloudBehaviour class in the module a unique name, even across namespaces.

> **Note:**
>
> A timer always runs on the CloudBehaviour that scheduled it, so calling `ScheduleAsync` on another CloudBehaviour's `I{Behaviour}` fails the build with `CCSG132`. To defer work on another CloudBehaviour, schedule your own endpoint and call `ForScope` from inside it. Refer to [Timers in CloudBehaviours](./timers.md).

## Additional resources

* [Game organization in CloudBehaviours](./organize-your-game-into-behaviours.md)
* [Multiplayer game security in CloudBehaviours](./secure-multiplayer-game-logic.md)
* [Timers in CloudBehaviours](./timers.md)
