← SDK Portfolio
API
Tokens

Run SDK Portfolio from your own code

Both metered lanes are available over HTTP and each returns one JSON object. The division of labour: the page reads every language against the add-threshold and the vertical's map, gives each one action, ranks the build model, checks the versioning split and holds every end-of-life to its evidence, then hands it all to the run as facts. The run writes the portfolio strategy or the end-of-life cases; the page then holds it to its own facts (spreply.js).

Base URL and the response shape

Every endpoint lives under https://api.skillsafe.ai/v1/app-api, and every response has one shape:

{ "ok": true,  "data":  { ... } }
{ "ok": false, "error": { "code": "...", "message": "..." } }

The token is minted for this app (the guest call takes {"slug":"sdk-portfolio"}), so no slug header is needed afterwards. Send it as Authorization: Bearer YOUR_TOKEN. The run body is the input object, sent bare.

StatusCodeMeaning
400validation_errorThe body is not a JSON object, or a required key (task, platform, languages, facts) is missing. The page always sends all five of task, platform, languages, carry, facts; carry is an empty string for strategy.
401unauthorizedNo token, or a stale one. Mint a guest token or sign in again.
402insufficient_creditsThe balance is under min_credits. Price with /estimate first.
403forbiddenA guest token tried to run: both lanes are metered and need a personal sign-in.
404not_foundUnknown job id.
429rate_limitedBack off and retry.
5xxserver_errorTransient. Retry with the same Idempotency-Key so a retry never double-bills.

The task field comes first

Two lanes, chosen by task, in the order the work runs - which SDKs exist, then which end:

A missing or unknown task is answered as strategy. Both lanes send the same five fields. The facts are the page's checks, one row per line, and a line starting "must:" is a rule the reply has to hold. The page's handoff carries the strategy's decisions into carry for the sunset lane.

The input

FieldRequiredWhat
taskyesstrategy or sunset. If it is missing or neither, do strategy and set "task": "strategy".
platformyesthe API, the vertical, the surface decision, telemetry, the threshold, the spec, procurement and self-hosting, the core-product answer, versioning, cadence, community policy, deadline, mandate and effort; "not given" means not given.
languagesyesevery language with its share, deal requests, fluency, today's status, build, downloads, exclusive accounts, revenue at risk and the page's action, then the vertical's map.
carrysend it (empty for strategy)sunset task only: the strategy's decisions, when the strategy task ran first; may be empty.
factsyesthe page's checks, one line each as area | id | status | evidence, the verdict, the ranked build models and the shape. A line starting "must:" applies to your reply.
retry_notenooptional; if present, your previous reply was unusable - follow it.

The rules the model applies

From the source skill, @samber/sdk-portfolio-strategy - the only standard the run applies:

House rules

The replies

strategy

Write the portfolio strategy - which SDKs exist, built how, supported how, versioned how. A number the input does not give (a runtime grace window, a cadence) is a proposal: start it with "Proposed: ".

  1. summary - one sentence, at most 40 words: the official set, the build model, and what must be fixed first.
  2. shape - lean, broad or flagship, exactly as the facts read it.
  3. languages - one object per language, in the input's order: language (as written), action (add, keep, promote, community, selfservice, evidence or retire, exactly as the facts read it), why (one sentence).
  4. build - model (commercial, free or flagship, exactly as the facts rank it), flagship (the handwritten language when the model is flagship, else empty), exit_plan (one sentence: who regenerates the SDKs if the vendor or pipeline goes away), guardrails (short strings: the spec as the source of truth, self-hosting, fixing a drifting spec first).
  5. tiers - shape (two-tier or numbered), official (one sentence: what official buys), community (one sentence: the explicit non-guarantee and where support routes), promotion_gate (short strings: checkable criteria).
  6. versioning - sdk (one sentence: SemVer per package, a major only when the SDK's surface breaks), api (one sentence: the API's scheme, owned by its own policy), runtime (one sentence: the language-runtime window), mapping (one sentence: the SDK-major to API-version table), cadence (one sentence).
  7. threshold - one sentence: the add-threshold, labelled self-set.
  8. community_policy - one sentence.
  9. caveats - at most 4 short strings; [] when there is nothing to say.
{
  "task": "strategy",
  "summary": "one sentence",
  "shape": "lean",
  "languages": [ { "language": "Java", "action": "keep", "why": "one sentence" } ],
  "build": { "model": "commercial", "flagship": "", "exit_plan": "one sentence", "guardrails": [ "a short string" ] },
  "tiers": { "shape": "two-tier", "official": "one sentence", "community": "one sentence", "promotion_gate": [ "a short string" ] },
  "versioning": { "sdk": "one sentence", "api": "one sentence", "runtime": "one sentence", "mapping": "one sentence", "cadence": "one sentence" },
  "threshold": "one sentence",
  "community_policy": "one sentence",
  "caveats": [ ]
}

