---
name: new-connector
description: Add a new business connector (ERP, CRM, émargement...) — registry entry, capabilities, icon, agent catalogue, propagation to running agents
disable-model-invocation: true
---

# Add a New Connector

Everything is registry-driven. Adding a non-OAuth connector requires **no DB migration** (generic `ConnectorCredential` table, one row per `(orgId, connectorSlug)`, AES-256-GCM-encrypted JSON) and **no UI work** (`ConnectorDialog` renders fields from the registry, `ConnectorIcon` falls back to `<Plug>` if no icon).

## Step 1 — Registry entry

Add a `ConnectorDef` in `packages/shared/src/connectors/registry.ts` (`CONNECTOR_REGISTRY`). Copy the closest existing entry (e.g. `neil:` for an ERP, `hubspot:` for a CRM).

- `category`: `gestion_scolaire | emargement | crm | stockage | productivite`
- `auth.type`: `api_key` (static credential to paste — also used for HTTP Basic, see AIMAIRA), `bearer_token`, `oauth_redirect` (Dropbox pattern), `oauth_app` (Google/Microsoft incremental scope)
- `auth.healthCheck`: **omit** when there is no cheap templatable HTTP probe. `resolveConnectorTemplate` only does `{{field}}` substitution — it cannot base64-encode `user:pass` for `Authorization: Basic` (AIMAIRA) or speak IMAP. Leave a comment explaining why. Moodle-style APIs that return 200 on bad tokens: use `errorBodyKey`.
- `agentConfig.instructions`: the prose the agent receives. Include base URL pattern, auth mechanism, key endpoints, read/write semantics, and any unconfirmed paths flagged as such.
- Field UX: use `prefix`/`suffix` for URL scaffolding, `pattern` + `patternMessage` for validation, `setupGuide` in French.

### Known auth quirks (verify against the provider's real docs, not assumptions)

| Provider         | Quirk                                                                                                                                                     |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Wedof/CertiPlace | `X-API-KEY` header, NOT `Bearer`. One API (`www.wedof.fr/api`) serving two products with separate accounts → two registry entries, disjoint endpoint sets |
| Neil             | `X-Lucius-Api-Key` header; searches are `POST /search`; attendance is `GET /formations/:id/attendances`                                                   |
| AIMAIRA          | HTTP Basic on `{sub}.aimaira.net/GraphV1`, OData v4 (not GraphQL despite the path); PUT resets absent fields                                              |

## Step 2 — Capabilities

Tag `capabilities` with `EvidenceCapability` values. This is the single contract keeping audits connector-agnostic: audits declare what they need, connectors declare what they hold, code intersects. `[]` = not an audit data source (the field is required so the decision can't be skipped).

Only tag what the API actually serves (e.g. gdocs is NOT `document-store`; no survey API = no `satisfaction-survey`).

## Step 3 — Icon

`apps/web/public/icons/connectors/<slug>.svg` (or `.png`), referenced as `icon: "/icons/connectors/<slug>.svg"`. No logo available → ask the user; don't ship a placeholder.

## Step 4 — Agent catalogue

```bash
pnpm gen:agents-vars
```

Regenerates `infra/ansible/group_vars/connector_catalogue.yml` from the registry (loaded by every playbook that renders AGENTS.md). **Commit the .yml.** Never hand-edit the prose catalogue in `AGENTS.md.j2`.

## Step 5 — Tests

Specs inside `packages/shared` are **never executed** (pre-push runs API tests only). Put schema/registry tests in `apps/api`.

## Step 6 — Propagation to running agents

Nothing propagates automatically after an `instructions` change — the sync pipeline only fires on credential upsert/remove in the UI, and the health-sweep heal does not re-push.

1. **UI path (preferred)**: after the Railway deploy of the new registry, re-save the connector credential in the sidebar.
2. **Manual path**: `playbooks/configure-connectors.yml --limit <agent>` with `--extra-vars @payload.json` containing the **full desired state** `{connectors: [...], connector_env_vars: {...}}`. An empty list **removes all connectors**. Build the payload from the server's `/etc/livia/client-vars.yml`, swapping only the targeted `instructions` with the one from the compiled dist (`pnpm --filter @livia/shared build` first).

⚠️ Reconfiguring connectors on a prod agent requires explicit user authorization — it is classified as a production deploy; a prior "deploy the fleet" authorization does not extend to it.

## Verification checklist

- [ ] `pnpm type-check` + `pnpm lint` pass
- [ ] Connector appears in the sidebar ConnectorsTab with correct fields and setup guide
- [ ] `connector_catalogue.yml` diff shows the new entry
- [ ] After propagation: SSH into an agent and grep the connector name in the rendered `AGENTS.md`
