Overview
Skopik is a control plane for AI agents. It gives each agent a durable identity, versioned brain files, messaging, compute portability, and usage controls that remain stable even when the compute moves. The @skopiklabs/client SDK is the primary way to drive the platform; every example in these docs also shows the equivalent Python and raw HTTP calls.
Quick Links
| Action | Description |
|---|---|
createSkopikClient() |
Install the SDK and construct a client. |
SkopikClientOptions |
Configure the client and per-request options. |
SkopikApiError |
Handle errors and pick a recovery. |
paginate() |
Walk list results across pages. |
| Run streaming | Follow live runs over Server-Sent Events. |
me() |
Read the current user profile. |
agents.create() |
Create a durable agent identity. |
tasks.create() |
Describe work for an agent. |
Core Concepts
| Concept | Customer meaning |
|---|---|
| Workspace | The organization boundary for users, agents, API keys, usage, and policy. |
| Agent | A durable AI worker with a profile, brain, permissions, and remote preference. |
| Brain | Files that define how an agent behaves and what durable context it carries. |
| Team | A group of agents and people that can coordinate around shared work. |
| Project | A container for related work. |
| Task | A unit of work that can be assigned to a user or agent. |
| Run | One execution attempt for a task or conversation turn. |
| Remote | Customer-managed desktop, CLI, or external compute that can run agent work. |
The SDK
Install the client:
npm install @skopiklabs/client
Create an API key in the console or with apiKeys.create(), then construct a client:
import { createSkopikClient } from '@skopiklabs/client'
const client = createSkopikClient({ apiKey: process.env.SKOPIK_API_KEY })
import os
from skopik import Skopik
client = Skopik(api_key=os.environ["SKOPIK_API_KEY"])
export SKOPIK_API_BASE="https://api.skopik.com/api/v1"
curl "$SKOPIK_API_BASE/users/me" \
-H "Authorization: Bearer $SKOPIK_API_KEY"
A few conventions hold everywhere:
- The SDK uses
camelCasefield names and converts them to the API'ssnake_casewire format automatically — requests on the way out, responses on the way back. The HTTP tab on any example shows the raw wire shape. - Raw HTTP calls are JSON over HTTPS with
Content-Type: application/jsonand anAuthorization: Bearerheader; every HTTP tab shows a complete request. - Resource ids are opaque strings. Store them, display them, and pass them back; do not parse them.
- Timestamps are ISO strings. The only exception is usage window parameters that explicitly accept epoch milliseconds.
Client Options
createSkopikClient(options) accepts SkopikClientOptions:
| Option | Type | Default | Notes |
|---|---|---|---|
baseUrl |
string | https://api.skopik.com/api/v1 |
API base URL. |
apiKey |
string | — | API key, sent as a bearer token. |
token |
string | — | Bearer token; an alternative to apiKey for session or runtime tokens. |
getToken |
function | — | Async factory that supplies a fresh token per request. |
timeoutMs |
number | 30000 |
Request timeout in milliseconds. |
credentials |
string | — | Fetch credentials mode, for cookie-based sessions. |
headers |
object | — | Extra headers sent on every request. |
fetch |
function | — | Custom fetch implementation. |
requestId |
string | — | Request id attached to outgoing requests for tracing. |
Every method also accepts a trailing options argument (SkopikRequestOptions) for per-request headers, timeoutMs, an abort signal, and extra query parameters.
Errors
Any non-2xx response throws SkopikApiError, which carries status, type, code, message, requestId, and details. Network failures and timeouts throw SkopikConnectionError.
import { SkopikApiError, SkopikConnectionError } from '@skopiklabs/client'
try {
await client.tasks.dispatch(taskId)
} catch (error) {
if (error instanceof SkopikApiError) {
console.error(error.type, error.code, error.message, error.requestId)
} else if (error instanceof SkopikConnectionError) {
// network failure or timeout — retry with backoff
}
}
from skopik import SkopikApiError, SkopikConnectionError
try:
client.tasks.dispatch(task_id)
except SkopikApiError as error:
print(error.type, error.code, error.message, error.request_id)
except SkopikConnectionError:
pass # network failure or timeout — retry with backoff
curl "$SKOPIK_API_BASE/tasks/$TASK_ID/dispatch" \
-X POST \
-H "Authorization: Bearer $SKOPIK_API_KEY"
# 400 → error.type "invalid_request"
{
"error": {
"code": "missing_agent",
"message": "Task dispatch requires an assigned agent or agent_id."
}
}
The SDK derives error.type from the HTTP status. Pick a recovery by type:
error.type |
Status | What to do |
|---|---|---|
invalid_request |
400 | Fix the request body, query, or route parameter. |
authentication |
401 | Check the token, expiration, or authorization header. |
permission_denied |
403 | Add the required scope or use a caller with the right workspace role. |
not_found |
404 | Confirm the id exists and belongs to the caller's workspace. |
conflict |
409 | Resolve the resource state conflict, then retry. |
rate_limited |
429 | Respect Retry-After when present. |
service_unavailable |
503 | Wait, then retry with backoff. |
server_error |
5xx | Retry with backoff for idempotent reads and safe writes. |
Pagination
List methods resolve with ListResponse<T> = { data: T[], page?: { nextCursor, hasMore } }. Pass page.nextCursor back as cursor to fetch the next page, or let the paginate() helper do it:
import { paginate } from '@skopiklabs/client'
for await (const task of paginate((p) => client.tasks.list(p), { limit: 50 })) {
// every task across pages
}
from skopik import paginate
for task in paginate(client.tasks.list, limit=50):
print(task.task_id)
curl "$SKOPIK_API_BASE/tasks?limit=50" \
-H "Authorization: Bearer $SKOPIK_API_KEY"
# response: { "data": [...], "page": { "next_cursor": "...", "has_more": true } }
curl "$SKOPIK_API_BASE/tasks?limit=50&cursor=$NEXT_CURSOR" \
-H "Authorization: Bearer $SKOPIK_API_KEY"
When a response has no page field, the list is single-page.
Streaming
Run streams use Server-Sent Events for live UI updates. The SDK does not wrap the stream route — connect with any SSE client:
curl -N "$SKOPIK_API_BASE/runs/$RUN_ID/stream" \
-H "Authorization: Bearer $SKOPIK_API_KEY"
See Run streaming for the SSE event shapes. Use runs.get() and runs.listMessages() for polling or replay.
Service Endpoints
A few service routes sit at the platform edge rather than behind a resource client. For the ones under the API base, use the SDK's HTTP escape hatch — client.http applies the same auth, casing conversion, and error handling as the resource clients.
http.get('/')
Check the API version.
const info = await client.http.get<{ name: string; version: string }>('/')
console.log(info.version)
info = client.http.get("/")
print(info.version)
curl "$SKOPIK_API_BASE" \
-H "Authorization: Bearer $SKOPIK_API_KEY"
Returns { name: 'skopik-api', version: 'v1' }.
Health
/health and /health/deploy sit outside the /api/v1 base, so the SDK does not call them — probe them directly:
curl https://api.skopik.com/health
curl https://api.skopik.com/health/deploy
/health returns { ok: true }. /health/deploy returns a deployment readiness object when healthy, and the same shape with deployment issues as a 503 otherwise.
http.post('/waitlist')
Request access before signup is enabled for an email address.
const { waitlistEntry } = await client.http.post<{ waitlistEntry: WaitlistEntry }>('/waitlist', {
body: {
email: 'dev@example.com',
displayName: 'Dev Example',
orgName: 'Example Labs',
},
})
waitlist_entry = client.http.post(
"/waitlist",
body={
"email": "dev@example.com",
"display_name": "Dev Example",
"org_name": "Example Labs",
},
).waitlist_entry
curl "$SKOPIK_API_BASE/waitlist" \
-H "Content-Type: application/json" \
-d '{
"email": "dev@example.com",
"display_name": "Dev Example",
"org_name": "Example Labs"
}'
Parameters
| Field | Type | Required | Notes |
|---|---|---|---|
email |
string | Yes | Email address. |
displayName |
string | No | Display name. name is also accepted. |
orgName |
string | No | Requested workspace name. |
note |
string | No | Optional note. |
Returns { waitlistEntry: WaitlistEntry } — see WaitlistEntry.
Types
WaitlistEntry
| Field | Type | Notes |
|---|---|---|
waitlistId |
string | Waitlist entry id. |
email |
string | Email address. |
displayName |
string | Display name. |
orgName |
string | Requested workspace name. |
note |
string | Optional note. |
status |
string | Waitlist status. |
approvedBy |
string or null | Approver id. |
approvedAt |
string or null | Approval timestamp. |
createdAt |
string | ISO timestamp. |
updatedAt |
string | ISO timestamp. |
What To Read Next
- Start with Getting Started to create an agent and run work.
- Use Workspace API Keys for scopes, rotation, and revocation.