@netiva-ai/sdk
v0.0.6
Published
Official Node.js SDK for Netiva
Readme
@netiva-ai/sdk
Official Node.js SDK for Netiva.
Installation
npm install @netiva-ai/sdkQuick Start
import { Netiva } from '@netiva-ai/sdk'
const netiva = new Netiva({
workspaceId: process.env.NETIVA_WORKSPACE_ID!,
apiKey: process.env.NETIVA_API_KEY!, // Format: nsk_xxx
})Customers
// Create with metadata (store your internal IDs for mapping)
const customer = await netiva.customers.create({
email: '[email protected]',
firstName: 'John',
lastName: 'Doe',
metadata: {
userUid: 'your-internal-user-id',
organizationUid: 'your-internal-org-id',
},
})
// Returns: { id, email, firstName, lastName, metadata }
// Get customer
const found = await netiva.customers.get(customer.id)
// List customers
const { data, meta } = await netiva.customers.list({ search: 'john', page: 1, limit: 25 })
// Update customer
const updated = await netiva.customers.update(customer.id, { firstName: 'Jane' })
// Delete customer
await netiva.customers.delete(customer.id)
// List organizations a customer belongs to
const orgs = await netiva.customers.listOrganizations(customer.id)
// Returns: Organization[] - all orgs where the customer is a memberOrganizations
// Create with metadata
const org = await netiva.organizations.create({
name: 'Acme Corp',
domain: 'acme-corp', // slug/key
metadata: {
organizationUid: 'your-internal-org-id',
},
})
// Returns: { id, name, domain, metadata }
// Get organization
const found = await netiva.organizations.get(org.id)
// List organizations
const { data } = await netiva.organizations.list()
// Update organization
const updated = await netiva.organizations.update(org.id, { name: 'Acme Inc' })
// Delete organization
await netiva.organizations.delete(org.id)
// Add member to organization
await netiva.organizations.addMember(org.id, {
customerId: customer.id,
role: 'admin', // or 'member'
})
// Remove member
await netiva.organizations.removeMember(org.id, customer.id)Add-ons
Manage add-ons (fixed, quantity-based, or metered) for organization subscriptions:
// List all available add-ons for the workspace
const { addons } = await netiva.addons.list()
// Returns: { addons: [{ id, name, description, type, unitLabel, prices, ... }] }
// List active add-ons for an organization
const { addons: orgAddons } = await netiva.addons.listForOrganization(org.id)
// Returns: { addons: [{ subscriptionItemId, name, type, quantity, price, ... }] }
// Add an add-on to an organization's subscription
const result = await netiva.addons.add(org.id, {
priceId: 'price_xxx',
quantity: 5, // optional, defaults to 1 for fixed/quantity types
})
// Returns: { success, subscriptionItem: { id, priceId, quantity } }
// Update add-on quantity
await netiva.addons.updateQuantity(org.id, 'si_xxx', 10)
// Returns: { success, subscriptionItem: { id, quantity } }
// Remove an add-on
await netiva.addons.remove(org.id, 'si_xxx')
// Returns: { success }Metered Usage
For metered add-ons, report usage and retrieve summaries:
// Report metered usage
await netiva.addons.reportUsage(org.id, {
subscriptionItemId: 'si_xxx',
quantity: 100,
timestamp: Math.floor(Date.now() / 1000), // optional, unix timestamp in seconds, defaults to now
action: 'increment', // optional: 'increment' (default) or 'set'
})
// Returns: { success, usageRecord: { id, quantity, timestamp } }
// Get usage summary for current billing period
const { usage } = await netiva.addons.getUsage(org.id, 'si_xxx')
// Returns: { usage: { subscriptionItemId, totalUsage, period: { start, end } } }Magic Links
Generate links for billing portal access. Use for both checkout and subscription management:
Checkout Flow (New Subscriptions)
Redirect users to select a plan and enter payment:
// Option 1: Let customer browse all plans
const { link } = await netiva.magicLink.generate({
customerId: customer.id,
organizationId: org.id,
returnUrl: 'https://app.example.com/dashboard?billing=complete',
redirectTo: '/plans',
})
// Option 2: Skip plan selection, go directly to checkout
const { link } = await netiva.magicLink.generate({
customerId: customer.id,
organizationId: org.id,
returnUrl: 'https://app.example.com/dashboard?billing=complete',
planId: 'prod_xxx', // Stripe Product ID - bypasses /plans, goes straight to checkout
})
// Redirect user to checkout
res.redirect(link)Checkout Flow:
- User signs up on your app
- You create customer + org in Netiva
- You redirect user to magic link (with
redirectTo: '/plans') - User selects plan, enters payment
- Netiva redirects back to returnUrl
- Webhook
subscription.createdfires
Manage Subscription
Generate links for "Manage Subscription" button - logs user into billing portal:
const { link } = await netiva.magicLink.generate({
customerId: customer.id,
returnUrl: 'https://app.example.com/settings/billing',
})
// Redirect user to manage their subscription
res.redirect(link)Parameters
| Parameter | Required | Description |
|-----------|----------|-------------|
| customerId | Yes | Customer to authenticate |
| organizationId | No | Organization context |
| returnUrl | No | URL to return to after portal session |
| expiresInMinutes | No | Link expiry (default: 15 minutes) |
| redirectTo | No | Portal destination: /plans, /payment-methods, /invoices, /addons |
| planId | No | Skip plan selection, go directly to checkout for this plan |
SSO Verification
When customers click "Launch App" in the portal, verify the token:
app.get('/auth/sso', (req, res) => {
const result = netiva.sso.verify(req.query.token as string)
if (!result.valid) {
return res.status(401).json({ error: result.error })
}
// Authenticated! result.customer contains { id, email, firstName, lastName }
req.session.userId = result.customer!.id
res.redirect('/dashboard')
})Webhooks
Verify incoming webhooks:
app.post('/webhooks/netiva', express.raw({ type: 'application/json' }), (req, res) => {
const result = netiva.webhooks.verify(req.body, req.headers)
if (!result.valid) {
return res.status(401).json({ error: result.error })
}
const { event, data } = result.payload!
switch (event) {
case 'customer.created':
// data: { id, email, metadata }
break
case 'subscription.created':
// data: { subscriptionId, customerId, organizationId, planId, planName, status, currentPeriodEnd, metadata }
break
case 'subscription.updated':
// data: { subscriptionId, customerId, organizationId, planId, currentPeriodEnd, metadata }
break
case 'subscription.cancelled':
// data: { subscriptionId, organizationId, cancelledAt, metadata }
break
case 'subscription.reactivated':
// data: { subscriptionId, organizationId, metadata }
break
case 'subscription.upgraded':
case 'subscription.downgraded':
// data: { subscriptionId, organizationId, previousPlanId, newPlanId, metadata }
break
case 'invoice.payment_succeeded':
// data: { invoiceId, subscriptionId, organizationId, amount, currency, metadata }
break
case 'invoice.payment_failed':
// data: { invoiceId, subscriptionId, organizationId, failureReason, metadata }
break
case 'addon.added':
// data: { subscriptionItemId, priceId, quantity, organizationId, metadata }
break
case 'addon.removed':
// data: { subscriptionItemId, organizationId, metadata }
break
case 'addon.quantity_updated':
// data: { subscriptionItemId, quantity, organizationId, metadata }
break
case 'addon.usage_reported':
// data: { subscriptionItemId, quantity, organizationId, metadata }
break
}
res.json({ received: true })
})Webhook Events
| Event | Description |
|-------|-------------|
| customer.created | New customer registered in the members portal |
| lead.created | New lead captured from a landing page form |
| subscription.created | Customer subscribed to a plan |
| subscription.updated | Subscription details changed (e.g., quantity) |
| subscription.cancelled | Customer cancelled their subscription |
| subscription.reactivated | Cancelled subscription was reactivated |
| subscription.upgraded | Customer upgraded to a higher plan |
| subscription.downgraded | Customer downgraded to a lower plan |
| invoice.payment_succeeded | Payment was successfully processed |
| invoice.payment_failed | Payment failed (card declined, expired, etc.) |
| addon.added | Add-on added to an organization's subscription |
| addon.removed | Add-on removed from an organization's subscription |
| addon.quantity_updated | Add-on quantity changed |
| addon.usage_reported | Metered usage reported for an add-on |
Metadata Propagation: Metadata passed when creating customers/orgs is included in webhook payloads.
Error Handling
import { NetivaApiError, NetivaNetworkError, NetivaTimeoutError } from '@netiva-ai/sdk'
try {
await netiva.customers.get('invalid-id')
} catch (error) {
if (error instanceof NetivaApiError) {
console.error(error.message, error.statusCode, error.errorCode)
}
}Configuration Options
| Option | Required | Default | Description |
|--------|----------|---------|-------------|
| workspaceId | Yes | — | Your Netiva workspace ID |
| apiKey | Yes | — | API key (format: nsk_xxx) |
| baseUrl | No | https://api.netiva.ai | API base URL |
| timeout | No | 30000 | Request timeout (ms) |
| retries | No | 0 | Retry attempts |
License
MIT
