> ## 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.

# API Reference

> Complete reference for the @noclick/sdk API

# API Reference

All methods are available in both TypeScript and Python SDKs. TypeScript uses camelCase, Python uses snake\_case.

***

<h2 id="nodes">
  Nodes
</h2>

Read and write node data in the workflow.

### `nodes.getOutput(nodeId)`

Read a node's last execution output. Returns whatever exists, which may be from a cron run, manual execution, or webhook trigger.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const output = await nodes.getOutput('node-id');
  // Returns the node's output object, or null if never executed
  ```

  ```python Python theme={null}
  output = await sdk.nodes.get_output("node-id")
  ```
</CodeGroup>

### `nodes.getConfig(nodeId)`

Read a node's configuration fields.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const config = await nodes.getConfig('node-id');
  // { action: 'read', max_results: 20, ... }
  ```

  ```python Python theme={null}
  config = await sdk.nodes.get_config("node-id")
  ```
</CodeGroup>

### `nodes.setConfig(nodeId, config)`

Set configuration fields on a node (merges with existing config).

<CodeGroup>
  ```typescript TypeScript theme={null}
  await nodes.setConfig('http-node', { url: 'https://api.example.com' });
  ```

  ```python Python theme={null}
  # Not yet supported in Python SDK (use nodes directly in workflow)
  ```
</CodeGroup>

### `nodes.list()`

List all nodes in the workflow.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const allNodes = await nodes.list();
  // [{ id, type, label, hasOutput }, ...]
  ```

  ```python Python theme={null}
  nodes = await sdk.nodes.list()
  # [{"id": "...", "type": "...", "label": "...", "hasOutput": False}, ...]
  ```
</CodeGroup>

***

<h2 id="execution">
  Execution
</h2>

Run nodes and track results.

### `execution.runNodesAndGetOutput(runNodes, targetNodes)`

Run nodes and stream output as each target node completes.

**Parameters:**

* `runNodes`: nodes to execute. String IDs or `{ id, config }` objects with temporary config overrides.
* `targetNodes`: node IDs whose output to collect.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const stream = execution.runNodesAndGetOutput(
    ['data-fetcher'],           // run these
    ['chart-data', 'summary']   // collect output from these
  );

  stream.on('output', (nodeId, data) => { /* fires per target */ });
  stream.on('error', (nodeId, error) => { /* target errored */ });
  stream.on('done', () => { /* all targets completed */ });

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

  ```python Python theme={null}
  results = await sdk.execution.run_nodes_and_get_output(
      ["data-fetcher"],
      ["chart-data", "summary"],
      timeout=30,
  )
  # {"chart-data": {...}, "summary": {...}}
  ```
</CodeGroup>

With config overrides (temporary, doesn't save):

```typescript theme={null}
execution.runNodesAndGetOutput(
  [{ id: 'http-node', config: { url: userInput } }],
  ['http-node']
);
```

### `execution.runNodesInBackground(runNodes)`

Fire-and-forget execution. Returns immediately.

<CodeGroup>
  ```typescript TypeScript theme={null}
  execution.runNodesInBackground(['data-fetcher']);

  // With config overrides
  execution.runNodesInBackground([
    { id: 'llm-node', config: { prompt: 'Summarize Q4 report' } }
  ]);
  ```

  ```python Python theme={null}
  await sdk.execution.run_nodes_in_background(["data-fetcher"])
  ```
</CodeGroup>

### `execution.stop()`

Stop the current workflow execution.

### `execution.onNodeState(nodeId, handler)`

Subscribe to a node's execution state changes. Works for any trigger (manual, cron, webhook).

```typescript theme={null}
const unsub = execution.onNodeState('node-id', (state) => {
  // 'idle' | 'running' | 'completed' | 'error'
});
unsub(); // unsubscribe
```

### `execution.onNodeOutput(nodeId, handler)`

Subscribe to a node's output in real-time. Fires whenever the node produces output, from any source.

```typescript theme={null}
const unsub = execution.onNodeOutput('data-node', (output) => {
  console.log('New data:', output);
});
```

***

<h2 id="state">
  State
</h2>

Persistent key-value storage via state-manager nodes. Requires a **State Manager** node in the workflow.

### `state.get(key)`

<CodeGroup>
  ```typescript TypeScript theme={null}
  const val = await state.get('counter');
  // Returns the value, or undefined if not set
  ```

  ```python Python theme={null}
  val = await sdk.state.get("counter")
  ```
</CodeGroup>

### `state.set(key, value)`

<CodeGroup>
  ```typescript TypeScript theme={null}
  await state.set('counter', 42);
  await state.set('user', { name: 'Alice', score: 95 });
  ```

  ```python Python theme={null}
  await sdk.state.set("counter", 42)
  ```
</CodeGroup>

### `state.del(key)`

Remove a key entirely.

<CodeGroup>
  ```typescript TypeScript theme={null}
  await state.del('counter');
  ```

  ```python Python theme={null}
  await sdk.state.delete("counter")
  ```
</CodeGroup>

### `state.update(key, updater)`

Read-modify-write with an updater function. The function runs locally (not on the server).

```typescript theme={null}
await state.update('counter', n => (n || 0) + 1);
await state.update('items', list => [...(list || []), newItem]);
await state.update('profile', p => ({ ...p, lastSeen: Date.now() }));
```

### `state.keys()`

List all available state keys.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const keys = await state.keys(); // ['counter', 'items', ...]
  ```

  ```python Python theme={null}
  keys = await sdk.state.keys()
  ```
