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

# Quickstart with TypeScript SDK

> Install the TypeScript client, authenticate with an API key, validate an example agreement, and prove EIP-712 signing readiness without deploying.

For the complete documentation index, see [llms.txt](https://docs.shodai.network/llms.txt).

Use this quickstart when you are building a TypeScript app or service with Shodai Agreements. You will authenticate with the Agreements API, validate a complete agreement JSON artifact, preflight deployment values, and sign the deploy permit locally without creating a live agreement.

For the agent/MCP path, use [Quickstart with MCP](/sdks/quickstart-with-mcp). To compare the two first-run paths, start with [Choose an integration surface](/integration-surfaces).

<Note>
  This quickstart stops before `deployWithPermit(...)`. The final signing step proves that your app can reach API auth, validation, deployment preflight, chain/RPC context, and EIP-712 signing without performing a live write.
</Note>

## Prerequisites

* Node.js `>=18`.
* A Shodai testnet API key. Create one in the [Developer Portal](https://developers.shodai.network/portal).
* A Linea Sepolia RPC URL for `chainId: 59141`.
* A local package or temporary directory where you can install npm packages.

## Make the first-flight script

<Steps>
  <Step title="Create an ESM project">
    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    mkdir shodai-sdk-quickstart
    cd shodai-sdk-quickstart
    npm init -y
    npm pkg set type=module
    ```
  </Step>

  <Step title="Install the SDK and signing dependencies">
    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    npm install @cns-labs/agreements-api-client viem
    npm install --save-dev tsx
    ```

    The API client handles typed Agreements API calls. `viem` provides the test wallet, public client, and EIP-712 signing primitives used by the SDK signing helpers.
  </Step>

  <Step title="Set environment variables">
    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    export API_KEY="api_key_replace_me"
    export RPC_URL="linea_sepolia_rpc_url_replace_me"
    ```

    `RPC_URL` must point at Linea Sepolia because this quickstart uses `chainId: 59141`.
  </Step>

  <Step title="Save the example agreement">
    Copy the complete `simple-agreement.json` code block from [Simple Agreement](/examples/simple) into:

    ```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
    simple-agreement.json
    ```

    Use the complete JSON artifact from the example page, not an abbreviated API request body.
  </Step>

  <Step title="Create the quickstart script">
    Create `quickstart.ts`:

    ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
    import { readFile } from 'node:fs/promises';
    import {
      ApiClient,
      computeDefaultDeadlineSeconds,
      signDeployWithPermit,
    } from '@cns-labs/agreements-api-client';
    import { createPublicClient, createWalletClient, http } from 'viem';
    import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
    import { lineaSepolia } from 'viem/chains';

    const apiKey = process.env.API_KEY;
    const rpcUrl = process.env.RPC_URL;

    if (!apiKey) throw new Error('Set API_KEY before running this script.');
    if (!rpcUrl) throw new Error('Set RPC_URL to a Linea Sepolia RPC endpoint.');

    const chainId = 59141;
    const client = new ApiClient({
      environment: 'testnet',
      apiKey,
    });

    const health = await client.getHealth();
    console.log('Health:', health.status, health.service);

    const agreementsPage = await client.listAgreements({ limit: 25 });
    console.log('Visible agreements:', agreementsPage.data.length);

    const agreement = JSON.parse(
      await readFile(new URL('./simple-agreement.json', import.meta.url), 'utf8'),
    );

    const templateValidation = await client.validateTemplate(agreement);
    console.log('Template validation:', {
      participantVariableKeys: templateValidation.participantVariableKeys,
      inputIds: templateValidation.inputIds,
      stateIds: templateValidation.stateIds,
      warnings: templateValidation.warnings,
    });

    const partyA = privateKeyToAccount(generatePrivateKey());
    const partyB = privateKeyToAccount(generatePrivateKey());

    const publicClient = createPublicClient({
      chain: lineaSepolia,
      transport: http(rpcUrl),
    });

    const walletClient = createWalletClient({
      account: partyA,
      chain: lineaSepolia,
      transport: http(rpcUrl),
    });

    const participants = [
      {
        variableKey: 'partyAEthAddress',
        walletAddress: partyA.address,
      },
      {
        variableKey: 'partyBEthAddress',
        walletAddress: partyB.address,
      },
    ];

    const deploymentValidation = await client.validateDeployment({
      agreement,
      chainId,
      participants,
    });

    console.log('Deployment preflight:', {
      variables: deploymentValidation.variables,
      participants: deploymentValidation.participants,
      observers: deploymentValidation.observers,
      contributors: deploymentValidation.contributors,
      warnings: deploymentValidation.warnings,
    });

    type ProtocolInitValue = string | bigint | boolean | `0x${string}`;
    const deploymentInitValues = deploymentValidation.variables as Record<string, ProtocolInitValue>;

    const deadline = computeDefaultDeadlineSeconds();
    const deployPermit = await signDeployWithPermit({
      walletClient,
      publicClient,
      chainId,
      agreement,
      deadline,
      permitOptions: {
        initValues: deploymentInitValues,
      },
    });

    console.log('Deploy permit signed but not submitted:', {
      signerAddress: deployPermit.signerAddress,
      deadline: deployPermit.deadline,
      signatureV: deployPermit.signature.v,
      signatureR: deployPermit.signature.r,
      signatureS: deployPermit.signature.s,
    });
    ```
  </Step>

  <Step title="Run the script">
    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    npx tsx quickstart.ts
    ```

    The generated Party A wallet signs the deploy permit. The generated wallets are for this local test only; do not persist generated private keys or use this pattern for production custody.
  </Step>
</Steps>

## Completion state

You have completed this quickstart when the script prints:

* a healthy API response
* an authenticated agreement list response
* template validation output for the Simple Agreement
* deployment preflight output
* `signatureV`, `signatureR`, and `signatureS` from `signDeployWithPermit(...)`

At that point your TypeScript integration has reached API auth, agreement validation, deployment preflight, chain/RPC context, and EIP-712 signing readiness. It has not deployed an agreement.

## Continue from here

<CardGroup cols={2}>
  <Card title="Run an end-to-end agreement workflow" icon="route" href="/examples/end-to-end-workflow">
    Continue from first-flight readiness to deployment, signed input submission, state reads, and input history.
  </Card>

  <Card title="TypeScript client reference" icon="code" href="/sdks/typescript-client">
    Reference constructor options, methods, signing helpers, diagnostics, path helpers, and exports.
  </Card>

  <Card title="Author agreement JSON" icon="file-code" href="/workflow/author-agreement-json">
    Model terms, variables, participants, states, inputs, issuers, and transitions.
  </Card>

  <Card title="Deploy an agreement" icon="rocket" href="/workflow/deploy-an-agreement">
    Learn the live deployment workflow after preflight and signing readiness are working.
  </Card>
</CardGroup>
