Deadlines
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?
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' },
})$ npx services.dev check
services/rush-translation.ts · 4 lines · $79 per job, within 24 hours, charged on delivery.On the price
| Key | Written | Left out | Does |
|---|---|---|---|
price.within | Within | '${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.late | Pct | number | none: a late job charges its price | What 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
| Key | Written | Left out | Does |
|---|---|---|---|
within | Within | none | This 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 job | Its deadline |
|---|---|
| works: AI, your code, a person at a step, an outside system | counts |
| waits on a party's ask | pauses: waiting carries "clock": "paused", and the record prints how long |
| is delivered | stops: 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:
"deadline": { "within": "24h", "used": "6h 40m", "remaining": "17h 20m", "at_risk_at": null, "late_at": null, "paused_since": "2026-11-24T14:02:11Z" }→ 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"}$ curl http://localhost:8787/jobs/job_7Hq2?wait=50
record glossary.terms · answered | glossary · buyer · answer · clock paused 2h 5min | translate · ai | review · ai · passWork 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.
| Key | Holds |
|---|---|
price.within | the deadline, every default written: '48h' unless you set it |
price.late | what a late job is charged less, when you set it |
price.final_after | how long after delivery the job is final: '7d' |
price.sentence | the price sentence every surface prints, deadline included |
$ 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:
$ 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:
$ 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 consoleAt 18 of 24 hours the step is your reviewer's, waiting in the owner console's inbox with the time it has left:
npx services.dev tick 18h --test-clock tc_7Hq5tc_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$ 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:
npx services.dev tick 6h --test-clock tc_7Hq5tc_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.20The reviewer delivers it 40 minutes later, at the lower charge:
→ POST /owner/jobs/job_7Hq8/approve {"document":"contract.fr-CA.pdf"}← 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.36The 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.
| Event | Fires when | Then |
|---|---|---|
job.at_risk | three quarters of within has gone while the work was the brand's | an open step with escalate.when: 'at_risk' goes to its role, and the owner console lists the job |
job.late | all of within has gone while the work was the brand's | the charge falls by price.late, and an open step with when: 'late' goes to its role |
clock.moved | the clock a '5d before file' deadline follows moves | the deadline moves with it, and its at-risk and late timers re-arm |
| a clock's rungs | a date the world sets comes near | Clocks |
$ 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' },
})$ 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$ 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| App | Shows | Acts |
|---|---|---|
| Customer portal | each job's due time, paused while it waits on them, then at risk and late, with what a late job is charged | cancel before delivery, at $0 |
| Delivery portal | the step's deadline as its Task's due date, beside its hold | extend the hold once, never past the due date |
| Owner console | jobs at risk, then late, and each escalated step waiting in the inbox with its time left | your 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.
| Op | API | CLI | MCP | SDK | Site | Docs | App |
|---|---|---|---|---|---|---|---|
service.quote | GET /:service.json | /<service>: the price sentence | 1 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.
reply: { by: 'ai', within: '30d' },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 · decideThe legal example runs every one of its files, captured.
price: { usd: 900, within: '30d', release: ['file.accepted', 'aar.accepted'] },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.adjustedA missed date prices a late return in apis.tax.