> Source: https://builder-docs.ema.ai/integrations-data/custom-integrations
> Title: Custom Integrations

# Custom Integrations

Custom Apps let you connect any external API to Ema so your AI Employees can use it directly. Whether you need to reach an internal system, a niche SaaS product, or a third-party service, you can build a Custom App for it. Once it is set up, you can add as many actions as you need -- and your AI Employees will be able to call them during their workflows.

## When to Build a Custom Integration

-   You need an AI Employee to call an internal microservice or a niche SaaS product not yet in the Ema catalog.
-   You want to wrap a third-party REST or GraphQL endpoint as a reusable tool that multiple AI Employees can share.
-   You have an on-premises system exposed through an API gateway and want Ema to interact with it securely.

## Getting Started

Getting a Custom App action running takes four steps:

1.  **Create a Custom App** -- Define the integration (name, category, description) on the Integrations page.
2.  **Connect and configure authentication** -- Set up the auth scheme (API Key or OAuth 2.0), define credential fields, and establish a live connection.
3.  **Build and test actions** -- Create actions, write the API call logic using `call_ema_connector` in a Custom Code agent, define inputs and outputs, and run the persona.
4.  **Add to an AI Employee** -- Add the registered action as a tool in an AI Employee's workflow using Intelligent Actions so it can be invoked at runtime.

## Key Concepts

Term

Definition

**Custom App**

A user-defined integration backed by an external API. It appears alongside pre-built integrations on the Integrations page and can hold one or more actions.

**Action**

A discrete API operation (for example, "Get User" or "Create Ticket") attached to a Custom App. Each action is configured in the Action Builder, where you define its inputs, outputs, and API call details.

**Connection**

A set of stored credentials that authenticate Ema against the Custom App's API. A Custom App can have one connection and multiple actions.

**Auth Scheme**

The authentication method used by the Custom App -- either OAuth 2.0 or API Key.

**Template Fields**

Auth-related fields defined at the integration level. These are shared across all connections.

**Connection Fields**

Auth-related fields that each individual connection supplies (for example, the actual API key value or user-specific token).

**Placement**

Determines where an auth field value is sent in the HTTP request. Options are Bearer Token, Custom Header, Query Parameter, JSON Body, and Form Body.

**Persona**

Every action is powered by a persona. These personas use Custom App configurations to call external APIs on your behalf. When you create an action, Ema generates a persona (Dashboard persona) that you configure in the Action Builder.

## Building a Custom Integration

### Step 1 -- Create a Custom App

1.  Navigate to the **Integrations** page.
2.  Click **New Custom App** in the top-right corner.
3.  In the modal that opens, fill in the required fields:
    -   **Application name** -- enter a unique name (letters, numbers, and spaces only).
    -   **Category** -- select a category from the dropdown (for example, CRM, Productivity, Custom).
    -   **Description** -- describe what this integration does. This text is shown on the Integrations page.
4.  Click **Confirm**.

The new Custom App appears in the integrations homepage under all apps.

> [INFO]
> App names must be unique across all integrations, including pre-built ones.

### Step 2 -- Connect and Configure Authentication

Authentication setup walks you through defining your auth scheme, filling in credentials, and establishing a connection. The steps differ depending on whether you choose API Key or OAuth 2.0.

#### Start the connection wizard

1.  Open the Custom App by clicking its card on the Integrations page.
2.  In the **Overview** tab, locate the Intelligent Actions integration banner and click **Connect**.
3.  A **Connection options** dropdown appears. Select your auth type:
    -   **API Key** -- for services that authenticate with a static key or token.
    -   **OAuth 2.0** -- for services that use the OAuth 2.0 authorization flow.

> [WARNING]
> **Important:** Once an auth type is selected and configured, it cannot be changed.

#### Phase 1 -- Define the auth configuration

This phase collects the Base URL, scheme-specific settings, and auth field definitions. Refer to your external API's documentation to determine the correct values for each field.

1.  In the setup modal, enter the **Base URL** -- the root URL of the API (for example, `https://api.linear.app/graphql`). This field is always required.
2.  If you selected **OAuth 2.0**, fill in these additional fields:
    -   **Auth URL** -- the authorization endpoint.
    -   **Token URL** -- the token endpoint.
    -   **Scopes** -- comma-separated OAuth scopes (optional).
    -   A read-only **Redirect URI** is displayed for you to copy and register with the OAuth provider.
