Dock Docs
Connectors

Triggers and replies

Start an agent run from a Linear issue, Slack mention, Sentry issue or Figma comment, and post the result back.

A connected tool can start work in Dock. The tool sends a webhook to the control plane, the control plane checks its signature, finds the workspace, and starts a run: a dock with that connector attached, running your agent on a prompt built from the event. When the agent finishes, Dock posts its summary back where the event came from.

KindStarts a run whenDock posts back
linearAn issue is assigned to the connected Linear user, or the label dock is addedA comment; the issue moves to a started state when the run begins and to a completed state when the agent reports done
slackThe app is mentioned (app_mention)A message in the mention's thread (thread_ts), plus an "On it." acknowledgement
sentryA new issue (issue.created) or an issue alert (event_alert) firesA note on the issue
figmaA file comment mentions the connected Figma user or writes @dockA reply in the comment thread, plus an "On it." acknowledgement

A Sentry issue that creates an issue webhook and an alert webhook starts one run: a subject (issue, Slack thread, Figma thread) has at most one run in flight. A workspace runs at most CONNECTOR_RUN_MAX_CONCURRENT runs at once (default 5); further events are not queued: Slack, Linear and Figma users get a "busy, try again" reply and Sentry events are dropped without a note.

Who can start a run

A run is an agent acting with the connected tool's credential, so Dock checks who sent the event before anything starts. A refused event starts no dock and spends no model usage. Where the tool allows, Dock replies with a short reason; the check fails closed, so an event it cannot verify is ignored silently.

ToolWho may start a run
SlackFull members of the connected Slack workspace. Guests (is_restricted, is_ultra_restricted), bots, deactivated users and Slack Connect users from other teams are refused. Dock asks Slack (users.info) with the stored token, so the token needs users:read.
LinearActive, non-guest members of the connected organization, checked with the stored token. Events caused by integrations or OAuth apps are ignored.
FigmaFigma users on the connector's allowlist (allowedUsers). By default the list holds only the Figma user of the token that was connected. Add colleagues with allowedUsers in the webhook configuration. When a different Figma user reconnects, the list is reset to that new user; the same user reconnecting keeps the list.
SentryNobody until an admin sets triggersEnabled: true in the webhook configuration. Event text can contain text from end users of the monitored app (exception messages, breadcrumbs), so Sentry triggers are opt-in.

Security model

Anyone who can start a run can steer an agent with prompt injection, so a run is built to hold nothing worth stealing and to be unable to hand it back:

  • No environment secrets. The dock is created without the workspace environment: none of its variables, secret files, repositories, provider keys or dock API key are applied. The agent installs from the image or from the operator's CONNECTOR_RUN_SETUP_SCRIPT, which must not contain secrets.
  • No provider key. The agent reaches its model through the LLM gateway with a run-scoped token (DOCK_LLM_TOKEN), exactly as GitHub runs do. The key stays in the control plane, and the token stops working when the run completes, fails or times out.
  • A scoped, revocable tool token. The dock holds DOCK_CONNECTOR_TOKEN, which works only through the connector gateway and only while the dock runs. Dock stops the dock when the run ends, which revokes it.
  • Scrubbed replies. Every reply, including summaries and failure reasons, passes through a redaction step before it is posted: the dock's own secret values, then common credential shapes (Sentry, Slack, Linear and Figma tokens, sk- and sk-ant- keys, GitHub tokens, AWS keys, JWTs, bearer headers, Dock tokens and other long hex strings). Replies are capped at 2000 characters and cannot contain @dock, so a reply never triggers another run.
  • Admins own the run. The dock is created as the admin who connected the tool, so the admins-only rule for docks with connectors holds. Reconnecting the tool makes the reconnecting admin the owner.

What remains: the agent can still use the connected tool with that credential's permissions while it runs (for example read every Linear issue), and it can send data it can see over the network. Connect tools with the least access that works, keep the sender checks above tight, and treat a run like a teammate with that tool's access.

Open pull requests in a linked repository

By default a triggered run can investigate and reply, and nothing more. An admin can link a connector scope to a GitHub repository so the run can change code and open a pull request there.

ToolScope key
linearTeam id
slackChannel id
sentryProject slug
figmaFile key

Use * as the scope to link every subject of that tool. A run uses the link for its own scope first and falls back to *. Without a matching link nothing changes: the run replies only.

  • GET /v1/workspaces/:workspaceId/connectors/:kind/repo-links lists the links.
  • PUT /v1/workspaces/:workspaceId/connectors/:kind/repo-links creates or replaces one with { "scope": "<scope key or *>", "repo": "owner/name" }.
  • DELETE /v1/workspaces/:workspaceId/connectors/:kind/repo-links?scope=<scope key> removes one.

