TypeScript/Node.js client for the Pre.dev API — AI-powered software specs + browser automation.
Specs
- 🚀 Fast Spec: Comprehensive specifications for MVPs and prototypes
- 🔍 Deep Spec: Ultra-detailed specifications for complex systems
- ⚡ Async Spec: Non-blocking async methods with status polling
- 📄 File upload support (PDFs, docs, images as reference context)
Browser automation (NEW)
- 🌐 Browser Tasks: Scrape, fill forms, navigate pages with structured JSON output
- 📡 SSE streaming: Watch execution live — screenshots, plans, actions as they happen
- ⏱ Async mode: Fire-and-forget, poll for progress
- 🔁 Retrieval: Get any past task with full timeline for audit/replay
Quality of life
- ✨ Full TypeScript types, modern ES modules
- 🛡 Custom exceptions for auth / rate limit / API errors
npm install predev-apiimport { PredevAPI } from 'predev-api';
// Initialize the predev client with your API key
const predev = new PredevAPI({ apiKey: 'your_api_key_here' });
// Generate a fast specification
const result = await predev.fastSpec({
input: 'Build a task management app with team collaboration'
});
console.log(result);All fastSpec, deepSpec, fastSpecAsync, and deepSpecAsync methods support optional file uploads. This allows you to provide architecture documents, requirements files, design mockups, or other context files to improve specification generation.
// Using File input from HTML form
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];
const result = await predev.fastSpec({
input: 'Generate specs based on this design document',
file: file // Pass the File object directly
});import fs from 'fs';
// Method 1: Using file path (simplest)
const result = await predev.fastSpec({
input: 'Build based on these requirements',
file: {
data: fs.readFileSync('requirements.pdf'),
name: 'requirements.pdf'
}
});
// Method 2: Using file object
const fileContent = fs.readFileSync('architecture.doc');
const result = await predev.deepSpec({
input: 'Create comprehensive specs',
file: {
data: fileContent,
name: 'architecture.doc'
}
});- PDF documents (
*.pdf) - Word documents (
*.doc,*.docx) - Text files (
*.txt) - Images (
*.jpg,*.png,*.jpeg)
When you upload a file, the response includes:
{
uploadedFileName?: string; // Name of the uploaded file
uploadedFileShortUrl?: string; // URL to access the file
codingAgentSpecUrl?: string; // Spec optimized for AI systems
humanSpecUrl?: string; // Spec optimized for humans
// ... other fields
}The Pre.dev API uses API key authentication. Get your API key from the pre.dev dashboard under Settings → API Keys:
const predev = new PredevAPI({ apiKey: 'your_api_key' });Generate a fast specification (30-40 seconds, ~5-10 credits).
Parameters:
options.input(required):string- Description of what you want to buildoptions.currentContext(optional):string- Existing project contextoptions.docURLs(optional):string[]- Documentation URLs to reference (e.g., Stripe docs, framework docs)
Returns: SpecResponse object with complete specification data
Example:
const result = await predev.fastSpec({
input: 'Build a SaaS project management tool with real-time collaboration'
});Example with Documentation URLs:
const result = await predev.fastSpec({
input: 'Build a payment processing integration with Stripe',
docURLs: ['https://stripe.com/docs/api']
});
// When docURLs are provided, the response includes zippedDocsUrls:
// result.zippedDocsUrls = [
// {
// platform: "stripe.com",
// masterZipShortUrl: "https://api.pre.dev/s/xyz789"
// }
// ]
// These zipped documentation folders can be downloaded and help coding agents
// stay on track by providing complete, up-to-date documentation context.Generate a deep specification (2-3 minutes, ~10-50 credits).
Parameters: Same as fastSpec
Returns: SpecResponse object with comprehensive specification data
Example:
const result = await predev.deepSpec({
input: 'Build a healthcare platform with HIPAA compliance'
});Generate a fast specification asynchronously (returns immediately).
Parameters: Same as fastSpec
Returns: AsyncResponse object with specId for polling
Example:
const result = await predev.fastSpecAsync({
input: 'Build a comprehensive e-commerce platform'
});
// Returns: { specId: "spec_123", status: "pending" }Generate a deep specification asynchronously (returns immediately).
Parameters: Same as fastSpec
Returns: AsyncResponse object with specId for polling
Example:
const result = await predev.deepSpecAsync({
input: 'Build a fintech platform with regulatory compliance'
});
// Returns: { specId: "spec_456", status: "pending" }Check the status of an async specification generation request.
Parameters:
specId(required):string- The specification ID from async methods
Returns: SpecResponse object with current status and data (when completed)
Example:
const status = await predev.getSpecStatus('spec_123');
// Returns full SpecResponse with status: "pending" | "processing" | "completed" | "failed"List all specs with optional filtering and pagination.
Parameters:
params.limit(optional):number- Results per page (1-100, default: 20)params.skip(optional):number- Offset for pagination (default: 0)params.endpoint(optional):"fast_spec" | "deep_spec"- Filter by endpoint typeparams.status(optional):"pending" | "processing" | "completed" | "failed"- Filter by status
Returns: ListSpecsResponse object with specs array and pagination metadata
Examples:
// Get first 20 specs
const result = await predev.listSpecs();
// Get completed specs only
const completed = await predev.listSpecs({ status: 'completed' });
// Paginate: get specs 20-40
const page2 = await predev.listSpecs({ skip: 20, limit: 20 });
// Filter by endpoint type
const fastSpecs = await predev.listSpecs({ endpoint: 'fast_spec' });Search for specs using regex patterns.
Parameters:
params.query(required):string- Regex pattern (case-insensitive)params.limit(optional):number- Results per page (1-100, default: 20)params.skip(optional):number- Offset for pagination (default: 0)params.endpoint(optional):"fast_spec" | "deep_spec"- Filter by endpoint typeparams.status(optional):"pending" | "processing" | "completed" | "failed"- Filter by status
Returns: ListSpecsResponse object with matching specs and pagination metadata
Examples:
// Search for "payment" specs
const paymentSpecs = await predev.findSpecs({ query: 'payment' });
// Search for specs starting with "Build"
const buildSpecs = await predev.findSpecs({ query: '^Build' });
// Search: only completed specs mentioning "auth"
const authSpecs = await predev.findSpecs({
query: 'auth',
status: 'completed'
});
// Complex regex: find SaaS or SASS projects
const saasSpecs = await predev.findSpecs({ query: 'saas|sass' });Regex Pattern Examples:
| Pattern | Matches |
|---|---|
payment |
"payment", "Payment", "make payment" |
^Build |
Specs starting with "Build" |
platform$ |
Specs ending with "platform" |
(API|REST) |
Either "API" or "REST" |
auth.*system |
"auth" then anything then "system" |
\\d{3,} |
3+ digits (budgets, quantities) |
saas|sass |
SaaS or SASS |
{
specId: string; // Unique ID for polling (e.g., "spec_abc123")
status: "pending" | "processing" | "completed" | "failed";
}{
// Basic info
_id?: string; // Internal ID
created?: string; // ISO timestamp
endpoint: "fast_spec" | "deep_spec";
input: string; // Original input text
status: "pending" | "processing" | "completed" | "failed";
success: boolean;
uploadedFileShortUrl?: string; // URL to input file
uploadedFileName?: string; // Name of input file
// Output URLs (when completed)
codingAgentSpecUrl?: string; // Spec optimized for AI/LLM systems
humanSpecUrl?: string; // Spec optimized for human readers
totalHumanHours?: number; // Estimated hours for human developers
// Direct returns (new)
codingAgentSpecJson?: CodingAgentSpecJson; // Simplified JSON for coding tools
codingAgentSpecMarkdown?: string; // Simplified markdown for coding tools
humanSpecJson?: HumanSpecJson; // Full JSON with hours/personas/roles
humanSpecMarkdown?: string; // Full markdown with all details
executionTime?: number; // Processing time in milliseconds
// Integration URLs (when completed)
predevUrl?: string; // Link to pre.dev project
// Documentation (when docURLs provided)
zippedDocsUrls?: Array<{
platform: string; // Platform name (e.g., "stripe.com")
masterZipShortUrl: string; // URL to download zipped documentation
}>; // Complete documentation as zipped folders
// Helps coding agents stay on track with full context
// Error handling
errorMessage?: string; // Error details if failed
progress?: number; // Overall progress percentage (0-100)
progressMessage?: string; // Detailed progress message
// Credit usage - available during processing (real-time) and on completion
// Fast spec: ~5-10 credits, Deep spec: ~10-50 credits
creditsUsed?: number; // Total credits consumed by this spec generation
}interface SpecCoreFunctionality { name: string; description: string; priority?: "High" | "Medium" | "Low"; }
interface SpecTechStackItem { name: string; category: string; }
interface SpecPersona { title: string; description: string; primaryGoals?: string[]; painPoints?: string[]; keyTasks?: string[]; }
interface SpecRole { name: string; shortHand: string; }
interface CodingAgentSubTask { id?: string; description: string; complexity: string; }
interface CodingAgentStory { id?: string; title: string; description?: string; acceptanceCriteria?: string[]; complexity?: string; subTasks: CodingAgentSubTask[]; }
interface CodingAgentMilestone { milestoneNumber: number; description: string; stories: CodingAgentStory[]; }
interface CodingAgentSpecJson {
title?: string;
executiveSummary: string;
coreFunctionalities: SpecCoreFunctionality[];
techStack: SpecTechStackItem[];
techStackGrouped: Record<string, string[]>;
milestones: CodingAgentMilestone[];
}
interface HumanSpecSubTask { id?: string; description: string; hours: number; complexity: string; roles?: SpecRole[]; }
interface HumanSpecStory { id?: string; title: string; description?: string; acceptanceCriteria?: string[]; hours: number; complexity?: string; subTasks: HumanSpecSubTask[]; }
interface HumanSpecMilestone { milestoneNumber: number; description: string; hours: number; stories: HumanSpecStory[]; }
interface HumanSpecJson {
title?: string;
executiveSummary: string;
coreFunctionalities: SpecCoreFunctionality[];
personas: SpecPersona[];
techStack: SpecTechStackItem[];
techStackGrouped: Record<string, string[]>;
milestones: HumanSpecMilestone[];
totalHours: number;
roles: SpecRole[];
}Run browser automation — navigate to, interact with, and/or extract data from any web page. Each task navigates a URL, optionally performs actions, and returns typed JSON.
import { PredevAPI } from 'predev-api';
const client = new PredevAPI({ apiKey: 'your_api_key' });
const result = await client.browserAgent([
{
url: 'https://news.ycombinator.com',
instruction: 'Extract the top 5 stories',
output: {
type: 'object',
properties: {
stories: {
type: 'array',
items: {
type: 'object',
properties: { title: { type: 'string' }, points: { type: 'number' } },
},
},
},
},
},
]);
console.log(result.results[0].data.stories);const result = await client.browserAgent([
{ url: 'https://example.com', output: { type: 'object', properties: { heading: { type: 'string' } } } }
]);
console.log(result.results[0].data); // { heading: "Example Domain" }
console.log(result.totalCreditsUsed); // 0.1Returns an async iterator yielding events as the agent runs. Good for showing progress in a UI.
for await (const msg of client.browserAgent([...tasks], { stream: true })) {
switch (msg.event) {
case 'task_event':
// navigation | screenshot | plan | action | validation | done
console.log(`[${msg.data.type}]`, msg.data.data);
break;
case 'task_result':
console.log(`Task ${msg.data.taskIndex} done:`, msg.data.data);
break;
case 'done':
console.log('Batch complete:', msg.data.totalCreditsUsed, 'credits');
break;
case 'error':
console.error('Batch error:', msg.data);
break;
}
}Returns the batch ID immediately. Use for long-running batches or background jobs.
const r = await client.browserAgent([...tasks], { async: true });
// { id: "batch_abc", status: "processing", completed: 0, total: 3 }
while (true) {
const state = await client.getBrowserAgent(r.id);
console.log(`${state.completed}/${state.total}`);
for (const done of state.results) {
console.log(` ✓ ${done.url} → ${JSON.stringify(done.data)}`);
}
if (state.status === 'completed') break;
await new Promise(res => setTimeout(res, 1000));
}Each task's behavior is determined by which fields are set:
| Fields | Shape | Example |
|---|---|---|
url + output |
Scrape | Extract structured data from a page |
url + instruction |
Act | Click, navigate, search |
url + instruction + input |
Form fill | Fill & submit a form |
url + instruction + output |
Act + extract | Navigate then extract data |
Every task records navigation, screenshots, LLM plans, actions, and validations. Retrieve for audit, replay, or debugging. Screenshots are uploaded to a CDN during execution — retrieved events contain data.url, not base64.
const details = await client.getBrowserAgent(batchId, { includeEvents: true });
for (const result of details.results) {
for (const ev of result.events || []) {
if (ev.type === 'screenshot') {
// ev.data.url is a permanent CDN URL. Use directly in an <img> tag.
console.log(`Iter ${ev.iteration} screenshot:`, ev.data.url);
}
if (ev.type === 'plan') console.log(`Iter ${ev.iteration} plan: ${ev.data.notes}`);
if (ev.type === 'action') console.log(` action: ${ev.data.type} ${ev.data.selector || ''}`);
}
}Note: The live SSE stream (
stream: true) still sends screenshots inline as base64 (ev.data.base64) so live UIs render instantly. The retrieval path (getBrowserAgent) always returns CDN URLs.
// Scrape 100 URLs with 20 browsers in parallel
const urls = [...100 urls...];
const result = await client.browserAgent(
urls.map(url => ({ url, output: { type: 'object', properties: { title: { type: 'string' } } } })),
{ concurrency: 20 }
);
console.log(`${result.completed}/${result.total} done in ${result.totalCreditsUsed} credits`);| Method | Returns | Use when |
|---|---|---|
browserAgent(tasks, { stream: false, async: false }) |
Promise<BrowserAgentResponse> |
Default — wait for completion |
browserAgent(tasks, { stream: true }) |
AsyncGenerator<BrowserAgentSSEMessage> |
Live UI showing execution timeline |
browserAgent(tasks, { async: true }) |
Promise<BrowserAgentResponse> (empty results, returned immediately) |
Long batches, background jobs |
getBrowserAgent(id, { includeEvents?: boolean }) |
Promise<BrowserAgentResponse> |
Poll progress or retrieve a completed batch |
| Status | Meaning |
|---|---|
SUCCESS |
Task completed, data extracted |
BLOCKED |
Page blocked automation (bot detection) |
TIMEOUT |
Task exceeded time limit |
LOOP |
Agent detected it was stuck in a loop |
ERROR |
Unexpected error |
NO_TARGET |
Could not find target elements |
CAPTCHA_FAILED |
CAPTCHA solve failed |
- Minimum: 0.1 credits per task ($0.01)
- 10x margin on underlying LLM + sandbox compute
- 1 credit = $0.10
The browser-task endpoints throw typed exceptions for the most common
gating cases so callers can react without parsing strings. Every billing-
gate exception carries an actionUrl — a deep link back to pre.dev that
auto-opens the right modal (subscribe / buy credits) when the user lands
there:
import {
PredevAPI,
InsufficientCreditsError,
SubscriptionRequiredError,
RateLimitError,
QueueFullError,
} from 'predev-api';
const client = new PredevAPI({ apiKey: process.env.PREDEV_API_KEY! });
try {
await client.browserAgent([{ url: 'https://example.com', instruction: 'extract h1' }]);
} catch (e) {
if (e instanceof InsufficientCreditsError) {
// Send the user to the credits modal — `actionUrl` is the canonical link.
window.open(e.actionUrl, '_blank');
} else if (e instanceof SubscriptionRequiredError) {
window.open(e.actionUrl, '_blank');
} else if (e instanceof RateLimitError) {
// Back off and retry.
} else if (e instanceof QueueFullError) {
// Wait for in-flight tasks to drain.
} else {
throw e;
}
}Mid-stream errors on the SSE stream throw the same typed exceptions, so a
for await loop wrapped in try/catch is enough — no special event
handling required.
| Exception | HTTP | code |
|---|---|---|
SubscriptionRequiredError |
402 | SUBSCRIPTION_REQUIRED |
InsufficientCreditsError |
402 | INSUFFICIENT_CREDITS |
RateLimitError |
429 | RATE_LIMITED |
QueueFullError |
429 | QUEUE_FULL |
BatchTooLargeError |
400 | BATCH_TOO_LARGE |
AuthenticationError |
401 | — |
PredevAPIError |
other | — |