3.  If you selected **API Key**, the Connection Fields section is pre-populated with a default field:
    
    -   Field Name: `api_key`
    -   Display Name: API Key
    -   Placement: Custom Header
    -   Header Name: `Authorization`
    -   Secret: checked
    
    You can edit these defaults or add more fields to match your API's requirements.
    

Configure **Template Fields** and **Connection Fields**. Both field types share the same structure -- the difference is who provides the value and when:

-   **Template Fields** are set once during integration setup and shared across all connections. Use these for credentials that belong to the integration itself.
-   **Connection Fields** are filled in each time a connection is created. Use these for credentials that are specific to a user or environment.

Template Fields

Connection Fields

**OAuth 2.0**

`client_id`, `client_secret` (provided by the OAuth provider when you register your app)

Typically none -- the OAuth flow handles user-specific tokens automatically

**API Key**

Rarely used -- most API key setups only need connection fields

The API key value itself (for example, `api_key`), workspace tokens, or environment-specific secrets

For each field, provide:

-   **Field Name** -- a machine-readable identifier. Check your API documentation for the expected parameter name.
-   **Display Name** -- the label shown to users.
-   **Placement** -- where the value is injected into outgoing HTTP requests:
    -   **Bearer Token** -- sent as `Authorization: Bearer <value>`.
    -   **Custom Header** -- sent as a custom HTTP header (specify **Header Name**).
    -   **Query Parameter** -- appended to the URL (specify **Param Name**, for example `api_key`).
    -   **JSON Body** -- included in the request body as JSON (specify **Field Name**).
    -   **Form Body** -- included in the request body as form data (specify **Field Name**).
-   Check **Secret** to mask the value in the UI.

> [TIP]
> Refer to your API provider's documentation to determine the correct placement. For example, some APIs expect the key in an `Authorization` header, while others expect it as a query parameter.

Click **Save & Continue**.

#### Phase 2 -- Fill template credentials

If your auth scheme has template-level fields, you are prompted to fill in their values. Enter values for each template field shown in the form and click **Save & Continue**.

> [INFO]
> If no template fields are defined, this phase is skipped automatically.

#### Phase 3 -- Enter connection credentials

This is where you provide the actual credentials for the connection:

-   **API Key:** Enter your API key value in the field shown.
-   **OAuth 2.0:** Enter any runtime connection fields required by the provider.

Click **Connect** when done.

#### Phase 4 -- Complete the connection

-   **API Key:** The connection is established immediately. A success toast confirms the connection.
-   **OAuth 2.0:** A browser popup opens to the OAuth provider's authorization page. Complete the sign-in flow in the popup. Once authorized, the popup closes and the connection is established.

On success, the banner button changes from **Connect** to **Disconnect**, and your actions can now execute against the API.

> [INFO]
> You can create and configure actions before connecting -- the Actions tab displays an info banner: "Connect this app to run actions. You can still create and configure them." Credentials are validated at runtime. Test the integration in an AI Employee to confirm that authentication works end-to-end.

### Step 3 -- Create an Action

1.  Open the Custom App modal and navigate to the **Actions** tab.
2.  Click **New action**.
3.  Enter a **Name** and **Description** for the action.
4.  Click **Confirm**.

A new browser tab opens to the Action Builder (GWE canvas) with the configuration modal pre-opened.

> [INFO]
> Action names must be unique within the Custom App. The UI shows an error if multiple actions share the same name within an integration.

## Build an Action

After clicking Confirm, the Action Builder persona opens in a new tab. This is the same GWE canvas used for AI Employees, but scoped to the three agents available in actions: **Custom Code Agent**, **Convert to Text**, and **Custom Agent**.

The canvas opens with a pre-configured trigger and a default workflow input called `tool_input`. You need to complete three things before the action can be saved and registered: define your inputs, write the workflow logic, and declare at least one named output.

### Define workflow inputs

Workflow inputs become the action's input parameters -- the values an AI Employee supplies when it calls this tool. The canvas starts with a default `tool_input` input. You can edit it or add more.

Open the trigger node and click to add or edit a workflow input. For each input, provide:

-   **Name** and **Display name** -- the machine name and human-readable label.
-   **Description** -- helps the AI Employee understand what value to pass. Write this clearly -- it directly affects how well the AI selects and fills this parameter.
-   **Type** -- select the data type (String, Int, Float, Boolean, Date, DateTime, Object, Array, and others).
-   **Required** -- whether the AI Employee must provide this value.