The page holds it to its own facts (check() in spreply.js; any warning makes the reply flagged): the reply answers the task it was sent; no URL; no backticks; a summary of at most 40 words; the page's shape; exactly the page's languages in order with their actions and a reason each; the ranked build model, the named flagship, an exit plan and spec-first guardrails; two-tier or numbered tiers with what official buys, the community non-guarantee and a promotion gate; SemVer SDKs never tied to API versions, the API scheme, a runtime window, a mapping table and a cadence; a self-set threshold; no popularity chart; a community policy.

sunset

Write the end-of-life case for every official SDK the page found below the threshold.

  1. summary - one sentence, at most 40 words.
  2. cases - one object per case, in the input's order: language (as written), action (evidence or retire, exactly as the facts read it), evidence (short strings: for an evidence case, the counts to gather - exclusive-usage accounts, revenue at risk, downloads; for a retire case, the counts that make it), successor (for a retire case, an official language from the strategy or spec-driven self-service; empty for an evidence case), steps (short strings in order: an evidence case gathers and decides, announcing nothing; a retire case runs final release, then announcement, then archive), announce_to_archive_months (a number from 6 to 24 for a retire case; 0 for an evidence case), cleanup_months (6 to 12 for a retire case; 0 for an evidence case), message (one or two sentences: for a retire case, the pinned deprecation notice naming the why, the successor and the timeline; for an evidence case, the internal note on what to gather - never a retirement announcement).
  3. caveats - at most 4 short strings; [] when there is nothing to say.
{
  "task": "sunset",
  "summary": "one sentence",
  "cases": [ { "language": "Ruby", "action": "retire", "evidence": [ "a short string" ], "successor": "spec-driven self-service", "steps": [ "final release", "announcement", "archive" ], "announce_to_archive_months": 12, "cleanup_months": 6, "message": "one or two sentences" } ],
  "caveats": [ ]
}

The page requires: the reply answers the task it was sent; the same text rules; exactly the page's cases in order; an evidence case naming the counts to gather, with no window and no announcement; a retire case running final release, announcement and archive in order, citing the exclusive-usage count, naming a successor from the official set or self-service, a 6-24 month window and 6-12 months of cleanup; a message each.

Worked examples

Three runs from the page's free samples (invented APIs): the strategy for a payments API with a drifting spec, the sunset for the same API carrying that strategy, and the strategy for an infrastructure API with a Go flagship. Each input is exactly what the page sends; each reply is the saved reply the sample replays, shipped unedited - but the facts and long strings are shortened in this display only, and every cut is listed under the example. The last line is the page's own check of the full reply.

strategy - A payments API with a drifting spec

The page read it as: a portfolio with open policies; shape lean; build commercial; languages: Java (keep), Python (keep), TypeScript (promote), C# (add), Go (add), Ruby (retire), PHP (evidence), Elixir (community); 2 of 5 gates; failing checks: promote, spec, versioning, evidence, community.

Input (languages is 2078 characters; the first 1394 are shown; facts is 2412 characters; the first 1391 are shown):

