Deadlines

The running jobjob_7Hq2 · working · $49.00 held

within is the job's deadline, 48 hours unless you set it, or a span before a date the world sets, stated in the price sentence. It counts only while the work is the brand's. At three quarters the job is at risk, at the end it is late, and price.late takes a share off the charge.

within answers one question: how long may the work take, and what happens when it runs long?

services/rush-translation.ts
import { Service } from 'services.dev'

// The same work in a day: within sets the deadline the price sentence states.
export default Service({
  name: 'Rush Translation',
  input: { document: 'file', to: 'language: a BCP 47 tag, such as fr-CA' },
  output: { document: 'file' },
  price: { usd: 79, within: '24h' },
})
captured/service-check.txt · magic values: none
$ npx services.dev check
services/rush-translation.ts · 4 lines · $79 per job, within 24 hours, charged on delivery.

On the price

KeyWrittenLeft outDoes
price.withinWithin | '${number}d before ${clock}''48h'The job's deadline, stated in the price sentence: hours or days from the order, or a span before one of the job's clocks, which it follows as the clock moves.
price.latePct | numbernone: a late job charges its priceWhat a late job costs you: a share of the price, '20%', or dollars, taken off the charge at release. The price sentence states it.

On a step

KeyWrittenLeft outDoes
withinWithinnoneThis step's own deadline, and the due date on its Task.

A span is elapsed time: '3d' is 72 hours, counted while the work is the brand's. A clock's rungs count civil days on the calendar instead.

While the jobIts deadline
works: AI, your code, a person at a step, an outside systemcounts
waits on a party's askpauses: waiting carries "clock": "paused", and the record prints how long
is deliveredstops: the job is final 7 days later, unless the buyer accepts or sends it back first

Every job carries its deadline as timer state, held on each pause with the time it had left and re-armed on each resume:

GET /jobs/job_7Hq2 · deadline
"deadline": { "within": "24h", "used": "6h 40m", "remaining": "17h 20m", "at_risk_at": null, "late_at": null, "paused_since": "2026-11-24T14:02:11Z" }
captured/grow-ask.txt · magic values: none
→ POST /translation {"document":"lease.pdf","to":"fr-CA","key":"grow","max_usd":49}
← job_7Hq2 · waiting · on party · $49.00 held
  waiting {"on":"party","since_at":"2026-10-25T14:02:11.000Z","step":"glossary","clock":"paused"}
captured/ask.txt · magic values: none
$ curl http://localhost:8787/jobs/job_7Hq2?wait=50
  record  glossary.terms · answered | glossary · buyer · answer · clock paused 2h 5min | translate · ai | review · ai · pass

Work due before a date the world sets states its deadline against that date. within: '5d before file' ends five days before the job's file clock and moves when the clock moves, at risk at three quarters of the span from the order. apis.tax's annual return writes within: '5d before file', late: '20%', and its price sentence reads "$1,800, filed 5 days before your deadline, 20% off if not". When the extension moves the date to file, the deadline moves with it. While the job waits on a party, its late_at never moves: only its at-risk timer is held. A clock with each gives the lead item's date.

KeyHolds
price.withinthe deadline, every default written: '48h' unless you set it
price.latewhat a late job is charged less, when you set it
price.final_afterhow long after delivery the job is final: '7d'
price.sentencethe price sentence every surface prints, deadline included
captured/api-quote.txt · 200 · request-id: req_cgqaTN · services-version: 2026-10-25 · magic values: none
$ curl http://localhost:8787/translation.json
{
  "type": "OK",
  "id": "translation",
  "name": "Translation",
  "price": {
    "per": "job",
    "cents": 4900,
    "within": "48h",
    "ai_cap": "20%",
    "final_after": "7d",
    "charged": "on delivery",
    "sentence": "$49 per job, within 48 hours, charged on delivery."
  },
  "surfaces": {
    "route": "POST /jobs {\"service\":\"translation\"}",
    "tool": "order_translation"
  }
}

