Skip to content
ConduitConduit

API reference

All endpoints require authentication unless noted otherwise. Authentication is via Auth0 OIDC session (web UI routes) or Bearer JWT (API/MCP routes).

Health check endpoint. No authentication required.

Response:

{ "status": "ok", "timestamp": "2026-03-26T12:00:00.000Z" }

Tenant-scoped vendor-container health — returns only the vendors this org has connected. See the Vendor Health reference for the full feature description.

Auth: Auth0 session (member+)

Response:

{
"vendors": [
{
"vendorSlug": "datto-rmm",
"displayName": "Datto RMM",
"status": "healthy",
"lastChecked": "2026-05-17T13:00:00.000Z",
"latencyMs": 142,
"version": "1.4.0",
"errorDetail": null
}
]
}

status is one of healthy / degraded / down / unknown. errorDetail is populated only when degraded or down.


GET /.well-known/oauth-authorization-server

Section titled “GET /.well-known/oauth-authorization-server”

RFC 8414 authorization server metadata. No authentication required. Advertises device_authorization_endpoint and lists urn:ietf:params:oauth:grant-type:device_code in grant_types_supported.

GET /.well-known/oauth-protected-resource/v1/mcp

Section titled “GET /.well-known/oauth-protected-resource/v1/mcp”

RFC 9728 protected resource metadata for the unified MCP endpoint.

Dynamic client registration (RFC 7591). Used by MCP clients (Claude Desktop, mcp-remote) to register before starting the OAuth flow.

Authorization endpoint. Requires PKCE (S256). Redirects user to Auth0 for authentication, then to the credential connection page if needed.

Query Parameters:

  • response_type – must be code
  • client_id – from dynamic registration
  • redirect_uri – client callback URL
  • code_challenge – PKCE S256 challenge
  • code_challenge_method – must be S256
  • scope – mcp:<vendor> (per-vendor) or mcp:all (unified)
  • state – client-generated state parameter

Device Authorization Grant (RFC 8628) — starts a flow for a client that has no browser and no redirect URI: an unattended agent on a server. No redirect URI is involved at any point.

Client auth: confidential service clients only. Send client_id and client_secret (HTTP Basic or the body). A public dynamically registered client is rejected with 400 and unauthorized_client; no device code is issued. An unknown client_id is 401 invalid_client. Rate limited to 20/minute.

Body:

client_id=<client_id>
&client_secret=<svc_client_secret> # service clients only
&scope=mcp # optional, defaults to mcp

Response:

{
"device_code": "<opaque>",
"user_code": "WDJB-MDBT",
"verification_uri": "https://conduit.wyre.ai/device",
"verification_uri_complete": "https://conduit.wyre.ai/device?user_code=WDJBMDBT",
"expires_in": 600,
"interval": 5
}

Show user_code and verification_uri to a human, then poll POST /oauth/token every interval seconds. expires_in is DEVICE_CODE_TTL (default 600 — 10 minutes); interval is DEVICE_POLL_INTERVAL (default 5).

Requiring the secret here is a security control, not bookkeeping: only the agent itself can open a flow capable of minting a token subjected to that agent.

The human-facing approval page. The signed-in person types the user_code — case- and dash-insensitive, so wdjb mdbt matches WDJB-MDBT — and chooses Approve or Deny. verification_uri_complete pre-fills it.

Auth: Auth0 session

Token exchange and refresh.

Code Exchange:

grant_type=authorization_code
&code=<auth_code>
&redirect_uri=<redirect_uri>
&client_id=<client_id>
&code_verifier=<pkce_verifier>

Refresh:

grant_type=refresh_token
&refresh_token=<refresh_token>
&client_id=<client_id>

Client Credentials (Service Clients):

grant_type=client_credentials
&client_id=<svc_client_id>
&client_secret=<svc_client_secret>
&scope=mcp:all

Device Code (RFC 8628):