All three are for organization admins only, and the tool must be connected. Linking checks that a GitHub App installation of the workspace lists the repository and that the App has Contents and Pull requests write access on it: 400 github_repo_not_accessible, 400 github_permissions_missing, 400 github_installation_missing or 503 github_app_not_configured otherwise.

curl -X PUT $DOCK_API/v1/workspaces/$WORKSPACE_ID/connectors/linear/repo-links \
  -H "Authorization: Bearer $DOCK_API_KEY" -H "content-type: application/json" \
  -d '{"scope":"<linear team id>","repo":"acme/app"}'

For a linked run:

  1. Dock pins the repository's default branch head and mints an installation token of the Dock GitHub App limited to that one repository with contents: read. The token reaches only the one exec that clones, through a git credential helper, so it is in no URL, file or git config; Dock revokes it as soon as the clone ends.
  2. The agent works on branch dock/<tool>-<short run id> in the clone. It holds no GitHub credential and cannot push.
  3. When the agent reports done, the dock returns the changed files. The control plane mints a second token limited to the same repository with contents: write and pull_requests: write, commits the changes on the branch from the pinned base commit, opens a draft pull request, and revokes the token. The title comes from the subject; the body links back to the Linear issue, Slack thread, Sentry issue or Figma file, and carries the run summary after the same redaction as replies (GitHub tokens included). @ mentions in it are neutralized.
  4. The reply in the tool includes the pull request link. A Linear issue is not moved to completed when a pull request was opened. If the pull request cannot be opened the reply says so with a short reason (push_failed, pr_create_failed, token_mint_failed, workflow_change_refused for a change under .github/workflows/, repo_unavailable).

A change set too large (over 1000 files or 8 MiB) or touching a symlink target Dock cannot represent fails the run. A run that cannot clone fails with repo_unavailable, as does a run whose link points at a GitHub App installation no longer linked to the workspace: removing an installation stops its repository links from being used.

Who can cause a pull request is exactly who can start a run, as set out in Who can start a run; nothing about the link widens it. Anyone allowed to start a run can steer the agent, so treat a link like giving those people a contributor who opens draft pull requests in that repository. Pull requests are authored by the Dock GitHub App, never by a person, never merged by Dock, and always opened as drafts for human review. The run still has no environment secrets, and no GitHub token is ever placed in the agent's environment.

