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

# Twins

> Find a twin's URL and token, call it like the real service, and read back what happened.

A twin is a running copy of one service. It has a `base_url`, a `token`, and a
`status`. Worldsmith creates one per service in a world; you can also create a
standalone twin.

Use a [configured SDK client](/api-reference/languages/overview#use-the-guide-examples).
The examples share that client and the IDs returned by earlier steps.

## Methods

| Method                                         | HTTP                              | Does                               |
| ---------------------------------------------- | --------------------------------- | ---------------------------------- |
| `twins.models()`                               | `GET /v1/twins/models`            | List supported services.           |
| `twins.list()`                                 | `GET /v1/twins`                   | List your twins.                   |
| `twins.create(service, ...)`                   | `POST /v1/twins`                  | Create a standalone twin.          |
| `twins.retrieve(twin_id)`                      | `GET /v1/twins/{twinId}`          | Read one twin.                     |
| `twins.wait(twin_id)`                          | polls the above                   | Block until `running` or `failed`. |
| `twins.reset(twin_id)`                         | `POST /v1/twins/{twinId}/reset`   | Restore the original seed.         |
| `twins.stop(twin_id)`                          | `DELETE /v1/twins/{twinId}`       | Stop it.                           |
| `twins.activity.list(twin_id)`                 | `GET /v1/twins/{twinId}/activity` | Request history.                   |
| `twins.activity.retrieve(twin_id, request_id)` | `GET .../activity/{requestId}`    | One request in full.               |

## Connect an agent to a twin

Use the world ID returned by [Worlds](/api-reference/worldsmith). Read the world,
pick the twin, and configure your existing vendor client with its URL and token.
The vendor paths are unchanged: Slack under `/api/`, Linear at `/graphql`,
Salesforce under `/services/data/`, SAP under `/sap/opu/odata/`.

<CodeGroup dropdown>
  ```python Python theme={null}
  world = client.worlds.retrieve(world_id)
  slack = next((t for t in world.twins if t.service == "slack"), None)
  if slack is None or slack.status != "running":
      raise RuntimeError("This world has no running Slack twin")
  slack_base_url, slack_token = slack.base_url, slack.token
  ```

  ```typescript TypeScript theme={null}
  const world = await client.worlds.retrieve(worldId);
  const slack = world.twins.find((t) => t.service === "slack");
  if (!slack || slack.status !== "running") throw new Error("This world has no running Slack twin");
  const slackBaseUrl = slack.baseUrl;
  const slackToken = slack.token;
  ```

  ```javascript JavaScript theme={null}
  const world = await client.worlds.retrieve(worldId);
  const slack = world.twins.find((t) => t.service === "slack");
  if (!slack || slack.status !== "running") throw new Error("This world has no running Slack twin");
  const slackBaseUrl = slack.baseUrl;
  const slackToken = slack.token;
  ```

  ```go Go theme={null}
  world, err := client.Worlds.Retrieve(ctx, worldID)
  if err != nil { return err }
  var slack *chronicle.Twin
  for i := range world.Twins {
      if world.Twins[i].Service == "slack" { slack = &world.Twins[i]; break }
  }
  if slack == nil || slack.Status != "running" { return fmt.Errorf("this world has no running Slack twin") }
  // Supply slack.BaseURL and slack.Token to your existing Slack client configuration.
  ```

  ```rust Rust theme={null}
  let world = client.worlds().retrieve(&world_id).await?;
  let slack = world.twins.iter().find(|t| t.service == "slack" && t.status == "running")
      .ok_or("This world has no running Slack twin")?;
  let slack_base_url = &slack.base_url;
  let slack_token = &slack.token;
  ```
</CodeGroup>

Set your Slack client's API base URL to `slack_base_url` and its credential to
`slack_token` (using your language's variable naming). Other services work the
same way: change the endpoint and credential in the existing tool integration,
then use IDs returned by that twin. Do not log the token.

[Explore the connected service records](/product-explorer#connected-records)
to see the issue, order, case, and channel your tools will work with.

<Note>
  In an evaluation you don't do this yourself. Chronicle creates fresh twins
  for each trial and passes their URLs and tokens to your agent as environment
  variables. See [Connect your agent](/platform/agents).
</Note>

## Read what happened

Every request to a twin is recorded. The list gives you a summary per request;
the detail gives you the full request, response, and every record that changed
with before and after values.

<CodeGroup dropdown>
  ```python Python theme={null}
  page = client.twins.activity.list(slack.id, limit=50)
  for a in page.items:
      print(a.request_id, a.request.method, a.request.path, a.response.status)

  if not page.items:
      raise RuntimeError("No requests yet; call the twin through your agent first")
  detail = client.twins.activity.retrieve(slack.id, page.items[0].request_id)
  for m in detail.mutations:
      print(m.collection, m.operation, m.before, m.after)
  ```

  ```typescript TypeScript theme={null}
  const page = await client.twins.activity.list(slack.id, { limit: 50 });
  for (const a of page.items) console.log(a.requestId, a.request.method, a.request.path, a.response.status);

  if (!page.items.length) throw new Error("No requests yet; call the twin through your agent first");
  const detail = await client.twins.activity.retrieve(slack.id, page.items[0].requestId);
  for (const m of detail.mutations) console.log(m.collection, m.operation, m.before, m.after);
  ```

  ```javascript JavaScript theme={null}
  const page = await client.twins.activity.list(slack.id, { limit: 50 });
  for (const a of page.items) console.log(a.requestId, a.request.method, a.request.path, a.response.status);

  if (!page.items.length) throw new Error("No requests yet; call the twin through your agent first");
  const detail = await client.twins.activity.retrieve(slack.id, page.items[0].requestId);
  for (const m of detail.mutations) console.log(m.collection, m.operation, m.before, m.after);
  ```

  ```go Go theme={null}
  page, err := client.Twins.Activity.List(ctx, slack.ID, chronicle.PageParams{Limit: 50})
  if err != nil {
      return err
  }
  for _, a := range page.Items {
      fmt.Println(a.RequestID, a.Request.Method, a.Request.Path, a.Response.Status)
  }

  if len(page.Items) == 0 { return fmt.Errorf("no requests yet; call the twin through your agent first") }
  detail, err := client.Twins.Activity.Retrieve(ctx, slack.ID, page.Items[0].RequestID)
  if err != nil {
      return err
  }
  for _, m := range detail.Mutations {
      fmt.Println(m.Collection, m.Operation, m.Before, m.After)
  }
  ```

  ```rust Rust theme={null}
  let page = client.twins().activity().list(&slack.id, PageParams { limit: Some(50), ..Default::default() }).await?;
  for a in &page.items {
      println!("{} {} {} {}", a.request_id, a.request.method, a.request.path, a.response.status);
  }

  let request = page.items.first().ok_or("No requests yet; call the twin through your agent first")?;
  let detail = client.twins().activity().retrieve(&slack.id, &request.request_id).await?;
  for m in &detail.mutations {
      println!("{} {} {:?} {:?}", m.collection, m.operation, m.before, m.after);
  }
  ```
</CodeGroup>

Very large payloads are truncated and flagged with `truncated: true`. For all
twins in a world at once, use `worlds.activity.list(world_id)`.

<Frame caption="A recorded Linear POST /graphql request changed one issue. Open that change to inspect the affected fields." className="product-capture">
  <img src="https://mintcdn.com/chroniclelabs-0d363efc/BRuFTKj-kt84RfoY/images/product/worldsmith-request-focused.png?fit=max&auto=format&n=BRuFTKj-kt84RfoY&q=85&s=4eef06cc5e88c5bfce300fdeeac60d64" alt="Linear twin request drawer showing POST /graphql and one update to linear.issues" style={{maxWidth: "540px", margin: "0 auto"}} width="539" height="1130" data-path="images/product/worldsmith-request-focused.png" />
</Frame>

[Open the request image](/images/product/worldsmith-request-focused.png).

## Reset or stop

Choose one action: reset restores the seed; stop ends the runtime.

<CodeGroup dropdown>
  ```python Python theme={null}
  client.twins.reset(slack.id)   # back to the original seed
  # Or shut it down: client.twins.stop(slack.id)
  ```

  ```typescript TypeScript theme={null}
  await client.twins.reset(slack.id);  // back to the original seed
  // Or shut it down: await client.twins.stop(slack.id);
  ```

  ```javascript JavaScript theme={null}
  await client.twins.reset(slack.id);  // back to the original seed
  // Or shut it down: await client.twins.stop(slack.id);
  ```

  ```go Go theme={null}
  _, err = client.Twins.Reset(ctx, slack.ID) // back to the original seed
  if err != nil { return err }
  // To stop instead, call client.Twins.Stop(ctx, slack.ID) and check its error.
  ```

  ```rust Rust theme={null}
  client.twins().reset(&slack.id).await?; // back to the original seed
  // Or shut it down: client.twins().stop(&slack.id).await?;
  ```
</CodeGroup>

After a reset the twin goes through `provisioning` again; `twins.wait` blocks
until it's `running`, then re-read `base_url` before reconnecting. Twins also
stop on their own when `ttl_hours` runs out.

## Create a standalone twin

For one service on its own, including Gmail, Google Sheets, or ModMed, which
Worldsmith doesn't author:

<CodeGroup dropdown>
  ```python Python theme={null}
  twin = client.twins.create(service="slack", ttl_hours=24)
  twin = client.twins.wait(twin.id, timeout=180)
  if twin.status != "running":
      raise RuntimeError(f"Twin {twin.id}: {twin.status}; {twin.error}")
  print(twin.id, twin.status)
  ```

  ```typescript TypeScript theme={null}
  let twin = await client.twins.create({ service: "slack", ttlHours: 24 });
  twin = await client.twins.wait(twin.id, { timeout: 180_000 });
  if (twin.status !== "running") throw new Error(`Twin ${twin.id}: ${twin.status}; ${twin.error}`);
  console.log(twin.id, twin.status);
  ```

  ```javascript JavaScript theme={null}
  let twin = await client.twins.create({ service: "slack", ttlHours: 24 });
  twin = await client.twins.wait(twin.id, { timeout: 180_000 });
  if (twin.status !== "running") throw new Error(`Twin ${twin.id}: ${twin.status}; ${twin.error}`);
  console.log(twin.id, twin.status);
  ```

  ```go Go theme={null}
  twin, err := client.Twins.Create(ctx, chronicle.TwinCreateParams{Service: "slack", TTLHours: 24})
  if err != nil {
      return err
  }
  twin, err = client.Twins.Wait(ctx, twin.ID)
  if err != nil {
      return err
  }
  if twin.Status != "running" { return fmt.Errorf("twin %s: %s; %v", twin.ID, twin.Status, twin.Error) }
  fmt.Println(twin.ID, twin.Status)
  ```

  ```rust Rust theme={null}
  let twin = client.twins().create(TwinCreateParams {
      service: "slack".into(),
      ttl_hours: Some(24),
      ..Default::default()
  }).await?;
  let twin = client.twins().wait(&twin.id, Duration::from_secs(180)).await?;
  if twin.status != "running" { return Err(format!("twin {}: {}; {:?}", twin.id, twin.status, twin.error).into()); }
  println!("{} {}", twin.id, twin.status);
  ```
</CodeGroup>

| Parameter                | Required | Meaning                                                                          |
| ------------------------ | -------- | -------------------------------------------------------------------------------- |
| `service`                | Yes      | A service ID from `twins.models()`.                                              |
| `ttl_hours`              | No       | Lifetime, 1 to 720. Default 72.                                                  |
| `environment_version_id` | No       | Seed it from a published environment version instead of the default sample data. |

## Twin status

| Status         | Meaning                             |
| -------------- | ----------------------------------- |
| `provisioning` | Starting up.                        |
| `running`      | Ready.                              |
| `degraded`     | Failed its last health check.       |
| `stopped`      | Stopped on request.                 |
| `expired`      | Stopped because its lifetime ended. |
| `failed`       | Never became usable. See `error`.   |