grant_type=urn:ietf:params:oauth:grant-type:device_code
&device_code=<device_code>
&client_id=<client_id>
&client_secret=<svc_client_secret> # service clients only

Response:

{
"access_token": "<jwt>",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "<token>"
}

Polling the device grant. The device_code is bound to the client_id that requested it and is single-use. Service clients must authenticate here too, for the same reason they must at /oauth/device_authorization.

While the flow is unresolved the device grant returns 400 with an OAuth error code:

error Meaning
authorization_pending Nobody has approved or denied the code yet. Keep polling.
slow_down Polled faster than the interval returned with the device code. Back off, then retry.
access_denied The person chose Deny.
expired_token The code passed its 10-minute TTL. Start a new device authorization request.

On approval it returns the standard response above — access token plus a refresh_token, so the agent renews itself without a further approval.

Subject: the issued token is subjected to the service account (sub: svc:<org-id>:<client-id>), not to the person who approved it — the human authorizes the agent rather than becoming it. Redeeming the device code as a public client returns unauthorized_client and does not issue a token.

An approval or denial is written to that organization’s audit log with the approver’s user id and the service account’s organization id.

Token revocation (RFC 7009).

token=<token>&token_type_hint=access_token

Returns the authenticated user’s profile.

Auth: Auth0 session

Response:

{
"id": "auth0|abc123",
"email": "user@example.com",
"name": "John Doe",
"firstName": "John",
"lastName": "Doe",
"displayName": "JD"
}

Update profile fields.

Auth: Auth0 session

Body:

{
"firstName": "John",
"lastName": "Doe",
"displayName": "JD"
}

Create a new organization.

Auth: Auth0 session Body:

{
"name": "Acme MSP"
}

Response: 201 with org object

List the authenticated user’s organizations.

Auth: Auth0 session Response: Array of org objects

Get org details.

Auth: Auth0 session (member+) Response: Org object

Update org name.

Auth: Auth0 session (owner) Body: { "name": "New Name" }

Delete organization.

Auth: Auth0 session (owner) Response: 204 No Content

Legacy endpoint from the pre-flat-pricing plan tiers. Under the current flat plan every organization already resolves to the same (only) plan, so a recognized code responds 409 (the plan is already active) — kept for backward compatibility with old integrations, not a working activation step. Invalid codes return 422 and an empty code 400. Rate limited: 5 per 15 minutes.

Auth: Auth0 session (owner) Body: { "code": "ALPHA123" }

Update org settings.

Auth: Auth0 session (owner) Body:

{ "defaultServerAccess": "all" }

Values: "none" or "all"


List org members.

Auth: Auth0 session (member+) Response: Array of member objects

Remove a member. Admins cannot remove other admins; only owners can.

Auth: Auth0 session (admin+) Response: 204 No Content

PATCH /api/orgs/:orgId/members/:userId/role

Section titled “PATCH /api/orgs/:orgId/members/:userId/role”

Change member role. Cannot change your own role.

Auth: Auth0 session (owner) Body: { "role": "admin" } (or "member")


Create an invitation link. Rate limited: 10 per hour. Requires an active subscription.

Auth: Auth0 session (admin+) Response:

{
"id": "...",
"token": "...",
"inviteUrl": "https://conduit.wyre.ai/invite/<token>",
"expiresAt": "...",
"maxUses": 1,
"useCount": 0
}

List pending invitations.

Auth: Auth0 session (admin+)

Revoke an invitation.

Auth: Auth0 session (admin+) Response: 204 No Content

Show invitation acceptance page (web UI).

Accept an invitation. Rate limited: 5 per 15 minutes.


List all server access grants for the org.

Auth: Auth0 session (admin+)

GET /api/orgs/:orgId/members/:userId/server-access

Section titled “GET /api/orgs/:orgId/members/:userId/server-access”

List a user’s vendor access grants. Users can view their own; admins can view any member’s.

Auth: Auth0 session (member for self, admin+ for others)

PUT /api/orgs/:orgId/members/:userId/server-access/:vendor

