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

# Connect an Integration

> Activate an integration in a workspace with real credential values

Activate an integration inside a workspace so a workflow can run it.

Everything else in this section authors the **catalog** — the definition shared across your organization. This is the step that supplies the real secret values and produces the connection a workflow actually refers to.

Requires the **Integration: Full** permission **in the workspace you are connecting into**, not at organization level. Your organization does not need to be verified.

<Note>
  You can connect any integration you can see, including the ones Opus publishes — not only the ones you authored.
</Note>

## Two kinds of credential

How this call behaves depends on the credential the integration authenticates with.

| `authType` | What happens |
| - | - |
| `api_key`, `basic`, `bearer`, `no_auth`, … | Connects immediately. `201` with the connection ids. |
| `oauth` | Cannot complete here — a person has to authorize in a browser. `201` with an `authorizationUrl` and a `connectionToken`. |

<Warning>
  For OAuth, **nothing is connected when this call returns**. The connection only exists once someone has opened `authorizationUrl` and authorized. Use [Check a Connection](/api-reference/v1-integration/connection-status) with the `connectionToken` to find out whether they did.
</Warning>

## Headers

<ParamField header="x-service-key" type="string" required>
  Your API authentication key
</ParamField>

## Body Parameters

The connection is nested under a `connection` key.

<ParamField body="connection" type="object" required>
  The connection to create. Fields below.
</ParamField>

<ParamField body="connection.name" type="string" required>
  Name for the connection, shown in My Integrations. Maximum 255 characters.
</ParamField>

<ParamField body="connection.workspaceId" type="string" required>
  The workspace to activate the integration in. You must hold **Integration: Full** here.
</ParamField>

<ParamField body="connection.integrationVersionId" type="string" required>
  The integration version to connect. This is `versionId` from [Create an Integration](/api-reference/v1-integration/create-integration).
</ParamField>

<ParamField body="connection.actionVersionId" type="string" required>
  The action to enable on this connection.
</ParamField>

<ParamField body="connection.credentialVersionId" type="string">
  The credential template to authenticate with, from [Add a Credential](/api-reference/v1-integration/create-credential). Required unless you are reusing an existing connection's credential with `activeCredentialId`.
</ParamField>

<ParamField body="connection.credentialData" type="object">
  The secret values, keyed by the `variableName`s of that credential template — `{ "api_key": "sk-…" }` for an API key, `{ "client_id": "…", "client_secret": "…" }` for OAuth. Encrypted before storage and never returned. Maximum 100 fields and 64KB.
</ParamField>

<ParamField body="connection.activeCredentialId" type="string">
  Reuse the credential from an existing connection instead of sending values again. Omit `credentialData` when you use this.
</ParamField>

<ParamField body="connection.description" type="string">
  What the connection is for. Maximum 2048 characters.
</ParamField>

<ParamField body="connection.testActionVersionId" type="string">
  An action to smoke-test the credential with once connected. The result comes back as `testPassed`. Omit it and no test runs.
</ParamField>

<ParamField body="connection.testInputData" type="object">
  Inputs for that smoke test.
</ParamField>

<Warning>
  The smoke test **really calls the action's `endpoint`** with the credential you just supplied. It is a live request to the third party, not a dry run, so pick an action that is safe to execute — a read, not a write. Whatever the endpoint answers decides the result: any 2xx or 3xx passes, anything else fails.
</Warning>

<Note>
  There is no `workflowId` field. A connection belongs to a workspace, so name the workspace directly.
</Note>

## Response

<ResponseField name="status" type="string" required>
  `connected` when the connection is live, `pending_authorization` when it is waiting on OAuth.
</ResponseField>

<ResponseField name="activeIntegrationId" type="string">
  The connection — this is what a workflow refers to. `null` until an OAuth authorization completes.
</ResponseField>

<ResponseField name="activeIntegrationVersionId" type="string">
  The connection's current version.
</ResponseField>

<ResponseField name="activeCredentialId" type="string">
  The stored credential. Pass it as `activeCredentialId` on a later connect to reuse these secrets.
</ResponseField>

<ResponseField name="workspaceId" type="string">
  The workspace the connection lives in.
</ResponseField>

<ResponseField name="testPassed" type="boolean">
  Whether the smoke test succeeded. `null` when you sent no `testActionVersionId`, because nothing was tested — that is not a pass.
</ResponseField>

<ResponseField name="testError" type="string">
  Why it failed, when it did. For a failed HTTP test this carries the status code and the first 200 characters of the response body, which for many servers is an HTML error page.
