generated: '2026-08-30' method: searched source: https://closedloop.sh/docs/account/api-keys, https://closedloop.sh/docs/api-reference/conventions, https://closedloop.sh/docs/account/mcp-service-clients, and live OAuth metadata probes; baseline derived from openapi/closedloop-public-api-openapi.yaml docs: https://closedloop.sh/docs/account/api-keys summary: types: - apiKey - oauth2 api_key_in: - header note: >- ClosedLoop AI runs THREE separate credential systems that are deliberately not interchangeable. The published OpenAPI declares only the first of them, which is why a spec-only derivation understates this provider's auth posture. credential_systems: - id: rest-api-key surface: /v1 REST API scheme: ApiKeyAuth declared_in_openapi: true - id: mcp-oauth surface: MCP servers (mcp.closedloop.sh, eu.mcp.closedloop.sh, api.closedloop.sh/mcp) declared_in_openapi: false - id: webhook-signing-secret surface: inbound webhook ingestion declared_in_openapi: false schemes: - name: ApiKeyAuth type: apiKey in: header parameter: X-API-Key alias_parameter: apikey description: |- Team-scoped API key created in the app (Settings → API Keys). Sent as `X-API-Key: ` on every request. The header name `apikey` is also accepted as an alias. key_prefix: clai_live_ key_prefix_source: published curl examples at https://closedloop.sh/docs/api-reference/introduction scope: team region_bound: true region_detail: >- Each team lives in exactly one region and each key is scoped to it. An EU key only works against https://eu.api.closedloop.sh/v1; a US key only against https://api.closedloop.sh/v1. There is no cross-region routing and no fallback. lifecycle: creation: Settings → API Keys in the app display: shown once at creation only storage: hashed with SHA-256 before storage; not retrievable by the provider afterwards revocation: immediate -- anything using a revoked key receives 401 at once rotation_guidance: rotate periodically, especially after team-member departures rotation_hazard: >- Incremental /insights cursors are bound to the exact key that created them. Rotating a key mid-export invalidates the cursor; restart from the last committed sync_until with the new key. intended_use: unattended server workloads -- scheduled jobs, ETL, Databricks, agents with no person present at run time prohibited_use: client-side code, mobile apps, public repositories errors: - status: 401 code: NO_API_KEY - status: 401 code: INVALID_API_KEY - status: 503 code: API_KEY_AUTH_UNAVAILABLE sources: - openapi/closedloop-public-api-openapi.yaml - https://closedloop.sh/docs/account/api-keys - name: MCPOAuth type: oauth2 declared_in_openapi: false discovered_by: probe surface: MCP endpoints: authorization: https://mcp.closedloop.sh/authorize token: https://mcp.closedloop.sh/token registration: https://mcp.closedloop.sh/register issuer: https://mcp.closedloop.sh flows: - authorization_code - refresh_token - client_credentials pkce: [S256] client_auth_methods: [none, client_secret_basic, client_secret_post, private_key_jwt] signing_algs: [RS256, ES256] scopes: - name: mcp:tools description: Invoke MCP tools. Scope name published in the authorization-server and protected-resource metadata; no per-scope description is published by the provider. - name: mcp:read description: Read access over the MCP surface. Scope name published in the metadata; no per-scope description is published by the provider. dynamic_client_registration: true dcr_detail: >- The provider states clients need not pre-register -- "the OAuth Client ID and Secret are optional, and ClosedLoop AI registers your client automatically." m2m: docs: https://closedloop.sh/docs/account/mcp-service-clients methods: - private_key_jwt with a registered RSA or P-256 public key (preferred) - shared secret, for clients such as LiteLLM that cannot do private_key_jwt challenge: status: 401 www_authenticate: Bearer resource_metadata="https://mcp.closedloop.sh/.well-known/oauth-protected-resource" rfc9728: true regional_variants: - host: eu.mcp.closedloop.sh issuer: https://eu.mcp.closedloop.sh - host: api.closedloop.sh issuer: https://api.closedloop.sh/mcp documented: false difference: does NOT advertise private_key_jwt -- only none, client_secret_basic, client_secret_post hard_separation: >- REST API keys DO NOT work with MCP. The provider states this explicitly on the API Keys page. An MCP caller must create an OAuth service client. sources: - well-known/closedloop-mcp-oauth-authorization-server.json - well-known/closedloop-mcp-oauth-protected-resource.json - https://closedloop.sh/docs/mcp-server/overview - https://closedloop.sh/docs/account/mcp-service-clients - name: WebhookSigningSecret type: hmac declared_in_openapi: false surface: inbound webhook ingestion algorithm: HMAC-SHA256 over the unmodified raw request body headers: [x-webhook-signature, x-hub-signature-256, x-signature, typeform-signature] encodings: [hex, base64] prefix_accepted: 'sha256=' credential_name: Webhook Key credential_location: Settings → API Keys page (same page, different credential) explicit_negative: >- An x-api-key header does NOT authenticate an inbound webhook. Only the signing secret and the configuration confirmed at provisioning are accepted. rotation: self-service regenerate; no dual-secret overlap window -- previously-signed requests fail immediately sources: - https://closedloop.sh/docs/integrations/webhooks - https://closedloop.sh/docs/account/api-keys workforce_sso: note: >- Not an API credential -- this authenticates HUMANS into the app, and is Enterprise-plan gated. Recorded because it is part of the provider's overall authentication posture and because the SCIM surface it drives is a live, probeable API. protocols: - name: OIDC idp: Okta role: relying-party validates: [issuer, audience, signature, state, nonce, email claim] jit_provisioning: supported when enabled callback: https://api.closedloop.sh/api/okta/oidc/callback - name: SAML 2.0 idps: [Google Workspace, Microsoft Entra ID] role: service-provider jit_provisioning: supported - name: SCIM 2.0 idps: [Okta, Microsoft Entra ID] role: service-provider base_url_template: 'https://api.closedloop.sh/api/scim/v2/{team_id}' scopes: [scim.read, scim.write, entitlements.read] token_model: OAuth 2.0 scoped bearer token resources: [Users, Groups, Roles] password_sync: not accepted -- must be left disabled probe: url: https://api.closedloop.sh/api/scim/v2/test/ServiceProviderConfig status: 401 body: '{"schemas":["urn:ietf:params:scim:api:messages:2.0:Error"],"status":"401","detail":"Bearer token is required"}' - name: Global Token Revocation idp: Okta (Universal Logout) endpoint: https://api.closedloop.sh/api/okta/universal-logout not_supported: OIDC Post Logout / browser Single Logout -- the provider says to leave the Post Logout URI blank mtls: false openid_connect_provider: false openid_connect_provider_note: >- No host serves /.well-known/openid-configuration -- probed on closedloop.sh, api.closedloop.sh, mcp.closedloop.sh, docs.closedloop.sh, eu.api.closedloop.sh and eu.mcp.closedloop.sh, all 404. ClosedLoop AI consumes OIDC as a relying party; it does not issue it.