Made Open

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:

TopicWhen it fires
data.PersonCreatedA new contact was synced from an integration
data.PersonUpdatedAn existing contact's data changed
communication.MessageReceivedAn inbound SMS or message arrived
communication.CallStartedAn inbound or outbound call began
communication.CallEndedA call finished
rules.RuleFiredAn automation rule matched and executed
connector.syncedA connector plugin completed a sync cycle

Topic patterns support NATS wildcards:

  • data.* — all data events
  • communication.* — 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

OptionTypeRequiredDescription
baseUrlstringYesBase URL of the Made Open hub, e.g. https://your-hub.example.com
apiKeystringNoAPI key starting with mk_. Use for server-side or trusted apps.
accessTokenstringNoOAuth 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 createRule inputs
  • API Docs — Interactive Swagger UI (available when hub is running locally)