TypeScript / JavaScript SDK
The official TypeScript / JavaScript client SDK for Scrydon — OAuth/PKCE auth and a typed surface for workflows, knowledge, storage, chat, and webhooks
@scrydon/sdk is the official client SDK for browser, TanStack Start / Next.js, and Node.js apps. It authenticates end-users through OAuth 2.0 with PKCE and exposes typed modules for workflows, chat, knowledge, storage, and webhooks, plus the platform data / ai / action SDK surface.
This SDK is for first-party apps acting on behalf of a user (OAuth flow). For server-to-server execution with a static API key — backends, cron jobs, CI — call the workflow API directly with x-api-key (see Execution).
Installation
bun add @scrydon/sdkQuick Start
import { ScrydonClient } from '@scrydon/sdk'
const client = new ScrydonClient({
baseUrl: 'https://scrydon.com',
clientId: 'your-oauth-client-id',
redirectUri: 'https://your-app.com/auth/callback',
})
// 1. Start the OAuth/PKCE flow — returns the authorization URL
const authUrl = await client.auth.signIn()
window.location.assign(authUrl)
// 2. On your callback page, exchange the code for a session
const session = await client.auth.handleCallback() // reads window.location by default
// 3. Call typed APIs
const result = await client.workflows.trigger({
workflowId: 'wf_…',
inputs: { message: 'Hello, world!' },
})
console.log(result.executionId, result.status, result.outputs)Authentication — OAuth 2.0 + PKCE
The SDK uses the PKCE flow so it's safe in browsers without a client secret. The verifier is held in sessionStorage (browser) or in-memory (other runtimes).
auth.signIn()
const authUrl = await client.auth.signIn()
// Redirect the user to authUrl. After consent they come back to redirectUri
// with ?code=… and ?state=… in the query string.auth.handleCallback(callbackUrl?)
// On the callback route — defaults to window.location.href
const session = await client.auth.handleCallback()
// session: { user: { id, email, name? }, accessToken, expiresAt }auth.getSession()
const session = await client.auth.getSession() // null if signed out / expiredauth.signOut()
await client.auth.signOut()auth.onAuthStateChange(cb)
const unsubscribe = client.auth.onAuthStateChange((session) => {
if (session) {
console.log('signed in as', session.user.email)
} else {
console.log('signed out')
}
})
// Later: unsubscribe()client.workflows
trigger(options) — synchronous
const result = await client.workflows.trigger({
workflowId: 'wf_abc',
inputs: { message: 'Hello' },
})
// result: { executionId, status, outputs? }POST /api/v1/workflows/{workflowId}/trigger. Best for short-running workflows that respond inline.
triggerAsync(options) — fire and poll
const { executionId } = await client.workflows.triggerAsync({
workflowId: 'wf_abc',
inputs: { message: 'Long task' },
})
// Poll for status when you're ready
const result = await client.workflows.getStatus(executionId)
// result: { executionId, status: 'running' | 'completed' | 'failed', outputs? }POST /api/v1/workflows/{workflowId}/trigger?async=true. Use for long-running workflows.
getStatus(executionId)
const result = await client.workflows.getStatus(executionId)GET /api/v1/workflows/executions/{executionId}.
client.chat
Streaming chat against a deployed chat surface.
for await (const chunk of client.chat.stream({
deploymentId: 'chat_abc',
message: 'Summarize Q3',
})) {
process.stdout.write(chunk)
}client.knowledge
const hits = await client.knowledge.query({
question: 'What is our refund policy?',
topK: 5,
})
// hits: Array<{ content, source, score }>
await client.knowledge.ingest({
documents: [file1, file2],
collection: 'policies',
})client.ontology
Map-ready geo layers projected from your ontology — one layer per object type
whose instances carry coordinates (latitude/lat, longitude/lng/lon).
Omit all options to discover every mappable type across your readable
ontologies; pass objectTypes to narrow.
const layers = await client.ontology.queryGeo({
objectTypes: ['Aircraft', 'MilitaryBase'], // optional — omit to discover
ontologySlug: 'my-domain', // optional — omit to search all
limit: 300, // max instances per type (1–1000)
})
// layers: Array<{ objectTypeSlug, label, features }>
// features: Array<{ id, lat, lng, label, properties }>Geo is a dedicated projection rather than a generic instance query: coordinate extraction, range validation, and binding traversal run server-side, so the client receives render-ready features.
client.storage
const { url } = await client.storage.upload(file, { path: 'reports/q3.pdf' })
const directLink = await client.storage.getUrl('reports/q3.pdf')client.webhooks
A simple in-process event emitter for delivering webhook payloads to subscribers in your app.
const off = client.webhooks.on('workflow.completed', (data) => {
console.log('workflow completed', data)
})
// Later: off()Platform SDK — client.data, client.ai, client.action
The client exposes the platform's typed ScrydonSDK:
client.data.knowledge.search(...),client.data.memex.query(...),client.data.storage.read(...),client.data.memory.add(...)— read / write the workspace data surfaceclient.ai.llm.complete(...)— capability-resolved LLM callsclient.action.workflow.execute(...),client.action.email.send(...),client.action.sms.send(...),client.action.functionExecute(...)— server-side actions
See @scrydon/sdk/types for the full set of platform types.
Discovering what a capability offers
client.integrations.capabilities.list(...) returns every provider your
organization has enabled for a capability, each with its selectable models:
const llm = await client.integrations.capabilities.list({ capability: "llm" });
llm.category; // "model" — selection descends to a model
llm.providers; // [{ productId, vendorId, name, models: [...], defaultModel? }]Models come back already merged: a vendor that declares a fixed catalogue and one that discovers deployments at runtime are indistinguishable in the response, so you never branch on which kind you are talking to.
The category tells you how the capability is chosen:
| Category | Meaning | models |
|---|---|---|
model | pick a provider and a model (LLM, embeddings, speech, OCR, image, video, moderation) | present |
general | pick a provider; there is nothing below it (web search) | absent |
Pass applyAllowlist: false to see the full set before your organization's
model allowlist is applied — useful when building a UI for editing that
allowlist. Pass refresh: true to bypass the discovery cache.
This differs from client.integrations.capabilities.resolve(...), which returns
the single target that would serve the capability right now. Use resolve to
learn what will run; use list to offer a choice.
Errors
import {
ScrydonAuthError,
ScrydonForbiddenError,
ScrydonRateLimitError,
ScrydonValidationError,
ScrydonServerError,
ScrydonError,
} from '@scrydon/sdk'
try {
await client.workflows.trigger({ workflowId: 'wf_abc' })
} catch (err) {
if (err instanceof ScrydonAuthError) {
// 401 — token expired or invalid; redirect to signIn()
} else if (err instanceof ScrydonForbiddenError) {
// 403 — user lacks permission for this workflow
} else if (err instanceof ScrydonRateLimitError) {
// 429 — err.retryAfter seconds
} else if (err instanceof ScrydonValidationError) {
// 400 — err.details has the Zod issues
} else if (err instanceof ScrydonServerError) {
// 5xx — retry with backoff
} else if (err instanceof ScrydonError) {
// Other 4xx — err.code, err.status
}
}React
@scrydon/sdk/react ships hooks for the common flows:
import { ScrydonProvider, useAuth, useWorkflow } from '@scrydon/sdk/react'
function App() {
return (
<ScrydonProvider config={{ baseUrl, clientId, redirectUri }}>
<Page />
</ScrydonProvider>
)
}
function Page() {
const { session, signIn, signOut } = useAuth()
const { trigger, result, loading, error } = useWorkflow('wf_abc')
if (!session) return <button onClick={signIn}>Sign in</button>
return (
<div>
<button onClick={() => trigger({ message: 'Hello' })} disabled={loading}>
Run workflow
</button>
{result && <pre>{JSON.stringify(result.outputs, null, 2)}</pre>}
</div>
)
}TanStack Start / Next.js callback route
// /auth/callback page
import { ScrydonClient } from '@scrydon/sdk'
const client = new ScrydonClient({
baseUrl: import.meta.env.VITE_SCRYDON_BASE_URL,
clientId: import.meta.env.VITE_SCRYDON_CLIENT_ID,
redirectUri: `${window.location.origin}/auth/callback`,
})
export function CallbackPage() {
useEffect(() => {
client.auth
.handleCallback()
.then(() => router.navigate({ to: '/' }))
.catch((err) => console.error('OAuth callback failed', err))
}, [])
return <p>Signing you in…</p>
}Backend / server-to-server
@scrydon/sdk is built around an end-user OAuth flow. For backend processes that need to execute workflows on behalf of the platform (cron, CI, internal services), call the API directly:
curl -X POST "https://scrydon.com/api/workflows/{workflowId}/execute" \
-H "x-api-key: $SCRYDON_API_KEY" \
-H "Content-Type: application/json" \
-d '{"input": "…"}'API keys are issued during the deploy flow (see Execution). They are the right tool for server contexts where there is no end user.
Requirements
- Node.js 18+ (for fetch + atob globals)
- TypeScript 5.0+ for full types
- A browser-like environment for the React module
License
Apache-2.0