@ledewire/node
v0.9.0
Published
LedeWire SDK for Node.js — full API surface for building merchant stores
Maintainers
Readme
@ledewire/node
Node.js SDK for the LedeWire content marketplace — full API surface for building merchant stores, managing sellers, and processing buyer flows on the server side.
Install
npm install @ledewire/nodeQuick Start
import { createClient } from '@ledewire/node'
// Full access: API key + secret grants read/write seller permissions
const client = createClient({
apiKey: process.env.LEDEWIRE_API_KEY,
apiSecret: process.env.LEDEWIRE_API_SECRET,
})
// Merchant email/password auth
const client = createClient()
await client.merchant.auth.loginWithEmail({ email, password })
const stores = await client.merchant.auth.listStores()Client Namespaces
| Namespace | Description |
| ------------------------------ | ---------------------------------------------------------------- |
| client.config | Platform-level public config (no auth required) |
| client.auth | Buyer signup, email/password login, Google OAuth, password reset |
| client.wallet | Buyer wallet balance and payment sessions |
| client.purchases | Buyer purchase history, create purchases, verify ownership |
| client.content | Fetch content with buyer access info |
| client.checkout | Checkout state — what action is required next |
| client.user.apiKeys | Manage buyer API keys for autonomous agents |
| client.merchant.auth | Merchant login (email / Google), store discovery, password reset |
| client.merchant.users | Merchant user management (invite, list, update, remove) |
| client.merchant.content | Merchant content CRUD + search (merchant JWT auth) |
| client.merchant.buyers | Buyer statistics within a store |
| client.merchant.sales | Sales reporting and revenue statistics |
| client.merchant.config | Store configuration |
| client.merchant.domains | x402 domain verification for URL-based content gating |
| client.merchant.pricingRules | x402 URL pattern-based pricing rules |
| client.seller.content | Seller content CRUD + search (API key auth) |
| client.seller.sales | Seller sales statistics and revenue reporting |
| client.seller.buyers | Anonymized buyer statistics (API key auth) |
| client.seller.config | Store configuration (API key auth) |
Configuration
const client = createClient({
apiKey: process.env.LEDEWIRE_API_KEY,
apiSecret: process.env.LEDEWIRE_API_SECRET, // omit for read-only seller access
baseUrl: 'https://api-staging.ledewire.com', // optional, defaults to production
// Persist tokens across server restarts (optional)
storage: {
getTokens: async () => JSON.parse((await redis.get('lw:tokens')) ?? 'null'),
setTokens: async (t) => redis.set('lw:tokens', JSON.stringify(t)),
clearTokens: async () => redis.del('lw:tokens'),
},
// Side-effects only — storage.setTokens is already the persistence hook.
// Use onTokenRefreshed for audit logging or cache invalidation on refresh.
onTokenRefreshed: async (tokens) => {
await auditLog.record('token_refreshed', { expiresAt: tokens.expiresAt })
},
onAuthExpired: () => {
console.error('LedeWire session expired — re-authenticate')
},
})Token refresh is handled automatically — you never need to call a refresh method manually.
Serverless / edge note: The default
MemoryTokenStorageresets on every cold start, which means tokens are lost between function invocations. Always provide a customstorageadapter (database, Redis, encrypted cookie) when deploying to serverless or edge runtimes.
Example: Merchant JWT Auth (no API key)
Use this flow when running a merchant backend that authenticates via email/password or Google. No API key is required. Token refresh is automatic — the SDK handles it transparently.
Email / password login
The one-step helper logs in and returns both the normalized tokens and the accessible stores list in a single HTTP call:
import { createClient, ForbiddenError } from '@ledewire/node'
const client = createClient({
// Required for serverless/edge — MemoryTokenStorage (default) resets on cold start.
storage: {
getTokens: async () => JSON.parse((await redis.get('lw:tokens')) ?? 'null'),
setTokens: async (t) => redis.set('lw:tokens', JSON.stringify(t)),
clearTokens: async () => redis.del('lw:tokens'),
},
onAuthExpired: () => redirect('/login'),
})
try {
const { tokens, stores } = await client.merchant.auth.loginWithEmailAndListStores({
email: '[email protected]',
password: process.env.MERCHANT_PASSWORD,
})
// tokens: StoredTokens — { accessToken, refreshToken, expiresAt: number (Unix ms) }
// stores: MerchantLoginStore[] — use .id, .name, .role
const storeId = stores[0].id
} catch (err) {
if (err instanceof ForbiddenError) {
// Valid credentials but account has no merchant store access (e.g. buyer account).
// err.message: "This account does not have merchant access. Use a merchant or owner account."
}
}Google OAuth login
Same flow with a Google ID token instead of email/password:
const { tokens, stores } = await client.merchant.auth.loginWithGoogleAndListStores({
id_token: googleIdToken, // from Google Identity Services callback
})
const storeId = stores[0].idPassword reset
Two-step flow — request a code, then submit it with the new password:
// Step 1 — send a 6-digit reset code to the merchant's email.
// Always returns 200 to prevent email enumeration.
await client.merchant.auth.requestPasswordReset({ email: '[email protected]' })
// Step 2 — submit the code and new password.
await client.merchant.auth.resetPassword({
email: '[email protected]',
reset_code: '246810',
password: 'new-secure-password',
})Separate login + store list (when you need full store detail)
Use this only when you need fields available on ManageableStore but not on MerchantLoginStore
(store_key, logo):
await client.merchant.auth.loginWithEmail({ email, password })
const stores = await client.merchant.auth.listStores() // ManageableStore[]
const storeId = stores[0].id // .id and .name match MerchantLoginStoreExample: Merchant Store Setup
const client = createClient()
await client.merchant.auth.loginWithEmail({
email: '[email protected]',
password: process.env.MERCHANT_PASSWORD,
})
const stores = await client.merchant.auth.listStores()
const storeId = stores[0].id
// Create a markdown article
await client.seller.content.create(storeId, {
content_type: 'markdown',
title: 'Hello World',
content_body: btoa('# Hello World\nFull article body here.'),
price_cents: 500,
visibility: 'public',
})
// Create an external reference (e.g. a Vimeo video)
await client.seller.content.create(storeId, {
content_type: 'external_ref',
title: 'Intro to Machine Learning',
content_uri: 'https://vimeo.com/987654321',
external_identifier: 'vimeo:987654321',
price_cents: 1500,
visibility: 'public',
})
const items = await client.seller.content.list(storeId)
// items.data — ContentListItem[]
// items.pagination — PaginationMeta
// Search by title (partial match), URI, and/or metadata
const results = await client.seller.content.search(storeId, { title: 'intro' })
const byUri = await client.seller.content.search(storeId, { uri: 'vimeo.com' })
const combined = await client.seller.content.search(storeId, {
title: 'tutorial',
metadata: { category: 'ml' },
})
// Fetch Google OAuth client ID before the user has signed in
const { google_client_id } = await client.config.getPublic()
// google.accounts.id.initialize({ client_id: google_client_id, callback })Error Handling
All SDK errors extend LedewireError — use instanceof checks on named subclasses:
import { createClient, ForbiddenError, AuthError, NotFoundError } from '@ledewire/node'
try {
await client.merchant.auth.loginWithGoogle({ id_token })
} catch (err) {
if (err instanceof ForbiddenError) {
// 403 — credentials are VALID but the account has no merchant store access.
// This is the expected error when a personal Google account previously
// registered as a buyer is used on the merchant login endpoint.
// err.message → "This account does not have merchant access. Use a merchant or owner account."
// Fix: use a dedicated merchant/owner account, or have a store owner add your account.
console.error('Wrong account role:', err.message)
} else if (err instanceof AuthError) {
// 401 — bad credentials or expired token. Re-authenticate.
console.error('Authentication failed:', err.message)
} else if (err instanceof NotFoundError) {
// 404 — resource not found (e.g. wrong email/password on email login).
console.error('Not found:', err.message)
}
}| Subclass | Status | When thrown |
| ---------------- | ------- | ----------------------------------------------------------------------- |
| AuthError | 401 | Invalid credentials, expired token, failed token refresh |
| ForbiddenError | 403 | Valid credentials, wrong account role (e.g. buyer on merchant endpoint) |
| NotFoundError | 404 | Resource not found, wrong email/password on email login |
| PurchaseError | 409/422 | Purchase validation failure (price mismatch, duplicate, etc.) |
| LedewireError | any | Catch-all base class for all other API errors |
Documentation
License
MIT
