> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usestatemachines.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create the apps your task needs

> Choose apps, assign names, and configure a disposable environment.

Create an environment with the apps needed for one task. A string such as `'salesforce'` selects an app and also names that copy inside the environment.

These examples use `@usestatemachines/sdk` 0.6.0 or later. Install it with `npm install @usestatemachines/sdk`.

## Discover apps before selecting them

Call `sm.apps.list()` to read the available app IDs. Follow `nextCursor` if the response has another page. Call `sm.apps.versions(appId)` to read published versions, their APIs, and their actors.

For creation with `apps`, a new environment uses each app's default version. Version metadata includes `isDefault`, `status`, `apis`, and `actors`. The list includes retired versions. You can read versions, but you cannot choose one when you create an environment. An app without a default version fails creation with `version_unavailable`.

## Name repeated apps

Use an object when an app needs its own name or settings. Names identify copies within the environment and must be unique. Repeating the string `'salesforce'` twice produces two identical names and fails validation. An app name is 1 to 40 characters. It starts with a lowercase letter and contains only lowercase letters, digits, and hyphens. An environment holds at most 10 apps. Pass the app name to `connect()`, not the underlying app ID.

This example creates two Salesforce copies and connects to `partner-org`:

```ts theme={null}
import { StateMachines } from '@usestatemachines/sdk';

const sm = new StateMachines();
const env = await sm.environments.create(
  {
    name: 'CRM comparison',
    labels: { suite: 'integration' },
    apps: ['salesforce', { app: 'salesforce', name: 'partner-org' }],
  },
  { wait: false },
);

try {
  await sm.environments.wait(env.id, { status: 'running' });
  const partner = await env.connect('partner-org');
  const response = await partner.fetch('/services/data/v67.0/limits');

  if (!response.ok) {
    throw new Error(`Salesforce returned ${response.status}`);
  }

  console.log(response.status);
} finally {
  await env.delete();
}
```

The example accepts creation without waiting, then waits inside `try`. Accepting first keeps the environment ID in hand, so `finally` can delete the environment if waiting fails.

## Choose an actor

`connect()` uses the app's default actor unless you pass `actorId`. To connect as another actor:

1. Find the version the named app runs. The helper below reads the environment's `versionId` for that app and follows pagination through `sm.apps.versions(appId)`.
2. Read the returned `actors` and `apis`. `isDefault` describes the current default version, which can differ from the version the environment runs.
3. Pass the chosen actor's `id` to `env.connect(appName, { actorId })`.

```ts theme={null}
import type { StateMachines, Version } from '@usestatemachines/sdk';

export async function findRunningVersion(
  sm: StateMachines,
  environmentId: string,
  appName: string,
): Promise<Version> {
  const env = await sm.environments.get(environmentId);
  const app = env.apps.find((entry) => entry.name === appName);

  if (!app) {
    throw new Error(`Environment has no app named ${appName}`);
  }

  let cursor: string | undefined;

  do {
    const page = await sm.apps.versions(app.appId, { cursor });
    const version = page.data.find((entry) => entry.id === app.versionId);

    if (version) {
      return version;
    }

    cursor = page.nextCursor ?? undefined;
  } while (cursor !== undefined);

  throw new Error(`Version ${app.versionId} was not found`);
}
```

Reading versions does not switch the environment to another version. [Credentials and actors](/environments/credentials#actors) describes actor IDs and permissions.

## Set task metadata and settings

Use `name` for a readable task name and `labels` for string metadata such as a test suite or CI run ID.

Set `timeLimitMinutes` when the task needs a specific time limit. `sm.me().limits.timeLimitMinutes` returns the default and the maximum. [Pause, resume, and delete environments](/environments/lifecycle#deleting-ends-ownership) describes what the time limit counts.

State Machines passes app `settings` unchanged to the selected app. Salesforce accepts its documented setup and settings shape. Arbitrary Salesforce metadata is not an environment setting. A rejected value produces an `invalid_settings` startup failure. See [Salesforce](/apps/salesforce).

## Start with existing data

To reuse prepared data, supply `snapshotId` instead of `apps`. A snapshot determines the apps and saved data of the new environment. Follow [creating environments from snapshots](/snapshots/reuse).

## Optional create fields

Supply exactly one of `apps` and `snapshotId`. For `apps`, each entry is an app ID or `{ app, name?, settings? }`. The TypeScript type does not enforce that rule. The API checks it.

The remaining fields are optional:

| Field | Effect |
| - | - |
| `workspaceId` | Required for a member token unless the `StateMachines` constructor sets `workspaceId`. An API key uses its own workspace. Naming any other workspace with an API key returns `not_found`. |
| `name` | Human-readable environment name, or null. It is separate from app names. |
| `labels` | String-to-string metadata map. An absent map starts empty. |
| `timeLimitMinutes` | Time limit in minutes. If omitted, the environment uses the default from `sm.me().limits.timeLimitMinutes`. |
| `tokenInUrl` | When true, credentials include environment authentication in the app URL path. Defaults to false. |

There is no create field for a pinned version. The accepted environment's `apps` contain the actual `versionId`, version label, name, and settings for each copy. Store those values with a test result when you need to reproduce it. A snapshot keeps its source versions and settings for later copies.

Creation requires `environments:write`. Creating from a snapshot also requires `snapshots:read`. An archived workspace refuses new environments.

The concurrent limit counts every `starting` and `running` environment in the organization, across all workspaces. `sm.me().limits.concurrentEnvironments` returns it. Past the limit, creation fails with `environment_limit_reached`. Pause or delete an environment to free a place. Read existing environments and their creators before you decide what to delete.

## Update task metadata

`sm.environments.update(id, { name, labels })` changes metadata on an existing environment. Omit a field to leave it unchanged. Updating `labels` replaces the entire map, so include every label you want to keep. Use `name: null` to clear the name or `labels: {}` to clear labels. Deleted environments reject metadata updates.

This call cannot change app selection, settings, the time limit, or `tokenInUrl`. Create another environment when the task needs a different configuration. Names and labels are descriptive metadata, so keep credentials and customer secrets out of both.

For the complete field shapes and validation constraints, see the [endpoint reference](/sdk-api/endpoints). For accepted operations whose outcome is uncertain, use [idempotent recovery](/sdk-api/recovery).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.