A deadline you state is one you keep. escalate with when: 'at_risk' hands a step to a person the moment the job turns at risk with that step still open. Here the model is unavailable, and AI retries the translation on its backoff while the deadline runs:

captured/within-late.txt · magic values: metadata.sandbox.ai = down
$ cat services/rush-translation.ts
import { Service } from 'services.dev'

// The same work in a day, and the deadline backed: a job delivered late is charged 20% less. If the job is at risk
// while AI is still on the translation, a reviewer on your staff takes the step.
export default Service({
  name: 'Rush Translation',
  input: { document: 'file', to: 'language: a BCP 47 tag, such as fr-CA' },
  output: { document: 'file' },
  roles: { reviewer: {} },
  deliver: { translate: { by: 'ai', escalate: { to: 'reviewer', when: 'at_risk' } }, harden: { review: 'adversarial' } },
  price: { usd: 79, within: '24h', late: '20%' },
})

check reads the deadline back, with what each moment does and what a late job leaves you:

captured/within-late.txt · magic values: metadata.sandbox.ai = down
$ npx services.dev check services/rush-translation.ts
services/rush-translation.ts · 6 lines · $79 per job, within 24 hours, 20% off if late, charged on delivery.
  who     AI delivers every job. A reviewer on your staff takes translate when the job is at risk.
  steps   translate · ai · to reviewer at risk → harden · ai · adversarial
  time    at risk at 18h of 24h: translate goes to reviewer · late at 24h: the charge falls to $63.20
  ledger  +$79.00 · AI $0.84 (example) · you $78.16 · late: +$63.20 · AI $0.84 (example) · you $62.36
  apps    customer portal · owner console

At 18 of 24 hours the step is your reviewer's, waiting in the owner console's inbox with the time it has left:

Run
npx services.dev tick 18h --test-clock tc_7Hq5
magic values: metadata.sandbox.ai = down
tc_7Hq5 · 2026-11-27 15:00:00 → 2026-11-28 09:00:00 · 1 fired
  2026-11-28 09:00:00  job_7Hq8  job.at_risk · 18h of 24h, counted only while the work is the brand's · translate escalates to reviewer
captured/within-late.txt · magic values: metadata.sandbox.ai = down
$ curl http://localhost:8787/owner/jobs/job_7Hq8
← job_7Hq8 · waiting · on person · at risk · $79.00 held · due 2026-11-28 15:00
  record  translate · ai · the model is unavailable, 41 attempts | at_risk · 18h of 24h | translate · escalated · reviewer · at risk
  inbox   translate · reviewer · at risk · 6h left
  next    npx services.dev approve job_7Hq8 document=…

A step a member of a Pool holds escalates through the Pool instead: its Task's due date is the step's within, and the fill ladder raises it before then.

price.late is what a late job costs you. At 24 hours the charge falls by its share, and the buyer hears it with the new amount:

Run
npx services.dev tick 6h --test-clock tc_7Hq5
magic values: metadata.sandbox.ai = down
tc_7Hq5 · 2026-11-28 09:00:00 → 2026-11-28 15:00:00 · 1 fired
  2026-11-28 15:00:00  job_7Hq8  job.late · 24h of 24h · 20% off if late: the charge falls to $63.20

The reviewer delivers it 40 minutes later, at the lower charge:

Call
→ POST /owner/jobs/job_7Hq8/approve {"document":"contract.fr-CA.pdf"}
magic values: metadata.sandbox.ai = down
← job_7Hq8 · delivered · contract.fr-CA.pdf · $63.20 charged · late by 40m
  record  translate · ai · the model is unavailable, 41 attempts | at_risk · 18h of 24h | translate · escalated · reviewer · at risk | late · 24h of 24h · 20% off | translate · person · reviewer | harden · ai · adversarial · pass
  ledger  +$79.00 · late −$15.80 · AI $0.84 (example) · you $62.36

The credit comes off your line. AI, a member's pay and pass-through fees stay whole, and services.dev's take is on what the buyer is charged. A job that is late and never delivered charges nothing: the buyer can cancel it at $0, as any job before delivery.

