<!-- https://leantools.ai/doc/connectors · HTML twin of this page; the whole documentation: https://leantools.ai/llms-full.txt -->
# Connectors and approvals

Agents

LeanConnect connectors let agents act inside the services your team already uses, with credentials the workspace stores and the model never sees. One completed action is one `call` at the connector's rate; refused, failed and timed-out actions cost nothing.

The [connector directory](https://leantools.ai/connectors) lists every connector by category with its actions; `GET /api/v1/connectors` answers the ones switched on for your workspace.

## Endpoints

 (GET)/api/v1/connectorsenabled connectors, their actions, credential types and your price per call

 (GET)/api/v1/connectors/credential-typescredential types with their properties (secrets flagged)

 (GET)/api/v1/connectors/credentialsyour stored credentials (never the secrets)

 (POST)/api/v1/connectors/credentials`{ name, credentialTypeKey, data }` → 201 credential

 (DELETE)/api/v1/connectors/credentials/{id}`{ ok, id }`

 (GET)/api/v1/connectors/{key}actions with `inputSchema` and `outputHint`, credential requirement

 (POST)/api/v1/connectors/{key}/actions/{action}`{ credentialId | credential, input }` → `{ output, usage }`

 (GET)/api/v1/connectors/approvals/{id}status of a held write action, and its output once approved

## List and describe

`GET /api/v1/connectors` answers `[{ key, name, description, category, version, credentialTypes[], actions[{ key, name, description, isReadOnly, isDestructive }], pricing { perCall } }]`. `GET /api/v1/connectors/{key}` adds each action's `inputSchema` (JSON Schema of the `input` object) and `outputHint`, plus `credential { required, types[] }`. Actions the workspace disabled in the console are not listed.

## Credentials

Credentials belong to the workspace. Link them in the console (Tools & Connectors → connector → Credentials) or through the API:

```
curl -s -X POST https://YOUR-HOST/api/v1/connectors/credentials \
  -H "Authorization: Bearer lt-..." -H "Content-Type: application/json" \
  -d '{ "name": "Twilio prod", "credentialTypeKey": "Twilio", "data": { "AccountSid": "AC...", "AuthToken": "..." } }'
```

201

```
{ "id": "7c1f0a...", "name": "Twilio prod", "credentialTypeKey": "Twilio", "status": "Active", "lastError": null, "createdTime": "2026-09-27T09:00:00+00:00", "lastUsedTime": null }
```

`data` holds the properties listed by `GET /api/v1/connectors/credential-types` (`properties[{ name, type, required, isSecret, options }]`); secrets are encrypted at rest and never returned. Another workspace's id is `404 credential_not_found`. Types that need a browser consent flow (OAuth) are linked from the console. At most `LeanTools.Connectors.MaxCredentialsPerAccount` (50) per workspace. A credential the upstream rejects is marked *Invalid* and the console shows a "needs attention" badge.

## Run an action

```
curl -s -X POST https://YOUR-HOST/api/v1/connectors/Twilio/actions/sms.send \
  -H "Authorization: Bearer lt-..." -H "Content-Type: application/json" \
  -d '{ "credentialId": "7c1f0a...", "input": { "from": "+15550001111", "to": "+60123456789", "body": "Hello" } }'
```

200

```
{ "connector": "Twilio", "action": "sms.send", "output": { "sid": "SM...", "status": "queued" },
  "usage": { "credits": 1, "balance": 1868.15, "durationMs": 412, "upstreamProvider": "Twilio" } }
```

Pass `credentialId` (a stored credential), `credential: { credentialTypeKey, data }` (used once, never persisted), or nothing to use the connector's default credential set in the console. `input` is validated against the action's `inputSchema` before anything runs (`400 invalid_input` / `unknown_field`, `413 payload_too_large` over `LeanTools.Connectors.MaxInputBytes`). Over MCP the same action is `connector.twilio.sms.send` with `arguments: { input, credentialId? }`.

## Guardrails

Each connector has a configure drawer in the console with four guardrails, enforced on every call whether it arrives over REST or MCP:

| Guardrail | Effect | Refusal |
| --- | --- | --- |
| Functions | Each action can be exposed or hidden from agents. Hidden actions vanish from the catalogue. | `403 action_disabled` |
| Allowed keys | Restrict the connector to specific API keys (all keys by default). | `403 key_not_allowed` |
| Rate limit and daily cap | Calls per minute and calls per UTC day across the workspace. | `429 rate_limited`, `429 daily_cap_reached` |
| Require approval for writes | Actions that are not read-only are parked until a member with the *approve calls* capability decides. | `202 approval_pending` |

## Approvals: human in the loop

With *require approval for writes* on, a write action does not run. The call answers `202` with an approval id and expiry; the request appears in the console's Activity → Approvals queue with its arguments, where an Owner or Admin approves (the action then runs with the workspace credential, and the credits are charged) or denies it. Pending requests expire after `LeanTools.Tenant.ApprovalExpiryMinutes` (30 by default).

202 on the call

```
{ "isSuccess": false, "errorCode": "approval_pending",
  "errorMessage": "The action 'update_deal' of connector 'hubspot' is held for human approval (request a1b2c3…). Poll GET /api/v1/connectors/approvals/a1b2c3… for the decision and the output; the request expires at 2026-09-28T10:30:00.0000000+00:00.",
  "details": { "approvalId": "a1b2c3…", "expiresUtc": "2026-09-28T10:30:00+00:00", "pollUrl": "/api/v1/connectors/approvals/a1b2c3…" } }
```

Poll the decision with the same key (a free `management` call):

GET /api/v1/connectors/approvals/{id}

```
{ "id": "a1b2c3…", "connector": "hubspot", "action": "update_deal", "status": "executed",
  "createdUtc": "2026-09-28T10:00:00+00:00", "expiresUtc": "2026-09-28T10:30:00+00:00", "decidedUtc": "2026-09-28T10:04:12+00:00",
  "output": { "id": "deal_123", "stage": "closedwon" }, "usage": { "credits": 1 } }
```

`status` is `pending`, `executing` (claimed by an approver, the call is running), `executed` (approved and run; `output` and `usage` are filled), `denied`, `expired` or `failed` (approved but the call could not run: the upstream rejected it, or its key was revoked in the meantime, `key_revoked`; an approved call is bounded to 10 minutes, and a claim that never finished because the node died mid-call is failed with `execution_interrupted` after 15 minutes, which polling a due request settles; an `executed` request whose charge could not be recorded at once carries `error { code: "billing_deferred" }`, the call ran and the charge is recorded later); denied and failed requests carry an `error { code, message }`. Over MCP the held call is an `isError: true` result with the same fields, and the agent can call the connector again after the decision or wait for it, as the harness prefers. Approvals also carry the note the reviewer wrote, so the audit trail is complete.

## Bring your own server

The *Generic MCP* connector type registers any remote MCP server (Streamable HTTP) as a connector: provide its URL and, as the credential, the header it expects. Its tools become `connector.<key>.<tool>` actions, metered per completed call, listed with the annotations the server declares, and subject to the same guardrails and approvals as built-in connectors. Stdio transports are refused (`403 transport_not_allowed`).

## Status codes

| Status | Code | Meaning |
| --- | --- | --- |
| 202 | `approval_pending` | Held for a human decision; poll the approval |
| 400 | `invalid_request`, `invalid_input`, `unknown_field`, `credential_required`, `invalid_credential`, `oauth_not_supported`, `expressions_not_supported` | Body shape, input validation, credential missing / wrong type / disabled |
| 401 | `authentication_error`, `key_expired` | Missing, unknown or expired bearer |
| 402 | `insufficient_credits`, `key_cap_reached`, `tool_budget_reached` | No credits, or a cap reached |
| 403 | `service_not_allowed`, `tool_disabled`, `connector_disabled`, `connector_paused`, `action_disabled`, `key_not_allowed`, `transport_not_allowed` | Key scope, a tool switched off, a connector switched off by the host (`connector_disabled`) or paused by the workspace (`connector_paused`), a hidden action, a key the connector does not allow |
| 404 | `connector_not_found`, `action_not_found`, `credential_not_found`, `credential_type_not_found`, `approval_not_found` |  |
| 413 | `payload_too_large` | `input` over the host limit |
| 422 | `connector_error` | The upstream rejected the request (0 credits) |
| 429 | `rate_limited`, `daily_cap_reached` | Workspace guardrails |
| 502 / 504 | `connector_upstream_error`, `connector_timeout` | The upstream failed or did not answer within `LeanTools.Connectors.CallTimeoutSeconds` (0 credits) |