Section titled “PUT /api/orgs/:orgId/members/:userId/server-access/:vendor”

Grant a member access to a vendor.

Auth: Auth0 session (admin+)

DELETE /api/orgs/:orgId/members/:userId/server-access/:vendor

Section titled “DELETE /api/orgs/:orgId/members/:userId/server-access/:vendor”

Revoke a member’s access to a vendor.

Auth: Auth0 session (admin+) Response: 204 No Content

PUT /api/orgs/:orgId/members/:userId/server-access

Section titled “PUT /api/orgs/:orgId/members/:userId/server-access”

Bulk-set a member’s vendor access (replaces all grants).

Auth: Auth0 session (admin+) Body: { "vendors": ["datto-rmm", "itglue", "autotask"] }


Store or update org-level credentials for a vendor. Rate limited: 10 per minute. Requires an active subscription. Validates credentials against vendor API when possible.

Auth: Auth0 session (admin+) Body: Vendor-specific fields (see vendor-integration.md)

{
"apiKey": "your-api-key",
"apiSecret": "your-api-secret",
"platform": "concord"
}

Response: 201 with { "id": "...", "vendor": "datto-rmm" }

List connected vendor slugs for the org.

Auth: Auth0 session (member+) Response: ["datto-rmm", "itglue", "autotask"]

DELETE /api/orgs/:orgId/credentials/:vendor

Section titled “DELETE /api/orgs/:orgId/credentials/:vendor”

Remove org-level credentials for a vendor.

Auth: Auth0 session (admin+) Response: 204 No Content

GET /api/orgs/:orgId/credentials/:vendor/keys

Section titled “GET /api/orgs/:orgId/credentials/:vendor/keys”

List named org credential keys for a vendor (metadata only — never the stored fields).

Auth: Auth0 session (admin+) Response: [{ "id": "...", "name": "...", "teamId": "..." | null }, ...]

POST /api/orgs/:orgId/credentials/:vendor/keys

Section titled “POST /api/orgs/:orgId/credentials/:vendor/keys”

Add a named org credential key for a vendor, optionally bound to a team. Rate limited: 10 per minute. Requires an active subscription. Validates credentials against the vendor API when possible.

Auth: Auth0 session (admin+) Body: { "name": "...", "teamId": "..." (optional), "fields": { ...vendor-specific } } Response: 201 with the created key (metadata only)

PUT /api/orgs/:orgId/credentials/:vendor/keys/:keyId

Section titled “PUT /api/orgs/:orgId/credentials/:vendor/keys/:keyId”

Rename a named org credential key and/or rebind it to a different team (teamId: null unbinds it). Metadata only — never the stored credential payload.

Auth: Auth0 session (admin+) Body: { "name": "...", "teamId": "..." | null }

DELETE /api/orgs/:orgId/credentials/:vendor/keys/:keyId

Section titled “DELETE /api/orgs/:orgId/credentials/:vendor/keys/:keyId”

Remove a named org credential key. A team bound to the deleted key falls back to the connection’s default credential.

Auth: Auth0 session (admin+) Response: 204 No Content


Create a team. Requires an active subscription.

Auth: Auth0 session (admin+) Body: { "name": "Tier 1 Support" } Response: 201 with team object

List teams with member counts and vendor connections.

Auth: Auth0 session (admin+)

Rename a team.

Auth: Auth0 session (admin+) Body: { "name": "New Name" }

Delete a team.

Auth: Auth0 session (owner) Response: 204 No Content

PUT /api/orgs/:orgId/teams/:teamId/members/:userId

Section titled “PUT /api/orgs/:orgId/teams/:teamId/members/:userId”

Add a member to a team.

Auth: Auth0 session (admin+)

DELETE /api/orgs/:orgId/teams/:teamId/members/:userId

Section titled “DELETE /api/orgs/:orgId/teams/:teamId/members/:userId”

Remove a member from a team.

Auth: Auth0 session (admin+) Response: 204 No Content

