---
title: Functions
slug: optimizely-connect-platform/docs/functions-ocp2
docTags: 
createdAt: 2026-07-23T12:37:05.104Z
---

:::hint{type="info"}
**📘 Note**
A function in Optimizely Connect Platform (OCP) is a webhook.
:::

In OCP, [functions](docId\:Uu00e5AC-qCQ2uuN5mLwh) are small, executable blocks of code typically triggered by a real-time event. An app can have any number of functions stored in the `src/functions` directory. Common examples of functions include the following:

- Subscription to a list.
- Order purchase, refund, or cancellation.
- Marketing consent update survey submission.
- Product review or rating.

Typically, functions are triggered by an HTTP request (a webhook), although you can also use functions to populate app settings form fields. This document covers functions for handling webhooks. For more information about how to use functions as a data source in app settings forms, see [Remote select options](docId\:Ymt8R5EsKRYBHwqG3pJkL).

## Define functions

Define functions in your app's `app.yml` file.

```yaml
functions:
  event_handler: #<-- name used for form sources and reference within the app
    entry_point: Event #<-- File and Class name from src/functions folder
    description: Processes incoming events
```

## Implement a function

The following are the requirements for any functions you define in your `app.yml` file:

- Add the function's code in the `src/functions` directory. For more information, see [\`src/functions\`](docId\:FifhNlzXjuvAjtdDXzMLZ).
- The file and class name must match the `entry_point` defined in the `app.yml` file.
- The class must extend the [\`Function class\`](docId:9aYPg-fHXViFISt2Ikd8C) from the [\`@zaiusinc/app-sdk package\`](docId\:gxEU_0AA37IEZJK-uoVcT) and implement the [\`perform method\`](docId:9aYPg-fHXViFISt2Ikd8C).

```typescript
import * as App from '@zaiusinc/app-sdk';

export class Event extends App.Function {
  public async perform(): Promise<App.Response> {
    
    // processing logic here
    
    return new App.Response(200);
  }
}
```

## Function execution (default)

An app is installed for a specific OCP account, and when executing a function, you need to execute the function in the correct installation context for the account.

The default method to resolve the installation context is to use a `GUUID` that is contained within the public URL. All functions using the default installation resolution method (`GUUID`), defined in the `app.yml` file, are exposed using a public URL of the following format:

```javascript
https://[REGIONAL_DOMAIN]/[APP_ID]/[FUNCTION]/[UUIDv4]
```

The `event_handler` function, defined in the [Define functions](docId\:Uu00e5AC-qCQ2uuN5mLwh) section, uses the default installation resolution and therefore has a public URL that contains the `GUUID` to identify the installation context on function execution. You can retrieve the public URL using the following CLI command:

```javascript
$ ocp directory list-functions app_id account_tracker_id
```

Usually, the default `GUUID` installation resolution method is sufficient to achieve most tasks and to integrate with third-party services.

## Function execution (fixed URL)

In some cases, there is a need to have a fixed URL that does not change on a per-installation basis, like the `GUUID`-based URLs described previously. This requirement most likely arises due to a third-party service that, for example, only lets you define one URL for all function calls it makes to your OCP app.

To enable this scenario, you can specify an `installation_resolution` strategy when defining your function.

```yaml
    installation_resolution: #<-- optional, default is GUUID
      type: HEADER | QUERY_PARAM | JSON_BODY_FIELD
      key: string
```

The `installation_resolution` is an optional property that, if defined, lets you specify how a function should resolve the installation context. The URL is the same for all installations, but the function is resolved to the correct installation based on the `installation_resolution` property.

Taking the previous example, extend the function definition in the `app.yml` file as follows:

```yaml
functions:
  event_handler_header: 
    entry_point: Event
    description: Processes incoming events
    installation_resolution:
      type: HEADER
      key: x-ocp-api-key
```

The value set for the `x-ocp-api-key` header is used to resolve the context of the correct account installation.

:::hint{type="warning"}
**🚧 Important**
The value following the key must always be the public API key of the account you want to resolve.
:::

The following are the supported types of installation resolution:

- `HEADER` – The function call is resolved to the installation context based on the value of the specified header. For example, `https://function.zaius.app/my_app/event_handler_header` with a header of `x-ocp-api-key` and a value of `trackerId`.
- `QUERY_PARAM` – The function call is resolved to the installation context based on the value of the specified query parameter. For example, `https://function.zaius.app/my_app/event_handler_query?apiKey=trackerId`.
- `JSON_BODY_FIELD` – The function call is resolved to the installation context based on the value of the specified field in the JSON body. For example, `https://function.zaius.app/my_app/event_handler_json` with key `$.apiKey` uses the value found for the `apiKey` field in the JSON body(`{"apiKey": "trackerId", "data": [1,2,3]}`). The `key` for the `JSON_BODY_FIELD` resolution must be a valid [JSONPath](https://github.com/json-path/JsonPath) expression and must point to a string value. Additionally, every request must contain the header `Content-Type: application/json`.

:::hint{type="warning"}
**🚧 Important**
If you modify a [global function](docId:5qPanRhjTFsuT38y6Gy-G) to a function with installation resolution between app versions, without changing the name of the function, both the global function and function with installation resolution are deployed under the same public URL.

In case an incoming request matches the defined installation resolution type, key, and value from the app manifest, OCP only calls the function with installation resolution. If the request does not match the defined installation resolution attributes, OCP calls the global function.
:::

You can use the same CLI command as previously documented to see the public URLs for the function and the required request attributes.

```shell
$ ocp directory list-functions ocp_shakedown vdl
https://function.staging.zaius.app/ocp_shakedown/event_handler/61258d4b-dc70-48cb-9e2a-************
https://function.staging.zaius.app/ocp_shakedown/event_handler_headers                                          (required HTTP Header with name 'x-api-key' and the value set to public tracking id)
https://function.staging.zaius.app/ocp_shakedown/event_handler_json
(required JSON body field that matches JSONPath '$.apiKey' and the value set to public tracking id and HTTP Header set to 'Content-Type: application/json')
https://function.staging.zaius.app/ocp_shakedown/event_handler_query                                            (required URL query parameter with name 'apiKey' and the value set to public tracking id)
```

In the app code, you can get the URL using the [App-SDK getEndpoints method](docId\:Uu00e5AC-qCQ2uuN5mLwh).

```typescript
import { functions } from '@zaiusinc/app-sdk';
let webhookURL = (await functions.getEndpoints())['event_handler']
```

You should then register the URL with the source of the event. The process for registering the URL depends on the third-party service that sends events.

:::hint{type="warning"}
**🚧 Important**
You must define any publicly available functions (for example, webhooks) in the `app.yml` file.
:::

Functions have access to the HTTP request path, query parameters, headers, and body. For example, the base URL for a function may look like the following: `https://function.staging.zaius.app/my_app/cart/5f57ecbb-ef28-4a25-9056-3ea98994e2a5/`

You can support additional paths and query parameters added to your URL. For example, `https://function.staging.zaius.app/my_app/cart/5f57ecbb-ef28-4a25-9056-3ea98994e2a5/update?cart-id=12345`

The path and query parameters are accessible to your function, body, and headers of the HTTP request.

```typescript
import * as OCP from '@zaiusinc/app-sdk';

export class Event extends OCP.Function {
  public async perform(): Promise<App.Response> {
    const path = this.request.path; // "/update"
    const cartId = this.request.params['cart-id']; // "12345"
    const body = this.request.bodyJSON || {}; // '{"request_id":"12345","products":[...], ...}'
    const headers = this.request.headers; // { "Content-Type": "application/json", ... }

    // processing logic here

    return new App.Response(200);
  }
}
```

## CMS UI extension functions

:::hint{type="info"}
**📘 Beta**
The following feature is in beta.
:::

A function in a [CMS UI extension](docId\:NV6r9sjdc_6Dhs0_dvf6S) app can act as the backend proxy for a UI bundle. Mark such functions with `accepts: cms_ui_extension` in `app.yml`:

```yaml
functions:
  cms_extension:
    entry_point: CmsUiExtension
    description: Backend proxy for the sidebar extension.
    accepts: cms_ui_extension
```

Set `accepts: cms_ui_extension` **only** on functions invoked from a CMS UI extension bundle through `context.extension.invokeFunction(<functionId>, payload)`. Functions exposed as HTTP webhooks (the default kind described above) or used as form sources keep their existing definition and omit `accepts`. A CMS UI extension app can declare both kinds of functions in the same `functions:` section.

Functions with `accepts: cms_ui_extension` are not reachable through a public URL — the CMS host delivers requests to them through the platform. The recommended shape lets one function serve many UI operations: read an `action` field from the request body, dispatch to a handler, and return a `{ok, result} | {ok, error}` envelope. See [CMS UI extensions](docId\:NV6r9sjdc_6Dhs0_dvf6S) for a complete example.

Two constraints apply to functions with `accepts: cms_ui_extension`:

- They cannot be [global functions](docId:5qPanRhjTFsuT38y6Gy-G).
- They cannot define an `installation_resolution` strategy.

## Function example

```typescript
import * as App from '@zaiusinc/app-sdk';
import {logger} from '@zaiusinc/app-sdk';
import {z} from '@zaiusinc/node-sdk';
import {IncomingEvent} from '../data/IncomingEvents';
import {transformToCustomer} from '../lib/transformToCustomer';

/**
 * Example event handler.
 * Expects a request in the form:
 *  url: https://[webhook-url]/?email=<email>
 * with a JSON body.
 * Fires an ODP event and updates the customer's name in ODP
 */
export class HandleEvent extends App.Function {
  /**
   * Handle a request to the handle_event function URL
   * this.request contains the request information
   * @returns App.Response as the HTTP response
   */
  public async perform(): Promise<App.Response> {

    const email = this.request.params['email'] as string;

    if (!email) {
      return new App.Response(400, 'Missing required email parameter');
    } else {
      try {
        const event = this.request.bodyJSON as IncomingEvent;

        // TODO: transform your event data into ODP API calls
        if (event.customer) {
          await z.customer(transformToCustomer(event.customer));
        }
        await z.event({
          type: event.type,
          identifiers: {
            email
          },
          data: {
            action: event.action
          }
        });

        // return the appropriate status or response
        return new App.Response(200);
      } catch (e) {
        logger.error(e);
        return new App.Response(500, `An unexpected error occurred: ${e}`);
      }
    }
  }
}
```
