ax-check.com
by Gauge
C
Celigo
celigo.com
ax-check.com
Checked 20 October 2026
B
74/100
Agents need your login
Docs and CLI install cleanly, but exact prices stay hidden.
Setup, MCP server, and CLI install all check out, but the pricing page lists editions with no dollar figures—only a contact prompt to scale.
4 fixes to start with
Concrete changes, backed by what we found.
Recorded coding sessions
Same task, 3 models, no celigo.com account. Recorded 20 October 2026 on Pi.
3 recorded · 4m 47s
4m 47s · recorded
Session findings
Select a finding to see it in context.
›Thinking…
The user is asking about Celigo. Let me understand the context. This appears to be a sandbox/repo. They want help building a simple example using Celigo, pricing info, and whether the product is easy to manage. They also want me to check if there's a developer workflow I can act on.
Let me first look at the current working directory to see if there's anything relevant.
Let me start by exploring the repo and checking for any Celigo-related info. Since I don't have internet access necessarily, let me check. But the instructions say "Stay light: use the hosted product through its SDK or API." I should investigate whether there's an SDK/API available.
Let me check the environment and repo first.
pwd && ls -la && echo "---ENV---" && env | grep -i -E 'celigo|api|key|token' | sed 's/=.*/=<redacted>/'
/sandbox/repo
/sandbox/repo total 16 drwxr-xr-x 3 root root 4096 Sep 18 08:00 . drwxr-xr-x 7 root root 4096 Oct 20 15:48 .. drwxr-xr-x 8 root root 4096 Sep 24 20:05 .git -rw-r--r-- 1 root root 408 Sep 18 08:00 .gitignore ---ENV--- PI_GATEWAY_API_KEY=<redacted> GIT_CONFIG_KEY_0=<redacted>
Recorded tool call
{
"type": "tool_call",
"seq": 3,
"ts": "2026-10-20T15:49:07.284Z",
"name": "bash",
"turn": 1,
"input": {
"command": "pwd && ls -la && echo \"---ENV---\" && env | grep -i -E 'celigo|api|key|token' | sed 's/=.*/=<redacted>/'"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_de5a6df2af0248b99c3e4096",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 6,
"ts": "2026-10-20T15:49:07.284Z",
"turn": 1,
"output": {
"content": [
{
"text": "/sandbox/repo\ntotal 16\ndrwxr-xr-x 3 root root 4096 Sep 18 08:00 .\ndrwxr-xr-x 7 root root 4096 Oct 20 15:48 ..\ndrwxr-xr-x 8 root root 4096 Sep 24 20:05 .git\n-rw-r--r-- 1 root root 408 Sep 18 08:00 .gitignore\n---ENV---\nPI_GATEWAY_API_KEY=<redacted>\nGIT_CONFIG_KEY_0=<redacted>\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_de5a6df2af0248b99c3e4096",
"is_error": false,
"tool_kind": "shell"
}which curl node npm python3 pip3 2>/dev/null; echo "---net test---"; curl -sS -m 8 -o /dev/null -w "%{http_code}\n" https://api.celigo.com 2>&1 | head -5/usr/bin/curl
/usr/bin/curl /usr/local/bin/node /usr/local/bin/npm /usr/local/bin/python3 /usr/local/bin/pip3 ---net test--- curl: (6) Could not resolve host: api.celigo.com 000
Recorded tool call
{
"type": "tool_call",
"seq": 4,
"ts": "2026-10-20T15:49:07.284Z",
"name": "bash",
"turn": 1,
"input": {
"command": "which curl node npm python3 pip3 2>/dev/null; echo \"---net test---\"; curl -sS -m 8 -o /dev/null -w \"%{http_code}\\n\" https://api.celigo.com 2>&1 | head -5"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_760dbc4e643440199dc1747b",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 7,
"ts": "2026-10-20T15:49:07.284Z",
"turn": 1,
"output": {
"content": [
{
"text": "/usr/bin/curl\n/usr/local/bin/node\n/usr/local/bin/npm\n/usr/local/bin/python3\n/usr/local/bin/pip3\n---net test---\ncurl: (6) Could not resolve host: api.celigo.com\n000\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_760dbc4e643440199dc1747b",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
No network access to api.celigo.com. Let me check if there's any internet at all and check the git repo for context.
Let me check the repo history and the .gitignore.
cd /sandbox/repo && cat .gitignore && echo "---LOG---" && git log --oneline -20 2>&1 | head -30 && echo "---BRANCHES---" && git branch -a 2>&1
# Keep generated and vendored files out of source diffs.
# Keep generated and vendored files out of source diffs. # Logs / OS cruft *.log .DS_Store # JavaScript / TypeScript node_modules/ .next/ dist/ build/ coverage/ # Python .venv/ venv/ __pycache__/ *.py[cod] .pytest_cache/ *.egg-info/ # Rust target/ # Go / PHP / Ruby vendored deps vendor/ .bundle/ # Java / Kotlin / Scala .gradle/ # .NET bin/ obj/ # Elixir _build/ deps/ # Dart / Flutter .dart_tool/ ---LOG--- 9ebf5fb Initial workspace ---BRANCHES--- * main
Recorded tool call
{
"type": "tool_call",
"seq": 10,
"ts": "2026-10-20T15:49:09.628Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /sandbox/repo && cat .gitignore && echo \"---LOG---\" && git log --oneline -20 2>&1 | head -30 && echo \"---BRANCHES---\" && git branch -a 2>&1"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_18d13e0081a54992b0a67aae",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 12,
"ts": "2026-10-20T15:49:09.628Z",
"turn": 1,
"output": {
"content": [
{
"text": "# Keep generated and vendored files out of source diffs.\n\n# Logs / OS cruft\n*.log\n.DS_Store\n\n# JavaScript / TypeScript\nnode_modules/\n.next/\ndist/\nbuild/\ncoverage/\n\n# Python\n.venv/\nvenv/\n__pycache__/\n*.py[cod]\n.pytest_cache/\n*.egg-info/\n\n# Rust\ntarget/\n\n# Go / PHP / Ruby vendored deps\nvendor/\n.bundle/\n\n# Java / Kotlin / Scala\n.gradle/\n\n# .NET\nbin/\nobj/\n\n# Elixir\n_build/\ndeps/\n\n# Dart / Flutter\n.dart_tool/\n---LOG---\n9ebf5fb Initial workspace\n---BRANCHES---\n* main\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_18d13e0081a54992b0a67aae",
"is_error": false,
"tool_kind": "shell"
}for h in github.com registry.npmjs.org pypi.org google.com; do printf "%s -> " "$h"; curl -sS -m 6 -o /dev/null -w "%{http_code}\n" "https://$h" 2>&1 | head -1; donegithub.com -> 200
github.com -> 200 registry.npmjs.org -> 200 pypi.org -> 200 google.com -> 301
Recorded tool call
{
"type": "tool_call",
"seq": 11,
"ts": "2026-10-20T15:49:09.628Z",
"name": "bash",
"turn": 1,
"input": {
"command": "for h in github.com registry.npmjs.org pypi.org google.com; do printf \"%s -> \" \"$h\"; curl -sS -m 6 -o /dev/null -w \"%{http_code}\\n\" \"https://$h\" 2>&1 | head -1; done"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_8836591467df415ea12471ec",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 13,
"ts": "2026-10-20T15:49:14.828Z",
"turn": 1,
"output": {
"content": [
{
"text": "github.com -> 200\nregistry.npmjs.org -> 200\npypi.org -> 200\ngoogle.com -> 301\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_8836591467df415ea12471ec",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
Interesting: general internet works (github, npm, pypi), but api.celigo.com does not resolve. Let me verify whether Celigo's actual API host resolves. Celigo's product is an iPaaS (Integration Platform as a Service). Their API is at api.integrator.io, and their docs at docs.celigo.com. Let me check DNS for those.
Let me also check npm/pypi for a Celigo SDK.
for h in api.integrator.io integrator.io docs.celigo.com celigo.com developer.celigo.com; do printf "%s -> " "$h"; getent hosts "$h" >/dev/null 2>&1 && echo "DNS ok" || echo "NO DNS"; done
api.integrator.io -> DNS ok
api.integrator.io -> DNS ok integrator.io -> DNS ok docs.celigo.com -> DNS ok celigo.com -> DNS ok developer.celigo.com -> DNS ok
Recorded tool call
{
"type": "tool_call",
"seq": 16,
"ts": "2026-10-20T15:49:18.013Z",
"name": "bash",
"turn": 1,
"input": {
"command": "for h in api.integrator.io integrator.io docs.celigo.com celigo.com developer.celigo.com; do printf \"%s -> \" \"$h\"; getent hosts \"$h\" >/dev/null 2>&1 && echo \"DNS ok\" || echo \"NO DNS\"; done"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_68a604b03321492e96742dcb",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 18,
"ts": "2026-10-20T15:49:18.013Z",
"turn": 1,
"output": {
"content": [
{
"text": "api.integrator.io -> DNS ok\nintegrator.io -> DNS ok\ndocs.celigo.com -> DNS ok\nceligo.com -> DNS ok\ndeveloper.celigo.com -> DNS ok\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_68a604b03321492e96742dcb",
"is_error": false,
"tool_kind": "shell"
}echo "=== npm search celigo ==="; npm search celigo --json 2>/dev/null | head -c 2000; echo; echo "=== npm view celigo ==="; npm view celigo 2>&1 | head -20; echo "=== npm view @celigo/... ==="; npm view @celigo/sdk 2>&1 | head -20
=== npm search celigo ===
=== npm search celigo ===
[
{"name":"@celigo/api-specs","keywords":["openapi","openapi3","celigo","integrator.io","api","specs"],"version":"0.2.124","description":"Public OpenAPI bundles (dist/) and the celigo-cli lint schema pack (schemas.json).","sanitized_name":"@celigo/api-specs","publisher":{"email":"shiva.alluri@celigo.com","username":"shiva.alluri"},"maintainers":[{"email":"shiva.alluri@celigo.com","username":"shiva.alluri"}],"license":"MIT","date":"2026-10-20T14:23:03.357Z","links":{"homepage":"https://developer.celigo.com/api/api-reference","repository":"git+https://github.com/celigo/integrator-api-specs.git","bugs":"https://docs.celigo.com/hc/en-us/requests/new","npm":"https://www.npmjs.com/package/@celigo/api-specs"}}
,
{"name":"@celigo/celigo-cli","keywords":["celigo","integrator","cli","ipaas"],"version":"2026.10.1","description":"Celigo CLI — command-line interface for the integrator.io platform","sanitized_name":"@celigo/celigo-cli","publisher":{"email":"shiva.alluri@celigo.com","username":"shiva.alluri"},"maintainers":[{"email":"shiva.alluri@celigo.com","username":"shiva.alluri"}],"license":"MIT","date":"2026-10-17T22:49:58.235Z","links":{"homepage":"https://developer.celigo.com/cli","repository":"git+https://github.com/celigo/celigo-cli.git","bugs":"https://docs.celigo.com/hc/en-us/requests/new","npm":"https://www.npmjs.com/package/@celigo/celigo-cli"}}
,
{"name":"celigo-mcp","keywords":["celigo","integrator.io","mcp","model-context-protocol","claude","ai","integration","ipaas"],"version":"2.0.2","description":"MCP server for Celigo integrator.io — gives Claude and other MCP clients access to integrations, flows, connections, exports, imports, and error logs.","sanitized_name":"celigo-mcp","publisher":{"email":"info@accentra.com.au","username":"accentra-australia"},"maintainers":[{"email":"info@accentra.com.au","username":"accentra-australia"}],"license":"MIT","date":"2026-04-09T09:28:39.509Z","links":{"homepage":"https://github.com/accentra-australia/celigo-mcp#rea
=== npm view celigo ===
npm error code E404
npm error 404 Not Found - GET https://registry.npmjs.org/celigo - Not found
npm error 404
npm error 404 'celigo@*' is not in this registry.
npm error 404
npm error 404 Note that you can also install from a
npm error 404 tarball, folder, http url, or git url.
npm error A complete log of this run can be found in: /sandbox/.npm/_logs/2026-10-20T15_49_18_890Z-debug-0.log
=== npm view @celigo/... ===
npm error code E404
npm error 404 Not Found - GET https://registry.npmjs.org/@celigo%2fsdk - Not found
npm error 404
npm error 404 '@celigo/sdk@*' is not in this registry.
npm error 404
npm error 404 Note that you can also install from a
npm error 404 tarball, folder, http url, or git url.
npm error A complete log of this run can be found in: /sandbox/.npm/_logs/2026-10-20T15_49_19_654Z-debug-0.log
Recorded tool call
{
"type": "tool_call",
"seq": 17,
"ts": "2026-10-20T15:49:18.013Z",
"name": "bash",
"turn": 1,
"input": {
"command": "echo \"=== npm search celigo ===\"; npm search celigo --json 2>/dev/null | head -c 2000; echo; echo \"=== npm view celigo ===\"; npm view celigo 2>&1 | head -20; echo \"=== npm view @celigo/... ===\"; npm view @celigo/sdk 2>&1 | head -20"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_9c746a0187a04ed296ad23cb",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 19,
"ts": "2026-10-20T15:49:21.155Z",
"turn": 1,
"output": {
"content": [
{
"text": "=== npm search celigo ===\n[\n\n{\"name\":\"@celigo/api-specs\",\"keywords\":[\"openapi\",\"openapi3\",\"celigo\",\"integrator.io\",\"api\",\"specs\"],\"version\":\"0.2.124\",\"description\":\"Public OpenAPI bundles (dist/) and the celigo-cli lint schema pack (schemas.json).\",\"sanitized_name\":\"@celigo/api-specs\",\"publisher\":{\"email\":\"shiva.alluri@celigo.com\",\"username\":\"shiva.alluri\"},\"maintainers\":[{\"email\":\"shiva.alluri@celigo.com\",\"username\":\"shiva.alluri\"}],\"license\":\"MIT\",\"date\":\"2026-10-20T14:23:03.357Z\",\"links\":{\"homepage\":\"https://developer.celigo.com/api/api-reference\",\"repository\":\"git+https://github.com/celigo/integrator-api-specs.git\",\"bugs\":\"https://docs.celigo.com/hc/en-us/requests/new\",\"npm\":\"https://www.npmjs.com/package/@celigo/api-specs\"}}\n\n,\n\n{\"name\":\"@celigo/celigo-cli\",\"keywords\":[\"celigo\",\"integrator\",\"cli\",\"ipaas\"],\"version\":\"2026.10.1\",\"description\":\"Celigo CLI — command-line interface for the integrator.io platform\",\"sanitized_name\":\"@celigo/celigo-cli\",\"publisher\":{\"email\":\"shiva.alluri@celigo.com\",\"username\":\"shiva.alluri\"},\"maintainers\":[{\"email\":\"shiva.alluri@celigo.com\",\"username\":\"shiva.alluri\"}],\"license\":\"MIT\",\"date\":\"2026-10-17T22:49:58.235Z\",\"links\":{\"homepage\":\"https://developer.celigo.com/cli\",\"repository\":\"git+https://github.com/celigo/celigo-cli.git\",\"bugs\":\"https://docs.celigo.com/hc/en-us/requests/new\",\"npm\":\"https://www.npmjs.com/package/@celigo/celigo-cli\"}}\n\n,\n\n{\"name\":\"celigo-mcp\",\"keywords\":[\"celigo\",\"integrator.io\",\"mcp\",\"model-context-protocol\",\"claude\",\"ai\",\"integration\",\"ipaas\"],\"version\":\"2.0.2\",\"description\":\"MCP server for Celigo integrator.io — gives Claude and other MCP clients access to integrations, flows, connections, exports, imports, and error logs.\",\"sanitized_name\":\"celigo-mcp\",\"publisher\":{\"email\":\"info@accentra.com.au\",\"username\":\"accentra-australia\"},\"maintainers\":[{\"email\":\"info@accentra.com.au\",\"username\":\"accentra-australia\"}],\"license\":\"MIT\",\"date\":\"2026-04-09T09:28:39.509Z\",\"links\":{\"homepage\":\"https://github.com/accentra-australia/celigo-mcp#rea\n=== npm view celigo ===\nnpm error code E404\nnpm error 404 Not Found - GET https://registry.npmjs.org/celigo - Not found\nnpm error 404\nnpm error 404 'celigo@*' is not in this registry.\nnpm error 404\nnpm error 404 Note that you can also install from a\nnpm error 404 tarball, folder, http url, or git url.\nnpm error A complete log of this run can be found in: /sandbox/.npm/_logs/2026-10-20T15_49_18_890Z-debug-0.log\n=== npm view @celigo/... ===\nnpm error code E404\nnpm error 404 Not Found - GET https://registry.npmjs.org/@celigo%2fsdk - Not found\nnpm error 404\nnpm error 404 '@celigo/sdk@*' is not in this registry.\nnpm error 404\nnpm error 404 Note that you can also install from a\nnpm error 404 tarball, folder, http url, or git url.\nnpm error A complete log of this run can be found in: /sandbox/.npm/_logs/2026-10-20T15_49_19_654Z-debug-0.log\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_9c746a0187a04ed296ad23cb",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
There is a real developer workflow. Celigo has:
@celigo/api-specs- OpenAPI bundles@celigo/celigo-cli- CLI for integrator.io- REST API at api.integrator.io
- docs at developer.celigo.com
The user asked me to build a simple example using Celigo, tell them how pricing works, and whether it's easy for me to manage. Also "If this product has no developer workflow you can act on, say so plainly and stop. Stay light: use the hosted product through its SDK or API. Do not start local service stacks or wait for long-running commands; if the quickstart requires either, say so plainly and stop."
So there IS a developer workflow. I should build a simple example. Let me investigate the CLI and API. Let me look at the celigo-cli package and the api-specs. Let me also check developer.celigo.com for quickstart and pricing.
Let me be light: I can install the CLI (npm install -g) and inspect it, and fetch docs. I should not start local service stacks or run long commands. Installing an npm package is quick.
Let me check the CLI's README and the API reference. Let me fetch docs.celigo.com / developer.celigo.com pages for pricing.
Let me first check npm package info and try installing the CLI.
cd /tmp && npm view @celigo/celigo-cli --json 2>/dev/null | head -c 3000; echo; echo "=== README ==="; npm view @celigo/celigo-cli readme 2>/dev/null | head -c 4000
{
{
"_id": "@celigo/celigo-cli@2026.10.1",
"_rev": "21-4681fa51f98084932b80d63faa7e0a55",
"name": "@celigo/celigo-cli",
"dist-tags": {
"latest": "2026.10.1"
},
"versions": [
"0.1.0",
"1.0.0",
"1.0.4",
"2026.4.1",
"2026.4.2",
"2026.4.3",
"2026.4.4",
"2026.4.5",
"2026.6.1",
"2026.6.2",
"2026.8.1",
"2026.8.2",
"2026.8.3",
"2026.8.4",
"2026.8.6",
"2026.8.7",
"2026.8.8",
"2026.9.1",
"2026.9.5",
"2026.9.6",
"2026.10.1"
],
"time": {
"created": "2026-04-07T16:44:57.966Z",
"modified": "2026-10-17T22:49:58.430Z",
"0.1.0": "2026-04-07T16:44:58.209Z",
"1.0.0": "2026-04-07T17:05:30.474Z",
"1.0.4": "2026-04-08T17:21:25.157Z",
"2026.4.1": "2026-04-13T17:39:20.970Z",
"2026.4.2": "2026-04-13T19:21:45.857Z",
"2026.4.3": "2026-04-14T20:08:54.857Z",
"2026.4.4": "2026-04-15T00:30:25.153Z",
"2026.4.5": "2026-04-18T17:13:36.920Z",
"2026.6.1": "2026-06-18T04:56:44.000Z",
"2026.6.2": "2026-06-26T14:35:30.910Z",
"2026.8.1": "2026-08-03T16:51:41.352Z",
"2026.8.2": "2026-08-03T22:05:59.875Z",
"2026.8.3": "2026-08-04T06:52:03.978Z",
"2026.8.4": "2026-08-06T18:14:29.374Z",
"2026.8.6": "2026-08-07T19:16:53.113Z",
"2026.8.7": "2026-08-23T20:02:41.612Z",
"2026.8.8": "2026-08-30T04:03:55.808Z",
"2026.9.1": "2026-09-13T21:55:48.515Z",
"2026.9.5": "2026-09-15T03:57:58.063Z",
"2026.9.6": "2026-09-26T03:24:02.146Z",
"2026.10.1": "2026-10-17T22:49:58.235Z"
},
"bugs": {
"url": "https://docs.celigo.com/hc/en-us/requests/new"
},
"author": "Celigo, Inc.",
"license": "MIT",
"homepage": "https://developer.celigo.com/cli",
"keywords": [
"celigo",
"integrator",
"cli",
"ipaas"
],
"description": "Celigo CLI — command-line interface for the integrator.io platform",
"maintainers": [
"shiva.alluri <shiva.alluri@celigo.com>"
],
"readmeFilename": "README.md",
"bin": {
"celigo": "dist/index.js"
},
"dist": {
"shasum": "a8e6b34da705987e6f2dc88c5660609bd4ef3eb4",
"tarball": "https://registry.npmjs.org/@celigo/celigo-cli/-/celigo-cli-2026.10.1.tgz",
"fileCount": 6,
"integrity": "sha512-dJ1M3tbBdtQVfJzVTPWZOhUjS0FLwooO5BWaUbml13kLu7nsNLsvf2bM6/3MAnxcqfi4ddi+MNmKYX8czB7sqA==",
"signatures": [
{
"sig": "MEQCIEt9ZQevk8x6OJDnf4+mc7bx9jlacosKUr+kWETyGskeAiB+4VKvzCMpQwCgJAKZj8N0u8qJM+iOPCRKZDruHNZNew==",
"keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"
},
{
"keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U",
"sig": "MEYCIQCZWovh0dpaxCnxunoJbFiucaIFSflo1mgx+A6Pa4wqGwIhAKC89wmBVpsidSOK62gF7PHs3Hn4LBIZ+RJBtEPG7Z/T"
}
],
"unpackedSize": 2152839
},
"type": "module",
"engines": {
"node": ">=22"
},
"gitHead": "a2622e2d80fe1038cb0245ead6dd548e0106d07e",
"scripts": {
"dev": "tsx src/index.ts",
"lint": "biome check src tests",
"test": "vi
=== README ===
# Celigo CLI
[](https://www.npmjs.com/package/@celigo/celigo-cli) [](https://opensource.org/license/mit)
The Celigo CLI is the command-line interface for [Celigo integrator.io](https://www.celigo.com). Manage integrations, flows, connections, and 30+ resource types from your terminal.
## Install
```bash
npm install -g @celigo/celigo-cli
```
Requires Node.js 22+.
npm 11 warns on a global install that `node-jq` has an install script: it downloads the jq
binary `--jq` runs. `npm install -g @celigo/celigo-cli --allow-scripts=node-jq` allows it (and
is what `--strict-allow-scripts` requires). Installed with `--ignore-scripts` instead, `--jq`
runs the jq that `JQ_PATH` names, or a `jq` on PATH.
The CLI checks npm for a newer version at most once every four hours. When one is available
it announces itself on stderr and installs the update in the background (the equivalent of
`npm install -g @celigo/celigo-cli@<latest>`), which applies from your next command. It never
runs in CI. `celigo config set auto_update false` keeps the check but only prints the upgrade
hint; `CELIGO_NO_UPDATE=1` disables checking entirely.
When the CLI cannot update itself, it says so instead of silently staying on the old version:
if the installed package directory is not writable by your user (the standard macOS and Linux
Node installers leave it owned by root), the CLI prints the exact
`sudo npm install -g @celigo/celigo-cli@<latest>` command to run instead, and if a background
install fails for any other reason, the next check reports that the update did not apply and
retries.
## Setup
Sign in with OAuth (authorization code + PKCE) or with an API token — a profile holds one or
the other, never both. Credential priority is `--token` > `CELIGO_API_TOKEN` > the profile's
credential.
```bash
celigo login
# EU
celigo login --base-url https://api.eu.integrator.io
```
`celigo login` names the profile after the account and environment the sign-in reaches —
`<company>-<environment>`, such as `acme-corp-production` or `acme-corp-sandbox` — creating it
the first time and signing the same profile in again after that. `--profile <name>` (or
`CELIGO_PROFILE`) names it yourself; the active profile is not the default. A name another
profile already holds is never taken over or suffixed: off a terminal the sign-in is refused and
revoked, and at one you are asked for a name. `celigo logout` revokes the session, and can also
end other CLI sign-ins made from the same browser session (the platform gives them one grant),
which then need `celigo login` again; it names those profiles. `celigo profile whoami` shows which account, environment
and credential the profile uses — for a sign-in, with its issuer, expiry and granted scopes.
Headless environments (CI, no TTY) use the device grant automatically;
pass `--device` to force it. Like a personal access token, a sign-in carries your own
permissions with no scopes to choose. A profile `login` creates is in `full` mode; for a
read-only OAuth profile, create it with `celigo profile add <name> --login --mode read`.
`login` records the account and environment the sign-in reached (from `GET /v1/tokenInfo`)
and refuses a later sign-in into another one under the same profile name — or one that
cannot be confirmed against it — and revokes the new session; `--allow-account-mismatch`
rebinds the profile. The session is only ever sent to the API host it
was issued for: a `--base-url` or `CELIGO_BASE_URL` naming another host is refused for a
signed-in profile, and so is `config set base_url` (sign out first).
API tokens from integrator.io still work: **Resources** (left nav) > **API tokens**.
- **Personal access token (PAT)** — any user (including manage and monitor roles) can generate one. It inherits your own account permissions, with no scopes to configure, and expires after 90 daysRecorded tool call
{
"type": "tool_call",
"seq": 22,
"ts": "2026-10-20T15:49:27.257Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /tmp && npm view @celigo/celigo-cli --json 2>/dev/null | head -c 3000; echo; echo \"=== README ===\"; npm view @celigo/celigo-cli readme 2>/dev/null | head -c 4000"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_51d445e96aff433399634ba5",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 24,
"ts": "2026-10-20T15:49:29.565Z",
"turn": 1,
"output": {
"content": [
{
"text": "{\n \"_id\": \"@celigo/celigo-cli@2026.10.1\",\n \"_rev\": \"21-4681fa51f98084932b80d63faa7e0a55\",\n \"name\": \"@celigo/celigo-cli\",\n \"dist-tags\": {\n \"latest\": \"2026.10.1\"\n },\n \"versions\": [\n \"0.1.0\",\n \"1.0.0\",\n \"1.0.4\",\n \"2026.4.1\",\n \"2026.4.2\",\n \"2026.4.3\",\n \"2026.4.4\",\n \"2026.4.5\",\n \"2026.6.1\",\n \"2026.6.2\",\n \"2026.8.1\",\n \"2026.8.2\",\n \"2026.8.3\",\n \"2026.8.4\",\n \"2026.8.6\",\n \"2026.8.7\",\n \"2026.8.8\",\n \"2026.9.1\",\n \"2026.9.5\",\n \"2026.9.6\",\n \"2026.10.1\"\n ],\n \"time\": {\n \"created\": \"2026-04-07T16:44:57.966Z\",\n \"modified\": \"2026-10-17T22:49:58.430Z\",\n \"0.1.0\": \"2026-04-07T16:44:58.209Z\",\n \"1.0.0\": \"2026-04-07T17:05:30.474Z\",\n \"1.0.4\": \"2026-04-08T17:21:25.157Z\",\n \"2026.4.1\": \"2026-04-13T17:39:20.970Z\",\n \"2026.4.2\": \"2026-04-13T19:21:45.857Z\",\n \"2026.4.3\": \"2026-04-14T20:08:54.857Z\",\n \"2026.4.4\": \"2026-04-15T00:30:25.153Z\",\n \"2026.4.5\": \"2026-04-18T17:13:36.920Z\",\n \"2026.6.1\": \"2026-06-18T04:56:44.000Z\",\n \"2026.6.2\": \"2026-06-26T14:35:30.910Z\",\n \"2026.8.1\": \"2026-08-03T16:51:41.352Z\",\n \"2026.8.2\": \"2026-08-03T22:05:59.875Z\",\n \"2026.8.3\": \"2026-08-04T06:52:03.978Z\",\n \"2026.8.4\": \"2026-08-06T18:14:29.374Z\",\n \"2026.8.6\": \"2026-08-07T19:16:53.113Z\",\n \"2026.8.7\": \"2026-08-23T20:02:41.612Z\",\n \"2026.8.8\": \"2026-08-30T04:03:55.808Z\",\n \"2026.9.1\": \"2026-09-13T21:55:48.515Z\",\n \"2026.9.5\": \"2026-09-15T03:57:58.063Z\",\n \"2026.9.6\": \"2026-09-26T03:24:02.146Z\",\n \"2026.10.1\": \"2026-10-17T22:49:58.235Z\"\n },\n \"bugs\": {\n \"url\": \"https://docs.celigo.com/hc/en-us/requests/new\"\n },\n \"author\": \"Celigo, Inc.\",\n \"license\": \"MIT\",\n \"homepage\": \"https://developer.celigo.com/cli\",\n \"keywords\": [\n \"celigo\",\n \"integrator\",\n \"cli\",\n \"ipaas\"\n ],\n \"description\": \"Celigo CLI — command-line interface for the integrator.io platform\",\n \"maintainers\": [\n \"shiva.alluri <shiva.alluri@celigo.com>\"\n ],\n \"readmeFilename\": \"README.md\",\n \"bin\": {\n \"celigo\": \"dist/index.js\"\n },\n \"dist\": {\n \"shasum\": \"a8e6b34da705987e6f2dc88c5660609bd4ef3eb4\",\n \"tarball\": \"https://registry.npmjs.org/@celigo/celigo-cli/-/celigo-cli-2026.10.1.tgz\",\n \"fileCount\": 6,\n \"integrity\": \"sha512-dJ1M3tbBdtQVfJzVTPWZOhUjS0FLwooO5BWaUbml13kLu7nsNLsvf2bM6/3MAnxcqfi4ddi+MNmKYX8czB7sqA==\",\n \"signatures\": [\n {\n \"sig\": \"MEQCIEt9ZQevk8x6OJDnf4+mc7bx9jlacosKUr+kWETyGskeAiB+4VKvzCMpQwCgJAKZj8N0u8qJM+iOPCRKZDruHNZNew==\",\n \"keyid\": \"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U\"\n },\n {\n \"keyid\": \"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U\",\n \"sig\": \"MEYCIQCZWovh0dpaxCnxunoJbFiucaIFSflo1mgx+A6Pa4wqGwIhAKC89wmBVpsidSOK62gF7PHs3Hn4LBIZ+RJBtEPG7Z/T\"\n }\n ],\n \"unpackedSize\": 2152839\n },\n \"type\": \"module\",\n \"engines\": {\n \"node\": \">=22\"\n },\n \"gitHead\": \"a2622e2d80fe1038cb0245ead6dd548e0106d07e\",\n \"scripts\": {\n \"dev\": \"tsx src/index.ts\",\n \"lint\": \"biome check src tests\",\n \"test\": \"vi\n=== README ===\n# Celigo CLI\n\n[](https://www.npmjs.com/package/@celigo/celigo-cli) [](https://opensource.org/license/mit)\n\nThe Celigo CLI is the command-line interface for [Celigo integrator.io](https://www.celigo.com). Manage integrations, flows, connections, and 30+ resource types from your terminal.\n\n## Install\n\n```bash\nnpm install -g @celigo/celigo-cli\n```\n\nRequires Node.js 22+.\n\nnpm 11 warns on a global install that `node-jq` has an install script: it downloads the jq\nbinary `--jq` runs. `npm install -g @celigo/celigo-cli --allow-scripts=node-jq` allows it (and\nis what `--strict-allow-scripts` requires). Installed with `--ignore-scripts` instead, `--jq`\nruns the jq that `JQ_PATH` names, or a `jq` on PATH.\n\nThe CLI checks npm for a newer version at most once every four hours. When one is available\nit announces itself on stderr and installs the update in the background (the equivalent of\n`npm install -g @celigo/celigo-cli@<latest>`), which applies from your next command. It never\nruns in CI. `celigo config set auto_update false` keeps the check but only prints the upgrade\nhint; `CELIGO_NO_UPDATE=1` disables checking entirely.\n\nWhen the CLI cannot update itself, it says so instead of silently staying on the old version:\nif the installed package directory is not writable by your user (the standard macOS and Linux\nNode installers leave it owned by root), the CLI prints the exact\n`sudo npm install -g @celigo/celigo-cli@<latest>` command to run instead, and if a background\ninstall fails for any other reason, the next check reports that the update did not apply and\nretries.\n\n## Setup\n\nSign in with OAuth (authorization code + PKCE) or with an API token — a profile holds one or\nthe other, never both. Credential priority is `--token` > `CELIGO_API_TOKEN` > the profile's\ncredential.\n\n```bash\nceligo login\n# EU\nceligo login --base-url https://api.eu.integrator.io\n```\n\n`celigo login` names the profile after the account and environment the sign-in reaches —\n`<company>-<environment>`, such as `acme-corp-production` or `acme-corp-sandbox` — creating it\nthe first time and signing the same profile in again after that. `--profile <name>` (or\n`CELIGO_PROFILE`) names it yourself; the active profile is not the default. A name another\nprofile already holds is never taken over or suffixed: off a terminal the sign-in is refused and\nrevoked, and at one you are asked for a name. `celigo logout` revokes the session, and can also\nend other CLI sign-ins made from the same browser session (the platform gives them one grant),\nwhich then need `celigo login` again; it names those profiles. `celigo profile whoami` shows which account, environment\nand credential the profile uses — for a sign-in, with its issuer, expiry and granted scopes.\nHeadless environments (CI, no TTY) use the device grant automatically;\npass `--device` to force it. Like a personal access token, a sign-in carries your own\npermissions with no scopes to choose. A profile `login` creates is in `full` mode; for a\nread-only OAuth profile, create it with `celigo profile add <name> --login --mode read`.\n`login` records the account and environment the sign-in reached (from `GET /v1/tokenInfo`)\nand refuses a later sign-in into another one under the same profile name — or one that\ncannot be confirmed against it — and revokes the new session; `--allow-account-mismatch`\nrebinds the profile. The session is only ever sent to the API host it\nwas issued for: a `--base-url` or `CELIGO_BASE_URL` naming another host is refused for a\nsigned-in profile, and so is `config set base_url` (sign out first).\n\nAPI tokens from integrator.io still work: **Resources** (left nav) > **API tokens**.\n\n- **Personal access token (PAT)** — any user (including manage and monitor roles) can generate one. It inherits your own account permissions, with no scopes to configure, and expires after 90 days",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_51d445e96aff433399634ba5",
"is_error": false,
"tool_kind": "shell"
}echo "=== api-specs README ==="; npm view @celigo/api-specs readme 2>/dev/null | head -c 3000; echo; echo "=== files/dist ==="; npm view @celigo/api-specs dist.tarball 2>/dev/null
=== api-specs README ===
=== api-specs README ===
<!--
This file ships as the README of the @celigo/api-specs npm package. Its audience is public, so it
must not describe repository internals or link into the repository. The repository README is
.github/README.md, which GitHub renders in place of this one.
-->
# @celigo/api-specs
OpenAPI 3.2 specifications for the [Celigo integrator.io](https://www.celigo.com) REST API,
packaged as files for tooling. They are the documents behind the
[API reference on the Celigo Developer Platform](https://developer.celigo.com/api/api-reference),
and what the [Celigo CLI](https://developer.celigo.com/cli) validates resource documents against.
```bash
npm install @celigo/api-specs
```
## What's in the package
Every platform resource has its own OpenAPI document under `dist/` — connections, flows, exports,
imports, integrations, jobs, scripts, tools, and the rest. Each file is self-contained, with every
`$ref` already inlined, so it can go straight into a client generator, a request validator, or an
AI agent's context with no loader configuration.
```
dist/connection.yml
dist/flow.yml
dist/export.yml
dist/import.yml
...
schemas.json
```
`schemas.json` is the schema pack that the Celigo CLI's `celigo lint` command validates documents
against, compiled from the same bundles. Other consumers can ignore it.
Resolve a document the same way as any other packaged file:
```js
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const specPath = require.resolve("@celigo/api-specs/dist/connection.yml");
```
## The API these specs describe
All operations live under the `/v1/` prefix and authenticate with a bearer token
(`Authorization: Bearer <token>`). Create a token in integrator.io under **Resources** >
**API tokens**.
The API is served from four regional hosts. Use the one your account lives in:
| Region | Base URL |
| ------------ | ------------------------------ |
| US (default) | `https://api.integrator.io` |
| EU | `https://api.eu.integrator.io` |
| AU | `https://api.au.integrator.io` |
| CA | `https://api.ca.integrator.io` |
API Management (`/v1/apim/...`) is served from the US and EU hosts only.
To browse the API interactively, use the
[rendered reference](https://developer.celigo.com/api/api-reference): each resource has its own
page, with a console for trying calls against your own account.
## What the specs cover
The specs are prescriptive. They describe the API surface Celigo supports and intends to keep
supporting — not everything the server happens to tolerate. Internal endpoints, deprecated fields,
and retired enum values are deliberately left out.
The practical consequence: a request that validates against these specs is one you can build on.
The reverse does not hold. The server may accept a payload no spec describes, and that payload may
be ignored or rejected later.
## Extensions
Alongside standard OpenAPI, the documents car
=== files/dist ===
https://registry.npmjs.org/@celigo/api-specs/-/api-specs-0.2.124.tgz
Recorded tool call
{
"type": "tool_call",
"seq": 23,
"ts": "2026-10-20T15:49:27.257Z",
"name": "bash",
"turn": 1,
"input": {
"command": "echo \"=== api-specs README ===\"; npm view @celigo/api-specs readme 2>/dev/null | head -c 3000; echo; echo \"=== files/dist ===\"; npm view @celigo/api-specs dist.tarball 2>/dev/null"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_ce60b9d88dad41daac4673d8",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 25,
"ts": "2026-10-20T15:49:30.467Z",
"turn": 1,
"output": {
"content": [
{
"text": "=== api-specs README ===\n<!--\n This file ships as the README of the @celigo/api-specs npm package. Its audience is public, so it\n must not describe repository internals or link into the repository. The repository README is\n .github/README.md, which GitHub renders in place of this one.\n-->\n\n# @celigo/api-specs\n\nOpenAPI 3.2 specifications for the [Celigo integrator.io](https://www.celigo.com) REST API,\npackaged as files for tooling. They are the documents behind the\n[API reference on the Celigo Developer Platform](https://developer.celigo.com/api/api-reference),\nand what the [Celigo CLI](https://developer.celigo.com/cli) validates resource documents against.\n\n```bash\nnpm install @celigo/api-specs\n```\n\n## What's in the package\n\nEvery platform resource has its own OpenAPI document under `dist/` — connections, flows, exports,\nimports, integrations, jobs, scripts, tools, and the rest. Each file is self-contained, with every\n`$ref` already inlined, so it can go straight into a client generator, a request validator, or an\nAI agent's context with no loader configuration.\n\n```\ndist/connection.yml\ndist/flow.yml\ndist/export.yml\ndist/import.yml\n...\nschemas.json\n```\n\n`schemas.json` is the schema pack that the Celigo CLI's `celigo lint` command validates documents\nagainst, compiled from the same bundles. Other consumers can ignore it.\n\nResolve a document the same way as any other packaged file:\n\n```js\nimport { createRequire } from \"node:module\";\n\nconst require = createRequire(import.meta.url);\nconst specPath = require.resolve(\"@celigo/api-specs/dist/connection.yml\");\n```\n\n## The API these specs describe\n\nAll operations live under the `/v1/` prefix and authenticate with a bearer token\n(`Authorization: Bearer <token>`). Create a token in integrator.io under **Resources** >\n**API tokens**.\n\nThe API is served from four regional hosts. Use the one your account lives in:\n\n| Region | Base URL |\n| ------------ | ------------------------------ |\n| US (default) | `https://api.integrator.io` |\n| EU | `https://api.eu.integrator.io` |\n| AU | `https://api.au.integrator.io` |\n| CA | `https://api.ca.integrator.io` |\n\nAPI Management (`/v1/apim/...`) is served from the US and EU hosts only.\n\nTo browse the API interactively, use the\n[rendered reference](https://developer.celigo.com/api/api-reference): each resource has its own\npage, with a console for trying calls against your own account.\n\n## What the specs cover\n\nThe specs are prescriptive. They describe the API surface Celigo supports and intends to keep\nsupporting — not everything the server happens to tolerate. Internal endpoints, deprecated fields,\nand retired enum values are deliberately left out.\n\nThe practical consequence: a request that validates against these specs is one you can build on.\nThe reverse does not hold. The server may accept a payload no spec describes, and that payload may\nbe ignored or rejected later.\n\n## Extensions\n\nAlongside standard OpenAPI, the documents car\n=== files/dist ===\nhttps://registry.npmjs.org/@celigo/api-specs/-/api-specs-0.2.124.tgz\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_ce60b9d88dad41daac4673d8",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
There's a clear developer workflow: Celigo CLI, REST API, OpenAPI specs. I can build a simple example. Let me get more of the CLI README to understand the commands and how to build a simple example.
Also I need pricing. Let me fetch the Celigo pricing page. Let me check docs.celigo.com and celigo.com/pricing.
Let me read the rest of the CLI README, and fetch pricing. Since api.celigo.com didn't resolve but api.integrator.io does, the actual API is reachable. But I don't have credentials, so I can't actually make authenticated calls. The user said "Stay light: use the hosted product through its SDK or API." I can build an example that uses the API/CLI, and verify what I can without credentials (e.g., unauthenticated request returns 401, confirming reachability). I should not start local service stacks.
Let me get the full CLI README and check the API reachability. Also fetch pricing page.
npm view @celigo/celigo-cli readme 2>/dev/null | sed -n '80,400p'
```
```
Without a name, `profile add` asks `GET /v1/tokenInfo` which account and environment the
credential reaches, names the profile after them, and records that environment on it, as a
sign-in does. `celigo profile rename <old>` renames an existing profile the same way; nothing
renames one on its own.
You can also pass credentials via `CELIGO_API_TOKEN` / `--token`. Those always win over a
profile's OAuth session, so CI is unaffected by an interactive login on the same machine.
### Agent skills
Celigo's AI-assistant skills are maintained in the public [`celigo/ai`](https://github.com/celigo/ai) repository and installed through the official [skills CLI](https://skills.sh). The CLI does not modify agent configuration during npm installation.
Because AI agents driving the CLI are its main use case, the CLI installs the skills for you: on first use, and at most once every 24 hours after that, a detached background process checks whether `celigo/ai` has moved since the last install and, if it has, announces itself on stderr and runs the equivalent of `celigo skills install --global --all -y`. Days on which the skills are already current cost nothing and print nothing. The check and the install both happen in the background, so they never block or fail a command — if git, npm, or GitHub is unreachable the CLI installs anyway and retries no sooner than the next day. It is skipped in CI and during `celigo skills`/`celigo config` invocations. Opt out either way:
```bash
export CELIGO_NO_SKILLS_INSTALL=1 # per environment
celigo config set skills_auto_install false # persistent, machine-wide
```
To keep some skills and drop others, name the ones you do not want. Deleting their files is not enough on its own: the background install has no way to tell a deliberate deletion from a half-finished install, so it would put them back the next time `celigo/ai` changed. This setting is the durable record, and the background install then installs everything except those skills:
```bash
celigo config set skills_auto_install_exclude writing-sql,building-b2b
celigo config unset skills_auto_install_exclude # clear it; the skills return on the next check
```
Excluding a skill does not delete it — remove the files once (the names are the folders `celigo skills list` reports) and the setting keeps them from coming back. Explicit `celigo skills install` commands ignore the setting, so `--all` still means all.
Manual management remains available:
```bash
celigo skills install # choose skills and detected agents interactively
celigo skills install --global --all # install every Celigo skill for the current user
celigo skills list # list installed skills
celigo skills update building-flows # update named installed skills
celigo skills update --all-installed # explicitly update every installed skill
```
`celigo skills` delegates to a pinned release of the official `skills` package through `npx`; skill content still comes directly from `celigo/ai`. Installation and updates therefore require access to npm and GitHub. Updating every skill—including non-Celigo skills—requires the explicit `--all-installed` flag; the automatic background install only ever touches the Celigo skills from `celigo/ai`.
### Multiple accounts
```bash
celigo profile add --api-token <token> # named after its account, e.g. acme-corp-production
celigo profile list # show all profiles (marks the active one)
celigo profile use <name> # switch the active profile — machine-wide
export CELIGO_PROFILE=<name> # bind THIS shell or agent session to a profile
celigo --profile <name> flows list # one-off override for a single command
```
A command resolves its profile as `--profile` > `CELIGO_PROFILE` > the active profile. The
active profile is one machine-wide value in `~/.celigo/config.json`, read fresh by every
invocation — so `celigo profile use` in one terminal redirects every un-pinned command in every
other terminal and agent session on the machine, and does NOT redirect a session that pins a
profile with `--profile` on each call or `CELIGO_PROFILE` (the command says both on stderr). A
session that must stay on one account sets `CELIGO_PROFILE` instead. A `--profile` or
`CELIGO_PROFILE` value that names no profile is an error before any request (`Profile 'x' does
not exist. Available: …`), even when `CELIGO_API_TOKEN` is set. Every command that writes to the
account first says which one on stderr — `celigo: profile 'acme-sandbox' → api.integrator.io` —
and confirmation prompts carry the same label; stdout stays pure JSON for `--jq` and pipes.
A profile pinned AWAY from the active one — `--profile acme-sandbox` while `profile use` moved
the machine to `acme-prod` — is announced on every command that targets it, reads included, with
the divergence in the line: `celigo: profile 'acme-sandbox' → api.integrator.io (pinned with
--profile; the active profile is 'acme-prod')`. That is the shape of a session the person at the
machine believes has followed their switch and has not. To make `profile use` the only way an
account gets selected on a machine, refuse such a pin outright:
```bash
celigo config set profile_pin_policy strict # machine-wide; default is allow
```
Under `strict`, a `--profile` or `CELIGO_PROFILE` value that differs from the active profile
fails before any request (exit 1) and names both exits — drop the pin, or have a person switch
with `celigo profile use` — for the API commands and for `config`, `profile`, `login` and
`logout` alike (a pinned session may not read or rewrite the other profile, rename or delete
it, or sign it in and out either), while `profile whoami --profile <name>` still answers, since
"which account is this profile?" is the question the policy exists to make people ask, and
`profile list` names what exists. A name no profile has yet is not a pin away from one, so
`config set … --profile new` and `login --profile new` still create a profile. `profile use`
itself needs a terminal under `strict` — the switch is the person's, never a session's.
`promote --to <profile>` is a write into another account the session selected, so under
`strict` a target profile that is not the active one needs a person at a terminal who types the
profile's name back; off a terminal it is refused in the same words (never `profile use`, never
the policy). A target no profile on the machine names (`CELIGO_TARGET_API_TOKEN` behind a
committed target file, the CI shape) is allowed — that environment was set by whoever runs the
process — and `diff --target` only reads. `skills` and the offline tree verbs are unaffected.
The setting protects the person at the machine from a session they are not watching, so that
session must not be able to switch it off. Loosening it (`config set profile_pin_policy allow`
from `strict`, or `config unset profile_pin_policy`, which leaves the default `allow`) is
refused without a terminal — agents have none — and on one asks the person to
type `allow` back; tightening never asks. Where even that is too soft (an agent with a shell can
still edit `~/.celigo/config.json`), the **managed policy file** puts the setting where the
account running the CLI cannot write: `/etc/celigo/policy.json` on macOS and Linux
(`%ProgramData%\Celigo\policy.json` on Windows), created with administrator rights:
```bash
sudo mkdir -p /etc/celigo && echo '{"profile_pin_policy":"strict"}' | sudo tee /etc/celigo/policy.json
```
It is tighten-only: `strict` there forces `strict` and locks the user-level key (`config set`
and `config unset` refuse; `config get`/`show` report `managed by /etc/celigo/policy.json`), and anything else in
it changes nothing — so a file an agent can plant somewhere it *can* write is worthless to it.
The CLI never writes the file; a malformed one is reported once on stderr and ignored.
#### Rules for agents driving the CLI
An agent once carried a stale `--profile` for a day and wrote to the wrong account; the same
happened again when the person switched profiles mid-session and the agent's pin did not follow.
Another purged a production connection's queue by appending `--yes`, because the `[y/N]`
prompt's own refusal told it to — whether a person had confirmed was then the harness's call,
not the prompt's. These rules prevent both (the `getting-started` agent skill carries them too):
1. **Check the target before the first mutating command of a session**: run
`celigo profile whoami`, and read the `celigo: profile '…' → …` stderr line every write
prints. If it is not the account you mean, stop.
2. **Do not pass `--profile` unless the user asked for that profile in this request.** Work on
the active profile; a session that must stay on one account is bound once with
`export CELIGO_PROFILE=<name>` — a repeated flag is exactly how a stale value survives.
3. **`(pinned with --profile; the active profile is '…')` on a stderr line is a stop sign**: the
person switched profiles and your pin did not follow. Ask which account they mean before the
next command. Under `profile_pin_policy = strict` the command is refused instead — never run
`celigo profile use`, or change `profile_pin_policy`, to get past that; both are the person's
decision.
4. **Never run `config set api_token` in response to an error.** `Profile 'x' does not exist`
means the NAME is wrong, not the token; writing a token "fixes" it by creating the misspelled
profile or overwriting another profile's token.
5. **`celigo profile list` shows what exists** — pick from it rather than guessing a name.
6. **Never pass `--yes` to a delete, purge, cancel, or `--prune` unless the user approved that
exact command in this conversation.** Off a terminal the `[y/N]` prompt cannot ask them, so
the command is refused (exit 1) and says so. The answer is theirs: quote the command back,
wait for it, then re-run with `--yes`. `--yes` exists for scripts and CI pipelines a person
set up, not for answering on their behalf.
### Permission modes
Each profile runs in one of three modes. Default is `full`.
| Mode | Allows |
|---|---|
| `read` | GET requests, plus the POST endpoints that only read (connection ping, previews, queries) and the RDBMS metadata lookup behind `metadata types` / `metadata fields` (a PUT by verb, a catalog read by effect). Every mutating command is refused before any request — the structural verbs, `set`, and the operational actions (`flows run`, `jobs cancel`, …) at the command level, with the HTTP client's method gate behind them. |
| `operate` | Read + operational actions (run flows, retry/resolve errors, enable debug, invoke an import or AI agent, cancel jobs, etc.). Cannot create/delete resources or restructure flows. `set` takes only each group's own operational fields — `exports set debugUntil=…`, `flows set disabled=true`, `tools set input.debugUntil=…` — or refuses when the group has none (`alerts`, `network-policies`, `file-server accounts`, `storage`). |
| `full` | No gates. |
```bash
celigo profile add prod --api-token <token> --mode read
celigo config set mode operate # change the active profile's mode
```
The mode is a boundary a session must not move for itself: tightening (`full` → `operate` →
`read`) never asks, but RAISING a profile's stored mode — `config set mode operate` or `full`
from `read`, `config set mode full` from `operate`, or `config unset mode` on either, which
leaves the default `full` — needs a person at a terminal, who types the new mode back; off a
terminal it is refused, and the refusal tells an agent not to raise the mode on the person's
behalf or get past it another way. Every mode refusal says who raises the mode and where (`A
person raises this profile's mode to full at a terminal ('celigo config set mode full')`, or
`whoever set` `CELIGO_MODE` when the variable set it) instead of telling the session to switch.
`CELIGO_MODE` only ever TIGHTENS a profile's mode, never raises it: the session runs in the
stricter of the profile's stored mode and the variable (`CELIGO_MODE=read` over a `full` profile
is `read`; `CELIGO_MODE=full` over a `read` profile is still `read`), and a profile with no stored
mode — or no profile at all, as in env-only CI — keeps the `full` default, so `CELIGO_MODE=full`
there still runs in full. A `promote` target is held the same way (see "Local tree" below and
[docs/ci-setup.md](docs/ci-setup.md)). A mode that is not a mode fails CLOSED: a stored
`"mode": "readonly"` (a typo, a corrupt config) runs the profile in `read`, every command warns
once on stderr naming the profile and the value, and `profile list` shows it as invalid — a person
fixes it with `config set mode` at a terminal; `CELIGO_MODE=readonly` likewise counts as `read`
with a warning, and an empty `CELIGO_MODE` still refuses every command.
See `celigo <resource> --help` or [developer.celigo.com/cli](https://developer.celigo.com/cli) for every command and its mode.
## Global options
```
--token <token> API bearer token (overrides config/env)
--base-url <url> API base URL (default: https://api.integrator.io)
--profile <name> Run against a named profile (must exist; overrides CELIGO_PROFILE and the active one)
--format <fmt> json (default) or table
--jq <expr> Transform JSON output with a jq expression (bundled; see Install)
--verbose Log every HTTP request to stderr
-v, --version Print the CLI version (as the first argument only: celigo --version)
```
Every option but `--version` may follow the subcommand (`celigo flows list --profile sandbox`).
`--version` after a subcommand is the subcommand's own option (`connections create --connector
<id> --version v2`), or an unknown-option error where it has none.
`list` commands return lean rows by default — `_id`, `name`, and the table columns, projected
server-side where the API supports it and trimmed client-side where it doesn't. Use
`--fields all` for complete documents (`celigo config set list_fields all` makes that the
profile default) or `--fields <a,b,c>` for a custom set; the standard `list` verb also takes
`--limit <n>` to cap rows (hand-rolled lists such as `ai-agents`, `notifications`, and
`stacks` take `--fields` only). A projected row can carry a key you didn't name: `_id` and
`name` always ride along, as does any field the command filters on internally. Field
selection resolves in order — `--fields`, then `--jq` (which implies complete documents),
then `list_fields`, then the built-in default — so `--jq` on its own gets complete documents,
but `--jq` alongside an explicit `--fields` still projects, and the expression then sees
`null` for anything it wasn't given. `get` and `account snapshot` are never projected.
JSON output (and a `--jq` result) carries the server's values byte for byte. What prints for a
person — `--format table`, error messages, warnings — first strips the terminal control
sequences a name or a remote system's error could carry (cursor moves, clipboard writes,
disguised links), so a crafted value cannot take over the terminal; every other character,
in any script, prints as it is.
## Examples
```bash
# Explore your account
celigo account snapshot
celigo account search "shopify"
# Build a flow (bottom-up: connection → export → import → flow)
celigo http-connectors match https://api.github.com # the pre-built connector, its default auth
celigo connections create --connector github --name "GitHub - Prod" # scaffolds what its form saves
celigo connections create < connection.json # hand-written; refused if a connector serves its host
celigo exports create < export.json
celigo imports create < import.json
celigo flows create < flow.json
# Modify resources (prefer `set` over `update` — `set` is safe GET+modify+PUT)
celigo flows set <flowId> disabled=false
celigo exports replace-connection <exportId> <newConnectionId>
# Investigate errors
celigo flows error-summary <flowId>
celigo flows error-analysis <flowId> <exportOrImportId>
# Run and monitor
celigo flows run <flowId> -y
celigo jobs current
```
## Commands
Run `celigo --help` to list the command groups, or `celigo <resource> --help` for a group's commands, arguments, and options. The full per-resource reference lives at **[developer.celigo.com/cli](https://developer.celigo.com/cli)**.
### Documentation
In-repo guides: [command reference](docs/command-reference.md) (every group, command, and permission mode), [local tree](docs/local-tree.md) (the tree's shape plus `pull`/`status`/`diff`/`push`/`resolve`/`lint`), [examples](docs/examples.md) (common workflows), [resource graph](docs/resource-graph.md) (how the local tree's resource types reference each other), and [CI setup](docs/ci-setup.md) (running the CLI in pipelines).
### Command groups
| Group | Purpose |
|---|---|
| `pull` / `status` / `diff` / `push` / `resolve` / `lint` | The local-tree workspace: download the account as a local file tree, inspect local changes offline, diff against the live account, push local edits back, resolve refs to files, and validate documents against the platform's API schemas. |
| `promote` | Write the local tree into ANOTHER account or environment (sandbox → production, account → account): creates what the target lacks and updates what it holds with every reference remapped through a committed id map (`.celigo/targets/<name>.json`); connections, iClients, MCP OAuth providers, stacks and agents are matched by name, pinned with `--map`, or created as credential-less shells — never promoted with credentials. Plan-first with push's guards and lock; `diff --target <name>` shows what it would send. |
| `ai-agents` | Manage AI agents (LLM-powered import steps). |
| `async-helpers` | Manage async helpers (polling configs for long-running API operations). |
| `guardrails` | Manage guardrails (PII detection, content moderation, AI safety checks). |
| `on-premise-agents` | Manage on-premise agents (OPA). |
| `apis` | Manage API endpoints (builder and script modes). |
| `edi-profiles` | Manage EDI document profiles for trading partners; `create --tp-connector <id\|name>` scaffolds the profile a trading partner connector prescribes, and a hand-written profile named after a partner that has one is told so. |
| `environments` | Manage the environments on your account. |
| `file-definitions` | Manage file format definitions (CSV, EDI, fixed-width, etc.). |
| `iclients` | Manage OAuth2 iClients (app registrations for OAuth flows). |
| `lookup-caches` | Manage lookup caches (in-memory key-value stores for enrichment and deduplication). |
| `topics` | Manage Event Streams topics (durable message channels): CRUD, retained messages, publish, purge, listener offsets. |
| `tags` | Manage tags for organizing resources. |
| `tools` | Manage tools (reusable building blocks callable from flows, APIs, agents, and MCP servers). |
| `mcp-servers` | Manage MCP servers that expose tools and APIs as MCP endpoints. |
| `network-policies` | Manage network policies: named IP allow and deny lists that restrict where API tokens, MCP servers and account sign-in may connect from, once assigned. `dependencies` lists a policy's assignments; `delete --force` clears them. |
| `integrations` | Manage integrations (containers for flows, connections, and resources); an Integration App's settings form via `settings` / `refresh-settings-metadata` / `persist-settings`. |
| `flows` | Manage flows (data pipelines connecting exports to imports). |
| `connections` | Manage connections (credentials and configuration for external systems). `create` is connector-first: `--connector <id\|name>` scaffolds what the pre-built connector's form saves; a hand-written `type: http` body is refused when a connector serves its base URI (`--allow-raw-http`) or configures an auth type the connector does not offer (`--auth-type`); an `asShowing the first 20,000 characters. Full output is available in raw details.
Recorded tool call
{
"type": "tool_call",
"seq": 28,
"ts": "2026-10-20T15:49:34.163Z",
"name": "bash",
"turn": 1,
"input": {
"command": "npm view @celigo/celigo-cli readme 2>/dev/null | sed -n '80,400p'"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_b38746825e4640d99bb5d332",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 32,
"ts": "2026-10-20T15:49:34.907Z",
"turn": 1,
"output": {
"content": [
{
"text": "```\n\nWithout a name, `profile add` asks `GET /v1/tokenInfo` which account and environment the\ncredential reaches, names the profile after them, and records that environment on it, as a\nsign-in does. `celigo profile rename <old>` renames an existing profile the same way; nothing\nrenames one on its own.\n\nYou can also pass credentials via `CELIGO_API_TOKEN` / `--token`. Those always win over a\nprofile's OAuth session, so CI is unaffected by an interactive login on the same machine.\n\n### Agent skills\n\nCeligo's AI-assistant skills are maintained in the public [`celigo/ai`](https://github.com/celigo/ai) repository and installed through the official [skills CLI](https://skills.sh). The CLI does not modify agent configuration during npm installation.\n\nBecause AI agents driving the CLI are its main use case, the CLI installs the skills for you: on first use, and at most once every 24 hours after that, a detached background process checks whether `celigo/ai` has moved since the last install and, if it has, announces itself on stderr and runs the equivalent of `celigo skills install --global --all -y`. Days on which the skills are already current cost nothing and print nothing. The check and the install both happen in the background, so they never block or fail a command — if git, npm, or GitHub is unreachable the CLI installs anyway and retries no sooner than the next day. It is skipped in CI and during `celigo skills`/`celigo config` invocations. Opt out either way:\n\n```bash\nexport CELIGO_NO_SKILLS_INSTALL=1 # per environment\nceligo config set skills_auto_install false # persistent, machine-wide\n```\n\nTo keep some skills and drop others, name the ones you do not want. Deleting their files is not enough on its own: the background install has no way to tell a deliberate deletion from a half-finished install, so it would put them back the next time `celigo/ai` changed. This setting is the durable record, and the background install then installs everything except those skills:\n\n```bash\nceligo config set skills_auto_install_exclude writing-sql,building-b2b\nceligo config unset skills_auto_install_exclude # clear it; the skills return on the next check\n```\n\nExcluding a skill does not delete it — remove the files once (the names are the folders `celigo skills list` reports) and the setting keeps them from coming back. Explicit `celigo skills install` commands ignore the setting, so `--all` still means all.\n\nManual management remains available:\n\n```bash\nceligo skills install # choose skills and detected agents interactively\nceligo skills install --global --all # install every Celigo skill for the current user\nceligo skills list # list installed skills\nceligo skills update building-flows # update named installed skills\nceligo skills update --all-installed # explicitly update every installed skill\n```\n\n`celigo skills` delegates to a pinned release of the official `skills` package through `npx`; skill content still comes directly from `celigo/ai`. Installation and updates therefore require access to npm and GitHub. Updating every skill—including non-Celigo skills—requires the explicit `--all-installed` flag; the automatic background install only ever touches the Celigo skills from `celigo/ai`.\n\n### Multiple accounts\n\n```bash\nceligo profile add --api-token <token> # named after its account, e.g. acme-corp-production\nceligo profile list # show all profiles (marks the active one)\nceligo profile use <name> # switch the active profile — machine-wide\nexport CELIGO_PROFILE=<name> # bind THIS shell or agent session to a profile\nceligo --profile <name> flows list # one-off override for a single command\n```\n\nA command resolves its profile as `--profile` > `CELIGO_PROFILE` > the active profile. The\nactive profile is one machine-wide value in `~/.celigo/config.json`, read fresh by every\ninvocation — so `celigo profile use` in one terminal redirects every un-pinned command in every\nother terminal and agent session on the machine, and does NOT redirect a session that pins a\nprofile with `--profile` on each call or `CELIGO_PROFILE` (the command says both on stderr). A\nsession that must stay on one account sets `CELIGO_PROFILE` instead. A `--profile` or\n`CELIGO_PROFILE` value that names no profile is an error before any request (`Profile 'x' does\nnot exist. Available: …`), even when `CELIGO_API_TOKEN` is set. Every command that writes to the\naccount first says which one on stderr — `celigo: profile 'acme-sandbox' → api.integrator.io` —\nand confirmation prompts carry the same label; stdout stays pure JSON for `--jq` and pipes.\n\nA profile pinned AWAY from the active one — `--profile acme-sandbox` while `profile use` moved\nthe machine to `acme-prod` — is announced on every command that targets it, reads included, with\nthe divergence in the line: `celigo: profile 'acme-sandbox' → api.integrator.io (pinned with\n--profile; the active profile is 'acme-prod')`. That is the shape of a session the person at the\nmachine believes has followed their switch and has not. To make `profile use` the only way an\naccount gets selected on a machine, refuse such a pin outright:\n\n```bash\nceligo config set profile_pin_policy strict # machine-wide; default is allow\n```\n\nUnder `strict`, a `--profile` or `CELIGO_PROFILE` value that differs from the active profile\nfails before any request (exit 1) and names both exits — drop the pin, or have a person switch\nwith `celigo profile use` — for the API commands and for `config`, `profile`, `login` and\n`logout` alike (a pinned session may not read or rewrite the other profile, rename or delete\nit, or sign it in and out either), while `profile whoami --profile <name>` still answers, since\n\"which account is this profile?\" is the question the policy exists to make people ask, and\n`profile list` names what exists. A name no profile has yet is not a pin away from one, so\n`config set … --profile new` and `login --profile new` still create a profile. `profile use`\nitself needs a terminal under `strict` — the switch is the person's, never a session's.\n`promote --to <profile>` is a write into another account the session selected, so under\n`strict` a target profile that is not the active one needs a person at a terminal who types the\nprofile's name back; off a terminal it is refused in the same words (never `profile use`, never\nthe policy). A target no profile on the machine names (`CELIGO_TARGET_API_TOKEN` behind a\ncommitted target file, the CI shape) is allowed — that environment was set by whoever runs the\nprocess — and `diff --target` only reads. `skills` and the offline tree verbs are unaffected.\n\nThe setting protects the person at the machine from a session they are not watching, so that\nsession must not be able to switch it off. Loosening it (`config set profile_pin_policy allow`\nfrom `strict`, or `config unset profile_pin_policy`, which leaves the default `allow`) is\nrefused without a terminal — agents have none — and on one asks the person to\ntype `allow` back; tightening never asks. Where even that is too soft (an agent with a shell can\nstill edit `~/.celigo/config.json`), the **managed policy file** puts the setting where the\naccount running the CLI cannot write: `/etc/celigo/policy.json` on macOS and Linux\n(`%ProgramData%\\Celigo\\policy.json` on Windows), created with administrator rights:\n\n```bash\nsudo mkdir -p /etc/celigo && echo '{\"profile_pin_policy\":\"strict\"}' | sudo tee /etc/celigo/policy.json\n```\n\nIt is tighten-only: `strict` there forces `strict` and locks the user-level key (`config set`\nand `config unset` refuse; `config get`/`show` report `managed by /etc/celigo/policy.json`), and anything else in\nit changes nothing — so a file an agent can plant somewhere it *can* write is worthless to it.\nThe CLI never writes the file; a malformed one is reported once on stderr and ignored.\n\n#### Rules for agents driving the CLI\n\nAn agent once carried a stale `--profile` for a day and wrote to the wrong account; the same\nhappened again when the person switched profiles mid-session and the agent's pin did not follow.\nAnother purged a production connection's queue by appending `--yes`, because the `[y/N]`\nprompt's own refusal told it to — whether a person had confirmed was then the harness's call,\nnot the prompt's. These rules prevent both (the `getting-started` agent skill carries them too):\n\n1. **Check the target before the first mutating command of a session**: run\n `celigo profile whoami`, and read the `celigo: profile '…' → …` stderr line every write\n prints. If it is not the account you mean, stop.\n2. **Do not pass `--profile` unless the user asked for that profile in this request.** Work on\n the active profile; a session that must stay on one account is bound once with\n `export CELIGO_PROFILE=<name>` — a repeated flag is exactly how a stale value survives.\n3. **`(pinned with --profile; the active profile is '…')` on a stderr line is a stop sign**: the\n person switched profiles and your pin did not follow. Ask which account they mean before the\n next command. Under `profile_pin_policy = strict` the command is refused instead — never run\n `celigo profile use`, or change `profile_pin_policy`, to get past that; both are the person's\n decision.\n4. **Never run `config set api_token` in response to an error.** `Profile 'x' does not exist`\n means the NAME is wrong, not the token; writing a token \"fixes\" it by creating the misspelled\n profile or overwriting another profile's token.\n5. **`celigo profile list` shows what exists** — pick from it rather than guessing a name.\n6. **Never pass `--yes` to a delete, purge, cancel, or `--prune` unless the user approved that\n exact command in this conversation.** Off a terminal the `[y/N]` prompt cannot ask them, so\n the command is refused (exit 1) and says so. The answer is theirs: quote the command back,\n wait for it, then re-run with `--yes`. `--yes` exists for scripts and CI pipelines a person\n set up, not for answering on their behalf.\n\n### Permission modes\n\nEach profile runs in one of three modes. Default is `full`.\n\n| Mode | Allows |\n|---|---|\n| `read` | GET requests, plus the POST endpoints that only read (connection ping, previews, queries) and the RDBMS metadata lookup behind `metadata types` / `metadata fields` (a PUT by verb, a catalog read by effect). Every mutating command is refused before any request — the structural verbs, `set`, and the operational actions (`flows run`, `jobs cancel`, …) at the command level, with the HTTP client's method gate behind them. |\n| `operate` | Read + operational actions (run flows, retry/resolve errors, enable debug, invoke an import or AI agent, cancel jobs, etc.). Cannot create/delete resources or restructure flows. `set` takes only each group's own operational fields — `exports set debugUntil=…`, `flows set disabled=true`, `tools set input.debugUntil=…` — or refuses when the group has none (`alerts`, `network-policies`, `file-server accounts`, `storage`). |\n| `full` | No gates. |\n\n```bash\nceligo profile add prod --api-token <token> --mode read\nceligo config set mode operate # change the active profile's mode\n```\n\nThe mode is a boundary a session must not move for itself: tightening (`full` → `operate` →\n`read`) never asks, but RAISING a profile's stored mode — `config set mode operate` or `full`\nfrom `read`, `config set mode full` from `operate`, or `config unset mode` on either, which\nleaves the default `full` — needs a person at a terminal, who types the new mode back; off a\nterminal it is refused, and the refusal tells an agent not to raise the mode on the person's\nbehalf or get past it another way. Every mode refusal says who raises the mode and where (`A\nperson raises this profile's mode to full at a terminal ('celigo config set mode full')`, or\n`whoever set` `CELIGO_MODE` when the variable set it) instead of telling the session to switch.\n`CELIGO_MODE` only ever TIGHTENS a profile's mode, never raises it: the session runs in the\nstricter of the profile's stored mode and the variable (`CELIGO_MODE=read` over a `full` profile\nis `read`; `CELIGO_MODE=full` over a `read` profile is still `read`), and a profile with no stored\nmode — or no profile at all, as in env-only CI — keeps the `full` default, so `CELIGO_MODE=full`\nthere still runs in full. A `promote` target is held the same way (see \"Local tree\" below and\n[docs/ci-setup.md](docs/ci-setup.md)). A mode that is not a mode fails CLOSED: a stored\n`\"mode\": \"readonly\"` (a typo, a corrupt config) runs the profile in `read`, every command warns\nonce on stderr naming the profile and the value, and `profile list` shows it as invalid — a person\nfixes it with `config set mode` at a terminal; `CELIGO_MODE=readonly` likewise counts as `read`\nwith a warning, and an empty `CELIGO_MODE` still refuses every command.\n\nSee `celigo <resource> --help` or [developer.celigo.com/cli](https://developer.celigo.com/cli) for every command and its mode.\n\n## Global options\n\n```\n--token <token> API bearer token (overrides config/env)\n--base-url <url> API base URL (default: https://api.integrator.io)\n--profile <name> Run against a named profile (must exist; overrides CELIGO_PROFILE and the active one)\n--format <fmt> json (default) or table\n--jq <expr> Transform JSON output with a jq expression (bundled; see Install)\n--verbose Log every HTTP request to stderr\n-v, --version Print the CLI version (as the first argument only: celigo --version)\n```\n\nEvery option but `--version` may follow the subcommand (`celigo flows list --profile sandbox`).\n`--version` after a subcommand is the subcommand's own option (`connections create --connector\n<id> --version v2`), or an unknown-option error where it has none.\n\n`list` commands return lean rows by default — `_id`, `name`, and the table columns, projected\nserver-side where the API supports it and trimmed client-side where it doesn't. Use\n`--fields all` for complete documents (`celigo config set list_fields all` makes that the\nprofile default) or `--fields <a,b,c>` for a custom set; the standard `list` verb also takes\n`--limit <n>` to cap rows (hand-rolled lists such as `ai-agents`, `notifications`, and\n`stacks` take `--fields` only). A projected row can carry a key you didn't name: `_id` and\n`name` always ride along, as does any field the command filters on internally. Field\nselection resolves in order — `--fields`, then `--jq` (which implies complete documents),\nthen `list_fields`, then the built-in default — so `--jq` on its own gets complete documents,\nbut `--jq` alongside an explicit `--fields` still projects, and the expression then sees\n`null` for anything it wasn't given. `get` and `account snapshot` are never projected.\n\nJSON output (and a `--jq` result) carries the server's values byte for byte. What prints for a\nperson — `--format table`, error messages, warnings — first strips the terminal control\nsequences a name or a remote system's error could carry (cursor moves, clipboard writes,\ndisguised links), so a crafted value cannot take over the terminal; every other character,\nin any script, prints as it is.\n\n## Examples\n\n```bash\n# Explore your account\nceligo account snapshot\nceligo account search \"shopify\"\n\n# Build a flow (bottom-up: connection → export → import → flow)\nceligo http-connectors match https://api.github.com # the pre-built connector, its default auth\nceligo connections create --connector github --name \"GitHub - Prod\" # scaffolds what its form saves\nceligo connections create < connection.json # hand-written; refused if a connector serves its host\nceligo exports create < export.json\nceligo imports create < import.json\nceligo flows create < flow.json\n\n# Modify resources (prefer `set` over `update` — `set` is safe GET+modify+PUT)\nceligo flows set <flowId> disabled=false\nceligo exports replace-connection <exportId> <newConnectionId>\n\n# Investigate errors\nceligo flows error-summary <flowId>\nceligo flows error-analysis <flowId> <exportOrImportId>\n\n# Run and monitor\nceligo flows run <flowId> -y\nceligo jobs current\n```\n\n## Commands\n\nRun `celigo --help` to list the command groups, or `celigo <resource> --help` for a group's commands, arguments, and options. The full per-resource reference lives at **[developer.celigo.com/cli](https://developer.celigo.com/cli)**.\n\n### Documentation\n\nIn-repo guides: [command reference](docs/command-reference.md) (every group, command, and permission mode), [local tree](docs/local-tree.md) (the tree's shape plus `pull`/`status`/`diff`/`push`/`resolve`/`lint`), [examples](docs/examples.md) (common workflows), [resource graph](docs/resource-graph.md) (how the local tree's resource types reference each other), and [CI setup](docs/ci-setup.md) (running the CLI in pipelines).\n\n### Command groups\n\n| Group | Purpose |\n|---|---|\n| `pull` / `status` / `diff` / `push` / `resolve` / `lint` | The local-tree workspace: download the account as a local file tree, inspect local changes offline, diff against the live account, push local edits back, resolve refs to files, and validate documents against the platform's API schemas. |\n| `promote` | Write the local tree into ANOTHER account or environment (sandbox → production, account → account): creates what the target lacks and updates what it holds with every reference remapped through a committed id map (`.celigo/targets/<name>.json`); connections, iClients, MCP OAuth providers, stacks and agents are matched by name, pinned with `--map`, or created as credential-less shells — never promoted with credentials. Plan-first with push's guards and lock; `diff --target <name>` shows what it would send. |\n| `ai-agents` | Manage AI agents (LLM-powered import steps). |\n| `async-helpers` | Manage async helpers (polling configs for long-running API operations). |\n| `guardrails` | Manage guardrails (PII detection, content moderation, AI safety checks). |\n| `on-premise-agents` | Manage on-premise agents (OPA). |\n| `apis` | Manage API endpoints (builder and script modes). |\n| `edi-profiles` | Manage EDI document profiles for trading partners; `create --tp-connector <id\\|name>` scaffolds the profile a trading partner connector prescribes, and a hand-written profile named after a partner that has one is told so. |\n| `environments` | Manage the environments on your account. |\n| `file-definitions` | Manage file format definitions (CSV, EDI, fixed-width, etc.). |\n| `iclients` | Manage OAuth2 iClients (app registrations for OAuth flows). |\n| `lookup-caches` | Manage lookup caches (in-memory key-value stores for enrichment and deduplication). |\n| `topics` | Manage Event Streams topics (durable message channels): CRUD, retained messages, publish, purge, listener offsets. |\n| `tags` | Manage tags for organizing resources. |\n| `tools` | Manage tools (reusable building blocks callable from flows, APIs, agents, and MCP servers). |\n| `mcp-servers` | Manage MCP servers that expose tools and APIs as MCP endpoints. |\n| `network-policies` | Manage network policies: named IP allow and deny lists that restrict where API tokens, MCP servers and account sign-in may connect from, once assigned. `dependencies` lists a policy's assignments; `delete --force` clears them. |\n| `integrations` | Manage integrations (containers for flows, connections, and resources); an Integration App's settings form via `settings` / `refresh-settings-metadata` / `persist-settings`. |\n| `flows` | Manage flows (data pipelines connecting exports to imports). |\n| `connections` | Manage connections (credentials and configuration for external systems). `create` is connector-first: `--connector <id\\|name>` scaffolds what the pre-built connector's form saves; a hand-written `type: http` body is refused when a connector serves its base URI (`--allow-raw-http`) or configures an auth type the connector does not offer (`--auth-type`); an `as2`/`ftp` body named after a trading partner that has a connector is told so, and `--tp-connector` links it. |\n| `exports` | Manage exports (data sources that read from external systems). |\n| `imports` | Manage imports (data destinations that write to external systems). |\n| `scripts` | Manage scripts (JavaScript hooks: preSavePage, preMap, postMap, postSubmit, etc.). |\n| `stacks` | Manage stacks (server/Lambda runtimes for custom code). |\n| `workspace-users` | Manage workspace users (your team's account collaborators) and their access levels. Formerly `users`, which still works as a hidden alias. |\n| `end-users` | Manage end users (external MCP users, not your team — see `workspace-users`): list, effective access, invite, revoke. |\n| `groups` | Manage end-user access groups (bundled roles and MCP grants; add/remove members). |\n| `roles` | Manage end-user roles (named, reusable sets of MCP-server grants). |\n| `jobs` | Manage flow execution jobs (status, files, diagnostics, cancellation). |\n| `audit` | View audit logs. |\n| `subscriptions` | View subscription and usage information. |\n| `notifications` | Manage notification subscriptions (integrations, flows, connections). |\n| `alerts` | Manage proactive alerts: long-runtime thresholds that notify recipients when a flow — or any flow of an integration — runs too long; `add-recipients` / `remove-recipients` edit many alerts at once. `flows run-stats` suggests a threshold. |\n| `account` | Account-wide operations: snapshot, search, dependencies, lint, stats. |\n| `edi-transactions` | Query EDI transaction logs from the B2B Manager dashboard. |\n| `file-definition-rules` | Generate EDI file definition rules from a specification PDF (X12 or EDIFACT). |\n| `http-connectors` | Browse and manage HTTP connector definitions; `match <url\\|name>` finds the connector for an API with the auth types, default auth, global iClient and fields its connection form prescribes. |\n| `http-connector-resources` | Manage your own HTTP connector resources (the API groupings inside a custom connector). |\n| `http-connector-endpoints` | Manage your own HTTP connector endpoints (the method + URI operations under a custom connector's resources). |\n| `trading-partner-connectors` | Browse trading partner connector definitions; write verbs manage definitions. |\n| `metadata` | Query metadata via a live connection (NetSuite, Salesforce, RDBMS). |\n| `processors` | Stateless parsers & generators: convert raw CSV/XML/EDI to JSON and back — validate parsing rules and EDI files without creating resources. |\n| `templates` | Browse, preview, manage, and install integration templates. |\n| `docs` | Search Celigo's product documentation (help center, community, guides): AI-generated answers with cited source articles. Not the account's AI-agent knowledge bases. |\n| `feedback` | Send product feedback about the CLI or platform from the terminal — a one-way channel: `submit` is the only verb, and it works in every mode, including read. |\n| `connectors` | Manage connectors (installable integration applications). |\n| `state` | Manage state key-value stores. |\n| `syncs` / `sync-jobs` | Manage and run syncs (Celigo Data Ingestion replication pipelines), track usage and drift events, and inspect or cancel the jobs they produce. |\n| `datasets` | Choose and inspect the tables/objects a sync replicates, and discover what a connection offers. |\n| `recycle-bin` | Browse, restore, and purge soft-deleted resources. |\n| `storage` | Manage files and folders in Celigo Storage. |\n| `file-server` | Manage the environment's Celigo-hosted SFTP file server (enable, disable, endpoint and host keys) and its partner accounts, each rooted in a Celigo Storage folder. Passwords are prompted for without echo or read with `--password-stdin` — never flag values. |\n| `mcp-oauth-providers` | Manage MCP OAuth providers. |\n| `event-reports` | Manage event reports: list, get, create, signed URLs, cancel. |\n| `platform` | GET any public REST API path the groups above do not cover (`platform get v1/preferences`), with the profile's token and host — read-only, one request, works in every mode. Not the account's API resources (that is `apis`). |\n| `profile` / `config` / `skills` | Manage local profiles and configuration; install agent skills. Never hit the Celigo API (except `profile whoami`). |\n| `login` / `logout` | OAuth sign-in and sign-out (`profile whoami` shows the session); without `--profile`, `login` names the profile after the account and environment it reaches. Not mode-gated. |\n\nMost resources support the same six verbs — `list`, `get <id>`, `create`, `update <id>`, `set <id> key=value`, and `delete <id>`. Exceptions: `environments` excludes `delete`; `workspace-users` excludes `create` (use `invite`). Body input for `create`/`update` (and other body-taking commands like `invoke` and `lookup-caches put-data`) is JSON piped via stdin, or read with `-f, --file <path>` (`-` also means stdin). Where the body is optional (`clone`, `templates install --zip`, `invoke <id>` on AI agents and guardrails, `connections create --connector`), a file redirected to stdin is always read, and a pipe counts when its first byte arrives within a second — the rest may take as long as it needs; for a slower producer such as `curl`, pass `--file -`, which waits for the pipe to close. `set` takes dot/bracket paths (`http.relativeURI=/new`, `pageGenerators[0]._exportId=abc`), parses each value as JSON first (so `null` removes a field, `true`/`false` are booleans), and can load one field from a file with `set <id> <field>=file://<path>`. **Prefer `set` over `update`** — `update` is a full PUT replace that erases any field you omit.\n\nTwo read-only helpers exist on most resources: `<resource> dependencies <id>` (alias `used-by`) lists what depends on a record — server-computed and authoritative, so an empty result means it's safe to delete — and `<resource> audit <id>` shows its change history (who changed what, when, and from which source).\n\n### Local tree (`celigo pull` / `celigo status` / `celigo diff` / `celigo push` / `celigo promote` / `celigo resolve` / `celigo lint`)\n\n`celigo pull` downloads the account — or one integration and everything it references (`--integration <id>`) — as a local file tree shaped like the Celigo UI, where EVERY RESOURCE IS A FOLDER: the folder is named after the resource and a fixed file name inside says what it is (`exports/Get Orders/export.json`, `connections/Shopify Store/connection.json`). Placement follows OWNERSHIP: a resource of an ownable type carrying an `_integrationId` lives inside that integration's folder, mirroring its UI tabs — flows (inside flow-group subfolders when grouped), syncs, tools, and the apis, mcp-servers, and storage roots scoped to it; owned apis nest one level deeper when they belong to an API group. Everything else pools at the top level: exports, imports, scripts, connections, and lookup caches (account-global, REGISTERED to integrations like connections rather than owned), plus global apis and mcp-servers, which stand alone with their own reference folders. AI agents and guardrails get their own `ai-agents/` and `guardrails/` directories rather than sitting in `imports/` — they are `/v1/imports` rows distinguished by `adaptorType`, and the platform treats them as their own resource kind, so a flow step naming one through `_importId` links into the directory that actually holds it. Instance flows nest in their parent's `instances/` folder as the sparse `_abstractFlowId` + `overrides` documents the API returns.\n\nEvery document exists exactly ONCE; everything else is a link — a real symlink named after its target, or a `.link.json` pointer file with `--no-symlinks` (recommended for trees shared across platforms via git). Links appear inside the resource that uses something (a flow folder's `exports/`/`imports/`, an export's `connections/`/`scripts/`), and inside an integration for what it registers and for the cross-boundary tools/apis it CONSUMES — never for what it owns, whose document is already in its folder. Folder names are display names (Windows-sanitized; a short id suffix only on collision), and `.celigo/manifest.json` holds the id ↔ path ↔ hash map, so identity is the id and a resource can move without losing tracking. Re-pulls are incremental: unchanged files untouched, renames become folder moves, deletions reported (removed only with `--prune`), locally-edited files never overwritten without `--force`, and volatile fields stripped so diffs stay meaningful. Storage is PULL-ONLY — `storage/` mirrors the service's hierarchy as metadata documents, with `--storage-files` opting into the actual bytes; the `celigo storage` commands are the write path. A `.celigoignore` in the tree root (gitignore syntax, `!` re-includes) keeps chosen paths out of reach entirely: ignored resources are never written, tracked, pruned, pushed, or deleted in either direction, so adding a pattern is never destructive — useful for parking a sandbox integration or the bulk of your lookup caches before a promote.\n\n`celigo status` is offline (tree vs. manifest: modified, missing, untracked, moved, parked, unfinished — what a killed pull was renaming, untracking or adding to the tree, which the next pull finishes — and unreadable pull-generated files). `celigo diff` compares against the LIVE account, field-level, ignoring volatile fields. `celigo push` writes changes back — plan-first, so the change set prints before anything is written and `--dry-run` stops there (every guard runs when the plan is built — an unresolvable reference, a file whose folder cannot be honored, and every dependent of a refused create included — so `--dry-run` exits 1 on exactly what the real push would refuse); a new file whose untracked namesake already exists on the server — the same file pushed from another clone — is refused before its POST (`--allow-duplicate-names` overrides); updates are rejected when the server copy moved since your last pull (the refusal names the exits: `celigo diff <path>`, `celigo pull --force <path>` to take the server copy of that one file, or `celigo push --force-remote <path>` to overwrite it; a server copy already identical to yours is adopted as up to date), and `--prune` deletes resources whose files you removed — held to the same lock, refused at plan time when the server copy changed since the pull (one GET per planned delete, so `--dry-run --prune` needs a token). Requires `full` mode. A tree is bound to the ACCOUNT it was pulled from (API host and token owner, recorded in the manifest), not to a profile name: `pull`, `diff`, and `push` refuse a profile that targets another account however it was selected (`--allow-account-mismatch` overrides on pull/push), while a different profile name on the same host — or no profile at all, as in env-only CI — just works once the token owner is verified as the tree's (one `GET /v1/tokenInfo`, before anything of the account is read; when that probe fails, the commands say so and rely on the host match). A tree pulled while the probe was failing has no recorded owner and is bound only by the profile it was pulled with or under `--allow-account-mismatch`; `push` under any other profile refuses the moment the probe answers — the binding table in [docs/local-tree-workflow.md](docs/local-tree-workflow.md#what-the-account-binding-can-and-cannot-detect-today) spells out every verdict. A tree pulled as one integration never writes into another (a foreign owner — an explicit `_integrationId`, one inferred from the folder, or a new integration authored in the tree — fails at plan time unless `--allow-dependencies`, the refusal naming the exit: re-pull with `--integration <id>` or without a scope), and keeps tracking every resource it tracks until the server deletes it — \"deleted on server\" means deleted; what merely left the scope is an orphan, kept and reported, dropped only by `pull --untrack-orphans`. Integration-App documents (`_connectorId`) are pull-only: push withholds edits (`--allow-connector-resources` pushes IA flows/exports/imports; a field the app locks, which the platform's PUT keeps, fails the item and stays edited in the file), never deletes them, and never PUTs an app's `integration.json` — `--allow-connector-settings` saves the settings values edited in it through the app's connector code instead (one persistSettings call per store, each field read back; values only — any other change to the file is refused), as `celigo integrations persist-settings` does one field at a time. `celigo resolve <ref>` turns any ref — a link, a tree path, `type:id`, or a bare id — into the real document's path, offline. `celigo flows graph <ref>` (and `apis graph`, `tools graph`) renders a flow's, an API's or a tool's FlowBuilder graph from the tree, offline — the same sources, steps, branchings, merges and terminals the builder's canvas draws, each step resolved to its name, category, application and step components — as JSON, or `--as mermaid` to paste into a pull request description where GitHub renders it (`--as dot` for Graphviz; `--status` adds the tree's modified/missing state per step). `celigo pull --neutral` writes an env-neutral tree (no ids in tracked files; references become tree paths and `@bindings/` keys resolved per environment via the untracked `.celigo/bindings.json`), so one git branch per environment merges cleanly. `celigo promote --to <profile>` is the other way to run several environments from one standard tree: it writes the tree into the account `--to` names through a committed id map per target (`.celigo/targets/<name>.json`), plan-first with push's guards and lock, under the target's own token (`CELIGO_TARGET_API_TOKEN` in CI — never the source session's), and `celigo diff --target <name>` shows what it would send.\n\n`celigo lint` validates the tree's documents against the platform's published API schemas, offline, with the account outranking the specs: a shape any pristine pulled document already exhibits only warns (the platform demonstrably accepts it), a create introducing a shape nothing on the account backs errors, and masked credential placeholders on a create always block. Six logic rules then ask whether a document holds together — a hook naming a function its script lacks, a script that does not parse, a `lookupName` with no lookup, a `nextRouterId` naming no router, a Handlebars template that does not compile, an Event Streams listener or publisher whose topic binding does not hold — reported at the referencing slot (so the Flow Canvas pins each to its step), as errors in the files a change touched and as warnings on a pristine pull; `celigo push` and `celigo promote` run the same six at plan time and refuse an error (`--allow-lint-errors` sends it anyway, the error printed rather than refused). Spec-contradicted findings are recorded to the per-machine `.celigo/local/lint-feedback.jsonl` — field paths and type classes only, never values — so real accounts continuously improve the specs. `--strict` turns warnings into errors for CI. The schemas update themselves in the background from the published `@celigo/api-specs` pack (sha512-verified, cached under `~/.celigo/specs/`, with the pack version pinned into the release as the fallback); opt out with `CELIGO_NO_SPEC_UPDATE=1` or `celigo config set specs_auto_update false`.\n\nFor a team, the tree lives in git: commit `.celigo/manifest.json` (the shared lock tokens; deterministic, validated on load) and never `.celigo/local/`, pull with `--no-symlinks`, keep feature commits off the manifest, review the PR's `celigo diff --exit-code` / `push --dry-run` output, deploy from CI and commit the manifest write-back, one writer per tree at a time. **[docs/local-tree-workflow.md](docs/local-tree-workflow.md)** is that setup end to end — repository layout, branch strategy and rollback, the PR-check and deploy jobs (env-only: token + base URL, no profile), `CELIGO_PROFILE` for local sessions, and `celigo promote` for several environments (sandbox → production, account → account) with a committed id map per target. `status`/`diff` are informational by default; `--exit-code` makes them CI gates, and `push --dry-run` exits 1 when a planned item is refused (`refused: <path> — <reason>`: a half-merged file, invalid JSON, a scope violation, the responseMapping wrapper, masked credentials — every guard runs at plan time). None of these commands exist in the published 2026.8.x releases.\n\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_b38746825e4640d99bb5d332",
"is_error": false,
"tool_kind": "shell"
}echo "=== unauth API check (expect 401/403) ==="; curl -sS -m 12 -o /tmp/celigo_resp.txt -w "HTTP %{http_code}\n" https://api.integrator.io/v1/integrations 2>&1; head -c 500 /tmp/celigo_resp.txt; echo; echo "=== pricing page ==="; curl -sSL -m 15 https://www.celigo.com/pricing/ -o /tmp/celigo_pricing.html -w "HTTP %{http_code} size=%{size_download}\n" 2>&1; wc -c /tmp/celigo_pricing.html 2>/dev/null=== unauth API check (expect 401/403) ===
=== unauth API check (expect 401/403) ===
HTTP 401
{"message":"Unauthorized"}
=== pricing page ===
HTTP 200 size=181537
181537 /tmp/celigo_pricing.html
Recorded tool call
{
"type": "tool_call",
"seq": 29,
"ts": "2026-10-20T15:49:34.163Z",
"name": "bash",
"turn": 1,
"input": {
"command": "echo \"=== unauth API check (expect 401/403) ===\"; curl -sS -m 12 -o /tmp/celigo_resp.txt -w \"HTTP %{http_code}\\n\" https://api.integrator.io/v1/integrations 2>&1; head -c 500 /tmp/celigo_resp.txt; echo; echo \"=== pricing page ===\"; curl -sSL -m 15 https://www.celigo.com/pricing/ -o /tmp/celigo_pricing.html -w \"HTTP %{http_code} size=%{size_download}\\n\" 2>&1; wc -c /tmp/celigo_pricing.html 2>/dev/null"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_dfd2924a2ca5493090b07cb8",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 33,
"ts": "2026-10-20T15:49:36.462Z",
"turn": 1,
"output": {
"content": [
{
"text": "=== unauth API check (expect 401/403) ===\nHTTP 401\n{\"message\":\"Unauthorized\"}\n=== pricing page ===\nHTTP 200 size=181537\n181537 /tmp/celigo_pricing.html\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_dfd2924a2ca5493090b07cb8",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
The API is reachable (401 unauthenticated), and I have the CLI. I can build a simple example. Let me get the pricing info from the downloaded page, and then build a runnable example. I should not require credentials necessarily, but I can build an example script that uses the API and works once a token is set.
Let me extract pricing info from the HTML.
Let me also install the CLI quickly (npm install -g) — that's light and not a local service stack. Actually, installing the CLI is reasonable. But the user said "Stay light: use the hosted product through its SDK or API." Installing the CLI is fine and lightweight. Let me do it but be mindful of time. Node >= 22 required. Let me check node version.
Let me extract pricing first.
node --version; echo "=== pricing text extraction ==="; python3 - <<'PY'
import re,html
h=open('/tmp/celigo_pricing.html',encoding='utf-8',errors='ignore').read()
# strip scripts/styles
h=re.sub(r'<(script|style)[^>]*>.*?</\1>',' ',h,flags=re.S|re.I)
t=re.sub(r'<[^>]+>',' ',h)
t=html.unescape(t)
t=re.sub(r'\s+',' ',t)
# find pricing-ish snippets
for kw in ['per year','per month','annual','pricing','starts at','transactions','flows','connector']:
for m in re.finditer(kw, t, re.I):
s=max(0,m.start()-160); e=min(len(t),m.end()+160)
print('...',t[s:e],'...')
break
PYv22.23.2
v22.23.2 === pricing text extraction === ... --> Celigo Pricing – Celigo Skip to content Celigo Platform CELIGO ORA Ora is now available to all. Build, troubleshoot, and manage your entire platform through natural language. ... ... Get a demo Toggle navigation Celigo Pricing No surprise costs. No hidden fees. Scale your integrations with confidence. Flat-rate pricing Pay for endpoints and flows, not per task or transaction, so you can scale without hidden costs. No overage fees Avoid unexpected cost spikes as your usage grows during peak times, ensuri ...
Recorded tool call
{
"type": "tool_call",
"seq": 36,
"ts": "2026-10-20T15:49:39.137Z",
"name": "bash",
"turn": 1,
"input": {
"command": "node --version; echo \"=== pricing text extraction ===\"; python3 - <<'PY'\nimport re,html\nh=open('/tmp/celigo_pricing.html',encoding='utf-8',errors='ignore').read()\n# strip scripts/styles\nh=re.sub(r'<(script|style)[^>]*>.*?</\\1>',' ',h,flags=re.S|re.I)\nt=re.sub(r'<[^>]+>',' ',h)\nt=html.unescape(t)\nt=re.sub(r'\\s+',' ',t)\n# find pricing-ish snippets\nfor kw in ['per year','per month','annual','pricing','starts at','transactions','flows','connector']:\n for m in re.finditer(kw, t, re.I):\n s=max(0,m.start()-160); e=min(len(t),m.end()+160)\n print('...',t[s:e],'...')\n break\nPY"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_0789566cb64846abada99963",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 37,
"ts": "2026-10-20T15:49:39.137Z",
"turn": 1,
"output": {
"content": [
{
"text": "v22.23.2\n=== pricing text extraction ===\n... --> Celigo Pricing – Celigo Skip to content Celigo Platform CELIGO ORA Ora is now available to all. Build, troubleshoot, and manage your entire platform through natural language. ...\n... Get a demo Toggle navigation Celigo Pricing No surprise costs. No hidden fees. Scale your integrations with confidence. Flat-rate pricing Pay for endpoints and flows, not per task or transaction, so you can scale without hidden costs. No overage fees Avoid unexpected cost spikes as your usage grows during peak times, ensuri ...\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_0789566cb64846abada99963",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
The pricing page is JS-heavy, so text extraction is limited. Let me search the raw HTML for pricing numbers or JSON data. Let me grep for "$" amounts and "per". Also check if there's a separate pricing data. Let me look more carefully. Maybe there's structured data in scripts. Let me grep.
python3 - <<'PY'
import re,html
h=open('/tmp/celigo_pricing.html',encoding='utf-8',errors='ignore').read()
for m in re.finditer(r'\$[\d,]+(?:\.\d+)?(?:\s*(?:/|per|a)\s*\w+)?', h):
print(repr(m.group(0)), '::', re.sub(r'\s+',' ',html.unescape(re.sub(r'<[^>]+>',' ',h[max(0,m.start()-120):m.end()+120])))[:260])
PY(no output)
(no output)
Recorded tool call
{
"type": "tool_call",
"seq": 40,
"ts": "2026-10-20T15:49:43.567Z",
"name": "bash",
"turn": 1,
"input": {
"command": "python3 - <<'PY'\nimport re,html\nh=open('/tmp/celigo_pricing.html',encoding='utf-8',errors='ignore').read()\nfor m in re.finditer(r'\\$[\\d,]+(?:\\.\\d+)?(?:\\s*(?:/|per|a)\\s*\\w+)?', h):\n print(repr(m.group(0)), '::', re.sub(r'\\s+',' ',html.unescape(re.sub(r'<[^>]+>',' ',h[max(0,m.start()-120):m.end()+120])))[:260])\nPY"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_47b9254e96e44d45b386fa13",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 42,
"ts": "2026-10-20T15:49:43.567Z",
"turn": 1,
"output": {
"content": [
{
"text": "(no output)",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_47b9254e96e44d45b386fa13",
"is_error": false,
"tool_kind": "shell"
}python3 - <<'PY'
import re,html
h=open('/tmp/celigo_pricing.html',encoding='utf-8',errors='ignore').read()
# look for tier names
for kw in ['Starter','Professional','Enterprise','Essentials','Growth','Standard','Premium','Free','tier','plan']:
idxs=[m.start() for m in re.finditer(r'\b'+kw+r'\b',h,re.I)][:2]
for i in idxs:
seg=re.sub(r'\s+',' ',html.unescape(re.sub(r'<[^>]+>',' ',h[max(0,i-100):i+200])))
print(kw,'::',seg[:280])
PYProfessional :: Professional Services Learn about a range of implementati
Professional :: Professional Services Learn about a range of implementati
Professional :: "https://www.celigo.com/professional-services/"> Professional Services Learn about a range of implementation services to help you succeed.
Enterprise :: header-v2__featured-copy">Connect, automate, and build what matters with practical conversations on enterprise AI. Listen now <li class="celigo-header-v2__nav-item me
Enterprise :: iwyg align-center"> Our scalable platform editions support everything from essential workflows to enterprise-wide automation, delivering performance, security, and support for your business needs.
Standard :: ing for HubSpot WordPress plugin v11.3.75--> var _hsq = _hsq || []; _hsq.push(["setContentType", "standard-page"]); <!-- DO NOT COPY THIS SNIPPET! End of Page Analytics Tracking for HubSpot WordPress plugi
Standard :: t-id" data-content-id="standard-page"> var _hsq = _hsq || []; _hsq.push(["setContentType", "standard-page"]); html.lenis, html.lenis body {height: auto;}.lenis.lenis-smoot
Premium :: ew:large, max-snippet:-1, max-video-preview:-1' /> Celigo Pricing – Celigo <meta name="description" content="No surprise costs.
Premium :: zed with the Yoast SEO Premium plugin v28.7 (Yoast SEO v28.7) - https://yoast.com/product/yoast-seo-premium-wordpress/ --> Celigo Pricing – Celigo <link rel="
Free :: rator.io/signup" target="_blank" rel="noopener"> Free Trial Get hands-on with Celigo
Free :: v class="cf-cta__text"> Start your 30-day free trial Unlimited access to the Celigo platform for 30 days <a class="cf-btn cf-btn--primary-dark " href="https://integrator.io/sign
plan :: <me
plan :: > <meta property="article:modified_time" co
Recorded tool call
{
"type": "tool_call",
"seq": 41,
"ts": "2026-10-20T15:49:43.567Z",
"name": "bash",
"turn": 1,
"input": {
"command": "python3 - <<'PY'\nimport re,html\nh=open('/tmp/celigo_pricing.html',encoding='utf-8',errors='ignore').read()\n# look for tier names\nfor kw in ['Starter','Professional','Enterprise','Essentials','Growth','Standard','Premium','Free','tier','plan']:\n idxs=[m.start() for m in re.finditer(r'\\b'+kw+r'\\b',h,re.I)][:2]\n for i in idxs:\n seg=re.sub(r'\\s+',' ',html.unescape(re.sub(r'<[^>]+>',' ',h[max(0,i-100):i+200])))\n print(kw,'::',seg[:280])\nPY"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_319ffe4beab047dea5a5136e",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 43,
"ts": "2026-10-20T15:49:43.567Z",
"turn": 1,
"output": {
"content": [
{
"text": "Professional :: Professional Services Learn about a range of implementati\nProfessional :: \"https://www.celigo.com/professional-services/\"> Professional Services Learn about a range of implementation services to help you succeed. \nEnterprise :: header-v2__featured-copy\">Connect, automate, and build what matters with practical conversations on enterprise AI. Listen now <li class=\"celigo-header-v2__nav-item me\nEnterprise :: iwyg align-center\"> Our scalable platform editions support everything from essential workflows to enterprise-wide automation, delivering performance, security, and support for your business needs. \nStandard :: ing for HubSpot WordPress plugin v11.3.75--> var _hsq = _hsq || []; _hsq.push([\"setContentType\", \"standard-page\"]); <!-- DO NOT COPY THIS SNIPPET! End of Page Analytics Tracking for HubSpot WordPress plugi\nStandard :: t-id\" data-content-id=\"standard-page\"> var _hsq = _hsq || []; _hsq.push([\"setContentType\", \"standard-page\"]); html.lenis, html.lenis body {height: auto;}.lenis.lenis-smoot\nPremium :: ew:large, max-snippet:-1, max-video-preview:-1' /> Celigo Pricing – Celigo <meta name=\"description\" content=\"No surprise costs. \nPremium :: zed with the Yoast SEO Premium plugin v28.7 (Yoast SEO v28.7) - https://yoast.com/product/yoast-seo-premium-wordpress/ --> Celigo Pricing – Celigo <link rel=\"\nFree :: rator.io/signup\" target=\"_blank\" rel=\"noopener\"> Free Trial Get hands-on with Celigo \nFree :: v class=\"cf-cta__text\"> Start your 30-day free trial Unlimited access to the Celigo platform for 30 days <a class=\"cf-btn cf-btn--primary-dark \" href=\"https://integrator.io/sign\nplan :: <me\nplan :: > <meta property=\"article:modified_time\" co\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_319ffe4beab047dea5a5136e",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
The pricing page doesn't show explicit dollar amounts (likely "contact sales" / flat-rate, pay for endpoints and flows). Let me extract the full pricing page text to get the plan structure. Let me get more of the visible text. Let me look for sections like "Flat-rate pricing", "endpoints and flows". Let me dump a cleaner text and grep around "edition".
Actually, the page likely has pricing tiers. Let me extract text blocks more thoroughly. Let me get all text and search for "per year", "endpoint", "flow". The earlier extraction only found one match because I broke out of the loop. Let me print all matches.
python3 - <<'PY'
import re,html
h=open('/tmp/celigo_pricing.html',encoding='utf-8',errors='ignore').read()
h=re.sub(r'<(script|style|noscript)[^>]*>.*?</\1>',' ',h,flags=re.S|re.I)
t=html.unescape(re.sub(r'<[^>]+>',' ',h))
t=re.sub(r'\s+',' ',t)
# print region around key pricing terms, dedup
seen=set()
for kw in ['Flat-rate','endpoint','flow','edition','no overage','per task','unlimited','contact sales','quote','connector','integration app','free trial']:
for m in re.finditer(kw,t,re.I):
s=max(0,m.start()-200); e=min(len(t),m.end()+250)
seg=t[s:e]
key=seg[:80]
if key in seen: continue
seen.add(key)
print('###',kw.upper(),'###',seg,'\n')
break
PY### FLAT-RATE ### estions and share knowledge. Sign In US EU AU CA Get a demo Sign In US EU AU CA Get a demo Toggle navigation Celigo Pricing No surprise costs.
### FLAT-RATE ### estions and share knowledge. Sign In US EU AU CA Get a demo Sign In US EU AU CA Get a demo Toggle navigation Celigo Pricing No surprise costs. No hidden fees. Scale your integrations with confidence. Flat-rate pricing Pay for endpoints and flows, not per task or transaction, so you can scale without hidden costs. No overage fees Avoid unexpected cost spikes as your usage grows during peak times, ensuring consistent and manageable expenses throughout the y ### ENDPOINT ### e. Sign In US EU AU CA Get a demo Sign In US EU AU CA Get a demo Toggle navigation Celigo Pricing No surprise costs. No hidden fees. Scale your integrations with confidence. Flat-rate pricing Pay for endpoints and flows, not per task or transaction, so you can scale without hidden costs. No overage fees Avoid unexpected cost spikes as your usage grows during peak times, ensuring consistent and manageable expenses throughout the year. Flexible scaling Eas ### FLOW ### EU AU CA Get a demo Sign In US EU AU CA Get a demo Toggle navigation Celigo Pricing No surprise costs. No hidden fees. Scale your integrations with confidence. Flat-rate pricing Pay for endpoints and flows, not per task or transaction, so you can scale without hidden costs. No overage fees Avoid unexpected cost spikes as your usage grows during peak times, ensuring consistent and manageable expenses throughout the year. Flexible scaling Easily add mo ### EDITION ### business needs evolve, without pricing surprises. Usage tracking Easily oversee how much you use Celigo and its impact on spend for clear budget control and forecasting. No hidden costs Our platform editions include more than the competition, with no surprise costs when you implement or expand. Simple metrics Pricing is determined by endpoints and flows—not complex units like tasks, data volume, or vCores. Platform Editions Our scalable platform editio ### NO OVERAGE ### Pricing No surprise costs. No hidden fees. Scale your integrations with confidence. Flat-rate pricing Pay for endpoints and flows, not per task or transaction, so you can scale without hidden costs. No overage fees Avoid unexpected cost spikes as your usage grows during peak times, ensuring consistent and manageable expenses throughout the year. Flexible scaling Easily add more endpoints or flows as your business needs evolve, without pricing surprises. U ### PER TASK ### t a demo Sign In US EU AU CA Get a demo Toggle navigation Celigo Pricing No surprise costs. No hidden fees. Scale your integrations with confidence. Flat-rate pricing Pay for endpoints and flows, not per task or transaction, so you can scale without hidden costs. No overage fees Avoid unexpected cost spikes as your usage grows during peak times, ensuring consistent and manageable expenses throughout the year. Flexible scaling Easily add more endpoints or ### UNLIMITED ### velopment playground Enhanced support package with SLAs at no additional cost Enterprise Edition Drive operational efficiency across your entire business. Automate everything with no limits. Includes unlimited endpoints Maximum performance Governance, security and auditability to meet compliance requirements Includes our best support package with our fastest SLAs at no additional cost Available on every Celigo edition Celigo AI capabilities are available ### QUOTE ### tration Payout Reconciliation Customer 360° Procure to Pay Demand Planning Product Data Management DevOps Product Management Digital Marketing Product Operations Ecommerce Marketing Product Usage EDI Quote-to-Cash Employee Onboarding Returns Management Expense Management SaaS Financial Planning & Analysis Common Applications Salesforce MS Dynamics 365 Oracle NetSuite SAP Walmart Amazon Shopify Snowflake Acumatica View all applications Resources Resour ### FREE TRIAL ### rce MS Dynamics 365 Oracle NetSuite SAP Walmart Amazon Shopify Snowflake Acumatica View all applications Resources Resources Find everything you need to become an integration and automation pro. Plan Free Trial Get hands-on with Celigo Build Headless Run integrations programmatically via CLI, Platform MCP, or API. Builder’s Hub Tutorials and video demos on getting started, best practices, use cases, and latest features. Celigo Connective A friendly user co
Recorded tool call
{
"type": "tool_call",
"seq": 46,
"ts": "2026-10-20T15:49:48.645Z",
"name": "bash",
"turn": 1,
"input": {
"command": "python3 - <<'PY'\nimport re,html\nh=open('/tmp/celigo_pricing.html',encoding='utf-8',errors='ignore').read()\nh=re.sub(r'<(script|style|noscript)[^>]*>.*?</\\1>',' ',h,flags=re.S|re.I)\nt=html.unescape(re.sub(r'<[^>]+>',' ',h))\nt=re.sub(r'\\s+',' ',t)\n# print region around key pricing terms, dedup\nseen=set()\nfor kw in ['Flat-rate','endpoint','flow','edition','no overage','per task','unlimited','contact sales','quote','connector','integration app','free trial']:\n for m in re.finditer(kw,t,re.I):\n s=max(0,m.start()-200); e=min(len(t),m.end()+250)\n seg=t[s:e]\n key=seg[:80]\n if key in seen: continue\n seen.add(key)\n print('###',kw.upper(),'###',seg,'\\n')\n break\nPY"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_8277a269f909411ab7d3a9d0",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 47,
"ts": "2026-10-20T15:49:48.645Z",
"turn": 1,
"output": {
"content": [
{
"text": "### FLAT-RATE ### estions and share knowledge. Sign In US EU AU CA Get a demo Sign In US EU AU CA Get a demo Toggle navigation Celigo Pricing No surprise costs. No hidden fees. Scale your integrations with confidence. Flat-rate pricing Pay for endpoints and flows, not per task or transaction, so you can scale without hidden costs. No overage fees Avoid unexpected cost spikes as your usage grows during peak times, ensuring consistent and manageable expenses throughout the y \n\n### ENDPOINT ### e. Sign In US EU AU CA Get a demo Sign In US EU AU CA Get a demo Toggle navigation Celigo Pricing No surprise costs. No hidden fees. Scale your integrations with confidence. Flat-rate pricing Pay for endpoints and flows, not per task or transaction, so you can scale without hidden costs. No overage fees Avoid unexpected cost spikes as your usage grows during peak times, ensuring consistent and manageable expenses throughout the year. Flexible scaling Eas \n\n### FLOW ### EU AU CA Get a demo Sign In US EU AU CA Get a demo Toggle navigation Celigo Pricing No surprise costs. No hidden fees. Scale your integrations with confidence. Flat-rate pricing Pay for endpoints and flows, not per task or transaction, so you can scale without hidden costs. No overage fees Avoid unexpected cost spikes as your usage grows during peak times, ensuring consistent and manageable expenses throughout the year. Flexible scaling Easily add mo \n\n### EDITION ### business needs evolve, without pricing surprises. Usage tracking Easily oversee how much you use Celigo and its impact on spend for clear budget control and forecasting. No hidden costs Our platform editions include more than the competition, with no surprise costs when you implement or expand. Simple metrics Pricing is determined by endpoints and flows—not complex units like tasks, data volume, or vCores. Platform Editions Our scalable platform editio \n\n### NO OVERAGE ### Pricing No surprise costs. No hidden fees. Scale your integrations with confidence. Flat-rate pricing Pay for endpoints and flows, not per task or transaction, so you can scale without hidden costs. No overage fees Avoid unexpected cost spikes as your usage grows during peak times, ensuring consistent and manageable expenses throughout the year. Flexible scaling Easily add more endpoints or flows as your business needs evolve, without pricing surprises. U \n\n### PER TASK ### t a demo Sign In US EU AU CA Get a demo Toggle navigation Celigo Pricing No surprise costs. No hidden fees. Scale your integrations with confidence. Flat-rate pricing Pay for endpoints and flows, not per task or transaction, so you can scale without hidden costs. No overage fees Avoid unexpected cost spikes as your usage grows during peak times, ensuring consistent and manageable expenses throughout the year. Flexible scaling Easily add more endpoints or \n\n### UNLIMITED ### velopment playground Enhanced support package with SLAs at no additional cost Enterprise Edition Drive operational efficiency across your entire business. Automate everything with no limits. Includes unlimited endpoints Maximum performance Governance, security and auditability to meet compliance requirements Includes our best support package with our fastest SLAs at no additional cost Available on every Celigo edition Celigo AI capabilities are available \n\n### QUOTE ### tration Payout Reconciliation Customer 360° Procure to Pay Demand Planning Product Data Management DevOps Product Management Digital Marketing Product Operations Ecommerce Marketing Product Usage EDI Quote-to-Cash Employee Onboarding Returns Management Expense Management SaaS Financial Planning & Analysis Common Applications Salesforce MS Dynamics 365 Oracle NetSuite SAP Walmart Amazon Shopify Snowflake Acumatica View all applications Resources Resour \n\n### FREE TRIAL ### rce MS Dynamics 365 Oracle NetSuite SAP Walmart Amazon Shopify Snowflake Acumatica View all applications Resources Resources Find everything you need to become an integration and automation pro. Plan Free Trial Get hands-on with Celigo Build Headless Run integrations programmatically via CLI, Platform MCP, or API. Builder’s Hub Tutorials and video demos on getting started, best practices, use cases, and latest features. Celigo Connective A friendly user co \n\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_8277a269f909411ab7d3a9d0",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
Let me get the full pricing "Platform Editions" section and the edition names/features. Let me extract the section around "Platform Editions".
python3 - <<'PY'
import re,html
h=open('/tmp/celigo_pricing.html',encoding='utf-8',errors='ignore').read()
h=re.sub(r'<(script|style|noscript)[^>]*>.*?</\1>',' ',h,flags=re.S|re.I)
t=html.unescape(re.sub(r'<[^>]+>',' ',h))
t=re.sub(r'\s+',' ',t)
i=t.find('Platform Editions')
print(t[i:i+2200])
PYPlatform Editions Our scalable platform editions support everything from essential workflows to enterprise-wide automation, delivering performance, security, an
Platform Editions Our scalable platform editions support everything from essential workflows to enterprise-wide automation, delivering performance, security, and support for your business needs. Standard Edition Start with your most critical integrations. Solid performance to match app API performance limits MFA and role-based access controls with option to add SSO Support package with standard SLAs at no additional cost Professional Edition Manage integrations across departments. Advanced capabilities such as API Builder and Lookup Cache SSO and role-based access controls with auditability Includes a sandbox for a development playground Enhanced support package with SLAs at no additional cost Enterprise Edition Drive operational efficiency across your entire business. Automate everything with no limits. Includes unlimited endpoints Maximum performance Governance, security and auditability to meet compliance requirements Includes our best support package with our fastest SLAs at no additional cost Available on every Celigo edition Celigo AI capabilities are available across all platform editions Experiment and build with Celigo Ora, now in beta Build and deploy agents with Agent Builder Connect any AI tool in your stack with MCP Server Use the LLM you already trust Learn more Frequently asked questions How does Celigo pricing work? + − Celigo charges based on endpoints and flows, so you can scale integrations without worrying about task-based overages. What happens if I need more endpoints or flows? + − You can easily upgrade your plan or add more endpoints and flows as needed by connecting with your Account Manager. Can I change my plan as my business grows? + − Yes! Celigo offers flexible pricing so you can scale up or down anytime according to the needs of your business. What types of APIs can be managed? + − Celigo’s API management can handle all types of APIs, including RESTful, SOAP, GraphQL, and WebSocket APIs. It also supports various communication protocols and data formats like JSON and XML. Do you offer volume-based pricing? + − No. Celigo offers simple, flat-rate pricing based on endpoints and flows, eliminating per-task or transaction volume charges
Recorded tool call
{
"type": "tool_call",
"seq": 50,
"ts": "2026-10-20T15:49:50.162Z",
"name": "bash",
"turn": 1,
"input": {
"command": "python3 - <<'PY'\nimport re,html\nh=open('/tmp/celigo_pricing.html',encoding='utf-8',errors='ignore').read()\nh=re.sub(r'<(script|style|noscript)[^>]*>.*?</\\1>',' ',h,flags=re.S|re.I)\nt=html.unescape(re.sub(r'<[^>]+>',' ',h))\nt=re.sub(r'\\s+',' ',t)\ni=t.find('Platform Editions')\nprint(t[i:i+2200])\nPY"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_adc6ab38588047b78b788ec5",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 51,
"ts": "2026-10-20T15:49:50.469Z",
"turn": 1,
"output": {
"content": [
{
"text": "Platform Editions Our scalable platform editions support everything from essential workflows to enterprise-wide automation, delivering performance, security, and support for your business needs. Standard Edition Start with your most critical integrations. Solid performance to match app API performance limits MFA and role-based access controls with option to add SSO Support package with standard SLAs at no additional cost Professional Edition Manage integrations across departments. Advanced capabilities such as API Builder and Lookup Cache SSO and role-based access controls with auditability Includes a sandbox for a development playground Enhanced support package with SLAs at no additional cost Enterprise Edition Drive operational efficiency across your entire business. Automate everything with no limits. Includes unlimited endpoints Maximum performance Governance, security and auditability to meet compliance requirements Includes our best support package with our fastest SLAs at no additional cost Available on every Celigo edition Celigo AI capabilities are available across all platform editions Experiment and build with Celigo Ora, now in beta Build and deploy agents with Agent Builder Connect any AI tool in your stack with MCP Server Use the LLM you already trust Learn more Frequently asked questions How does Celigo pricing work? + − Celigo charges based on endpoints and flows, so you can scale integrations without worrying about task-based overages. What happens if I need more endpoints or flows? + − You can easily upgrade your plan or add more endpoints and flows as needed by connecting with your Account Manager. Can I change my plan as my business grows? + − Yes! Celigo offers flexible pricing so you can scale up or down anytime according to the needs of your business. What types of APIs can be managed? + − Celigo’s API management can handle all types of APIs, including RESTful, SOAP, GraphQL, and WebSocket APIs. It also supports various communication protocols and data formats like JSON and XML. Do you offer volume-based pricing? + − No. Celigo offers simple, flat-rate pricing based on endpoints and flows, eliminating per-task or transaction volume charges\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_adc6ab38588047b78b788ec5",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
I have solid pricing info. No dollar amounts published — it's quote-based ("contact your Account Manager"). Three editions: Standard, Professional, Enterprise. Pricing based on endpoints and flows, flat-rate, no overage/task charges. 30-day free trial.
Now let me build a simple example. I should use the CLI or API. Since I have no credentials, I'll build an example that:
- Uses the Celigo REST API (hosted product) via a small Node or Python script — a "simple example."
- Optionally demonstrates the CLI.
The user wants me to "build a simple example using Celigo." I'll create a small script that uses the hosted API to list integrations, and maybe create a connection/flow. But without a token I can't fully test. I should be honest about that.
Let me check the OpenAPI specs to build an accurate example. Let me install @celigo/api-specs (small) to get the exact shapes, or fetch from GitHub. Actually, let me install the api-specs package locally (light) and inspect integration/connection/flow specs. That will make the example accurate.
Let me also install the CLI to verify it runs (light). Let me do both.
Actually, the user said stay light and don't start local service stacks or long-running commands. Installing npm packages is fine. Let me install the api-specs into a temp dir and inspect. Let me also install the CLI globally.
Let me do these in parallel? They both use npm. Let me just run them.
Let me first check if @celigo/api-specs installs quickly and inspect the connection/flow spec. And install CLI.
cd /tmp && rm -rf celigo-ex && mkdir celigo-ex && cd celigo-ex && npm init -y >/dev/null 2>&1 && timeout 120 npm install @celigo/api-specs@latest --no-audit --no-fund 2>&1 | tail -5; echo "exit=$?"; ls node_modules/@celigo/api-specs/dist/ 2>/dev/null | head -40
added 1 package in 27s
added 1 package in 27s exit=0 account.yml agent.yml ai-agent.yml alert.yml api.yml apim.yml asynchelper.yml audit.yml bartendertemplate.yml connection.yml connector.yml ediprofile.yml editransaction.yml enduser.yml environment.yml eventreport.yml export.yml filedefinition.yml filedefinitionrules.yml fileserver.yml filestorage.yml flow.yml guardrail.yml httpconnector.yml httpconnectorendpoint.yml httpconnectorresource.yml iclient.yml import.yml integration.yml job.yml lookupcache.yml marketplace.yml mcp-oauth-provider.yml mcp-server.yml networkpolicy.yml notification.yml processor.yml profile.yml recyclebin.yml script.yml
Recorded tool call
{
"type": "tool_call",
"seq": 54,
"ts": "2026-10-20T15:50:06.221Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /tmp && rm -rf celigo-ex && mkdir celigo-ex && cd celigo-ex && npm init -y >/dev/null 2>&1 && timeout 120 npm install @celigo/api-specs@latest --no-audit --no-fund 2>&1 | tail -5; echo \"exit=$?\"; ls node_modules/@celigo/api-specs/dist/ 2>/dev/null | head -40"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_43ab20083967487cb9972cfb",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 57,
"ts": "2026-10-20T15:50:33.743Z",
"turn": 1,
"output": {
"content": [
{
"text": "\nadded 1 package in 27s\nexit=0\naccount.yml\nagent.yml\nai-agent.yml\nalert.yml\napi.yml\napim.yml\nasynchelper.yml\naudit.yml\nbartendertemplate.yml\nconnection.yml\nconnector.yml\nediprofile.yml\neditransaction.yml\nenduser.yml\nenvironment.yml\neventreport.yml\nexport.yml\nfiledefinition.yml\nfiledefinitionrules.yml\nfileserver.yml\nfilestorage.yml\nflow.yml\nguardrail.yml\nhttpconnector.yml\nhttpconnectorendpoint.yml\nhttpconnectorresource.yml\niclient.yml\nimport.yml\nintegration.yml\njob.yml\nlookupcache.yml\nmarketplace.yml\nmcp-oauth-provider.yml\nmcp-server.yml\nnetworkpolicy.yml\nnotification.yml\nprocessor.yml\nprofile.yml\nrecyclebin.yml\nscript.yml\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_43ab20083967487cb9972cfb",
"is_error": false,
"tool_kind": "shell"
}timeout 180 npm install -g @celigo/celigo-cli@latest --allow-scripts=node-jq --no-audit --no-fund 2>&1 | tail -8; echo "exit=$?"; which celigo && celigo --version 2>&1 | head -3
added 25 packages in 22s
added 25 packages in 22s exit=0 /usr/local/bin/celigo 2026.10.1
Recorded tool call
{
"type": "tool_call",
"seq": 55,
"ts": "2026-10-20T15:50:06.221Z",
"name": "bash",
"turn": 1,
"input": {
"command": "timeout 180 npm install -g @celigo/celigo-cli@latest --allow-scripts=node-jq --no-audit --no-fund 2>&1 | tail -8; echo \"exit=$?\"; which celigo && celigo --version 2>&1 | head -3"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_df3155c9e5b94b49991bed81",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 56,
"ts": "2026-10-20T15:50:30.080Z",
"turn": 1,
"output": {
"content": [
{
"text": "\nadded 25 packages in 22s\nexit=0\n/usr/local/bin/celigo\n2026.10.1\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_df3155c9e5b94b49991bed81",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
Both installed. Now let me build the example. Let me inspect the integration/connection/flow specs to get correct field names and required fields. Let me look at the integration.yml create schema, connection.yml, flow.yml.
Let me extract the key parts. I'll use python to parse YAML or just grep. Let me look at the integration spec.
cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && echo "=== integration.yml paths ===" && grep -nE '^ /|operationId:|required:' integration.yml | head -60 && echo "=== integration schema props ===" && python3 - <<'PY'
import yaml
d=yaml.safe_load(open('integration.yml'))
# find create request schema
print("servers:", d.get('servers'))
print("paths:", list(d.get('paths',{}).keys())[:10])
PY=== integration.yml paths ===
=== integration.yml paths ===
40: /v1/integrations:
45: operationId: listIntegrations
53: required: false
108: operationId: createIntegration
112: required: true
158: /v1/integrations/{_id}:
162: operationId: getIntegration
169: required: true
212: operationId: updateIntegration
223: required: true
232: required: true
297: operationId: patchIntegration
303: required: true
312: required: true
337: operationId: deleteIntegration
346: required: true
363: /v1/integrations/{_id}/clone:
370: operationId: cloneIntegration
377: required: true
385: required: true
419: /v1/integrations/{_id}/clone/preview:
428: operationId: previewCloneIntegration
435: required: true
502: /v1/integrations/{_id}/revisions:
505: operationId: listIntegrationRevisions
515: required: true
553: /v1/integrations/{_id}/revisions/{_revisionId}:
556: operationId: getIntegrationRevision
567: required: true
577: required: true
612: /v1/integrations/{_id}/revisions/create:
615: operationId: createIntegrationRevision
627: required: true
636: required: true
675: /v1/integrations/{_id}/revisions/{_revisionId}/diff:
678: operationId: getIntegrationRevisionDiff
694: required: true
704: required: true
753: /v1/integrations/{_id}/revisions/{_revisionId}/errors:
756: operationId: listIntegrationRevisionErrors
775: required: true
785: required: true
826: /v1/integrations/{_id}/errors:
829: operationId: listIntegrationErrors
842: required: true
876: /v1/integrations/{_id}/errors/assign:
879: operationId: assignIntegrationErrors
894: required: false
906: required: true
915: required: true
973: /v1/integrations/{_id}/clonefamily:
976: operationId: listIntegrationCloneFamily
991: required: true
1002: required: true
1064: /v1/integrations/{_id}/flows:
1067: operationId: listIntegrationFlows
1077: required: true
1087: required: false
1096: required: false
1135: /v1/integrations/{_id}/imports:
=== integration schema props ===
servers: [{'url': 'https://api.integrator.io', 'description': 'Production (US / default region)'}, {'url': 'https://api.eu.integrator.io', 'description': 'Production (EU region)'}, {'url': 'https://api.au.integrator.io', 'description': 'Production (AU region)'}, {'url': 'https://api.ca.integrator.io', 'description': 'Production (CA region)'}]
paths: ['/v1/integrations', '/v1/integrations/{_id}', '/v1/integrations/{_id}/clone', '/v1/integrations/{_id}/clone/preview', '/v1/integrations/{_id}/revisions', '/v1/integrations/{_id}/revisions/{_revisionId}', '/v1/integrations/{_id}/revisions/create', '/v1/integrations/{_id}/revisions/{_revisionId}/diff', '/v1/integrations/{_id}/revisions/{_revisionId}/errors', '/v1/integrations/{_id}/errors']
Recorded tool call
{
"type": "tool_call",
"seq": 60,
"ts": "2026-10-20T15:50:53.415Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && echo \"=== integration.yml paths ===\" && grep -nE '^ /|operationId:|required:' integration.yml | head -60 && echo \"=== integration schema props ===\" && python3 - <<'PY'\nimport yaml\nd=yaml.safe_load(open('integration.yml'))\n# find create request schema\nprint(\"servers:\", d.get('servers'))\nprint(\"paths:\", list(d.get('paths',{}).keys())[:10])\nPY"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_200bf053794e403596af4d3b",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 63,
"ts": "2026-10-20T15:50:53.804Z",
"turn": 1,
"output": {
"content": [
{
"text": "=== integration.yml paths ===\n40: /v1/integrations:\n45: operationId: listIntegrations\n53: required: false\n108: operationId: createIntegration\n112: required: true\n158: /v1/integrations/{_id}:\n162: operationId: getIntegration\n169: required: true\n212: operationId: updateIntegration\n223: required: true\n232: required: true\n297: operationId: patchIntegration\n303: required: true\n312: required: true\n337: operationId: deleteIntegration\n346: required: true\n363: /v1/integrations/{_id}/clone:\n370: operationId: cloneIntegration\n377: required: true\n385: required: true\n419: /v1/integrations/{_id}/clone/preview:\n428: operationId: previewCloneIntegration\n435: required: true\n502: /v1/integrations/{_id}/revisions:\n505: operationId: listIntegrationRevisions\n515: required: true\n553: /v1/integrations/{_id}/revisions/{_revisionId}:\n556: operationId: getIntegrationRevision\n567: required: true\n577: required: true\n612: /v1/integrations/{_id}/revisions/create:\n615: operationId: createIntegrationRevision\n627: required: true\n636: required: true\n675: /v1/integrations/{_id}/revisions/{_revisionId}/diff:\n678: operationId: getIntegrationRevisionDiff\n694: required: true\n704: required: true\n753: /v1/integrations/{_id}/revisions/{_revisionId}/errors:\n756: operationId: listIntegrationRevisionErrors\n775: required: true\n785: required: true\n826: /v1/integrations/{_id}/errors:\n829: operationId: listIntegrationErrors\n842: required: true\n876: /v1/integrations/{_id}/errors/assign:\n879: operationId: assignIntegrationErrors\n894: required: false\n906: required: true\n915: required: true\n973: /v1/integrations/{_id}/clonefamily:\n976: operationId: listIntegrationCloneFamily\n991: required: true\n1002: required: true\n1064: /v1/integrations/{_id}/flows:\n1067: operationId: listIntegrationFlows\n1077: required: true\n1087: required: false\n1096: required: false\n1135: /v1/integrations/{_id}/imports:\n=== integration schema props ===\nservers: [{'url': 'https://api.integrator.io', 'description': 'Production (US / default region)'}, {'url': 'https://api.eu.integrator.io', 'description': 'Production (EU region)'}, {'url': 'https://api.au.integrator.io', 'description': 'Production (AU region)'}, {'url': 'https://api.ca.integrator.io', 'description': 'Production (CA region)'}]\npaths: ['/v1/integrations', '/v1/integrations/{_id}', '/v1/integrations/{_id}/clone', '/v1/integrations/{_id}/clone/preview', '/v1/integrations/{_id}/revisions', '/v1/integrations/{_id}/revisions/{_revisionId}', '/v1/integrations/{_id}/revisions/create', '/v1/integrations/{_id}/revisions/{_revisionId}/diff', '/v1/integrations/{_id}/revisions/{_revisionId}/errors', '/v1/integrations/{_id}/errors']\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_200bf053794e403596af4d3b",
"is_error": false,
"tool_kind": "shell"
}cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && echo "=== connection paths ===" && grep -nE '^ /|operationId:' connection.yml | head -40 && echo "=== flow paths ===" && grep -nE '^ /|operationId:' flow.yml | head -40 && echo "=== export paths ===" && grep -nE '^ /|operationId:' export.yml | head -20
=== connection paths ===
=== connection paths ===
47: /v1/connections:
54: operationId: listConnections
164: operationId: createConnection
724: /v1/connections/{_id}:
731: operationId: getConnectionById
841: operationId: updateConnection
1025: operationId: patchConnection
1129: operationId: deleteConnection
1157: /v1/connections/{_id}/ping:
1171: operationId: pingConnectionByGet
1253: operationId: pingConnection
1357: /v1/connections/{_id}/proxy:
1395: operationId: proxyConnectionRequest
1553: /v1/connections/{_id}/import:
1556: operationId: invokeVirtualImport
1753: /v1/connections/{_id}/export:
1756: operationId: invokeVirtualExport
1896: /v1/integrations/{_integrationId}/flows/{_flowId}/connections/{_connectionId}/import:
1899: operationId: invokeVirtualImportForFlow
2074: /v1/integrations/{_integrationId}/tools/{_toolId}/connections/{_connectionId}/import:
2077: operationId: invokeVirtualImportForTool
2252: /v1/integrations/{_integrationId}/apis/{_apiId}/connections/{_connectionId}/import:
2255: operationId: invokeVirtualImportForApi
2430: /v1/connections/{_id}/debug:
2433: operationId: getConnectionDebugLogs
2487: operationId: clearConnectionDebugLogs
2518: /v1/connections/{_id}/debug/{typeId}:
2521: operationId: getConnectionDebugLogsForResource
2608: /v1/connections/{_id}/metadata:
2611: operationId: queryConnectionMetadata
2829: /v1/connections/{_id}/purgeMessages:
2832: operationId: purgeConnectionMessages
2892: /v1/connection/{_id}/oauth2:
2895: operationId: getConnectionOAuth2AuthorizationUrl
2968: /v1/connections/{_id}/dependencies:
2971: operationId: listConnectionDependencies
3027: /v1/connections/{_id}/distributedApps:
3040: operationId: getConnectionDistributedApps
3126: /v1/connections/{_id}/distributed:
3141: operationId: pingConnectionDistributedAdaptor
=== flow paths ===
42: /v1/flows:
47: operationId: listFlows
203: operationId: createFlow
264: /v1/flows/{_id}:
277: operationId: getFlowById
343: operationId: updateFlow
444: operationId: patchFlow
502: operationId: deleteFlow
529: /v1/flows/{_id}/clone:
540: operationId: cloneFlow
606: /v1/flows/{_id}/clone/preview:
617: operationId: previewCloneFlow
666: /v1/flows/{_id}/descendants:
669: operationId: getFlowDescendants
714: /v1/flows/{_id}/errors:
717: operationId: listFlowErrors
762: /v1/flows/{_id}/replaceConnection:
765: operationId: replaceConnectionOnFlow
812: /v1/flows/{_id}/run:
815: operationId: runFlow
911: /v1/flows/{_flowId}/runs/stats:
914: operationId: getFlowRuntimeChartStats
1015: /v1/flows/{_id}/traceKey:
1018: operationId: getFlowTraceKey
1070: /v1/flows/{_id}/lastExportDateTime:
1073: operationId: getFlowLastExportDateTime
1117: /v1/flows/updateFlowGrouping:
1120: operationId: updateFlowGrouping
1194: /v1/flows/{_id}/{_stepId}/errors:
1197: operationId: listFlowStepErrors
1317: /v1/flows/{_id}/{_stepId}/errors/assign:
1320: operationId: assignFlowStepErrors
1379: /v1/flows/{_id}/{_stepId}/errors/unassign:
1382: operationId: unassignFlowStepErrors
1438: /v1/flows/{_id}/{_stepId}/errors/signedURL:
1441: operationId: getFlowStepErrorsSignedURL
1494: /v1/flows/{_id}/{_stepId}/resolved:
1497: operationId: listFlowStepResolvedErrors
1652: operationId: resolveFlowStepErrors
1723: operationId: deleteFlowStepResolvedErrors
=== export paths ===
29: /v1/exports:
36: operationId: listExports
95: operationId: createExport
167: /v1/exports/{_id}:
173: operationId: getExportById
219: operationId: updateExport
292: operationId: patchExport
335: operationId: deleteExport
358: /v1/exports/{_id}/clone:
365: operationId: cloneExport
412: /v1/exports/{_id}/replaceConnection:
419: operationId: replaceConnectionOnExport
462: /v1/exports/preview:
465: operationId: previewExport
563: /v1/integrations/{_integrationId}/flows/{_flowId}/exports/preview:
571: operationId: previewExportData
612: /v1/exports/{_id}/invoke:
615: operationId: invokeExport
728: /v1/exports/{_id}/dependencies:
731: operationId: listExportDependencies
Recorded tool call
{
"type": "tool_call",
"seq": 61,
"ts": "2026-10-20T15:50:53.415Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && echo \"=== connection paths ===\" && grep -nE '^ /|operationId:' connection.yml | head -40 && echo \"=== flow paths ===\" && grep -nE '^ /|operationId:' flow.yml | head -40 && echo \"=== export paths ===\" && grep -nE '^ /|operationId:' export.yml | head -20"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_832fd43096a644f5951b0f3c",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 62,
"ts": "2026-10-20T15:50:53.415Z",
"turn": 1,
"output": {
"content": [
{
"text": "=== connection paths ===\n47: /v1/connections:\n54: operationId: listConnections\n164: operationId: createConnection\n724: /v1/connections/{_id}:\n731: operationId: getConnectionById\n841: operationId: updateConnection\n1025: operationId: patchConnection\n1129: operationId: deleteConnection\n1157: /v1/connections/{_id}/ping:\n1171: operationId: pingConnectionByGet\n1253: operationId: pingConnection\n1357: /v1/connections/{_id}/proxy:\n1395: operationId: proxyConnectionRequest\n1553: /v1/connections/{_id}/import:\n1556: operationId: invokeVirtualImport\n1753: /v1/connections/{_id}/export:\n1756: operationId: invokeVirtualExport\n1896: /v1/integrations/{_integrationId}/flows/{_flowId}/connections/{_connectionId}/import:\n1899: operationId: invokeVirtualImportForFlow\n2074: /v1/integrations/{_integrationId}/tools/{_toolId}/connections/{_connectionId}/import:\n2077: operationId: invokeVirtualImportForTool\n2252: /v1/integrations/{_integrationId}/apis/{_apiId}/connections/{_connectionId}/import:\n2255: operationId: invokeVirtualImportForApi\n2430: /v1/connections/{_id}/debug:\n2433: operationId: getConnectionDebugLogs\n2487: operationId: clearConnectionDebugLogs\n2518: /v1/connections/{_id}/debug/{typeId}:\n2521: operationId: getConnectionDebugLogsForResource\n2608: /v1/connections/{_id}/metadata:\n2611: operationId: queryConnectionMetadata\n2829: /v1/connections/{_id}/purgeMessages:\n2832: operationId: purgeConnectionMessages\n2892: /v1/connection/{_id}/oauth2:\n2895: operationId: getConnectionOAuth2AuthorizationUrl\n2968: /v1/connections/{_id}/dependencies:\n2971: operationId: listConnectionDependencies\n3027: /v1/connections/{_id}/distributedApps:\n3040: operationId: getConnectionDistributedApps\n3126: /v1/connections/{_id}/distributed:\n3141: operationId: pingConnectionDistributedAdaptor\n=== flow paths ===\n42: /v1/flows:\n47: operationId: listFlows\n203: operationId: createFlow\n264: /v1/flows/{_id}:\n277: operationId: getFlowById\n343: operationId: updateFlow\n444: operationId: patchFlow\n502: operationId: deleteFlow\n529: /v1/flows/{_id}/clone:\n540: operationId: cloneFlow\n606: /v1/flows/{_id}/clone/preview:\n617: operationId: previewCloneFlow\n666: /v1/flows/{_id}/descendants:\n669: operationId: getFlowDescendants\n714: /v1/flows/{_id}/errors:\n717: operationId: listFlowErrors\n762: /v1/flows/{_id}/replaceConnection:\n765: operationId: replaceConnectionOnFlow\n812: /v1/flows/{_id}/run:\n815: operationId: runFlow\n911: /v1/flows/{_flowId}/runs/stats:\n914: operationId: getFlowRuntimeChartStats\n1015: /v1/flows/{_id}/traceKey:\n1018: operationId: getFlowTraceKey\n1070: /v1/flows/{_id}/lastExportDateTime:\n1073: operationId: getFlowLastExportDateTime\n1117: /v1/flows/updateFlowGrouping:\n1120: operationId: updateFlowGrouping\n1194: /v1/flows/{_id}/{_stepId}/errors:\n1197: operationId: listFlowStepErrors\n1317: /v1/flows/{_id}/{_stepId}/errors/assign:\n1320: operationId: assignFlowStepErrors\n1379: /v1/flows/{_id}/{_stepId}/errors/unassign:\n1382: operationId: unassignFlowStepErrors\n1438: /v1/flows/{_id}/{_stepId}/errors/signedURL:\n1441: operationId: getFlowStepErrorsSignedURL\n1494: /v1/flows/{_id}/{_stepId}/resolved:\n1497: operationId: listFlowStepResolvedErrors\n1652: operationId: resolveFlowStepErrors\n1723: operationId: deleteFlowStepResolvedErrors\n=== export paths ===\n29: /v1/exports:\n36: operationId: listExports\n95: operationId: createExport\n167: /v1/exports/{_id}:\n173: operationId: getExportById\n219: operationId: updateExport\n292: operationId: patchExport\n335: operationId: deleteExport\n358: /v1/exports/{_id}/clone:\n365: operationId: cloneExport\n412: /v1/exports/{_id}/replaceConnection:\n419: operationId: replaceConnectionOnExport\n462: /v1/exports/preview:\n465: operationId: previewExport\n563: /v1/integrations/{_integrationId}/flows/{_flowId}/exports/preview:\n571: operationId: previewExportData\n612: /v1/exports/{_id}/invoke:\n615: operationId: invokeExport\n728: /v1/exports/{_id}/dependencies:\n731: operationId: listExportDependencies\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_832fd43096a644f5951b0f3c",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
Let me get the actual request body schemas for createIntegration, createFlow, and createConnection so my example is accurate. Let me extract the requestBody schema for these operations.
Let me write a Python script to pull the request body schemas for create operations and print required fields and top-level properties.
cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && python3 - <<'PY'
import yaml,json
def resolve(d,ref):
if not isinstance(ref,str) or not ref.startswith('#/'): return ref
cur=d
for p in ref[2:].split('/'):
cur=cur[p]
return cur
for fn,op in [('integration.yml','createIntegration'),('connection.yml','createConnection'),('flow.yml','createFlow')]:
d=yaml.safe_load(open(fn))
for path,methods in d['paths'].items():
for verb,o in methods.items():
if isinstance(o,dict) and o.get('operationId')==op:
print('='*70)
print(fn,verb.upper(),path)
rb=o.get('requestBody',{})
for mt,media in rb.get('content',{}).items():
sch=media.get('schema',{})
if '$ref' in sch: sch=resolve(d,sch['$ref'])
print('media:',mt)
print('required:',sch.get('required'))
props=sch.get('properties',{})
for k,v in props.items():
t=v.get('type') or ('$ref:'+v['$ref'].split('/')[-1] if '$ref' in v else '?')
print(f' - {k}: {t} {v.get("description","")[:70]}')
PY======================================================================
====================================================================== integration.yml POST /v1/integrations media: application/json required: ['name'] ====================================================================== connection.yml POST /v1/connections media: application/json required: ['name', 'type'] - name: string Display name for the connection. - type: string Connection type, which determines the authentication and connectivity - externalId: string External identifier for the connection, often used to correlate it wit - assistant: string Lowercase application name for HTTP connections to systems with an int - accessScope: object What a Celigo APIs connection (`assistant: integratorceligoapi`) may r - _agentId: string Reference to a Celigo on-premise Agent. Required for types that need l - _borrowConcurrencyFromConnectionId: string Reference to another connection to share concurrency limits with. When - autoRecoverRateLimitErrors: boolean When true, rate-limit errors from the target system trigger automatic - __integrationIds: array Integrations to register the new connection on immediately after creat - endUserProvidedFields: ['array', 'null'] Dot-paths of this connection's fields (for example `http.encrypted.api - debugDate: string Enables debug logging until this timestamp. Set to a future time to ca - settingsForm: object Dynamic form definition for connection-specific custom settings. - settings: object Connection-specific settings captured by settingsForm. At runtime, ste - pgp: ? Present for file-based connections (ftp, s3, filesystem, …) that encry - ssl: $ref:SSL - netsuite: ? Present when `type` is `netsuite`. - salesforce: ? Present when `type` is `salesforce`. - ftp: ? Present when `type` is `ftp`. - s3: ? Present when `type` is `s3`. - http: ? Present when `type` is `http`. - rdbms: ? Present when `type` is `rdbms`. - mongodb: ? Present when `type` is `mongodb`. - as2: ? Present when `type` is `as2`. - filesystem: ? Present when `type` is `filesystem`. - dynamodb: ? Present when `type` is `dynamodb`. - jdbc: ? Present when `type` is `jdbc`. - van: ? Present when `type` is `van`. - mcp: ? Present when `type` is `mcp`. - wrapper: ? Present when `type` is `wrapper`. ====================================================================== flow.yml POST /v1/flows media: application/json required: ['name']
Recorded tool call
{
"type": "tool_call",
"seq": 66,
"ts": "2026-10-20T15:51:14.721Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && python3 - <<'PY'\nimport yaml,json\ndef resolve(d,ref):\n if not isinstance(ref,str) or not ref.startswith('#/'): return ref\n cur=d\n for p in ref[2:].split('/'):\n cur=cur[p]\n return cur\nfor fn,op in [('integration.yml','createIntegration'),('connection.yml','createConnection'),('flow.yml','createFlow')]:\n d=yaml.safe_load(open(fn))\n for path,methods in d['paths'].items():\n for verb,o in methods.items():\n if isinstance(o,dict) and o.get('operationId')==op:\n print('='*70)\n print(fn,verb.upper(),path)\n rb=o.get('requestBody',{})\n for mt,media in rb.get('content',{}).items():\n sch=media.get('schema',{})\n if '$ref' in sch: sch=resolve(d,sch['$ref'])\n print('media:',mt)\n print('required:',sch.get('required'))\n props=sch.get('properties',{})\n for k,v in props.items():\n t=v.get('type') or ('$ref:'+v['$ref'].split('/')[-1] if '$ref' in v else '?')\n print(f' - {k}: {t} {v.get(\"description\",\"\")[:70]}')\nPY"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_bd48f4f681be4a629274fa1f",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 67,
"ts": "2026-10-20T15:51:16.683Z",
"turn": 1,
"output": {
"content": [
{
"text": "======================================================================\nintegration.yml POST /v1/integrations\nmedia: application/json\nrequired: ['name']\n======================================================================\nconnection.yml POST /v1/connections\nmedia: application/json\nrequired: ['name', 'type']\n - name: string Display name for the connection.\n - type: string Connection type, which determines the authentication and connectivity \n - externalId: string External identifier for the connection, often used to correlate it wit\n - assistant: string Lowercase application name for HTTP connections to systems with an int\n - accessScope: object What a Celigo APIs connection (`assistant: integratorceligoapi`) may r\n - _agentId: string Reference to a Celigo on-premise Agent. Required for types that need l\n - _borrowConcurrencyFromConnectionId: string Reference to another connection to share concurrency limits with. When\n - autoRecoverRateLimitErrors: boolean When true, rate-limit errors from the target system trigger automatic \n - __integrationIds: array Integrations to register the new connection on immediately after creat\n - endUserProvidedFields: ['array', 'null'] Dot-paths of this connection's fields (for example `http.encrypted.api\n - debugDate: string Enables debug logging until this timestamp. Set to a future time to ca\n - settingsForm: object Dynamic form definition for connection-specific custom settings.\n - settings: object Connection-specific settings captured by settingsForm. At runtime, ste\n - pgp: ? Present for file-based connections (ftp, s3, filesystem, …) that encry\n - ssl: $ref:SSL \n - netsuite: ? Present when `type` is `netsuite`.\n - salesforce: ? Present when `type` is `salesforce`.\n - ftp: ? Present when `type` is `ftp`.\n - s3: ? Present when `type` is `s3`.\n - http: ? Present when `type` is `http`.\n - rdbms: ? Present when `type` is `rdbms`.\n - mongodb: ? Present when `type` is `mongodb`.\n - as2: ? Present when `type` is `as2`.\n - filesystem: ? Present when `type` is `filesystem`.\n - dynamodb: ? Present when `type` is `dynamodb`.\n - jdbc: ? Present when `type` is `jdbc`.\n - van: ? Present when `type` is `van`.\n - mcp: ? Present when `type` is `mcp`.\n - wrapper: ? Present when `type` is `wrapper`.\n======================================================================\nflow.yml POST /v1/flows\nmedia: application/json\nrequired: ['name']\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_bd48f4f681be4a629274fa1f",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
The request body schemas are minimal (required only name/type) because the specs likely reference external $refs that got inlined but the top-level schema may be broad. Let me look at the actual full schema for createIntegration and createFlow to see if there's an allOf or additional properties. Actually, the output shows only name required. Let me get the full schema for integration create to know what fields are available. Let me print the raw requestBody schema for createIntegration and createFlow.
Actually, the important thing: I can build a valid simple example. Let me think about what "simple example" to build.
Given the developer workflow, the cleanest simple example is a script using the REST API to:
- Verify auth (
GET /v1/tokenInfo). - List integrations.
- Create an integration (container).
- Optionally create a connection and flow.
But the user might not have a Celigo account. The example should be runnable with a token env var. Since I can't authenticate, I'll make the script handle a missing token gracefully and show exactly what to set.
Alternatively, a "simple example" could use the CLI: celigo account snapshot, celigo integrations create, etc. Since the CLI is installed and verified, I could write a small shell script. But the CLI requires auth too.
I think the best deliverable: a small Node.js example using the hosted REST API with fetch (Node 22 has global fetch), no dependencies. It:
- Reads
CELIGO_API_TOKENand optionalCELIGO_BASE_URL. - Calls
GET /v1/tokenInfo. - Lists integrations.
- Creates an integration named from env.
- Creates a simple HTTP connection? That needs real credentials. Maybe just create the integration and an export/import? Actually creating a flow requires export/import ids.
Let me keep it simple but meaningful: create an integration, then list it, then delete it (cleanup) — a full CRUD round trip that proves the API works. And show how to build a flow bottom-up as commented extension.
Actually, better: use the CLI for the "build a flow" example because the CLI handles scaffolding, and use the REST script for a minimal read. Hmm.
Let me reconsider the user's ask: "Help me build a simple example using Celigo." They want something tangible. "Tell me how pricing works" and "briefly tell me whether this product will be easy for you to manage." "Let me know if you get blocked. If this product has no developer workflow you can act on, say so plainly and stop."
So: There IS a developer workflow (CLI + REST API + OpenAPI specs + MCP). I should build the example, explain pricing, and assess manageability. I should mention I'm blocked only on authentication (no token), not on tooling.
Let me create the example in the repo. Let me build a self-contained Node script + a README. Let me verify the exact API behavior I can without auth (401, tokenInfo endpoint exists). Let me check the account spec for tokenInfo.
Let me look at the account.yml for tokenInfo and snapshot endpoints.
cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && grep -nE 'tokenInfo|snapshot' account.yml | head -20; echo "=== check tokenInfo reachable unauth ==="; curl -sS -m 10 -o /dev/null -w "GET /v1/tokenInfo -> HTTP %{http_code}\n" https://api.integrator.io/v1/tokenInfo29: /v1/tokenInfo:
29: /v1/tokenInfo: === check tokenInfo reachable unauth === GET /v1/tokenInfo -> HTTP 401
Recorded tool call
{
"type": "tool_call",
"seq": 70,
"ts": "2026-10-20T15:51:24.271Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && grep -nE 'tokenInfo|snapshot' account.yml | head -20; echo \"=== check tokenInfo reachable unauth ===\"; curl -sS -m 10 -o /dev/null -w \"GET /v1/tokenInfo -> HTTP %{http_code}\\n\" https://api.integrator.io/v1/tokenInfo"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_ac7ad686a8f94c69942838ba",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 74,
"ts": "2026-10-20T15:51:24.633Z",
"turn": 1,
"output": {
"content": [
{
"text": "29: /v1/tokenInfo:\n=== check tokenInfo reachable unauth ===\nGET /v1/tokenInfo -> HTTP 401\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_ac7ad686a8f94c69942838ba",
"is_error": false,
"tool_kind": "shell"
}cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && python3 - <<'PY'
import yaml
d=yaml.safe_load(open('integration.yml'))
o=d['paths']['/v1/integrations']['post']
sch=o['requestBody']['content']['application/json']['schema']
print("createIntegration schema keys:", list(sch.keys()))
print(yaml.dump(sch, sort_keys=False)[:1500])
PYcreateIntegration schema keys: ['$ref']
createIntegration schema keys: ['$ref'] $ref: '#/components/schemas/Request'
Recorded tool call
{
"type": "tool_call",
"seq": 72,
"ts": "2026-10-20T15:51:24.271Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && python3 - <<'PY'\nimport yaml\nd=yaml.safe_load(open('integration.yml'))\no=d['paths']['/v1/integrations']['post']\nsch=o['requestBody']['content']['application/json']['schema']\nprint(\"createIntegration schema keys:\", list(sch.keys()))\nprint(yaml.dump(sch, sort_keys=False)[:1500])\nPY"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_9afb0a9b7388419aaf5ac5c7",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 73,
"ts": "2026-10-20T15:51:24.633Z",
"turn": 1,
"output": {
"content": [
{
"text": "createIntegration schema keys: ['$ref']\n$ref: '#/components/schemas/Request'\n\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_9afb0a9b7388419aaf5ac5c7",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
Let me look at the Request schema for integration. Let me dump components.schemas.Request.
cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && python3 - <<'PY'
import yaml
for fn,refname in [('integration.yml','Request'),('flow.yml','Request')]:
d=yaml.safe_load(open(fn))
sch=d['components']['schemas'].get(refname,{})
print('='*60, fn, refname)
print('required:', sch.get('required'))
for k,v in list(sch.get('properties',{}).items())[:40]:
t=v.get('type') or ('$ref:'+v['$ref'].split('/')[-1] if '$ref' in v else '?')
print(' ',k,':',t)
PY============================================================ integration.yml Request
============================================================ integration.yml Request required: ['name'] ============================================================ flow.yml Request required: ['name']
Recorded tool call
{
"type": "tool_call",
"seq": 77,
"ts": "2026-10-20T15:51:37.446Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && python3 - <<'PY'\nimport yaml\nfor fn,refname in [('integration.yml','Request'),('flow.yml','Request')]:\n d=yaml.safe_load(open(fn))\n sch=d['components']['schemas'].get(refname,{})\n print('='*60, fn, refname)\n print('required:', sch.get('required'))\n for k,v in list(sch.get('properties',{}).items())[:40]:\n t=v.get('type') or ('$ref:'+v['$ref'].split('/')[-1] if '$ref' in v else '?')\n print(' ',k,':',t)\nPY"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_9db5f8d7d68e434697427636",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 80,
"ts": "2026-10-20T15:51:38.166Z",
"turn": 1,
"output": {
"content": [
{
"text": "============================================================ integration.yml Request\nrequired: ['name']\n============================================================ flow.yml Request\nrequired: ['name']\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_9db5f8d7d68e434697427636",
"is_error": false,
"tool_kind": "shell"
}cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && python3 - <<'PY'
import yaml
d=yaml.safe_load(open('flow.yml'))
# print all schema names
print("flow schemas:", list(d['components']['schemas'].keys()))
# find the actual flow properties (maybe under Request allOf)
import json
def show(name):
s=d['components']['schemas'][name]
print('---',name,'required=',s.get('required'))
for k,v in list(s.get('properties',{}).items())[:30]:
t=v.get('type') or ('$ref:'+v['$ref'].split('/')[-1] if '$ref' in v else '?')
print(' ',k,':',t)
for n in ['Request','Flow','FlowPost']:
if n in d['components']['schemas']: show(n)
PYflow schemas: ['PageGenerator', 'ResponseMapping', 'PageProcessor', 'Branch', 'Router', 'Overrides', 'OverridesHelper', 'AIDescription', 'FlowBase', 'ResourceRe
flow schemas: ['PageGenerator', 'ResponseMapping', 'PageProcessor', 'Branch', 'Router', 'Overrides', 'OverridesHelper', 'AIDescription', 'FlowBase', 'ResourceResponse', 'Flow', 'Error', 'Request', 'JsonPatchOperation', 'JsonPatchRequest', 'CloneRequest', 'CloneResponse', 'ClonePreviewResponse', 'FlowDescendantsResponse', 'FlowErrorsResponse', 'FlowReplaceConnectionRequest', 'FlowRunRequest', 'FlowRunResponse', 'FlowRunsStatsRequest', 'FlowRunsStatsResponse', 'FlowTraceKeyResponse', 'LastExportDateTimeResponse', 'UpdateFlowGroupingRequest', 'FlowStepError', 'FlowStepErrorsResponse', 'FlowStepErrorsAssignRequest', 'FlowStepAffectedErrorsResponse', 'FlowStepErrorsUnassignRequest', 'FlowStepErrorsUnassignResponse', 'FlowStepSignedURLResponse', 'FlowStepResolvedResponse', 'FlowStepResolveRequest', 'FlowStepResolvedDeleteRequest', 'FlowStepRetryRequest', 'JobFileRef', 'JobLogRef', 'JobErrorFileRef', 'Job', 'FlowStepRetryExponentialRequest', 'FlowStepRetryData', 'FlowStepRequestResponse', 'FlowStepRequestsListResponse', 'FlowStepRequestsDeleteRequest', 'FlowStepRequestsDeleteResponse', 'FlowStepTagsRequest', 'FlowStepTagsResponse', 'FlowJobsSearchResponse', 'ExecutionLogEntry', 'ExecutionLogsResponse', 'ExecutionLogMetadataRequest', 'ExecutionLogStep', 'ExecutionLogMetadataResponse', 'ExecutionLogDataRequest', 'ExecutionLogDataResponse', 'FlowJobLogEntry', 'FlowJobLogChildrenResponse', 'ErrorsFilterMetadataResponse', 'PageProcessorsPreviewRequest', 'ExportPreviewResponse', 'DependencyEntry', 'DependencyResponse'] --- Request required= ['name'] --- Flow required= ['_id', 'name', 'disabled', 'logging', 'createdAt', 'lastModified']
Recorded tool call
{
"type": "tool_call",
"seq": 78,
"ts": "2026-10-20T15:51:37.446Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && python3 - <<'PY'\nimport yaml\nd=yaml.safe_load(open('flow.yml'))\n# print all schema names\nprint(\"flow schemas:\", list(d['components']['schemas'].keys()))\n# find the actual flow properties (maybe under Request allOf)\nimport json\ndef show(name):\n s=d['components']['schemas'][name]\n print('---',name,'required=',s.get('required'))\n for k,v in list(s.get('properties',{}).items())[:30]:\n t=v.get('type') or ('$ref:'+v['$ref'].split('/')[-1] if '$ref' in v else '?')\n print(' ',k,':',t)\nfor n in ['Request','Flow','FlowPost']:\n if n in d['components']['schemas']: show(n)\nPY"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_2ac0ae90f0754552872df597",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 79,
"ts": "2026-10-20T15:51:37.880Z",
"turn": 1,
"output": {
"content": [
{
"text": "flow schemas: ['PageGenerator', 'ResponseMapping', 'PageProcessor', 'Branch', 'Router', 'Overrides', 'OverridesHelper', 'AIDescription', 'FlowBase', 'ResourceResponse', 'Flow', 'Error', 'Request', 'JsonPatchOperation', 'JsonPatchRequest', 'CloneRequest', 'CloneResponse', 'ClonePreviewResponse', 'FlowDescendantsResponse', 'FlowErrorsResponse', 'FlowReplaceConnectionRequest', 'FlowRunRequest', 'FlowRunResponse', 'FlowRunsStatsRequest', 'FlowRunsStatsResponse', 'FlowTraceKeyResponse', 'LastExportDateTimeResponse', 'UpdateFlowGroupingRequest', 'FlowStepError', 'FlowStepErrorsResponse', 'FlowStepErrorsAssignRequest', 'FlowStepAffectedErrorsResponse', 'FlowStepErrorsUnassignRequest', 'FlowStepErrorsUnassignResponse', 'FlowStepSignedURLResponse', 'FlowStepResolvedResponse', 'FlowStepResolveRequest', 'FlowStepResolvedDeleteRequest', 'FlowStepRetryRequest', 'JobFileRef', 'JobLogRef', 'JobErrorFileRef', 'Job', 'FlowStepRetryExponentialRequest', 'FlowStepRetryData', 'FlowStepRequestResponse', 'FlowStepRequestsListResponse', 'FlowStepRequestsDeleteRequest', 'FlowStepRequestsDeleteResponse', 'FlowStepTagsRequest', 'FlowStepTagsResponse', 'FlowJobsSearchResponse', 'ExecutionLogEntry', 'ExecutionLogsResponse', 'ExecutionLogMetadataRequest', 'ExecutionLogStep', 'ExecutionLogMetadataResponse', 'ExecutionLogDataRequest', 'ExecutionLogDataResponse', 'FlowJobLogEntry', 'FlowJobLogChildrenResponse', 'ErrorsFilterMetadataResponse', 'PageProcessorsPreviewRequest', 'ExportPreviewResponse', 'DependencyEntry', 'DependencyResponse']\n--- Request required= ['name']\n--- Flow required= ['_id', 'name', 'disabled', 'logging', 'createdAt', 'lastModified']\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_2ac0ae90f0754552872df597",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
Let me look at FlowBase properties to know the flow structure (pageGenerators etc.). Let me dump FlowBase.
cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && python3 - <<'PY'
import yaml
d=yaml.safe_load(open('flow.yml'))
s=d['components']['schemas']['FlowBase']
print('FlowBase required=',s.get('required'))
for k,v in s.get('properties',{}).items():
t=v.get('type') or ('$ref:'+v['$ref'].split('/')[-1] if '$ref' in v else '?')
desc=(v.get('description') or '')[:80]
print(' ',k,':',t,'-',desc)
print()
print('PageGenerator required=',d['components']['schemas']['PageGenerator'].get('required'))
for k,v in d['components']['schemas']['PageGenerator'].get('properties',{}).items():
t=v.get('type') or ('$ref:'+v['$ref'].split('/')[-1] if '$ref' in v else '?')
print(' ',k,':',t)
PYFlowBase required= None
FlowBase required= None name : string - Display name for the flow. description : string - Free-text description of the flow's purpose. _integrationId : ['string', 'null'] - Integration this flow belongs to. Omitting it (or sending `null`) on create no l schedule : ['string', 'null'] - Celigo 6-field cron expression: `? minute hour day-of-month month day-of-week`. timezone : ['string', 'null'] - IANA timezone for the schedule (e.g. `America/New_York`). Defaults to UTC. disabled : boolean - When true, scheduled executions are suspended and the flow cannot be triggered. pageGenerators : array - Data sources (exports) that produce pages of records for the flow. Mutually excl pageProcessors : array - Sequential processing steps (imports, lookups, routers). Mutually exclusive with routers : array - Routers that direct records into branches. Mutually exclusive with top-level `pa _exportId : string - Source export of a simple (single source, single destination) flow — the legacy _importId : string - Destination import of a simple (single source, single destination) flow — the le _runNextFlowIds : ['array', 'null'] - Flow IDs to start when a run of this flow completes — the flow's "Next integrati _runNextExportIds : array - Specific exports in other flows to trigger after completion. externalId : ['string', 'null'] - External identifier for correlating the flow with a record in another system. wizardState : string - Tracks how far the user has progressed through the flow setup wizard in the UI. settings : object - Custom key-value settings for this flow. At runtime the flow's steps read them u runPageGeneratorsInParallel : boolean - When true, all page generators start simultaneously instead of sequentially. autoResolveMatchingTraceKeys : boolean - When true, a new error for a record whose trace key already has open errors on t isAbstract : boolean - Marks this as an abstract (multi-instance) flow — a reusable template that canno _abstractFlowId : string - Abstract flow this instance inherits from. Immutable once set. The instance inhe overrides : $ref:Overrides - overridesHelper : $ref:OverridesHelper - _flowGroupingId : ['string', 'null'] - Flow grouping for organizing related flows in the UI. References an entry in the _keepDeltaBehindFlowId : string - Coordinates delta timing so this flow's checkpoint stays behind the referenced f _keepDeltaBehindExportId : string - Like `_keepDeltaBehindFlowId` but targets a specific export within a flow. aiDescription : $ref:AIDescription - aliases : array - Named aliases that map to specific resources within the flow. draft : boolean - When true, this flow is a draft that auto-deletes when its expiry passes (`draft preSave : object - Pre-save hook that runs a script before persisting flow changes. settingsForm : object - Form definition for collecting flow-level settings from users in the UI. PageGenerator required= None _exportId : string schedule : string skipRetries : boolean setupInProgress : boolean _keepDeltaBehindFlowId : string _keepDeltaBehindExportId : string __startDateHelper : string
Recorded tool call
{
"type": "tool_call",
"seq": 83,
"ts": "2026-10-20T15:51:47.717Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && python3 - <<'PY'\nimport yaml\nd=yaml.safe_load(open('flow.yml'))\ns=d['components']['schemas']['FlowBase']\nprint('FlowBase required=',s.get('required'))\nfor k,v in s.get('properties',{}).items():\n t=v.get('type') or ('$ref:'+v['$ref'].split('/')[-1] if '$ref' in v else '?')\n desc=(v.get('description') or '')[:80]\n print(' ',k,':',t,'-',desc)\nprint()\nprint('PageGenerator required=',d['components']['schemas']['PageGenerator'].get('required'))\nfor k,v in d['components']['schemas']['PageGenerator'].get('properties',{}).items():\n t=v.get('type') or ('$ref:'+v['$ref'].split('/')[-1] if '$ref' in v else '?')\n print(' ',k,':',t)\nPY"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_1a007c95b3a24ef5be1525dc",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 85,
"ts": "2026-10-20T15:51:48.020Z",
"turn": 1,
"output": {
"content": [
{
"text": "FlowBase required= None\n name : string - Display name for the flow.\n description : string - Free-text description of the flow's purpose.\n _integrationId : ['string', 'null'] - Integration this flow belongs to. Omitting it (or sending `null`) on\ncreate no l\n schedule : ['string', 'null'] - Celigo 6-field cron expression: `? minute hour day-of-month month day-of-week`.\n\n timezone : ['string', 'null'] - IANA timezone for the schedule (e.g. `America/New_York`). Defaults to UTC.\n disabled : boolean - When true, scheduled executions are suspended and the flow cannot be triggered.\n\n pageGenerators : array - Data sources (exports) that produce pages of records for the flow. Mutually excl\n pageProcessors : array - Sequential processing steps (imports, lookups, routers). Mutually exclusive with\n routers : array - Routers that direct records into branches. Mutually exclusive with top-level\n`pa\n _exportId : string - Source export of a simple (single source, single destination) flow — the legacy\n\n _importId : string - Destination import of a simple (single source, single destination) flow — the le\n _runNextFlowIds : ['array', 'null'] - Flow IDs to start when a run of this flow completes — the flow's \"Next integrati\n _runNextExportIds : array - Specific exports in other flows to trigger after completion.\n externalId : ['string', 'null'] - External identifier for correlating the flow with a record in another system.\n wizardState : string - Tracks how far the user has progressed through the flow setup wizard in the UI.\n settings : object - Custom key-value settings for this flow. At runtime the flow's steps read them\nu\n runPageGeneratorsInParallel : boolean - When true, all page generators start simultaneously instead of sequentially.\n autoResolveMatchingTraceKeys : boolean - When true, a new error for a record whose trace key already has open errors on t\n isAbstract : boolean - Marks this as an abstract (multi-instance) flow — a reusable template that canno\n _abstractFlowId : string - Abstract flow this instance inherits from. Immutable once set. The instance inhe\n overrides : $ref:Overrides - \n overridesHelper : $ref:OverridesHelper - \n _flowGroupingId : ['string', 'null'] - Flow grouping for organizing related flows in the UI. References an entry in the\n _keepDeltaBehindFlowId : string - Coordinates delta timing so this flow's checkpoint stays behind the referenced f\n _keepDeltaBehindExportId : string - Like `_keepDeltaBehindFlowId` but targets a specific export within a flow.\n aiDescription : $ref:AIDescription - \n aliases : array - Named aliases that map to specific resources within the flow.\n draft : boolean - When true, this flow is a draft that auto-deletes when its expiry passes\n(`draft\n preSave : object - Pre-save hook that runs a script before persisting flow changes.\n settingsForm : object - Form definition for collecting flow-level settings from users in the UI.\n\nPageGenerator required= None\n _exportId : string\n schedule : string\n skipRetries : boolean\n setupInProgress : boolean\n _keepDeltaBehindFlowId : string\n _keepDeltaBehindExportId : string\n __startDateHelper : string\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_1a007c95b3a24ef5be1525dc",
"is_error": false,
"tool_kind": "shell"
}cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && echo "=== integrations list query params ===" && python3 - <<'PY'
import yaml
d=yaml.safe_load(open('integration.yml'))
o=d['paths']['/v1/integrations']['get']
for p in o.get('parameters',[]):
print(p.get('name'),'-',p.get('in'),'-',(p.get('description') or '')[:60])
PY
echo "=== connection http example schema ==="; python3 - <<'PY'
import yaml
d=yaml.safe_load(open('connection.yml'))
s=d['components']['schemas']
print('schemas:', [k for k in s if 'ttp' in k or k in ('Request','HTTP','Http')][:20])
for name in s:
if name.lower()=='http':
print('HTTP required=',s[name].get('required'))
for k,v in s[name].get('properties',{}).items():
print(' ',k,':',v.get('type') or v.get('$ref'))
PY=== integrations list query params ===
=== integrations list query params === None - None - None - None - limit - query - Maximum number of integrations to return per page. When more === connection http example schema === schemas: ['HTTP', 'Request'] HTTP required= ['baseURI', 'mediaType'] mediaType : string successMediaType : string errorMediaType : string baseURI : string disableStrictSSL : boolean concurrencyLevel : integer targetConcurrencyLevel : integer retryHeader : string formType : string type : string clientCertificates : object ping : object auth : object rateLimit : object headers : array encrypted : ['object', 'string'] encryptedFields : array unencrypted : object unencryptedFields : array _iClientId : string _httpConnectorId : string _httpConnectorApiId : string _httpConnectorVersionId : string isRest : boolean useNewAuthFailSchema : boolean
Recorded tool call
{
"type": "tool_call",
"seq": 84,
"ts": "2026-10-20T15:51:47.717Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && echo \"=== integrations list query params ===\" && python3 - <<'PY'\nimport yaml\nd=yaml.safe_load(open('integration.yml'))\no=d['paths']['/v1/integrations']['get']\nfor p in o.get('parameters',[]):\n print(p.get('name'),'-',p.get('in'),'-',(p.get('description') or '')[:60])\nPY\necho \"=== connection http example schema ===\"; python3 - <<'PY'\nimport yaml\nd=yaml.safe_load(open('connection.yml'))\ns=d['components']['schemas']\nprint('schemas:', [k for k in s if 'ttp' in k or k in ('Request','HTTP','Http')][:20])\nfor name in s:\n if name.lower()=='http':\n print('HTTP required=',s[name].get('required'))\n for k,v in s[name].get('properties',{}).items():\n print(' ',k,':',v.get('type') or v.get('$ref'))\nPY"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_a2bdff3b47114bb7a6c6e90e",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 86,
"ts": "2026-10-20T15:51:48.379Z",
"turn": 1,
"output": {
"content": [
{
"text": "=== integrations list query params ===\nNone - None - \nNone - None - \nlimit - query - Maximum number of integrations to return per page. When more\n=== connection http example schema ===\nschemas: ['HTTP', 'Request']\nHTTP required= ['baseURI', 'mediaType']\n mediaType : string\n successMediaType : string\n errorMediaType : string\n baseURI : string\n disableStrictSSL : boolean\n concurrencyLevel : integer\n targetConcurrencyLevel : integer\n retryHeader : string\n formType : string\n type : string\n clientCertificates : object\n ping : object\n auth : object\n rateLimit : object\n headers : array\n encrypted : ['object', 'string']\n encryptedFields : array\n unencrypted : object\n unencryptedFields : array\n _iClientId : string\n _httpConnectorId : string\n _httpConnectorApiId : string\n _httpConnectorVersionId : string\n isRest : boolean\n useNewAuthFailSchema : boolean\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_a2bdff3b47114bb7a6c6e90e",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
I now have enough to build a correct example. Let me build a self-contained Node script that:
- Reads
CELIGO_API_TOKEN(required) andCELIGO_BASE_URL(default US). GET /v1/tokenInfoto confirm identity/region.- Lists integrations (
GET /v1/integrations?limit=10). - Creates an integration (
POST /v1/integrationswith{name}). - Gets it back.
- Cleans up by deleting it (with
--keepflag to skip).
Also demonstrate building a flow bottom-up? That requires real external connection creds. I could create a minimal HTTP connection to a public API (e.g., https://api.github.com) with no auth, then an export and import... but the import would need a destination. Too much. Keep it to the integration CRUD + mention the CLI flow-building commands.
Actually, a nice "simple example" that's fully self-contained and doesn't need external systems: use the API to create an integration and a flow shell? A flow with no steps may not be valid. Better to keep the CRUD example and add commented guidance.
Alternatively, I can make the example create an integration, then an HTTP connection to a public API with auth: {type: "none"}, ping it, and delete it. That's a genuinely useful end-to-end test that touches the hosted product and doesn't require third-party credentials. Let me check the HTTP auth schema for "none". Let me check the connection HTTP auth object and ping endpoint.
Actually, let me keep the example focused but add a --with-connection option that creates a public HTTP connection and pings it. But I can't test it without a token, so there's risk of schema inaccuracy. I'll keep the example minimal and robust: integration CRUD. I'll include a commented "build a flow bottom-up" section referencing the CLI.
Hmm, but the user asked to "build a simple example." A minimal integration CRUD via REST API is a fine simple example. Let me also provide the equivalent CLI one-liners.
Let me now create the files in /sandbox/repo:
celigo-example/celigo.mjs— Node script.celigo-example/README.md— usage, pricing summary, notes.celigo-example/package.jsonmaybe not needed (no deps). Node 22 has fetch. I'll add a package.json with"type":"module"and a script.
Wait — should I put it in /sandbox/repo? The repo is a git workspace. Yes, create the example there. Let me make a clean directory.
Let me verify the delete endpoint and list response shape. Let me check the integration list response schema quickly.
Let me also double check: createIntegration required only name. Good.
Let me write the script carefully. Since I can't run it authenticated, I should at least run it with no token to verify graceful handling and lint/syntax-check it.
Let me also add a --dry-run mode that prints requests without sending. Actually, better: add a CELIGO_DRY_RUN=1 that prints the planned calls. That lets me demonstrate/verify the script runs even without a token.
Let me check the list response and delete status.
cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && python3 - <<'PY'
import yaml
d=yaml.safe_load(open('integration.yml'))
o=d['paths']['/v1/integrations']['get']
print("list responses:", list(o['responses'].keys()))
for code,r in o['responses'].items():
c=r.get('content',{}).get('application/json',{}).get('schema')
print(code, c)
o2=d['paths']['/v1/integrations/{_id}']['delete']
print("delete responses:", list(o2['responses'].keys()))
o3=d['paths']['/v1/integrations/{_id}']['get']
for code,r in o3['responses'].items():
print('get',code, r.get('content',{}).get('application/json',{}).get('schema'))
PYlist responses: ['200', '204', '401']
list responses: ['200', '204', '401']
200 {'type': 'array', 'items': {'$ref': '#/components/schemas/Integration'}}
204 None
401 None
delete responses: ['204', '401', '404', '422']
get 200 {'$ref': '#/components/schemas/Integration'}
get 401 None
get 404 None
Recorded tool call
{
"type": "tool_call",
"seq": 89,
"ts": "2026-10-20T15:52:09.731Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && python3 - <<'PY'\nimport yaml\nd=yaml.safe_load(open('integration.yml'))\no=d['paths']['/v1/integrations']['get']\nprint(\"list responses:\", list(o['responses'].keys()))\nfor code,r in o['responses'].items():\n c=r.get('content',{}).get('application/json',{}).get('schema')\n print(code, c)\no2=d['paths']['/v1/integrations/{_id}']['delete']\nprint(\"delete responses:\", list(o2['responses'].keys()))\no3=d['paths']['/v1/integrations/{_id}']['get']\nfor code,r in o3['responses'].items():\n print('get',code, r.get('content',{}).get('application/json',{}).get('schema'))\nPY"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_36c8fbf4f80d488c8fe4a09b",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 91,
"ts": "2026-10-20T15:52:10.648Z",
"turn": 1,
"output": {
"content": [
{
"text": "list responses: ['200', '204', '401']\n200 {'type': 'array', 'items': {'$ref': '#/components/schemas/Integration'}}\n204 None\n401 None\ndelete responses: ['204', '401', '404', '422']\nget 200 {'$ref': '#/components/schemas/Integration'}\nget 401 None\nget 404 None\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_36c8fbf4f80d488c8fe4a09b",
"is_error": false,
"tool_kind": "shell"
}cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && python3 - <<'PY'
import yaml
d=yaml.safe_load(open('integration.yml'))
s=d['components']['schemas']
print([k for k in s if 'ntegration' in k or k in ('ResourceResponse','Error','Page')])
# show Integration schema
for n in s:
if n.lower() in ('integration','request'):
sch=s[n]
print('---',n,'required=',sch.get('required'))
for k,v in list(sch.get('properties',{}).items())[:20]:
print(' ',k,':',v.get('type') or v.get('$ref'))
PY['IntegrationBase', 'ResourceResponse', 'Integration', 'Error', 'IntegrationErrorsResponse', 'IntegrationErrorsAssignRequest', 'IntegrationErrorsAssignResponse'
['IntegrationBase', 'ResourceResponse', 'Integration', 'Error', 'IntegrationErrorsResponse', 'IntegrationErrorsAssignRequest', 'IntegrationErrorsAssignResponse'] --- Integration required= ['_id', 'name', 'createdAt', 'lastModified', 'install', 'installSteps', 'uninstallSteps', 'changeEditionSteps', 'flowGroupings', 'apiGroupings', '_registeredConnectionIds'] --- Request required= ['name']
Recorded tool call
{
"type": "tool_call",
"seq": 90,
"ts": "2026-10-20T15:52:09.731Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && python3 - <<'PY'\nimport yaml\nd=yaml.safe_load(open('integration.yml'))\ns=d['components']['schemas']\nprint([k for k in s if 'ntegration' in k or k in ('ResourceResponse','Error','Page')])\n# show Integration schema\nfor n in s:\n if n.lower() in ('integration','request'):\n sch=s[n]\n print('---',n,'required=',sch.get('required'))\n for k,v in list(sch.get('properties',{}).items())[:20]:\n print(' ',k,':',v.get('type') or v.get('$ref'))\nPY"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_eef0c69bbd754d58af316730",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 92,
"ts": "2026-10-20T15:52:10.648Z",
"turn": 1,
"output": {
"content": [
{
"text": "['IntegrationBase', 'ResourceResponse', 'Integration', 'Error', 'IntegrationErrorsResponse', 'IntegrationErrorsAssignRequest', 'IntegrationErrorsAssignResponse']\n--- Integration required= ['_id', 'name', 'createdAt', 'lastModified', 'install', 'installSteps', 'uninstallSteps', 'changeEditionSteps', 'flowGroupings', 'apiGroupings', '_registeredConnectionIds']\n--- Request required= ['name']\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_eef0c69bbd754d58af316730",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
Good. Now let me write the example. I'll create /sandbox/repo/celigo-example/.
The script:
- Node ESM, no deps.
- Config: base URL, token from env or
--token. - Modes:
check(tokenInfo + list),demo(create integration, get, delete unless --keep). --dry-runprints requests.- Clean error handling, prints HTTP status and JSON.
Let me write it.
I should also include the CLI equivalents in the README.
Let me verify the tokenInfo response shape. Let me check account.yml tokenInfo.
cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && python3 - <<'PY'
import yaml
d=yaml.safe_load(open('account.yml'))
o=d['paths']['/v1/tokenInfo']['get']
print('op:',o.get('operationId'))
for code,r in o['responses'].items():
sch=r.get('content',{}).get('application/json',{}).get('schema')
print(code,sch)
if isinstance(sch,dict) and '$ref' in sch:
name=sch['$ref'].split('/')[-1]
s=d['components']['schemas'][name]
print(' required=',s.get('required'))
for k,v in s.get('properties',{}).items():
print(' ',k,':',v.get('type'))
PYop: getTokenInfo
op: getTokenInfo
200 {'$ref': '#/components/schemas/TokenInfo'}
required= ['_userId', 'token', 'ownerUser']
_userId : string
token : object
ownerUser : object
byUser : object
environment : object
401 None
Recorded tool call
{
"type": "tool_call",
"seq": 95,
"ts": "2026-10-20T15:52:16.225Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && python3 - <<'PY'\nimport yaml\nd=yaml.safe_load(open('account.yml'))\no=d['paths']['/v1/tokenInfo']['get']\nprint('op:',o.get('operationId'))\nfor code,r in o['responses'].items():\n sch=r.get('content',{}).get('application/json',{}).get('schema')\n print(code,sch)\n if isinstance(sch,dict) and '$ref' in sch:\n name=sch['$ref'].split('/')[-1]\n s=d['components']['schemas'][name]\n print(' required=',s.get('required'))\n for k,v in s.get('properties',{}).items():\n print(' ',k,':',v.get('type'))\nPY"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_c83105c9b9aa4dc6820a51e6",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 96,
"ts": "2026-10-20T15:52:16.225Z",
"turn": 1,
"output": {
"content": [
{
"text": "op: getTokenInfo\n200 {'$ref': '#/components/schemas/TokenInfo'}\n required= ['_userId', 'token', 'ownerUser']\n _userId : string\n token : object\n ownerUser : object\n byUser : object\n environment : object\n401 None\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_c83105c9b9aa4dc6820a51e6",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
Let me check TokenInfo properties more (environment name, account). Let me dump.
cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && python3 - <<'PY'
import yaml
d=yaml.safe_load(open('account.yml'))
s=d['components']['schemas']['TokenInfo']
print(yaml.dump(s, sort_keys=False)[:1500])
PYtype: object
type: object
description: 'Identity envelope for the bearer token on the request: the credential
kind, the
account owner, the user acting through it, and the environment it is bound to.
Read the account id from `ownerUser._id` and the actor from `byUser`; `_userId`
is
the user record the token is bound to, which for a token on the production
environment is the account owner.'
required:
- _userId
- token
- ownerUser
x-celigo-ai-guidance:
- 'Use ownerUser._id as the account id and token.type to tell credential kinds apart.
byUser is absent on on-premise agent tokens (type agent), so never dereference it
without checking the type first.'
properties:
_userId:
type: string
format: objectId
x-celigo-refModel: users
examples:
- 5e58483b68b52e5827d56a86
description: 'User record the token is bound to. For a token on the production
environment this
is the account owner, so it matches both `ownerUser._id` and
`environment._envUserId`.'
token:
type: object
description: The credential that authenticated the request. Never echoes the secret
value.
required:
- type
properties:
type:
type: string
examples:
- service
description: 'Credential kind: `service` for an account API token, `agent`
for an on-premise
agent''s access token. Read this field rather than inferring the kind from
which
other fields are present.'
Recorded tool call
{
"type": "tool_call",
"seq": 99,
"ts": "2026-10-20T15:52:18.444Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /tmp/celigo-ex/node_modules/@celigo/api-specs/dist && python3 - <<'PY'\nimport yaml\nd=yaml.safe_load(open('account.yml'))\ns=d['components']['schemas']['TokenInfo']\nprint(yaml.dump(s, sort_keys=False)[:1500])\nPY"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_8d53c36b5a7542be927f0224",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 100,
"ts": "2026-10-20T15:52:18.444Z",
"turn": 1,
"output": {
"content": [
{
"text": "type: object\ndescription: 'Identity envelope for the bearer token on the request: the credential\n kind, the\n\n account owner, the user acting through it, and the environment it is bound to.\n\n Read the account id from `ownerUser._id` and the actor from `byUser`; `_userId`\n is\n\n the user record the token is bound to, which for a token on the production\n\n environment is the account owner.'\nrequired:\n- _userId\n- token\n- ownerUser\nx-celigo-ai-guidance:\n- 'Use ownerUser._id as the account id and token.type to tell credential kinds apart.\n\n byUser is absent on on-premise agent tokens (type agent), so never dereference it\n\n without checking the type first.'\nproperties:\n _userId:\n type: string\n format: objectId\n x-celigo-refModel: users\n examples:\n - 5e58483b68b52e5827d56a86\n description: 'User record the token is bound to. For a token on the production\n environment this\n\n is the account owner, so it matches both `ownerUser._id` and\n\n `environment._envUserId`.'\n token:\n type: object\n description: The credential that authenticated the request. Never echoes the secret\n value.\n required:\n - type\n properties:\n type:\n type: string\n examples:\n - service\n description: 'Credential kind: `service` for an account API token, `agent`\n for an on-premise\n\n agent''s access token. Read this field rather than inferring the kind from\n which\n\n other fields are present.'\n \n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_8d53c36b5a7542be927f0224",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
Now I have enough. Let me write the example files.
Let me create the directory and files.
Now I have what I need. Let me build the example.
File edits
1 file
Writtenceligo-example/celigo.mjsView changes
/sandbox/repo/celigo-example/celigo.mjs
Written content · previous contents were not recorded
#!/usr/bin/env node/*** Minimal Celigo integrator.io example — talks to the HOSTED API only.* No dependencies; uses the global fetch in Node 22+.** Auth: create a token in integrator.io under Resources > API tokens, then:* export CELIGO_API_TOKEN="<token>"* node celigo.mjs check # confirm identity + list integrations* node celigo.mjs demo # create an integration, read it back, delete it** Region: the default host is the US. For EU/AU/CA set:* export CELIGO_BASE_URL="https://api.eu.integrator.io"** Safe by default: `demo` deletes what it creates unless you pass --keep.* `--dry-run` (or CELIGO_DRY_RUN=1) prints the planned calls without sending.*/const BASE_URL = (process.env.CELIGO_BASE_URL || "https://api.integrator.io").replace(/\/+$/, "");const TOKEN = process.env.CELIGO_API_TOKEN || "";const DRY_RUN = process.env.CELIGO_DRY_RUN === "1" || process.argv.includes("--dry-run");const KEEP = process.argv.includes("--keep");const command = process.argv[2] || "check";function log(msg) {process.stderr.write(`${msg}\n`);}async function call(method, path, body) {const url = `${BASE_URL}${path}`;log(`→ ${method} ${url}`);if (DRY_RUN) {if (body !== undefined) log(` body: ${JSON.stringify(body)}`);return { status: 0, data: null, dryRun: true };}const res = await fetch(url, {method,headers: {Authorization: `Bearer ${TOKEN}`,Accept: "application/json",...(body !== undefined ? { "Content-Type": "application/json" } : {}),},...(body !== undefined ? { body: JSON.stringify(body) } : {}),});const text = await res.text();let data = null;try {data = text ? JSON.parse(text) : null;} catch {data = text;}if (!res.ok) {const err = new Error(`${method} ${path} failed: HTTP ${res.status}`);err.status = res.status;err.body = data;throw err;}return { status: res.status, data };}async function check() {const info = await call("GET", "/v1/tokenInfo");const account = info.data?.ownerUser?._id ?? "unknown";const kind = info.data?.token?.type ?? "unknown";log(`authenticated: account=${account} credential=${kind}`);const list = await call("GET", "/v1/integrations?limit=10");const rows = Array.isArray(list.data) ? list.data : [];log(`integrations: ${rows.length}`);for (const it of rows) log(` - ${it._id} ${it.name}`);return { account, count: rows.length };}async function demo() {const name = `CLI Example ${new Date().toISOString().replace(/[:.]/g, "-")}`;const created = await call("POST", "/v1/integrations", { name });const id = created.data?._id;log(`created integration ${id} (${name})`);if (DRY_RUN) {log("dry run: stopping before read/delete");return;}const fetched = await call("GET", `/v1/integrations/${id}`);log(`read back: ${fetched.data?.name}`);if (KEEP) {log(`--keep set; leaving integration ${id} in place`);return;}await call("DELETE", `/v1/integrations/${id}`);log(`deleted integration ${id}`);}async function main() {if (DRY_RUN) log("DRY RUN — no requests will be sent");if (!TOKEN && !DRY_RUN) {log("error: CELIGO_API_TOKEN is not set.");log("Create one at integrator.io > Resources > API tokens, then export it.");process.exit(2);}if (command === "check") return check();if (command === "demo") return demo();log(`unknown command '${command}'. Use: check | demo [--keep] [--dry-run]`);process.exit(2);}main().catch((err) => {log(`error: ${err.message}`);if (err.body) log(`response: ${JSON.stringify(err.body)}`);process.exit(1);});
Source: write · call_8596f5ff23944bba9082e2cd
Recorded tool call
{
"type": "tool_call",
"seq": 104,
"ts": "2026-10-20T15:52:30.107Z",
"name": "write",
"turn": 1,
"input": {
"path": "/sandbox/repo/celigo-example/celigo.mjs",
"content": "#!/usr/bin/env node\n/**\n * Minimal Celigo integrator.io example — talks to the HOSTED API only.\n * No dependencies; uses the global fetch in Node 22+.\n *\n * Auth: create a token in integrator.io under Resources > API tokens, then:\n * export CELIGO_API_TOKEN=\"<token>\"\n * node celigo.mjs check # confirm identity + list integrations\n * node celigo.mjs demo # create an integration, read it back, delete it\n *\n * Region: the default host is the US. For EU/AU/CA set:\n * export CELIGO_BASE_URL=\"https://api.eu.integrator.io\"\n *\n * Safe by default: `demo` deletes what it creates unless you pass --keep.\n * `--dry-run` (or CELIGO_DRY_RUN=1) prints the planned calls without sending.\n */\n\nconst BASE_URL = (process.env.CELIGO_BASE_URL || \"https://api.integrator.io\").replace(/\\/+$/, \"\");\nconst TOKEN = process.env.CELIGO_API_TOKEN || \"\";\nconst DRY_RUN = process.env.CELIGO_DRY_RUN === \"1\" || process.argv.includes(\"--dry-run\");\nconst KEEP = process.argv.includes(\"--keep\");\nconst command = process.argv[2] || \"check\";\n\nfunction log(msg) {\n process.stderr.write(`${msg}\\n`);\n}\n\nasync function call(method, path, body) {\n const url = `${BASE_URL}${path}`;\n log(`→ ${method} ${url}`);\n if (DRY_RUN) {\n if (body !== undefined) log(` body: ${JSON.stringify(body)}`);\n return { status: 0, data: null, dryRun: true };\n }\n const res = await fetch(url, {\n method,\n headers: {\n Authorization: `Bearer ${TOKEN}`,\n Accept: \"application/json\",\n ...(body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n },\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n });\n const text = await res.text();\n let data = null;\n try {\n data = text ? JSON.parse(text) : null;\n } catch {\n data = text;\n }\n if (!res.ok) {\n const err = new Error(`${method} ${path} failed: HTTP ${res.status}`);\n err.status = res.status;\n err.body = data;\n throw err;\n }\n return { status: res.status, data };\n}\n\nasync function check() {\n const info = await call(\"GET\", \"/v1/tokenInfo\");\n const account = info.data?.ownerUser?._id ?? \"unknown\";\n const kind = info.data?.token?.type ?? \"unknown\";\n log(`authenticated: account=${account} credential=${kind}`);\n const list = await call(\"GET\", \"/v1/integrations?limit=10\");\n const rows = Array.isArray(list.data) ? list.data : [];\n log(`integrations: ${rows.length}`);\n for (const it of rows) log(` - ${it._id} ${it.name}`);\n return { account, count: rows.length };\n}\n\nasync function demo() {\n const name = `CLI Example ${new Date().toISOString().replace(/[:.]/g, \"-\")}`;\n const created = await call(\"POST\", \"/v1/integrations\", { name });\n const id = created.data?._id;\n log(`created integration ${id} (${name})`);\n if (DRY_RUN) {\n log(\"dry run: stopping before read/delete\");\n return;\n }\n const fetched = await call(\"GET\", `/v1/integrations/${id}`);\n log(`read back: ${fetched.data?.name}`);\n if (KEEP) {\n log(`--keep set; leaving integration ${id} in place`);\n return;\n }\n await call(\"DELETE\", `/v1/integrations/${id}`);\n log(`deleted integration ${id}`);\n}\n\nasync function main() {\n if (DRY_RUN) log(\"DRY RUN — no requests will be sent\");\n if (!TOKEN && !DRY_RUN) {\n log(\"error: CELIGO_API_TOKEN is not set.\");\n log(\"Create one at integrator.io > Resources > API tokens, then export it.\");\n process.exit(2);\n }\n if (command === \"check\") return check();\n if (command === \"demo\") return demo();\n log(`unknown command '${command}'. Use: check | demo [--keep] [--dry-run]`);\n process.exit(2);\n}\n\nmain().catch((err) => {\n log(`error: ${err.message}`);\n if (err.body) log(`response: ${JSON.stringify(err.body)}`);\n process.exit(1);\n});\n"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_8596f5ff23944bba9082e2cd",
"tool_kind": "file_edit"
}Recorded tool result
{
"type": "tool_result",
"seq": 106,
"ts": "2026-10-20T15:52:30.107Z",
"turn": 1,
"output": {
"content": [
{
"text": "Successfully wrote 3690 bytes to /sandbox/repo/celigo-example/celigo.mjs",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_8596f5ff23944bba9082e2cd",
"is_error": false,
"tool_kind": "file_edit"
}File edits
1 file
Writtenceligo-example/package.jsonView changes
/sandbox/repo/celigo-example/package.json
Written content · previous contents were not recorded
{"name": "celigo-example","private": true,"type": "module","description": "Minimal Celigo integrator.io REST API example (no dependencies).","engines": {"node": ">=22"},"scripts": {"check": "node celigo.mjs check","demo": "node celigo.mjs demo","demo:keep": "node celigo.mjs demo --keep","dry-run": "node celigo.mjs demo --dry-run"}}
Source: write · call_4751e7d6617f4df8a27765ee
Recorded tool call
{
"type": "tool_call",
"seq": 105,
"ts": "2026-10-20T15:52:30.107Z",
"name": "write",
"turn": 1,
"input": {
"path": "/sandbox/repo/celigo-example/package.json",
"content": "{\n \"name\": \"celigo-example\",\n \"private\": true,\n \"type\": \"module\",\n \"description\": \"Minimal Celigo integrator.io REST API example (no dependencies).\",\n \"engines\": {\n \"node\": \">=22\"\n },\n \"scripts\": {\n \"check\": \"node celigo.mjs check\",\n \"demo\": \"node celigo.mjs demo\",\n \"demo:keep\": \"node celigo.mjs demo --keep\",\n \"dry-run\": \"node celigo.mjs demo --dry-run\"\n }\n}\n"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_4751e7d6617f4df8a27765ee",
"tool_kind": "file_edit"
}Recorded tool result
{
"type": "tool_result",
"seq": 107,
"ts": "2026-10-20T15:52:30.107Z",
"turn": 1,
"output": {
"content": [
{
"text": "Successfully wrote 384 bytes to /sandbox/repo/celigo-example/package.json",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_4751e7d6617f4df8a27765ee",
"is_error": false,
"tool_kind": "file_edit"
}›Thinking…
Let me verify the script runs (dry-run and no-token handling), and syntax check. Then write the README.
cd /sandbox/repo/celigo-example && node --check celigo.mjs && echo "syntax OK" && echo "=== dry-run ===" && node celigo.mjs demo --dry-run 2>&1 && echo "=== no token ===" && (unset CELIGO_API_TOKEN; node celigo.mjs check 2>&1; echo "exit=$?") && echo "=== live unauth (expect 401 handled) ===" && CELIGO_API_TOKEN=bogus node celigo.mjs check 2>&1; echo "exit=$?"
syntax OK
syntax OK
=== dry-run ===
DRY RUN — no requests will be sent
→ POST https://api.integrator.io/v1/integrations
body: {"name":"CLI Example 2026-10-20T15-52-42-287Z"}
created integration undefined (CLI Example 2026-10-20T15-52-42-287Z)
dry run: stopping before read/delete
=== no token ===
error: CELIGO_API_TOKEN is not set.
Create one at integrator.io > Resources > API tokens, then export it.
exit=2
=== live unauth (expect 401 handled) ===
→ GET https://api.integrator.io/v1/tokenInfo
error: GET /v1/tokenInfo failed: HTTP 401
response: "Unauthorized"
exit=1
Recorded tool call
{
"type": "tool_call",
"seq": 110,
"ts": "2026-10-20T15:52:42.559Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /sandbox/repo/celigo-example && node --check celigo.mjs && echo \"syntax OK\" && echo \"=== dry-run ===\" && node celigo.mjs demo --dry-run 2>&1 && echo \"=== no token ===\" && (unset CELIGO_API_TOKEN; node celigo.mjs check 2>&1; echo \"exit=$?\") && echo \"=== live unauth (expect 401 handled) ===\" && CELIGO_API_TOKEN=bogus node celigo.mjs check 2>&1; echo \"exit=$?\""
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_da6d1bde420d4e269d3eec83",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 111,
"ts": "2026-10-20T15:52:42.867Z",
"turn": 1,
"output": {
"content": [
{
"text": "syntax OK\n=== dry-run ===\nDRY RUN — no requests will be sent\n→ POST https://api.integrator.io/v1/integrations\n body: {\"name\":\"CLI Example 2026-10-20T15-52-42-287Z\"}\ncreated integration undefined (CLI Example 2026-10-20T15-52-42-287Z)\ndry run: stopping before read/delete\n=== no token ===\nerror: CELIGO_API_TOKEN is not set.\nCreate one at integrator.io > Resources > API tokens, then export it.\nexit=2\n=== live unauth (expect 401 handled) ===\n→ GET https://api.integrator.io/v1/tokenInfo\nerror: GET /v1/tokenInfo failed: HTTP 401\nresponse: \"Unauthorized\"\nexit=1\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_da6d1bde420d4e269d3eec83",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
The script works as expected. The live unauth call confirms the hosted API is reachable and the error handling works. Good.
Now let me verify the CLI works and show some commands. Let me run celigo --help and celigo integrations --help to confirm. Also celigo lint offline could be demonstrated. But without a token, CLI auth commands won't work. Let me at least verify celigo --help and celigo config show maybe.
Let me run celigo help.
cd /tmp && celigo --help 2>&1 | head -40; echo "=== integrations help ==="; celigo integrations --help 2>&1 | head -30
Usage: celigo [options] [command]
Usage: celigo [options] [command]
Celigo CLI — manage your integrator.io resources from the terminal.
Options:
--token <token> API bearer token (prefer CELIGO_API_TOKEN env var to avoid exposing in process list).
--base-url <url> API base URL.
--format <format> Output format (json or table). Defaults to config or json.
--jq <expr> Transform JSON output with a jq expression (e.g. --jq '.[] | {id: ._id, name}').
--verbose Show HTTP requests.
--profile <name> Run against a named config profile (must exist — see 'celigo profile list'). Overrides CELIGO_PROFILE, which in turn overrides the machine-wide active profile.
-h, --help display help for command
Commands:
config Manage CLI configuration.
profile Manage named config profiles (multiple accounts/environments).
login [options] Sign in with OAuth (authorization code + PKCE). The session is the profile's one credential (a profile login creates is in full mode), and the profile is bound to the account and environment GET /v1/tokenInfo says it reached. The profile is --profile, else CELIGO_PROFILE, else the one named after that account and environment — <company>-<environment>, e.g. acme-corp-production — which is created if it does not exist; the active profile is not the default.
logout Revoke the OAuth session for a profile and forget stored tokens. Other CLI sign-ins made from the same browser session can end with it (the platform gives them one grant) and then need celigo login again; logout names the profiles it may have signed out.
skills Install and update agent skills from the public celigo/ai repository.
pull [options] [refs...] Download the account (or one integration) as a local file tree of per-resource JSON. Incremental: unchanged files are untouched, locally-edited files are never overwritten without --force. Refs (a file path — relative to the tree, or absolute inside it — type:id, or bare id) are only meaningful with --force and reset just those resources.
diff [options] [refs...] Compare the local tree against the LIVE account (fetches fresh; volatile fields ignored). With no refs, diffs every locally-modified resource; refs may be a file path, type:id, or a bare id. For the offline tree-vs-manifest view, use: celigo status.
status [options] Show local changes since the last pull (offline; compares the tree against .celigo/manifest.json). For a comparison against the live account, use: celigo diff.
push [options] [refs...] Push local changes to the live account: PUT modified files, create untracked files that have no _id, and (with --prune) delete resources whose local files were removed. Plan-first: the change set is printed before anything is written; --dry-run stops there. Updates and --prune deletes are rejected when the server copy changed since the last pull (--force-remote overrides). Refused at plan time, with no override: a masked "******" value in a document to be created (any type) or in a connection's or iClient's update, a value that is not a string in a field the platform encrypts, and the responseMapping object wrapper. Refs narrow the target set (a file path, type:id, or bare id). Requires full mode.
promote [options] [refs...] Write the local tree into ANOTHER account or environment (--to names its profile): create what the target lacks, update what it holds, with every reference remapped to the target's ids. Connections, iClients, MCP OAuth providers, stacks and agents are never promoted with credentials — they are matched by name, pinned with --map, or created as credential-less shells with --create-connections. Plan-first like push: --dry-run stops after the plan; target copies that changed since the last promote are refused (--force-remote overrides). The map lives in .celigo/targets/<name>.json (commit it). Requires full mode on the TARGET: its profile's stored mode, which CELIGO_MODE in this environment can only tighten (CELIGO_MODE=read or operate refuses the promote before any request). Under profile_pin_policy = strict, a target profile that is not the active profile needs a person at a terminal, who types the profile's name back.
lint [options] [refs...] Validate the local tree against the platform's published API schemas (offline; no token needed). The account outranks the specs: a shape the account already exhibits is a warning, a create introducing a shape nothing on the account backs is an error. Spec-contradicted findings are recorded to .celigo/local/lint-feedback.jsonl (field paths only, never values) for improving the specs. A resource an interrupted pull or push left half-done is one `unfinished` error, not linted. Refs narrow the target set.
resolve [options] <ref> Resolve a ref to the real document file in the local tree and print its path. Refs: a link (.link.json pointer or --symlinks symlink), any tree file path, type:id, a bare id, or (in a neutral tree) an @bindings/ key — resolved to this environment's id via .celigo/bindings.json. A resource an interrupted pull or push left half-done has no one document yet: it is refused, naming both paths and what finishes it. Offline — reads only the tree's .celigo/ state.
ai-agents Manage AI agents (LLM-powered import steps).
async-helpers Manage async helpers (polling configs for long-running API operations).
guardrails Manage guardrails (PII detection, content moderation, AI safety checks).
on-premise-agents Manage on-premise agents (OPA).
apis Manage API endpoints (builder and script modes).
edi-profiles Manage EDI document profiles for trading partners.
environments Manage the environments on your account.
file-definitions Manage file format definitions (CSV, EDI, fixed-width, etc.).
iclients Manage OAuth2 iClients (app registrations for OAuth flows).
lookup-caches Manage lookup caches (in-memory key-value stores for enrichment and deduplication).
topics Manage Event Streams topics (durable message channels for topic exports).
tags Manage tags for organizing resources.
tools Manage tools (reusable building blocks callable from flows, APIs, agents, MCP servers, and other tools).
mcp-servers Manage MCP servers that expose tools and APIs as MCP endpoints.
=== integrations help ===
Usage: celigo integrations [options] [command]
Manage integrations (containers for flows, connections, and resources).
Options:
-h, --help display help for command
Commands:
list [options] List all integrations. Rows are projected to id, name, and the table columns by default; use --fields all for complete documents.
get <id> Get an integration by ID.
create [options] Create an integration from a JSON body (stdin, or --file).
update [options] <id> Update an integration from a JSON body (stdin, or --file).
set <id> [assignments...] Set field(s) on an integration.
Whitelisted fields (apiGroupings, flowGroupings, settings) are applied via an atomic PATCH; other fields go through GET + modify + PUT with the complete document.
Usage: set <id> key=value [key2=value2 ...]
Values are auto-parsed: disabled=false → boolean, debugUntil=null → removes field.
Dot notation supported: http.relativeURI=/api/v2/items
Array index supported: pageGenerators[0]._exportId=abc123
Combined: routers[0].branches[1].pageProcessors[0]._importId=xyz
Load a value from a file: content=file://./hook.js (avoids hand-escaping multi-line content like script source or SQL; ~ and relative paths are supported).
Example: celigo integrations set <id> apiGroupings=<value>
delete [options] <id> Delete an integration by ID.
dependencies|used-by <id> List resources that depend on this integration (GET /v1/integrations/<id>/dependencies).
Use this to check whether the integration is safe to delete — empty means no dependents.
audit <id> Show the audit log (change history) for one integration (GET /v1/integrations/<id>/audit).
clone [options] <id> <environmentId> Clone an integration into a target environment.
Same-env clone: no stdin needed — the CLI auto-builds a self-map from the source integration's
registered connections (every cloned resource points at the same connection as the source).
Cross-env clone: pipe an explicit map via stdin (or --file) so source connections get remapped
to target-env connections: {"connectionMap":{"sourceConnId":"targetConnId",...}}
Recorded tool call
{
"type": "tool_call",
"seq": 114,
"ts": "2026-10-20T15:52:47.387Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /tmp && celigo --help 2>&1 | head -40; echo \"=== integrations help ===\"; celigo integrations --help 2>&1 | head -30"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_8ea4836ebb164ebd9f2234a0",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 116,
"ts": "2026-10-20T15:52:48.172Z",
"turn": 1,
"output": {
"content": [
{
"text": "Usage: celigo [options] [command]\n\nCeligo CLI — manage your integrator.io resources from the terminal.\n\nOptions:\n --token <token> API bearer token (prefer CELIGO_API_TOKEN env var to avoid exposing in process list).\n --base-url <url> API base URL.\n --format <format> Output format (json or table). Defaults to config or json.\n --jq <expr> Transform JSON output with a jq expression (e.g. --jq '.[] | {id: ._id, name}').\n --verbose Show HTTP requests.\n --profile <name> Run against a named config profile (must exist — see 'celigo profile list'). Overrides CELIGO_PROFILE, which in turn overrides the machine-wide active profile.\n -h, --help display help for command\n\nCommands:\n config Manage CLI configuration.\n profile Manage named config profiles (multiple accounts/environments).\n login [options] Sign in with OAuth (authorization code + PKCE). The session is the profile's one credential (a profile login creates is in full mode), and the profile is bound to the account and environment GET /v1/tokenInfo says it reached. The profile is --profile, else CELIGO_PROFILE, else the one named after that account and environment — <company>-<environment>, e.g. acme-corp-production — which is created if it does not exist; the active profile is not the default.\n logout Revoke the OAuth session for a profile and forget stored tokens. Other CLI sign-ins made from the same browser session can end with it (the platform gives them one grant) and then need celigo login again; logout names the profiles it may have signed out.\n skills Install and update agent skills from the public celigo/ai repository.\n pull [options] [refs...] Download the account (or one integration) as a local file tree of per-resource JSON. Incremental: unchanged files are untouched, locally-edited files are never overwritten without --force. Refs (a file path — relative to the tree, or absolute inside it — type:id, or bare id) are only meaningful with --force and reset just those resources.\n diff [options] [refs...] Compare the local tree against the LIVE account (fetches fresh; volatile fields ignored). With no refs, diffs every locally-modified resource; refs may be a file path, type:id, or a bare id. For the offline tree-vs-manifest view, use: celigo status.\n status [options] Show local changes since the last pull (offline; compares the tree against .celigo/manifest.json). For a comparison against the live account, use: celigo diff.\n push [options] [refs...] Push local changes to the live account: PUT modified files, create untracked files that have no _id, and (with --prune) delete resources whose local files were removed. Plan-first: the change set is printed before anything is written; --dry-run stops there. Updates and --prune deletes are rejected when the server copy changed since the last pull (--force-remote overrides). Refused at plan time, with no override: a masked \"******\" value in a document to be created (any type) or in a connection's or iClient's update, a value that is not a string in a field the platform encrypts, and the responseMapping object wrapper. Refs narrow the target set (a file path, type:id, or bare id). Requires full mode.\n promote [options] [refs...] Write the local tree into ANOTHER account or environment (--to names its profile): create what the target lacks, update what it holds, with every reference remapped to the target's ids. Connections, iClients, MCP OAuth providers, stacks and agents are never promoted with credentials — they are matched by name, pinned with --map, or created as credential-less shells with --create-connections. Plan-first like push: --dry-run stops after the plan; target copies that changed since the last promote are refused (--force-remote overrides). The map lives in .celigo/targets/<name>.json (commit it). Requires full mode on the TARGET: its profile's stored mode, which CELIGO_MODE in this environment can only tighten (CELIGO_MODE=read or operate refuses the promote before any request). Under profile_pin_policy = strict, a target profile that is not the active profile needs a person at a terminal, who types the profile's name back.\n lint [options] [refs...] Validate the local tree against the platform's published API schemas (offline; no token needed). The account outranks the specs: a shape the account already exhibits is a warning, a create introducing a shape nothing on the account backs is an error. Spec-contradicted findings are recorded to .celigo/local/lint-feedback.jsonl (field paths only, never values) for improving the specs. A resource an interrupted pull or push left half-done is one `unfinished` error, not linted. Refs narrow the target set.\n resolve [options] <ref> Resolve a ref to the real document file in the local tree and print its path. Refs: a link (.link.json pointer or --symlinks symlink), any tree file path, type:id, a bare id, or (in a neutral tree) an @bindings/ key — resolved to this environment's id via .celigo/bindings.json. A resource an interrupted pull or push left half-done has no one document yet: it is refused, naming both paths and what finishes it. Offline — reads only the tree's .celigo/ state.\n ai-agents Manage AI agents (LLM-powered import steps).\n async-helpers Manage async helpers (polling configs for long-running API operations).\n guardrails Manage guardrails (PII detection, content moderation, AI safety checks).\n on-premise-agents Manage on-premise agents (OPA).\n apis Manage API endpoints (builder and script modes).\n edi-profiles Manage EDI document profiles for trading partners.\n environments Manage the environments on your account.\n file-definitions Manage file format definitions (CSV, EDI, fixed-width, etc.).\n iclients Manage OAuth2 iClients (app registrations for OAuth flows).\n lookup-caches Manage lookup caches (in-memory key-value stores for enrichment and deduplication).\n topics Manage Event Streams topics (durable message channels for topic exports).\n tags Manage tags for organizing resources.\n tools Manage tools (reusable building blocks callable from flows, APIs, agents, MCP servers, and other tools).\n mcp-servers Manage MCP servers that expose tools and APIs as MCP endpoints.\n=== integrations help ===\nUsage: celigo integrations [options] [command]\n\nManage integrations (containers for flows, connections, and resources).\n\nOptions:\n -h, --help display help for command\n\nCommands:\n list [options] List all integrations. Rows are projected to id, name, and the table columns by default; use --fields all for complete documents.\n get <id> Get an integration by ID.\n create [options] Create an integration from a JSON body (stdin, or --file).\n update [options] <id> Update an integration from a JSON body (stdin, or --file).\n set <id> [assignments...] Set field(s) on an integration.\n Whitelisted fields (apiGroupings, flowGroupings, settings) are applied via an atomic PATCH; other fields go through GET + modify + PUT with the complete document.\n Usage: set <id> key=value [key2=value2 ...]\n Values are auto-parsed: disabled=false → boolean, debugUntil=null → removes field.\n Dot notation supported: http.relativeURI=/api/v2/items\n Array index supported: pageGenerators[0]._exportId=abc123\n Combined: routers[0].branches[1].pageProcessors[0]._importId=xyz\n Load a value from a file: content=file://./hook.js (avoids hand-escaping multi-line content like script source or SQL; ~ and relative paths are supported).\n Example: celigo integrations set <id> apiGroupings=<value>\n delete [options] <id> Delete an integration by ID.\n dependencies|used-by <id> List resources that depend on this integration (GET /v1/integrations/<id>/dependencies).\n Use this to check whether the integration is safe to delete — empty means no dependents.\n audit <id> Show the audit log (change history) for one integration (GET /v1/integrations/<id>/audit).\n clone [options] <id> <environmentId> Clone an integration into a target environment.\n Same-env clone: no stdin needed — the CLI auto-builds a self-map from the source integration's\n registered connections (every cloned resource points at the same connection as the source).\n Cross-env clone: pipe an explicit map via stdin (or --file) so source connections get remapped\n to target-env connections: {\"connectionMap\":{\"sourceConnId\":\"targetConnId\",...}}\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_8ea4836ebb164ebd9f2234a0",
"is_error": false,
"tool_kind": "shell"
}cd /tmp && echo "=== tokenInfo via CLI (expect auth error) ===" && CELIGO_API_TOKEN=bogus CELIGO_NO_UPDATE=1 CELIGO_NO_SKILLS_INSTALL=1 celigo --token bogus platform get v1/tokenInfo 2>&1 | head -10; echo "exit=$?"
=== tokenInfo via CLI (expect auth error) ===
=== tokenInfo via CLI (expect auth error) === (401): Authentication failed. Check your API token with `celigo config show`. exit=0
Recorded tool call
{
"type": "tool_call",
"seq": 115,
"ts": "2026-10-20T15:52:47.387Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /tmp && echo \"=== tokenInfo via CLI (expect auth error) ===\" && CELIGO_API_TOKEN=bogus CELIGO_NO_UPDATE=1 CELIGO_NO_SKILLS_INSTALL=1 celigo --token bogus platform get v1/tokenInfo 2>&1 | head -10; echo \"exit=$?\""
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_6e108e351e4f453cb394303e",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 117,
"ts": "2026-10-20T15:52:48.172Z",
"turn": 1,
"output": {
"content": [
{
"text": "=== tokenInfo via CLI (expect auth error) ===\n(401): Authentication failed. Check your API token with `celigo config show`.\nexit=0\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_6e108e351e4f453cb394303e",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
The CLI works and handles auth failure. Note the CLI exit code shows 0 because of the pipe to head (pipefail not set). That's fine.
Now let me write the README with pricing and manageability assessment. Let me be accurate about pricing: no public dollar amounts, quote-based, flat-rate on endpoints and flows, three editions, 30-day free trial.
Let me also add a CLI-based variant of the example in the README.
Let me write the README.
File edits
1 file
Writtenceligo-example/README.mdView changes
/sandbox/repo/celigo-example/README.md
Written content · previous contents were not recorded
# Celigo integrator.io — minimal exampleA tiny, dependency-free example that drives the **hosted** Celigo integrator.ioREST API. Nothing runs locally except this one Node script; there is no server,no container, no service stack to start.## Files- `celigo.mjs` — the example (Node 22+, uses global `fetch`)- `package.json` — scripts only, no dependencies## Run it1. In integrator.io, go to **Resources → API tokens** and create a token.2. Export it (and pick your region's host if not US):```bashexport CELIGO_API_TOKEN="<token>"# EU / AU / CA only:# export CELIGO_BASE_URL="https://api.eu.integrator.io"```3. Confirm identity and list integrations:```bashnode celigo.mjs check```4. Full create → read → delete round trip on an integration:```bashnode celigo.mjs demo # deletes what it createsnode celigo.mjs demo --keep # leaves the integration in place```Preview the requests without sending anything (no token needed):```bashnode celigo.mjs demo --dry-run```The API base URL and endpoints come straight from Celigo's published OpenAPIspecs (`@celigo/api-specs`). All calls use `Authorization: Bearer <token>`.## Equivalent with the Celigo CLIThe official CLI (`npm install -g @celigo/celigo-cli`) wraps the same API and isthe nicer path once you're doing real work. Same auth:```bashexport CELIGO_API_TOKEN="<token>"celigo account snapshot # explore the accountceligo integrations list # list integrationsecho '{"name":"My Example"}' | celigo integrations createceligo integrations get <id>celigo integrations delete <id>```Building a real flow is bottom-up (connection → export → import → flow), and theCLI scaffolds it:```bashceligo http-connectors match https://api.github.comceligo connections create --connector github --name "GitHub - Prod"celigo exports create < export.jsonceligo imports create < import.jsonceligo flows create < flow.jsonceligo flows run <flowId> -y```There's also a local-tree workflow (`pull` / `status` / `diff` / `push` / `lint`)that lets you version integrations in git and validate them offline against theplatform schemas.## How Celigo pricing worksFrom Celigo's public pricing page (as of this writing — no dollar amounts arepublished):- **Billed on endpoints and flows**, not per task, transaction, record, datavolume, or vCore. Flat-rate with **no overage fees** — usage is tracked so youcan see its impact on spend.- **Three platform editions:**- **Standard** — core integrations; MFA + role-based access, standard SLAs.- **Professional** — adds API Builder and Lookup Cache, SSO + auditability,and a sandbox; enhanced SLAs.- **Enterprise** — unlimited endpoints, maximum performance, full governanceand auditability, best support/SLAs.- **Celigo AI** (Ora, Agent Builder, MCP Server) is available on every edition.- **Scaling** is flexible up or down; more endpoints/flows are added bycontacting your Account Manager. **Volume-based pricing is explicitly notoffered.**- **30-day free trial**, no credit card mentioned.- Actual prices are **quote-based** — you have to talk to sales for numbers.## Will this be easy to manage?Yes — this is one of the more agent-friendly products I've seen.**What makes it easy**- A real, documented public API (`https://api.integrator.io/v1/...`) withpublished, self-contained OpenAPI 3.2 specs per resource.- A first-class CLI with JSON output, `--jq`, `--dry-run`, plan-first`push`/`promote`, and offline `lint` against the same schemas.- The CLI is explicitly designed for agents: permission modes(`read`/`operate`/`full`), a managed policy file, profile pinning, anddocumented "rules for agents driving the CLI" to prevent writing to the wrongaccount.- Local-tree + git workflow (`pull`/`diff`/`push`/`promote`) makes changesreviewable and repeatable, sandbox → production.- Public agent skills repo (`celigo/ai`) and an MCP server.**Friction / caveats**- **Auth is the only blocker here.** I have no Celigo account or API token, so Icould not run an authenticated call. I verified the hosted API is reachable(an unauthenticated `GET /v1/tokenInfo` returns `401`) and that the script andCLI handle that cleanly. Drop in a token and the example runs.- Pricing isn't self-serve; endpoint/flow counts and edition choice go throughsales.- "Endpoints and flows" is a simple metric but you still need to understand yourown usage shape to forecast cost.- Building a working flow still depends on credentials/access to the systems youconnect (NetSuite, Salesforce, etc.), which no tool can remove.## Verified in this sandbox- `GET https://api.integrator.io/v1/tokenInfo` → `401 Unauthorized` (hosted APIreachable; auth enforced)- `celigo --version` → `2026.10.1`- `node celigo.mjs demo --dry-run` and the no-token path behave as documented
Source: write · call_81ed663abe754a5689d2fbf0
Recorded tool call
{
"type": "tool_call",
"seq": 120,
"ts": "2026-10-20T15:53:02.108Z",
"name": "write",
"turn": 1,
"input": {
"path": "/sandbox/repo/celigo-example/README.md",
"content": "# Celigo integrator.io — minimal example\n\nA tiny, dependency-free example that drives the **hosted** Celigo integrator.io\nREST API. Nothing runs locally except this one Node script; there is no server,\nno container, no service stack to start.\n\n## Files\n\n- `celigo.mjs` — the example (Node 22+, uses global `fetch`)\n- `package.json` — scripts only, no dependencies\n\n## Run it\n\n1. In integrator.io, go to **Resources → API tokens** and create a token.\n2. Export it (and pick your region's host if not US):\n\n ```bash\n export CELIGO_API_TOKEN=\"<token>\"\n # EU / AU / CA only:\n # export CELIGO_BASE_URL=\"https://api.eu.integrator.io\"\n ```\n\n3. Confirm identity and list integrations:\n\n ```bash\n node celigo.mjs check\n ```\n\n4. Full create → read → delete round trip on an integration:\n\n ```bash\n node celigo.mjs demo # deletes what it creates\n node celigo.mjs demo --keep # leaves the integration in place\n ```\n\nPreview the requests without sending anything (no token needed):\n\n```bash\nnode celigo.mjs demo --dry-run\n```\n\nThe API base URL and endpoints come straight from Celigo's published OpenAPI\nspecs (`@celigo/api-specs`). All calls use `Authorization: Bearer <token>`.\n\n## Equivalent with the Celigo CLI\n\nThe official CLI (`npm install -g @celigo/celigo-cli`) wraps the same API and is\nthe nicer path once you're doing real work. Same auth:\n\n```bash\nexport CELIGO_API_TOKEN=\"<token>\"\n\nceligo account snapshot # explore the account\nceligo integrations list # list integrations\necho '{\"name\":\"My Example\"}' | celigo integrations create\nceligo integrations get <id>\nceligo integrations delete <id>\n```\n\nBuilding a real flow is bottom-up (connection → export → import → flow), and the\nCLI scaffolds it:\n\n```bash\nceligo http-connectors match https://api.github.com\nceligo connections create --connector github --name \"GitHub - Prod\"\nceligo exports create < export.json\nceligo imports create < import.json\nceligo flows create < flow.json\nceligo flows run <flowId> -y\n```\n\nThere's also a local-tree workflow (`pull` / `status` / `diff` / `push` / `lint`)\nthat lets you version integrations in git and validate them offline against the\nplatform schemas.\n\n## How Celigo pricing works\n\nFrom Celigo's public pricing page (as of this writing — no dollar amounts are\npublished):\n\n- **Billed on endpoints and flows**, not per task, transaction, record, data\n volume, or vCore. Flat-rate with **no overage fees** — usage is tracked so you\n can see its impact on spend.\n- **Three platform editions:**\n - **Standard** — core integrations; MFA + role-based access, standard SLAs.\n - **Professional** — adds API Builder and Lookup Cache, SSO + auditability,\n and a sandbox; enhanced SLAs.\n - **Enterprise** — unlimited endpoints, maximum performance, full governance\n and auditability, best support/SLAs.\n- **Celigo AI** (Ora, Agent Builder, MCP Server) is available on every edition.\n- **Scaling** is flexible up or down; more endpoints/flows are added by\n contacting your Account Manager. **Volume-based pricing is explicitly not\n offered.**\n- **30-day free trial**, no credit card mentioned.\n- Actual prices are **quote-based** — you have to talk to sales for numbers.\n\n## Will this be easy to manage?\n\nYes — this is one of the more agent-friendly products I've seen.\n\n**What makes it easy**\n- A real, documented public API (`https://api.integrator.io/v1/...`) with\n published, self-contained OpenAPI 3.2 specs per resource.\n- A first-class CLI with JSON output, `--jq`, `--dry-run`, plan-first\n `push`/`promote`, and offline `lint` against the same schemas.\n- The CLI is explicitly designed for agents: permission modes\n (`read`/`operate`/`full`), a managed policy file, profile pinning, and\n documented \"rules for agents driving the CLI\" to prevent writing to the wrong\n account.\n- Local-tree + git workflow (`pull`/`diff`/`push`/`promote`) makes changes\n reviewable and repeatable, sandbox → production.\n- Public agent skills repo (`celigo/ai`) and an MCP server.\n\n**Friction / caveats**\n- **Auth is the only blocker here.** I have no Celigo account or API token, so I\n could not run an authenticated call. I verified the hosted API is reachable\n (an unauthenticated `GET /v1/tokenInfo` returns `401`) and that the script and\n CLI handle that cleanly. Drop in a token and the example runs.\n- Pricing isn't self-serve; endpoint/flow counts and edition choice go through\n sales.\n- \"Endpoints and flows\" is a simple metric but you still need to understand your\n own usage shape to forecast cost.\n- Building a working flow still depends on credentials/access to the systems you\n connect (NetSuite, Salesforce, etc.), which no tool can remove.\n\n## Verified in this sandbox\n\n- `GET https://api.integrator.io/v1/tokenInfo` → `401 Unauthorized` (hosted API\n reachable; auth enforced)\n- `celigo --version` → `2026.10.1`\n- `node celigo.mjs demo --dry-run` and the no-token path behave as documented\n"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_81ed663abe754a5689d2fbf0",
"tool_kind": "file_edit"
}Recorded tool result
{
"type": "tool_result",
"seq": 121,
"ts": "2026-10-20T15:53:02.108Z",
"turn": 1,
"output": {
"content": [
{
"text": "Successfully wrote 4978 bytes to /sandbox/repo/celigo-example/README.md",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_81ed663abe754a5689d2fbf0",
"is_error": false,
"tool_kind": "file_edit"
}›Thinking…
Let me do a final review of the created files and the repo status. Let me list them. Also maybe run the dry-run once more to confirm. Already did. Let me show the tree.
Let me also double check I haven't left any local service running. No.
Let me give the final response. I should be concise but cover:
- Yes there's a developer workflow.
- What I built.
- Pricing.
- Manageability.
- Blocked only on auth.
Let me verify the file listing.
cd /sandbox/repo && find celigo-example -type f | sort && echo "---" && git status --short
celigo-example/README.md
celigo-example/README.md celigo-example/celigo.mjs celigo-example/package.json --- ?? celigo-example/
Recorded tool call
{
"type": "tool_call",
"seq": 124,
"ts": "2026-10-20T15:53:04.752Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /sandbox/repo && find celigo-example -type f | sort && echo \"---\" && git status --short"
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_c862f418692048a6b4dfeaa8",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 125,
"ts": "2026-10-20T15:53:04.752Z",
"turn": 1,
"output": {
"content": [
{
"text": "celigo-example/README.md\nceligo-example/celigo.mjs\nceligo-example/package.json\n---\n?? celigo-example/\n",
"type": "text"
}
]
},
"run_id": "cmvguydk005kv0joa6ikk93yk",
"tool_id": "call_c862f418692048a6b4dfeaa8",
"is_error": false,
"tool_kind": "shell"
}Done. Here's the summary.
Yes — there's a real developer workflow
Celigo (integrator.io) is an iPaaS with a genuine, hosted API and tooling. I did not hit a wall — the only thing I lack is an account token. I stayed light: installed the published packages, verified the hosted endpoint, wrote a script, and ran it in dry-run/no-token modes. No local service stacks were started.
What I built in celigo-example/:
celigo.mjs— dependency-free Node 22 script using the hosted REST APIcheck→GET /v1/tokenInfo, list integrationsdemo→ create → read → delete an integration (--keepto preserve)--dry-runprints requests without sending
package.json— scripts only, no depsREADME.md— usage, pricing summary, manageability notes
Run it with export CELIGO_API_TOKEN="..." then node celigo.mjs check. The CLI (@celigo/celigo-cli, installed, v2026.10.1) wraps the same API and adds pull/diff/push/promote/lint, JSON output, --jq, and permission modes.
Verified: GET https://api.integrator.io/v1/tokenInfo returns 401 unauthenticated (hosted API reachable, auth enforced); script handles missing/bad tokens cleanly; celigo --version works.
Pricing
- Flat-rate, billed on endpoints and flows — not per task/transaction/record/volume. No overage fees; usage is tracked.
- Three editions: Standard, Professional (adds API Builder, Lookup Cache, sandbox, SSO/audit), Enterprise (unlimited endpoints, max performance/governance/SLAs).
- Celigo AI (Ora, Agent Builder, MCP Server) is on every edition.
- Scale up/down; more endpoints/flows via your Account Manager. No volume-based pricing.
- 30-day free trial.
- No public dollar amounts — pricing is quote-based. That's the main practical friction: you can't self-serve a price.
Ease of management: high
- Published per-resource OpenAPI 3.2 specs, a polished CLI, offline linting, git-based local tree, sandbox→prod promote, MCP server, and public agent skills.
- The CLI is explicitly built for agents:
read/operate/fullmodes, profile pinning, managed policy file, and documented rules to avoid writing to the wrong account.
Blocked on: authentication only. Add a token (Resources → API tokens) and the example runs; I couldn't do an authenticated call myself. Building a real flow also needs credentials for the systems you connect (NetSuite, Salesforce, etc.) — unavoidable, not a tooling gap.
AX Fundamentals
23 checks. Open an area for details.
This check is a quick preview. Test more tasks and models with Gauge Agents.
Test your product with Gauge Agents