Overview
The/startup API lets AI agents (Claude, GPT, Cursor, etc.) drive Ekso’s first-run setup wizard — the same outcome as a human filling out the wizard form, but via two anonymous JSON endpoints. After the wizard succeeds the install has its first tenant, an admin user, the auth config, and (optionally) an AI provider — ready to log into.
Self-host means the agent is operating against the customer’s own install, not a hosted ekso.app endpoint. The customer has already:
- Acquired a license — ekso.app/get-started emails a Free-tier JWT (3 users, no credit card); paid tiers with unlimited users come from ekso.app/pricing via Stripe checkout. This is its own one-shot flow today; future agent automation for this step is on the roadmap.
- Stood up the install — Docker, behind whatever DNS the customer chose (e.g.
https://ekso.acme.com). See Install.
/startup wizard with the operator’s details (name, email, password, organization, public URL) plus the license JWT. The install bootstraps the tenant and the wizard URL closes (410 Gone) on success.
Endpoints
Both endpoints are anonymous (no auth header), live on the customer’s install URL, and are gated byStartupGuard middleware — they return 410 Gone once the install has at least one tenant. There is no email-verification step: the operator running the agent has direct host access to the freshly-bootstrapped install already, so abuse-prevention is solved by network reachability rather than email round-trips.
Step 1 (optional): Validate the public URL
Before posting the wizard, the agent can sanity-check the URL the customer plans to use. Permissive — format check only:{ "error": "Invalid URL format" } / "URL must be http:// or https://" / "URL must include a host".
Step 2: Run the wizard
/api/startup/* flips to 410 Gone — the install is configured. The agent should hand the operator the install URL and the email it was bootstrapped with so they can log in.
Request fields
| Field | Required | Notes |
|---|---|---|
name | Yes | Admin user’s full name. |
email | Yes | Admin user’s email. Receives any password-reset / future-notification mail. |
password | Yes | 8–64 characters. Stored hashed; never recoverable. |
organization | Yes | Tenant display name (e.g. “Acme Corp”). |
publicUrl | Yes | The PublicUrl the install is reachable at. Must match the host the agent is POSTing to. Used in outbound emails and OAuth redirect URIs. |
activation | Optional but recommended | License JWT — Free tier from ekso.app/get-started, paid tiers from ekso.app/pricing. Empty = install runs in Free-tier defaults until pasted later via Settings → Account. |
aiProvider | Optional | "" (disabled), "openai", or "anthropic". Sets up ConfigAI so vector embeddings during sample-data onboarding work without a separate Settings step. |
aiModel | Conditional | Required when aiProvider is set. Suggested defaults: gpt-5.5 (OpenAI), claude-sonnet-4-6 (Anthropic). |
aiKey | Conditional | Required when aiProvider is set. Empty key with non-empty provider is rejected (would silently no-op every AI call). |
Error responses
All failures return a structured error body:| HTTP Status | Cause |
|---|---|
400 | Invalid input — empty name/organization/publicUrl, malformed email, password too short/long, malformed PublicUrl, AI provider/key inconsistency. The error body’s error string identifies the specific failure. |
410 Gone | Install already configured. The wizard is closed and /api/startup/* is no longer reachable. The agent should hand the operator the existing login URL instead of trying again. |
429 | Rate limited (the Startup rate-limit policy gates the endpoint). Back off and retry. |
Example agent conversation
What the wizard actually does
POST /api/startup/go delegates to the IInstallSetup workhorse, which atomically:
- Creates the install-default
DataTenantwith the supplied organization name + public URL. - Creates the admin
DataUser(super admin, hashed password). - Seeds default groups, permissions, and the forms-auth config.
- Stores the license JWT in
DataTenant.Activation(verified later byLicenseProvider). - If
aiProvideris set, writesConfigAIso the next request sees AI enabled. - Enqueues a background sample-data onboarding job to populate the tenant with example items, fields, and processes.
IInstallConfigured.MarkConfigured() flips an in-process flag that StartupGuard reads to refuse subsequent wizard calls — making the bootstrap genuinely one-shot.
Discovery
For self-host, agents can discover the install’s onboarding API through the install itself once it’s reachable:/api/startup/urland/api/startup/go— the canonical paths, documented here. Both return 410 once configured, so an agent that pings these can also detect “this install is already set up” before prompting the operator for credentials.- OpenAPI —
https://ekso.acme.com/api/openapi/v1.json(the Backend serves the same spec the public docs render). - This page —
https://ekso.dev/guide/agent-onboardingis the public-facing reference.
llms.txt and agent-card.json discovery for the SaaS-era hosted onboarding endpoints (/api/bo/on/agent/*) are gone — those endpoints were removed when the BackOfficeService deleted in Lane G of the self-host pivot. A self-host install can publish its own /.well-known/agent-card.json advertising the /api/startup/* endpoints; this is on the roadmap.