SynapSynapDocs

SDK first query

Install the typed SDK, create a client for your pod and read your first entities.

For BuildersSDKOpen-source podSynap CloudReviewed 2026-10-20

@synap-core/sdk is a typed tRPC client for a Synap pod. Every procedure the pod exposes is available with autocomplete and compile-time checks, because the client is typed with the pod's own router type.

The SDK calls the pod's /trpc endpoint, which accepts only a sign-in session: a person's session cookie in the browser, or their session token anywhere else. It is for apps that act as a signed-in person. It does not take API keys: for a script, an integration or an agent that holds an API key, use the Hub REST API instead.

What you need

  • A pod: hosted or self-hosted.
  • An account on it you can sign in with.
  • The ID of the workspace you want to read.

1. Install

npm install @synap-core/sdk

For a React app, install the React bindings instead; they include the SDK:

npm install @synap-core/react

2. Sign in and create a client

In the browser, on a subdomain of the pod's domain (for example app.example.com next to pod.example.com) where the person is already signed in, pass no credential: every request is sent with credentials: "include", so the browser's session cookie authenticates it.

import { createSynapClient } from "@synap-core/sdk";

const synap = createSynapClient({
  podUrl: "https://pod.example.com", // no trailing slash, no /trpc
  workspaceId,
});

Anywhere else (a server, a script, a desktop or mobile app), sign in once to get a session token, and pass it as sessionToken. A pod with password sign-in issues one through the native login flow of its sign-in service:

FLOW=$(curl -fsS https://pod.example.com/.ory/kratos/public/self-service/login/api | jq -r .id)
curl -fsS -X POST "https://pod.example.com/.ory/kratos/public/self-service/login?flow=$FLOW" \
  -H "Content-Type: application/json" \
  -d '{"method": "password", "identifier": "you@example.com", "password": "<your password>"}' \
  | jq -r .session_token
const synap = createSynapClient({
  podUrl: "https://pod.example.com",
  sessionToken: process.env.SYNAP_SESSION_TOKEN,
  workspaceId: process.env.SYNAP_WORKSPACE_ID!,
  onAuthError: () => {
    // 401: the session expired or was revoked. Sign in again.
  },
});

A session lasts 30 days and is not extended by use; treat the token like a password.

OptionMeaning
podUrlBase URL of the pod. The client calls <podUrl>/trpc.
sessionTokenA sign-in session token, sent as x-session-token. Leave it out to use the browser's session cookie.
workspaceIdSent as x-workspace-id to scope requests. Pass "" for procedures that need no workspace.
extraHeadersAdded to every request.
onAuthErrorCalled on 401. The client then rejects with a clean tRPC error instead of a parse error.

The type also has an apiKey option. It is only for the desktop app's local pod; a pod refuses API keys (synap_user_…, synap_hub_…) on /trpc with 401.

Calls made in the same tick are batched into one HTTP request, and values are encoded with superjson, which is why plain HTTP clients should use the Hub REST API instead of /trpc.

3. Read entities

const { items, pagination } = await synap.entities.list.query({ limit: 20 });

for (const entity of items) {
  console.log(entity.id, entity.title);
}

entities.list takes limit (1 to 1000, default 50) and offset, and filters such as profileSlug (one kind, for example "task") and facetSlug (one role). With a workspace set, it returns that workspace's entities.

4. Write, and expect proposals

const result = await synap.entities.create.mutate({
  profileSlug: "note",
  title: "My first note",
});

Writes go through the pod's governance. Depending on who the key belongs to and the rules you set, a write is applied directly or comes back as a proposal (status: "proposed" with a proposalId) that a person approves. Handle both. See Validation policy.

React

import { SynapProvider, useSynap } from "@synap-core/react";

export function App() {
  return (
    <SynapProvider podUrl="https://pod.example.com" workspaceId={workspaceId}>
      <Entities />
    </SynapProvider>
  );
}

function Entities() {
  const { data } = useSynap.entities.list.useQuery({ limit: 20 });
  return <ul>{data?.items.map((e) => <li key={e.id}>{e.title}</li>)}</ul>;
}

useSynap is the tRPC React client itself, not a function: write useSynap.entities.list.useQuery(…), not useSynap(). SynapProvider takes the same options as createSynapClient (here, the signed-in person's session cookie; pass sessionToken outside the browser), plus an optional queryClient to share an existing TanStack Query client.

@synap-core/react also exports ready-made RouterInputs and RouterOutputs types, useSynapContext (the provider's podUrl and workspaceId), and the hooks useEntity, useEntities, useLinks and useView.

Package reference

PackageExports
@synap-core/sdkcreateSynapClient, and the types SynapClient, SynapClientOptions and AppRouter
@synap-core/reactSynapProvider, useSynap, useSynapContext, useEntity, useEntities, useLinks, useView, and the types RouterInputs, RouterOutputs, SynapProviderProps, AppRouter

Both packages ship ES modules and CommonJS with TypeScript declarations, under the MIT license. The router types come from @synap-core/api-types, a dependency, so the client's types follow the version of that package you install. Use only what the package root exports: other subpaths are not part of the published API.

Scripts and agents with an API key

An API key works on the Hub REST API under /api/hub, not on /trpc. From TypeScript, @synap-core/hub-rest-client is a zero-dependency client for it:

npm install @synap-core/hub-rest-client
import { HubRestClient } from "@synap-core/hub-rest-client";

const hub = new HubRestClient({
  podUrl: "https://pod.example.com",
  apiKey: process.env.SYNAP_API_KEY!,
  workspaceId: process.env.SYNAP_WORKSPACE_ID,
});

const entities = await hub.getRecentEntities({ profileSlug: "task", limit: 20 });

From any other language, call the same routes over HTTP with Authorization: Bearer <key>:

curl "https://pod.example.com/api/hub/entities?profileSlug=task&limit=20" \
  -H "Authorization: Bearer $SYNAP_API_KEY"

Every route is in the Hub REST API reference.

On this page