@linwheel/sdk
v0.1.0
Published
TypeScript SDK for the LinWheel content engine API
Maintainers
Readme
@linwheel/sdk
TypeScript SDK for the LinWheel content engine API. Analyze, reshape, refine, and schedule LinkedIn content programmatically.
Zero runtime dependencies. Uses native fetch. Ships ESM + CJS with full type declarations.
Install
npm install @linwheel/sdkQuick Start
import { LinWheel } from "@linwheel/sdk";
const lw = new LinWheel({ apiKey: "lw_sk_..." });
// Analyze content for LinkedIn potential
const analysis = await lw.analyze({ text: "Today I shipped a new SDK..." });
// → { linkedinFit: { score: 8 }, suggestedAngles: [...], recommendedPostCount: 3 }
// Reshape into angle-specific posts
const { posts } = await lw.reshape({
text: "Today I shipped a new SDK...",
angles: ["field_note", "contrarian"],
saveDrafts: true,
});
// Refine a draft
const { refined } = await lw.refine({
text: posts[0].text,
intensity: "medium",
});
// Approve and schedule
await lw.posts.approve(postId, true);
await lw.posts.schedule(postId, {
scheduledAt: "2026-02-24T09:00:00Z",
autoPublish: true,
});API Reference
Content Processing
// Analyze text for LinkedIn potential — topics, angles, fit score
const analysis = await lw.analyze({ text, context? });
// Reshape content through 7 angle lenses
const result = await lw.reshape({
text,
angles: ["field_note", "contrarian", "demystification", ...],
preEdit?: boolean, // light-edit input first
instructions?: string, // custom instructions
saveDrafts?: boolean, // persist as drafts
});
// Refine with parameterized intensity
const result = await lw.refine({
text,
intensity?: "light" | "medium" | "heavy", // default: "medium"
instructions?: string,
postType?: string, // target angle for tone
saveDraft?: boolean,
});
// Split long content into a post series
const result = await lw.split({
text,
maxPosts?: number, // 2-10, default 5
instructions?: string,
saveDrafts?: boolean,
});Drafting
// Create a manual draft
const draft = await lw.draft({
fullText: "Your post content...",
hook?: string,
postType?: string, // default: "field_note"
approved?: boolean,
autoPublish?: boolean,
scheduledAt?: string, // ISO 8601
});
// Create a draft with image + carousel in one call
const bundle = await lw.bundle({
fullText: "Your post content...",
imageHeadlineText?: string,
imageStylePreset?: "typographic_minimal" | "gradient_text" | "dark_mode" | "accent_bar" | "abstract_shapes",
carouselSlides?: [{ headlineText: string, caption?: string }],
});Post Management
// List posts with filters
const { posts } = await lw.posts.list({
approved?: boolean,
scheduled?: boolean,
published?: boolean,
type?: string,
limit?: number,
offset?: number,
});
// Get, update, approve, schedule
const post = await lw.posts.get(postId);
await lw.posts.update(postId, { fullText: "..." });
await lw.posts.approve(postId, true);
await lw.posts.schedule(postId, { scheduledAt: "2026-02-24T09:00:00Z" });
// Generate visuals
await lw.posts.image(postId, { headlineText: "...", stylePreset: "dark_mode" });
await lw.posts.carousel(postId, { slides: [...], stylePreset: "accent_bar" });Voice Profiles
Voice profiles inject your writing style into all content generation. Provide writing samples and LinWheel matches your voice.
// Create a voice profile from your writing
const { profile } = await lw.voiceProfiles.create({
name: "My Writing Voice",
description: "Technical, direct, slightly irreverent",
samples: [article1, article2, article3],
isActive: true,
});
// List all profiles
const { profiles, activeProfileId } = await lw.voiceProfiles.list();
// Switch active profile
await lw.voiceProfiles.activate(profileId);
// Delete a profile
await lw.voiceProfiles.delete(profileId);Configuration
const lw = new LinWheel({
apiKey: "lw_sk_...", // required
baseUrl: "https://www.linwheel.io", // default
signingSecret: "your-hmac-secret", // optional, for request signing
timeoutMs: 60_000, // default: 60s
});HMAC Request Signing
If your API key has a signing secret, the SDK automatically signs every request with X-LW-Signature:
X-LW-Signature: t=<unix-seconds>,v1=<hmac-sha256-hex>Canonical payload: <timestamp>.<METHOD>.<path>.<body-sha256>
Error Handling
import { LinWheelApiError, LinWheelAuthError } from "@linwheel/sdk";
try {
await lw.analyze({ text: "..." });
} catch (e) {
if (e instanceof LinWheelAuthError) {
// 401 — invalid or missing API key
console.error(e.message, e.responseBody);
} else if (e instanceof LinWheelApiError) {
// 4xx/5xx
console.error(e.statusCode, e.message, e.responseBody);
}
}The 7 Content Angles
| Angle | What it does |
|-------|-------------|
| contrarian | Challenge conventional wisdom |
| field_note | Share a specific observation from experience |
| demystification | Break down something complex |
| identity_validation | Make the reader feel seen |
| provocateur | Be deliberately provocative |
| synthesizer | Connect dots across domains |
| curious_cat | Ask genuine questions |
License
MIT
