> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ravenna.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Build Foundry functions

> Create, test, publish, and roll back Foundry functions to add custom code actions to Ravenna workflows and AI agents, with built-in editor and runtime context.

A function is something Foundry can do, like "send a DocuSign envelope" or "look up a Salesforce contact." You build it by describing what you want, and Foundry writes and tests the code for you.

Functions live inside the Foundry app (in the workspace sidebar between **Agents** and **Workflows**). They use integrations created by your org admins in [Settings → Integrations](/documentation/automate/foundry/integrations).

***

## The workspace

Open Foundry from the sidebar. The left panel drills down one level at a time, with a breadcrumb back up:

1. **Integrations**. Every integration available to Foundry, with search. Functions that do not call an integration are grouped under **Ravenna Internal**. Organization admins get a **+** (**New integration**) button in the header, which opens **Settings → Integrations** in a new tab. See [Integrations](/documentation/automate/foundry/integrations) for each row's menu.
2. **Functions**. Click an integration to see its functions, with their last test status (Passing, Failing, or Never run). The **+** (**New function**) button in the header starts a new one. Each row's overflow menu has **Delete**, which removes the function after you confirm, without opening it.
3. **Function**. Click a function to open it.

When you open a function, the right side becomes the **function workspace** with the generated code and a publish button. The panel on the left shifts into four tabs for that function:

* **Chat**. The conversation Foundry uses to generate and refine code.
* **Test**. Run the function with sample inputs.
* **Integrations**. Connect or disconnect integrations for this function.
* **Configure**. Edit the name, description, and AI tool prompt, or delete the function.

***

## Build a function