PUT /api/orgs/:orgId/teams/:teamId/server-access/:vendor

Section titled “PUT /api/orgs/:orgId/teams/:teamId/server-access/:vendor”

Grant a team access to a vendor.

Auth: Auth0 session (admin+)

DELETE /api/orgs/:orgId/teams/:teamId/server-access/:vendor

Section titled “DELETE /api/orgs/:orgId/teams/:teamId/server-access/:vendor”

Revoke a team’s access to a vendor.

Auth: Auth0 session (admin+) Response: 204 No Content


GET /api/orgs/:orgId/teams/:teamId/credentials

Section titled “GET /api/orgs/:orgId/teams/:teamId/credentials”

List vendors with team-level credentials.

Auth: Auth0 session (admin+)

POST /api/orgs/:orgId/teams/:teamId/credentials/:vendor

Section titled “POST /api/orgs/:orgId/teams/:teamId/credentials/:vendor”

Store team-level credentials for a vendor.

Auth: Auth0 session (admin+) Body: Vendor-specific credential fields

DELETE /api/orgs/:orgId/teams/:teamId/credentials/:vendor

Section titled “DELETE /api/orgs/:orgId/teams/:teamId/credentials/:vendor”

Remove team-level credentials.

Auth: Auth0 session (admin+) Response: 204 No Content


Create a service client for M2M authentication.

Auth: Auth0 session (admin+) Body:

{
"name": "Nightly Sync",
"expires_in_days": 90
}

Response: 201

{
"id": "...",
"name": "Nightly Sync",
"client_id": "svc_...",
"client_secret": "<shown-once>",
"expires_at": "2026-06-24T00:00:00.000Z",
"created_at": "2026-03-26T00:00:00.000Z"
}

List service clients (secret is never returned after creation).

Auth: Auth0 session (admin+)

DELETE /api/orgs/:orgId/service-clients/:clientId

Section titled “DELETE /api/orgs/:orgId/service-clients/:clientId”

Revoke a service client.

Auth: Auth0 session (admin+) Response: 204 No Content

POST /api/orgs/:orgId/service-clients/:clientId/agent-key

Section titled “POST /api/orgs/:orgId/service-clients/:clientId/agent-key”

Mint — or rotate — this service account’s agent key: a long-lived bearer for /v1/mcp, for agents that set one Authorization header and cannot run an OAuth flow. It resolves to the account’s own svc:<org-id>:<client-id> identity, so its teams, access grants and audit entries are unchanged.

Calling this again rotates in place. The previous key stops working immediately; the account keeps its identity and all of its grants.

Prefer the device-code flow above where the client can run it: it ends with a rotating refresh token rather than a static bearer in a config file. An agent key is for clients that can only set one header.

Auth: Auth0 session (admin+). Rate limited to 10/hour. Requires: the organization’s serviceClients entitlement — an org whose subscription has lapsed cannot mint new agent credentials. Revoking stays open so such an org can still wind existing keys down. Response: 201

{
"agent_key": "cak_a4f9b2c8d1e0_<shown-once>",
"prefix": "cak_a4f9b2c8d1e0",
"rotated": false
}
Status Meaning
402 The organization lacks the serviceClients entitlement. Distinct from an auth failure — the session is valid, the subscription is not.
404 No service account with that clientId in this organization.

agent_key is returned once and is never retrievable again. prefix is the public half, safe to display, and is what the Service Accounts page lists.

DELETE /api/orgs/:orgId/service-clients/:clientId/agent-key

Section titled “DELETE /api/orgs/:orgId/service-clients/:clientId/agent-key”

Revoke the agent key. Takes effect immediately; the service account itself is untouched.

Auth: Auth0 session (admin+) Response: 204 No Content — or 404 if the account has no key.

GET /api/orgs/:orgId/service-clients/:clientId/credentials

Section titled “GET /api/orgs/:orgId/service-clients/:clientId/credentials”

List vendors with service-client-level credentials.

