Deploy

The running jobjob_7Hq2 · accepted · document.fr-CA.pdf · $49.00 final

A deploy builds your folder into a version, checks it against live, serves it in test mode and runs your brand's own suite against it. deploy --live sends that exact version to buyers, all at once or in stages that compare it job for job against live and abort on their own, and every job already running keeps the version it started on.

A deploy answers one question: is this exact version ready for a buyer's money? Every version passes through these states, and only the last two take real orders.

StateWhat it isWho reaches it
draftyour folder, built into a version: a name, 2026-11-02.1, and the sha256 of the whole bundleno one yet: check runs, and check --against live diffs it
testedthe draft, served in test mode at your real address, with your suite run against itevery sk_test_ key, your staff and the simulated market
stagedthe tested version, taking a share of new orders beside livethe buyers whose orders fall in its share
livethe tested version, promoted by its sha256every buyer, member and agent, on live keys

deploy takes the folder to tested. A version that fails check or a suite case stays a draft, and the run names the case:

Run
npx services.dev deploy
magic values: none
api.lawyer · 9 files · 6 Services, 1 Pool, rules 2026-10 and the brand
  version 2026-11-02.1 · sha256:4e1b77c09a3f5d21… · draft
  check   0 failed · against live 2026-10-19.3: 1 additive, 0 breaking
  test    https://api.lawyer in test mode · every sk_test_ key reaches 2026-11-02.1 now
  suite   268 passed · 0 failed · 83 live rows wait for deploy --live
✓ tested  2026-11-02.1 · live is still 2026-10-19.3 · npx services.dev deploy --live sends it

The suite is the one your brand publishes, so the test a buyer could run against you is the test your deploy runs first. Test and live covers what test mode simulates and where each mode serves.

deploy --live promotes the tested version. It never rebuilds: the sha256 that passed is the sha256 that serves.

Run
npx services.dev deploy --live
magic values: none
api.lawyer · 2026-11-02.1 · sha256:4e1b77c09a3f5d21… · tested 14 minutes ago
  plan    Team (services.dev) · your plan · every gated key covered
  stripe  acct_1Q8Lw4 · charges on · payouts on
  model   your model key · 9 AI steps, 8 pinned to a build
  warns   trademark-response.draft names only the family claude-x: its build can move under you · pin one, claude-x@<date>
  domain  api.lawyer · verified · certificate renews 2027-01-30
  suite   83 live rows, read-only: auth, audience and leak cases · 0 failed · nothing ordered, nothing charged
✓ live    https://api.lawyer · api, mcp, cli, sdk, site, docs and every app
  pinned  41 matters in flight stay on 2026-10-19.3 until they end · new orders take 2026-11-02.1
  event   version.live · 2026-10-19.3 retires when its last matter, clock and timer close

Five things are checked first, and each refusal names its cure:

CheckedRefused withCure
each Service's plan, derived from its keysOFFER, with plan, because and cureupgrade, or check --fit for the rewrite that stays put: going live on a gated key
no breaking change under pinned jobs or keysbreaking_changeVersions
Stripe connected, with charges and payouts onstripe_unconnectedservices.dev connect stripe
a model key for live, and a model on every AI stepmodel_unsetservices.dev model set
Brand.domain verified, on Launch and updomain_unverifiedthe two DNS records services.dev domains prints

The live suite rows then run read-only, so the auth, audience and leak cases test live keys without ordering anything. A rename under jobs that still have the step ahead is refused:

Run
npx services.dev deploy --live   # the same tree, with a step renamed
magic values: none
✗ breaking_change · services/trademark-response.ts:18 · draft → write
  pinned  37 matters on 2026-11-02.1 still have draft ahead of them
  cure    keep the step's name, ship it under a new Service id, or deploy --live --breaking: pinned matters keep their version, and keys pinned to it keep its shapes
  docs    https://services.dev/docs/operate/versions#what-counts-as-breaking

A step's model pins a family or a build. model: 'claude-x' pins the family, and the provider may move its build under you. model: 'claude-x@2026-11-08' pins the build, so a new build reaches buyers only through a deploy you make. deploy --live warns on every reviewed step pinned only to a family, as the run above does for draft. The incident is what a family pin allows.

