One command = one API
Semantic hierarchy (entities, packages, permissions…) — no URL guessing for agents or scripts.
CLI · CI/CD · AI agents
Dedicated subcommands, machine-readable --json, and service-principal or API-key auth —
the same /api/v1 contract as the UI. Built for CI pipelines and LLM tool loops.
Semantic hierarchy (entities, packages, permissions…) — no URL guessing for agents or scripts.
--jsonHTTP status and body in one JSON object on stdout. Parse with jq — no regex over human text.
Interactive browser login, or service principal / API key for long-running agents and hands-off CI.
Calls the same /api/v1 — one source of truth for business rules, approvals, and permissions.
api escape hatchMethod, path, query, and body from file when a dedicated subcommand is not enough.
Server Swagger describes schemas and stable error codes — a clear map for tool-calling agents.
whoami --json--json for scripts and agents# Instance config and identity
entihub config set --base-url https://your-enti-hub/ \
--tenant-id <tenant> --client-id <app-id>
entihub login
# Verify identity (also useful in scripts after login)
entihub whoami --json
# Agent / CI with service principal: set ENTIHUB_CLIENT_SECRET then e.g.
entihub entities list --json
Build an entity package in CI, gate on health and JSON status, deploy to staging or production with a service principal.
entihub packages deploy … --json and assert statusCodeENTIHUB_BASE_URL — e.g. https://mdm.staging.contoso.comENTIHUB_TENANT_ID, ENTIHUB_CLIENT_ID, ENTIHUB_CLIENT_SECRET — Entra app with MDM admin rolesENTIHUB_SCOPE — typically api://<your-api-app-id>/.defaultENTIHUB_API_KEY — alternative CI auth via X-API-KeyCommit something like mdm-config/export-package.json — omit entityStorageKeys to export all entities.
{
"entityStorageKeys": ["Customer", "Vendor", "Region"],
"autoIncludeDependencies": true,
"includePermissions": true,
"includeWebhooks": true,
"includeSeed": false
}
name: Promote MDM package
on:
push:
branches: [main]
paths: ['mdm-config/**', '.github/workflows/mdm-promote.yml']
jobs:
promote:
runs-on: ubuntu-latest
environment: staging-mdm
steps:
- uses: actions/checkout@v4
- name: Install entihub CLI
run: |
curl -sSL -o entihub.tgz "https://entihub.com/api/cli/download/ubuntu"
tar xzf entihub.tgz && sudo mv entihub /usr/local/bin/
- name: Smoke — API readiness
env:
ENTIHUB_BASE_URL: ${{ secrets.ENTIHUB_BASE_URL }}
run: |
curl -fsS "$ENTIHUB_BASE_URL/health/ready" | jq -e '.status == "ready" or . == {}'
- name: Export package (ZIP is binary — use curl -o, not entihub --json)
env:
ENTIHUB_BASE_URL: ${{ secrets.ENTIHUB_BASE_URL }}
ENTIHUB_TENANT_ID: ${{ secrets.ENTIHUB_TENANT_ID }}
ENTIHUB_CLIENT_ID: ${{ secrets.ENTIHUB_CLIENT_ID }}
ENTIHUB_CLIENT_SECRET: ${{ secrets.ENTIHUB_CLIENT_SECRET }}
ENTIHUB_SCOPE: ${{ secrets.ENTIHUB_SCOPE }}
run: |
TOKEN=$(curl -sS -X POST "https://login.microsoftonline.com/${ENTIHUB_TENANT_ID}/oauth2/v2.0/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "client_id=${ENTIHUB_CLIENT_ID}" \
--data-urlencode "client_secret=${ENTIHUB_CLIENT_SECRET}" \
--data-urlencode "grant_type=client_credentials" \
--data-urlencode "scope=${ENTIHUB_SCOPE}" | jq -er .access_token)
curl -fsS -X POST "${ENTIHUB_BASE_URL}/api/v1/packages/export" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
--data-binary @mdm-config/export-package.json \
-o mdm-package.zip
file mdm-package.zip | grep -q ZIP
- name: Export package with API key (alternative to OAuth)
env:
ENTIHUB_BASE_URL: ${{ secrets.ENTIHUB_BASE_URL }}
ENTIHUB_API_KEY: ${{ secrets.ENTIHUB_API_KEY }}
run: |
curl -fsS -X POST "${ENTIHUB_BASE_URL}/api/v1/packages/export" \
-H "X-API-Key: ${ENTIHUB_API_KEY}" \
-H "Content-Type: application/json" \
--data-binary @mdm-config/export-package.json \
-o mdm-package.zip
file mdm-package.zip | grep -q ZIP
- name: Deploy package to target
env:
ENTIHUB_BASE_URL: ${{ secrets.ENTIHUB_BASE_URL }}
ENTIHUB_TENANT_ID: ${{ secrets.ENTIHUB_TENANT_ID }}
ENTIHUB_CLIENT_ID: ${{ secrets.ENTIHUB_CLIENT_ID }}
ENTIHUB_CLIENT_SECRET: ${{ secrets.ENTIHUB_CLIENT_SECRET }}
run: |
entihub packages deploy --file mdm-package.zip \
--overwrite-existing true \
--import-permissions true \
--import-webhooks true \
--import-seed false \
--json > deploy-result.json
jq -e '.statusCode >= 200 and .statusCode < 300' deploy-result.json
jq '.body' deploy-result.json
- name: Post-deploy — list entities
env:
ENTIHUB_BASE_URL: ${{ secrets.ENTIHUB_BASE_URL }}
ENTIHUB_TENANT_ID: ${{ secrets.ENTIHUB_TENANT_ID }}
ENTIHUB_CLIENT_ID: ${{ secrets.ENTIHUB_CLIENT_ID }}
ENTIHUB_CLIENT_SECRET: ${{ secrets.ENTIHUB_CLIENT_SECRET }}
run: entihub entities summary --json | jq '.body | fromjson'
packages/deploy returns JSON — use entihub … --json + jq.
packages/export returns a ZIP — write bytes with curl -o.
Most CLI calls can also use ENTIHUB_API_KEY / --api-key.
Give the model a closed set of tools, deterministic arguments, and structured results.
Map each tool to one entihub call with --json; parse statusCode and body for the next step.
mdm_list_entities → entihub entities list --json
mdm_get_entity_yaml → entihub entities yaml <name> --json
mdm_validate_yaml → entihub entities parse-yaml --file <path> --json
mdm_deploy_entity → entihub entities deploy <name> --json
mdm_list_data → entihub entities data list <name> --take 20 --json
mdm_search_data → entihub search data --q <terms> [--take N] [--include-refs] --json
mdm_raw_api → entihub api GET|POST|PUT|DELETE <path> [--body file] [--query k=v]
# Auth option: API key mode (X-API-Key)
export ENTIHUB_API_KEY=<secret>
# Step A — discover entities (agent picks Customer from summary)
entihub entities summary --json > step_a.json
# Step B — fetch sample rows for fuzzy match input
entihub entities data list Customer --take 50 --json > step_b.json
# Step C — call similarity API (file body)
cat > similar-req.json <<'EOF'
{ "values": { "name": "Acme Corp", "code": "ACM-01" } }
EOF
entihub entities data similar Customer --file similar-req.json --json > step_c.json
# Step D — optional write (human-approved or policy-gated)
entihub entities data update Customer <record-guid> --file patch.json --json
You manage EntiHub via the entihub CLI only. Each tool call must use --json.
After every command, parse stdout as JSON: check statusCode (2xx = success).
The "body" field may be a JSON string or raw text — parse accordingly.
Never invent entity names; call mdm_list_entities first.
For bulk changes, prefer packages or bulk import endpoints; do not loop
hundreds of single-row updates without explicit user approval.
Use mdm_raw_api only when no dedicated subcommand exists.
Wire this to Cursor, Copilot, LangGraph, or any agent runtime: execute the shell command the model emits and feed the JSON result back into the conversation.
Works with any EntiHub tier · same API as the UI · Features · Pricing