<Steps>
  <Step title="Create the function">
    In Foundry, open the integration the function will use, then click **+** (**New function**) in the sidebar header. A blank function opens in the workspace.
  </Step>

  <Step title="Connect integrations">
    Open the function's **Integrations** tab and connect the integration the function should call. You can add more than one, which is useful when a function needs to read from one tool and write to another. The first integration is the **primary** one and supplies the function's default scope and OAuth connected account.

    If you don't have the integration you need, an admin can create one in [Settings → Integrations](/documentation/automate/foundry/integrations).
  </Step>

  <Step title="Describe what it should do">
    In the **Chat** tab, write what you want the function to do in plain language. Be specific:

    <Prompt description="List all active users and return their name, email, and role">
      List all active users and return their name, email, and role
    </Prompt>

    <Prompt description="Create a project with the given name, description, and team">
      Create a project with the given name, description, and team
    </Prompt>

    <Prompt description="Look up a contact by email and return their company and last activity date">
      Look up a contact by email and return their company and last activity date
    </Prompt>

    Foundry generates code, type-checks it, and runs a dry-run against the API.
  </Step>

  <Step title="Test with real inputs">
    Open the **Test** tab, fill in the inputs, pick a runtime environment (development, staging, or production), and click **Run Test**. Foundry shows the result, every HTTP request the function made, and any log output.

    Secret-typed inputs accept credentials from your organization's [Vault](/documentation/platform/organizations/vault). Vault references are decrypted in memory when the test runs, and the decrypted values are scrubbed from the test output.

    Turn on **Dry Run** in the function workspace header to test without changing external state. See [Dry Run mode](#dry-run-mode) below.
  </Step>

  <Step title="Refine in chat">
    If something isn't right, ask in plain language from the **Chat** tab:

    <Prompt description="Add pagination so it fetches every page.">
      Add pagination so it fetches every page.
    </Prompt>

    <Prompt description="If the API returns a 429, wait and retry up to 3 times.">
      If the API returns a 429, wait and retry up to 3 times.
    </Prompt>

    <Prompt description="Only return users where status is active.">
      Only return users where status is active.
    </Prompt>

    When Foundry suggests a follow-up, clickable suggestion chips appear in a strip above the input. Click a chip to prefill the composer so you can edit before sending.
  </Step>

  <Step title="Publish">
    Click **Publish** in the function workspace. Foundry validates the code one more time, generates an AI tool prompt (so agents know when to call it), and publishes the function. It's now available as a workflow step and as a tool for your AI agents.
  </Step>
</Steps>

<Info>
  Foundry shows generation status (`Idle`, `In progress`, `Success`, or `Error`) in the function workspace, so if a generation stalls or fails, you can see why.
</Info>

<Callout icon="sparkles" color="#7C3AED">
  Want prompts you can copy? See [Foundry examples](/guides/how-to/foundry/examples) for worked recipes, or [Tips & troubleshooting](/guides/how-to/foundry/tips-and-troubleshooting) for ways to get better code out of Foundry.
</Callout>

***

## Version history

Foundry snapshots a function every time its code changes, so you can go back to a version that worked.

### Version numbers

Two numbers track a function, and they move for different reasons:

* The **published version** (`published v3` in the workspace header) is the release number. It only moves when you publish.
* The internal **revision** moves on every code write: an AI generation, a hand edit in the editor, or a checkpoint restore. It is what tells Foundry whether your draft has drifted from what is live, and it is why a test result recorded against an older revision is flagged as stale.

So a function can sit at `published v3` while you make ten draft edits. Publishing again makes it `v4`.

### Restore a checkpoint

Every code-changing step in the **Chat** tab gets a **Checkpoint** marker with its timestamp. Click **Restore** on a marker to roll the function back to that point.

Restoring reverts the code, schemas, settings, and integration bindings, and rewinds the chat to that point so the conversation matches the code. Your current state is saved as a checkpoint first, so a restore is itself reversible. The checkpoint at your current state shows the marker without a **Restore** option, since there is nothing to change.

<Note>
  Restoring changes your draft, not what is live. Publish afterward to push the restored code to workflows and agents.
</Note>

### Roll back the published version

Publishing also records a checkpoint, which gives you a deployable history. Click the **published v3** badge in the function workspace header to open **Publish history**, pick an earlier version, and confirm.

A rollback reverts the code, schemas, settings, and integration bindings to that version and re-publishes, so anything running picks up the old code right away. Two differences from a checkpoint restore:

* **Your chat is preserved.** A rollback is a deployment operation, not a rewind of your work, so the conversation stays where it is.
* **It publishes a new version rather than reusing the old number.** Rolling back from `v5` to `v3` produces `v6`, annotated `↳ rolled back to v3` in the history so you can tell the two apart.

The version currently live is marked **(current)** and cannot be selected. Rollback needs a published function, so an unpublished draft has no publish history.

<Warning>
  A rollback takes effect immediately for every workflow and agent using the function. There is no separate confirmation step after the dialog.
</Warning>

***

## Dry Run mode

Dry Run lets you exercise a function end-to-end without changing external state. Use it to safely test functions that send email, create tickets, post messages, or update records. Turn on the **Dry Run** toggle in the function workspace header, then click **Run Test** from the **Test** tab as usual.

When you publish a function, Foundry analyzes the code and picks one of two strategies. The active strategy shows up in the toggle's tooltip:

| Strategy | When Foundry picks it | What runs |
| - | - | - |
| As-is | The function only reads or searches (no external writes). | The original code runs live. |
| Variant | The function makes at least one state-changing call (create, update, delete, send, etc.). | Foundry runs a generated copy of the code where only the writes are mocked. Reads stay live, so bad inputs and auth errors surface exactly like a real run. |

A few things to know:

* Dry Run is available once a function has been published or had its dry-run code generated. New, never-published functions show the toggle disabled.
* The code editor switches to read-only while Dry Run is on, and a banner tells you which strategy is active. Turn Dry Run off before editing.
* Each mocked write is logged in the test output so you can see exactly what the function *would* have done.
* If you change the function and the dry-run variant looks stale, click the **Sparkles** button next to the toggle to regenerate it. Only workspace admins can regenerate dry-run code.
* `dryRun` is also exposed on the `POST /foundry/actions/run-test` API via the `useDryRunVariant` flag.

<Callout icon="flask-conical" color="#D97706">
  Dry Run is the safest way to test a function against production credentials. Use it whenever a failed test would create unwanted side effects, like duplicate tickets or real customer notifications.
</Callout>

***

## Advanced: the ActionContext SDK

This section is for users who want to read or hand-edit the generated TypeScript code. You don't need any of this to build, test, or publish a function.

Every function exports a `run` function that receives an `ActionContext`. The context gives you authenticated HTTP clients (one per integration), the Ravenna operations namespace, and runtime info.

```typescript theme={"system"}
import type { ActionContext } from '@ravenna/actions'

export async function run(ctx: ActionContext, input: RunInput): Promise<RunOutput> {
  // Each integration is available by its slug, with auth and default headers injected.
  const salesforce = ctx.integrations.salesforce
  const response = await salesforce.fetch(`${salesforce.baseUrl}/services/data/v59.0/query`)
  const { records } = await response.json()

  // Call Ravenna operations the same way you'd call any built-in action.
  await ctx.ravenna.createTicket({
    title: `New account: ${records[0].Name}`,
    queueId: input.queueId,
    statusId: input.statusId,
    requesterId: input.requesterId,
    authorId: input.authorId,
  })

  ctx.log(`Processed ${records.length} records in ${ctx.env}`)

  return { count: records.length }
}
```

### Context properties

| Property | What it is |
| - | - |
| `ctx.integrations` | A map of integration clients keyed by slug. Each has `.fetch` (auth-injected) and `.baseUrl`. |
| `ctx.ravenna` | The Ravenna operations namespace. See the list below. |
| `ctx.log()` | Writes to the function's log output. |
| `ctx.workspaceId` / `ctx.organizationId` | IDs for the current execution context. |
| `ctx.env` | `'development'`, `'staging'`, or `'production'`. |
| `ctx.workflowExecutionId` | Set when the function is running as a workflow step. |

<Info>
  `ctx.fetch`, `ctx.baseUrl`, and `ctx.auth` still exist for backwards compatibility but are deprecated. They point at the primary integration. New code should use `ctx.integrations.<slug>` so it keeps working when more integrations are added to the function.
</Info>

### Ravenna operations

Available on `ctx.ravenna`:

**Tickets:** `createTicket`, `updateTicket`, `addTicketComment`, `setTicketStatus`, `setTicketPriority`, `setTicketAssignee`, `addTicketFollowers`, `addTicketTags`, `moveTicket`

**Users:** `getUser`

Slack is not a `ctx.ravenna` operation. To post to Slack from an action, select the Slack integration and call the Slack Web API directly with `ctx.integrations.native_slack.fetch`. Your workspace's bot token is attached automatically, so you pass Slack arguments only (such as channel and text), never a workspace or team id.

<Note>
  Foundry actions for Microsoft Teams are not yet available. See the [Microsoft Teams integration overview](/integrations/microsoft-teams/overview) for the current beta scope.
</Note>

***

## Advanced: how generation and testing work

Foundry validates generated code in two phases, automatically:

1. **TypeScript compilation** catches syntax errors, type mismatches, and bad imports. Foundry auto-fixes failures up to 3 times.
2. **Dry-run execution** runs the code in a sandbox without making real API calls, to catch issues like malformed URLs. Foundry auto-fixes failures up to 2 times.

When you run a real test from the **Test** tab, you pick the runtime environment (`development`, `staging`, or `production`) that the function sees via `ctx.env`. The function runs in an isolated sandbox with the credentials resolved for each of its integrations, limited to 30 seconds per execution.

When you publish, Foundry runs a final validation, then generates an AI tool prompt that helps agents decide when to call the function. You can regenerate that prompt from the function's **Configure** tab.

<Callout icon="link" color="#6B7280">
  Learn how to [use published functions](/documentation/automate/foundry/using-actions) in workflows and with AI agents.
</Callout>


## Related topics

- [Use Foundry functions in workflows and agents](/documentation/automate/foundry/using-actions.md)
- [OAuth setup for Google Cloud](/guides/how-to/foundry/setup-oauth/google-cloud.md)
- [Set up a custom API integration](/guides/how-to/foundry/setup-custom-api.md)
- [OAuth setup for GitHub](/guides/how-to/foundry/setup-oauth/github.md)
- [OAuth setup for DocuSign](/guides/how-to/foundry/setup-oauth/docusign.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.