---
title: Использование WIF с Google Cloud
url: https://platform.claude.com/docs/ru/manage-claude/wif-providers/gcp
description: Федерация рабочих нагрузок Google Cloud (Cloud Run, Cloud Functions, App Engine, GCE, GKE) с Claude API с использованием подписанных Google токенов идентификации вместо статических ключей API.
---

Любая вычислительная среда Google Cloud с доступом к серверу метаданных экземпляра (Cloud Run, Cloud Functions, App Engine, Compute Engine (GCE) и GKE с Workload Identity) может запросить подписанный Google токен идентификации (identity token) для прикреплённого к ней сервисного аккаунта. Издатель токена — `https://accounts.google.com`, и Anthropic может проверить его напрямую через стандартное обнаружение OIDC (OIDC discovery) без какой-либо дополнительной настройки Google Cloud.

В этом руководстве показано, как зарегистрировать издателя Google в Anthropic, привязать сервисный аккаунт Google к сервисному аккаунту Anthropic и настроить вашу рабочую нагрузку так, чтобы она обменивала свой токен идентификации на краткосрочный токен доступа к Claude API.

## Предварительные требования

* Знакомство с [концепциями WIF](https://platform.claude.com/docs/ru/manage-claude/workload-identity-federation#concepts): сервисные аккаунты, издатели федерации и правила федерации.
* Проект Google Cloud с рабочей нагрузкой, запущенной в Cloud Run, Cloud Functions, App Engine, Compute Engine или GKE.
* Управляемый пользователем сервисный аккаунт Google, прикреплённый к этой рабочей нагрузке (не сервисный аккаунт Compute Engine по умолчанию).
* Разрешение на создание сервисных аккаунтов, издателей федерации и правил федерации в Claude Console для вашей организации Anthropic.

## Настройка Google Cloud

Google автоматически выдаёт токены идентификации любой рабочей нагрузке с прикреплённым сервисным аккаунтом. На стороне Google не нужно ничего включать, кроме прикрепления правильного сервисного аккаунта, но шаги немного различаются для стандартных вычислительных сред и GKE.

<Tabs>
  <Tab title="Cloud Run, Cloud Functions, App Engine, GCE">
    Прикрепите выделенный сервисный аккаунт к вашему сервису или экземпляру:

    ```bash CLI
    gcloud run deploy my-service \
      --service-account inference-worker@my-project.iam.gserviceaccount.com
    ```

    Внутри рабочей нагрузки сервер метаданных возвращает подписанный токен идентификации по запросу. Запросите его с тем значением `audience`, которое вы собираетесь зарегистрировать на стороне Anthropic, и добавьте `format=full`, чтобы ответ содержал утверждение (claim) `email`:

    ```text wrap
    GET http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full
    Metadata-Flavor: Google
    ```

    Или с помощью gcloud CLI:

    ```bash CLI
    gcloud auth print-identity-token \
      --audiences="https://api.anthropic.com" \
      --include-email
    ```

    Эквиваленты для SDK показаны в разделе [Получение и использование токена](https://platform.claude.com/docs/ru/manage-claude/wif-providers/gcp#acquire-and-use-the-token).

    Декодированная полезная нагрузка токена выглядит так:

    ```json
    {
      "iss": "https://accounts.google.com",
      "aud": "https://api.anthropic.com",
      "sub": "104892...",
      "azp": "104892...",
      "email": "inference-worker@my-project.iam.gserviceaccount.com",
      "email_verified": true,
      "exp": 1775527120
    }
    ```

    Утверждение `sub` — это непрозрачный числовой уникальный идентификатор сервисного аккаунта Google. Утверждение `email` — это человекочитаемый адрес сервисного аккаунта. Сопоставляйте и `sub`, и `email` в вашем правиле федерации.
  </Tab>

  <Tab title="GKE с Workload Identity">
    Включите [Workload Identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) в вашем кластере и привяжите ваш сервисный аккаунт Kubernetes к сервисному аккаунту Google с помощью аннотации `iam.gke.io/gcp-service-account`:

    ```yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: inference-worker
      namespace: prod
      annotations:
        iam.gke.io/gcp-service-account: inference-worker@my-project.iam.gserviceaccount.com
    ```

    При наличии этой привязки сервер метаданных GKE возвращает подписанный Google токен, идентичный случаю Cloud Run и GCE: тот же издатель `https://accounts.google.com`, то же утверждение `email`, тот же URL для получения. Настройте Anthropic точно так же, как описано в следующем разделе.

    Токен `format=full` из GKE дополнительно включает утверждения `google.compute_engine.project_id`, `google.compute_engine.zone` и `google.compute_engine.instance_name`, на которые вы можете ссылаться в сопоставителе `condition` правила федерации (CEL-выражение вида `claims.google.compute_engine.project_id == "my-project"`), чтобы ограничить доступ конкретным кластером или пулом узлов.

    <Note>
      Если вы не хотите привязывать сервисные аккаунты Kubernetes к сервисным аккаунтам Google, поды GKE могут вместо этого использовать собственный издатель OIDC кластера (`https://container.googleapis.com/v1/projects/PROJECT/locations/REGION/clusters/CLUSTER`) с проецируемым томом `serviceAccountToken`. Этот путь использует издателя для каждого кластера, а не `accounts.google.com`. См. [Использование WIF с Kubernetes](https://platform.claude.com/docs/ru/manage-claude/wif-providers/kubernetes) для описания этого подхода.
    </Note>
  </Tab>
</Tabs>

## Настройка Anthropic

В Claude Console перейдите на страницу [Workload identity](https://platform.claude.com/settings/workload-identity-federation) (`platform.claude.com/settings/workload-identity-federation`), нажмите **Connect workload** и выберите плитку **Google Cloud**. Мастер проведёт вас через регистрацию издателя, создание сервисного аккаунта и создание правила федерации.

Мастер создаёт эти ресурсы за вас. Используйте следующие значения независимо от того, вводите ли вы их в мастере или отправляете в [Admin API](https://platform.claude.com/docs/ru/manage-claude/wif-admin-api):

**Издатель федерации:** Google публикует свой документ обнаружения OIDC в открытом доступе, поэтому используйте режим обнаружения (discovery mode). Этот единственный издатель охватывает все среды Google Cloud (Cloud Run, GCE, Cloud Functions, App Engine и GKE с Workload Identity). Различайте рабочие нагрузки с помощью правил, а не издателей.

```json
{
  "name": "gcp",
  "issuer_url": "https://accounts.google.com",
  "jwks": { "type": "discovery" }
}
```

**Правило федерации:** Сопоставляйте оба утверждения — `sub` и `email`. `email` — это читаемый адрес сервисного аккаунта; `sub` — числовой уникальный идентификатор сервисного аккаунта, который Google никогда не использует повторно, поэтому его закрепление защищает правило в случае, если сервисный аккаунт будет удалён, а позже будет создан новый с тем же email. Найдите уникальный идентификатор с помощью `gcloud iam service-accounts describe SA_EMAIL --format='value(uniqueId)'`.

```json
{
  "name": "gcp-inference-worker",
  "issuer_id": "fdis_...",
  "match": {
    "audience": "https://api.anthropic.com",
    "claims": {
      "sub": "104892101234567890123",
      "email": "inference-worker@my-project.iam.gserviceaccount.com"
    }
  },
  "target": {
    "type": "service_account",
    "service_account_id": "svac_..."
  },
  "workspace_id": "wrkspc_...",
  "oauth_scope": "workspace:developer",
  "token_lifetime_seconds": 600
}
```

## Получение и использование токена

Внутри рабочей нагрузки Google Cloud получите токен идентификации с сервера метаданных, обменяйте его через `POST /v1/oauth/token` и используйте возвращённый bearer-токен для вызова Claude API. Claude SDK выполняет обмен и цикл обновления за вас, если вы передаёте `identity_token_provider` (typescript: `identityTokenProvider`; php: `identityProvider`; csharp: `IdentityTokenProvider`; go: `option.WithFederationTokenProvider`; java: `IdentityTokenConfig`), как показано в следующих примерах. За исключением Java (см. комментарий в этом примере), этот параметр принимает поставщика токенов, который возвращает свежий токен идентификации с сервера метаданных.

<CodeGroup>
  ```bash cURL
  # Получаем подписанный Google токен идентификации с сервера метаданных
  JWT=$(curl -sS -H "Metadata-Flavor: Google" \
    "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full")

  # Обмениваем его на токен доступа Anthropic
  RESPONSE=$(curl -sS https://api.anthropic.com/v1/oauth/token \
    -H "content-type: application/json" \
    --data @- <<JSON
  {
    "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
    "assertion": "$JWT",
    "federation_rule_id": "$ANTHROPIC_FEDERATION_RULE_ID",
    "organization_id": "$ANTHROPIC_ORGANIZATION_ID",
    "service_account_id": "$ANTHROPIC_SERVICE_ACCOUNT_ID",
    "workspace_id": "$ANTHROPIC_WORKSPACE_ID"
  }
  JSON
  )
  ACCESS_TOKEN=$(echo "$RESPONSE" | jq -r .access_token)

  # Вызываем Claude API
  curl -sS https://api.anthropic.com/v1/messages \
    -H "authorization: Bearer $ACCESS_TOKEN" \
    -H "anthropic-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 1024,
      "messages": [{"role": "user", "content": "Hello from Cloud Run"}]
    }' | jq -r '.content[] | select(.type == "text") | .text'
  ```

  ```python Python
  import os
  import anthropic
  import google.auth.transport.requests
  import google.oauth2.id_token
  from anthropic import WorkloadIdentityCredentials

  AUDIENCE = "https://api.anthropic.com"


  def fetch_google_identity_token() -> str:
      request = google.auth.transport.requests.Request()
      return google.oauth2.id_token.fetch_id_token(request, AUDIENCE)


  client = anthropic.Anthropic(
      credentials=WorkloadIdentityCredentials(
          identity_token_provider=fetch_google_identity_token,
          federation_rule_id=os.environ["ANTHROPIC_FEDERATION_RULE_ID"],
          organization_id=os.environ["ANTHROPIC_ORGANIZATION_ID"],
          service_account_id=os.environ["ANTHROPIC_SERVICE_ACCOUNT_ID"],
          workspace_id=os.environ.get("ANTHROPIC_WORKSPACE_ID"),
      ),
  )

  message = client.messages.create(
      model="claude-opus-5-5",
      max_tokens=1024,
      messages=[{"role": "user", "content": "Hello from Cloud Run"}],
  )
  print(next(block.text for block in message.content if block.type == "text"))
  ```

  ```typescript TypeScript
  import Anthropic from "@anthropic-ai/sdk";
  import { oidcFederationProvider } from "@anthropic-ai/sdk/lib/credentials/oidc-federation";

  const METADATA_URL =
    "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full";

  async function fetchGoogleIdentityToken(): Promise<string> {
    const response = await fetch(METADATA_URL, {
      headers: { "Metadata-Flavor": "Google" }
    });
    return response.text();
  }

  const client = new Anthropic({
    credentials: oidcFederationProvider({
      identityTokenProvider: fetchGoogleIdentityToken,
      federationRuleId: process.env.ANTHROPIC_FEDERATION_RULE_ID!,
      organizationId: process.env.ANTHROPIC_ORGANIZATION_ID!,
      serviceAccountId: process.env.ANTHROPIC_SERVICE_ACCOUNT_ID,
      workspaceId: process.env.ANTHROPIC_WORKSPACE_ID,
      baseURL: "https://api.anthropic.com",
      fetch
    })
  });

  const message = await client.messages.create({
    model: "claude-opus-5-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Hello from Cloud Run" }]
  });
  for (const block of message.content) {
    if (block.type === "text") {
      console.log(block.text);
    }
  }
  ```

  ```go Go
  const audience = "https://api.anthropic.com"

  googleIDToken := func(ctx context.Context) (string, error) {
  	creds, err := idtoken.NewCredentials(&idtoken.Options{Audience: audience})
  	if err != nil {
  		return "", err
  	}
  	tok, err := creds.Token(ctx)
  	if err != nil {
  		return "", err
  	}
  	return tok.Value, nil
  }

  client := anthropic.NewClient(
  	option.WithFederationTokenProvider(googleIDToken, option.FederationOptions{
  		FederationRuleID: os.Getenv("ANTHROPIC_FEDERATION_RULE_ID"),
  		OrganizationID:   os.Getenv("ANTHROPIC_ORGANIZATION_ID"),
  		ServiceAccountID: os.Getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"),
  		WorkspaceID:      os.Getenv("ANTHROPIC_WORKSPACE_ID"),
  	}),
  )

  message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Hello from Cloud Run")),
  	},
  })
  if err != nil {
  	panic(err)
  }
  for _, block := range message.Content {
  	if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
  		fmt.Println(textBlock.Text)
  		break
  	}
  }
  ```

  ```java Java
  import com.anthropic.config.AuthenticationConfig;
  import com.anthropic.config.AuthenticationType;
  import com.anthropic.config.IdentityTokenConfig;
  import com.anthropic.config.InMemoryProfileConfigProvider;
  import com.anthropic.config.ProfileConfig;
  // ...
  import java.net.URI;
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;

  void main() throws Exception {
      HttpClient http = HttpClient.newHttpClient();
      HttpRequest metadataRequest = HttpRequest.newBuilder()
              .uri(URI.create("http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full"))
              .header("Metadata-Flavor", "Google")
              .build();

      // Java SDK принимает сам токен или путь к файлу, а не функцию; при
      // source("value") path(...) содержит токен. SDK отправляет тот же токен
      // при каждом обновлении токена доступа Anthropic, что завершится ошибкой, когда он
      // истечёт или, если у него есть утверждение jti, будет использован. Для долго работающих
      // процессов передайте source("file") и записывайте новый токен в файл перед
      // каждым обновлением.
      String googleIdentityToken = http.send(metadataRequest, HttpResponse.BodyHandlers.ofString()).body();

      AnthropicClient client = AnthropicOkHttpClient.builder()
              .configurationProvider(InMemoryProfileConfigProvider.of(ProfileConfig.builder()
                      .organizationId(System.getenv("ANTHROPIC_ORGANIZATION_ID"))
                      .workspaceId(System.getenv("ANTHROPIC_WORKSPACE_ID"))
                      .authentication(AuthenticationConfig.builder()
                              .type(AuthenticationType.OIDC_FEDERATION)
                              .federationRuleId(System.getenv("ANTHROPIC_FEDERATION_RULE_ID"))
                              .serviceAccountId(System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
                              .identityToken(IdentityTokenConfig.builder()
                                      .source("value")
                                      .path(googleIdentityToken)
                                      .build())
                              .build())
                      .build()))
              .build();

      var message = client.messages().create(MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(1024)
              .addUserMessage("Hello from Cloud Run")
              .build());

      IO.println(message.content());
  }
  ```

  ```csharp C#
  using Anthropic.Credentials;
  // ...

  var credentials = new WorkloadIdentityCredentials(new WorkloadIdentityOptions
  {
      FederationRuleId = Environment.GetEnvironmentVariable("ANTHROPIC_FEDERATION_RULE_ID")!,
      OrganizationId = Environment.GetEnvironmentVariable("ANTHROPIC_ORGANIZATION_ID"),
      ServiceAccountId = Environment.GetEnvironmentVariable("ANTHROPIC_SERVICE_ACCOUNT_ID"),
      WorkspaceId = Environment.GetEnvironmentVariable("ANTHROPIC_WORKSPACE_ID"),
      IdentityTokenProvider = new MetadataTokenProvider(),
  });
  using var client = new AnthropicClient(new ClientOptions { Credentials = credentials });

  var message = await client.Messages.Create(new()
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Messages = [new() { Role = Role.User, Content = "Hello from Cloud Run" }],
  });
  foreach (var block in message.Content)
  {
      if (block.Value is TextBlock textBlock)
      {
          Console.WriteLine(textBlock.Text);
      }
  }

  class MetadataTokenProvider : IIdentityTokenProvider
  {
      private const string METADATA_URL =
          "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full";

      private static readonly HttpClient httpClient = new()
      {
          DefaultRequestHeaders = { { "Metadata-Flavor", "Google" } },
      };

      public async Task<string> GetIdentityTokenAsync(CancellationToken ct = default)
      {
          return await httpClient.GetStringAsync(METADATA_URL, ct);
      }
  }
  ```

  ```bash CLI
  # Записываем подписанный Google токен идентификации в файл, доступный для чтения CLI
  ANTHROPIC_IDENTITY_TOKEN_FILE=$(mktemp)
  curl -sS -H "Metadata-Flavor: Google" \
    "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full" \
    > "$ANTHROPIC_IDENTITY_TOKEN_FILE"
  export ANTHROPIC_IDENTITY_TOKEN_FILE

  # Переменные ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
  # ANTHROPIC_SERVICE_ACCOUNT_ID и ANTHROPIC_WORKSPACE_ID считываются из окружения.
  ant messages create \
    --model claude-opus-5-5 \
    --max-tokens 1024 \
    --message '{role: user, content: "Hello from Cloud Run"}'
  ```

  ```php PHP
  use Anthropic\Client;
  use Anthropic\Lib\Credentials\CredentialResult;
  use Anthropic\Lib\Credentials\IdentityTokenProvider;
  use Anthropic\Lib\Credentials\TokenCache;
  use Anthropic\Lib\Credentials\WorkloadIdentityCredentials;

  const METADATA_URL = 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full';

  class GoogleIdentityToken implements IdentityTokenProvider
  {
      public function getIdentityToken(): string
      {
          $context = stream_context_create([
              'http' => ['header' => "Metadata-Flavor: Google\r\n"],
          ]);

          return file_get_contents(METADATA_URL, false, $context);
      }
  }

  $credentials = new CredentialResult(
      provider: new TokenCache(new WorkloadIdentityCredentials(
          identityProvider: new GoogleIdentityToken(),
          federationRuleId: getenv('ANTHROPIC_FEDERATION_RULE_ID'),
          organizationId: getenv('ANTHROPIC_ORGANIZATION_ID'),
          serviceAccountId: getenv('ANTHROPIC_SERVICE_ACCOUNT_ID'),
          workspaceId: getenv('ANTHROPIC_WORKSPACE_ID') ?: null,
      )),
  );
  $client = new Client(credentials: $credentials);

  $message = $client->messages->create(
      model: 'claude-opus-5-5',
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Hello from Cloud Run']],
  );
  $textBlock = array_find($message->content, static fn ($block): bool => $block->type === 'text');
  echo $textBlock->text, PHP_EOL;
  ```

  ```ruby Ruby
  require "anthropic"
  require "net/http"

  METADATA_URL = "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full"

  credentials = Anthropic::WorkloadIdentityCredentials.new(
    identity_token_provider: -> { Net::HTTP.get(URI(METADATA_URL), {"Metadata-Flavor" => "Google"}) },
    federation_rule_id: ENV.fetch("ANTHROPIC_FEDERATION_RULE_ID"),
    organization_id: ENV.fetch("ANTHROPIC_ORGANIZATION_ID"),
    service_account_id: ENV.fetch("ANTHROPIC_SERVICE_ACCOUNT_ID"),
    workspace_id: ENV["ANTHROPIC_WORKSPACE_ID"]
  )
  client = Anthropic::Client.new(credentials: credentials)

  message = client.messages.create(
    model: "claude-opus-5-5",
    max_tokens: 1024,
    messages: [{role: "user", content: "Hello from Cloud Run"}]
  )
  puts message.content.find { it.type == :text }.text
  ```
</CodeGroup>

Срок действия токенов идентификации Google истекает примерно через час. За исключением Java (см. комментарий в этом примере), SDK автоматически повторно вызывает поставщика токенов и повторно выполняет обмен до истечения срока действия. Для shell-скриптов, работающих дольше, чем `expires_in` токена доступа, обновляйте токен по таймеру и повторяйте обмен.

## Проверка настройки

Изнутри вашей рабочей нагрузки декодируйте токен идентификации и убедитесь, что утверждения соответствуют вашему правилу:

```bash cURL
curl -sS -H "Metadata-Flavor: Google" \
  "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full" \
  | jq -rR 'split(".")[1] | gsub("-";"+") | gsub("_";"/") | @base64d | fromjson'
```

Убедитесь, что `iss` равно `https://accounts.google.com`, `aud` равно `https://api.anthropic.com`, а `email` совпадает со значением в вашем правиле федерации. Затем выполните обмен из предыдущего раздела. Успешный обмен возвращает `access_token`, начинающийся с `sk-ant-oat01-`, и значение `expires_in` в секундах. Если обмен завершается непрозрачным ответом `401` `authentication_error` (сообщение `Authentication failed`), проверьте причину отказа на [странице истории аутентификации](https://platform.claude.com/settings/workload-identity-federation?tab=history) в Claude Console и обратитесь к разделу [Устранение неполадок при неудачном обмене](https://platform.claude.com/docs/ru/manage-claude/wif-reference#troubleshoot-a-failed-exchange); наиболее распространённая причина на стороне Google Cloud — отсутствие утверждения `email` (запрашивайте токен с `format=full`, чтобы оно было включено).

## Ограничение области действия правила

<Warning>
  Утверждение Google `sub` — это непрозрачный числовой уникальный идентификатор сервисного аккаунта, не имеющий стабильного префикса. `subject_prefix` с завершающим `*` соответствует произвольным сервисным аккаунтам во всех проектах Google Cloud, и любой из них сможет получить федеративный токен Anthropic.
</Warning>

Ограничьте блок `match` правила самой узкой областью, подходящей для вашего сценария использования:

* **Точное сопоставление `sub`:** Укажите полный числовой уникальный идентификатор в `claims.sub` и никогда не используйте `subject_prefix` для токенов Google.
* **Закрепление утверждения `email`:** Добавьте `claims.email` вместе с `sub`, чтобы совпадать должны были и стабильный идентификатор, и читаемый адрес.
* **Закрепление аудитории:** Установите `audience` в точное значение, которое вы запрашиваете у сервера метаданных, чтобы токены, выпущенные для других потребителей, отклонялись.
* **Закрепление проекта в GKE:** Для токенов `format=full` добавьте `condition`, например `claims.google.compute_engine.project_id == "my-project"`, чтобы ограничить правило узлами одного проекта.

## Следующие шаги

* Прочитайте страницу [Workload Identity Federation](https://platform.claude.com/docs/ru/manage-claude/workload-identity-federation), чтобы ознакомиться с полной моделью ресурсов и приоритетом учётных данных в SDK.
* Добавьте отдельное правило федерации для каждой среды (production, staging), чтобы вы могли отозвать одно, не затрагивая остальные.
