npm package: @getnotisxyz/sdk
Description: SDK for Notis. 3 lines to connect your AI agent to your phone.
Latest version: 0.9.0

# @getnotisxyz/sdk

The official SDK for [Notis](https://www.getnotis.xyz) — the agent-to-human communication channel.

Let your AI agents send push notifications to your phone, receive replies, and maintain conversations. Zero dependencies. TypeScript-first.

## Install

```bash
npm install @getnotisxyz/sdk
```

## Quick Start

```ts
import { Notis } from '@getnotisxyz/sdk';

const notis = new Notis(); // reads NOTIS_API_KEY from env
await notis.send("I'm alive"); // push notification on your phone
```

Don't have an API key yet? Run `npx @getnotisxyz/cli login` then `npx @getnotisxyz/cli init` to create an agent and save a key to `.env` automatically.

## Getting Your API Key

**Recommended (CLI):**

```bash
npx @getnotisxyz/cli login    # authenticate via browser
npx @getnotisxyz/cli init     # name your agent, key saved to .env
```

**Manual (mobile app):**

1. Download the Notis app
2. Create an agent channel
3. Copy the API key (shown once)

## API

### `new Notis(config?)`

```ts
// Zero-config — reads NOTIS_API_KEY from env
const notis = new Notis();

// Explicit config
const notis = new Notis({
  apiKey: 'png_live_...', // your API key
  baseUrl: 'https://www.getnotis.xyz', // optional
});
```

### `notis.send(body)` — Send a message

```ts
// String shorthand
await notis.send("ETH broke $4,000");

// With options
await notis.send("ETH broke $4,000", { priority: 'urgent' });

// Full object form
await notis.send({
  title: 'Trade Alert',
  body: 'ETH broke $4,000. Want to take profit?',
  priority: 'high',
  actions: [
    { id: 'sell', label: 'Sell 50%' },
    { id: 'hold', label: 'Hold' },
  ],
});
```

**Priority levels:**

| Name | Value | Behavior |
|------|-------|----------|
| `lowest` | -2 | Silently delivered |
| `low` | -1 | Quiet delivery |
| `normal` | 0 | Standard push notification |
| `high` | 1 | Prominent notification |
| `urgent` | 2 | Bypasses quiet hours |

### `notis.listen(handler)` — Listen for human messages (Realtime)

Subscribes via Supabase Realtime WebSocket. Messages arrive in <100ms. Auto-falls back to polling if Realtime is unavailable.

Requires `@supabase/supabase-js` as a peer dependency:

```bash
npm install @supabase/supabase-js
```

```ts
const stop = await notis.listen(async (message, recentHistory) => {
  console.log(message.body); // "What's the portfolio looking like?"

  // recentHistory = last 10 messages (both directions)
  for (const turn of recentHistory) {
    console.log(`[${turn.role}] ${turn.body}`);
  }
});

stop(); // disconnect
```

Options:

```ts
await notis.listen(handler, {
  fallbackToPoll: true,   // fall back to polling on failure (default: true)
  pollIntervalMs: 5000,   // polling interval if falling back (default: 5000)
  onError: (err) => {},   // called on Realtime errors
});
```

### `notis.onMessage(handler)` — Listen for human messages (polling)

> **Deprecated.** Use `listen()` for instant delivery. `onMessage()` still works for environments where WebSockets are blocked.

```ts
const stop = notis.onMessage(async (message, recentHistory) => {
  console.log(message.body);
}, 5000); // poll every 5s

stop(); // stop polling
```

### `notis.onReply(handler)` — Listen for action button taps

```ts
const stop = notis.onReply(async (reply) => {
  console.log(reply.action);     // 'sell' | 'hold' | null
  console.log(reply.reply_text); // free-text reply or null
}, 5000);

stop();
```

### `notis.context()` — Get user preferences

```ts
const ctx = await notis.context();

console.log(ctx.channel);     // Channel name
console.log(ctx.preferences); // "Alert me on markets closing within 2 hours"
console.log(ctx.timezone);    // "America/Los_Angeles"
console.log(ctx.threshold);   // Minimum priority level
console.log(ctx.quiet_hours); // { start: "22:00", end: "08:00" }
```

### `notis.getHistory(options?)` — Load conversation history

```ts
const history = await notis.getHistory({ limit: 100 });

for (const msg of history.messages) {
  const who = msg.direction === 'agent_to_human' ? 'Agent' : 'Human';
  console.log(`[${who}] ${msg.body}`);
}
```

### Memory — `remember()`, `recall()`, `forget()`

```ts
// Store persistent memory (max 32KB, survives restarts)
await notis.remember({
  summary: 'User prefers conservative trades',
  preferences: { risk: 'low' },
});

// Read memory
const memory = await notis.recall();

// Clear memory
await notis.forget();
```

### `notis.ping()` — Health check

```ts
const alive = await notis.ping(); // true if API key is valid
```

## Two-Way Agent Example

```ts
import { Notis } from '@getnotisxyz/sdk';
import Anthropic from '@anthropic-ai/sdk';

const notis = new Notis();
const claude = new Anthropic();

await notis.listen(async (msg, recentHistory) => {
  const messages = recentHistory.map((turn) => ({
    role: turn.role === 'agent' ? 'assistant' as const : 'user' as const,
    content: turn.body,
  }));

  const response = await claude.messages.create({
    model: 'claude-sonnet-4-5-20250929',
    max_tokens: 1024,
    system: 'You are a helpful trading assistant.',
    messages,
  });

  const reply = response.content[0].type === 'text'
    ? response.content[0].text : 'Done.';

  await notis.send(reply);
});

console.log('Agent is listening...');
```

## REST API (No SDK)

```bash
# Send a message
curl -X POST https://www.getnotis.xyz/api/notify \
  -H "Authorization: Bearer png_live_..." \
  -H "Content-Type: application/json" \
  -d '{"body": "Something happened", "priority": 1}'

# Poll for human messages
curl https://www.getnotis.xyz/api/inbox \
  -H "Authorization: Bearer png_live_..."

# Get user context
curl https://www.getnotis.xyz/api/context \
  -H "Authorization: Bearer png_live_..."
```

## Upgrading from v0.5

The `Ping` and `PingClient` class names still work — no breaking changes:

```ts
// Old (still works)
import { Ping } from '@getnotisxyz/sdk';
const ping = new Ping({ apiKey: '...' });

// New (recommended)
import { Notis } from '@getnotisxyz/sdk';
const notis = new Notis(); // reads from env
```

Method renames (old names still work):
- `getContext()` → `context()`
- `getMemory()` → `recall()`
- `setMemory()` → `remember()`
- `clearMemory()` → `forget()`

## Error Handling

```ts
import { Notis, NotisError } from '@getnotisxyz/sdk';

try {
  await notis.send('Test');
} catch (err) {
  if (err instanceof NotisError) {
    console.error(err.message); // Human-readable error
    console.error(err.status);  // HTTP status code
  }
}
```

## License

MIT