Aller au contenu principal

Option E — SDK and direct API access

For developers building custom apps, LLM pipelines, or anything that needs full typed control over pod data.


What it is

Two access surfaces, same underlying API:

  • @synap/sdk — typed tRPC client generated from the live AppRouter. Full autocomplete, runtime validation, and tree-shaking. Use this for TypeScript/JavaScript projects.
  • Hub Protocol REST — the same access surface the pod's own AI uses internally. Raw HTTP, language-agnostic, no client library required.

Both give you direct read/write access to entities, documents, views, channels, search, and more. You manage the context you pass to your own LLM — the pod is your structured data backend.


Install

# TypeScript / Node.js
npm install @synap/sdk

# React apps (includes hooks + provider)
npm install @synap/react

Pattern E1 — Read data, inject into your own LLM

Pull structured data from your pod, build a prompt, write results back.

import { createSynapClient } from '@synap/sdk';
import Anthropic from '@anthropic-ai/sdk';

const synap = createSynapClient({
podUrl: 'https://YOUR_POD.synap.live',
apiKey: 'sk_live_...',
workspaceId: 'ws_...',
});

// 1. Read data from your pod
const tasks = await synap.entities.list.query({
profileSlug: 'task',
filter: { status: 'in-progress' },
limit: 50,
});

// 2. Feed into your own model
const claude = new Anthropic();
const response = await claude.messages.create({
model: 'claude-opus-4-5',
max_tokens: 1024,
messages: [
{
role: 'user',
content: `Here are my in-progress tasks:\n${JSON.stringify(tasks.items, null, 2)}\n\n${userQuestion}`,
},
],
});

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

// 3. Write the result back to your pod
await synap.entities.create.mutate({
profileSlug: 'note',
title: 'AI Summary — In-progress tasks',
properties: {
content: answer,
tags: ['ai-generated'],
},
});

Other useful reads

// Search full-text + semantic
const results = await synap.search.global.query({ q: 'Q3 budget projections' });

// Get a specific entity with all properties
const entity = await synap.entities.get.query({ id: 'ent_...' });

// List views
const views = await synap.views.list.query({ workspaceId: 'ws_...' });

// Read a document
const doc = await synap.documents.get.query({ entityId: 'ent_...' });

Pattern E2 — Hub Protocol REST

Hub Protocol REST is a language-agnostic HTTP API. Same capabilities as the SDK, no client library needed. Useful for Python pipelines, shell scripts, or any language without a Synap SDK.

# Search your pod
curl "https://YOUR_POD.synap.live/api/hub-protocol/search?q=contract+renewal&limit=10" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "x-workspace-id: ws_..."

Response:

{
"results": [
{
"id": "ent_abc",
"profileSlug": "deal",
"title": "Acme Corp — contract renewal",
"score": 0.94,
"excerpt": "...renewal deadline is March 31..."
}
],
"totalCount": 3
}
# Trigger AI on a channel
curl -X POST "https://YOUR_POD.synap.live/api/hub-protocol/channels/trigger-ai" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channelId": "ch_01hx...",
"workspaceId": "ws_...",
"message": "Draft a follow-up email to Acme about the renewal."
}'

Response:

{
"messageId": "msg_...",
"channelId": "ch_01hx...",
"status": "queued"
}

The AI response will be delivered as a message in that channel. Poll GET /api/hub-protocol/channels/:id/messages or subscribe via webhook to receive it.

Hub Protocol scopes

ScopeWhat it allows
hub-protocol.readSearch, entity reads, document reads, memory reads
hub-protocol.writeCreate/update entities, send messages, trigger AI

Pattern E3 — Compose C + D in one script

Call a skill to extract structured data, then chat with the result:

import { createSynapClient } from '@synap/sdk';

const POD_URL = 'https://YOUR_POD.synap.live';
const API_KEY = 'YOUR_API_KEY';

// Step 1: Invoke a skill to extract entities from raw text
const skillRes = await fetch(`${POD_URL}/api/external/skills/sk_01hx.../invoke`, {
method: 'POST',
headers: { Authorization: `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
input: { text: rawMeetingNotes, workspaceId: 'ws_...' },
}),
});
const { result: extracted } = await skillRes.json();
console.log('Extracted:', extracted.entitiesCreated);

// Step 2: Ask the AI to summarise and add context
const chatRes = await fetch(`${POD_URL}/api/external/chat/stream`, {
method: 'POST',
headers: { Authorization: `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
query: `I just added entities from a meeting. Summarise what needs follow-up and flag anything urgent.`,
}),
});

// Parse SSE stream
const reader = chatRes.body!.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
for (const line of decoder.decode(value, { stream: true }).split('\n')) {
if (!line.startsWith('data: ')) continue;
const frame = JSON.parse(line.slice(6));
if (frame.type === 'content') process.stdout.write(frame.content);
}
}

Required scopes per pattern

PatternRequired scopes
E1 — SDK readsread:entities, hub-protocol.read
E1 — SDK writesread:entities, write:entities
E2 — Hub Protocol readshub-protocol.read
E2 — Hub Protocol writeshub-protocol.read, hub-protocol.write
E3 — Compose C + Dskills.invoke, chat.stream
Full external accessread:entities, write:entities, skills.invoke, chat.stream, hub-protocol.read, hub-protocol.write

Type safety

The SDK exports input and output types for every procedure:

import type { RouterInputs, RouterOutputs } from '@synap/sdk';

type CreateInput = RouterInputs['entities']['create'];
// → { profileSlug: string; title: string; properties?: Record<string, unknown>; workspaceId?: string; ... }

type Entity = RouterOutputs['entities']['list']['items'][number];
// → { id: string; title: string; profileSlug: string; properties: Record<string, unknown>; createdAt: Date; ... }

React integration

// app.tsx
import { SynapProvider } from '@synap/react';

export default function App({ children }: { children: React.ReactNode }) {
return (
<SynapProvider
podUrl="https://YOUR_POD.synap.live"
apiKey="sk_live_..."
workspaceId="ws_..."
>
{children}
</SynapProvider>
);
}
// TaskList.tsx
import { useSynap } from '@synap/react';

export function TaskList() {
const { data, isLoading } = useSynap().entities.list.useQuery({
profileSlug: 'task',
filter: { status: 'in-progress' },
limit: 20,
});

if (isLoading) return <p>Loading…</p>;

return (
<ul>
{data?.items.map(task => (
<li key={task.id}>
<strong>{task.title}</strong>
<span>{task.properties.dueDate as string}</span>
</li>
))}
</ul>
);
}

The useSynap() hook returns the full typed tRPC client. All queries use TanStack Query under the hood — caching, refetching, and optimistic updates work out of the box.


Next steps