{
 "task": "strategy",
 "platform": "Platform: Paxdrel API\nThe API and its integrators: A payments and ledger API; integrators are bank and fintech backend teams, plus a growing set of commerce platforms.\nVertical: Fintech or enterprise backend; SDKs chosen as a surface: yes; per-language telemetry: yes; add-threshold 10% of traffic or 3 named deal requests (self-set)\nSpec: A spec that drifts from the API; a hosted generator vendor acceptable: yes; self-hosted generation required: no; the API is the core product: no\nVersioning: SDK majors track API versions: yes; API scheme: dated API versions; mapping table: no; runtime support window: no; cadence: No stated cadence\nCommunity: No written policy; promotion gate written: [object Object]; deadline: no; a compounding policy; effort ceiling: medium; as-of: 2026-11-01",
 "languages": "Languages in the order given (language | share | deals | fluent | today | downloads | exclusive accounts | revenue at risk | action):\n- Java | 34% | 5 deal requests | fluent yes | today official, generated | downloads up | exclusive accounts 120 | revenue at risk $2100000 | KEEP: 34% of API traffic, at or over the 10% add-threshold; 5 named deal requests, at or over 3\n- Python | 22% | 1 deal request | fluent yes | today official, handwritten | downloads up | exclusive accounts 40 | revenue at risk $600000 | KEEP: 22% of API traffic, at or over the 10% add-threshold\n- TypeScript | 18% | 0 deal requests | fluent no | today community | downloads up | exclusive accounts unknown | revenue at risk unknown | PROMOTE: 18% of API traffic, at or over the 10% add-threshold - a community SDK crossing the threshold goes through the promotion gate, not a competing build\n- C# | 6% | 4 deal requests | fluent no | today none | downloads - | exclusive accounts unknown | revenue at risk unknown | ADD: 4 named deal requests, at or over 3\n- Go | 8% | 0 deal requests | fluent yes | today none | downloads - | exclusive accounts unknown | revenue at risk unknown | ADD: the team already maintains it fluently and the vertical map ranks it top four\n- Ruby | 3% | 0 deal requests | fluent no | today official, handwritten | downloads down | exclusive accounts 0 | revenue at risk $0 | RETIRE: below the\n\n[... 684 more characters cut from this display ...]",
 "carry": "",
 "facts": "The page's checks (5 fail, 3 warn, 3 pass, 1 n/a); verdict: a portfolio with open policies:\n- The language order | surface | pass | the surface decision is made\n- The language order | coverage | pass | every language over 10% of traffic or 3 deal requests has an official decision\n- The language order | order | pass | Java, Python, TypeScript, C#, Go (5 official SDKs)\n- The language order | promote | fail | TypeScript - no promotion gate written; write it before building a competitor\n- The build model | build | na | A commercial generator driven by the spec first, the free OpenAPI Generator as the alternative - the spec drifts from the API - fix it before any generated rung\n- The build model | spec | fail | A spec that drifts from the API - fix the spec first\n- The build model | exit | warn | who regenerates the SDKs if the vendor disappears? Keep the spec, not a vendor DSL, as the source of truth, and prefer a generator that can self-host\n- The build model | handwritten | warn | Python, Ruby are handwritten - one decision portfolio-wide, at most one flagship exception\n- Versioning, tiers and EOL | versioning | fail | SDK majors track API versions - a breaking release for integrators whose code did not break; no SDK-major to API-version mapping table; no language-runtime support window\n- Versioning, tiers and EOL | cadence | warn | cadence is a predictability promise to\n\n[... 1021 more characters cut from this display ...]"
}

Reply:

{
 "task": "strategy",
 "summary": "Paxdrel ships five official SDKs - Java, Python, TypeScript, C# and Go - from a commercial spec-driven generator, but the drifting spec must be fixed before any generated release.",
 "shape": "lean",
 "languages": [
  {
   "language": "Java",
   "action": "keep",
   "why": "Java carries 34% of traffic with 5 named deal requests, 120 exclusive accounts and $2100000 of revenue at risk, well over the 10% add-threshold."
  },
  {
   "language": "Python",
   "action": "keep",
   "why": "Python carries 22% of traffic with 40 exclusive accounts and $600000 of revenue at risk, over the add-threshold."
  },
  {
   "language": "TypeScript",
   "action": "promote",
   "why": "The community TypeScript SDK carries 18% of traffic, so it goes through a written promotion gate rather than a competing in-house build."
  },
  {
   "language": "C#",
   "action": "add",
   "why": "C# has only 6% of traffic but 4 named deal requests, over the 3-request threshold, and the fintech map ranks it early."
  },
  {
   "language": "Go",
   "action": "add",
   "why": "Go holds 8% of traffic, the team already maintains it fluently and the fintech map ranks it in the top four, so its official SDK comes nearly free."
  },
  {
   "language": "Ruby",
   "action": "retire",
   "why": "Ruby sits at 3% of traffic with falling downloads, 0 exclusive accounts and $0 of revenue at risk, so the retirement case holds."
  },
  {
   "language": "PHP",
   "action": "evidence",
   "why": "PHP is an official SDK at 4% of traffic with no exclusive-usage count or revenue figure, so the case must be built before any end-of-life announcement."
  },
  {
   "language": "Elixir",
   "action": "community",
   "why": "Elixir carries 1% of traffic with flat downloads, so it stays a listed community SDK with an explicit non-guarantee."
  }
 ],
 "build": {
  "model": "commercial",
  "flagship": "",
  "exit_plan": "Before signing, the contract and runbook name the free OpenAPI Generator, run in-house CI from the same spec, as the pipeline that regenerates every SDK if the vendor goes away.",
  "guardrails": [
   "Fix the drifting spec first and gate generation on spec-versus-API contract tests",
   "Keep the OpenAPI spec, not a vendor DSL, as the single source of truth",
   "Prefer a generator that can self-host so builds survive the vendor",
   "Fold the handwritten Python SDK into the generated pipeline - one portfolio-wide build decision"
  ]
 },
 "tiers": {
  "shape": "two-tier",
  "official": "Official buys a support commitment: maintained releases, security fixes, the documented runtime window and tickets answered by the Paxdrel team, marked in the version number and package metadata.",
  "community": "Community SDKs carry an explicit non-guarantee - no support, security or compatibility promise from Paxdrel - and their support routes to each project's own repository.",
  "promotion_gate": [
   "The language sustains 10% of traffic or 3 named deal requests",
   "The package namespace transfers to the Paxdrel organisation",
   "The maintainers agree to cooperate through handover",
   "Feature-complete against the current spec and passing the contract test suite",
   "Releases can move onto the official generated pipeline"
  ]
 },
 "versioning": {
  "sdk": "Each SDK package follows its own SemVer and bumps a major only when that SDK's own surface breaks, never because the API released a new dated version.",
  "api": "The API keeps its dated versions under its own policy, separate from SDK package numbers.",
  "runtime": "Proposed: each SDK supports every language runtime still under upstream maintenance and drops one only in a minor release announced 6 months after its upstream end of life.",
  "mapping": "Proposed: publish a table mapping each SDK major to the dated API versions it supports.",
  "cadence": "Proposed: a monthly release train for all official SDKs, with security fixes shipped out of band."
 },
 "threshold": "An official SDK is added at a sustained 10% of traffic or 3 named deal requests, a self-set threshold the team owns and may revise.",
 "community_policy": "Write and publish a policy that lists community SDKs with an explicit non-guarantee, routes their support to their own repositories and invokes the promotion gate when a language crosses the threshold instead of building a competitor.",
 "caveats": [
  "No generated release ships until the spec stops drifting from the API",
  "Decoupling SDK majors from API versions changes current practice and needs an announcement",
  "PHP needs exclusive-usage and revenue counts before any decision"
 ]
}

What step 5 prints for it: Java keep | Python keep | TypeScript promote | C# add | Go add | Ruby retire | PHP evidence | Elixir community

The page's check: kept - nothing to flag

sunset - A payments API with a drifting spec

The input carries the saved strategy's decisions (R.laneForm fills carry from the strategy reply), as the page's handoff does. The end-of-life cases: Ruby (retire), PHP (evidence).

Input (languages is 2078 characters; the first 1394 are shown; facts is 2301 characters; the first 1391 are shown):

