The official Node.js SDK for the Subconscious API
pnpm add subconscious
# or
npm install subconscious
# or
yarn add subconsciousimport { Subconscious } from 'subconscious';
const client = new Subconscious({
apiKey: process.env.SUBCONSCIOUS_API_KEY!,
});
const run = await client.run({
engine: 'tim-gpt',
input: {
instructions: 'Search for the latest AI news and summarize the top 3 stories',
tools: [{ type: 'platform', id: 'fast_search', options: {} }],
},
options: { awaitCompletion: true },
});
console.log(run.result?.answer);Create an API key in the Subconscious dashboard.
The simplest way to use the SDK—create a run and wait for completion:
const run = await client.run({
engine: 'tim-gpt',
input: {
instructions: 'Analyze the latest trends in renewable energy',
tools: [{ type: 'platform', id: 'fast_search', options: {} }],
},
options: { awaitCompletion: true },
});
console.log(run.result?.answer);
console.log(run.result?.reasoning); // Structured reasoning nodesStart a run without waiting, then check status later:
const run = await client.run({
engine: 'tim-gpt',
input: {
instructions: 'Generate a comprehensive report',
tools: [],
},
});
console.log(`Run started: ${run.runId}`);
// Check status later
const status = await client.get(run.runId);
console.log(status.status); // 'queued' | 'running' | 'succeeded' | 'failed' | 'canceled' | 'timed_out'const run = await client.run({
engine: 'tim-gpt',
input: {
instructions: 'Complex task',
tools: [{ type: 'platform', id: 'fast_search' }],
},
});
// Wait with custom polling options
const result = await client.wait(run.runId, {
intervalMs: 2000, // Poll every 2 seconds
maxAttempts: 60, // Give up after 60 attempts
});Stream text as it's generated:
const stream = client.stream({
engine: 'tim-gpt',
input: {
instructions: 'Write a short essay about space exploration',
tools: [{ type: 'platform', id: 'fast_search' }],
},
});
for await (const event of stream) {
if (event.type === 'delta') {
process.stdout.write(event.content);
} else if (event.type === 'done') {
console.log('\n\nRun completed:', event.runId);
} else if (event.type === 'error') {
console.error('Error:', event.message);
}
}Note: Rich streaming events (reasoning steps, tool calls) are coming soon. Currently, the stream provides text deltas as they're generated.
Simple Search Tools — Use these tools to get started quickly in our playground or with our API. For example: { type: 'platform', id: 'fast_search' }.
| Tool Name | API Name | Description |
|---|---|---|
| Fast Search | fast_search |
Extremely fast search for simple factual lookups |
| Web Search | web_search |
Comprehensive web search for detailed research |
| Fresh Search | fresh_search |
Search the web for content from the last 7 days |
| Page Reader | page_reader |
Extract content from a specific webpage URL |
| Find Similar | find_similar |
Find similar links to a given URL |
| People Search | people_search |
Search for people, profiles, and bios |
| Company Search | company_search |
Search for companies, funding info, and business details |
| News Search | news_search |
Search for news articles and press coverage |
| Tweet Search | tweet_search |
Search for tweets and Twitter/X discussions |
| Research Paper Search | research_paper_search |
Search for academic research papers and studies |
| Google Search | google_search |
Search the web using Google |
// Platform tools (hosted by Subconscious)
const fastSearch = {
type: 'platform',
id: 'fast_search',
options: {},
};
// Function tools (your own HTTP endpoints)
const customFunction = {
type: 'function',
name: 'get_weather',
description: 'Get current weather for a location',
url: 'https://api.example.com/weather',
method: 'GET',
timeout: 30,
parameters: {
type: 'object',
properties: {
location: { type: 'string', description: 'City name' },
},
required: ['location'],
},
};
// MCP tools
const mcpTool = {
type: 'mcp',
url: 'https://mcp.example.com',
allow: ['read', 'write'],
};Function tools support two powerful features for injecting data at call time:
headers: HTTP headers sent with the request to your tool endpointdefaults: Parameter values hidden from the model and injected automatically
const toolWithHeadersAndDefaults = {
type: 'function',
name: 'search_database',
description: 'Search the database',
url: 'https://api.example.com/search',
method: 'POST',
parameters: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search query' },
// Define these for validation, but they'll be hidden from the model
sessionId: { type: 'string' },
apiKey: { type: 'string' },
},
required: ['query'], // Only query is required - model generates this
},
// HEADERS: Sent as HTTP headers when this tool's endpoint is called
headers: {
'x-custom-auth': 'my-secret-token',
'x-request-source': 'my-app',
},
// DEFAULTS: Injected into parameters, hidden from model
defaults: {
sessionId: 'user-session-abc123',
apiKey: 'secret-api-key',
},
};How it works:
| Feature | Where it goes | When |
|---|---|---|
headers |
HTTP request headers | Sent to your tool's URL |
defaults |
Merged into request body parameters |
At tool call time |
Default arguments flow:
- Define all parameters in
properties(required for validation) - Parameters with defaults are stripped from the schema before the model sees them
- Model only generates values for non-defaulted parameters (e.g.,
query) - At call time, defaults are merged into the request body
- Default values always take precedence over model-generated values
Each tool can have its own headers and defaults - they're only applied when that specific tool is called.
Get structured responses using JSON Schema. We recommend using Zod to define your schema, then convert it with zodToJsonSchema():
import { z } from 'zod';
import { Subconscious, zodToJsonSchema } from 'subconscious';
const client = new Subconscious({ apiKey: process.env.SUBCONSCIOUS_API_KEY! });
// Define your output schema with Zod
const AnalysisSchema = z.object({
summary: z.string().describe('A brief summary of the findings'),
keyPoints: z.array(z.string()).describe('Main takeaways'),
sentiment: z.enum(['positive', 'neutral', 'negative']),
confidence: z.number().describe('Confidence score from 0 to 1'),
});
const run = await client.run({
engine: 'tim-gpt',
input: {
instructions: 'Analyze the latest news about electric vehicles',
tools: [{ type: 'platform', id: 'fast_search', options: {} }],
answerFormat: zodToJsonSchema(AnalysisSchema, 'Analysis'),
},
options: { awaitCompletion: true },
});
// Result is typed according to your schema
const result = run.result?.answer as z.infer<typeof AnalysisSchema>;
console.log(result.summary);
console.log(result.keyPoints);You can also define a reasoningFormat to structure the agent's reasoning:
const ReasoningSchema = z.object({
steps: z.array(
z.object({
thought: z.string(),
action: z.string(),
}),
),
conclusion: z.string(),
});
const run = await client.run({
engine: 'tim-gpt',
input: {
instructions: 'Research and explain quantum computing',
tools: [{ type: 'platform', id: 'fast_search', options: {} }],
answerFormat: zodToJsonSchema(AnalysisSchema, 'Analysis'),
reasoningFormat: zodToJsonSchema(ReasoningSchema, 'Reasoning'),
},
options: { awaitCompletion: true },
});You can also provide the JSON Schema directly without Zod:
const run = await client.run({
engine: 'tim-gpt',
input: {
instructions: 'Analyze this topic',
tools: [],
answerFormat: {
title: 'Analysis',
type: 'object',
properties: {
summary: { type: 'string', description: 'Brief summary' },
score: { type: 'number', description: 'Score from 1-10' },
},
required: ['summary', 'score'],
},
},
options: { awaitCompletion: true },
});import { SubconsciousError, AuthenticationError, RateLimitError } from 'subconscious';
try {
const run = await client.run({
/* ... */
});
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Invalid API key');
} else if (error instanceof RateLimitError) {
console.error('Rate limited, retry later');
} else if (error instanceof SubconsciousError) {
console.error(`API error: ${error.code} - ${error.message}`);
}
}// Cancel via AbortController
const controller = new AbortController();
const stream = client.stream(params, { signal: controller.signal });
setTimeout(() => controller.abort(), 30000);
// Or cancel a running run
await client.cancel(run.runId);The main client class.
| Option | Type | Required | Default |
|---|---|---|---|
apiKey |
string |
Yes | - |
baseUrl |
string |
No | https://api.subconscious.dev/v1 |
| Method | Description |
|---|---|
run(params) |
Create a new run |
stream(params, options?) |
Stream text deltas |
get(runId) |
Get run status |
wait(runId, options?) |
Poll until completion |
cancel(runId) |
Cancel a running run |
Convert a Zod schema to the JSON Schema format expected by answerFormat and reasoningFormat.
| Param | Type | Description |
|---|---|---|
schema |
Zod object | A Zod object schema (z.object()) |
title |
string |
Title for the schema |
Returns an OutputSchema compatible with answerFormat and reasoningFormat.
| Engine | Type | Description |
|---|---|---|
tim |
Unified | Our flagship unified agent engine for a wide range of tasks |
tim-edge |
Unified | Highly efficient engine tuned for performance with search tools |
timini |
Compound | Complex reasoning engine for long-context and tool use backed by Gemini-3 Flash |
tim-gpt |
Compound | Complex reasoning engine for long-context and tool use backed by OpenAI GPT-4.1 |
tim-gpt-heavy |
Compound | Complex reasoning engine for long-context and tool use backed by OpenAI GPT-5.2 |
| Status | Description |
|---|---|
queued |
Waiting to start |
running |
Currently executing |
succeeded |
Completed successfully |
failed |
Encountered an error |
canceled |
Manually canceled |
timed_out |
Exceeded time limit |
- Node.js ≥ 18
- ESM only
Contributions are welcome! Please feel free to submit a pull request.
Apache-2.0
For support and questions:
- Documentation: https://docs.subconscious.dev
- Email: {hongyin,jack}@subconscious.dev
Apache-2.0