> Documentation index: https://docs.beetl.io/llms.txt

# Building with a coding agent

> The workflow for writing an integration with a coding agent.

Source: https://docs.beetl.io/connect/building-with-an-agent/
Last updated: 2026-10-03

Coding agents such as Claude Code, Cursor or Codex can write most of a Connect package. The SDK
CLI runs on your machine, so an agent can build, run and check its own work before you look at
it.

## Give the agent the right context

An agent writes a better integration when it reads the real contract instead of guessing from
memory.

### The SDK

`@beetlio/connect` is open source under the Apache-2.0 license, so the agent can read its full
source and type definitions in the [GitHub repository](https://github.com/beetlio/connect) and
the [user guide](https://beetlio.github.io/connect/). Once the SDK is installed, the type
definitions are also in `node_modules/@beetlio/connect/dist/`. For how Beetl runs a package, add
[Authoring a sync](https://docs.beetl.io/connect/authoring-a-source),
[Materialization](https://docs.beetl.io/connect/write-modes), [Authentication](https://docs.beetl.io/connect/oauth-and-rest) and
[The sandbox](https://docs.beetl.io/connect/sandbox).

### The target API's documentation

Give the agent the provider's official reference for authentication, pagination, rate limits,
filtering by last change, and how deleted records are reported. An agent left to infer an API
from examples tends to miss the edge cases.

### An example package

The SDK repository has runnable [examples](https://github.com/beetlio/connect/tree/main/examples):
a basic one with connection
verification, an all-features one with OAuth, pagination and checkpoints, and a Wikidata one
against a public API.

## Plan before code

Ask the agent for a short written plan first and review it. A useful plan states:

* what the integration syncs and what it leaves out
* how authentication works, which permissions it needs, and how credentials are renewed
* for each sync: the source endpoint, a stable record ID to use as the primary key, full or
  incremental reads, what goes into the checkpoint, and how deletions are handled
* the pagination style and rate limits, with links to the docs that support each choice

The plan is cheap to correct. Sync keys, record schemas and primary keys are not: they become part
of the package's [contract](https://docs.beetl.io/connect/schema-change-contract) once it is installed.

## A loop that works

    ### Scaffold [#scaffold]

    Have the agent create `package.json` with `"type": "module"` and a `files` allowlist, install the
    SDK with `npm install --save-dev @beetlio/connect`, and create `integration.ts`. The
    [quickstart](https://beetlio.github.io/connect/#getting-started) is a good template.

    ### Implement authentication and one sync [#implement-authentication-and-one-sync]

    Use the SDK's `auth` helpers in `connection.auth` and add a `verify` function that makes one small
    authenticated request. Then write one representative sync and leave the others until it works.

    ### Run it locally [#run-it-locally]

    `configure` asks for credentials and needs an interactive terminal, so run it yourself:

    ```sh
    npx beetl-connect configure . orders
    ```

    The agent can then run the sync on its own and read the output:

    ```sh
    npx beetl-connect sync . orders --output orders.ndjson
    ```

    For syncs that take no credentials, the agent can pass settings with `--inputs` and configure
    without prompts. Both commands exit with a non-zero status on failure.

    ### Check the records [#check-the-records]

    The SDK validates every record against the sync's schema, so a passing run proves the shape. Ask
    the agent to check what validation does not cover: the record count matches what the API reports,
    primary keys are unique across the whole output, and fields that should be filled are not empty.
    Compare a few records with the provider's own UI yourself. For a Merge sync, the output file holds
    batches of records and deletions rather than one record per line.

    ### Add incremental checkpoints [#add-incremental-checkpoints]

    Declare a checkpoint and save the position after each batch. Local runs store checkpoints under
    `.beetl/state/`, so running the same sync twice shows whether the second run reads only what
    changed. Delete the state file, or pass `--state` with a new path, to start from scratch.

    ### Add the remaining syncs, then pack [#add-the-remaining-syncs-then-pack]

    Repeat the loop for each sync. Finish with `npx beetl-connect pack . --output acme.tgz` and read
    the printed file list. See [Pack and upload](https://docs.beetl.io/connect/distribution).

## A starting prompt

Adapt this to your agent and API:

```text
Build a Beetl Connect integration for the Acme Orders API using @beetlio/connect.

Read first:
- The SDK user guide: https://beetlio.github.io/connect/
- The SDK source and examples: https://github.com/beetlio/connect
- The Acme API reference: <link>

Write a plan before any code: syncs, primary keys, auth, pagination,
incremental strategy, deletions, rate limits, with doc links. Wait for my review.

Then implement auth with verify() and the "orders" sync only. I will run
`npx beetl-connect configure . orders` myself. You run
`npx beetl-connect sync . orders --output orders.ndjson` and report the record
count, duplicate primary keys and empty required fields.

Never print, log or commit credentials. Only read from the API.
```

## Keep credentials away from the agent

Credentials saved by `configure` live in your operating system's user configuration folder, not in
the project. They are readable by your user account but not encrypted, so an agent with full disk
access could read them. Use a test account with the narrowest permissions the API allows, and keep
any local secrets files out of version control. `pack` refuses `.env*` files and the `.beetl`
folder in any case.

## Review before you upload

Read the code yourself before a TenantAdmin uploads it. Check that:

* credentials are declared in `connection.auth`, never as ordinary inputs, custom secret fields
  are marked with `secret()`, and `verify` fails on bad access
* pagination reaches the last page, and API errors fail the run instead of returning an empty
  result
* every primary key is stable, unique, required and never null
* each batch carries the checkpoint to resume after it, and a repeated run does not duplicate
  records
* the integration only reads from the provider
* nothing logs tokens, request headers or full responses, and `ctx.log` is used for messages
* the `files` allowlist ships only what the package needs

Run the whole loop again after any change to the code or its dependencies. A result from an
earlier version says nothing about the new package.