{
 "task": "sunset",
 "platform": "Platform: Paxdrel API\nThe API and its integrators: A payments and ledger API; integrators are bank and fintech backend teams, plus a growing set of commerce platforms.\nVertical: Fintech or enterprise backend; SDKs chosen as a surface: yes; per-language telemetry: yes; add-threshold 10% of traffic or 3 named deal requests (self-set)\nSpec: A spec that drifts from the API; a hosted generator vendor acceptable: yes; self-hosted generation required: no; the API is the core product: no\nVersioning: SDK majors track API versions: yes; API scheme: dated API versions; mapping table: no; runtime support window: no; cadence: No stated cadence\nCommunity: No written policy; promotion gate written: [object Object]; deadline: no; a compounding policy; effort ceiling: medium; as-of: 2026-11-01",
 "languages": "Languages in the order given (language | share | deals | fluent | today | downloads | exclusive accounts | revenue at risk | action):\n- Java | 34% | 5 deal requests | fluent yes | today official, generated | downloads up | exclusive accounts 120 | revenue at risk $2100000 | KEEP: 34% of API traffic, at or over the 10% add-threshold; 5 named deal requests, at or over 3\n- Python | 22% | 1 deal request | fluent yes | today official, handwritten | downloads up | exclusive accounts 40 | revenue at risk $600000 | KEEP: 22% of API traffic, at or over the 10% add-threshold\n- TypeScript | 18% | 0 deal requests | fluent no | today community | downloads up | exclusive accounts unknown | revenue at risk unknown | PROMOTE: 18% of API traffic, at or over the 10% add-threshold - a community SDK crossing the threshold goes through the promotion gate, not a competing build\n- C# | 6% | 4 deal requests | fluent no | today none | downloads - | exclusive accounts unknown | revenue at risk unknown | ADD: 4 named deal requests, at or over 3\n- Go | 8% | 0 deal requests | fluent yes | today none | downloads - | exclusive accounts unknown | revenue at risk unknown | ADD: the team already maintains it fluently and the vertical map ranks it top four\n- Ruby | 3% | 0 deal requests | fluent no | today official, handwritten | downloads down | exclusive accounts 0 | revenue at risk $0 | RETIRE: below the\n\n[... 684 more characters cut from this display ...]",
 "carry": "Official: Java, Python, TypeScript, C#, Go\nBuild: commercial\nTiers: two-tier - official: Official buys a support commitment: maintained releases, security fixes, the documented runtime window and tickets answered by the Paxdrel team, marked in the version number and package metadata.; community: Community SDKs carry an explicit non-guarantee - no support, security or compatibility promise from Paxdrel - and their support routes to each project's own repository.\nVersioning: SDK Each SDK package follows its own SemVer and bumps a major only when that SDK's own surface breaks, never because the API released a new dated version.; runtime Proposed: each SDK supports every language runtime still under upstream maintenance and drops one only in a minor release announced 6 months after its upstream end of life.; cadence Proposed: a monthly release train for all official SDKs, with security fixes shipped out of band.",
 "facts": "The page's checks (5 fail, 3 warn, 3 pass, 1 n/a); verdict: a portfolio with open policies:\n- The language order | surface | pass | the surface decision is made\n- The language order | coverage | pass | every language over 10% of traffic or 3 deal requests has an official decision\n- The language order | order | pass | Java, Python, TypeScript, C#, Go (5 official SDKs)\n- The language order | promote | fail | TypeScript - no promotion gate written; write it before building a competitor\n- The build model | build | na | A commercial generator driven by the spec first, the free OpenAPI Generator as the alternative - the spec drifts from the API - fix it before any generated rung\n- The build model | spec | fail | A spec that drifts from the API - fix the spec first\n- The build model | exit | warn | who regenerates the SDKs if the vendor disappears? Keep the spec, not a vendor DSL, as the source of truth, and prefer a generator that can self-host\n- The build model | handwritten | warn | Python, Ruby are handwritten - one decision portfolio-wide, at most one flagship exception\n- Versioning, tiers and EOL | versioning | fail | SDK majors track API versions - a breaking release for integrators whose code did not break; no SDK-major to API-version mapping table; no language-runtime support window\n- Versioning, tiers and EOL | cadence | warn | cadence is a predictability promise to\n\n[... 910 more characters cut from this display ...]"
}

Reply (long strings shortened for display - see the cuts below):

{
 "task": "sunset",
 "summary": "Retire the handwritten Ruby SDK on its evidence, with spec-driven self-service as the successor once the drifting spec is fixed; hold PHP until its exclusive-usage case is built, announcing nothing.",
 "cases": [
  {
   "language": "Ruby",
   "action": "retire",
   "evidence": [
    "0 accounts use the Ruby SDK exclusively",
    "$0 revenue at risk",
    "downloads trending down",
    "3% of API traffic, below the self-set 10% add-threshold, with 0 deal requests"
   ],
   "successor": "spec-driven self-service",
   "steps": [
    "Ship the final release of the Ruby SDK with current fixes",
    "Announce: pinned deprecation issue naming the why, the successor and the timeline; deprecation markers on the registry; banners on the Ruby docs; support told",
    "Archive the Ruby repository at the end of the window",
    "Clean up packages, docs and samples after archive"
   ],
   "announce_to_archive_months": 12,
   "cleanup_months": 6,
   "message": "The official Ruby SDK is deprecated: it carries 3% of traffic, downloads are falling and no account depends on it alone, so effort moves to the official set. Ruby integrators should generate a client from the published spec; the repository [... 46 more characters cut from this display]"
  },
  {
   "language": "PHP",
   "action": "evidence",
   "evidence": [
    "accounts that use the PHP SDK exclusively",
    "revenue at risk from those exclusive accounts",
    "downloads trend over time (flat today)",
    "PHP traffic share against the 10% threshold (4% today)"
   ],
   "successor": "",
   "steps": [
    "Count accounts that call the API only through the PHP SDK",
    "Sum the revenue at risk from those accounts",
    "Pull the downloads trend",
    "Decide retire or keep from those counts; announce nothing until then"
   ],
   "announce_to_archive_months": 0,
   "cleanup_months": 0,
   "message": "Internal: before any decision on the PHP SDK, gather its exclusive-usage account count, the revenue at risk from those accounts and its downloads trend. Nothing is announced until the case is built."
  }
 ],
 "caveats": [
  "Proposed: the 12-month announcement-to-archive window and 6-month cleanup; no timeline was given.",
  "The spec drifts from the API; fix it before pointing Ruby integrators at self-service.",
  "PHP is on the vertical map, so a small share alone is not a reason to drop it."
 ]
}

