SDK Usage
This guide covers integrating @made-open/sdk into a third-party application. The SDK provides a typed TypeScript client for the Made Open hub API, with built-in error handling, authentication, and real-time event streaming.
Installation
npm install @made-open/sdk
# or
pnpm add @made-open/sdk
The SDK requires Node.js 18+ or a modern browser (it uses the global fetch API). No polyfills are needed for Node.js 18+.
Authentication
The SDK supports two authentication methods. Use one or the other — not both.
API Keys (mk_*)
API keys are intended for server-side or trusted client applications. Create one at /developer in the Made Open web UI (requires a logged-in account).
API keys are long-lived and should be treated as secrets. Do not embed them in client-side browser code or mobile apps.
OAuth Access Tokens
For applications that act on behalf of a specific user (e.g. a mobile app or a third-party web app), use the OAuth flow to obtain an access token. Pass the token as accessToken in the client config.
OAuth tokens are scoped to a single user and expire. Your application is responsible for refreshing them.
Quick Start
import { MadeOpenClient } from '@made-open/sdk';
const client = new MadeOpenClient({
baseUrl: 'https://your-hub.example.com',
apiKey: 'mk_your_api_key_here',
});
// Check the hub is reachable
const health = await client.health();
console.log(health); // { status: 'ok', version: '0.0.1' }
// Get all contacts
const contacts = await client.getContacts();
console.log(contacts);
Using an OAuth access token instead:
const client = new MadeOpenClient({
baseUrl: 'https://your-hub.example.com',
accessToken: userAccessToken,
});
Method Reference
health()
Check whether the hub is reachable and operational.
const result = await client.health();
// { status: 'ok', version: '0.0.1' }
Returns { status: string; version: string }. Call this as a startup check before making other requests.
getContacts()
Fetch all contacts (persons) visible to the authenticated user.
const contacts = await client.getContacts();
console.log(`Found ${contacts.length} contacts`);
Returns Promise<Person[]>. The Person type is re-exported from @made-open/sdk:
import type { Person } from '@made-open/sdk';
const contacts: Person[] = await client.getContacts();
getContact(id)
Fetch a single contact by ID. Returns null if the contact is not found (404), and throws MadeOpenError for all other errors.
const contact = await client.getContact('person-uuid-here');
if (contact === null) {
console.log('Contact not found');
} else {
console.log(contact);
}
getMessages(conversationId)
Fetch all messages within a specific conversation. To list conversations, use getConversations().
const messages = await client.getMessages('conversation-uuid-here');
getRules()
Fetch all automation rules for the authenticated user.
const rules = await client.getRules();
console.log(`${rules.length} rules configured`);
createRule(input)
Create a new automation rule. The condition and actions fields are JSON structures evaluated by the Rules Engine (see Rules Engine documentation).
import type { CreateRuleInput } from '@made-open/sdk';
const input: CreateRuleInput = {
name: 'Forward calls when at work',
condition: 'caller.group == "Family" AND location.label == "Work"',
actions: 'send_sms(to: caller.phone, body: "I am in the office. Call +1-555-0100.")',
};
const rule = await client.createRule(input);
console.log('Rule created:', rule);
deleteRule(id)
Delete an automation rule by ID. Returns void on success.
await client.deleteRule('rule-uuid-here');
query(text)
Send a natural-language query to the hub's AI agent. The agent has access to the full unified data model (contacts, messages, calendar, location, etc.).
const response = await client.query('Who called me last Tuesday?');
console.log(response.content); // AI-generated answer
console.log(response.conversationId); // Use to continue the conversation
Returns { content: string; conversationId: string }.
openapi()
Fetch the OpenAPI 3.1 specification for the hub API. Useful for generating client code, validating responses, or displaying documentation.
const spec = await client.openapi();
// Full OpenAPI spec object — browse at /api/docs in the web UI
streamUrl(topics)
Build the URL for the Server-Sent Events (SSE) stream endpoint. This is a browser-oriented method — it returns a URL string rather than an EventSource instance, because EventSource is not available in Node.js without a polyfill.
const url = client.streamUrl(['data.PersonCreated', 'communication.MessageReceived']);
// Returns: https://your-hub.example.com/api/stream/events?topics=data.PersonCreated,...&token=mk_...
Pass an empty array to subscribe to all events:
const url = client.streamUrl([]);
// Subscribes to the full event stream
Real-Time Events
Subscribe to live events in a browser using the native EventSource API:
import { MadeOpenClient } from '@made-open/sdk';
const client = new MadeOpenClient({
baseUrl: 'https://your-hub.example.com',
apiKey: 'mk_your_api_key_here',
});
const url = client.streamUrl([
'data.PersonCreated',
'data.PersonUpdated',
'communication.MessageReceived',
'communication.CallStarted',
]);
const source = new EventSource(url);
source.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Event type:', data.eventType);
console.log('Payload:', data);
};
source.onerror = (err) => {
console.error('SSE connection error:', err);
// EventSource reconnects automatically by default
};
// Clean up when done
function cleanup() {
source.close();
}
Common topic patterns:
| Topic | When it fires |
|---|---|
data.PersonCreated | A new contact was synced from an integration |
data.PersonUpdated | An existing contact's data changed |
communication.MessageReceived | An inbound SMS or message arrived |
communication.CallStarted | An inbound or outbound call began |
communication.CallEnded | A call finished |
rules.RuleFired | An automation rule matched and executed |
connector.synced | A connector plugin completed a sync cycle |
Topic patterns support NATS wildcards:
data.*— all data eventscommunication.*— all communication events
Error Handling
All API errors are thrown as MadeOpenError instances, which include the HTTP status code:
import { MadeOpenClient, MadeOpenError } from '@made-open/sdk';
const client = new MadeOpenClient({
baseUrl: 'https://your-hub.example.com',
apiKey: 'mk_your_api_key_here',
});
try {
await client.getContact('nonexistent-id');
} catch (err) {
if (err instanceof MadeOpenError) {
console.error(`API error ${err.status}: ${err.message}`);
// err.status: 404
// err.message: "Not Found" or a descriptive error from the hub
} else {
// Network error, JSON parse failure, etc.
throw err;
}
}
getContact is the one exception: it returns null on 404 rather than throwing. All other methods throw MadeOpenError on any non-2xx response.
TypeScript Types
All entity types from @made-open/shared are re-exported from @made-open/sdk for convenience. You do not need to install @made-open/shared separately.
import type { Person, Message, Rule } from '@made-open/sdk';
const contacts: Person[] = await client.getContacts();
const firstContact: Person = contacts[0];
console.log(firstContact.name);
console.log(firstContact.emails);
The CreateRuleInput type for createRule is also exported:
import type { CreateRuleInput } from '@made-open/sdk';
Configuration Reference
| Option | Type | Required | Description |
|---|---|---|---|
baseUrl | string | Yes | Base URL of the Made Open hub, e.g. https://your-hub.example.com |
apiKey | string | No | API key starting with mk_. Use for server-side or trusted apps. |
accessToken | string | No | OAuth access token for user-scoped requests. |
Provide either apiKey or accessToken, not both. If neither is provided, requests are sent unauthenticated (only public endpoints will succeed).
Further Reading
- Deployment — Set up a production hub to point the SDK at
- Event Catalog — All canonical NATS events and their payload shapes
- Rules Engine — JSON AST reference for
createRuleinputs - API Docs — Interactive Swagger UI (available when hub is running locally)