What a link does give those people:

  • Read access to the repository. The agent has the whole clone and the dock has network egress, so a requester can have it send the code elsewhere. Do not link a repository the people who can start runs should not read.
  • Code in your CI. The branch is pushed to the repository itself, not a fork, and pushes made by a GitHub App start workflows. push and pull_request workflows then run the agent's version of the code (build scripts, tests, dependency manifests) with the repository's Actions secrets. Changes under .github/workflows/ are refused, but that does not stop a changed test script. Before linking, keep secrets out of workflows that run on dock/** branches, for example with deployment environments that require reviewers.

How a run works

The run acts on behalf of the organization admin who connected the tool (created_by on the connector), so the admins-only rule for docks with connectors holds. See Security model for what the dock holds.

  1. The control plane picks a model the organization's provider keys can serve, then creates a dock in the workspace with the connector attached and no environment. Dock never installs agents: the image, or CONNECTOR_RUN_SETUP_SCRIPT, must provide the agent named by CONNECTOR_RUN_AGENT (opencode by default, or codex), and the organization must have a provider key the agent can use through the gateway (otherwise the run fails with provider_not_configured).
  2. The agent gets a prompt built from the event (sanitized and bounded like a GitHub issue) and the tool's API at $DOCK_<KIND>_API_URL with $DOCK_CONNECTOR_TOKEN. It writes RESULT.json with a status of done, no_change or failed and a summary.
  3. The dock calls back with POST /v1/connectors/runs/:id/complete, authorized by a token only that dock holds; the first result wins. Dock then posts the summary and stops the dock.
  4. A run that never reports is failed after CONNECTOR_RUN_TIMEOUT_MINUTES plus a 15 minute margin (default 30), with a reply naming the reason (timed_out, launch_failed, dock_provision_failed, provider_not_configured, agent_unsupported, no_owner, repo_unavailable). A completion and a timeout cannot both win: the first one to settle the run is the only one that replies.

Configure verification

Each tool signs its webhooks differently. Dock finds the workspace from a vendor account id stored at connect time or by you, and verifies the signature with a secret stored sealed on the connector or, for Linear and Slack, an app-level secret from the environment.

PUT /v1/workspaces/:workspaceId/connectors/:kind/webhook (admins only) records the secret and routing id for a connected tool:

curl -X PUT $DOCK_API/v1/workspaces/$WORKSPACE_ID/connectors/sentry/webhook \
  -H "Authorization: Bearer $DOCK_API_KEY" -H "content-type: application/json" \
  -d '{"secret":"<client secret>","externalId":"<installation uuid>"}'
FieldMeaning
secretThe signing secret or passcode; 8 to 512 characters. Stored sealed, never returned. Omit it for Figma to have Dock generate a passcode and return it once.
allowedUsersFigma user ids that may start runs (up to 100). Replaces the list.
triggersEnabledtrue lets Sentry events start runs. Off by default.
externalIdThe routing id: Sentry installation uuid or Figma webhook id. Linear (organization) and Slack (team) ids are recorded automatically when the token is connected; a different Linear or Slack id is accepted only with your own secret, since the app-level secret cannot vouch for it.

400 webhook_secret_required means no secret is stored and no app-level secret covers the kind. 409 connector_account_in_use means another workspace already holds that vendor account.

An unknown account answers 404 connector_not_found; a wrong signature answers 401 invalid_signature; a stale Linear or Slack timestamp answers 401 stale_webhook. A delivery seen before is acknowledged with duplicate: true and starts nothing. Deliveries are deduplicated per tool by Linear's Linear-Delivery, Slack's event_id, Sentry's Request-ID and, for Figma, webhook id, event type, comment id and timestamp. Linear and Sentry do not sign those headers, so a repeat of the same signed body is a duplicate too.

Inbound routes

These routes are called by the tools, not by clients. Each answers 200 at once and processes the event in the background (Slack requires an answer within three seconds).

Linear

POST /v1/connectors/linear/webhooks. Verified with Linear-Signature: the hex HMAC-SHA256 of the raw body with the webhook's signing secret, and the webhookTimestamp in the body must be within one minute. Routed by organizationId.

Slack

POST /v1/connectors/slack/events. Verified with X-Slack-Signature (v0= plus the hex HMAC-SHA256 of v0:{timestamp}:{raw body} with the signing secret); requests whose X-Slack-Request-Timestamp is more than five minutes old are refused. url_verification is answered with the challenge. Routed by team_id. Messages from bots are ignored.

Sentry

POST /v1/connectors/sentry/webhooks. Verified with Sentry-Hook-Signature: the hex HMAC-SHA256 of the request body with the integration's client secret. Sentry's timestamp header is not signed, so replays are stopped by deduplicating the signed body. Routed by installation.uuid; the resource comes from Sentry-Hook-Resource.

Figma

POST /v1/connectors/figma/webhooks. Webhooks v2 put the passcode chosen at creation in every payload; Dock compares it in constant time. Routed by webhook_id. The PING sent when a webhook is created is acknowledged. Only allowlisted commenters start runs; replies never contain @dock, so they do not loop.

Run completion

POST /v1/connectors/runs/:id/complete is used only by the run's dock, with Authorization: Bearer <run token> and { "status": "done" | "no_change" | "failed", "summary"?, "reason"? }.

Set up each tool

LinearSlackSentryFigma
Webhook URL<PUBLIC_API_URL>/v1/connectors/linear/webhooks<PUBLIC_API_URL>/v1/connectors/slack/events<PUBLIC_API_URL>/v1/connectors/sentry/webhooks<PUBLIC_API_URL>/v1/connectors/figma/webhooks
Subscribe toIssuesBot event app_mentionissue (created) and, for alerts, an alert rule action that calls the integrationFILE_COMMENT
SecretWebhook signing secret, or LINEAR_WEBHOOK_SECRETSigning secret, or SLACK_SIGNING_SECRETThe integration's client secret, sent in secretThe passcode (generated by Dock when omitted)
Routing idAutomaticAutomaticInstallation uuid, sent in externalIdWebhook id from the create response, sent in externalId
Scopes the reply needswritechat:write, app_mentions:read, users:readevent:write (user token)file_comments:write
Who may start a runNon-guest membersFull members of the teamOpt in with triggersEnabledAllowlisted Figma users

Tokens connected before these triggers existed may lack the reply scopes: reconnect with a token that has them.

Environment

VariableMeaning
LINEAR_WEBHOOK_SECRETApp-level Linear signing secret, used when the connector stores none
SLACK_SIGNING_SECRETApp-level Slack signing secret, used when the connector stores none
CONNECTOR_RUN_AGENTopencode (default) or codex; must be in the image or installed by the setup script
CONNECTOR_RUN_MODELprovider/model served through the LLM gateway; empty picks one from the organization's provider keys
CONNECTOR_RUN_SETUP_SCRIPTNon-secret script that installs the agent in connector docks, which receive no environment
CONNECTOR_RUN_MAX_CONCURRENTRuns per workspace at once (default 5)
CONNECTOR_RUN_TIMEOUT_MINUTESAgent budget in minutes, at least 5 (default 30)

Sentry and Figma have no app-level secret on purpose: their routing ids are typed by an admin, so only a secret the admin holds may vouch for them.

On this page