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

# TypeScript SDK

> Install and use the NoClick TypeScript SDK in external apps and custom components

# TypeScript SDK

The TypeScript SDK connects external Node.js/browser applications to NoClick workflows via WebSocket (Socket.IO). It's also the SDK used inside [custom components](/sdk/custom-components).

## Installation

```bash theme={null}
npm install noclick socket.io-client
```

`socket.io-client` is required for external apps (WebSocket transport). Custom components don't need it, since the SDK uses postMessage in iframe context.

## Quick Start

```typescript theme={null}
import { init, nodes, execution, state } from 'noclick';

// Connect with your API key
await init({
  apiKey: 'nk_live_...',
  workflowId: '...',  // optional — scope to a specific workflow
});

// Read a node's output
const output = await nodes.getOutput('gmail-node-id');

// Run a node and get results
const results = await execution.runNodesAndGetOutput(
  ['data-fetcher'],
  ['data-fetcher']
).all();

// Persistent state
await state.set('counter', 42);
const val = await state.get('counter'); // 42
```

## Authentication

External apps use API keys created in **Settings > Developer**. Keys have three permission levels:

| Permission | Allows                                                                                                                                     |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `read`     | `nodes.getOutput`, `nodes.list`, `nodes.getConfig`, `state.get`, `state.keys`, `dataset.getRows`, `resources.list`, `auth.listCredentials` |
| `execute`  | `execution.runNodesAndGetOutput`, `execution.runNodesInBackground`, `execution.stop`                                                       |
| `write`    | `nodes.setConfig`, `state.set`, `state.del`, `dataset.appendRows`, `dataset.create`, `resources.upload`, `auth.createCredential`           |

## Reading Data

```typescript theme={null}
// List all nodes in the workflow
const allNodes = await nodes.list();
// [{ id, type, label, hasOutput }, ...]

// Read a node's last output
const output = await nodes.getOutput('gmail-node-id');
console.log(output.emails);

// Read a node's config
const config = await nodes.getConfig('http-node-id');
console.log(config.url);
```

## Running Nodes

```typescript theme={null}
// Fire and forget
execution.runNodesInBackground(['data-fetcher']);

// With temporary config overrides (doesn't persist to the node)
execution.runNodesInBackground([
  { id: 'http-node', config: { url: 'https://api.example.com/data' } }
]);

// Run and stream output as nodes complete
const stream = execution.runNodesAndGetOutput(
  ['data-fetcher'],        // nodes to execute
  ['formatter', 'chart']   // nodes whose output we want
);

stream.on('output', (nodeId, data) => {
  console.log(`${nodeId} completed:`, data);
});

stream.on('done', () => {
  console.log('All targets completed');
});

// Or await all results at once
const results = await stream.all();
// { 'formatter': {...}, 'chart': {...} }
```

## State Management

Requires a **State Manager** node in the workflow.

```typescript theme={null}
await state.set('counter', 0);
const val = await state.get('counter');  // 0

await state.update('counter', n => (n || 0) + 1);
await state.del('counter');

const keys = await state.keys();  // ['counter', ...]

// Subscribe to changes (fires from any source — other clients, workflow execution)
state.onChange('counter', (newVal) => {
  console.log('Counter changed:', newVal);
});
```

## Real-time Subscriptions

Subscribe to node changes from any trigger (cron, webhooks, manual runs, other SDK clients):

```typescript theme={null}
// React to new data from a cron-triggered node
execution.onNodeOutput('data-fetcher', (output) => {
  console.log('New data:', output);
});

// Track execution state
execution.onNodeState('data-fetcher', (state) => {
  console.log('State:', state);  // 'running' | 'completed' | 'error'
});
```

## Credentials

```typescript theme={null}
// List all credentials
const creds = await auth.listCredentials();
// [{ id, type, name }, ...]

// Check if a credential type exists
const has = await auth.hasCredential('google_gmail_oauth'); // true/false

// Create an API key credential
const cred = await auth.createCredential('telegram_bot_token', {
  token: apiKey
}, 'My Bot');
// { id, type, name }
```

<Note>
  `auth.requestCredential()` (OAuth popup flow) is only available in custom components, not external apps.
</Note>

## Resources (File Storage)

```typescript theme={null}
// Upload a file
const { resourceId, uploadUrl } = await resources.upload('photo.jpg', 'image/jpeg', file.size);
await fetch(uploadUrl, { method: 'PUT', body: file });

// Get download URL
const url = await resources.getUrl(resourceId);

// List resources
const files = await resources.list('file');

// Delete
await resources.remove(resourceId);
```

## Dataset CRUD

```typescript theme={null}
// Create
const dsId = await dataset.create('User Submissions');

// Append rows
await dataset.appendRows(dsId, [
  { name: 'Alice', score: 95 },
  { name: 'Bob', score: 87 },
]);

// Read (paginated)
const page = await dataset.getRows(dsId, { limit: 100, offset: 0 });
// { rows: [{ id, data, created_at }], totalCount: 1000 }

// Update a row
await dataset.updateRow(dsId, rowId, { score: 98 });

// Delete rows
await dataset.deleteRows(dsId, [rowId]);

// List all datasets
const datasets = await dataset.list();
```

## Workflow Info

```typescript theme={null}
const info = await workflow.getInfo();
// { id, name, nodeCount }
```

## Full API Reference

See the [API Reference](/sdk/api-reference) for complete method signatures with TypeScript and Python examples side by side.