Optionally, expand **Additional parameters** to configure advanced settings:

-   **Input dependencies** -- declare that this input depends on the output of another action within the same Custom App.
-   **User ID domain** -- indicate that this input represents a user identity (Email or MS Active Directory ID). When set, Ema auto-fills the value from the actioning user's identity.

### Build the workflow on the canvas

Use the visual canvas to define the action's logic. Actions support three agents:

-   **Custom Code Agent** -- write JavaScript to call the external API and process the response.
-   **Convert to Text** -- convert any input types into a single text string without using LLMs.
-   **Custom Agent** -- a configurable agent where you define a role, task instructions, and a structured output schema. The Custom Agent uses an LLM to carry out the described task -- useful for summarizing API responses, classifying data, extracting fields from unstructured text, or making decisions based on returned data. Unlike Custom Code (which runs deterministic JavaScript), the Custom Agent handles tasks that benefit from language understanding.

#### Writing the API call with Custom Code

The Custom Code agent uses `call_ema_connector` -- a built-in JavaScript function that makes authenticated HTTP calls through Ema's proxy. Ema automatically injects your stored credentials (API key or OAuth token) into every request.

**Function signature:**

```javascript
call_ema_connector(integration_id, method, path, body?, headers?)
// Returns: { ok, status, body, headers, request_log_id }
```

**Parameters:**

Parameter

Type

Description

`integration_id`

string

The Custom App's integration ID (must be declared in the action's config)

`method`

string

HTTP method: `"GET"`, `"POST"`, `"PUT"`, `"PATCH"`, `"DELETE"`

`path`

string

API path appended to the Base URL (for example, `"/tickets"`)

`body`

string | null

(Optional) Request body -- sent as JSON

`headers`

object

(Optional) Additional HTTP headers

**Response object:**

Field

Type

Description

`ok`

boolean

`true` if the HTTP status is 2xx

`status`

number

HTTP status code

`body`

string

Response body (UTF-8 text)

`headers`

object

Response headers

`request_log_id`

string

Unique ID for correlating with request logs

**Restrictions and limits:**

Restriction

Detail

Integration allowlist

The `integration_id` passed to `call_ema_connector` must be declared in the action's Tool Persona Config. Calls to any other integration are blocked with a runtime error.

Call limit

Maximum 20 `call_ema_connector` calls per script execution. If your use case requires more, batch your requests or paginate efficiently.

Request body size

Capped at 1 MB per request.

Response format

Text and JSON only. Binary responses (files, images) and streaming are not supported.

Timeout

Each external HTTP call has a 60-second deadline. If the API does not respond in time, the call throws an error.

Error behavior

`call_ema_connector` does not throw on 4xx/5xx responses. Your script must check `response.ok` or `response.status` and handle errors explicitly.

Credential injection

You never pass API keys or tokens in your script. Ema injects credentials automatically based on your Placement config. Do not hardcode secrets.

#### Building the workflow

1.  Click the **Add Agent** button on the canvas to add an agent step.
2.  Select **Custom Code Agent** to write a script, **Convert to Text** to transform data, or **Custom Agent** for configurable task execution.
3.  Connect the steps with edges to define data flow.
4.  Reference workflow inputs in your script using `inputs.<input_name>`.
5.  Return the data you want to pass to downstream steps or to the named output.

### Define named outputs

Named outputs define what the action returns to the AI Employee. At least one named output is required -- without it, the action cannot be saved or registered as a tool.

1.  Open the **Named Results** panel on the right side of the canvas.
2.  Add one or more named results. For each, provide:
    -   **Name** -- identifier for the output.
    -   **Description** -- explain what this output contains.
    -   **Type** -- the data type of the returned value.

### Save and register the action

Click **Save Changes** in the canvas header. On save, Ema automatically registers the action as an external tool in the action registry. No separate publish step is needed.

The action is now discoverable by any AI Employee in your workspace. Every subsequent save re-registers the action with any updated inputs, outputs, or logic.

## Test an Action

Actions are fully runnable personas. You can test them in two ways: directly from the Custom Code agent during development, and from the dashboard as a full end-to-end run.

### Test from the Custom Code agent

While building your script in the Custom Code agent, you can run it directly to verify your API calls work:

1.  In the Custom Code agent node on the canvas, write or edit your script.
2.  Use the **Test** button within the agent to execute the script.
3.  The agent makes live `call_ema_connector` calls against the real API.
4.  Review the script output inline to confirm the response shape and values.

This is useful for iterating quickly on your script logic -- checking response parsing, error handling, and field mapping -- without running the full workflow.

### Test from the dashboard

For a full end-to-end test that exercises the entire workflow (trigger, inputs, agents, and named outputs):

1.  In the Action Builder canvas, click **Use AI Employee** in the top-right header.
2.  This opens the dashboard view for the action's persona.
3.  Add a row with test input values (for example, a sample ticket title or user email).
4.  Run the workflow. Ema executes the full flow -- including all `call_ema_connector` calls -- against the live API.
5.  Review the output rows in the dashboard results. Each run produces a row showing the named output values.

You can add multiple rows with different input combinations to test various scenarios in a single session.

> [INFO]
> Even when this action runs as an embedded tool inside an AI Employee at runtime, the execution is still recorded. You can return to this persona's dashboard at any time to see rows from all runs -- including those triggered by AI Employees in production. This gives you full visibility into how the action is being called and what it returns.

### What to verify

-   **Correct API calls:** Check that the right endpoints are hit with the right method and payload. Use the `request_log_id` from the response to correlate with request logs.
-   **Auth works end-to-end:** If you see authentication errors (401/403), verify your connection credentials in the Custom App settings (Step 2).
-   **Error handling:** Test with invalid inputs, missing fields, and edge cases. Confirm your script handles non-2xx responses gracefully rather than silently failing.
-   **Output shape:** Verify that the named outputs contain the expected data and types. The output schema is what AI Employees see when they consume this tool.
-   **Connection status:** The Custom App must be connected for test runs to succeed. If disconnected, the `call_ema_connector` calls will fail.

> [INFO]
> Credentials are validated at runtime, not at save time. A successful save does not guarantee that API calls will work -- always test with real credentials and inputs.

## Run an Action in GWE Using Intelligent Actions

Once an action is saved and registered, it is available as a tool and must be added to an AI Employee before it can execute in production workflows.

### Add the tool to an AI Employee

1.  Navigate to **AI Employees** and open any AI Employee of your choice, or create a new one.
2.  In the canvas, click to add a tool step.
3.  In the **Select App** modal, find and select your Custom App from the list.
4.  The **Tools** tab opens showing all registered actions for this Custom App. Click the action you want to add.
5.  In the **Configure Tool** modal, set up each input parameter:
    -   **Auto fill by Ema** -- the AI Employee infers the value at runtime based on conversation context. Use this for dynamic inputs like user queries, email content, or extracted entities.
    -   **Fixed value** -- you provide a constant value that is used every time the tool runs. Use this for static config like environment names, default statuses, or fixed IDs.
6.  Optionally, enable **HITL** -- when checked, the AI Employee pauses before executing this tool and presents the proposed parameters for human approval. The tool only runs after the human confirms.
7.  Click **Save**.

### What happens at runtime

When an AI Employee invokes a Custom App action during a workflow:

1.  The Intelligent Actions framework identifies that the action matches the current task and calls the action.
2.  The Workflow Engine executes the action synchronously.
3.  The Custom Code agent runs your JavaScript. Each `call_ema_connector` call is routed through EmaConnector, which resolves the tenant's stored credentials, injects authentication, and forwards the request to the external API.
4.  The external API responds. EmaConnector returns the response to your script.
5.  Your script processes the response and returns the result.
6.  The workflow's named outputs are returned to the AI Employee as the tool response.
7.  The AI Employee uses the returned data in subsequent workflow steps.

### Runtime behavior and limits

Behavior

Detail

Auth injection

Ema handles credential injection automatically -- API keys are placed per your Placement config, OAuth tokens are refreshed and injected as Bearer tokens

Connection scope

One connection per Custom App, shared across the entire tenant

Call limit

Maximum 20 `call_ema_connector` calls per script execution

Request size

Request body capped at 1 MB

Timeout

60-second deadline per external HTTP call

Supported content

Text and JSON APIs only -- no binary or streaming responses

Error behavior

Non-2xx responses do not throw automatically -- your script must handle them

Versioning

Updates take effect immediately after save. There is no version history

Human-in-the-Loop

