Dashboard SDK
Dashboard SDK¶
The Dashboard SDK is a monorepo of separate npm packages that work together to build Huitzo Dashboards that run inside Hub. The core is framework-agnostic; React bindings are layered on top.
Packages¶
| Package | npm Name | Provides |
|---|---|---|
| Core | @huitzo/dashboard-sdk |
Auth client, API client, types, error classes. Framework-agnostic. No DOM, no framework imports. |
| React | @huitzo/dashboard-sdk-react |
Hooks (useCommand, useHuitzo, useRealtime, useHubNavigation, etc.) and components (HuitzoProvider). Depends on core as peer. |
Installation¶
# Core SDK (framework-agnostic)
npm install @huitzo/dashboard-sdk
# React hooks and components
npm install @huitzo/dashboard-sdk @huitzo/dashboard-sdk-react
Quick Start¶
Core Client (Framework-Agnostic)¶
The core package works in any JavaScript/TypeScript environment—Node, Deno, or browser—without React or any framework:
import { HuitzoClient } from '@huitzo/dashboard-sdk';
const huitzo = new HuitzoClient({
apiUrl: 'https://huitzo.ai', // Optional, defaults to current origin
});
// Execute a command
const result = await huitzo.commands.execute('@acme/claims/process-claim', {
claimId: 'CLM-12345'
});
console.log(result);
React (Recommended for Hub Dashboards)¶
For React dashboards running inside Hub, pass the context object from mount() to HuitzoProvider:
# pseudocode — conceptual React usage
import type { HuitzoMountContext } from '@huitzo/dashboard-sdk-react';
import { HuitzoProvider, useCommand } from '@huitzo/dashboard-sdk-react';
// 1. Dashboard entry point receives context from Hub's mount()
export function mount(container: HTMLElement, context: HuitzoMountContext) {
const root = createRoot(container);
root.render(
<HuitzoProvider context={context}>
<ClaimsList />
</HuitzoProvider>
);
}
// 2. Use hooks in your components
function ClaimsList() {
const { execute, data, loading, error } = useCommand('@acme/claims/list-claims');
useEffect(() => { execute({ status: 'pending' }) }, []);
if (loading) return <Spinner />;
if (error) return <Error message={error.message} />;
return <ul>{data?.claims.map(claim => <li key={claim.id}>{claim.title}</li>)}</ul>;
}
Core Client¶
The HuitzoClient is the foundation of the SDK. It handles authentication and API calls. The core package uses openapi-fetch for typed HTTP requests against the Huitzo REST API.
Initialization¶
# pseudocode
import { HuitzoClient } from '@huitzo/dashboard-sdk';
// Basic initialization (uses current origin)
const huitzo = new HuitzoClient();
// With options
const huitzo = new HuitzoClient({
apiUrl: 'https://huitzo.ai',
timeout: 60000,
onAuthError: (error) => { /* redirect to login */ },
});
Configuration Options¶
| Option | Type | Default | Description |
|---|---|---|---|
apiUrl |
string | Current origin | Huitzo API base URL |
timeout |
number | 60000 |
Request timeout (ms) |
onAuthError |
function | - | Called when auth fails (receives Error) |
Sub-Objects¶
| Sub-object | Class | Description |
|---|---|---|
huitzo.auth |
AuthApi |
Login, register, refresh, logout, API key |
huitzo.commands |
CommandsApi |
Execute and list Intelligence Pack commands |
huitzo.packs |
PacksApi |
List and manage packs |
huitzo.account |
AccountApi |
Account mode checks, developer mode |
huitzo.organizations |
OrganizationsApi |
Organization and member management |
huitzo.health |
HealthApi |
Liveness, readiness, and integration probes |
Authentication¶
The SDK handles authentication via email/password login, token refresh, or API key. When running inside Hub, the HuitzoProvider syncs the JWT from the mount context automatically.
# pseudocode
// Email/password login
await huitzo.auth.login('[email protected]', 'password');
// Register a new account
await huitzo.auth.register('[email protected]', 'password', 'acme', 'access_code');
// API key (for development)
huitzo.auth.setApiKey('sk_...');
// Check auth status
const isAuthenticated = huitzo.auth.isAuthenticated();
const user = await huitzo.auth.getUser();
// Logout
await huitzo.auth.logout();
Commands API¶
Execute Intelligence Pack commands from your dashboard.
Execute Command¶
// Basic execution
const result = await huitzo.commands.execute('@acme/claims/process-claim', {
claimId: 'CLM-12345',
priority: 'high'
});
// With options
const result = await huitzo.commands.execute(
'@acme/claims/process-claim',
{ claimId: 'CLM-12345' },
{
timeout: 60000, // Override timeout
signal: controller.signal, // AbortController signal
}
);
Command Response¶
# pseudocode
interface CommandResult<T> {
result: T; // The command's return value
execution: {
duration_ms: number; // Server-side execution time
status: string; // e.g. "success"
};
}
// Type-safe usage
interface ClaimResult {
id: string;
status: string;
processedAt: string;
}
const result = await huitzo.commands.execute<ClaimResult>(
'@acme/claims/process-claim',
{ claimId: 'CLM-12345' }
);
console.log(result.result.status); // "pending" | "approved" | ...
console.log(result.execution.duration_ms);
CommandResult<T>is the unwrapped payload. The HTTP envelope fields (success,correlationId, timestamps) are consumed by the client and surfaced on errors viaHuitzoError.correlationId, not on successful responses.
List Available Commands¶
# pseudocode
// List all commands accessible to the current tenant
const all = await huitzo.commands.list();
// Filter by pack namespace (client-side filter over the full list)
const claims = await huitzo.commands.list({ pack: 'claims' });
// Get details for one command
const command = await huitzo.commands.get('@acme/claims/process-claim');
console.log(command.description);
console.log(command.input_schema);
CommandInfo.namespace is the pack namespace (e.g. "claims"), and
CommandInfo.name is the command name within that pack. The pack filter
matches commands whose namespace is exactly equal to the value passed.
Check Pack Availability¶
Pack-availability checks are cached in React via the usePacks hook, which
exposes an isInstalled(packId) helper backed by the provider's cached list:
# pseudocode
import { usePacks } from '@huitzo/dashboard-sdk-react';
function ClaimsFeature() {
const { isInstalled, loading } = usePacks();
if (loading) return null;
if (!isInstalled('@acme/claims-processor')) return <UpsellBanner />;
return <ClaimsPanel />;
}
In framework-agnostic code, query the list directly:
# pseudocode
const packs = await huitzo.packs.list();
const isInstalled = packs.some(p => p.id === '@acme/claims-processor');
Real-Time API¶
Status: Planned (not yet implemented). The
huitzo.realtimesub-object does not exist in the current SDK. The hooksuseRealtimeanduseConnectionStatusare exported as stubs that throwHuitzoErroron invocation. This section describes the planned API.
Subscribe to real-time events via WebSocket.
Subscribe to Events (Planned)¶
# pseudocode — planned API
const unsubscribe = huitzo.realtime.subscribe('claims:updated', {
onMessage: (event) => { console.log('Claim updated:', event.data) },
onError: (error) => { console.error('Subscription error:', error) },
});
unsubscribe();
Event Types (Planned)¶
| Event | Description | Payload |
|---|---|---|
{pack}:created |
Item created | { id, type, data } |
{pack}:updated |
Item updated | { id, type, changes } |
{pack}:deleted |
Item deleted | { id, type } |
command:started |
Command execution started | { correlationId, command } |
command:completed |
Command execution completed | { correlationId, result } |
command:failed |
Command execution failed | { correlationId, error } |
Connection Management (Planned)¶
# pseudocode — planned API
const status = huitzo.realtime.getStatus(); // 'connected' | 'connecting' | 'disconnected'
await huitzo.realtime.connect();
huitzo.realtime.disconnect();
huitzo.realtime.on('connected', () => {});
huitzo.realtime.on('disconnected', () => {});
React Integration¶
The React integration provides hooks and components for building dashboards with React.
HuitzoProvider¶
Wrap your application with HuitzoProvider to enable hooks. When running inside Hub, pass the context object received from mount():
// Dashboard entry point (src/main.tsx)
import { createRoot } from 'react-dom/client';
import type { HuitzoContext } from '@huitzo/dashboard-sdk';
import { HuitzoProvider } from '@huitzo/dashboard-sdk-react';
export function mount(container: HTMLElement, context: HuitzoContext) {
const root = createRoot(container);
root.render(
<HuitzoProvider context={context}>
<Dashboard />
</HuitzoProvider>
);
}
Context Integration¶
When a context object is provided (passed from Hub's mount() call), the provider:
- Uses Hub's shared JWT session via context.token
- Enables Hub navigation hooks via context.navigate
- Provides user info and dashboard metadata
- Enables the Hub event bus for cross-boundary communication
See Dashboard Loading Architecture — HuitzoContext for the full context shape.
useHuitzo¶
Access the Huitzo client, auth state, mount context, and provider-level init state.
Returns: { client, user, isAuthenticated, initError, packs, mountContext, isPackInstalled }
| Field | Type | Description |
|---|---|---|
client |
HuitzoClient |
The client instance created by the provider. |
user |
UserInfo \| null |
The currently authenticated user, or null. |
isAuthenticated |
boolean |
Whether the client holds valid auth tokens. |
initError |
Error \| null |
Error from the provider's initial data fetch (user or packs). 401s do not appear here — they trigger onAuthError instead. |
packs |
PackInfo[] |
Cached list of installed packs from the provider's initial fetch. |
mountContext |
HuitzoMountContext |
The vanilla JS context object Hub passed to mount(). Used by Hub-specific hooks. |
isPackInstalled |
(packId: string) => boolean |
Membership check against the cached packs list. |
# pseudocode
import { useHuitzo } from '@huitzo/dashboard-sdk-react';
function UserInfo() {
const { client, user, isAuthenticated, initError, isPackInstalled } = useHuitzo();
if (initError) return <ErrorBanner error={initError} />;
if (!isAuthenticated) return <LoginButton />;
return (
<div>
<p>Welcome, {user.email}</p>
{isPackInstalled('@huitzo/analytics') && <AnalyticsWidget />}
</div>
);
}
useCommand¶
Execute commands with automatic state management:
import { useCommand } from '@huitzo/dashboard-sdk-react';
interface Claim {
id: string;
title: string;
status: string;
}
interface ListClaimsResult {
claims: Claim[];
total: number;
}
function ClaimsList() {
const {
execute,
data,
loading,
error,
reset
} = useCommand<ListClaimsResult>('@acme/claims/list-claims');
// Execute on mount
useEffect(() => {
execute({ status: 'pending', limit: 20 });
}, []);
// Execute with different params
const handleFilter = (status: string) => {
execute({ status, limit: 20 });
};
if (loading) return <Skeleton count={5} />;
if (error) return <ErrorMessage error={error} onRetry={() => execute()} />;
return (
<div>
<FilterBar onFilter={handleFilter} />
<ul>
{data?.claims.map(claim => (
<ClaimItem key={claim.id} claim={claim} />
))}
</ul>
<p>Total: {data?.total}</p>
</div>
);
}
useCommand Options¶
Returns: { execute, data, loading, error, reset, status, isIdle, isSuccess, isError }
# pseudocode
const {
execute,
data,
loading,
error,
reset,
status, // 'idle' | 'loading' | 'success' | 'error'
isIdle,
isSuccess,
isError
} = useCommand('@acme/claims/process', {
// Execute immediately with these args
initialArgs: { claimId: 'CLM-123' },
// Callbacks
onSuccess: (data) => { toast.success('Claim processed!') },
onError: (error) => { toast.error(error.message) },
// Cache result for 5 seconds (keyed by args)
cacheTime: 5000,
});
useRealtime¶
Status: Stub — throws
HuitzoErroron invocation. Depends onclient.realtimewhich is not yet implemented in the core SDK.
Will subscribe to real-time events with automatic cleanup on unmount. Planned signature:
# pseudocode — planned API
useRealtime('claims:updated', (event) => { /* handle event */ });
useRealtime(['claims:created', 'claims:deleted'], (event) => { /* handle */ });
useConnectionStatus¶
Status: Stub — throws
HuitzoErroron invocation. Depends onclient.realtimewhich is not yet implemented in the core SDK.
Will monitor WebSocket connection. Planned return: { status, isConnected, reconnect }
usePacks¶
Query installed packs.
Returns: { packs, loading, error, isInstalled, refetch }
# pseudocode
import { usePacks } from '@huitzo/dashboard-sdk-react';
function PackList() {
const { packs, loading, error, isInstalled, refetch } = usePacks();
if (loading) return <p>Loading packs...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<ul>
{packs.map(pack => (
<li key={pack.name}>{pack.name} - {pack.version}</li>
))}
</ul>
);
}
Hub Integration¶
These hooks work when the dashboard runs inside Hub with a HuitzoContext provided via mount().
useHubNavigation¶
Navigate between Hub views.
Returns: { navigateToHub, navigateToDashboard, navigateToExplore, navigateToSettings, currentDashboard }
# pseudocode
import { useHubNavigation } from '@huitzo/dashboard-sdk-react';
function DashboardHeader() {
const {
navigateToHub, // Go to Hub home
navigateToDashboard, // Go to specific dashboard
navigateToExplore, // Go to marketplace
navigateToSettings, // Go to settings
currentDashboard, // Current dashboard slug
} = useHubNavigation();
return (
<header>
<button onClick={navigateToHub}>← Hub</button>
<h1>Claims Dashboard</h1>
<button onClick={() => navigateToDashboard('analytics')}>Open Analytics</button>
</header>
);
}
useHubContext¶
Access Hub environment context. Theme is reactive — updates when Hub toggles data-theme.
Returns: { apiUrl, dashboardSlug, user, theme }
# pseudocode
import { useHubContext } from '@huitzo/dashboard-sdk-react';
function MyComponent() {
const {
apiUrl, // Huitzo API base URL
dashboardSlug, // Current dashboard slug
user, // Current user from Hub session
theme, // Hub's current theme ('light' | 'dark')
} = useHubContext();
return (
<div className={theme}>
<span>Dashboard: {dashboardSlug}</span>
<span>User: {user?.email}</span>
</div>
);
}
useHubBreadcrumbs¶
Status: Stub — throws
HuitzoErroron invocation.
Will set breadcrumbs in Hub's header. Planned signature:
# pseudocode — planned API
useHubBreadcrumbs([
{ label: 'Claims Dashboard', href: '/d/claims-dashboard' },
{ label: 'Claim #123' },
]);
useHubActions¶
Status: Stub — throws
HuitzoErroron invocation.
Will trigger Hub-level UI actions (notifications, dialogs, settings). Planned return: { showNotification, showConfirmDialog, openSettings }
Components¶
The React package ships a small set of presentational components that match
the Huitzo Hub design system. They are framework-agnostic in intent — they
take typed props, accept either onClick or href for navigation, and apply
design tokens from @huitzo/dashboard-sdk-react/styles/tokens.css.
| Component | Source | Purpose |
|---|---|---|
DashboardTile |
packages/dashboard-sdk-react/src/components/DashboardTile.tsx |
Clickable tile with icon, name, description, optional pinned indicator, and a custom accent color. Pass either onClick or href (mutually exclusive). |
DashboardInfoBlock |
packages/dashboard-sdk-react/src/components/DashboardInfoBlock.tsx |
Card-like block displaying a title, description, optional icon, and optional metadata key-value pairs. Variant: default / elevated / outlined. |
DashboardTile¶
# pseudocode — conceptual usage
import { DashboardTile } from '@huitzo/dashboard-sdk-react';
<DashboardTile
name="Claims"
description="Process and review insurance claims"
icon="📋"
isPinned
color="var(--color-accent)"
href="/d/claims"
/>
The href form is preferred over onClick when navigating within Hub, so
the SDK can apply same-origin / https:// allowlisting (cross-origin and
http:// URLs are blocked by default; see allowInsecureHttp for local
dev).
DashboardInfoBlock¶
# pseudocode — conceptual usage
import { DashboardInfoBlock } from '@huitzo/dashboard-sdk-react';
<DashboardInfoBlock
title="Claims Dashboard"
description="Manage and process insurance claims"
icon="📋"
variant="elevated"
metadata={[
{ label: 'Version', value: '1.2.0' },
{ label: 'Last updated', value: '2 hours ago' },
]}
/>
Theming¶
Both components consume CSS custom properties from
@huitzo/dashboard-sdk-react/styles/tokens.css. Import that file once at
the top of the dashboard entry point. Dark mode is the default; switch
to light by setting data-theme="light" on a parent element.
Primitives¶
In addition to the React components above, the SDK ships a small set of
theme-aware utility CSS classes for building dashboards that match the
Hub visual language. They are delivered alongside the design tokens and
are picked up automatically when the dashboard imports
@huitzo/dashboard-sdk-react/styles.
Class names follow hz-{block}__{element}--{modifier} (BEM-ish). All
colors resolve through Hub --color-* tokens, so the primitives flip
between light and dark themes without any additional work. The terminal
chrome uses fixed dark hex values intentionally — a terminal should look
like a terminal regardless of the surrounding theme.
| Family | Purpose |
|---|---|
.hz-card, .hz-card--lg, .hz-card--md |
Elevated card with shadow + border |
.hz-card--accent, .hz-card--success, .hz-card--warning |
Card variants with accent border / top-border |
.hz-stat__number, .hz-stat__number--success, .hz-stat__number--warning |
Iconic numeric display, clamp(3.75rem, 8vw, 6rem) |
.hz-rail, .hz-step, .hz-step__badge |
Numbered onboarding rail with vertical gradient line |
.hz-terminal, .hz-terminal__header, .hz-terminal__dots, .hz-terminal__dot, .hz-terminal__label |
Terminal block chrome (traffic-light dots + label) |
.hz-terminal__body |
Terminal command line (light text on dark bg, monospace) |
.hz-terminal__prompt |
Green $ prompt prefix; pair with .hz-terminal__body |
.hz-terminal__output |
Expected output below a dashed divider |
.hz-terminal__copy, .hz-terminal__copy--ok |
Copy button with success state |
.hz-btn, .hz-btn--primary, .hz-btn--secondary, .hz-btn--ghost |
Standardized buttons |
.hz-eyebrow, .hz-eyebrow--accent |
Uppercase section labels |
.hz-kbd |
Inline keyboard chip |
.hz-code |
Inline code |
Example: numbered onboarding step + terminal block¶
# pseudocode — conceptual usage
<div className="hz-rail">
<section className="hz-step hz-card hz-card--lg">
<span className="hz-step__badge" aria-hidden>1</span>
<h3>Run a pack</h3>
<div className="hz-terminal">
<div className="hz-terminal__header">
<span className="hz-terminal__dots">
<span className="hz-terminal__dot" />
<span className="hz-terminal__dot" />
<span className="hz-terminal__dot" />
</span>
<span className="hz-terminal__label">Terminal</span>
<button className="hz-terminal__copy">Copy</button>
</div>
<pre className="hz-terminal__body">
<span className="hz-terminal__prompt">$</span>
huitzo run @yc/yc-weather-demo/current-weather --city san-francisco
</pre>
<pre className="hz-terminal__output">
→ {`{"city": "san-francisco", "temp_c": 14.2}`}
</pre>
</div>
</section>
</div>
When to use what¶
- Use a
DashboardTile/DashboardInfoBlockcomponent when the unit is a discrete reusable thing (a tile in a grid, an info card with metadata). - Use
hz-*primitives when you are composing a page-level layout (hero + onboarding rail + terminal block + CTA). They are layout-free by design — bring your own grid / flex.
The primitives intentionally do not impose display: flex or grid
layouts — combine them with whatever layout system your dashboard uses
(CSS grid, Tailwind utilities, flexbox).
Error Handling¶
Error Hierarchy¶
All SDK errors extend HuitzoError and carry structured metadata:
| Class | HTTP Status | Error Code | Description |
|---|---|---|---|
AuthenticationError |
401 | AUTHENTICATION_FAILED |
Missing or invalid credentials |
AuthorizationError |
403 | PERMISSION_DENIED |
Insufficient permissions |
NotFoundError |
404 | NOT_FOUND |
Resource does not exist |
ValidationError |
400/422 | VALIDATION_FAILED |
Input validation failure |
TimeoutError |
408 | TIMEOUT |
Request timed out |
RateLimitError |
429 | RATE_LIMITED |
Rate limit exceeded (retryAfter field) |
CommandError |
varies | COMMAND_ERROR |
Command execution failure |
NetworkError |
- | NETWORK_ERROR |
DNS failure, offline, CORS |
InternalError |
500 | INTERNAL_ERROR |
Unexpected server error |
IntegrationError |
502 | INTEGRATION_ERROR |
External integration failure |
ServiceUnavailableError |
503 | SERVICE_UNAVAILABLE |
Temporary unavailability |
Error Properties¶
Every HuitzoError instance carries:
| Property | Type | Description |
|---|---|---|
code |
ErrorCode |
Machine-readable error code |
statusCode |
number \| undefined |
HTTP status (undefined for network errors) |
details |
unknown |
Structured context (field errors, etc.) |
correlationId |
string \| undefined |
Request ID for debugging |
timestamp |
string \| undefined |
Server ISO 8601 timestamp |
# pseudocode
import {
HuitzoError,
AuthenticationError,
ValidationError,
RateLimitError,
NetworkError
} from '@huitzo/dashboard-sdk';
try {
await huitzo.commands.execute('@acme/claims/process', args);
} catch (error) {
if (error instanceof AuthenticationError) {
// 401 — redirect to login
} else if (error instanceof ValidationError) {
// 400/422 — field errors in error.details
} else if (error instanceof RateLimitError) {
// 429 — retry after error.retryAfter seconds
} else if (error instanceof NetworkError) {
// DNS failure, offline, etc.
} else if (error instanceof HuitzoError) {
// Catch-all for any SDK error
console.error(error.code, error.message);
}
}
React Error Handling¶
# pseudocode
function ClaimProcessor() {
const { execute, error, isError } = useCommand('@acme/claims/process');
if (isError) {
if (error instanceof ValidationError) {
return <ValidationErrors errors={error.details} />;
}
if (error instanceof AuthorizationError) {
return <PermissionDenied />;
}
return <GenericError error={error} />;
}
}
TypeScript Support¶
The SDK is written in TypeScript and provides full type definitions.
Command Types¶
// Define your command types
interface ProcessClaimArgs {
claimId: string;
priority?: 'low' | 'medium' | 'high';
}
interface ProcessClaimResult {
id: string;
status: 'approved' | 'denied' | 'pending';
processedAt: string;
reviewer?: string;
}
// Use with execute
const result = await huitzo.commands.execute<ProcessClaimResult>(
'@acme/claims/process',
{ claimId: 'CLM-123', priority: 'high' } satisfies ProcessClaimArgs
);
// result.result is typed as ProcessClaimResult
React Hook Types¶
# pseudocode
// useCommand is generic
const { data } = useCommand<ListClaimsResult>('@acme/claims/list');
// data is ListClaimsResult | null
Best Practices¶
1. Handle Loading States¶
function ClaimDetails({ id }: { id: string }) {
const { execute, data, loading, error } = useCommand('@acme/claims/get');
useEffect(() => {
execute({ claimId: id });
}, [id]);
// Always handle all states
if (loading) return <Skeleton />;
if (error) return <ErrorDisplay error={error} />;
if (!data) return null;
return <ClaimCard claim={data} />;
}
2. Optimistic Updates¶
function ClaimActions({ claim }: { claim: Claim }) {
const [optimisticStatus, setOptimisticStatus] = useState(claim.status);
const { execute } = useCommand('@acme/claims/update-status');
const handleApprove = async () => {
setOptimisticStatus('approved'); // Optimistic update
try {
await execute({ claimId: claim.id, status: 'approved' });
} catch (error) {
setOptimisticStatus(claim.status); // Revert on error
toast.error('Failed to approve claim');
}
};
return (
<StatusBadge status={optimisticStatus} />
);
}
3. Debounce Search¶
function ClaimSearch() {
const [query, setQuery] = useState('');
const { execute, data, loading } = useCommand('@acme/claims/search');
// Debounce search
useEffect(() => {
const timer = setTimeout(() => {
if (query.length >= 2) {
execute({ query });
}
}, 300);
return () => clearTimeout(timer);
}, [query]);
return (
<div>
<input value={query} onChange={e => setQuery(e.target.value)} />
{loading ? <Spinner /> : <Results data={data} />}
</div>
);
}
4. Clean Up Subscriptions (Planned — Real-Time API)¶
Once useRealtime is implemented, it will automatically clean up on unmount:
# pseudocode — planned API
useRealtime('claims:created', (event) => {
setClaims(prev => [event.data, ...prev]);
});
Related Documentation¶
- Hub Overview - Hub architecture and concepts
- Hub Navigation - URL structure and routing
- Dashboard Overview - Dashboard Framework concepts
- Dashboard Manifest - Configuration reference
- Deployment Guide - Hosting options
- REST API Reference - Underlying API