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:
- Create a Custom App -- Define the integration (name, category, description) on the Integrations page.
- Connect and configure authentication -- Set up the auth scheme (API Key or OAuth 2.0), define credential fields, and establish a live connection.
- Build and test actions -- Create actions, write the API call logic using
call_ema_connectorin a Custom Code agent, define inputs and outputs, and run the persona. - 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
- Navigate to the Integrations page.
- Click New Custom App in the top-right corner.
- 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.
- Click Confirm.
The new Custom App appears in the integrations homepage under all apps.
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
- Open the Custom App by clicking its card on the Integrations page.
- In the Overview tab, locate the Intelligent Actions integration banner and click Connect.
- 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.
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.
- 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. - 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.
- 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.
- Field Name:
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).
- Bearer Token -- sent as
- Check Secret to mask the value in the UI.
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.
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.
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
- Open the Custom App modal and navigate to the Actions tab.
- Click New action.
- Enter a Name and Description for the action.
- Click Confirm.
A new browser tab opens to the Action Builder (GWE canvas) with the configuration modal pre-opened.
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:
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
- Click the Add Agent button on the canvas to add an agent step.
- Select Custom Code Agent to write a script, Convert to Text to transform data, or Custom Agent for configurable task execution.
- Connect the steps with edges to define data flow.
- Reference workflow inputs in your script using
inputs.<input_name>. - 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.
- Open the Named Results panel on the right side of the canvas.
- 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:
- In the Custom Code agent node on the canvas, write or edit your script.
- Use the Test button within the agent to execute the script.
- The agent makes live
call_ema_connectorcalls against the real API. - 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):
- In the Action Builder canvas, click Use AI Employee in the top-right header.
- This opens the dashboard view for the action's persona.
- Add a row with test input values (for example, a sample ticket title or user email).
- Run the workflow. Ema executes the full flow -- including all
call_ema_connectorcalls -- against the live API. - 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.
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_idfrom 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_connectorcalls will fail.
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
- Navigate to AI Employees and open any AI Employee of your choice, or create a new one.
- In the canvas, click to add a tool step.
- In the Select App modal, find and select your Custom App from the list.
- The Tools tab opens showing all registered actions for this Custom App. Click the action you want to add.
- 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.
- 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.
- Click Save.
What happens at runtime
When an AI Employee invokes a Custom App action during a workflow:
- The Intelligent Actions framework identifies that the action matches the current task and calls the action.
- The Workflow Engine executes the action synchronously.
- The Custom Code agent runs your JavaScript. Each
call_ema_connectorcall is routed through EmaConnector, which resolves the tenant's stored credentials, injects authentication, and forwards the request to the external API. - The external API responds. EmaConnector returns the response to your script.
- Your script processes the response and returns the result.
- The workflow's named outputs are returned to the AI Employee as the tool response.
- 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 |
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
- Open the Custom App modal from the Integrations page.
- Click the pencil icon (Edit integration) in the header.
- Update any combination of Application name, Category, Description, Base URL, auth URLs, or auth fields.
- Click Save Changes.
Unsaved changes are discarded if you close the modal without saving.
Exporting and importing
Export:
- Open the Custom App modal and go to the Actions tab.
- Click Export (visible only when the app has at least one action).
- Follow the export wizard to generate a portable definition of the app's actions.
Import:
- On the Integrations page, click Import in the top-right corner.
- Follow the import wizard to upload a previously exported definition.
- The imported Custom App and its actions appear in the integration list.
Deleting a Custom App
- Open the Custom App modal.
- Click the trash icon (Delete integration) in the header.
- A confirmation dialog appears.
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_idis 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 -- How the Intelligent Actions agent selects and executes tools.
- Integration List -- Full catalog of pre-built connectors.