Skip to main content
Early access. This feature is in early access, which means it’s undergoing ongoing testing and development while we gather feedback, validate functionality, and improve outputs. Contact the ConductorOne Support team if you’d like to try it out or share feedback.

SDK namespaces

The pre-authenticated sdk object provides type-safe access to the ConductorOne API through 60+ namespaces organized by resource type.
CategoryNamespaces
Users & Identityuser, userSearch, directory, attributes, attributeSearch
Apps & Entitlementsapps, appSearch, appEntitlements, appEntitlementSearch, appUser, appResource, appResourceSearch, appOwners, appEntitlementOwners
Tasks & Accesstask, taskActions, taskSearch, taskAudit
Automationsautomation, automationExecution, automationSearch
Policies & Catalogpolicies, policySearch, requestCatalogManagement, requestCatalogSearch
Connectorsconnector, connectorCatalog
Functionsfunctions, functionsSearch
Webhooks & Exportwebhooks, webhooksSearch, export, exportsSearch, systemLog

Common SDK operations

import { JSONObject, sdk, functions } from "@c1/functions-sdk";

// Users
await sdk.user.list({ pageSize: 100, pageToken });
await sdk.user.get({ id: "user_123" });
await sdk.userSearch.search({ query: "alice@example.com" });

// Apps
await sdk.apps.list();
await sdk.apps.get({ id: "app_456" });
await sdk.appSearch.search({ query: "Slack" });

// Entitlements
await sdk.appEntitlements.list({ appId: "app_456" });
await sdk.appEntitlementSearch.search({ appIds: ["app_456"] });

// Tasks
await sdk.task.get({ id: "task_789" });
await sdk.taskSearch.search({ taskTypes: ["grant", "revoke"] });
await sdk.task.createGrantTask({ appEntitlementId: "ent_456", appUserId: "appuser_123" });
await sdk.task.createRevokeTask({ appEntitlementId: "ent_456", appUserId: "appuser_123" });

// Connectors
await sdk.connector.list();
await sdk.connector.get({ id: "conn_123" });

Pagination

Use the pagination pattern to retrieve all results:
const all = [];
let pageToken: string | undefined;
do {
  const resp = await sdk.user.list({ pageSize: 100, pageToken });
  if (resp.userServiceListResponse?.list) all.push(...resp.userServiceListResponse.list);
  pageToken = resp.userServiceListResponse?.nextPageToken;
} while (pageToken);

Importing external libraries

Dependency versions are pinned when you publish. To pick up newer versions, re-publish your function.
The runtime is Deno-based. Import npm packages using the npm: specifier:
import { z } from "npm:zod";
import { format, subDays } from "npm:date-fns";
import Stripe from "npm:stripe";
Packages are resolved at deploy time. No package.json or node_modules needed. @c1/functions-sdk and @c1/test are pre-configured and available without the npm: prefix.

Configuration options

Configure secrets, network access, and scopes in the ConductorOne UI:
1
Navigate to Workflows > Functions.
2
Click on your function.
3
Click (the more actions menu) and select Edit config.
The config drawer has three sections: Secrets, Outbound network access, and Scopes.

Secrets

Store API keys and configuration values as key/value pairs. Secrets are encrypted at rest and decrypted at runtime. To access secrets in code:
import { functions } from "@c1/functions-sdk";

const config = await functions.getConfig();
const webhookUrl = config.secrets["SLACK_WEBHOOK_URL"];
const threshold = parseInt(config.secrets["THRESHOLD"], 10);
functions.getConfig() returns { secrets: Record<string, string> }. Keys and values match what you defined in the UI.

Outbound network access

External domains must be added to the Outbound network access section in your function’s config or the request will be blocked. All outbound network traffic is routed through an egress proxy that enforces your allowlist.
Add allowed domains to your function’s config. This ensures functions can only communicate with endpoints you’ve explicitly approved, preventing unauthorized data exfiltration or unintended external calls. Here’s an example external API call:
import { JSONObject, functions } from "@c1/functions-sdk";

export default async function main(input: JSONObject): Promise<JSONObject> {
  const config = await functions.getConfig();
  const apiKey = config.secrets["EXTERNAL_API_KEY"];

  const resp = await fetch("https://api.example.com/data", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${apiKey}`,
    },
    body: JSON.stringify({ query: input.query }),
  });

  if (!resp.ok) {
    return { success: false, error: `API returned ${resp.status}` };
  }

  return { success: true, data: await resp.json() };
}

Scopes

By default, functions have no access to the ConductorOne APIs. To grant access, assign one or more roles in the Scopes section of the config drawer. The function’s SDK credentials are created with only the permissions those roles allow. Currently available:
  • Read-Only Administrator: Read-only access to all ConductorOne APIs (such as users, apps, entitlements, tasks). The function can query data but cannot create, update, or delete resources.
When you publish a function with scoped roles, new OAuth credentials are generated with those roles attached. At runtime, every SDK call is authorized against the function’s assigned roles—calls outside the granted permissions are denied.

Testing with @c1/test

New functions are scaffolded with a main.test.ts file alongside main.ts. The test file exports a registerTests function that receives your main function as handler, so each test can invoke it with different inputs.
// main.test.ts
import { JSONObject } from "@c1/functions-sdk";
import { test } from "@c1/test";

export default function registerTests({handler}: {handler: (input: JSONObject) => Promise<JSONObject>}) {
  test("validates required fields", async ({ equal }) => {
    const result = await handler({});
    equal(result.success, false);
    equal(result.error, "Missing required fields");
  });

  test("dry run returns without side effects", async ({ equal, ok }) => {
    const result = await handler({ userId: "u1", action: "revoke", appId: "a1", dryRun: true });
    equal(result.success, true);
    ok(result.dryRun);
  });
}

Assertions

Each test() callback receives an assertion object with these methods:
MethodDescription
equal(actual, expected)Strict equality check (===)
ok(value)Truthy check
fail(message)Force a test failure

Test options

Pass an options object as the third argument to test():
test("work in progress", async ({ ok }) => {
  // ...
}, { skip: true });

test("debug this one", async ({ equal }) => {
  // ...
}, { only: true });

Running tests

Click Test draft when viewing a draft, or Run tests when viewing published code. Each test() call becomes an individual result in the test dialog, displayed with a pass/fail status chip. Click on a result to expand assertion details and logs.

Runtime constraints

ConstraintDetail
RuntimeDeno (V8-based). Native TS, ES modules, web standard APIs (fetch, URL, etc.)
FilesystemNone. Functions are stateless.
NetworkEgress allowlist only. ConductorOne API subdomains pre-approved.
StateNo persistence between invocations. Each call starts fresh.
AuthOAuth2 + DPoP auto-injected. Never handle tokens.
Dependencies@c1/functions-sdk auto-available. Other npm packages resolved at publish time.
Loggingconsole.log/error/warn captured. Retained 30 days. Don’t log secrets.

Troubleshooting functions

Common issues and how to resolve them.

Function times out

  • Check for infinite loops
  • Verify external domains are in your allowlist
  • Break large operations into smaller batches

External API calls fail

  • Ensure domain is in the outbound network allowlist
  • Verify API credentials in secrets
  • Check console logs for detailed error messages

API returns 403 Forbidden

  • Check function’s scopes configuration
  • Verify you have permissions for the API operation

Cannot deploy function

  • Verify you have admin access
  • Check for TypeScript syntax errors
  • Ensure all imports are valid