# Integration

URL: /business/nouns/integration · Id: `noun:integration`

> An Integration is an outside Source connected to a System with credentials, at Studio, Startup, or Customer scope.

An Integration is an outside [Source](/business/nouns/source) connected to a [System](/business/nouns/system) with credentials, at [Studio](/business/nouns/studio), [Startup](/business/nouns/startup), or [Customer](/business/nouns/customer) scope.

## Properties
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `scope` | `string` | Yes | One of `studio`, `startup`, or `customer`. |
| `sync` | `string` | No | Which side leads a sync connector: `platform` or `provider`. |
| `mode` | `string` | Yes | How records move: `mirror` writes db.sb out to the outside tool; `import` turns edits there into Changes. db.sb is the record in both. |
| `state` | `string` | Yes | Where the connected account stands; read fresh before every call. |
| `venue` | `string` | No | The id of the ad, social or app-store venue the account is on, when it is on one. |
| `account` | `string` | No | The outside account’s own id, such as an ad account, a workspace or a repository. |
| `grants` | `string[]` | No | The outside permissions the account holder approved: the narrowest set its Verbs need. |
| `connectedBy` | [Party](/business/nouns/party) | No | Who approved the connection; a Human’s own account is used only under that Human’s Mandate. |
| `environment` | `string` | No | A `test` Integration reaches only the upstream’s test account, as a Stripe test account does. |
| `expiresAt` | `datetime` | No | When access lapses unless refreshed. |
| `refreshedAt` | `datetime` | No | When the platform last renewed the outside token. |
| `lastError` | `string` | No | The error slug of the last failed call or refresh, cleared by the next success. |
| `cursor` | `string` | No | Where the last sync run stopped, so the next run reads only what changed. |

<details>
<summary>Inherited from [Source](/business/nouns/source): 2 properties</summary>

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `acquired` | `string` | Yes | One of `owned`, `wrapped`, or `recruited`. |
| `terms` | `text` | No | The terms the Startup holds the right to use and resell it under. |

</details>

## Object
```json
{
  "$id": "https://api.sb/petpantry.shop/integrations/integration_1",
  "$type": "Integration",
  "acquired": "owned",
  "terms": "Resale allowed under the provider’s published API terms; 10,000 calls a month included.",
  "scope": "studio",
  "sync": "Example sync",
  "mode": "mirror",
  "state": "connecting",
  "venue": "Example venue",
  "account": "Example account",
  "grants": [
    "Example grants"
  ],
  "connectedBy": "party_1",
  "environment": "test",
  "expiresAt": "2026-10-22T12:00:00Z",
  "refreshedAt": "2026-10-22T12:00:00Z",
  "lastError": "Example last error",
  "cursor": "Example cursor"
}
```