Cut from this display only (the saved reply and the check use the full text): cases[0].message (285 characters, 239 shown).

What step 5 prints for it: Ruby retire | PHP evidence

The page's check: kept - nothing to flag

strategy - An infrastructure API with a Go flagship

The page read it as: a portfolio ready to approve; shape flagship; build flagship; languages: Go (keep), Python (keep), TypeScript (keep), Java (community), Rust (selfservice), Perl (retire); 5 of 5 gates; failing checks: none.

Input (languages is 1510 characters; the first 1399 are shown; facts is 1763 characters; the first 1398 are shown):

{
 "task": "strategy",
 "platform": "Platform: Routhane\nThe API and its integrators: A routing and fleet API sold to infrastructure teams; the API is the product, and Go services carry most of the traffic.\nVertical: Infrastructure or cloud; SDKs chosen as a surface: yes; per-language telemetry: yes; add-threshold 10% of traffic or 3 named deal requests (self-set)\nSpec: A linted spec, the source of truth in CI; a hosted generator vendor acceptable: yes; self-hosted generation required: no; the API is the core product: yes\nVersioning: SDK majors track API versions: no; API scheme: dated API versions; mapping table: yes; runtime support window: yes; cadence: Continuous release-on-change\nCommunity: Listed with an explicit non-guarantee; promotion gate written: [object Object]; deadline: no; a compounding policy; effort ceiling: high; as-of: 2026-11-01",
 "languages": "Languages in the order given (language | share | deals | fluent | today | downloads | exclusive accounts | revenue at risk | action):\n- Go | 62% | 2 deal requests | fluent yes | today official, handwritten | downloads up | exclusive accounts 310 | revenue at risk $4800000 | KEEP: 62% of API traffic, at or over the 10% add-threshold\n- Python | 18% | 1 deal request | fluent yes | today official, generated | downloads up | exclusive accounts 25 | revenue at risk $300000 | KEEP: 18% of API traffic, at or over the 10% add-threshold\n- TypeScript | 11% | 0 deal requests | fluent no | today official, generated | downloads up | exclusive accounts 8 | revenue at risk $90000 | KEEP: 11% of API traffic, at or over the 10% add-threshold\n- Java | 4% | 0 deal requests | fluent no | today community | downloads flat | exclusive accounts unknown | revenue at risk unknown | COMMUNITY: below the threshold - list it with an explicit non-guarantee; it staffs the long tail\n- Rust | 2% | 0 deal requests | fluent no | today none | downloads - | exclusive accounts unknown | revenue at risk unknown | SELFSERVICE: below the threshold - publish the spec and point it at open-source generators\n- Perl | 0.3% | 0 deal requests | fluent no | today official, generated | downloads down | exclusive accounts 0 | revenue at risk $0 | RETIRE: below the threshold, downloads down, no account uses it exclusively and no\n\n[... 111 more characters cut from this display ...]",
 "carry": "",
 "facts": "The page's checks (0 fail, 0 warn, 7 pass, 2 n/a); verdict: a portfolio ready to approve:\n- The language order | surface | pass | the surface decision is made\n- The language order | coverage | pass | every language over 10% of traffic or 3 deal requests has an official decision\n- The language order | order | pass | Go, Python, TypeScript (3 official SDKs)\n- The build model | build | na | A handwritten flagship, generated rest first, a commercial generator driven by the spec as the alternative - the API is the core product and Go carries 62% of traffic - handwrite that one, generate the rest\n- The build model | spec | pass | A linted spec, the source of truth in CI\n- The build model | exit | na | no hosted vendor in the default rung\n- Versioning, tiers and EOL | versioning | pass | SDK SemVer, API dated API versions and runtime windows written separately, with a mapping table\n- Versioning, tiers and EOL | evidence | pass | Perl (0 exclusive accounts) - the case holds\n- Versioning, tiers and EOL | community | pass | Listed with an explicit non-guarantee\nBuild models by the skill's ranking, in order: flagship, commercial, free (moved by: the API is the core product and Go carries 62% of traffic - handwrite that one, generate the rest); portfolio shape: flagship.\nmust: shape flagship; languages lists exactly Go keep, Python keep, TypeScript keep, Java community, Rust selfservice,\n\n[... 365 more characters cut from this display ...]"
}

