Partner API
White-label OverSkill's app-generation engine inside your own product. Your editor is the surface; OverSkill is the headless engine underneath. Your platform holds one partner key server-side and provisions an isolated OverSkill workspace per creator; each creator's builds run under their own key, carry their own context, and are branded as yours.
How it works
The full lifecycle is four partner-scoped calls. The partner key never reaches the browser — it only provisions creators server-side.
partner key ──▶ POST /api/v1/partner/teams → creator team + creator key
creator key ──▶ POST /api/v1/generation_queue → job (context + brand + embed applied)
── GET …/:id/messages · GET …/:id/stream · webhook → live chat pane + push
── (build) → preview_url → iframe in your editor
creator key ──▶ POST /api/v1/managed_apps/:id/deploy → production_url + embed handoff
Authentication
Every partner call authenticates with an API key in the
X-API-Key header:
curl -H "X-API-Key: os_your_key_here" \
https://staging.overskill.com/api/v1/managed_apps
Scopes
Partner flows need apps:write (provision, deploy) and
generation:queue (generate) — both are granted by
read_write and full_access. read_only has neither.
| Scope | Grants |
|---|---|
read_only | apps:read, users:read, analytics:read, webhooks:read |
read_write | read_only + apps:write, users:write, generation:queue |
full_access | read_write + apps:delete, users:delete, webhooks:write |
Rate limits
Current enforced limits per API key:
| Operation | Limit |
|---|---|
| Queue generation | 20 / hour |
| Reads (GET) | 300 / minute |
| Writes (POST/PATCH/DELETE) | 60 / minute |
| Deploy | 10 / hour |
A 429 response includes a retry_after (seconds). Exceeding a
limit never fails a build already in flight.
POSTProvision a creator workspace
Map each of your creators to their own OverSkill team and API key. Call this with the partner key; store the returned creator key server-side.
Endpoint
POST https://staging.overskill.com/api/v1/partner/teams · requires apps:write
Request body
| Parameter | Type | Description |
|---|---|---|
name | string · required | The creator team's name. |
user_email | string | An existing OverSkill user to add as team admin. If provided but not found, the team is still created and a warning is returned. |
generate_api_key | boolean · default true | Mint a scoped key for this creator team. Pass false to skip. |
api_key_scope | string · default full_access | Scope of the minted key. |
Response (201 Created)
{
"team": { "id": 188, "name": "Priya's Baking Studio",
"subscription_tier": "free", "credit_tier": "tier_500",
"created_at": "2026-08-22T18:04:00Z" },
"user_added": true,
"warning": null,
"api_key": { "key": "os_...", "name": "...", "scope": "full_access",
"note": "Save this key now - it cannot be retrieved later" }
}
POSTQueue a generation
One call injects the creator's context, brands the app, opts it into embedding, and
starts the build. Call it with the creator key
(needs generation:queue + builder access).
Endpoint
POST https://staging.overskill.com/api/v1/generation_queue
Request body
| Parameter | Type | Description |
|---|---|---|
prompt | string · required | What to build (or the next edit). |
app_id | string | Omit to create a new app; include (team-scoped id) to iterate on an existing one. |
partner_context | string or object | The creator's world (courses, communities, calls, content, audience). ≤ 48 KB — larger is truncated, never blocks. Objects are rendered as a JSON block. Persisted on the app and reused across turns; injected as a bounded, cached layer framed as data, not instructions. |
partner_attribution | string or object | White-label badge. Object keys: partner, display_name, powered_by, show_attribution (defaults powered_by:"OverSkill", show_attribution:true). ≤ 2 KB. |
partner_frame_ancestors | string or array | Origins allowed to iframe the preview (comma/space/newline-separated, or an array). Each is sanitized; unusable entries are dropped, never blocks the build. |
callback_url | string (URL) | Webhook target for terminal events (see Webhooks). http:// accepted in dev only. |
metadata | object | Your correlation data, echoed back on webhooks. ≤ 4 KB serialized or it is dropped. |
ai_model, thinking_effort | string | Optional model + reasoning-effort overrides (low|medium|high|max|xhigh). |
Response (202 Accepted)
{
"job_id": "4ebc...",
"app_id": "OJzZVJ",
"message_id": 278,
"status": "queued",
"estimated_time_seconds": 45,
"status_url": ".../generation_queue/4ebc...",
"app_url": ".../account/apps/OJzZVJ/edit",
"partner_context": { "applied": true, "bytes": 1454, "truncated": false },
"partner_attribution": { "applied": true, "partner": "tagmango", "show_attribution": true },
"partner_frame_ancestors":{ "applied": true, "origins": ["https://app.tagmango.com"] }
}
Each partner_* echo object is null unless that param was applied
on this request. Every response also carries a next_steps hint object.
POSTFork a template (remix)
Start a build from a template or an existing app instead of a blank prompt.
Requires apps:write. This is clone-only (no credits spent):
it copies files, entity schema, custom endpoints, non-secret env vars, roles and
(when the source opts in) public seed data, then deploys a preview — it does not run
the AI. Iterate afterward with POST /generation_queue using the new
app_id.
POST https://staging.overskill.com/api/v1/managed_apps/remix
{
"source_app_id": "OJzZVJ", // any remixable app — a template or your own
"custom_name": "Priya's Portal", // optional; defaults to "<name> (Remix)"
"include_messages": "none" // optional: "none" (default) | "all", clamped to the source's ceiling
}
// 202 Accepted
{
"app_id": "Ab12Cd",
"source_app_id": "OJzZVJ",
"name": "Priya's Portal",
"status": "remixing",
"status_url": ".../managed_apps/Ab12Cd"
}
status is remixing until the clone finishes and a preview
deploys, then generated — poll status_url to learn when it's
ready. The source must be remixable (its creator's remix toggle on and the app built);
otherwise the call returns 422 with a reason.
GETWatch the build
Three ways to follow a build, all team-scoped (apps:read):
- Poll:
GET /generation_queue/:id→{ status, progress, message, app, started_at, completed_at }.status ∈ queued · processing · completed · failed · cancelled. - Chat-left pane:
GET /generation_queue/:id/messages(?limit=1–200, default 50) → a secret-redacted transcript with aflowtool timeline to render a faithful editor pane. - Stream (SSE):
GET /generation_queue/:id/stream—statusthenprogress/completed/failedevents (2 s cadence, 5 min timeout). - Cancel:
DELETE /generation_queue/:id(apps:write) →{ cancelled: true, job_id }.
Messages / flow shape
{ "messages": [
{ "role": "user", "content": "..." },
{ "role": "assistant", "status": "generating", "thinking_status": "Building...",
"content": "Design Direction: ...",
"flow": [ { "type": "message", "content": "Planning screens..." },
{ "type": "tools", "status": "complete",
"tools": [ { "name": "edit-file", "status": "complete" },
{ "name": "os-search", "status": "error" } ] } ] } ] }
POSTSteer & control a build
Beyond cancel, a running build can be paused, resumed, and steered — the same
controls the OverSkill editor has, so your editor can offer them too. All require
apps:write and act on the app's current generation.
- Pause:
POST /managed_apps/:id/pause→ the build pauses after its current step. Returns{ status: "pause_requested", pause_requested_at }. - Resume:
POST /managed_apps/:id/resume→ continues a paused build from where it left off. Returns{ status: "resumed" };422if the build isn't paused. - Follow up while running:
POST /managed_apps/:id/follow_up{ message, mode }—mode: "queue"(default) runs the message as its own turn after the current build finishes;mode: "steer"injects it into the running build's next step so the model adapts mid-flight. Requires an in-flight generation (otherwise422). Returns{ queued_message_id, delivery_mode, position, queue_length }.
Pause/resume and mid-flight steering are gated features; where a control isn't enabled for
your account the endpoint returns 403 (steer transparently falls back to
queue). Ask your OverSkill contact to enable them for your partner account.
POSTPreview & deploy
Preview: once the build produces a dist/ bundle the app has a
preview_url (a Cloudflare host). It is iframe-embeddable by the origins you
passed in partner_frame_ancestors.
Deploy
POST https://staging.overskill.com/api/v1/managed_apps/:id/deploy · requires apps:write · free
Refused with 422 (reason, fix_url) unless the app is
deployable — i.e. not a draft and it has a built dist/ bundle.
Response (202 Accepted)
{
"deployment_id": 129,
"status": "queued",
"message": "Production deployment queued",
"white_label": { "display_name": "TagMango", "powered_by": "OverSkill",
"show_attribution": true },
"embed": {
"preview_url": "https://preview-ojzzvj.overskill.app",
"production_url": "https://ojzzvj.overskill.app",
"frame_ancestors": ["https://app.tagmango.com"],
"embeddable": true,
"iframe_snippet": "<iframe src=\"...\" title=\"...\"></iframe>"
}
}
The same embed (and white_label) block is also on
GET /managed_apps/:id?detailed=true — the drop-in handoff for surfacing the
finished app back inside your platform. embed is null until the
app has a live URL; embeddable is true only when frame_ancestors
is non-empty.
Webhooks
Set callback_url on generate to receive a signed POST at each
terminal state — instead of polling.
| Event | When |
|---|---|
app.generation.completed | Build finished; includes credits_used + tokens_used. |
app.generation.failed | Error / retries exhausted. |
app.generation.blocked | Business block (e.g. insufficient credits); adds block_type, balance, required_credits so you don't auto-retry. |
Payload
POST <callback_url>
X-OverSkill-Signature: sha256=<hmac>
{ "event": "app.generation.completed", "app_id": "OJzZVJ", "app_name": "...",
"status": "published",
"urls": { "preview": "...", "production": "...", "editor": "..." },
"generation": { "message_id": 278, "started_at": "...", "completed_at": "..." },
"metadata": { "your_correlation_id": "..." },
"credits_used": 12, "tokens_used": { "input": 40311, "output": 8822 } }
Verify the signature: HMAC-SHA256 over the raw request body, key =
OVERSKILL_PLATFORM_TOKEN; reject on mismatch. Delivery retries up to 3× with
backoff. http:// callback URLs are accepted in OverSkill dev only.
SSO handoff (optional)
An alternative identity path lets a creator land in OverSkill signed-in from your platform, without a separate signup:
GET https://staging.overskill.com/auth/sso?token=<JWT>
HS256, signed with your partner secret. Required claims: source (your partner
slug), email, aud = "overskill-sso", exp,
iat, and a unique jti (replay-protected — a token without
jti, or a reused one, is rejected). On success OverSkill find-or-creates the
user, ensures a team, and signs them in. New partner sources are onboarded server-side
(secret env var per partner).
Example integration
fakeMango is the reference partner integration — a small Next.js app that drives this exact flow end to end (auth → provision → inject context → generate → watch → preview → deploy) against the real partner API, with a built-in demo engine so it runs before you have a key. Clone it, set your partner key + API base, and the header flips from Demo engine to Live OverSkill.
Clone & run
git clone <example-repo-url> overskill-partner-example
cd overskill-partner-example
cp .env.example .env.local
npm install && npm run dev # starts on the demo engine
Minimal .env.local
Leave these blank to run against the built-in demo engine. Fill them in — at minimum the API base + your partner key — and the app switches to Live OverSkill.
OVERSKILL_API_BASE=https://staging.overskill.com/api/v1 # OverSkill API host + /api/v1
OVERSKILL_PARTNER_API_KEY=os_... # your partner key (server-side only)
OVERSKILL_PARTNER_SLUG=yourbrand # your partner slug
OVERSKILL_PARTNER_EMBED_ORIGINS=https://*.yourbrand.com # who may iframe previews
OVERSKILL_PUBLIC_BASE=https://overskill.app # host for preview/production URLs
OVERSKILL_WEBHOOK_SECRET= # = OverSkill's OVERSKILL_PLATFORM_TOKEN (verifies webhooks)
OVERSKILL_MOCK=0 # 1 = force demo engine even with a key
The partner key is used server-side only — never ship it to the browser. Ask your OverSkill partner contact for access to the example repo and a partner key.