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

# Build an agent

> Grow a read-only prototype into a buying loop that survives retries, restarts and human approval.

This guide assumes you finished [Connect in five minutes](/v3/quickstart) and have a buyer account.
It moves from reading sellers to launching a campaign, and shows where your code must take
responsibility for durability.

## 1. Start with seller discovery

Discovery is read-only and safe to run as often as you like.

```json search theme={null}
{ "kind": "seller", "limit": 25 }
```

Each result carries the seller's channels, markets, whether your account already buys from it, and
whether it needs anything before it can transact (for example an account mapping). An empty list is a
valid answer.

To change which sellers receive your briefs, call `save_connection` with one intent per call:

<CodeGroup>
  ```json Always include a seller theme={null}
  { "target": { "kind": "seller", "id": 7 }, "selection": "ALWAYS_INCLUDE" }
  ```

  ```json Enable a seller for one advertiser theme={null}
  {
    "target": { "kind": "seller", "id": 7 },
    "advertiserActivation": { "advertiserId": 12, "decision": "ENABLED" }
  }
  ```
</CodeGroup>

## 2. Give the loop a durable owner

A model's memory is not a database. Before your agent writes anything, decide where these live:

* the person's request and any approvals they gave;
* the active `customerId` and advertiser id;
* every Semicola id you create (`cmp_…`, `exe_…`, `sfp1:…`, `mb_…`);
* the `idempotencyKey` for each write you attempted, and whether it finished.

When the process restarts, read those ids back and ask Semicola for the current state with `get`
before deciding what to do next.

## 3. Add buying steps

<Steps>
  <Step title="Advertiser">
    `save_advertiser` with a name, brand domain and primary currency. If a fact is missing you get
    `needs_input`; ask the person, do not guess. Use `sandbox: true` while you build.
  </Step>

  <Step title="Draft campaign">
    `save_campaign` with the brief, flight, budget and an `idempotencyKey`. Save the returned
    `campaignId` and `revision`.
  </Step>

  <Step title="Request proposals">
    `request_proposals` with `campaignId`, `expectedCampaignRevision` and a new `idempotencyKey`. It
    returns at once with an execution id and a status of `running`.
  </Step>

  <Step title="Collect results">
    Poll with `get` or call `request_proposals` again with the same key and a `resultCursor` to page
    through what has arrived. Each seller reports `quoted`, `products` or `failed`.
  </Step>

  <Step title="Refine">
    `refine_proposal` with the `proposalId` and plain instructions, optionally `keepProductIds`,
    `dropProductIds` or a `budgetDelta`. The seller may decline parts of the request.
  </Step>

  <Step title="Stage">
    `save_media_buy` with `fromProposalId` for each proposal the person picked. Nothing is booked.
  </Step>

  <Step title="Launch, with the person">
    `save_campaign` with `desiredPhase: "active"` returns `pending_confirmation`. Show the summary;
    only after the person approves, call again with `confirmLaunch: true`. See the [Campaign object
    guide](/buy/campaign#launch).
  </Step>
</Steps>

### Resuming a proposal request

Only one execution runs per buyer at a time. If your process dies mid-request, do not start a new
one: call `request_proposals` again with the **same** `idempotencyKey` and you get the existing
execution back, including everything that has arrived since.

### Recovering from interrupted writes

If a write times out or the connection drops, the write may still have happened. Read the object
first. If it is unchanged, retry with the same `idempotencyKey`. If it changed, compare it with what
you intended, then decide. After a `REVISION_CONFLICT`, re-read and re-decide rather than retrying
with a bumped number.

## 4. Test the boundaries

* Run the whole loop against a **sandbox** advertiser: sellers answer, buys are placed, delivery is
  simulated, and no money moves.
* Test the empty cases: no sellers, a seller that fails, an execution where every seller passes.
* Test that your agent stops at `pending_confirmation` and `needs_input` instead of pushing through.
* Test resuming after a restart at every step above.

<CardGroup cols={2}>
  <Card title="Errors" icon="triangle-exclamation" href="/v3/errors">
    Codes, partial results and safe retries.
  </Card>

  <Card title="v3 Tool Catalog" icon="toolbox" href="/v3/tool-catalog">
    Every input field for the tools used here.
  </Card>
</CardGroup>