Auth: Auth0 session (admin+)

POST /api/orgs/:orgId/service-clients/:clientId/credentials/:vendor

Section titled “POST /api/orgs/:orgId/service-clients/:clientId/credentials/:vendor”

Store service-client-level credentials.

Auth: Auth0 session (admin+)

DELETE /api/orgs/:orgId/service-clients/:clientId/credentials/:vendor

Section titled “DELETE /api/orgs/:orgId/service-clients/:clientId/credentials/:vendor”

Remove service-client-level credentials.

Auth: Auth0 session (admin+) Response: 204 No Content


Get tool allowlists for all roles for a vendor.

Auth: Auth0 session (admin+)

PUT /api/orgs/:orgId/tool-access/:vendor/:role

Section titled “PUT /api/orgs/:orgId/tool-access/:vendor/:role”

Set the tool allowlist for a role. When set, members with that role can only use the listed tools.

Auth: Auth0 session (owner) Body: { "tools": ["datto_list_devices", "datto_list_sites"] }

DELETE /api/orgs/:orgId/tool-access/:vendor/:role

Section titled “DELETE /api/orgs/:orgId/tool-access/:vendor/:role”

Clear the allowlist (revert to allow-all).

Auth: Auth0 session (owner) Response: 204 No Content

GET /api/orgs/:orgId/tool-access/:vendor/discover

Section titled “GET /api/orgs/:orgId/tool-access/:vendor/discover”

Discover available tools from a vendor container (queries the live container).

Auth: Auth0 session (admin+)


Query the request audit log. Requires an active subscription and admin+ role.

Auth: Auth0 session Query Parameters:

  • org_id – filter by org (defaults to user’s primary org)
  • user_id – filter by user
  • vendor – filter by vendor slug
  • start – ISO date start
  • end – ISO date end
  • limit – page size
  • offset – page offset
  • format – csv for CSV export

Each JSON entry includes userLabel (name, then email, then a service-account name, then a short last-4 fallback) alongside the stable userId. CSV export leads with that label in the user column and keeps user_id as the next column.

Query the admin audit log (administrative actions).

Auth: Auth0 session (admin+, active subscription) Query Parameters:

  • org_id, event_type, actor_id, start, end, limit, offset, format

JSON entries include actorLabel and, when a target is present, targetLabel, alongside actorId and targetId. Service accounts use their configured name; background jobs use a readable sentinel name (for example “Log shipper”). CSV export leads with those labels (actor, target) and keeps actor_id / target_id beside them.


Usage analytics for an organization. All three endpoints require an active subscription and admin+ role, and resolve to the caller’s primary organization. They read from the request_log — the same metadata described in Prompt Capture & Privacy.

Time window. Every endpoint accepts the same window parameters:

  • range – one of 7d, 30d, 1y, all. Defaults to 30d when neither start nor end is given. all covers the org’s whole retained history and must be requested explicitly.
  • start / end – ISO 8601 bounds. If either is present it overrides range.

The Overview page’s usage section exposes the same presets as a 7d / 30d / 1y / All time picker and keeps the choice in the URL (/org?range=7d).

Aggregate tool-call usage for the org.

Auth: Auth0 session (admin+, active subscription) Query Parameters:

  • range – 7d | 30d (default) | 1y | all
  • start – ISO 8601 range start (overrides range)
  • end – ISO 8601 range end (overrides range)

Response: total calls, unique users, average response time, plus breakdowns by vendor, by user, by day, and by source (mcp / cli).

Estimated token and cost savings from CLI-wrapper calls versus raw MCP calls.

Auth: Auth0 session (admin+, active subscription) Query Parameters: range, start, end

Response: { "totalMcpCalls": ..., "totalCliCalls": ..., "estimatedTokensSaved": ..., "estimatedCostSavedUsd": ... }. The savings figures are a heuristic estimate, not a metered measurement.

Per-vendor usage breakdown.

Auth: Auth0 session (admin+, active subscription) Query Parameters: range, start, end