Reply:

{
 "task": "strategy",
 "summary": "Routhane ships three official SDKs - a handwritten Go flagship with generated Python and TypeScript - and only needs to run the evidenced Perl retirement; nothing blocks approval.",
 "shape": "flagship",
 "languages": [
  {
   "language": "Go",
   "action": "keep",
   "why": "Go carries 62% of traffic with 310 exclusive accounts and $4800000 of revenue at risk, making it the handwritten flagship."
  },
  {
   "language": "Python",
   "action": "keep",
   "why": "Python carries 18% of traffic with 25 exclusive accounts and $300000 of revenue at risk, over the 10% add-threshold."
  },
  {
   "language": "TypeScript",
   "action": "keep",
   "why": "TypeScript carries 11% of traffic with 8 exclusive accounts and $90000 of revenue at risk, just over the add-threshold."
  },
  {
   "language": "Java",
   "action": "community",
   "why": "Java holds 4% of traffic with flat downloads, so it stays a listed community SDK with an explicit non-guarantee."
  },
  {
   "language": "Rust",
   "action": "selfservice",
   "why": "Rust holds 2% of traffic with no deal requests, so the published spec and open-source generators cover it."
  },
  {
   "language": "Perl",
   "action": "retire",
   "why": "Perl holds 0.3% of traffic with falling downloads, 0 exclusive accounts and $0 of revenue at risk, so the retirement case holds."
  }
 ],
 "build": {
  "model": "flagship",
  "flagship": "Go",
  "exit_plan": "The Go team owns the handwritten flagship, and the generated SDKs are rebuilt from the spec in CI with a self-hostable generator so the platform team can regenerate them if any vendor or pipeline goes away.",
  "guardrails": [
   "Keep the linted spec, not a vendor DSL, as the source of truth, including for the handwritten Go SDK",
   "Prefer a self-hostable generator for Python and TypeScript",
   "Contract-test the Go SDK against the spec so a drift is caught and the spec fixed first",
   "No second handwritten exception"
  ]
 },
 "tiers": {
  "shape": "two-tier",
  "official": "Official buys an Routhane support commitment: maintained releases, security fixes, the published runtime window and team support, marked in version and package metadata.",
  "community": "Community SDKs such as Java carry an explicit non-guarantee of support or compatibility, and their support routes to their own repositories.",
  "promotion_gate": [
   "A sustained 10% of traffic or 3 named deal requests",
   "The package namespace transfers to Routhane",
   "The maintainers agree to cooperate through handover",
   "Feature-complete against the current spec and passing the contract tests"
  ]
 },
 "versioning": {
  "sdk": "Each SDK package keeps its own SemVer and bumps a major only when its own surface breaks, never because the API released a version.",
  "api": "The API keeps its dated versions under its own policy.",
  "runtime": "Keep the written language-runtime support window, tied to each runtime's upstream lifecycle.",
  "mapping": "Keep publishing the table mapping each SDK major to the dated API versions it supports.",
  "cadence": "Continuous release-on-change: each spec or SDK change ships a release."
 },
 "threshold": "An official SDK is added at a sustained 10% of traffic or 3 named deal requests, a self-set threshold the team owns.",
 "community_policy": "Keep listing community SDKs with an explicit non-guarantee, route their support to their repositories and invoke the promotion gate when a language crosses the threshold rather than building a competitor.",
 "caveats": [
  "TypeScript at 11% sits close to the threshold; watch it before any change",
  "Perl retirement still runs final release, announcement and archive in order"
 ]
}

What step 5 prints for it: Go keep | Python keep | TypeScript keep | Java community | Rust selfservice | Perl retire

The page's check: kept - nothing to flag

Build the input yourself

Load the engine in node (global.window = global; require("./spkit.js"); require("./spreply.js")), then:

