> **Building with AI coding agents?** Install the authstack plugin with one command. This equips your agent with accurate Scalekit implementation patterns.
>
> **Recommended** (the `-y` flags skip prompts, so the command runs without an interactive terminal):
> ```bash
> npx -y @scalekit-inc/cli setup -y
> ```
>
> Global:
> ```bash
> npm install -g @scalekit-inc/cli
> scalekit setup -y
> ```
>
> Supports Claude Code, Cursor, GitHub Copilot, Codex + skills for other Agent Skills-compatible agents in `~/.agents/skills`.
> Skills: integrate-agentkit, implement-saaskit, add-mcp-oauth, implement-sso, implement-scim.
> [Full setup guide](https://docs.scalekit.com/dev-kit/build-with-ai/)

---

# Resource

Manage resource clients and the consents your end users grant against them
<div class="sdk-client-page">

Use `scalekitClient.resources()` to manage resource clients and to read and revoke the consents your end users grant against one. A consent records that one end user allowed a specific resource client to act on their behalf.

The same audit and revoke actions are available in the dashboard under [Managing MCP clients](/authenticate/mcp/managing-mcp-clients/).

### getResource
<div class="sdk-method-section">

      Retrieves a single resource by id.

        The resource to fetch (format: `res_...`).

        Resource object.

```java
GetResourceResponse resource = scalekitClient.resources().getResource("res_xxx");
System.out.println(resource.getResource());
```

</div>

### listResources
<div class="sdk-method-section">

      Lists resources of a given type in the environment, with pagination.

        The resource type to filter by. Supported value: `ResourceType.MCP_SERVER`.

        Page size for pagination (max 30). `0` uses the server default.

        Page token for pagination; empty for the first page.

        Paginated resources.

```java
ListResourcesResponse resources = scalekitClient.resources().listResources(ResourceType.MCP_SERVER, 20, "");
resources.getResourcesList().forEach(r -> System.out.println(r.getId() + " " + r.getScopesList()));
```

</div>

### createResourceClient
<div class="sdk-method-section">

      Creates a resource client. Returns the created `client` and a `plainSecret` - the plaintext client secret, only available at creation time.

        The resource to create the client for (format: `res_...`).

        The client properties, built via `ResourceClient.newBuilder()`. `scopes` should be the same or a subset of the scopes available for the resource. `customClaims` is a flat key/value structure only. `expiry` (access token lifetime in seconds) defaults to the resource's configured expiry. `redirectUris` are the allowed redirect URIs for a pre-registered client. There is no `audience` field - a non-empty `audience` list throws `IllegalArgumentException`, since audience is always server-determined.

        The created client and its plaintext secret.

```java
import com.scalekit.grpc.scalekit.v1.clients.CreateResourceClientResponse;
import com.scalekit.grpc.scalekit.v1.clients.GetResourceResponse;
import com.scalekit.grpc.scalekit.v1.clients.ResourceClient;
import com.scalekit.grpc.scalekit.v1.clients.Scope;
import java.util.List;
import java.util.stream.Collectors;

GetResourceResponse resource = scalekitClient.resources().getResource("res_xxx");
List<String> allowedScopes = resource.getResource().getScopesList().stream()
        .filter(Scope::getEnabled)
        .map(Scope::getName)
        .collect(Collectors.toList());

CreateResourceClientResponse created = scalekitClient.resources().createResourceClient(
    "res_xxx",
    ResourceClient.newBuilder().setName("My Resource Client").addAllScopes(allowedScopes).build()
);

System.out.println(created.getClient().getClientId());
// Store created.getPlainSecret() in your secret manager now - it is never
// returned again. It grants full access as this client, so if it leaks,
// replace it right away: create a new secret and delete the compromised one
// (delete first if you're already at your secret limit; if it's your only
// secret, raise the limit before rotating).
```

</div>

### getResourceClient
<div class="sdk-method-section">

      Fetches a single resource client, along with the end-users who have granted it consent.

        The resource the client must belong to (format: `res_...`).

        The client ID (format: `m2m_...`).

        The resource client.

```java
GetResourceClientResponse got = scalekitClient.resources().getResourceClient("res_xxx", "m2m_xxx");
System.out.println(got.getClient().getName());
```

</div>

### listResourceClients
<div class="sdk-method-section">

      Lists resource clients.

        The resource whose clients to list (format: `res_...`).

        The resource's clients, plus `totalDcrClients` and `totalStaticClients` counts.

```java
ListResourceClientsResponse list = scalekitClient.resources().listResourceClients("res_xxx");

System.out.println(list.getTotalDcrClients() + " " + list.getTotalStaticClients());
list.getClientsList().forEach(c -> System.out.println(c.getClientId() + " " + c.getName()));
```

</div>

### updateResourceClient
<div class="sdk-method-section">

      Updates a resource client.

        The resource the client must belong to (format: `res_...`).

        The client ID to update (format: `m2m_...`).

        Built via `UpdateResourceClientOptions.builder()`. Only the fields set (non-null) are changed. `name`/`description` are a no-op server-side when set to an empty string, not a clear. `scopes`, `customClaims`, and `redirectUris` replace their existing values; set an empty list to clear one of them. There's no `audience` field.

        The updated client.

```java
GetResourceResponse resource = scalekitClient.resources().getResource("res_xxx");
List<String> allowedScopes = resource.getResource().getScopesList().stream()
        .filter(Scope::getEnabled)
        .map(Scope::getName)
        .collect(Collectors.toList());

UpdateResourceClientResponse updated = scalekitClient.resources().updateResourceClient(
    "res_xxx", "m2m_xxx",
    UpdateResourceClientOptions.builder().name("Updated Name").scopes(allowedScopes).build()
);

System.out.println(updated.getClient().getName() + " " + updated.getClient().getScopesList());
```

</div>

### deleteResourceClient
<div class="sdk-method-section">

      Deletes resource clients. Throws if the client is missing or scoped to a different resource.

        The resource the client must belong to (format: `res_...`).

        The client ID to delete (format: `m2m_...`).

        Empty response on success.

```java
scalekitClient.resources().deleteResourceClient("res_xxx", "m2m_xxx");
```

</div>

### createResourceClientSecret
<div class="sdk-method-section">

      Creates a new secret for a resource client. Only 2 client secrets are recommended to exist at a given point in time - use `deleteResourceClientSecret` to remove an existing one first if you need more.

      The plaintext client secret is only ever returned here, at creation time.

        The resource the client must belong to (format: `res_...`).

        The client ID to create a secret for (format: `m2m_...`).

        The new secret, including its plaintext value.

```java
CreateClientSecretResponse secret = scalekitClient.resources().createResourceClientSecret("res_xxx", "m2m_xxx");
// Store secret.getPlainSecret() in your secret manager now - it is never
// returned again. It grants full access as this client, so if it leaks,
// replace it right away: create a new secret and delete the compromised one
// (delete first if you're already at your secret limit; if it's your only
// secret, raise the limit before rotating).
```

</div>

### deleteResourceClientSecret
<div class="sdk-method-section">

      Permanently deletes a secret from a resource client. A client must always keep at least 1 secret - calling this on a client's last remaining secret throws an error.

        The resource the client must belong to (format: `res_...`).

        The client ID the secret belongs to (format: `m2m_...`).

        The secret ID to delete (format: `sks_...`).

        Nothing on success.

```java
scalekitClient.resources().deleteResourceClientSecret("res_xxx", "m2m_xxx", "sks_xxx");
```

</div>

### listUserConsents
<div class="sdk-method-section">

      Lists the end-user consents granted against a resource, with pagination. Use this to audit who authorized a client, and to find the `consentId` you need before revoking.

      Filter by user in one of two ways. Pass `userIds` to match external user IDs exactly and case-sensitively. Pass `search` for a case-insensitive substring match. When you give both, `userIds` wins and `search` is ignored.

        The resource whose consents to list (format: `res_...`).

        Built via `ListUserConsentsOptions.builder()`. Optional fields: `search`, `pageSize` (max 30), `pageToken`, `userIds` (max 25, takes precedence over `search`).

        Consents with `id`, `externalUserId`, `clientId`, `clientName`, `scopes`, and `grantedAt`, plus `totalSize` and the `nextPageToken` / `prevPageToken` cursors.

```java
ListResourceUserConsentsResponse consents = scalekitClient.resources().listUserConsents(
    "res_xxx",
    ListUserConsentsOptions.builder().pageSize(20).userIds(List.of("user_456")).build() // userIds optional; takes precedence over search
);

System.out.println(consents.getTotalSize() + " " + consents.getNextPageToken());
consents.getConsentsList().forEach(c ->
    System.out.println(c.getId() + " " + c.getExternalUserId() + " " + c.getClientId() + " " + c.getScopesList())
);
```

</div>

### revokeUserConsent
<div class="sdk-method-section">

      Revokes a single end-user consent held by a resource client. The client is prompted for consent again on its next authorization attempt, and every active refresh token issued to that client for the same user is revoked.

      Access tokens that Scalekit already issued stay valid until they expire. See [How revocation affects active access tokens](/authenticate/mcp/managing-mcp-clients/#how-revocation-affects-active-access-tokens) for ways to shorten that window.

        The resource client that holds the consent (format: `m2m_...`), not the resource ID.

        The consent to revoke (format: `usrcnst_...`), taken from `listUserConsents`.

        Empty response on success. The call throws on failure.

```java
scalekitClient.resources().revokeUserConsent("m2m_xxx", "usrcnst_789");
```

</div>

</div>


---

## More Scalekit documentation

| Resource | What it contains | When to use it |
|----------|-----------------|----------------|
| [/llms.txt](/llms.txt) | Structured index with routing hints per product area | Start here — find which documentation set covers your topic before loading full content |
| [/llms-full.txt](/llms-full.txt) | Complete documentation for all Scalekit products in one file | Use when you need exhaustive context across multiple products or when the topic spans several areas |
| [sitemap-0.xml](https://docs.scalekit.com/sitemap-0.xml) | Full URL list of every documentation page | Use to discover specific page URLs you can fetch for targeted, page-level answers |
