Safe to depend on

The running jobjob_7Hq2 · delivered · document.fr-CA.pdf · $49.00 charged

Your brand's client types each Service's names as literal unions, returns an OFFER as a value, narrows every refusal to its operation's codes, and verifies webhooks into a typed event.

A developer building on your brand should learn at compile time what would otherwise break in production. Every type in the generated client comes from your manifest and the registries, so it says exactly what your brand serves, and nothing more.

Each Service's input, output, stages, ends, policy and signals:

TypeScript
  translation: {
    input: { document: File; to: string }
    output: { document: File }
    stage: "draft" | "review"
    end: "lapsed"
    policy: {}
    signal: never
  }

So a typo fails before it runs, in the words tsc printed:

Output
// a field Translation doesn't take
await lingua.translation.order({ document: lease, to: 'fr-CA', pages: 3 })
  ✗ Object literal may only specify known properties, and 'pages' does not exist in type 'Order<"translation">'.
// an end no Service of lingua's declares
await lingua.jobs.end('job_7Hq2', { end: 'won' })
  ✗ Type '"won"' is not assignable to type '"lapsed"'.
// a stage Translation never shows
if (job.service === 'translation' && job.stage === 'certify') return
  ✗ This comparison appears to be unintentional because the types '"draft" | "review" | null' and '"certify"' have no overlap.
// an id that isn't a job's
await lingua.jobs.get('7Hq2')
  ✗ Argument of type '"7Hq2"' is not assignable to parameter of type '`job_${string}`'.

An order without max_usd returns the offer. Nothing throws, and 'type' in offer tells the two apart:

TypeScript
  const offer = await lingua.translation.order({ document: lease, to: 'fr-CA', key: 'lease' })
  if ('type' in offer) offer.price.sentence satisfies string

  const ordered = await lingua.translation.order({ document: lease, to: 'fr-CA', max_usd: 49, key: 'lease' })
  if ('type' in ordered) return ordered.next.http
Output
const offer = await lingua.translation.order({ document: lease, to: "fr-CA", key: "lease" })
← OFFER · $49 per job, within 48 hours, charged on delivery.   returned, not thrown
  next    order_translation {"document":{"url":"https://example.com/lease.pdf","name":"lease.pdf"},"to":"fr-CA","max_usd":49,"key":"lease"}

const job = await lingua.translation.order(offer.next.arguments)
← job_7Hq2 · working · $49.00 held

A job that isn't there is a not_found refusal, EMPTY at 404, thrown like any other.

A refusal throws a ServicesError subclass for its type: InvalidRequestError, BlockedError, ConflictError and the rest. isServicesError(e, op) narrows e.code to the codes that operation can answer:

TypeScript
  try {
    await lingua.jobs.accept(job.id)
  } catch (e) {
    if (isServicesError(e, 'job.accept')) e.code satisfies 'not_found' | 'not_delivered' | 'needs_the_party' | 'key_missing' | 'key_invalid' | 'body_invalid' | 'version_unknown' | 'api_error'
    if (e instanceof ConflictError) e.category satisfies 'conflict'
  }
Output
await lingua.jobs.accept("job_7Hq2")   // still working
× throws ConflictError<'job.accept'> · 409 · BLOCKED · not_delivered
  reason  Only a delivered job can be accepted or sent back.
  isServicesError(e, 'job.accept')  true

await lingua.jobs.get("job_none")
× throws InvalidRequestError<'job.get'> · 404 · EMPTY · not_found
  reason  No job job_none.
  isServicesError(e, 'job.get')  true

Test for a code the operation never answers, and tsc says so:

Output
// a code job.accept never answers
if (isServicesError(e, 'job.accept') && e.code === 'ask_expired') return
  ✗ This comparison appears to be unintentional because the types '…' and '"ask_expired"' have no overlap.

webhooks.verify() checks the services-signature header against the watch's whsec_ secret and its age, then returns the event narrowed on type:

TypeScript
export function receive(body: string, header: string, secret: string) {
  const event = webhooks.verify(body, header, secret)
  if (event.type === 'job.delivered') return event.job
}
Output
export type EventType = "job.ordered" | "step.committed" | "step.skipped" | "job.paused" | "job.delivered" | "job.sent_back" | "job.accepted" | "job.canceled" | "job.ended" | "job.at_risk" | "job.late" | "policy.set" | "signal.raised" | "outcome.reported" | "charge.held" | "charge.collected" | "charge.earned" | "charge.released" | "charge.voided" | "charge.refunded" | "submission.sent" | "receipt.accepted" | "receipt.rejected" | "receipt.timed_out" | "ask.opened" | "item.done" | "ask.reminded" | "ask.done" | "ask.expired" | "ask.voided" | "ask.relinked" | "grant.given" | "grant.expired" | "grant.revoked" | "thread.posted" | "message.sent" | "consent.given" | "clock.opened" | "clock.noticed" | "clock.escalated" | "clock.ack_due" | "clock.ack_missed" | "clock.ran" | "clock.moved" | "clock.due" | "clock.satisfied" | "clock.rolled" | "clock.recurred" | "clock.acked" | "clock.chosen" | "clock.assigned" | "clock.outlived" | "subscription.created" | "subscription.renewed" | "subscription.minted" | "allowance.used" | "allowance.exceeded" | "subscription.canceled"
export type Event = { [T in EventType]: EventOf<T> }[EventType]

services-signature: t=1792936931,v1=bcd16e28974679fcbeb5e926812867dd1029ffd7caa115baeff4e4d0504dcd48
const event = webhooks.verify(body, header, secret)
← job.ordered · evt_5gX1X2B5swzY61 · job_7Hq2

// the body changed
× throws SignatureVerificationError: The signature doesn't match: wrong secret, or the body changed.
// ten minutes late
× throws SignatureVerificationError: The signature is 600 seconds old, past the 300-second tolerance.

Watching an event a buyer never receives is a type error too:

Output
// an event a watch never receives
await lingua.watches.create({ url: 'https://lingua.example/hook', events: ['step.proposed'] })
  ✗ Type '"step.proposed"' is not assignable to type 'EventType'.

A new stage, end, event or error code is additive on the wire, but it breaks a switch that was exhaustive over the old union. diff marks every such change, so you see it before your buyers' builds do. connect({ version }) pins the API version a client was written against.