If enabled, the AI Employee pauses and shows proposed parameters before executing the tool. Execution resumes only after human approval

> [INFO]
> The Custom App must be connected (Step 2) for the AI Employee to execute the action at runtime. You can add and configure tools before connecting, but execution will fail until credentials are in place.

## Managing Custom Apps

### Editing a Custom App

1.  Open the Custom App modal from the Integrations page.
2.  Click the pencil icon (**Edit integration**) in the header.
3.  Update any combination of Application name, Category, Description, Base URL, auth URLs, or auth fields.
4.  Click **Save Changes**.

Unsaved changes are discarded if you close the modal without saving.

### Exporting and importing

**Export:**

1.  Open the Custom App modal and go to the **Actions** tab.
2.  Click **Export** (visible only when the app has at least one action).
3.  Follow the export wizard to generate a portable definition of the app's actions.

**Import:**

1.  On the Integrations page, click **Import** in the top-right corner.
2.  Follow the import wizard to upload a previously exported definition.
3.  The imported Custom App and its actions appear in the integration list.

### Deleting a Custom App

1.  Open the Custom App modal.
2.  Click the trash icon (**Delete integration**) in the header.
3.  A confirmation dialog appears.

> [WARNING]
> **Destructive action:** Deleting a Custom App permanently removes the integration and all its connections. Any actions (tools) built on this integration will be marked as deprecated and will no longer execute during AI Employee runs. This cannot be undone.

Click **Delete** to confirm, or **Cancel** to go back.

## Best Practices

-   **Write clear descriptions.** The Intelligent Actions agent relies on tool and action descriptions to decide which tool to invoke. Ambiguous descriptions lead to incorrect tool selection.
-   **Define all required parameters explicitly.** If a required parameter is missing, the API call will fail at runtime.
-   **Test before enabling.** Use the Custom Code agent's test button and the dashboard view (**Use AI Employee**) to catch errors before adding the action to a production workflow.
-   **Keep actions granular.** Define one action per distinct operation rather than combining multiple operations into a single action.
-   **Rotate credentials proactively.** Use maintenance mode when rotating API keys or OAuth secrets to avoid transient failures.
-   **Always check for API errors in your script.** If you do not add error handling, your action might look like it worked even when the API call actually failed -- returning empty or incorrect data without any warning.
-   **Write descriptive workflow input descriptions.** The AI Employee uses input descriptions to decide what value to pass at runtime. Vague descriptions like "ID" lead to incorrect parameter mapping -- use something like "The unique project ID returned by the Get Projects action."
-   **Enable HITL for high-impact write actions.** For actions that create, update, or delete data (for example, "Create Ticket" or "Delete User"), enable Human-in-the-Loop so a human can review parameters before execution.
-   **Use named outputs with accurate types.** The output schema is what AI Employees see when consuming the tool. Mismatched types or vague names cause downstream steps to misinterpret the data.

## FAQ

-   **Can I create a Custom App with the same name as a pre-built integration?** No. App names are checked case-insensitively across all integrations -- pre-built and custom. Choose a unique name.
-   **What happens to actions if I delete the Custom App?** All actions tied to the deleted app are marked as deprecated and will no longer run during AI Employee execution. Any AI Employees consuming those tools are flagged as broken. This is irreversible.
-   **What characters are allowed in the app name?** Only letters, numbers, and spaces. Special characters and symbols are not permitted.
-   **Can I rename the Tool ID after creating an action?** No. The `tool_id` is auto-generated from the action name at creation time and is immutable. If you need a different Tool ID, delete the action and create a new one.

## Limitations

-   Custom tool integrations currently support HTTP-based APIs only. gRPC, WebSocket, and other protocols are not supported.
-   Custom data connectors (for importing content into Ema's knowledge base) are under development and not yet available.
-   File upload as part of a custom tool action is not supported in the current release.
-   Each Custom App supports only one connection per tenant. You cannot configure multiple connections (for example, for different environments or users) under the same integration.
-   There is no version history for actions. Every save overwrites the previous configuration immediately with no rollback option.

## Related

-   Custom Tool Integrations (Workflow Building) -- Detailed reference for building custom tools in the workflow builder.
-   [Intelligent Actions](/legacy-docs/integrations-data/intelligent-actions) -- How the Intelligent Actions agent selects and executes tools.
-   [Integration List](/legacy-docs/integrations-data/integration-list) -- Full catalog of pre-built connectors.
