Sprint Desk — API

Drive the sprint planner from your own code.

API tokens Open the app

Sprint Desk over HTTP

Everything the page does, your script can do: paste a backlog and a scenario, get back the commitment call, the sprint goal, the capacity arithmetic, the committed backlog, the cut list, the risks, the actions and the open questions. Six steps, each shown in cURL, Python, JavaScript, Go, Java, Ruby, PHP and C#. Pick a language once and the whole page follows.

Base URL, envelope and errors

Every route lives under https://api.skillsafe.ai/v1/app-api. There is no /apps/{slug}/ segment — the app is bound to the token, not to the path, so a token minted for Sprint Desk can only ever run Sprint Desk.

POST /guest GET /me POST /estimate POST /run GET /jobs/{job_id} POST /run-stream

Every response is the same envelope: {"ok":true,"data":{...}} on success, {"ok":false,"error":{"code":"...","message":"..."}} on failure. Check ok, never the HTTP status alone.

CodeMeaningWhat to do
unauthorizedMissing, stale or revoked token.Mint a new one — POST /guest, or sign in on the token page.
payment_requiredBalance below the run's minimum.Compare me.credits against estimate.hold_credits before submitting; top up.
not_foundUsually a wrong path.There is no /apps/{slug}/ segment. Check the base URL above.
rate_limitedToo many calls too fast.Back off and retry; do not tight-loop a poll.
validation_errorThe input object was malformed.Send the input object directly, not wrapped in {"input": ...}.

1. Get a token

Every call carries Authorization: Bearer <token>. The easiest token is the one this browser already holds: open the token page and press Copy shell export — never open the DevTools console for it. For a fully scripted client with no browser at all, mint a guest token with POST /guest. Guest tokens can call /me and the free /estimate; a metered /run needs a personal token so the credits bill your account.

2. Check the session and the balance

GET /me tells you whether the token is a personal or a guest one and how many credits it can spend. Compare that balance against hold_credits from step 3 before you submit a run: a 402 after submit is a client bug, not a user error. The app itself does exactly this and disables its run button with the shortfall named.

3. Estimate — free, no job, no charge

POST /estimate takes the same input object a run does and returns the model binding and the reservation. It creates no job and charges nothing, which makes it the right smoke test for a new client. Assert the three binding fields: model is gpt-5.6-terra, model_alias is gpt-terra, and markup_bps is 1000. hold_credits is what a run reserves, priced against the full output cap — it is not the price. The charge is usually far lower.

4. Run it and poll

POST /run queues a job. The body is the input object directly — not wrapped in {"input": {...}}. Send an Idempotency-Key on every run, including any automatic retry: without one, a network blip on the way to the server can bill the same plan twice. Derive the key from a hash of the input plus an attempt counter. Then poll GET /jobs/{job_id} until status is succeeded, failed or canceled. The plan text is at data.output.output; truncated: true means the balance capped the output and what you have is incomplete.

5. Or stream it

POST /run-stream returns Server-Sent Events: an event: job with the job id, a run of event: delta frames each carrying {"text": "..."}, and a terminal event: done with the whole output, the charge and the truncation flag. Treat done as authoritative and prefer its output.output over your accumulated deltas — a stream can drop its tail. If the stream dies mid-body, keep what arrived: it may already have been paid for, and the plan format parses partially.

6. Parse the plan

The reply is plain text, not JSON. Four tag lines, then six ## sections in a fixed order. Bullets under Sprint backlog and Deferred and at risk carry exactly four fields separated by | ; every other section is plain bullets. A section with nothing to report is the single bullet - None. If COMMITMENT is Not plannable, both row sections are - None. and Open questions says what to paste.

The input object and the output contract

Input fields

FieldTypeMeaning
datastring, requiredThe paste: story lines like PAY-412: Refund webhook retry queue, 5 pts (owner Devin, depends on PAY-411), plus prose about the team, sprint dates, velocity history, PTO, carry-over and priorities. The app clips very long pastes through the middle, keeping both ends, and declares the cut in-band.
notesstring, optionalBusiness context: what the product owner wants, hard commitments, team-health worries, what you are being pressured to squeeze in.
taskstringOne of Full sprint plan, Estimation review, Capacity check, Goal setting, Scope negotiation.
factsstring, optionalA mechanical scan of the paste — detected stories, the point total, unestimated and off-Fibonacci flags, any velocity numbers. A hint for cross-checking, never a verdict; the model trusts its own reading of the paste over a misparsed line.
retry_notestring, optionalOnly for a reformat retry: instructions restating the output shape after a reply that did not parse. Send it with the same idempotency-key base as the first attempt and a bumped attempt counter.

Output contract

Plain text. Four tag lines, a blank line, then six ## sections in exactly this order. Anything that breaks the shape should be retried once with retry_note.

COMMITMENT: Committable | Overcommitted - cuts needed | Not plannable
GOAL: <one sentence, or exactly: Not identified>
CONFIDENCE: <bare integer 0-100>
SUMMARY: <2-4 sentences, ends at the first blank line>

## Capacity assessment
- <plain bullets, with the arithmetic shown>

## Sprint backlog
- <story> | <points> | <owner> | <why it makes the cut>

## Deferred and at risk
- <story> | <points> | <why it waits> | <what unblocks it>

## Risks and dependencies
- <plain bullets>

## Recommended actions
- <plain bullets, each starting with a verb>

## Open questions
- <plain bullets>
Worth re-doing client-side, because the app does: total the points in the Sprint backlog rows and compare them against the velocity history in your own paste. A reply tagged Committable whose own rows do not fit the stated velocity is the exact failure a sprint planner exists to catch, and the arithmetic is free. Check too that every story you pasted appears in one of the two tables — a ticket in neither was neither committed nor cut.

What you do not need the API for

The story scan, the tracker-export conversion, the point totals, the off-Fibonacci and unestimated flags, the capacity calculator and the velocity averager all run in the browser with no account and no network. If all you want is arithmetic over a backlog, the app itself is free. The API is for the judgement part — the goal, the commitment call and the reasoned cut list.