---
title: "The SDKs"
url: https://services.dev/docs/sdk
---
# The SDKs

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

> Two packages for two readers. You build with services.dev. Your buyers' developers get a typed client for your brand, generated from your Services.

There are two packages, for two readers.

| Package | Who reads it | What it holds |
|---|---|---|
| `services.dev` | you, building | `Service()`, `dev`, `check`, the CLI, and `services.dev/agent`, a buyer for any brand |
| your brand's client | your buyers' developers | one typed function per operation your brand serves, named for your brand |

## Your brand's client

One command writes it from your manifest:

```txt
$ npx services.dev sdk
  wrote   sdk/index.js · sdk/index.d.ts · sdk/package.json
  next    import { connect } from './sdk/index.js'
```

Every function is one operation row's SDK column, the same row that gives the API its route and MCP its tool. Each Service gets `<service>.order()`. The job, ask, event, watch and test-clock calls follow, each documented with its route and the error it throws:

```ts
export interface Client {
  translation: {
    /** Order a job, or get the offer: the price and the exact next call. A Service whose price has every answers a subscription, sbn_…. `POST /jobs` with service: "translation"; refused with ServicesError<'service.order'>. */
    order(input: Order<"translation">): Promise<Job<"translation"> | Offer>
  }
  jobs: {
    /** Read a job, now or after waiting up to wait seconds while it works or waits on a person. Waiting on a party, it answers at once with the link to hand them. `GET /jobs/:id`; refused with ServicesError<'job.get'>. */
    get(id: JobId, params?: { wait?: number; expand?: string[] }): Promise<Job>
    /** Wait up to 50 seconds for a job to finish, through any person's line. `GET /jobs/:id?wait=<seconds>`; refused with ServicesError<'job.wait'>. */
    wait(id: JobId, params?: { wait?: number }): Promise<Job>
    /** Accept a delivered job. It is final; a job nobody accepts is final 7 days after delivery. `POST /jobs/:id/accept`; refused with ServicesError<'job.accept'>. */
    accept(id: JobId): Promise<Job>
    …
  }
  asks: {
    /** Reissue an open ask's link, to hand to its party again. The old one stops working at once. `POST /asks/:id/reissue`; refused with ServicesError<'ask.reissue'>. */
    reissue(id: AskId, params?: { key?: string }): Promise<Ask>
  }
  …
}
```

## Connect

```ts
export interface ConnectOptions { key?: `sk_${'test' | 'live'}_${string}`; origin?: string; fetch?: (request: Request) => Promise<Response>; version?: "2026-10-25"; maxRetries?: number }
export declare function connect(options?: ConnectOptions): Client
```

`key` is a buyer's key, sent as a bearer token: `sk_test_` in the sandbox, `sk_live_` for real money. On localhost, a call with none uses the test key `dev` printed. `version` pins the API version every answer carries in `Services-Version`. A retry reuses its first Idempotency-Key, so a retried order never orders twice. `fetch` lets a test run the brand in the same process.

## A call

```txt
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
```

An order without `max_usd` returns the offer as a value. Its `next` carries the arguments that order, key included. How every type in this file is made, and what it refuses at compile time, is [Safe to depend on](https://services.dev/docs/sdk/types.md).

## Published under your name

Each deploy publishes the client to npm under your brand's name, automatically, with nothing for you to sign or approve. `services.dev sdk` also writes the package into your folder, `package.json` already named for your brand, so a test can import it before you deploy.

## Which one to reach for

Your buyers' developers use your brand's client: it's typed to your Services, and a wrong field or stage fails before it runs. An agent that buys from many brands uses `services.dev/agent`, which needs no generated code, only a brand's card ([A buyer that is software](https://services.dev/docs/sdk/agent.md)).

---

- Up: [Your first paid job](https://services.dev/docs.md)
- Previous: [What an agent can never do](https://services.dev/docs/mcp/never.md)
- Next: [Safe to depend on](https://services.dev/docs/sdk/types.md)
