# External data

An agent that can read your CRM is more useful than one that can only discuss it.
External data is the bridge: connect a service once, then let named agents call it
mid-conversation.

Gated on the **module-external-data** entitlement.

## Connect a service

`GET /integrations/providers` lists what can be connected and what each one requires:

```
hubspot · salesforce · mailchimp · constant-contact · webflow
wordpress · canva · alphavantage · …
```

It is a static catalog, and deliberately a narrowed view of one: the credential recipe
internals are not part of the response. Read it rather than hard-coding the list, because
it includes industry-specific providers beyond those above, and providers are added over
time.

```bash
curl -X POST https://api.genuineai.app/api/v1/integrations \
  -H "X-Api-Key: gai_…" -H "X-Tenant-Id: <workspace-id>" \
  -H "Content-Type: application/json" \
  -d '{
    "conn_name": "Marketing HubSpot",
    "conn_provider": "hubspot",
    "fields": { "…": "…" },
    "allow_write": false
  }'
```

`fields` carries whatever that provider's recipe requires. Creating a connection needs
`integration:create`, because connecting a company's CRM is an administrative act rather
than something an integration does for itself.

<Callout type="lock">
**Credentials are passed to the connector service and are not stored by this API**,
and `GET /integrations` never includes keys. There is no endpoint that reads a
credential back. If you lose one, reconnect.
</Callout>

## Read-only by default

`allow_write` is the single most consequential field on a connection:

| `allow_write` | An agent may |
|---|---|
| `false` (default) | Call read operations only. Write attempts are refused at execution. |
| `true` | Call operations that create, update or delete in the connected system. |

Leave it false unless an agent genuinely needs to change the other system. It is enforced
when the tool runs rather than only when it is described to the model, so a model that
attempts a write anyway is still stopped.

`PATCH /integrations/{id}` changes it later, and `DELETE /integrations/{id}` disconnects.

## What a connection can do

```
GET /integrations/{id}/operations?query=contact
```

Searches the operations this connection exposes, which are the actions an agent can be
given through it. `query` is required, because a connected CRM exposes hundreds of
operations and the goal is to find the few that matter rather than enumerate them all.

Call this directly while designing an agent. What you find here is exactly what the agent
will be able to reach, so it answers "can this agent do what I need" before you wire it up
and test by conversation.

## Give an agent access

Two fields on the agent:

```json
{
  "agent_tools": ["external_data"],
  "agent_conns": ["3f2a9c1e-…"]
}
```

`external_data` is the [tool](/agents-and-conversations#tools) itself. `agent_conns`
narrows which connections this agent may use.

<Callout type="caution">
**An empty `agent_conns` means every connection, not none.** The list is an
allow-list only when it has entries. Leave it empty and the agent can reach every
active connection in the workspace. If an agent should only touch the CRM, name the
CRM.
</Callout>

The narrowing happens when the tool is bound, so the agent's tool description mentions
only connections it is allowed to use. It cannot attempt a connection it was not given and
be refused, because it does not know that connection exists.

Read/write status travels into that description too. Each connection is presented to the
model as read-only or read/write, so it plans accordingly rather than proposing writes it
cannot make.

## When there are no connections

If a workspace has none, the tool instructs the agent to say so and to explain that an
administrator must connect a service first. An agent with `external_data` and nothing
connected explains the situation rather than failing or inventing data.

The same applies to failures: if connections cannot be listed, the tool binds with an
empty set rather than ending the conversation.

## Design considerations

The combination worth building toward is this page plus [autonomous
AI](/autonomous-ai): a routine that fires on a schedule, whose copilot agent reads from a
connected system, does the work, and submits it for review. Nobody has to start it or
remember it.

Two limits shape the design. First, there is no webhook *in* from these services: the
platform reads when an agent asks, so the model is pull rather than push, and a scheduled
routine is how you approximate reacting to change. Second, connections are
workspace-level, so an agent acts as the connection rather than as the person talking to
it, and the connected system's audit log shows the integration rather than the end user.

## Next steps

- [Agents and conversations](/agents-and-conversations) holds the tool catalog this belongs to.
- [Autonomous AI](/autonomous-ai) covers scheduled work that acts on other systems.
- [Roles and permissions](/roles-and-permissions) explains the `integration:*` permissions.
