---
title: "Service()"
url: https://services.dev/docs/service
captured: [legal-walk.txt, tax-check.txt, walk.txt]
codes: [input_invalid]
---
# Service()

URL: https://services.dev/docs/service

> Service({...}) is the only constructor. One file in services/ is one kind of work you sell and how it earns; its path is its address, and it runs nothing until it serves.

## Smallest form

```ts title="services/translation.ts"
import { Service } from 'services.dev'

export default Service({
  name: 'Translation',
  input: { document: 'file', to: 'language: a BCP 47 tag, such as fr-CA' },
  output: { document: 'file' },
  price: 49,
})
```

```bash
npx services.dev check
```

```txt title="captured/walk.txt · captured"
services/translation.ts · 4 lines · $49 per job, within 48 hours, charged on delivery.
  who     AI delivers every job.
  steps   draft · ai → review · ai · adversarial
  agent   order_translation · 24 of 30 words · bought job_7Hq2 · $49.00 · no person
  ledger  +$49.00 · AI $0.84 (example) · you $48.16
  margin  at least $39.20 a job (80%) · a job that stops costs you at most $9.80
  apps    customer portal · owner console
```

## Every form

| Key | Written | Left out | Does |
|---|---|---|---|
| `name` | `string` | required | What buyers call the work: the Site's title and the tool description's first word. |
| [`input`](https://services.dev/docs/service/fields.md) | `{ <field>: <type word> }` | required | What a buyer sends, field by field. |
| [`output`](https://services.dev/docs/service/fields.md) | `{ <field>: <type word> }` | required | What the buyer gets back. |
| [`deliver`](https://services.dev/docs/service/deliver.md) | `Deliver` | `'ai'`, briefed by services/&lt;id>.md | Who does the work: `'ai'`, a person or a role word, a URL, your function, or named steps in order. |
| [`human`](https://services.dev/docs/people.md) | `Human` | none: AI delivers every job alone | Optional: `'risky'`, `'always'`, your rule, or a credential that signs. |
| [`price`](https://services.dev/docs/money.md) | `number \| PriceForm` | required | What a job costs: dollars, or `{ usd, payout, within, approved, share, of, release }`; with `every`, what each cycle of a subscription costs, and `includes` its allowances. |
| [`outcome`](https://services.dev/docs/money/outcomes.md) | `Outcome` | none | What a share is paid on: the amount, who proves it, how a payment finds the job, and how long the money stays held. |
| [`on`](https://services.dev/docs/service/subscriptions.md) | `Triggers` | none | What starts jobs on a subscription: dates in `schedule`, events in `event`, each minting this Service or the one it names. Needs `price.every`. |
| `description` | `string` | written from name, input and output | One sentence for the Site's lede and the tool description. |
| [`review`](https://services.dev/docs/service/review.md) | `Review` | `'adversarial'` | How the AI checks every job before it ships: `'adversarial'`, or `'affirmative'` for creative work. |
| [`policy`](https://services.dev/docs/service/policy.md) | `{ <name>: PolicyEntry }` | none | What an order may set, within your bounds: the buyer's mechanics, and the merits only the client sets. |
| [`signals`](https://services.dev/docs/service/signals.md) | `{ <name>: Signal }` | none | Named facts that route the work: a predicate, `silent` for a span, or `inbound`; `goto` moves the job. |
| [`ends`](https://services.dev/docs/service/ends.md) | `{ <end>: EndSpec }` | none | Ends beyond accepted, canceled, lapsed and declined, each with its charge, who may reach it, and what can follow. |
| [`status`](https://services.dev/docs/service/status.md) | `Status` | `'live'` | `'live'`, `'preview'` (test keys only) or `'roadmap'` (published, answering not_live). |
| [`roles`](https://services.dev/docs/people/roles.md) | `{ [K in R]: Role }` | none: a role word is your staff | Who may do, sign or review a step: each role's Pool, credential, region, pay and separation. A step's role word names one. |
| [`rules`](https://services.dev/docs/people/rules.md) | `Rules` | none: no act is reserved | The rules table, from rules.ts or a package you pin: which acts are reserved, open or refused. |
| [`subject`](https://services.dev/docs/service/subject.md) | `SubjectSpec` | none | What outlives the job: a subject keyed by an input or a receipt, the member of record on it, its kept facts, its periods, and the Service that watches its clocks. |
| [`clocks`](https://services.dev/docs/service/clocks.md) | `{ <name>: Clock }` | none | Dates the world sets, by name: when each is due, who answers for it, the rungs that notice, escalate, ask for an acknowledgement and run a step, the steps that move it, and what the owner chooses at the end. The rungs, moves, `every` and `on` run, and the clock's own calls acknowledge, satisfy, choose and name its owner. |
| [`talk`](https://services.dev/docs/service/talk.md) | `Talk \| false` | on, the AI answering the client | The job's thread: the AI answers the client in it, and holds an answer in an advice class the rules reserve until a member of `adopt` adopts it. `false` turns it off. |
| [`calendar`](https://services.dev/docs/service/time.md#the-calendar) | `Calendar` | the brand's | This Service's own calendar, in place of the brand's: its zone, closed days, closing minute and roll. |

**`subject`, written out**

| Key | Written | Left out | Does |
|---|---|---|---|
| `subject.kind` | `string` | required | What it is, in your words: 'patent family', 'trademark', 'company'. |
| `subject.key` | `(keyof I \| keyof O) & string` | required | The input, or the output a receipt fills, whose value names it: `application_number`, `ein`. |
| `subject.of_record` | `{ role: RoleWord; named_at: <step>; custodian?: (RoleWord \| 'brand')[] }` | none | The member of record: whoever attests at `named_at` stays of record on the subject across jobs. Withdrawal names a successor; a member whose credential or coverage lapses leaves it vacant, and `custodian` covers it, rung by rung, then the brand. |
| `subject.keeps` | `K` | none | Facts the subject keeps across jobs, each from the output that writes it: `{ carryforwards: 'carryforwards', tax: 'tax' }`, typed as that output is. Written only when a job ends accepted, never when it is canceled or lapses, and keyed by the period the job held. Insert-only: each value records `{ value, job, step, at, period }`, and nothing is overwritten. |
| `subject.period` | `(keyof I & string) \| ((job) => string)` | none | One live or ended job of this Service per subject per period: the input that names the period, or a function of the order. A second order for a period answers 409 `period_taken`, its next the follow-on the end offers, such as an amendment. A canceled job frees its period. |
| `subject.steward` | `AnyService` | none | The Service that keeps the subject's clocks once a job ends with one still running: ordered with the subject's key, it keeps the date in someone's hands. Until it is, the customer portal offers it beside an ask in which the client acknowledges they own the date, and nothing lapses into a gap between jobs. |

**`price`, for a subscription**

| Key | Written | Left out | Does |
|---|---|---|---|
| `price.every` | `Every` | none | A subscription: an order is a Subscription, `sbn_…`, charged `usd` at the start of each cycle, and `on` mints its jobs. |
| `price.includes` | `{ <name>: number }` | none | Allowances per cycle, by Service id: inside one a job is $0 and counted; past it, the order answers 402 OFFER at the job's price. Renewal resets them. |

## From what the work needs to the key

| The work needs | Write | Read |
|---|---|---|
| a credentialed person to do or sign a step | a role from a Pool, then `by: '<role>'` and `stamp: true` on the step | [Roles](https://services.dev/docs/people/roles.md) |
| some acts reserved to a credential, region by region | `rules`: rows that are reserved, open or refused | [Rules](https://services.dev/docs/people/rules.md) |
| that person paid for each act, fixed when its Task posts | `pay` on the role | [Roles](https://services.dev/docs/people/roles.md) |
| the client's consent, signature or decision | an ask step: `{ ask: 'client', to: 'consent' \| 'sign' \| 'decide' }` | [Asks](https://services.dev/docs/service/ask.md) |
| documents or answers from the client first | an ask with `to: 'answer'` or `'upload'`, and `items` | [Intake and request lists](https://services.dev/docs/service/intake.md) |
| permission to act in the client's name, for a while | a `grant` ask, and `needs` on the steps that use it | [Grants and decisions](https://services.dev/docs/service/grants.md) |
| work hardened before anyone signs it | a review step, with lenses and rounds | [Review steps](https://services.dev/docs/service/review.md) |
| a floor, a cap or a required field on the output | `guard` on the step | [Guards](https://services.dev/docs/service/guards.md) |
| choices only the client may make | `policy`, with `binds: 'merits'` | [Policy](https://services.dev/docs/service/policy.md) |
| a calculation that follows a named authority | a function step with `cite: '<authority>'` | [Your code as a step](https://services.dev/docs/service/code.md) |
| every extracted value traced to its page | `cite: 'source'` on the AI step | [Sourced output](https://services.dev/docs/service/sourced.md) |
| a filing with a system you don't control, and its receipt | a submit step: `to`, `receipt`, `rejected`, `perfect`, `fallback` | [Sending to outside systems](https://services.dev/docs/service/submit.md) |
| the charge held until that system answers | nothing: a Service with a submit step charges on its receipt | [Sending to outside systems](https://services.dev/docs/service/submit.md) |
| linked filings, the lead one first | `each: { of, lead }` on the submit step | [One submission per element](https://services.dev/docs/service/each.md) |
| advice only under a credentialed member's name | rules rows of `kind: 'advice'`, and `talk: { adopt: '<role>' }` | [Talk](https://services.dev/docs/service/talk.md) |
| a client who can stop midway and owe only for acts done | `ends: { withdrawn: { charge: 'acts', by: 'client' } }` | [How a job ends](https://services.dev/docs/service/ends.md) |
| facts that carry from one job to the next | `subject` with `keeps` | [Subjects](https://services.dev/docs/service/subject.md) |
| one member of record across many jobs | `subject.of_record` | [Of record](https://services.dev/docs/people/of-record.md) |
| one job per year, quarter or month, and an amendment after | `subject.period`, and `follow` on the end | [One job per period](https://services.dev/docs/service/periods.md) |
| a date a registry or a regulator sets, and someone who answers for it | `clocks`: `due`, `owner`, `rungs`, `moves`, `lapse` | [Clocks](https://services.dev/docs/service/clocks.md) |
| a step run before that date, such as an extension | a `run <step> <n>d` rung on the clock | [Clocks](https://services.dev/docs/service/clocks.md#what-happens-on-its-own) |
| that date on a calendar: its zone, weekends, closed days and closing minute | `calendar` on `Brand()`, or on the Service | [Keeping time](https://services.dev/docs/service/time.md#the-calendar) |
| a date closed by a step's commit, with no one acting | `satisfied_by` on the clock | [Clocks](https://services.dev/docs/service/clocks.md#two-dates-two-clocks) |
| a date nothing moves, and no work after it | `hard: { bars }` on the clock | [Clocks](https://services.dev/docs/service/clocks.md#a-date-nothing-moves) |
| a deadline that costs you when it's missed, and a person before it is | `price.late`, and `escalate: { when: 'at_risk' }` | [Deadlines](https://services.dev/docs/service/within.md#late) |
| last period's figures in this period's code | `keeps`, read with `job.subject.fact` | [Subjects](https://services.dev/docs/service/subject.md#kept-facts) |
| a rejection that re-asks only what its code names, element by element | `rejected: { <code>: { to: '<ask>', items } }` | [One submission per element](https://services.dev/docs/service/each.md#one-rejects-the-others-stay-filed) |
| work that recurs, on dates or on events | `price.every`, `on.schedule` and `on.event` | [Schedules and triggers](https://services.dev/docs/service/subscriptions.md) |
| the period each scheduled date orders, such as last year | `each` on the schedule entry | [Schedules and triggers](https://services.dev/docs/service/subscriptions.md#the-dates-a-schedule-takes) |
| some jobs included each cycle | `price.includes` | [Subscriptions](https://services.dev/docs/money/subscriptions.md#inside-the-allowance) |
| a listing before it takes orders | `status: 'roadmap'` or `'preview'` | [Publish before it takes orders](https://services.dev/docs/service/status.md) |
| your own words on every surface, such as a different word for a job | `Brand.words` | [Your brand's words](https://services.dev/docs/service/brand-words.md) |

## Beside Service()

| Constructor | Its file | What it holds | Read |
|---|---|---|---|
| `Service({...})` | `services/<id>.ts`, one per kind of work | the work, who does each step, and how it earns | this page |
| `Rules({...})` | `rules.ts`, or a package you pin | which acts are reserved, open or refused, per Service and shared, under one version | [Rules](https://services.dev/docs/people/rules.md) |
| `Pool({...})` | `pools/<name>.ts`, which several brands may share | one kind of people: their roles and credentials, how they join and verify, how a Task reaches them | [Pool()](https://services.dev/docs/people/pool.md) |
| `Brand({...})` | `brand.ts`, optional | the domain, the brand's own words, and the credentials it holds | [Brand()](https://services.dev/docs/service/brand.md) |

### All four in the legal example, as `check` reads them

```bash
npx services.dev check
```

```txt title="captured/legal-walk.txt · magic values: member = mbr_eligible"
brand.ts · 10 lines · legal · api.lawyer · words job → matter, subject → asset, task → review, member → signer, pool → network, end → outcome
rules.ts · 36 lines · rules 2026-10 · 6 Services · 16 rows · 1 shared
pools/attorneys.ts · 20 lines · attorneys · 4 roles: attorney, practitioner, watcher, student · joins in 3 steps · cascade · hold 24h
✓ ready to sell · 6 Services · each keeps at least 3% of every job
```

## Addresses

| Surface | `services/translation.ts` is |
|---|---|
| API | `POST /jobs {"service":"translation"}`, described in `/openapi.json` |
| CLI | `npx services.dev translation k=v …` |
| MCP | `order_translation`, at `/mcp` |
| SDK | `connect({ origin }).translation.order(input)` |
| Site | `/translation` |
| Docs | `/llms.txt` |
| App | customer portal at `/portal`, owner console at `/console` |

The tool description agents read: `Translation: send a document and a language; get a document back. $49 per job, within 48 hours, charged on delivery. AI delivers every job.`

## What TypeScript says

| The rule | What TypeScript says |
|---|---|
| The price is required. | `Property 'price' is missing.` |
| A lone function returns the whole output. | `Property 'recovery' is missing in type '{ document: InFile; }' but required in type '{ readonly document: FileRef; readonly recovery: number; }'.` |
| A share is of a money output. | `Type '"document"' is not assignable to type '"recovery"'.` |
| Order options own their names. | `Type '"text"' is not assignable to type '"✗ key is an order option; rename this input"'.` |
| `before` names a step. | `Type '"certfy"' is not assignable to type '"certify" \| "delivery" \| "draft" \| undefined'. Did you mean '"certify"'?` |
| A rule says why. | `Property 'because' is missing.` |
| `after` names a step. | `Type '"drat"' is not assignable to type 'After<"draft" \| "examine"> \| undefined'. Did you mean '"draft"'?` |
| A review hardens or extends. | `Type '"none"' is not assignable to type 'Review \| undefined'.` |
| An ask asks the buyer or a party input. | `Type '"document"' is not assignable to type '"applicant" \| "buyer" \| undefined'.` |

## The manifest

| As JSON | Read by |
|---|---|
| [The manifest](https://services.dev/docs/service/manifest.md): every default written, money in integer cents | every surface: the API, CLI, MCP server, SDK, Site, docs and apps |

## Seven surfaces

| Op | API | CLI | MCP | SDK | Site | Docs | App |
|---|---|---|---|---|---|---|---|
| `service.order` | `POST /jobs`, `POST /:service` | `<service> k=v …` | `order_<service>` | `<service>.order()` | `/<service>`: the order form | 10 error pages | customer portal |
| `service.quote` | `GET /:service.json` |  |  |  | `/<service>`: the price sentence | 1 error page |  |
| `brand.manifest` | `GET /manifest.json` |  |  |  | `/manifest.json` |  |  |
| `brand.pricing` | `GET /pricing` |  |  |  | `/pricing` |  |  |

[Operations](https://services.dev/docs/reference/operations.md) has every column of every row.

## Refusals

| Code | Category | HTTP | Why |
|---|---|---|---|
| [`input_invalid`](https://services.dev/docs/reference/errors.md#input_invalid) | invalid_request | 422 | The order doesn't match the Service's input. |

| `check` gate | Why |
|---|---|
| [`description_too_long`](https://services.dev/docs/reference/check/description_too_long.md) | The tool description is over 30 words, so agents skim past it. |
| [`price_unstated`](https://services.dev/docs/reference/check/price_unstated.md) | The tool description omits the price or when it's charged. |
| [`price_below_cost`](https://services.dev/docs/reference/check/price_below_cost.md) | A delivered job could lose money: payout, nested Services and the AI cap add up to more than the price. |
| [`upgrade_below_payout`](https://services.dev/docs/reference/check/upgrade_below_payout.md) | The approved price adds less than the person's payout. |
| [`agent_cannot_buy`](https://services.dev/docs/reference/check/agent_cannot_buy.md) | An agent couldn't order, wait for and accept a job. |
| [`service_id_reserved`](https://services.dev/docs/reference/check/service_id_reserved.md) | A Service's file name, or a portal's, takes a word services.dev's own routes use. |
| [`surface_drift`](https://services.dev/docs/reference/check/surface_drift.md) | A surface serves a call with no row, or lacks a live row. |

## In the example brands

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

```ts title="legal · services/docket-watch.ts · 10 lines"
export default Service({
  name: 'Docket Watch',
  description: 'A docket watched every week, and an office action routed to its response.',
  input: { matter: 'text: an application or file number' },
  output: { status: 'text', next_due: 'date' },
  rules,
  subject: { kind: 'patent family', key: 'matter' },
  roles: { watcher: { pool: attorneys, pay: { usd: 4, on: 'act' } } },
  on: { schedule: { check: 'every monday' }, event: { 'office_action.issued': trademarkResponse } },
  deliver: { check: 'ai', ack: { by: 'watcher', when: job => job.clocks.inside('7d') } },
  price: { usd: 12, every: 'month' },
})
```

```txt title="npx services.dev check · captured/legal-walk.txt"
services/docket-watch.ts · 10 lines · $12 per month, charged at the start of each month.
  steps   check · ai → ack · patent watcher → review · ai · adversarial
```

[The legal example](https://services.dev/docs/examples/legal.md) runs every one of its files, captured.

```ts title="tax · services/question.ts · 10 lines"
export default Service({
  name: 'Tax Question',
  description: 'A tax question, answered with its sources.',
  input: { owner: 'party: who asks', ein: 'text: such as 12-3456789', question: 'text: such as Can we deduct the van?' },
  output: { answer: 'text' },
  rules,
  subject: { kind: 'taxpayer', key: 'ein' },
  roles: { signer: { pool: preparers, pay: 25 } },
  talk: { with: 'owner', adopt: { by: 'signer', may: ['adopt', 'revise', 'refuse'], checklist: ['the authority cited says what the answer says', 'the facts it assumes are the facts in the thread'] } },
  deliver: { answer: { by: 'ai', cite: 'the code, its regulations and the agency guidance it rests on' } },
  price: 49,
})
```

```txt title="npx services.dev check · captured/tax-check.txt"
services/question.ts · 10 lines · $49 per job, within 48 hours, charged on delivery.
  steps   answer · ai · cites the code, its regulations and the agency guidance it rests on → review · ai · adversarial
```

[The tax example](https://services.dev/docs/examples/tax.md) runs every one of its files, captured.

---

- Up: [Your first paid job](https://services.dev/docs.md)
- Previous: [Your business](https://services.dev/docs/business.md)
- Next: [Fields and type words](https://services.dev/docs/service/fields.md)