</ResponseField>

<ResponseField name="authorizationUrl" type="string">
  Where to send a person to authorize. OAuth only.
</ResponseField>

<ResponseField name="connectionToken" type="string">
  Handle for [Check a Connection](/api-reference/v1-integration/connection-status). OAuth only, and valid for **10 minutes**.
</ResponseField>

## Errors

<ResponseField name="400" type="Bad Request">
  Invalid body — a missing required field, a malformed id other than `workspaceId`, neither `credentialVersionId` nor `activeCredentialId`, or `credentialData` sent together with `activeCredentialId`.
</ResponseField>

<ResponseField name="401" type="Unauthorized">
  Missing, invalid, or expired API key.
</ResponseField>

<ResponseField name="403" type="Forbidden">
  The key owner lacks **Integration: Full** in that workspace, or the workspace belongs to another organization. A `workspaceId` that is not a UUID also lands here rather than on `400`, because the permission check runs before the body is validated.
</ResponseField>

<ResponseField name="404" type="Not Found">
  One of the ids you named does not exist: `integrationVersionId`, `actionVersionId`, `credentialVersionId`, `testActionVersionId` or `activeCredentialId`. Nothing is created when this happens.
</ResponseField>

<ResponseField name="422" type="Unprocessable Entity">
  The body is well-formed but the credential is not usable as given — `credentialData` that does not match the credential template's fields, no `credentialData` for a credential that authenticates, or an OAuth credential missing `client_id` / `client_secret`.
</ResponseField>

<Warning>
  Connecting is **not idempotent**. Calling this twice with the same body creates two connections. If you retry after a timeout, check whether the first attempt succeeded.
</Warning>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://operator.opus.com/api/v1/integration/connect \
    --header 'Content-Type: application/json' \
    --header 'x-service-key: {YOUR_SERVICE_KEY}' \
    --data '{
      "connection": {
        "name": "Acme Production",
        "workspaceId": "{WORKSPACE_ID}",
        "integrationVersionId": "{INTEGRATION_VERSION_ID}",
        "actionVersionId": "{ACTION_VERSION_ID}",
        "credentialVersionId": "{CREDENTIAL_VERSION_ID}",
        "credentialData": {
          "api_key": "{ACME_API_KEY}"
        }
      }
    }'
  ```

  ```python Python theme={null}
  import requests

  url = "https://operator.opus.com/api/v1/integration/connect"
  headers = {
      "Content-Type": "application/json",
      "x-service-key": "{YOUR_SERVICE_KEY}"
  }
  payload = {
      "connection": {
          "name": "Acme Production",
          "workspaceId": "{WORKSPACE_ID}",
          "integrationVersionId": "{INTEGRATION_VERSION_ID}",
          "actionVersionId": "{ACTION_VERSION_ID}",
          "credentialVersionId": "{CREDENTIAL_VERSION_ID}",
          "credentialData": {"api_key": "{ACME_API_KEY}"},
      }
  }

  response = requests.post(url, json=payload, headers=headers)
  print(response.json())
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://operator.opus.com/api/v1/integration/connect", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-service-key": "{YOUR_SERVICE_KEY}",
    },
    body: JSON.stringify({
      connection: {
        name: "Acme Production",
        workspaceId: "{WORKSPACE_ID}",
        integrationVersionId: "{INTEGRATION_VERSION_ID}",
        actionVersionId: "{ACTION_VERSION_ID}",
        credentialVersionId: "{CREDENTIAL_VERSION_ID}",
        credentialData: { api_key: "{ACME_API_KEY}" },
      },
    }),
  });

  const data = await response.json();
  console.log(data);
  ```
</RequestExample>

<ResponseExample>
  ```json 201 Connected theme={null}
  {
    "status": "connected",
    "activeIntegrationId": "{ACTIVE_INTEGRATION_ID}",
    "activeIntegrationVersionId": "{ACTIVE_INTEGRATION_VERSION_ID}",
    "activeCredentialId": "{ACTIVE_CREDENTIAL_ID}",
    "workspaceId": "{WORKSPACE_ID}",
    "testPassed": null,
    "testError": null
  }
  ```

  ```json 201 OAuth theme={null}
  {
    "status": "pending_authorization",
    "activeIntegrationId": null,
    "authorizationUrl": "https://accounts.google.com/o/oauth2/auth?client_id=…&state=…",
    "connectionToken": "{CONNECTION_TOKEN}"
  }
  ```
</ResponseExample>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.