> For the complete documentation index, see [llms.txt](https://docs.siit.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.siit.io/integrations/custom-integrations.md).

# Custom integrations

Connect Siit to any HTTPS API without a built-in connector, then declare the actions Siit can run on it from requests, workflows and app access roles

***

A custom integration connects Siit to a tool that has an HTTPS API but no built-in Siit connector: an internal service, a niche SaaS or a homegrown tool. You set it up once with its credentials, then declare **custom actions**, the operations Siit can run on that API, such as *Create a VPN account*, *Reset a badge* or *Open a purchase order*.

Actions are configured with URLs and input definitions, without code. Siit calls the API at the moment an action runs. Custom integrations don't sync any data in the background.

#### Where custom actions run

| Where                                            | How                                                                                                                            |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| On a request or a person                         | An admin runs the action by hand from the request side panel or the person's **About** menu                                    |
| In a workflow                                    | As a workflow step, with inputs filled from workflow variables. Outputs become variables for later steps and branch conditions |
| In an app access role                            | As a provisioning or deprovisioning step, for apps that have no native connector                                               |
| With [Assist](https://claude.ai/pages/ai/assist) | As a tool Assist can use                                                                                                       |

Every run is recorded with its inputs, the response and its status. Successful runs also appear on the request or person timeline.

#### Before you start

* Custom integrations are available on every plan. The **Custom** category sits in the Integration library of every workspace.
* Creating and editing them requires the **Can configure integrations** permission.
* From the third party's API documentation, gather:
  * the credentials: an API token, an API key and the header it goes in, or a username and password
  * the URL and HTTP method of each endpoint you want to call
  * the response format, if you want to reuse a value from it (a created ID, a status) in later workflow steps

All URLs must use `https://` and point to a public address. Siit refuses `http://` URLs, private or internal IP addresses, and redirects.

#### Create a custom integration

#### Open the Custom category

Go to **Settings → Workspace → Integration library** and open the **Custom** category. It lists one card per custom integration, next to a **Create a custom integration** card.

An integration you open has two tabs, each with its own address so you can link straight to either:

* **Connection** — who the tool is and how Siit signs in to it.
* **Actions** — the operations Siit can run on it.

#### Describe the integration

Under **App details** on the **Connection** tab:

* **Name** (required): usually the name of the tool. It must be unique among your custom integrations.
* **Logo** (optional): shown on cards, workflow steps and menus.
* **API documentation URL** (optional): a link for the next admin who maintains the integration.

#### Choose an authentication method

Pick one of three cards:

| Method             | What Siit sends on every call                                | Fields                        |
| ------------------ | ------------------------------------------------------------ | ----------------------------- |
| **Bearer token**   | `Authorization: Bearer <token>`                              | Bearer token                  |
| **API key header** | The key in a header you name, for example `X-API-Key: <key>` | Header name, API key          |
| **Basic auth**     | `Authorization: Basic <base64(username:password)>`           | Username (no colon), Password |

**Siit will send on every call** under the fields shows the header that will travel, so you can check it against the third party's documentation before you save.

The secret is encrypted and write-only: nobody can view it again, admins included. On a saved integration the field is masked; click **Replace** to type a new one. Leave it alone to keep the stored value.

#### Save

Custom headers and custom actions become available after the first save, because the variables they can use are only known once the integration exists.

#### Custom headers

Add custom headers when the API needs more than the authentication header, for example a tenant ID, an API version or a second credential. They sit under **Advanced: default headers sent on every call**, at the bottom of the **Connection** tab, which stays folded with a count of what it holds.

* Up to 20 headers per integration, sent on every call of every action.
* Names and values accept company variables, custom variables and secrets, such as `{{secrets.my_api_secret}}`. See [Variables & Secrets](https://claude.ai/pages/workflow/variables-and-secrets).
* Siit sets `Accept` and `Content-Type` itself. If a custom header has the same name as the authentication header, the authentication header is used.

Insert variables with the **Insert a variable** button or the `{{` autocomplete. A variable typed by hand, like `{{company.name}}`, stays plain text and reaches the third party as written.

#### Declare a custom action

Open the integration's **Actions** tab and click **New action**. The table lists each action with what it runs on, its status, where its inputs come from, and its last update.

The editor walks through four numbered sections: **What it does**, **What it needs**, **The call** and **Output**.

**1. What it does**

* **Name** (required): what admins see in menus. It must be unique within the integration.
* **Description** (optional): shown to admins when they pick the action.
* **Enabled**: on by default. A disabled action is offered nowhere — not on requests or people, not to workflows, not to app access roles — though workflows and roles already using it keep running it.

**Runs on** decides which variables the action can use and where it can be offered.

| Runs on     | The action runs on    | Variables it can use          | Offered on                                                                     |
| ----------- | --------------------- | ----------------------------- | ------------------------------------------------------------------------------ |
| **Request** | A request             | Title, requester, form fields | Request pages and request workflows                                            |
| **People**  | A person              | Name, email, manager, team    | People and request pages, request and people workflows                         |
| **All**     | Nothing in particular | Request, people and apps      | Request and people pages, and every workflow                                   |
| **Apps**    | An app access request | App, access role, owner       | App access workflows and app access role provisioning steps. Never run by hand |

**Where it's available** is two cards, and at least one must be ticked — an action available nowhere could never run, so Siit refuses to save it.

* **Manual runs** — admins run it by hand from a request page, or from a request page or a person page, depending on what it runs on. An action that runs on **Apps** can't be started by hand, so the card is unavailable.
* **Automations** — workflows can run it as a step, and for **Apps**, app access provisioning too.

**2. What it needs**

Inputs come from one of two places, and you pick which on the action:

* **Static inputs** — you declare them here. Everyone running the action fills in the same ones, by hand or from a workflow.
* **Dynamic inputs** — Siit asks a URL for the inputs each time the action is about to run, so they can depend on live data, for example a list of valid projects. A workflow fills its inputs when it is built, long before that URL is called, so a dynamic action carries a second table, **Inputs for workflows**, with its own slugs.

Add or edit an input in a modal and the table lists what you declared.

**3. The call**

* **HTTP method**: `GET`, `POST`, `PUT` or `DELETE`. New actions default to `POST`.
* **Submit URL** (required): the HTTPS endpoint Siit calls when the action runs.
* **Advanced: extra headers for this action**, folded, for headers on top of the integration's own.

With `POST`, `PUT` and `DELETE`, Siit sends the input values as a JSON object keyed by input slug, previewed under **Body (JSON)**:

```json
{ "email": "jane@acme.com", "department": "sales", "notify": true }
```

An input left empty is sent as `null`, and the shape itself isn't editable from the interface. With dynamic inputs the body is only known when the action runs, so Siit says so instead of previewing it.

{% hint style="info" %} A `GET` request has no body, so a `GET` action can't declare inputs. Siit blocks the save and tells you to remove them or pick another method. {% endhint %}

**Inputs**

Every input uses the same format:

| Field               | Description                                                                                                                                     |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| **Slug**            | The key sent to the API. Lowercase letters, digits and underscores, starting with a letter (for example `user_email`). Unique within the action |
| **Label**           | What admins see                                                                                                                                 |
| **Type**            | **Text**, **Number**, **Yes / No**, **Date** or **Choice**                                                                                      |
| **Choices**         | For **Choice** inputs, a comma-separated list of accepted values, for example `low, medium, high`                                               |
| **Required**        | Whether a value is mandatory                                                                                                                    |
| **Multiple values** | Available for **Text** and **Choice** inputs only                                                                                               |

An input definition URL must return a JSON array in the same format, where `kind` is `text`, `number`, `boolean`, `date` or `enum`:

```json
[
  { "slug": "project", "label": "Project", "kind": "enum", "values": ["Alpha", "Beta"], "required": true },
  { "slug": "reason", "label": "Reason", "kind": "text" }
]
```

**4. Output**

Declare the fields of the JSON response you want to reuse in later workflow steps. Each output has a slug, a label and a type, and the slug can be a path into the response. As with inputs, you add or edit one in a modal and the table lists them:

| Response                    | Output slug   | Value     |
| --------------------------- | ------------- | --------- |
| `{"status": "created"}`     | `status`      | `created` |
| `{"data": {"userId": 42}}`  | `data.userId` | `42`      |
| `{"items": [{"id": "a1"}]}` | `items.0.id`  | `a1`      |

Outputs show up in the workflow variable picker and in [branch conditions](https://claude.ai/pages/workflow/branching-and-approvals), for example to continue only when `status` is `created`. A path missing from the response gives an empty value. One output can't sit inside another, so you can't declare both `data` and `data.userId`.

**Action headers**

Each action can have up to 20 headers of its own, sent on top of the integration headers. An action header overrides an integration header with the same name. The variables you can insert depend on what the action runs on:

| Runs on | Variables                                               |
| ------- | ------------------------------------------------------- |
| Request | Request, requester, assignee and request form variables |
| People  | The person's variables                                  |
| Apps    | App access request variables                            |
| All     | Company and date variables                              |

Secrets are available in every case.

#### Use custom actions

**On a request or a person**

Enabled actions ticked for **Manual runs** appear in the **Custom actions** section of the [request side panel](https://claude.ai/pages/request-management/side-panel), grouped by integration. **People** and **All** actions also appear in the person's **About** menu, grouped by integration the same way, with each integration's actions revealed on hover.

Pick an action, fill in the inputs if any, and confirm. Siit runs it right away and shows whether it succeeded, with the error if it failed. A successful run is added to the timeline, for example "*Jane* ran *Create VPN account*".

**In a workflow**

In the workflow editor, each custom integration has its own group of actions. Only enabled actions that match the workflow's trigger are listed. Pick one, fill in its inputs, and save. When the workflow runs on a request, the timeline shows "*Create VPN account* ran automatically".

**In an app access role**

Actions that run on **Apps** can grant and revoke access in tools with no native connector:

1. Open the application's access rules and edit an app access role.
2. Under provisioning or deprovisioning, choose **Custom action**.
3. Select the custom integration, then the action, and fill in its inputs.
4. To have someone finish the job by hand, add a manual step after the custom action.

If no deprovisioning action is set, revoking access creates a manual task for the app owner. See [App access policies](https://claude.ai/pages/data-management/app-access-policies).

**With Assist**

Assist can find and run the custom actions ticked for **Manual runs**. It only sees the actions the person it works for is allowed to run.

#### Permissions

* Configuring custom integrations and actions requires **Can configure integrations**.
* Running actions and seeing their run history is granted per integration in role permissions, and each custom integration counts as a separate integration. A new custom integration is only granted to **Super admin** roles. Grant it to other roles as needed in [Roles & permissions](https://claude.ai/pages/workspace/roles-and-permissions).
* A role without access to an integration doesn't see its actions anywhere: not on requests, not in the workflow editor, not in app access roles.

#### Enable, disable or delete an action

| State        | Effect                                                                                              |
| ------------ | --------------------------------------------------------------------------------------------------- |
| **Enabled**  | Offered wherever **Where it's available** allows: requests and people, workflows, app access roles  |
| **Disabled** | No longer offered for new uses. Workflows and app access roles that already use it keep running it  |
| **Deleted**  | Hidden everywhere. Workflows and app access roles that use it stop working. Its run history is kept |

{% hint style="warning" %} To stop an action from running at all, remove it from the workflows and app access roles that use it, then disable or delete it. Deleting a custom integration also deletes all its actions. {% endhint %}

#### Test a new integration

There is no built-in health check. To test the URL, the authentication and the API permissions in one go, create a read-only `GET` action on the API's "current user" or "status" endpoint, tick **Manual runs** and declare no inputs, then run it from a test request.

#### Limits and behavior

| Topic          | Behavior                                                                                                                                                                            |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Timeout        | Each run must complete within about 20 seconds                                                                                                                                      |
| Response       | Must be a JSON object of 256 KB or less. Text outputs longer than 10,000 characters are cut before reaching workflow variables                                                      |
| Retries        | None. Each call is sent exactly once, so the third party never receives the same request twice                                                                                      |
| Missing secret | If a header uses a secret that no longer exists, the run fails before anything is sent. Siit never sends an empty credential                                                        |
| Edited actions | Workflows always use the action's current definition. If you rename or remove an input a workflow fills in, that workflow's runs fail and nothing is sent until you update the step |
| Network        | HTTPS and public addresses only, no redirects                                                                                                                                       |

#### Troubleshooting

| Symptom                                          | What to check                                                                                                                         |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| The action doesn't appear anywhere               | It is enabled, what it runs on matches the page or the workflow trigger, and your role has the integration under **External actions** |
| The action doesn't appear on requests or people  | **Manual runs** is ticked, and the action doesn't run on **Apps**                                                                     |
| The action doesn't appear in the workflow editor | **Automations** is ticked                                                                                                             |
| The third party receives `{{...}}` as text       | The variable was typed by hand. Insert it with the picker or the `{{` autocomplete                                                    |
| A workflow fails after an action was edited      | An input used by the workflow was renamed or removed. Update the workflow step                                                        |

#### Manage custom integrations with the API and MCP

Custom integrations and actions can also be managed outside the Siit interface, for example to keep them in version control or to have an AI client set them up.

* [**Siit API**](https://claude.ai/pages/developer/api): `/v1/custom_integrations` and `/v1/custom_integrations/:uid/custom_actions` support list, read, create, update and delete. Each action's run history is at `…/custom_actions/:uid/executions`. Requires the `admin.custom_integrations.read` and `admin.custom_integrations.write` scopes. Logos can only be uploaded from the Siit interface.
* [**Siit MCP**](https://claude.ai/pages/integrations/mcp): tools such as `list_custom_integrations`, `create_custom_integration`, `create_custom_action` and `update_custom_action` let an MCP client like Claude or Cursor configure integrations and actions.

The API and MCP also support settings the interface doesn't offer yet:

* `PATCH` as an HTTP method.
* A custom request body (`body`): free text such as JSON, GraphQL or plain text, with the same variables as headers, plus input values as `{{inputs.<slug>}}`. Values are inserted as raw text, so check that the result is valid, and set `Content-Type` yourself in the custom headers.
* No authentication (`auth_method: none`), for APIs that expect their credentials in custom headers only.

**Data model**

| Resource               | Description                                                                                                                                                                                                        |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Custom integration** | A third-party API: name, logo, documentation URL, credentials and shared headers                                                                                                                                   |
| **Custom action**      | One operation on that API: context, method, URL, inputs, outputs and its own headers. Belongs to one integration                                                                                                   |
| **Execution**          | One run of an action: what triggered it (an admin, a workflow or an app access role), the inputs, the response and the status. It keeps the version of the action it ran, so later edits don't change past history |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.siit.io/integrations/custom-integrations.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