EventFires whenThen
job.at_riskthree quarters of within has gone while the work was the brand'san open step with escalate.when: 'at_risk' goes to its role, and the owner console lists the job
job.lateall of within has gone while the work was the brand'sthe charge falls by price.late, and an open step with when: 'late' goes to its role
clock.movedthe clock a '5d before file' deadline follows movesthe deadline moves with it, and its at-risk and late timers re-arm
a clock's rungsa date the world sets comes nearClocks
captured/within-at-risk.txt · magic values: client.email = silent@example.test
$ cat services/staged.ts
import { Service } from 'services.dev'

// Ten hours to deliver, counted only while the work is the brand's: the staff check it, the client confirms, and
// the staff finish it. The client's time on their ask never counts.
export default Service({
  name: 'Staged Review',
  input: { client: 'party' },
  output: { draft: 'text', confirmed: 'text', final: 'text' },
  deliver: { draft: 'ai', check: 'person', confirm: { ask: 'client', to: 'answer' }, finish: 'person' },
  price: { usd: 60, within: '10h' },
})
captured/within-at-risk.txt · magic values: client.email = silent@example.test
$ npx services.dev tick 4h
tc_7Hq2 · 2026-10-28 19:00:00 → 2026-10-28 23:00:00 · 2 fired
  2026-10-28 19:30:00  job_7Hq2  job.at_risk · 7.5h of 10h, counted only while the work is the brand's
  2026-10-28 22:00:00  job_7Hq2  job.late · 10h of 10h, counted only while the work is the brand's
captured/within-at-risk.txt · magic values: client.email = silent@example.test
$ npx services.dev get job_7Hq2
job_7Hq2 · waiting · on person · $60.00 held
  record  draft · ai | check · person | confirm · client · answer · clock paused 3d
  next    npx services.dev approve job_7Hq2
AppShowsActs
Customer portaleach job's due time, paused while it waits on them, then at risk and late, with what a late job is chargedcancel before delivery, at $0
Delivery portalthe step's deadline as its Task's due date, beside its holdextend the hold once, never past the due date
Owner consolejobs at risk, then late, and each escalated step waiting in the inbox with its time leftyour staff take the step, in their own session

job.at_risk and job.late reach the buyer on the events feed and its webhooks.

A late job's credit comes off your line, as Late shows, and a late job never delivered charges nothing. A deadline and a late price run on every plan. Every kind of time, by plan: Keeping time.

OpAPICLIMCPSDKSiteDocsApp
service.quoteGET /:service.json/<service>: the price sentence1 error page

Operations has every column of every row.

None of its own.

The lines that use it in the example brands, as their files write them, and what check read aloud for each file.

legal · services/demand-letter.ts · 26 lines
    reply: { by: 'ai', within: '30d' },
npx services.dev check · captured/legal-walk.txt
services/demand-letter.ts · 26 lines · $149 per job, within 48 hours, charged on send's receipt.
  steps   consent · client · consent → draft · ai → attack · ai · adversarial · 3 lenses → sign · attorney · client · stamped · 2-item checklist → send · the recipient → courier · a courier · routed → reply · ai → settle · client · decide

The legal example runs every one of its files, captured.

tax · services/amended-return.ts · 22 lines
  price: { usd: 900, within: '30d', release: ['file.accepted', 'aar.accepted'] },
npx services.dev check · captured/tax-check.txt
services/amended-return.ts · 22 lines · $900 per job, within 30 days, charged when file or aar is accepted.
  steps   compute · code · cites depreciation and carryforward schedules → draft · ai · cites the workpapers and the records the organizer took → check · ai · adversarial · 2 lenses → prepare · preparer · * · 1-item checklist → sign · signer · * · stamped in the paid-preparer block → authorize · owner · sign → file · IRS and states, unless a partnership → aar · IRS · Form 8082, for a partnership · emits k1.adjusted

A missed date prices a late return in apis.tax.