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. Please contact our Support team if you’d like to try it out or have any feedback.
Functions work as steps in automation workflows, allowing you to add custom logic between other automation actions. When configured as a function step, the automation engine:
- Evaluates argument expressions (CEL) against the automation context
- Passes the evaluated arguments as your function’s
input
- Captures your return value for use by subsequent steps
When writing functions for use in automations, remember:
- Your function doesn’t need to know it’s called from an automation. It receives JSON in, returns JSON out.
- The automation UI defines which CEL expressions map to which input keys.
- Your return value becomes available to subsequent automation steps via the step name (such as
checkTraining.approved).
- The function runs with the same security model regardless of trigger source: pre-authenticated SDK, egress allowlist, no filesystem.
Step 1: Add a function step to an automation
Before you begin, make sure you’ve created and published a function and set up an automation. Once your function is published, you can add it as a step in any automation workflow:
Navigate to Workflows > Automations and open your automation in the editor.
Choose your published function from the dropdown.
Click Add argument to define the input your function receives. Read more about arguments in the next section.
In the automation UI, each argument is a CEL expression evaluated against the workflow context. These expressions are evaluated, marshaled to JSON, and passed as your function’s input.
Available context
| Variable | Description |
|---|
trigger.user_id | User ID from the automation trigger |
trigger.app_id | App ID from the trigger event |
trigger.entitlement_id | Entitlement ID from the trigger |
previous_step_name.field | Output field from a previously completed step |
The examples below show how you configure arguments in the automation UI. The left side is the key name your function receives; the right side is a CEL expression that gets evaluated at runtime.
Pass data from a trigger:
| Key | CEL Expression |
|---|
userId | trigger.user_id |
appId | trigger.app_id |
action | "verify" (literal string) |
Your function receives the evaluated values:
{
"userId": "u-abc123",
"appId": "app-xyz789",
"action": "verify"
}
Pass output from a previous step:
| Key | CEL Expression |
|---|
userId | steps.getUser.output.id |
department | steps.getUser.output.profile.department |
timestamp | now() |
Static values:
For literal values, wrap strings in quotes within the CEL expression:
| Key | CEL Expression |
|---|
department | "Engineering" |
minAccessLevel | 3 |
Step 3: Access function output in later steps
The output of your function is available in subsequent steps via the step context. If your function step is named checkTraining, access its output like this:
steps.checkTraining.output.trainingCompleted
steps.checkTraining.output.eligible
steps.checkTraining.output.userId
Example: Training verification workflow
This example shows a complete workflow that checks a user’s training status before approving access.
Function code
import { JSONObject, functions, sdk } from "@c1/functions-sdk";
export default async function main(input: JSONObject): Promise<JSONObject> {
const userId = input.userId as string;
// Get user from ConductorOne
const userResponse = await sdk.user.get({ id: userId });
const user = userResponse.user;
// Check training status from external API
const config = await functions.getConfig();
const trainingApiKey = config.secrets["TRAINING_API_KEY"];
const trainingResponse = await fetch(
`https://training.example.com/api/users/${user.email}/status`,
{
headers: { "Authorization": `Bearer ${trainingApiKey}` },
}
);
const training = await trainingResponse.json();
return {
success: true,
userId: userId,
email: user.email,
trainingCompleted: training.completed,
trainingDate: training.completedDate,
eligible: training.completed,
};
}
Automation workflow
- Trigger: Access request created
- Step 1: Run Function
checkTraining
- Input:
{ "userId": "trigger.user_id" }
- Step 2: Conditional - Check if training is completed
- Condition:
steps.checkTraining.output.eligible == true
- If true: Approve the access request
- If false: Send notification to user with training link
Best practices
Design your function outputs to work well with automation conditionals and debugging.
Return structured data
Make your function outputs easy to use in conditionals:
return {
success: true,
eligible: trainingCompleted && securityCheckPassed,
reason: trainingCompleted ? "approved" : "training_required",
metadata: {
trainingDate: training.completedDate,
securityScore: securityCheck.score,
}
};
Use Boolean flags for conditionals
Return clear true/false values that work well in automation conditions:
return {
shouldApprove: true,
shouldNotify: false,
requiresReview: false,
};
Include context for debugging
Return enough information to troubleshoot issues:
return {
success: true,
userId: userId,
timestamp: new Date().toISOString(),
checksPerformed: ["training", "security", "compliance"],
result: "approved",
};