@withsema/sdk
v0.1.0
Published
Official Node.js SDK for Sema - Universal ingestion infrastructure for AI Agents
Maintainers
Readme
Sema Node.js SDK
Official Node.js SDK for Sema - Universal ingestion infrastructure for AI Agents.
Installation
npm install @withsema/sdkQuick Start
API Client
import { SemaClient } from '@withsema/sdk';
const client = new SemaClient({ apiKey: 'sk_live_...' });
// Create an inbox
const inbox = await client.createInbox({
name: 'My Inbox',
webhook_url: 'https://example.com/webhooks/sema',
});
console.log(`Created inbox: ${inbox.id}`);
// Upload content
const item = await client.uploadItem(
inbox.id,
Buffer.from('Hello, World!'),
{
sender_address: '[email protected]',
subject: 'Test Upload',
}
);
console.log(`Created item: ${item.id}`);
// Check deliveries
const deliveries = await client.getItemDeliveries(item.id);
for (const d of deliveries.deliveries) {
console.log(`Delivery ${d.id}: ${d.status}`);
}Webhook Verification
import { WebhookVerifier, WebhookVerificationError } from '@withsema/sdk';
const verifier = new WebhookVerifier('whsec_...');
function handleWebhook(req: Request, res: Response) {
try {
const event = verifier.verify(req.body, req.headers);
// Use event.webhookId as idempotency key
console.log(`Received: ${event.webhookId}`);
console.log(`Item ID: ${event.payload.item_id}`);
console.log(`Event type: ${event.payload.event_type}`);
res.status(200).send('OK');
} catch (e) {
if (e instanceof WebhookVerificationError) {
res.status(400).send(e.message);
return;
}
throw e;
}
}API Reference
SemaClient
The client for the Sema API.
const client = new SemaClient({
apiKey: 'sk_live_...',
baseUrl: 'https://api.withsema.com', // optional
timeout: 30000, // optional, in milliseconds
maxRetries: 3, // optional, set to 0 to disable retries
});The client automatically retries requests on transient failures (5xx errors, network errors, rate limits) with exponential backoff. Rate-limited requests (429) respect the Retry-After header when present.
Inbox Methods
createInbox(params)- Create a new inboxgetInbox(inboxId)- Get an inbox by IDupdateInbox(inboxId, params)- Update an inboxlistInboxes(params?)- List inboxes with optionallimit/offset; returns{ inboxes, total }
Item Methods
uploadItem(inboxId, file, params)- Upload content to an inboxgetItem(itemId)- Get an item by IDlistItems(inboxId, params?)- List items in an inbox
Delivery Methods
getItemDeliveries(itemId)- Get deliveries for an item
Attachment Methods
getItemAttachments(itemId)- Get attachments for an item with presigned download URLs
Pagination Iterators
For large result sets, use async iterators to automatically paginate:
// Iterate through all items in an inbox
for await (const item of client.iterItems(inboxId)) {
console.log(item.id, item.status);
}
// Iterate through all inboxes
for await (const inbox of client.iterInboxes()) {
console.log(inbox.id, inbox.name);
}
// Custom page size
for await (const item of client.iterItems(inboxId, { pageSize: 50 })) {
process.stdout.write('.');
}Observability Hooks
Add optional hooks for logging, tracing, or metrics:
const client = new SemaClient({
apiKey: 'sk_live_...',
hooks: {
beforeRequest: (ctx) => {
console.log(`→ ${ctx.method} ${ctx.url} (attempt ${ctx.attempt})`);
},
afterResponse: (ctx) => {
console.log(`← ${ctx.status} in ${ctx.durationMs}ms`);
},
onError: (ctx, error) => {
console.error(`✗ ${ctx.method} ${ctx.url}: ${error.message}`);
},
},
});Hook context includes:
method- HTTP method (GET, POST, etc.)url- Request URLattempt- Attempt number (1-based, increases on retries)status- HTTP status code (afterResponse only)durationMs- Request duration in milliseconds (afterResponse only)
Hook behavior:
beforeRequestruns before every attempt (including retries)afterResponseruns only for successful responsesonErrorruns for errors (HTTP errors, timeouts, network failures), including before a retry
Hooks can be sync or async. Errors thrown in hooks are silently ignored to prevent breaking SDK functionality.
WebhookVerifier
Verifies Standard Webhooks signatures.
const verifier = new WebhookVerifier('whsec_...', {
toleranceSeconds: 300, // optional, default 5 minutes
});
const event = verifier.verify(payload, headers);
// Returns WebhookEvent with:
// - webhookId: string (use as idempotency key)
// - timestamp: number
// - payload: WebhookPayloadEmail Utilities
resolveEmailInlineImages
HTML emails embed inline images using cid: references. This utility replaces them with download URLs:
import { SemaClient, WebhookVerifier, resolveEmailInlineImages } from '@withsema/sdk';
const client = new SemaClient({ apiKey: 'sk_live_...' });
const verifier = new WebhookVerifier('whsec_...');
async function handleWebhook(req: Request, res: Response) {
const event = verifier.verify(req.body, req.headers);
// Get the HTML body with cid: references
const bodyHtml = event.payload.deliverable.content_summary?.body_html ?? '';
// Fetch attachments with presigned download URLs
const { attachments } = await client.getItemAttachments(event.payload.item_id);
// Replace cid: references with actual URLs
const resolvedHtml = resolveEmailInlineImages(bodyHtml, attachments);
// resolvedHtml now has working image URLs
res.status(200).send('OK');
}partitionEmailAttachments
Email clients like Gmail set content_id on ALL attachments, not just inline ones. This utility correctly identifies which attachments are truly inline (embedded in HTML) vs which should be listed separately:
import { SemaClient, partitionEmailAttachments, resolveEmailInlineImages } from '@withsema/sdk';
const client = new SemaClient({ apiKey: 'sk_live_...' });
async function handleWebhook(req: Request, res: Response) {
// ... verify webhook ...
const bodyHtml = event.payload.deliverable.content_summary?.body_html ?? '';
const { attachments } = await client.getItemAttachments(event.payload.item_id);
// Resolve inline images in HTML
const resolvedHtml = resolveEmailInlineImages(bodyHtml, attachments);
// Partition: inline images vs files to list separately
const { inline, nonInline } = partitionEmailAttachments(bodyHtml, attachments);
// inline attachments are already displayed in resolvedHtml
// nonInline attachments should be listed separately
for (const att of nonInline) {
console.log(`Attachment: ${att.filename} (${att.content_type})`);
}
}Error Classes
SemaError- Base error for all SDK errorsSemaAPIError- Error returned by the API (hasstatusCodeandresponseBody)AuthenticationError- Invalid or missing API key (401)NotFoundError- Resource not found (404)RateLimitError- Rate limit exceeded (429)WebhookVerificationError- Signature or timestamp validation failed
Examples
See the examples/ directory for runnable examples:
send-content.ts- Create inbox and upload contentreceive-webhook.ts- HTTP webhook receiver with signature verification
Run examples with:
export SEMA_API_KEY="sk_live_..."
npx tsx examples/send-content.tsVersion
The SDK version is exported for programmatic use:
import { VERSION } from '@withsema/sdk';
console.log(VERSION); // "0.1.0"Requirements
- Node.js 18+ (uses native
fetch) - TypeScript 5+ (for development)
License
MIT