--stages sends a share of new orders to the new version and leaves the rest on live. Every job keeps its version for life, so the console compares the two cohorts job for job: send-back rate, gate refusals, AI a job and time to each step.

Run
npx services.dev deploy --live --stages 25%,100% --hold 7d   # 16 Nov: the draft's brief argues a prior registration
magic values: none
api.lawyer · 2026-11-16.1 · sha256:b83e0f5a72c1d946… · tested 9 minutes ago
  checks  plan, stripe, model, domain and the live suite, as deploy --live · 0 failed
  staged  25% of new orders take 2026-11-16.1 for 7 days · the rest take live 2026-11-09.1 · version.staged
  judged  the two cohorts, job for job: send-back rate, gate refusals, AI a job, time to each step
  aborts  on its own, the first alarm on the new cohort: new orders go back to 2026-11-09.1 · version.aborted

A stage promotes when its window passes, its cohort holds at least 10 jobs and no alarm fires on it. On a breach it aborts on its own: new orders go back to live, the staged jobs finish on their version, and version.aborted names the measure that fell. The cohorts are judged the way evals judge a change before it ships and holdouts judge a line after it moves: three readings of one record. Stages run on Team.

Run
open https://api.lawyer/console/deploys   # 23 Nov, 14:00
magic values: none
Deploys · live 2026-11-09.1 · staged 2026-11-16.1 at 25%, day 7 of 7
  cohort      trademark responses · 17 on 2026-11-16.1 · 58 on 2026-11-09.1
  send-backs  0 of 17 (0%) · 2 of 58 (3%)
  refusals    0 · 0
  AI a job    $0.74 · $0.71
  to respond  p50 4h 10m · 4h 25m
✓ promoted    2026-11-16.1 to 100% · no alarm on its cohort, 17 jobs over the 10 a stage needs · version.promoted

apis.tax ships nothing in the week before the filing date. Brand.freeze names the window and what still runs inside it:

brand.ts
freeze: [{ from: '2027-04-08', to: '2027-04-16', allow: ['hold', 'rollback', 'respond'] }],
Run
npx services.dev deploy --live   # apis.tax, 12 Apr, inside its freeze
magic values: none
✗ frozen · brand.ts:14 · Brand.freeze holds live deploys from 2027-04-08 to 2027-04-16
  runs    hold, rollback and respond, as the freeze allows
  cure    deploy after 2027-04-16, or deploy --live --break-freeze --reason "<why>": written to the audit log

--break-freeze --reason ships anyway, and the reason goes on the audit log.

Run
open https://api.lawyer/console/deploys
magic values: none
Deploys · live 2026-11-02.1 since 2 Nov 15:12 · Team
  2026-11-02.1  live     sha256:4e1b77c0  suite 268 + 83 live · 1 additive · 212 matters since
  2026-10-19.3  pinned   sha256:9c02d1aa  9 matters · 18 clocks · 63 timers · retires when they close
  2026-10-19.2  retired  sha256:51f0b6e2  nothing pinned since 30 Oct
  actions   diff against live · roll back to 2026-10-19.3 · repin its 9 matters

The first live deploy, start to finish, with Stripe connected and the first payout: take the first real job.

Call (CLI)APIEventOn the consolePlan
deployPOST https://services.dev/v1/deploysversion.deployed, version.tested, version.failed/console/deploysevery plan
deploy --live [--breaking]POST https://services.dev/v1/deploys/:version/liveversion.live/console/deploysevery plan
deploy --live --stages <shares> --hold <span>POST https://services.dev/v1/deploys/:version/live { stages, hold }version.staged, version.promoted, version.aborted/console/deploysTeam
deploy --live --break-freeze --reason <why>POST https://services.dev/v1/deploys/:version/live { break_freeze }version.live/console/auditTeam
connect stripePOST https://services.dev/v1/stripe/connectstripe.connected/console/keysevery plan
model set <key>PUT https://services.dev/v1/modelmodel.set/console/keysevery plan
domainsGET https://services.dev/v1/domainsdomain.verified/console/keysLaunch