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/sdknpm install @scrydon/sdkyarn 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.
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