# Interfaces

URL: /code/builders/software/interfaces · Id: `page:code/builders/software/interfaces`

> Every Product ships its Verbs through eight Interfaces, and each Verb keeps one name, one Action, and one set of rules on all of them.

An [Interface](/business/nouns/interface) is how a person or an [Agent](/business/nouns/agent) reaches a [Product](/business/nouns/product). Every Product ships the same [Verbs](/business/nouns/verb) through eight: API, MCP, CLI, SDK, [Site](/business/nouns/site), [App](/business/nouns/app), docs, and webhooks.

## One Verb, every Interface
Each Verb has one name, `Noun.verb`, on every Interface: the API route, the SDK method, the CLI command, the MCP call, and the webhook [Event](/business/nouns/event). Every Interface checks the same [Permissions](/business/nouns/permission) and [Limits](/business/nouns/limit), and every call returns the same [Action](/business/nouns/action). An Interface changes how a call reads, never what it does.

Here is one Verb on every Interface. Pick an Interface in the header, and every example on the site follows.

**Ask**

```text
Qualify the latest lead with an offer.
```

**API**

```bash
curl -X POST https://api.sb/shiftnote.app/leads/lead_1/qualify \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"offer":"offer_1"}'
```

```json
{
  "api": "https://api.sb/shiftnote.app",
  "deal": {
    "$id": "https://api.sb/shiftnote.app/deals/deal_1",
    "$type": "Deal",
    "status": "Open"
  },
  "lead": {
    "$id": "https://api.sb/shiftnote.app/leads/lead_1",
    "$type": "Lead",
    "status": "new"
  },
  "action": {
    "$id": "https://api.sb/shiftnote.app/actions/action_1",
    "operation": "Lead.qualify",
    "status": "done"
  },
  "links": {
    "self": "https://api.sb/shiftnote.app/actions/action_1"
  }
}
```

**SDK**

```ts
import { $ } from 'startup-builder'

const { deal, lead } = await $.Lead.qualify({ lead: 'lead_1', offer: 'offer_1' })
```

**CLI**

```bash
npx startup-builder do Lead.qualify --json '{"lead":"lead_1","offer":"offer_1"}'
```

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "code": "return await $.Lead.qualify({ lead: 'lead_1', offer: 'offer_1' })"
  }
}
```

## What each Interface is for
| Interface | Who uses it | What it gives them |
| --- | --- | --- |
| API | Any program | One HTTPS route per Verb, JSON in and out |
| SDK | TypeScript code | Typed methods on `$`, from the `startup-builder` package |
| CLI | Terminals, scripts, coding Agents | One `do` command per Verb, with JSON output outside a terminal |
| MCP | Agents | A `do` call that runs a short program against the SDK |
| Site | [Visitors](/business/nouns/visitor) and buyers | Public [Pages](/business/nouns/page) and [Offers](/business/nouns/offer) under the [Brand](/business/nouns/brand) |
| App | Each [Persona](/business/nouns/persona) at work | Screens over the same Verbs |
| docs | Developers and Agents | The contract in HTML and Markdown, generated from the definition |
| webhooks | Other systems | Each Event, signed, as it happens |

Docs are an Interface because they compile from the same definition. A Verb's reference cannot describe behavior the API lacks.

## Screens for people
An App derives three screens from each [Noun](/business/nouns/noun). A list draws as a [`DataTable`](/code/ui/components/data-table) or a [`Board`](/code/ui/components/board), and a record as a [`ThingDetail`](/code/ui/components/thing-detail). Each Verb the Persona may run gets a [`VerbSheet`](/code/ui/components/verb-sheet).

The first session opens on the onboarding checklist and ends at first value, as the [Product Template](/business/nouns/product-template) defines it: a shared gallery, a closed month. An empty list renders one sentence and the one Verb that fills it, such as `No galleries yet. Create gallery`, never a blank table.

Every `pending` state has exactly one rendering, as [`ActionStatus`](/code/ui/components/action-status) defines.

## Each caller sees its own Verbs
Authorization runs once, below every Interface, and narrows everything above it. A record's `actions` map lists only the Verbs this caller may run on it now. An Agent's MCP tool list holds only its [Role](/business/nouns/role)'s Permissions, and an App draws only the buttons its Persona may press.

Two callers reading the same record can see different Verbs. Neither sees a Verb it would be refused.

## One address, three renderings
Every record has one URL, its `$id`, and that URL answers in three forms. A browser gets HTML, code gets JSON, and an Agent sending `Accept: text/markdown` gets Markdown in fewer tokens. The data and the links are identical in all three.

plugatlas.com, a map of EV chargers, declares a `Station` Noun:

```bash
curl https://api.sb/plugatlas.com/stations/st_204 -H "Accept: text/markdown"
```

Every API also publishes four machine registers: an OpenAPI document, `llms.txt`, a Markdown copy of every page, and `agents.json`. An Agent reads the contract a developer reads, in the form it parses best. A new Verb appears in all four at its next deploy.

## Same call, different Product
An HTTPS `POST` says little about the Product behind it. The Action it returns, and the Event the [Offer](/business/nouns/offer) earns on, say the rest.

| Call | Product | The Action reports | Earns on |
| --- | --- | --- | --- |
| `Station.lookup` | [Dataset](/business/nouns/dataset) | `done`, with records naming their [Source](/business/nouns/source) and `refreshedAt` | The `done` Action |
| `Swap.approve` | [Software](/business/nouns/software) | `done` with the new state, or `refused` naming the broken rule | The `done` Action |
| `Period.close` by a bookkeeping Agent | Agent | `pending` with reason `running`, then `done` with the closed books | The [Evaluator](/business/nouns/evaluator)'s passing verdict |
| `Order.place` for a vendor review | [Service](/business/nouns/service) | `pending` with reason `awaitingWorker` until the work is delivered | The [Customer](/business/nouns/customer)'s acceptance |

A successful HTTP response never means the Customer got value. Clients read the Action's status, and webhooks deliver its Events.

## Addresses
Every Interface answers on [api.sb](/code/platform/api-sb) under the [Startup](/business/nouns/startup)'s namespace, such as `https://api.sb/plugatlas.com`. Web Interfaces also mount on the Startup's own [Domain](/business/nouns/domain) at a path, such as `plugatlas.com/api`. The SDK and CLI need no path.

A new hostname is another address for the same Product, never another Product. Its Customers, [Tenants](/business/nouns/tenant), and history stay where they are.

## Next
[Composition](/products/composition) covers building one Product from others.