Response: { "vendors": [ ... ] } — each entry has total calls, unique users, average response time, and the vendor’s top 5 tools by call count.


Create a Stripe Checkout session for the flat subscription.

Auth: Auth0 session (owner) Body:

{
"org_id": "...",
"coupon": "DISCOUNT20"
}

Response: { "url": "https://checkout.stripe.com/..." }

Create a Stripe Customer Portal session for managing subscription.

Auth: Auth0 session (owner) Body: { "org_id": "..." } Response: { "url": "https://billing.stripe.com/..." }

Stripe webhook ingest. Called by Stripe, not by API clients — signature-verified per request (see Security § Billing & Payment Security). Listed here for completeness; not a customer-callable endpoint.

Auth: Stripe webhook signature (HMAC)


List log shipping destinations (configs are masked).

Auth: Auth0 session (admin+, active subscription)

Create a log shipping destination.

Auth: Auth0 session (admin+, active subscription)

platform is one of loki, graylog, logscale, huntress, splunk, elastic, sentinel, webhook. endpointUrl may be omitted for huntress (preset to https://hec.huntress.io); huntress also rejects any other host, and sentinel requires an *.ingest.monitor.azure.com host. Required config keys per platform: logscale/huntress/splunk → token; sentinel → tenantId, clientId, clientSecret, dcrImmutableId. Secret values are returned masked (abcd****). See the log shipping guide for each platform’s fields.

Body:

{
"label": "Production Loki",
"platform": "loki",
"endpointUrl": "https://loki.example.com/loki/api/v1/push",
"config": { "username": "...", "password": "..." }
}

Execute a tool call via plain JSON (not MCP JSON-RPC). Rate limited: 5,000/hour per user — one shared bucket across this endpoint and the two MCP endpoints below, not a separate allowance per vendor or per transport.

Auth: Bearer JWT Body:

{
"tool": "datto_list_devices",
"args": { "siteId": "123" }
}

Response:

{ "result": { "content": [...] } }

Response Headers: X-Auth-Ms, X-Session-Ms, X-Vendor-Ms, X-Total-Ms

Get CLI-friendly tool definitions for a vendor. Rate limited: 600/minute, shared with every other route that has no endpoint-specific limit (global backstop, not a per-user allowance).

Auth: Bearer JWT Response:

{
"vendor": "datto-rmm",
"vendorName": "Datto RMM",
"commands": [
{
"command": "list-devices",
"description": "List all devices",
"flags": [
{ "name": "siteId", "type": "string", "required": false, "description": "..." }
]
}
]
}

Single MCP endpoint for all vendors. Handles initialize, tools/list (aggregated), and tools/call (routed by vendor prefix). Rate limited: 5,000/hour per user — one shared bucket across this endpoint, the per-vendor endpoint below, and the CLI endpoint above.

Auth: Authorization: Bearer <token>, where the token is either a JWT from the OAuth 2.1 flow (a person, or a service account via client_credentials) or a service account’s long-lived agent key (cak_…). Both resolve to the same identity and the same access grants; an agent key resolves to its service account’s svc:<org-id>:<client-id>. See Connect an unattended agent.

initialize response: echoes back the client’s requested protocolVersion when it’s one Conduit recognizes (2025-11-25 / 2025-06-18 / 2025-03-26 / 2024-11-05), falling back to 2025-03-26 for an absent or unrecognized request. serverInfo includes icons (Conduit’s logo, requires a negotiated protocol version of 2025-11-25 or later to be read), plus title, description, and websiteUrl.

POST /v1/:vendor/mcp (Per-Vendor, Deprecated)

Section titled “POST /v1/:vendor/mcp (Per-Vendor, Deprecated)”

Per-vendor MCP endpoint. Legacy; prefer the unified endpoint. Same shared 5,000/hour-per-user rate limit as the unified and CLI endpoints.

Auth: Bearer JWT (via OAuth 2.1 flow)