> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getcargo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# End-to-end recipe

> Wire a connector, model, tool, agent, and play together in one workspace, deploy it with cargo-ai cdk, and run it — the full loop end to end.

This recipe builds a small but complete workspace: a **connector** feeds a
**model**, a **tool** enriches a record, an **agent** reasons over the data, and
a **play** runs a workflow as rows change. Then you deploy it all with one
command and run it. Every snippet uses the same `define*` builders documented in
[Resources](/connectors/overview).

It matches the tree scaffolded by `cargo-ai cdk init <dir> --template full` (see
[Project layout](/get-started/project-layout)).

## 1. Connect

Define the source system and the LLM provider. Use `secret()` for API keys, or
`adopt: true` for a connector authorized via OAuth in the app.

```ts connectors/hubspot.ts theme={null}
import { defineConnector, secret } from "@cargo-ai/cdk";

export const hubspot = defineConnector("hubspot", {
  integration: "hubspot",
  config: { method: "privateApp", accessToken: secret("HUBSPOT_API_KEY") },
});
```

```ts connectors/openai.ts theme={null}
import { defineConnector } from "@cargo-ai/cdk";

export const openai = defineConnector("open_ai", {
  integration: "openAi",
  adopt: true,
});
```

## 2. Model

Source a model from the connector's dataset with an `extractSlug`. Discover the
valid slugs and config with `cargo-ai cdk types` or
`cargo-ai connection integration get <slug>`.

```ts models/contacts.ts theme={null}
import { defineModel } from "@cargo-ai/cdk";
import { hubspot } from "../connectors/hubspot";

export const contacts = defineModel("contacts", {
  dataset: hubspot,
  extractSlug: "fetchRecords",
  config: { objectType: "contacts", columnSelectionMode: "all" },
  schedule: { type: "cron", cron: "0 * * * *" },
});
```

## 3. Tool

A tool is a [workflow](/workflows/overview) you can run on demand. Build the
workflow with `defineWorkflow`, then wrap it in `defineTool`.

```ts tools/enrich.ts theme={null}
import { defineTool, defineWorkflow } from "@cargo-ai/cdk";
import { z } from "zod";

const enrichFlow = defineWorkflow(
  "enrich-contact",
  {
    input: z.object({ email: z.string() }),
    output: z.object({ company: z.string() }),
  },
  ({ input, ai }) => ({
    company: ai(`Which company owns the domain of ${input.email}?`),
  }),
);

export const enrich = defineTool("enrich", { workflow: enrichFlow });
```

## 4. Agent

An agent reasons over the models, tools, and connector actions you hand it. Pass
handles — Cargo deploys dependencies first and injects their uuids.

```ts agents/sdr.ts theme={null}
import { defineAgent } from "@cargo-ai/cdk";
import { openai } from "../connectors/openai";
import { contacts } from "../models/contacts";
import { enrich } from "../tools/enrich";

export const sdr = defineAgent("sdr", {
  connector: openai,
  languageModel: "gpt-4o",
  systemPrompt: "Qualify inbound contacts and enrich missing company info.",
  capabilities: ["webSearch", "memory"],
  models: [{ ref: contacts, readOnly: true }],
  tools: [enrich],
});
```

## 5. Play

A play runs a per-row workflow as the model's rows change.

```ts plays/onboarding.ts theme={null}
import { definePlay, defineWorkflow } from "@cargo-ai/cdk";
import { z } from "zod";
import { contacts } from "../models/contacts";
import { enrich } from "../tools/enrich";

const onboardRow = defineWorkflow(
  "onboard-contact",
  { input: z.object({ email: z.string() }), output: z.object({ done: z.boolean() }), uses: { enrich } },
  ({ input, uses }) => {
    uses.enrich({ email: input.email });
    return { done: true };
  },
);

export const onboarding = definePlay("onboarding", {
  model: contacts,
  workflow: onboardRow,
  changeKinds: ["added"],
  schedule: { type: "realtime" },
});
```

## 6. Deploy

```bash theme={null}
export HUBSPOT_API_KEY=...   # secret() reads this at deploy time
cargo-ai cdk plan            # offline diff — no API calls
cargo-ai cdk deploy          # create everything; writes cargo.state.json
```

`deploy` applies in dependency order (connector → model → tool → agent → play)
and records each resource's uuid in `cargo.state.json`. **Commit that file.** See
[Secrets & environments](/reference/secrets-and-environments) for `secret()` and
the `CARGO_*` variables.

## 7. Run

Run the tool on a single record, then chat with the agent:

```bash theme={null}
cargo-ai orchestration tool list                         # → workflowUuid
cargo-ai orchestration run create \
  --workflow-uuid <uuid> \
  --data '{"email":"ada@acme.com"}' \
  --wait-until-finished

cargo-ai ai agent list                                   # → agentUuid
cargo-ai ai chat create --agent-uuid <uuid> --trigger '{"type":"draft"}' --name "Triage"
```

Plays enrol through `batch create`, never `run create` — see [Plays](/plays/overview).

## Next steps

<CardGroup cols={2}>
  <Card title="Deploying" icon="rocket" href="/deploy/deploying">
    plan, deploy, prune, refresh, import, and destroy.
  </Card>

  <Card title="State & drift" icon="database" href="/deploy/state-and-drift">
    How state tracks live resources and detects out-of-band changes.
  </Card>
</CardGroup>
