npx skills add ...
npx skills add celigo/ai --skill configuring-connections
Configure Celigo connections and iClients -- credential and configuration objects that authenticate to external systems. Use when creating or editing connections, choosing auth methods, setting up OAuth, managing iClients (shared credential stores), or troubleshooting connectivity.
npx skills add celigo/ai --skill configuring-connections
A connection is a credential and configuration object that lets Celigo communicate with an external system. Every export and import references a connection via _connectionId. Connections must be created before the resources that use them.
Concerns when configuring a connection:
concurrencyLevel). Can be shared across connections via _borrowConcurrencyFromConnectionIdoffline status)Used across flows, APIs, and tools.
| Target System | type | Schema | Notes |
|---|---|---|---|
| REST/GraphQL API (with connector) | http | http.yml | formType: "assistant", set _httpConnectorId |
| REST/GraphQL API (manual) | http | http.yml | Three form types: assistant, http, graph_ql |
| NetSuite ERP | netsuite | netsuite.yml | Use token-auto for new connections |
| Salesforce CRM | salesforce | salesforce.yml | Use packagedOAuth: true for new connections |
| SQL Server, MySQL, Postgres, Oracle | rdbms | rdbms.yml | |
| Snowflake, BigQuery, Redshift | rdbms | rdbms.yml | Check sub-type in schema |
| Active Directory, Databricks, DB2 | jdbc | jdbc.yml | |
| MongoDB/Atlas | mongodb | mongodb.yml | |
| DynamoDB | dynamodb | dynamodb.yml | |
| FTP/SFTP/FTPS server | ftp | ftp.yml | Optional PGP encryption |
| Amazon S3 | s3 | s3.yml | |
| Local filesystem | filesystem | filesystem.yml | Requires agent via _agentId |
| AS2 EDI partner | as2 | as2.yml | |
| Celigo VAN (EDI hub) | van | van.yml | |
| AI tool server (MCP) | mcp | mcp.yml | |
| Stack-deployed connector | wrapper | wrapper.yml | |
| Legacy REST (do not use) | rest | rest.yml | Use http instead |
| Target system | Use type | Auth method | Read schema |
|---|---|---|---|
| Any REST/GraphQL API with a Celigo connector | http | Connector-defined (usually OAuth2 or token) | http.yml |
| Any REST/GraphQL API without a connector | http | Token, basic, OAuth2, custom headers | http.yml |
| NetSuite ERP | netsuite | token-auto (Celigo-managed TBA) | netsuite.yml |
| Salesforce CRM | salesforce | packagedOAuth: true (Celigo OAuth) | salesforce.yml |
| SQL databases (Postgres, MySQL, SQL Server, Oracle) | rdbms | Username/password + host/port | rdbms.yml |
| Snowflake / BigQuery / Redshift | rdbms | Key-pair or username/password | rdbms.yml |
| MongoDB / Atlas | mongodb | Connection string or host/credentials | mongodb.yml |
| FTP / SFTP / FTPS | ftp | Username/password or SSH key | ftp.yml |
| Amazon S3 | s3 | IAM access key or role ARN | s3.yml |
| MCP server | mcp | Varies (OAuth2 or token) | mcp.yml |
Which of the two HTTP rows applies is determined by search, not preference: check for a pre-built connector first (celigo http-connectors list) and use the manual row only when no connector exists or it doesn't fit -- see Check for a pre-built connector and global iClient.
Every connection needs at minimum: name, type, and the type-specific config block.
| Type | Required fields |
|---|---|
http (connector) | name, type: "http", http._httpConnectorId, http._httpConnectorVersionId, connector-specific auth fields |
http (manual) | name, type: "http", http.baseURI, http.auth.type, auth credentials |
netsuite | name, type: "netsuite", netsuite.account, netsuite.environment, netsuite.authType: "token-auto", netsuite._iClientId |
salesforce | name, type: "salesforce", salesforce.sandbox (boolean), salesforce.packagedOAuth: true |
rdbms | name, type: "rdbms", rdbms.host, rdbms.port, rdbms.database, rdbms.user, rdbms.password |
ftp | name, type: "ftp", ftp.host, ftp.port, ftp.username, auth (password or key) |
s3 | name, type: "s3", s3.region, s3.bucket, IAM credentials |
mongodb | name, type: "mongodb", mongodb.host or mongodb.connectionString |
Rule: Always read the base request.yml for shared fields, then the type-specific schema for the connection type you are configuring.
Connection schemas (in references/schemas/):
iClient schemas (in references/iclient-schemas/):
What system do you need to connect to? This determines the connection type, auth method, and configuration shape.
Connection names should describe the system and environment -- not what a specific flow does with them. Connections are shared across exports, imports, and flows, so operation-specific names become misleading as soon as a second resource uses the same connection.
| Bad (operation-specific) | Good (system/environment) |
|---|---|
Shopify - Customer Upsert | Shopify - my-store |
Microsoft Dynamics 365 Business Central - Companies Export | Microsoft Dynamics 365 Business Central - sandbox |
Stripe - Invoice Fetch | Stripe - Production |
If the account has multiple environments or instances of the same system, include the distinguishing detail (store name, environment, account ID). Otherwise just the system name is fine.
Before creating a new connection, check what already exists in the account and marketplace:
The account index auto-refreshes when stale (>4 hours). Force a fresh snapshot with celigo account snapshot.
Reusing an existing connection avoids duplicate credentials and shares concurrency.
When presenting connection choices to the user, filter out connections that are offline: true or have status: "offline". Only show online/active connections as options. If ALL matching connections are offline, mention that and let the user decide whether to proceed with an offline connection or fix connectivity first.
For HTTP connections, search for a pre-built connector before configuring manually. Configure by hand only when no connector exists for the application or the connector doesn't support the auth scheme or endpoints you need:
If an HTTP connector exists, set http._httpConnectorId and http._httpConnectorVersionId on the connection. The connector provides auth templates, base URL, and pre-built endpoints.
Check for a global iClient. Many pre-built connectors ship with a global (Celigo-managed) iClient -- a shared OAuth app registration that handles authorization out of the box (e.g., Microsoft Business Central, Shopify, Google). When a global iClient is available:
http.auth.type: "oauth" with http.auth.oauth.useIClientFields: true and http._iClientId pointing to the global iClient ID.auth.type: "token") just because you don't have live credentials yet. The connection should be created with the correct OAuth auth shape and saved as offline: true.To find existing global iClients, check any working connection in the account that uses the same connector -- its http._iClientId will reference the global iClient. You can also inspect the connector's auth configuration via http-connectors get <id> --full.
Use the Connection Types table above to pick the type value and open the matching schema for available auth options and required fields.
Every connection needs at minimum: name, type, and the type-specific config block (http{}, netsuite{}, ftp{}, etc.).
Offline connections must use the correct auth shape. When creating a connection without live credentials (e.g., demo, placeholder, or pre-staging), always configure the full auth structure the connection will ultimately use -- OAuth type, iClient reference, grant type, etc. -- and save with offline: true. This ensures the connection can be authorized in place later without reconfiguration. Never substitute static token auth as a shortcut for an OAuth connection.
For OAuth connections, authorize via browser first: celigo connections authorize <id>.
Note: connections set and iclients set only apply PATCH-whitelisted fields (e.g. name, debugDate, debugUntil; iclients also oauth2.failPath). PATCH never re-sends the masked credentials GET returns as "******", so it's safe. Any non-whitelisted field errors instead of falling back to a full PUT that would overwrite stored secrets -- use update (which guards against submitting masked values) for those.
Before creating or updating a connection, verify:
name describes the system/environment, not a specific operation (e.g., "Shopify - my-store", not "Shopify - Customer Upsert")type matches the target system (see Connection Types)http{}, netsuite{}, rdbms{}, etc.)"******" from a prior GETceligo http-connectors list); manual config only because none exists or it doesn't fithttp._httpConnectorId and http._httpConnectorVersionId are setauth.type is "oauth" (not "token" with a static bearer), even if saving offline: truenetsuite.authType is token-auto (not deprecated basic)These apply to both connections and iClients unless noted:
"******". Never round-trip a GET response back to PUT without restoring the real values. This is why set only PATCHes whitelisted non-credential fields, and update refuses a payload still containing "******" unless you pass --force.celigo connections authorize <id>.Connections only:
rest type is legacy. Always use type: "http" for new REST connections.basic auth is deprecated. Use token-auto (Celigo-managed TBA) for new connections._borrowConcurrencyFromConnectionId shares slots. The borrowing connection's concurrencyLevel is ignored.auth.type: "oauth" -- do not substitute auth.type: "token" with a static bearer token, even for offline/dummy connections. Static tokens expire and produce the wrong auth shape.| Error | Cause | Fix |
|---|---|---|
401 Unauthorized | Invalid or expired credentials | Verify auth credentials; for OAuth, re-run celigo connections authorize <id> |
403 Forbidden | Valid credentials but insufficient permissions | Check the user/role permissions in the target system |
422 Unprocessable Entity -- invalid type | type value is not recognized or misspelled | Use exact values from Connection Types: http, netsuite, salesforce, rdbms, etc. |
422 Unprocessable Entity -- missing fields | Required type-specific fields are absent | Check Minimum Required Fields for the connection type |
ping returns offline | Connection created but cannot reach the target | Verify host/URL, credentials, firewall rules, and VPN/agent requirements |
ECONNREFUSED / ETIMEDOUT | Network-level failure to target system | Check host/port, DNS resolution, firewall rules; for on-prem systems, verify _agentId is set |
OAuth invalid_grant | Refresh token expired or revoked | Re-authorize: celigo connections authorize <id> |
"******" saved as credential | Round-tripped a GET response back to PUT | Never PUT masked values; always provide real credentials on update |
429 Too Many Requests | Destination rate limit exceeded | If auto-recover is enabled, the connection throttles and retries automatically; otherwise enable it or lower concurrencyLevel. See Credential Discipline & Runtime Behavior |
How connections behave once they exist -- the credential rules every update must follow, and the runtime model (state, queues, rate-limit recovery, debug) to reason about when troubleshooting.
Celigo requires the external system's credentials to be re-submitted on every connection update -- a security guardrail proving the person making the change controls the target system, not a UI quirk. Because chat conversations are logged in clear text, never accept, request, or echo a real credential (API key, OAuth token or client secret, SFTP password, cert key, AS2 cert pair) in chat.
The credentials live on the durable connection record; the runtime auth state (token still valid, cert still trusted, system reachable) is separate and ephemeral.
A connection is online (credentials valid, target reachable, every dependent can run) or offline (token expired, API changed, network unreachable, credentials rotated without updating Celigo, cert expired). The connection resource exists in either state -- going offline doesn't delete it. Probe it with celigo connections ping <id>.
When a user reports "my flow is failing" and the root cause is the connection, fix the shared connection, not each dependent flow. One connection backs many consumers (flows, APIs, Tools, AI agents); every dependent recovers the moment the connection is back online. That one-fix-all-recover payoff is the whole point of the connection abstraction.
Every connection is backed by its own dedicated FIFO queue. Records routed through a connection land in its queue and process first-in, first-out. Two flows using the same connection share one queue; a flow that touches multiple connections lands in multiple queues (one per connection). This is the mental model for throughput, rate limits, and "why are my flows competing for capacity?"
concurrencyLevel sets how many messages from the queue process in parallel -- match it to the external system's published API governance limit (if the destination permits 25 parallel requests, set concurrencyLevel: 25 to run at the ceiling without going over). Queue depth is connection-level, owned collectively by every consumer -- never attribute a deep queue to a single flow, and treat a deep queue as an explanation (work ahead in line), not a defect.
Throughput symptoms almost always point back to the connection, not the flow:
| Symptom | Likely cause / fix |
|---|---|
| Flow hitting rate limits | concurrencyLevel too high for what the destination permits, or auto-recover disabled |
| Flow slow / not keeping up | concurrencyLevel too low; the system permits more parallelism than the connection uses |
| Some flows starve others | High-volume flows share one connection's queue -- partition into separate connections (high-priority vs back-office) and set concurrency per priority |
| Need to throttle a system | Lower concurrencyLevel on the connection serving it |
New connections enable auto-recover rate limit errors by default, with a per-adaptor target concurrency (HTTP default 25, FTP default 1, tunable per connection). On a rate-limit error (429 or equivalent), instead of piling errors into Open errors the connection throttles itself and recovers:
Recovered records land in the Resolved errors tab (not Open errors), and the concurrency adjustments appear in the connection's audit log. Mid-run, disabling auto-recover cancels recovery (the flow continues at the target concurrency; unresolved rate-limit errors go to Open errors), and changing the target concurrency takes effect immediately for subsequent retries.
When multiple connections point at the same system but the system enforces an account-wide rate limit, have them share one budget: set _borrowConcurrencyFromConnectionId on each borrowing connection to point at a parent, and set concurrencyLevel on the parent to the system's limit. All borrowers draw from that shared budget (a borrower's own concurrencyLevel is ignored). This fits the "partition by identity" pattern -- different credentials or teams, one global API limit. A borrowing connection has no auto-recover toggle of its own; the parent connection's auto-recover setting governs.
When a flow fails in ways online/offline doesn't explain -- the target is reachable and credentials look fine, but records are rejected with cryptic errors or the wrong data comes back -- capture the raw traffic with the connection debugger:
An iClient is a reusable OAuth credential store -- it holds the client ID, client secret, scopes, and provider-specific OAuth configuration that can be shared across multiple connections. Instead of embedding OAuth app credentials directly in each connection, you create one iClient and reference it.
| Scenario | iClient needed? |
|---|---|
| HTTP connection with pre-built connector that has a global iClient | Use the global iClient -- set http._iClientId to the connector's built-in iClient ID. No custom iClient needed. |
| HTTP connection with OAuth2 using a custom app registration | Yes -- create a custom iClient with your clientId/clientSecret, reference via http._iClientId |
Salesforce connection with packagedOAuth: false | Yes -- store Connected App credentials in iClient |
NetSuite connection with authType: "token-auto" | Yes -- store integration record's consumer key/secret in iClient |
| HTTP connection with pre-built connector (no global iClient) | Maybe -- check if the connector's auth requires one |
| HTTP connection with token auth (no OAuth) | No -- credentials go directly on the connection |
| Database, FTP, or non-OAuth connections | No |
The rule of thumb: if a global iClient exists for the connector, use it. If the connection uses OAuth and you're bringing your own app registration, create a custom iClient.
http._iClientId -- when http.auth.type is oauth and oauth.useIClientFields: truenetsuite._iClientId -- when authType: "token-auto" (Celigo-managed TBA)packagedOAuth: false (custom Connected App)mcp._iClientId -- for OAuth-based MCP server authThe provider field selects which auth configuration is used:
| System | Provider value |
|---|---|
| Google APIs | google |
| Salesforce | salesforce |
| Azure AD / Microsoft | azureoauth |
| NetSuite (TBA) | netsuite |
| Shopify | shopify |
| Any custom OAuth2 API | custom_oauth2 |
| eBay | ebay or ebay-xml |
See request.yml for the full provider enum.
Use the schema for the matching provider. All schemas are in references/iclient-schemas/:
| Provider | Schema | Key fields |
|---|---|---|
| Base fields (all) | request.yml | provider, name, formType |
| Response shape | response.yml | _id, _userId, timestamps |
custom_oauth2, google, azureoauth, shopify, etc. | oauth2.yml | clientId, clientSecret, scope, grantType, token/refresh/revoke endpoints, PKCE |
netsuite | netsuite.yml | consumerKey, consumerSecret |
salesforce | salesforce.yml | clientId, clientSecret, optional privateKey for JWT bearer |
ebay, ebay-xml | ebay.yml | appId, devId, certId |
amazonmws | ebay.yml | accessKeyId, secretKey |
Every iClient needs at minimum: provider and the matching provider-specific config block (oauth2{}, netsuite{}, salesforce{}, etc.).
After creating the iClient, set the _iClientId on the connection (http._iClientId, netsuite._iClientId, mcp._iClientId).
For OAuth connections, authorize via browser: celigo connections authorize <connectionId>.
custom_oauth2 -- the generic OAuth2 escape hatchPick a named provider whenever one matches the system -- it carries provider-aware defaults and the correct sub-config shape. Reach for custom_oauth2 only for OAuth2 APIs with no named provider; because nothing is preset, you supply the flow details yourself:
clientId / clientSecret -- the registered app's credentialsscope (+ scopeDelimiter) and redirectUri (must match the callback registered with the provider exactly)grantType -- authorization-code, client-credentials, or passwordclientCredentialsLocation (send client credentials in a basic-auth header vs the request body)validDomainNames -- required for custom_oauth2; list each unique domain from your auth/token/revoke URLs (host only, no scheme or path)See oauth2.yml for the full field set.
Some providers require a JWT assertion as part of the token request. Set enableJWT: true and populate the jwt block on the iClient; for Salesforce JWT bearer, supply the privateKey (see salesforce.yml). Handlebars templates reference the signed token via {{{iClient.jwt.token}}}. The clientSecret and private key are credentials -- the Credential Discipline & Runtime Behavior rules apply: never paste them in chat.
An iClient is the app registration, not an identity. It holds the app's clientId / clientSecret, while the per-user access and refresh tokens produced by actually running the OAuth flow live on the connection, not the iClient. That's why the default is one iClient, many connections -- register the app once, store it as an iClient, and point every connection that should authenticate as that app at it via _iClientId. Ten Salesforce connections for ten different orgs can all share one iClient: same app, ten distinct authenticated identities.
Minting a separate iClient per connection duplicates the same clientId / clientSecret and multiplies rotation work. Reach for distinct iClients only when connections genuinely need different registered apps -- different developer accounts, different scope grants, separate rate-limit pools, or a hard separation between environments where each has its own provider-side app.
Smell test (mirrors connections): renewing the same app's credentials -> update in place; switching to a different app -> new iClient.
_httpConnectorId is immutable. Once an iClient is linked to an HTTP connector, it cannot be changed. Create a new iClient if you need a different connector.provider determines valid fields. Setting provider: "netsuite" means the netsuite block is used; provider: "custom_oauth2" means the oauth2 block. Mismatching provider and config block silently ignores the wrong block.{{{iClient.fieldName}}} to access values stored in encrypted or unencrypted objects. For JWT: {{{iClient.jwt.token}}}.validDomainNames is required for custom OAuth2. Provide each unique domain from your auth/token/revoke URLs (without scheme or path).clientSecret re-points OAuth for all of them at once. Confirm which connections depend on the iClient before changing it.***# Search HTTP connectors (550+ apps: Shopify, Stripe, HubSpot, etc.)
celigo http-connectors list
celigo http-connectors get <id> --full # see auth config, endpoints, resources
# Search trading partner connectors (EDI, AS2, VAN)
celigo tp-connectors listceligo connections ping <id># CRUD
celigo connections list
celigo connections get <id>
celigo connections create < connection.json
celigo connections update <id> < connection.json
celigo connections delete <id>
# Test connectivity
celigo connections ping <id>
# OAuth authorization (opens browser for OAuth flow)
celigo connections authorize <id> [--timeout <seconds>] [--print-url]
# Debug
celigo connections enable-debug <id> [--duration <minutes>]
celigo connections disable-debug <id>
celigo connections debug-logs <id>
# Integration-level connection management
celigo integrations register-connections <integrationId> <connectionIds...>
celigo integrations deregister-connections <integrationId> <connectionIds...>
# Replace connection across a flow's exports/imports
celigo flows replace-connection <flowId> <oldConnectionId> <newConnectionId>celigo connections enable-debug <id> [--duration <minutes>] # 15 min default, up to ~1 hour
celigo connections debug-logs <id>
celigo connections disable-debug <id>┌──────────────┐ _iClientId ┌──────────────┐
│ Connection │ ──────────────────────► │ iClient │
│ (HTTP/SF/NS) │ │ (OAuth app) │
└──────────────┘ └──────────────┘
│
stores clientId, clientSecret,
scopes, token/refresh/revoke
endpoints, provider config# CRUD
celigo iclients list
celigo iclients get <id>
celigo iclients create < iclient.json
celigo iclients update <id> < iclient.json
celigo iclients delete <id>