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

# Conduct a study

> Runnable TypeScript example: conduct a study, with inputs, output, source code, and execution limits.

[Raw TypeScript source](https://raw.githubusercontent.com/user-intuition/examples/main/examples/conduct-a-study/index.ts) · [Workflow walkthrough](https://github.com/user-intuition/examples/tree/main/examples/conduct-a-study) · [All examples](/api-reference/developer-examples)

## Run locally

Requires Node.js 22.18 or later. Run from the cloned repository; this file imports the shared client and contracts in `src/`. The default uses fictional local fixtures with no credentials, network calls, invitations, or spending.

```sh theme={null}
git clone https://github.com/user-intuition/examples.git
cd examples
npm run study
```

For live calls, supply `USERINTUITION_API_KEY` securely in the environment and explicitly select `--live`. Follow the walkthrough for action-specific arguments and approvals. The B2/C1 adapters are illustrative pending [release verification](https://github.com/user-intuition/examples/blob/main/docs/release-check.md). Passing fixture checks does not establish live contract compatibility.

## Inputs

Fixture mode needs no inputs. Live actions use --action, a study ID after creation, and the action-specific name, message, or request file described in the walkthrough.

## Expected output

Fictional plan/fieldwork walkthrough labeled with fixture mode. Live mode prints the selected operation response.

## Side effects

Fixture mode has no side effects. Live create/customize/launch/report-generation actions write data; launch starts paid recruitment and requires a separate approval flag.

## Failures

Missing study/action inputs, malformed request JSON, a missing launch approval, and HTTP failures stop the operation. An interrupted write may have succeeded; inspect saved state before retrying.

## Complete source file

The following is generated from `examples/conduct-a-study/index.ts`, not maintained as a separate snippet. SHA-256: `f8cd9f0bfeb836b06c65f54595edd330775f835fbe825a50afb07a27e927f990`.

```typescript theme={null}
import { readFile } from "node:fs/promises";
import { args, required, assertLaunchApproval } from "../../src/args.ts";
import { ResearchClient, fixture, output } from "../../src/client.ts";
import { routes } from "../../src/contracts.ts";
const options = args();
if (!options.live) {
  output({
    mode: "fictional fixture; no network, invitations, or spend",
    workflow: await fixture("workflow"),
  });
} else {
  const client = new ResearchClient();
  if (options.action === "create") {
    const name = required(options.name, "name");
    if (name.length > 40)
      throw new Error("Study name must be at most 40 characters.");
    output(
      await client.request("POST", routes.studies, {
        name,
        study_type: "in-depth-interview",
        recruiting_method: "panel",
      }),
    );
  } else {
    const id = required(options.study, "study");
    if (options.action === "customize")
      output(
        await client.request("POST", routes.customize(id), {
          message: required(options.message, "message"),
        }),
      );
    else if (options.action === "review")
      output(await client.request("GET", routes.study(id)));
    else if (options.action === "estimate" || options.action === "launch") {
      const request = JSON.parse(
        await readFile(
          required(options["request-file"], "request-file"),
          "utf8",
        ),
      );
      if (options.action === "launch")
        assertLaunchApproval(id, options["approve-launch"]);
      const study = await client.request<{ provisioning_status: string }>(
        "GET",
        routes.study(id),
      );
      if (study.provisioning_status !== "provisioned")
        throw new Error(
          "Study interviewer is not provisioned. Complete and review the saved plan. This is not a fielding-status check.",
        );
      output(
        await client.request("POST", routes.panel(id), {
          ...request,
          dry_run: options.action === "estimate",
        }),
      );
    } else if (options.action === "monitor") {
      output(
        await client.request(
          "GET",
          `${routes.interviews}?study_id=${encodeURIComponent(id)}&status=completed&page=1&page_size=20`,
        ),
      );
    } else if (options.action === "generate-report")
      output(await client.request("POST", routes.report(id)));
    else
      throw new Error(
        "Choose create, customize, review, estimate, launch, monitor, or generate-report.",
      );
  }
}
```
