Skip to main content
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. It matches the tree scaffolded by cargo-ai cdk init <dir> --template full (see 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.
connectors/hubspot.ts
import { defineConnector, secret } from "@cargo-ai/cdk";

export const hubspot = defineConnector("hubspot", {
  integration: "hubspot",
  config: { method: "privateApp", accessToken: secret("HUBSPOT_API_KEY") },
});
connectors/openai.ts
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>.
models/contacts.ts
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 you can run on demand. Build the workflow with defineWorkflow, then wrap it in defineTool.
tools/enrich.ts
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.
agents/sdr.ts
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.
plays/onboarding.ts
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

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 for secret() and the CARGO_* variables.

7. Run

Run the tool on a single record, then chat with the agent:
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.

Next steps

Deploying

plan, deploy, prune, refresh, import, and destroy.

State & drift

How state tracks live resources and detects out-of-band changes.