## Relationships
- <a id="rel-connects"></a>An Integration **connects** one [System](/business/nouns/system): an Integration connects an outside [Source](/business/nouns/source) to one [System](/business/nouns/system), with credentials at [Studio](/business/nouns/studio), [Startup](/business/nouns/startup) or [Customer](/business/nouns/customer) scope. `connects`, inverse [`connectedVia`](/business/nouns/system#rel-connected-via).

## Lifecycle

| State | Meaning | Moves to |
| --- | --- | --- |
| `connecting` (first) | Waiting for the account holder to approve the outside account. | `connected` by [Integration.connect](/business/nouns/integration/connect); `revoked` by [Integration.revoke](/business/nouns/integration/revoke) |
| `connected` | Calls and syncs run with a live token. | `needsReauth` by [Integration.refresh](/business/nouns/integration/refresh); `paused` by [Integration.pause](/business/nouns/integration/pause); `revoked` by [Integration.revoke](/business/nouns/integration/revoke) |
| `needsReauth` | The upstream refused a refresh, so calls wait until the account holder approves again. | `connected` by [Integration.reconnect](/business/nouns/integration/reconnect); `revoked` by [Integration.revoke](/business/nouns/integration/revoke) |
| `paused` | Every call stops, and the token and cursor are kept. | `connected` by [Integration.resume](/business/nouns/integration/resume); `revoked` by [Integration.revoke](/business/nouns/integration/revoke) |
| `revoked` | Its token is revoked with the upstream; every record it brought in stays. | None: it ends here. |

## Verbs
| Verb | Description | Subject | Event | Class |
| --- | --- | --- | --- | --- |
| [Integration.call](/business/nouns/integration/call) | Call sends one request to an upstream through an Integration, with its token filled in server-side, for endpoints no Mapping covers. | [Integration Engineer](/agents/engineering/integration-engineer) | [`Integration.called`](/business/events/Integration.called) | One-way (customer): `send`, `do` |
| [Integration.connect](/business/nouns/integration/connect) | Connect asks an account holder to approve an outside account for one System and records the grants they approve. | [Integration Engineer](/agents/engineering/integration-engineer) | [`Integration.connected`](/business/events/Integration.connected) | Two-way: `try`, `send`, `do` |
| [Integration.pause](/business/nouns/integration/pause) | Pause stops every call through an Integration at once and keeps its token and cursor. | [Worker](/business/nouns/worker) | [`Integration.paused`](/business/events/Integration.paused) | Two-way: `try`, `send`, `do` |
| [Integration.reconnect](/business/nouns/integration/reconnect) | Reconnect re-approves an Integration in place, keeping its id and cursor, so nothing re-imports. | [Worker](/business/nouns/worker) | [`Integration.reconnected`](/business/events/Integration.reconnected) | Two-way: `try`, `send`, `do` |
| [Integration.refresh](/business/nouns/integration/refresh) | Refresh renews an Integration’s outside token before it expires; a refused refresh moves it to `needsReauth`. | [Integration Engineer](/agents/engineering/integration-engineer) | [`Integration.refreshed`](/business/events/Integration.refreshed) | Two-way: `try`, `send`, `do` |
| [Integration.resume](/business/nouns/integration/resume) | Resume restarts a paused Integration from its cursor and releases the Actions that waited on it. | [Worker](/business/nouns/worker) | [`Integration.resumed`](/business/events/Integration.resumed) | Two-way: `try`, `send`, `do` |
| [Integration.revoke](/business/nouns/integration/revoke) | Revoke ends an Integration at once: its token is revoked with the upstream, and every record it brought in stays. | [Security Engineer](/agents/engineering/security-engineer) | [`Integration.revoked`](/business/events/Integration.revoked) | None. |
| [Integration.sync](/business/nouns/integration/sync) | Sync exchanges records between a System and its connector, and the declared leader wins every conflict. | [Reliability Engineer](/agents/engineering/reliability-engineer) | [`Integration.synced`](/business/events/Integration.synced) | None. |
| <a id="create"></a>[Integration.create](/business/nouns/integration/create) | Creates an Integration. Inherited from [Noun](/business/nouns/noun). | None. | `Integration.created` | Two-way: `try`, `send`, `do` |
| <a id="get"></a>[Integration.get](/business/nouns/integration/get) | Returns one Integration by id. Inherited from [Noun](/business/nouns/noun). | None. | None. | Read: `try`, `do` |
| <a id="list"></a>[Integration.list](/business/nouns/integration/list) | Returns Integrations, newest first, one page at a time. Inherited from [Noun](/business/nouns/noun). | None. | None. | Read: `try`, `do` |
| <a id="update"></a>[Integration.update](/business/nouns/integration/update) | Changes the given properties of an Integration. Inherited from [Noun](/business/nouns/noun). | None. | `Integration.updated` | Two-way: `try`, `send`, `do` |
| <a id="delete"></a>[Integration.delete](/business/nouns/integration/delete) | Deletes an Integration; its history is kept. Inherited from [Noun](/business/nouns/noun). | None. | `Integration.deleted` | One-way: `send`, `do` |

## Permissions
Which Access level may call each Verb. One-way Verbs wait their delay window.

| Operation | `owner` | `admin` | `member` | `viewer` |
| --- | --- | --- | --- | --- |
| `Integration.create` | Yes | Yes | Yes | No |
| `Integration.get` | Yes | Yes | Yes | Yes |
| `Integration.list` | Yes | Yes | Yes | Yes |
| `Integration.update` | Yes | Yes | Yes | No |
| `Integration.delete` | Yes | Yes | Yes | No |
| `Integration.call` | Yes | No | No | No |
| `Integration.connect` | Yes | Yes | Yes | No |
| `Integration.pause` | Yes | Yes | Yes | No |
| `Integration.reconnect` | Yes | Yes | Yes | No |
| `Integration.refresh` | Yes | Yes | Yes | No |
| `Integration.resume` | Yes | Yes | Yes | No |
| `Integration.revoke` | Yes | Yes | Yes | No |
| `Integration.sync` | Yes | Yes | Yes | No |

## Call it

**Ask**

```text
Sync the latest integration.
```

**API**

```bash
curl -X POST https://api.sb/shiftnote.app/integrations/integration_1/sync \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
```

```json
{
  "api": "https://api.sb/shiftnote.app",
  "integration": {
    "$id": "https://api.sb/shiftnote.app/integrations/integration_1",
    "$type": "Integration",
    "acquired": "owned",
    "scope": "studio",
    "mode": "mirror",
    "state": "connecting"
  },
  "action": {
    "$id": "https://api.sb/shiftnote.app/actions/action_1",
    "operation": "Integration.sync",
    "status": "done"
  },
  "links": {
    "self": "https://api.sb/shiftnote.app/actions/action_1"
  }
}
```

**SDK**

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

const integration = await $.Integration.sync({ integration: 'integration_1' })
```

**CLI**

```bash
npx startup-builder do Integration.sync --json '{"integration":"integration_1"}'
```

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "code": "return await $.Integration.sync({ integration: 'integration_1' })"
  }
}
```

## Example
A bookkeeping service connects each client's bank feed as an Integration at [Customer](/business/nouns/customer) scope. The feed syncs nightly into the client's own [Tenant](/business/nouns/tenant), and [db.sb](/code/platform/db-sb) stays the record.