const K = PortfolioKit, R = PortfolioReply;
const f = K.form(Object.assign(K.blank(), { name: "Acme", vertical: "fintech", telemetry: "yes", threshold: "10", deals: "3",
  languages: "Java | 40 | 2 | yes | official\nRuby | 2 | 0 | no | official | handwritten | down | 0 | $0" }));
// f.langRows (each language with its action and why), f.build (the ranked build models), f.shape, f.cases (the end-of-life cases), f.gate
// K.portfolioMd(f), K.languagesCsv(f) - the portfolio table and the languages view
const b = K.buildInput(f, "strategy");                       // or "sunset" (R.laneForm(form, "sunset", strategy) fills carry)
const wire = K.toWire(b.input);                              // POST this to /estimate and /run
// then: const reply = R.normalize(R.parseReply(job.output.output), "strategy");
// R.check(reply, b)                                         // {status: kept or flagged, notes}

Get a token for the steps below from /tokens.html.

Step 1 — a tiny client helper

One function that sends JSON and unwraps the response. Paste a personal token from /tokens.html, or leave it empty and mint a guest token in step 2.

BASE="https://api.skillsafe.ai/v1/app-api"
TOKEN="YOUR_TOKEN"   # from https://sdk-portfolio.skillsafe.ai/tokens.html
api() { # api METHOD PATH [JSON-BODY]
  curl -s -X "$1" "$BASE$2" -H "Content-Type: application/json" \
    ${TOKEN:+-H "Authorization: Bearer $TOKEN"} ${3:+--data "$3"}
}

Step 2 — a token

A guest token is enough for /me and /estimate. A run is metered, so it needs a personal token: sign in on /tokens.html and copy it from there.

# a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
TOKEN=$(curl -s -X POST "$BASE/guest" -H "Content-Type: application/json" -d '{"slug":"sdk-portfolio"}' | jq -r '.data.token')

Step 3 — GET /me

Who the token is (subject_type: user or guest) and the credit balance.

api GET /me | jq '.data | {subject_type, credits}' 

Step 4 — POST /estimate

Free. The body is the run input itself, the bare object (never wrapped in {"input": ...}). hold_credits is a reservation, not the price; min_credits is what the balance must cover; model_alias reads gpt-terra.

# input.json: the run input object - {"task": "strategy", "platform": "Platform: Paxdrel API
The API and its integrators: ...", "languages": "Languages in the order given (language | share | deals | ...):
- Java | 34% | 5 deal requests | ... | KEEP: ...", "carry": "", "facts": "The page's checks (...); verdict: a portfolio with open policies:
- The language order | surface | pass | ..."}
# - see the input table
api POST /estimate "$(cat input.json)" | jq '.data | {hold_credits, min_credits, model, model_alias, markup_bps, warnings}' 

Step 5 — POST /run, then poll the job

Send an Idempotency-Key built from the lane and the input: a retry with the same key never bills twice. The page uses sdk-portfolio:<task>:<inputHash>:a<attempt> - the lane is part of the key, retry_note is left out of the hash - and its one automatic reformat retry is a new attempt number, named in the page as one extra run. A strategy reply carries summary, shape, languages, build, tiers, versioning, threshold, community_policy and caveats; a sunset reply carries summary, cases and caveats. The code below prints the gist of either: each language and its action, or each case and its action.

KEY="sdk-portfolio:$(jq -r .task input.json):$(shasum -a 256 input.json | cut -c1-16):a1"
JOB=$(curl -s -X POST "$BASE/run" -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: $KEY" --data @input.json | jq -r '.data.job_id')
while :; do
  J=$(api GET "/jobs/$JOB"); S=$(echo "$J" | jq -r '.data.status')
  case "$S" in succeeded|failed|cancelled) break;; esac; sleep 2
done
echo "$J" | jq -r '.data.output.output' > reply.json   # one JSON object, the reply contract
echo "$J" | jq '.data | {charged_credits, truncated}'
jq -r 'if .task == "sunset" then (.cases[] | "\(.language): \(.action)") 
       else (.languages[] | "\(.language): \(.action)") end' reply.json

Step 6 — POST /run-stream

The same run as Server-Sent Events, with the same input.json. Browsers receive progress ticks rather than text deltas; the final done event carries the reply.

# Server-Sent Events: tick/delta events while the job runs, then a final event with the job
curl -N -s -X POST "$BASE/run-stream" -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: sdk-portfolio:$(jq -r .task input.json):$(shasum -a 256 input.json | cut -c1-16):a1" --data @input.json