</CodeGroup>

### `state.onChange(key, handler)`

Subscribe to state changes. Fires on `set` and `del` from any source.

```typescript theme={null}
state.onChange('counter', (newVal) => {
  console.log('Counter changed:', newVal);
});
```

***

<h2 id="auth">
  Auth
</h2>

Credential management and OAuth flows.

### `auth.listCredentials()`

List all available credentials for the user.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const creds = await auth.listCredentials();
  // [{ id, type, name }, ...]
  ```

  ```python Python theme={null}
  creds = await sdk.auth.list_credentials()
  ```
</CodeGroup>

### `auth.hasCredential(credentialType)`

Check if a credential of the given type exists.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const has = await auth.hasCredential('google_gmail_oauth'); // true/false
  ```

  ```python Python theme={null}
  has = await sdk.auth.has_credential("google_gmail_oauth")
  ```
</CodeGroup>

### `auth.requestCredential(credentialType)`

<Note>
  Only available in custom components (iframe context). Opens the NoClick OAuth popup.
</Note>

Trigger an OAuth flow. Scopes are automatically resolved from node schemas.

```typescript theme={null}
const cred = await auth.requestCredential('google_gmail_oauth');
// { id, type, name } or null if user cancelled
```

### `auth.createCredential(credentialType, data, name?)`

Create a non-OAuth credential (API key, token, etc). The component provides its own input UI.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const cred = await auth.createCredential('telegram_bot_token', {
    token: apiKey
  }, 'My Bot');
  ```

  ```python Python theme={null}
  cred = await sdk.auth.create_credential("telegram_bot_token", {"token": key}, "My Bot")
  ```
</CodeGroup>

***

<h2 id="resources">
  Resources
</h2>

File and blob storage via R2. Scoped to the workflow.

### `resources.upload(name, mimeType, sizeBytes)`

Create a resource and get a presigned upload URL. PUT the file body to the URL.

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

  ```python Python theme={null}
  result = await sdk.resources.upload("photo.jpg", "image/jpeg", file_size)
  # PUT file to result["upload_url"]
  ```
</CodeGroup>

### `resources.getUrl(resourceId)`

Get a presigned download URL.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const url = await resources.getUrl(resourceId);
  ```

  ```python Python theme={null}
  url = await sdk.resources.get_url(resource_id)
  ```
</CodeGroup>

### `resources.list(resourceType?)`

List resources in the workflow. Optionally filter by type (`'file'`, `'image'`, `'dataset'`, etc).

### `resources.remove(resourceId)`

Delete a resource and its stored data.

***

<h2 id="dataset">
  Dataset
</h2>

Tabular data with CRUD operations. Backed by PostgreSQL, scalable for large datasets. Rows are schemaless JSONB.

### `dataset.create(name)`

Create a new dataset. Returns the resource ID.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const resourceId = await dataset.create('User Submissions');
  ```

  ```python Python theme={null}
  resource_id = await sdk.dataset.create("User Submissions")
  ```
</CodeGroup>

### `dataset.list()`

List all datasets in the workflow.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const datasets = await dataset.list();
  // [{ id, name, rowCount }, ...]
  ```

  ```python Python theme={null}
  datasets = await sdk.dataset.list()
  ```
</CodeGroup>

### `dataset.appendRows(resourceId, rows)`

Append rows to a dataset. Each row is an arbitrary JSON object.

<CodeGroup>
  ```typescript TypeScript theme={null}
  await dataset.appendRows(resourceId, [
    { name: 'Alice', score: 95 },
    { name: 'Bob', score: 87 },
  ]);
  ```

  ```python Python theme={null}
  count = await sdk.dataset.append_rows(resource_id, [
      {"name": "Alice", "score": 95},
      {"name": "Bob", "score": 87},
  ])
  ```
</CodeGroup>

### `dataset.getRows(resourceId, options?)`

Get paginated rows from a dataset.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const page = await dataset.getRows(resourceId, { limit: 100, offset: 0 });
  // { rows: [{ id, data, created_at, updated_at }], totalCount: 1000 }
  ```

  ```python Python theme={null}
  page = await sdk.dataset.get_rows(resource_id, limit=100, offset=0)
  # {"rows": [...], "total_count": 1000}
  ```
</CodeGroup>

### `dataset.updateRow(resourceId, rowId, data)`

Update a single row's data.

### `dataset.deleteRows(resourceId, rowIds)`

Delete rows by their UUIDs.

***

<h2 id="workflow">
  Workflow
</h2>

### `workflow.getInfo()`

Get information about the current workflow.

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

  ```python Python theme={null}
  info = await sdk.workflow.get_info()
  ```
</CodeGroup>

### `workflow.nodeId` (TypeScript only)

The current component's node ID. Set automatically in iframe context.

```typescript theme={null}
import { workflow } from '@noclick/sdk';
console.log(workflow.nodeId);
```
