Source: https://docs.cumuluslabs.io/sdk-bindings
Content version: f70c9aac

# Bindings and integrations

Bindings connect an agent to your environment. The graph refers to a name such as `customer_api`; development can map it to your test backend, while production maps it to the production backend. Your graph and tool contracts stay the same.

An integration selects how that agent uses phone numbers, initial context and lifecycle notifications. Publishing includes its context and notification configuration. Assigning numbers is a separate step so a graph change does not silently change live routing.

## Bind an endpoint, a number and events

This example uses the `agent` from the [tutorial project](/docs/sdk-tutorial). The workspace already contains the imported number and tool/event secrets. Obtain their IDs and current versions from that workspace:

```typescript
import { defineEnvironment, defineIntegration } from '@cumulus/sdk/agent';
import { agent } from './project.js';

function required(name: string): string {
  const value = process.env[name];
  if (!value) throw new Error(`Set ${name}`);
  return value;
}

export const bindings = defineEnvironment({
  endpoints: {
    customer_api: {
      baseUrl: required('CUSTOMER_API_URL'),
      auth: {
        scheme: 'bearer',
        secret_ref: {
          id: required('TOOL_SECRET_ID'),
          version: required('TOOL_SECRET_VERSION'),
        },
      },
    },
  },
  numbers: { test_number: required('TEST_NUMBER_ID') },
  notifications: {
    customer_events: {
      url: required('CUSTOMER_EVENTS_URL'),
      secretRef: {
        id: required('EVENT_SECRET_ID'),
        version: required('EVENT_SECRET_VERSION'),
      },
    },
  },
});

export const integration = defineIntegration({
  key: 'service_guide',
  agent,
  inbound: { numberBinding: 'test_number' },
  outbound: { callerIdBinding: 'test_number' },
  events: {
    destinationBinding: 'customer_events',
    subscriptions: ['run.ended', 'recording.updated', 'analysis.updated'],
  },
});
```

The tool's `execution.binding: 'customer_api'` now resolves to your endpoint and authentication reference. `test_number` resolves to an imported number resource ID. A call's `to` field is different: it holds the recipient's E.164 phone number.

Secret references contain an opaque ID and version, never secret bytes. Your server needs the matching bytes from your secret manager. When you have a `SecretMetadata` response, `secretReference(metadata)` builds the reference from its `id` and `current_version`.

## Compile, publish and assign routes

Pass the integration when compiling or previewing so its configuration is included:

```typescript
import { compileAgent, authoring } from '@cumulus/sdk/agent';

const definition = compileAgent(agent, bindings, integration); // local validation
const plan = await authoring.preview(client, agent, bindings, { integration });
```

Here `client` is your authenticated Cumulus client. [The release workflow](/docs/sdk-lifecycle) covers reviewing the plan, applying it and saving the publication receipt. Once that publication is the selected head, assign the integration's numbers:

```typescript
import { assignIntegration } from '@cumulus/sdk/runtime';

const assigned = await assignIntegration(client, integration, bindings, receipt);
```

The CLI exposes these stages as `preview`, `apply`, `publish` and the explicit `route` command. Before routing, send an authenticated request to the deployed tool endpoint and check its result. Save the previous number assignments.

Assignment checks that the current head matches the receipt publication, then updates imported numbers. It does not purchase a number or place a call. Multiple updates are separate operations: inspect every returned assignment and avoid changing the head concurrently.

## Add inbound context

If the agent includes a context resolver action, extend `inbound` with its action key and declared output names:

```typescript
import { initialContext } from './tools.js';

const inbound = {
  numberBinding: 'test_number',
  contextResolver: initialContext.key,
  contextOutputs: ['demo_channel'],
};
```

Pass this `inbound` value to `defineIntegration`. The tutorial exports `initialContext` from `src/tools.ts`; the action must exist in the agent definition and its server must return those string variables. See [the context adapter](/docs/sdk-server#receive-initial-context-and-lifecycle-events). A phone lookup can select a candidate account; use your business verification flow before exposing or changing account data.

## Binding reference

| Binding | Value |
| --- | --- |
| `endpoints[alias]` | HTTPS `baseUrl`, native `auth` with a pinned secret reference, optional noncredential headers. |
| `numbers[alias]` | An already imported number resource ID. |
| `notifications[alias]` | Public HTTPS callback `url` and pinned `secretRef`. |
| `edits` | Additional typed agent configuration checked before publication. |

| Integration property | Purpose |
| --- | --- |
| `inbound.numberBinding` | Number alias for incoming calls. |
| `inbound.contextResolver`, `contextOutputs` | Initial-context action and allowed variables. |
| `outbound.callerIdBinding` | Number alias for outbound assignment. |
| `events.destinationBinding`, `subscriptions` | Event callback and selected lifecycle events. |

Tool endpoints use HTTPS on port 443. Tool paths are absolute: `/cumulus/tools/guide` resolves from the origin even when `baseUrl` contains a pathname. Include the full public prefix in the path, without a query, fragment or origin change. Use native authentication references instead of literal credential headers.

Configure the underlying models, secrets and carrier resources in the selected workspace. Where required, use a native telephony edit for trunk and outbound `from` settings; `callerIdBinding` alone is not that configuration. The CLI selects one integration per agent; use the library when you need to choose among integrations explicitly.
