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

# Create an Integration

> Build custom integrations that connect Guild agents to any service.

<iframe width="100%" style={{ aspectRatio: "16/9" }} src="https://www.youtube.com/embed/xhTXV9F43CI" title="Create an integration" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />

You can build a custom Guild integration for any HTTP-accessible service. You don't need to be a Guild engineer or wait for first-party support — if your service has an API, you can connect it.

Create a custom integration when:

* You need an integration for a private or internal service (e.g., an internal deployment platform, a self-hosted tool)
* A first-party integration doesn't exist yet for a public service you use
* You want to customize how agents interact with a service beyond what a first-party integration offers

## How it works

A custom integration is a versioned package that tells the Guild runtime how to proxy HTTP requests to your service on behalf of agents. You define the endpoints, authentication method, and request/response schemas. Once published, agents can call your integration's tools exactly like any first-party integration.

```
Agent → Guild runtime → Integration proxy → Your service API
                ↓
        Handles auth, rate limiting,
        credential injection
```

The runtime injects credentials automatically, so agents never see raw API keys or tokens.

## Creating an integration

You can create integrations from the Guild UI or entirely from the CLI.

<Warning>
  Guild validates all integration endpoint URLs to prevent server-side request forgery (SSRF). This applies to the base URL, MCP server URL, OAuth install URL (`--install-url`), and OAuth token URL (`--token-url`). Private network ranges, loopback addresses (such as `localhost` or `127.0.0.1`), and internal DNS names are blocked.

  For local development and testing, expose your local service using a tunneling tool such as [ngrok](https://ngrok.com) or [Localtunnel](https://theboroer.github.io/localtunnel-www/) and use the public URL it provides.
</Warning>

<Tabs>
  <Tab title="CLI">
    ### 1. Create the integration

    ```bash theme={null}
    guild integration create my-service \
      --base-url https://api.example.com \
      --auth-scheme api-key \
      --description "Connect to the Acme API"
    ```

    For OAuth services, provide the OAuth configuration:

    ```bash theme={null}
    guild integration create my-oauth-service \
      --base-url https://api.example.com \
      --auth-scheme oauth \
      --install-url https://example.com/oauth/authorize \
      --token-url https://example.com/oauth/token \
      --client-id <id> --client-secret <secret> \
      --scopes "read,write"
    ```

    Some OAuth servers require the client credentials to be sent as an HTTP Basic Authentication header on the token exchange request instead of in the POST body. Pass `--token-auth-method basic` to enable this:

    ```bash theme={null}
    guild integration create my-oauth-service \
      --base-url https://api.example.com \
      --auth-scheme oauth \
      --install-url https://example.com/oauth/authorize \
      --token-url https://example.com/oauth/token \
      --client-id <id> --client-secret <secret> \
      --scopes "read,write" \
      --token-auth-method basic
    ```

    The default (`--token-auth-method post`) sends credentials in the POST body, which is the OAuth 2.0 standard. Use `basic` only when the token endpoint explicitly requires it.

    Use `--public` to make the integration visible to all users. By default it is private to your account.

    The command also creates a draft version automatically.

    ### 2. Define operations

    Add operations (endpoints) to your draft version. Each operation becomes a tool that agents can call.

    **Add operations manually:**

    ```bash theme={null}
    guild integration operation create myorg~my-service \
      --operation list_users \
      --method GET \
      --path /users \
      --summary "List all users"

    guild integration operation create myorg~my-service \
      --operation get_user \
      --method GET \
      --path /users/{id} \
      --summary "Get a user by ID"
    ```

    **Or import from an OpenAPI 3.0 or 3.1 spec:**

    ```bash theme={null}
    guild integration operation create myorg~my-service --openapi ./openapi.yaml
    ```

    <Note>
      When importing from an OpenAPI spec, Guild handles HTTP redirect response codes as follows:

      * **Temporary redirects (`302`, `303`, `307`)**: the operation is kept. The response schema is parsed as unknown because the redirect does not define the final response shape.
      * **Permanent redirects (`301`, `308`)**: the operation is skipped. These codes indicate deprecated or moved endpoints.
    </Note>

    You can optionally provide JSON schema files for request and response bodies:

    ```bash theme={null}
    guild integration operation create myorg~my-service \
      --operation create_user \
      --method POST \
      --path /users \
      --summary "Create a new user" \
      --input-body-schema ./create-user-input.json \
      --output-body-schema ./create-user-output.json
    ```

    ### 3. Build and publish

    ```bash theme={null}
    # Build (validate) and assign a version number
    guild integration version build myorg~my-service --version-number 1.0.0

    # Publish the built version
    guild integration version publish myorg~my-service --version-number 1.0.0
    ```

    Version numbers must be strictly increasing — you cannot publish `1.0.0` after `1.1.0`.

    ### 4. Test

    Test an operation against the live API:

    ```bash theme={null}
    guild integration version test myorg~my-service \
      --operation list_users \
      --account my-account \
      --input-query '{"limit": 10}'
    ```

    Pass path parameters, query parameters, and request bodies as JSON:

    ```bash theme={null}
    guild integration version test myorg~my-service \
      --operation get_user \
      --account my-account \
      --input-path '{"id": "user-123"}'
    ```

    ### 5. Connect credentials

    ```bash theme={null}
    guild integration connect myorg~my-service --token <api-key>
    ```

    Omit `--token` for an interactive prompt. Use `--owner` to connect credentials for an organization account.

    ### 6. Configure webhooks (optional)

    Pass webhook event definitions as JSON when creating or updating the integration:

    ```bash theme={null}
    guild integration create my-service \
      --base-url https://api.example.com \
      --auth-scheme api-key \
      --webhook-events '[{"event": "push"}, {"event": "pull_request", "actions": ["opened", "closed"]}]'
    ```

    Or update an existing integration:

    ```bash theme={null}
    guild integration update myorg~my-service \
      --webhook-events '[{"event": "deploy", "actions": ["started", "completed", "failed"]}]'
    ```

    See [Webhook format](#webhook-format) below for delivery requirements.
  </Tab>

  <Tab title="UI">
    ### 1. Create the integration

    Go to the **Integration Hub** in the Guild UI and click **Create Integration**. Provide:

    * **Name** — a unique identifier for your integration
    * **Description** — what the integration connects to and what it does
    * **Protocol** — choose REST or MCP:
      * **REST** — the base URL of the target API. Optionally set **Extra headers** (a JSON object mapping header names to values, for example `{"X-Custom-Header": "value"}`) to include additional static headers on every proxied request.
      * **MCP** — the MCP server URL. Optionally set **Extra headers** (a JSON object mapping header names to values, for example `{"X-Custom-Header": "value"}`) to include additional static headers on every proxied request.
    * **Authentication** — how the target service authenticates requests

    **Authentication types:**

    | Type      | Use for                                                               |
    | --------- | --------------------------------------------------------------------- |
    | API key   | Services that authenticate with a static token or API key             |
    | OAuth     | Services that use OAuth 2.0 flows (Guild manages the token lifecycle) |
    | OAuth M2M | Services that use the OAuth 2.0 client credentials flow               |

    When you select **OAuth M2M**, you can configure **Token Auth Method** (`token_auth_method`) — how the client credentials are sent to the token endpoint.

    | Value   | API value | Description                                                             |
    | ------- | --------- | ----------------------------------------------------------------------- |
    | `Body`  | `BODY`    | Sends the client credentials in the request body. This is the default.  |
    | `Basic` | `BASIC`   | Sends the client credentials using the HTTP Basic Authorization header. |

    For OAuth, you can optionally set **Token auth method**:

    | Value            | Behavior                                                                   |
    | ---------------- | -------------------------------------------------------------------------- |
    | `post` (default) | Client credentials are sent in the POST body on token exchange             |
    | `basic`          | Client credentials are sent as an HTTP Basic Auth header on token exchange |

    Set this to `basic` only when the token endpoint requires it — most services use `post`.

    For OAuth, you can also toggle **Use PKCE**:

    | Value             | Behavior                                                                                           |
    | ----------------- | -------------------------------------------------------------------------------------------------- |
    | Enabled (default) | Guild sends a PKCE (Proof Key for Code Exchange) code challenge and verifier during the OAuth flow |
    | Disabled          | Guild omits PKCE from the OAuth flow                                                               |

    Leave **Use PKCE** enabled — most modern providers support it. Disable it only for providers that reject PKCE, such as LinkedIn. The integration's configuration view shows whether PKCE is enabled or disabled.

    ### 2. Create a version

    Creating the integration automatically creates a draft version 1. Each integration can have multiple versions that follow [semantic versioning](https://semver.org/).

    ### 3. Define endpoints

    Each endpoint maps to an operation on the target service. An endpoint definition includes:

    | Field          | Description                                                              |
    | -------------- | ------------------------------------------------------------------------ |
    | Operation name | A unique identifier for this endpoint (becomes the tool name agents see) |
    | HTTP method    | `GET`, `POST`, `PUT`, `PATCH`, or `DELETE`                               |
    | Path           | The URL path on the target service (appended to the base URL)            |
    | Description    | What the endpoint does — shown to LLMs when choosing tools               |

    You can define endpoints in three ways:

    * **Manually** — add endpoints one at a time in the UI
    * **From an OpenAPI specification** — upload an OpenAPI 3.0 or 3.1 spec (YAML or JSON) and Guild generates endpoint definitions automatically
    * **Copy from a previous version** — carry forward endpoints from an earlier version and modify as needed

    ### 4. Build and publish

    Once your endpoints are defined:

    1. **Build** — Guild validates the configuration and assigns the semver you specified. Build status is tracked as a job.
    2. **Publish** — the version becomes available for agents to use in workspaces.

    Version numbers must be strictly increasing — you cannot publish `1.0.0` after `1.1.0`.

    ### 5. Test

    Open the **Versions** tab on your integration and click any version row to open that version's detail page. Anyone can view and test a published version — edit, build, publish, and delete actions appear only for the integration owner.

    From the version detail page, click **Test** to invoke endpoints interactively. You can set path parameters, query parameters, and a request body, then see the full response including status code, headers, and body.

    The test page shows credential connection status. A warning indicator means no account is connected — click **Connect** to add one. A green indicator confirms credentials are ready.

    If an endpoint has no request or response schema defined, the endpoint detail shows: "No request or response schema is defined for this endpoint."

    ### 6. Configure webhooks (optional)

    If your service sends events to Guild, you can define which webhook events the integration supports. This is optional — skip this step if your integration is request-only. Once configured, workspace users can create [webhook triggers](/platform/triggers) that fire agents automatically when your service emits an event.

    Open the integration's **Configuration** tab and click **Configure webhooks** in the Webhooks section. See [Webhook format](#webhook-format) below for delivery requirements.
  </Tab>
</Tabs>

## Request body encoding

For `POST`, `PUT`, and `DELETE` requests, the REST proxy sends the request body as JSON and sets `Content-Type: application/json` by default.

To send a form-encoded body instead, add `"Content-Type": "application/x-www-form-urlencoded"` to the integration's extra headers. When you set this header, the proxy preserves it and encodes the request body as URL-encoded form data rather than JSON.

## Webhook format

If your integration receives inbound webhooks, your service must send a JSON body matching this format:

```json theme={null}
{
  "event": "push",
  "action": "opened",
  "payload": { ... }
}
```

`action` is `null` for events that don't have sub-actions.

Each delivery must include these headers:

**`X-Guild-Webhook-Signature`**

A cryptographic signature that proves the delivery came from the integration and hasn't been tampered with in transit. Guild rejects deliveries with a missing or invalid signature.

Generate it as follows:

1. Take the raw request body as a string.
2. Hash it using HMAC-SHA256 with the webhook secret key.
3. Hex-encode the result and prefix it: `sha256=<hex-encoded hash>`
4. Set that as the value of the `X-Guild-Webhook-Signature` header.

**`X-Guild-Webhook-ID`**

A unique identifier for each individual delivery (e.g., a UUID generated fresh per request). Guild uses this to detect and discard duplicate deliveries, which is important because many systems retry failed webhook requests.

**`Content-Type`**

Must be set to `application/json`.

**How the webhook URL is set up**

When a user connects the integration and wants to receive webhook events, Guild generates a unique webhook URL for that connection. The user registers this URL with the external service (usually on its webhook configuration page) so the service knows where to send deliveries.

**How the secret key is established**

During that same registration, the external service typically reveals a secret key. The user copies the secret back into Guild, which stores it with the connection and uses it to verify the `X-Guild-Webhook-Signature` on every incoming delivery. Deliveries with a missing or mismatched signature are rejected.

**Defining events**

Each event has a name and an optional list of actions. For example:

| Event          | Actions                                  |
| -------------- | ---------------------------------------- |
| `push`         | *(none)*                                 |
| `pull_request` | `opened`, `closed`, `merged`, `reopened` |

## Using your integration

Once published, your integration's tools appear alongside first-party tools in the workspace. Agents import and use them the same way as any other service integration:

```typescript theme={null}
import { myServiceTools } from "@guildai-services/my-org~my-service";
import { llmAgent, guildTools } from "@guildai/agents-sdk";

export default llmAgent({
  description: "An agent that uses a custom integration.",
  tools: { ...myServiceTools, ...guildTools },
  systemPrompt: `You have access to the Acme API.
Use it to look up customer records when asked.`,
});
```

## Connecting credentials

When a custom integration requires authentication, the workspace administrator configures credentials in **Settings > Credentials** at [app.guild.ai](https://app.guild.ai), just like first-party integrations. If an agent invokes a tool from an unconfigured integration, Guild prompts the user to connect their account.

## Webhooks

Custom integrations can also receive inbound webhooks from integrations. Configure a webhook URL in your integration, and external events will be routed to agents via [triggers](/triggers).
