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

# Slack turnkey onboarding research

**Slack onboarding: feasibility and implementation proposal**

Implementation status: the automatic onboarding flow, identity tools, OAuth activation and recovery are implemented in [PR #243](https://github.com/TextureHQ/mesh/pull/243). See [setup instructions, verification and remaining work](/slack-automatic-setup) for the shipped scope. The research below records the original feasibility assessment and broader proposal; proposed extensions are not all part of this PR.

Researched September 10, 2026. This is a proposal grounded in the current Mesh checkout, Slack's documentation and official CLI source, and the providers' documentation. No live Slack apps were created, live tokens exchanged, or paid images generated during research or implementation. Workspace-specific capabilities still need the targeted live verification described below.

**Recommendation: build automatic Slack provisioning.** Mesh can remove manifest copying, app creation, settings entry, and credential copying from the normal per-agent flow. Keep one Slack app per agent, configure provisioning once in instance settings, and use OAuth for installation. Automatic avatars are a credible implementation target, with a capability check and a graceful fallback. The achievable product is: choose provider → name and describe agent → review profile and picture → approve installation in Slack → start chatting.

This substantially reduces user work, but “90% automated” should be a design objective rather than a measured claim. Initial Slack credential setup, workspace approval policies, and private-channel invitations remain distinct sources of friction.

**Baseline before this change.** The current manifest is generated JSON, not a file the user must locate. It already fills in event subscriptions, scopes, and `PUBLIC_URL + /slack/events`, and provides a prefilled Slack creation link. The three manually entered values are App ID, signing secret, and bot token. There is no Socket Mode app token in this flow. Mesh verifies the bot token with `auth.test`, encrypts credentials, saves the connector, and reloads the runtime.

At the start of the investigation, the implementation was more current than parts of the provisioning guide: the wizard supports both OpenRouter and Ramp Router, and setup is separate from adding an agent. The original wizard/profile implementation had no agent-avatar upload or generation feature. Existing image support in the model interface concerns image input.

Relevant implementation locations:

| Existing component | Reuse or change |
| - | - |
| [Manifest builder](https://github.com/TextureHQ/mesh/blob/codex/slack-automatic-onboarding/internal/admin/manifest.go#L31) | Shared source for manual and automatic configuration; add staged creation and OAuth redirect configuration. |
| [Shared Slack setup panels](https://github.com/TextureHQ/mesh/blob/codex/slack-automatic-onboarding/web/src/components/ConnectorConfigurator.tsx#L297) | Preserve manual mode; add automatic mode. |
| [Agent wizard](https://github.com/TextureHQ/mesh/blob/codex/slack-automatic-onboarding/web/src/components/AgentWizard.tsx#L128) | Move provider readiness ahead of generated profile; simplify ordinary model choices. |
| [Connector persistence](https://github.com/TextureHQ/mesh/blob/codex/slack-automatic-onboarding/internal/admin/handlers.go#L617) | Reuse encrypted storage, route-conflict checks, and runtime reload behavior after OAuth. |
| [Slack challenge verifier](https://github.com/TextureHQ/mesh/blob/codex/slack-automatic-onboarding/internal/app/slack_ingress.go#L538) | Add access to pending app credentials before installation. |
| [Instance settings](https://github.com/TextureHQ/mesh/blob/codex/slack-automatic-onboarding/internal/settings/settings.go#L38) | Store non-secret configuration here; credentials remain in encrypted `secrets`. |
| [Provider registry](https://github.com/TextureHQ/mesh/blob/codex/slack-automatic-onboarding/internal/admin/server.go#L87) | Reuse provider validation; add explicit image-generation capability through a separate interface. |

**The credential is an app configuration token.** Slack's Manifest APIs accept an app configuration access token. It is associated with a Slack user and development workspace and can manage that user's apps there. Slack documents generating it under “Your App Configuration Tokens” on the apps page; generation also adds Slack's tooling-token app to the workspace. This supports a once-per-workspace setup in Mesh, rather than repeating credential work for every agent. [Slack manifest guide](https://api.slack.com/reference/manifests)

Label the setting **Slack app provisioning**, not “Slack admin API key.” A configuration token does not override workspace policy. Slack ordinarily allows members to install apps, but owners can require approval; administrator status is not universally required. [Slack app permissions](https://slack.com/help/articles/1500009181142-Manage-app-settings-and-permissions)

Access tokens expire after 12 hours. `tooling.tokens.rotate` returns a new access token, refresh token, workspace/user identity, and expiry. Mesh must persist the replacement pair atomically and refresh before expiry, with a lock preventing concurrent refreshes. Slack still labels the rotation API beta, so retain manual recovery. [Token rotation method](https://docs.slack.dev/reference/methods/tooling.tokens.rotate/)

For setup, accept the access/refresh pair shown by Slack. A streamlined refresh-token-only form is technically plausible because rotation requires only that token; validate this in the spike before choosing the final UI. Once saved, display workspace, connecting user, last successful check, and reconnection status. Never redisplay the credential.

**Capability assessment.** “Confirmed” below means documented API support, not a successful call against the user's workspace.

| Requested capability | Assessment | Implementation direction |
| - | - | - |
| Create an app for an agent | Confirmed | `apps.manifest.create`. |
| Configure name, description, bot identity | Confirmed for manifest fields | Derive app display name and Slack-safe bot handle separately. |
| Configure scopes, events, request URL, OAuth callback | Confirmed | Versioned manifest and `apps.manifest.update`. |
| Retrieve App ID and signing secret | Confirmed | Creation response also supplies OAuth client credentials. |
| Retrieve bot token without copy/paste | Confirmed after authorization | Server-side OAuth exchange. |
| Generate persona and avatar | Text supported with either provider; image supported by OpenRouter | Separate text and image capabilities. |
| Upload app icon automatically | Documented, reinforced by official CLI | Verify the intended token/workspace combination; provide fallback. |
| Set separate Slack first/last-name fields | Conditional, separate permission path | Optional advanced feature; do not make it a prerequisite. |
| Skip every approval/install interaction | Not established for the general self-hosted path | Keep ordinary OAuth as baseline. |
| Automatically join every channel | No | Public-channel join can be automated with scopes; private channels need an authorized member's invitation. |

`apps.manifest.create` returns `app_id`, `client_id`, `client_secret`, `signing_secret`, and an authorization URL. It does not return the installed bot token. This cleanly separates app creation from installation. [Creation API](https://docs.slack.dev/reference/methods/apps.manifest.create/)

OAuth exchanges the returned authorization code for the bot access token, app identity, workspace identity, and bot user ID. Mesh can save all of those without exposing them in a form. [OAuth token exchange](https://docs.slack.dev/reference/methods/oauth.v2.access/)

**Identity and avatar details matter.** Manifest app names and bot display-name fields are distinct. The documented bot field has a restricted character set; use a normalized handle and validate the generated manifest rather than assuming a full human name is valid everywhere. Store optional first/last names in Mesh, but expose a single primary name to keep creation easy. [Manifest field reference](https://docs.slack.dev/reference/app-manifest/)

Slack's `users.profile.set` supports first and last names, but requires a user token with `users.profile:write`; changing other users' profiles carries paid-plan, role, and profile-source requirements. Its bot-specific permission error also distinguishes owners/selected members. Treat setting bot first/last fields as a separately tested enhancement, not something an ordinary bot token or configuration token guarantees. [Profile API](https://docs.slack.dev/reference/methods/users.profile.set/)

For pictures, use `apps.icon.set`, which accepts an app ID and either uploaded file bytes or a public image URL. Its documented image bounds are 512–2000 pixels in each dimension. The method page labels its credential a user token with `app_configurations:write`, while the scope page labels that scope Configuration. This is a documentation mismatch worth verifying, not evidence that icons are impossible. [Icon API](https://docs.slack.dev/reference/methods/apps.icon.set/), [scope reference](https://docs.slack.dev/reference/scopes/app_configurations.write/)

The official CLI initially added this feature behind an experiment in April 2026. At inspected commit `5ad9fbd03792f392af6c22fd1e2e420cfea8590c`, its installation code directly calls `IconSet` using its tooling credential, without that experiment gate. This is strong supporting evidence for automation, although CLI credentials may differ from manually generated configuration tokens. Verify before release; do not rely on older claims that Slack has no icon API. [Original Slack PR](https://github.com/slackapi/slack-cli/pull/469), [current inspected installation code](https://github.com/slackapi/slack-cli/blob/5ad9fbd03792f392af6c22fd1e2e420cfea8590c/internal/pkg/apps/install.go#L221)

Prefer multipart upload of normalized PNG bytes so onboarding does not require public image hosting. If Slack denies icon access, keep the generated avatar in Mesh and offer “Download picture” plus “Open Slack app settings.” Report “Connected; Slack picture needs upload.” Do not block a working agent on an avatar failure.

**OpenRouter and Ramp Router should both work for onboarding, with different image capabilities.** OpenRouter currently documents a dedicated Image API: discover image-capable models, submit a model and prompt to `POST /api/v1/images`, and decode returned image bytes. Select a model from the live catalog; do not hardcode a marketing name or route image requests through the text model default. [Image generation guide](https://openrouter.ai/docs/guides/overview/multimodal/image-generation), [image model discovery](https://openrouter.ai/docs/api/api-reference/images/list-image-models)

Nano Banana models are currently listed on OpenRouter, making that a reasonable selectable option. Availability and callable IDs should come from discovery. [OpenRouter image collection](https://openrouter.ai/collections/image-models)

Ramp Router documents Responses, Chat Completions, and model listing, including image input. The reviewed documentation does not establish an image-output endpoint or supported image-generation model. Do not equate vision support with image generation. Accept Ramp Router for persona creation and runtime inference; offer an optional OpenRouter image key, upload, or a generated initials avatar when image output is unavailable. [Ramp Router API surface](https://docs.router.com/api/endpoint), [model capabilities](https://docs.router.com/guides/choose-a-model)

Reuse an agent's existing provider key when applicable. Optionally let an administrator configure instance onboarding defaults and a shared image-generation credential. Keep this billing choice explicit: permission to generate onboarding art should not silently change every agent's runtime billing identity. A shared runtime provider key would require a deliberate credential-reference/inheritance feature beyond the current per-agent model.

Generate an editable persona from the name and a short purpose. Build the avatar prompt from approved identity/style details, not the entire private persona. Keep a consistent visual style, offer regenerate/upload/skip, enforce generation limits, and record model, prompt version, and cost when available. Never overwrite an edited profile during retries. Use a deterministic local avatar if no image service is configured; do not make a second key mandatory.

**Proposed user experience.** Instance settings contain Slack provisioning, workspace connection status, and optional onboarding/model defaults. An operator completes the Slack credential setup once. Validate public HTTPS and the encryption key before beginning any remote app creation.

For each new agent:

1. **Choose how to connect.** “Create a Slack app for me” is the default when provisioning is ready. “Connect an existing Slack app” retains today's manual fields. A missing/expired connection links to the precise settings repair.
2. **Choose provider, if needed.** Accept OpenRouter or Ramp Router early. Reuse an authorized configured credential; keep advanced model-role choices collapsed with reasonable defaults.
3. **Name and describe the agent.** One name and one purpose field; derive an editable handle. Select a workspace only when there is more than one.
4. **Review its profile.** Show editable persona and avatar together. Regenerate individual pieces, upload a picture, or skip generation. Keep this within one screen rather than multiplying steps.
5. **Create and install.** Mesh shows progress while creating/configuring the app. “Install in Slack” takes the user through Slack's authorization page and returns them to the same draft. If policy requires an app manager, retain the draft with a clear pending state and a resumable installation link.
6. **Start a conversation.** Open the agent in Slack. A short test DM validates inbound delivery and outbound response. Public-channel selection can follow as an optional action; explain how to invite the agent to private channels.

Use an ordinary redirect as the reliable OAuth baseline; a popup is optional polish with a redirect fallback. Closing Slack, denying installation, or refreshing Mesh must not erase progress. Treat “credentials saved,” “events verified,” “avatar synced,” and “conversation tested” as separate facts.

**Backend sequence and the webhook bootstrap fix.** Today `authenticateChallenge` tries signatures against registered installations. A newly created app is not yet one of them. Slack's verification payload contains a challenge without app/workspace routing IDs, and authenticity still must be checked. [Slack verification event](https://docs.slack.dev/reference/events/url_verification/)

Use staged provisioning:

1. Persist a draft and provisioning job with a stable ID. Validate the provider, target workspace, name, callback URL, public endpoint configuration, and encryption availability.
2. Render and validate a creation manifest containing identity, bot scopes, and OAuth redirect URL, but omitting event subscriptions and interactivity. This prevents Slack probing a URL before Mesh knows the app's secret.
3. Create the app. Immediately encrypt and persist its returned secrets and ID. Record ownership by the provisioning connection and owning Mesh agent.
4. Register the pending app's signing secret for challenge verification. Prefer a new app-specific route such as `/slack/apps/{mesh_app_record_id}/events`; the path selects a secret but is never authorization. Verify Slack's signature and timestamp before returning a challenge. Keep the existing shared route for manual connectors.
5. Apply the complete manifest with its events URL. Record successful signed verification. Attempt icon upload independently. These are retryable operations against the same app.
6. Start OAuth using the created app's client ID and an expiring random state bound to this job, Mesh user/session, expected app, and intended workspace. Keep client secrets server-side. Validate state and callback identity before exchanging or persisting anything. Slack documents authorization-code expiry and state verification. [OAuth installation guide](https://docs.slack.dev/authentication/installing-with-oauth/)
7. Verify the returned app/workspace IDs and granted scopes, validate the bot token, and persist the installation through the existing connector service. Reject a workspace mismatch; an OAuth workspace hint is not an enforcement mechanism.
8. Reload the runtime, then finalize delivery health. Pending routes must never dispatch messages to an agent; events racing ahead of activation need durable intake/retry behavior so they are not silently discarded. Show connection and conversation-test status separately.

Keep HTTP Events API transport. Socket Mode would introduce a separate connection lifecycle and app-token provisioning problem without solving the main onboarding work.

**Data and service boundaries.** Add a small provisioning service rather than embedding remote calls in a wizard POST. Suggested records:

| Record | Responsibility |
| - | - |
| `slack_provisioning_connections` | Instance-managed workspace/user authorization, token expiry and health; encrypted credential references. Start with one connection in the UI, allow multiple in the schema. |
| `slack_apps` | Mesh agent, Slack app ID, client ID, owning connection, manifest version/hash, encrypted app credentials, icon status. Exists before OAuth installation. |
| `slack_provisioning_jobs` | Step state, external IDs, retry metadata, initiating user, sanitized errors, and OAuth attempt references. |
| Existing `agent_connectors` | Workspace installation, bot token, bot user ID, routing and activation. |
| `agent_avatars` or equivalent asset record | Validated image bytes, MIME type, hash, dimensions, generation metadata, and owning agent. |

App-level signing/client secrets and workspace bot tokens have different lifetimes. Represent that explicitly for generated apps, while preserving existing manual connector credentials. Add secret owner types for provisioning connections and apps, with a migration of the current owner-type constraint; use the existing encryption implementation. Do not store credentials or OAuth codes in plaintext JSON settings/job payloads.

For small avatars, bounded PostgreSQL `bytea` storage is a reasonable first implementation: normalize to a 512×512 PNG, cap upload/decoded size, strip metadata, and serve through an authorized asset endpoint. This avoids requiring a separate object store just to create an agent. A storage interface leaves room for object storage later. Multipart Slack upload means the Mesh avatar endpoint can stay private.

Suggested APIs cover settings connection create/test/replace, profile generation, avatar upload/generation, provisioning start/status/resume, and an OAuth callback. Return sanitized progress and credential-presence indicators. Reuse the same Slack manifest representation and final connector persistence for both setup modes.

**Failures must resume rather than restart.** Use a state progression such as `draft → app_created → configured → awaiting_install → installed → active`, with separate icon/delivery state. Add paused/retryable and terminal errors without losing successful earlier steps.

| Failure | Required behavior |
| - | - |
| Two clicks or browser retries | One local job/app intent; lock against concurrent creation. |
| Creation call times out after Slack may have created the app | Mark outcome uncertain and reconcile; do not blindly create another app. The documented API provides no client idempotency argument. |
| Remote app created but database commit fails | Retain the prewritten job intent and retry credential persistence while the response is available. If credentials are lost, require explicit recovery of that app through Slack settings or cleanup; do not claim an automatic rollback or log secrets. |
| Configuration or icon fails | Retry against recorded app ID. Icon failure does not prevent installation. |
| OAuth cancelled, expired, replayed, or wrong workspace | Keep draft; issue a fresh attempt where appropriate. Never attach the wrong installation. |
| OAuth exchange outcome uncertain | Do not repeatedly spend the same code; provide a fresh authorization attempt and safe reconciliation. |
| Provisioning token revoked or owner leaves | Pause provisioning and flag reconnect/ownership repair; existing bot installations continue unless separately revoked. |
| Runtime reload fails | Preserve Mesh's existing saved-but-restart-required distinction. |
| Model generation fails or budget exhausted | Keep edited content and offer upload/initials/manual persona. |
| User abandons or deletes draft | Keep cleanup visible; delete only a known app created by that job after explicit cleanup intent. |

Slack and PostgreSQL cannot participate in one transaction. A local idempotency key prevents duplicate submissions but cannot guarantee exactly-once remote creation after an ambiguous timeout. Where automatic reconciliation is unavailable, show the recorded intent and a guided Slack-app recovery action before allowing another creation.

Respect `Retry-After`, check Slack's `ok` field even on HTTP 200, and serialize appropriate operations per connection. Manifest updates are Tier 1, so asynchronous progress is preferable to pretending each step is instantaneous. [Update API](https://docs.slack.dev/reference/methods/apps.manifest.update/)

Keep provisioning credentials confined to the control plane; agents and tools must never receive them. Bind operations to authenticated Mesh users and target agents, audit administrative changes, and scrub OAuth codes, tokens, refresh tokens, and client secrets from traces/errors. Limit the shared credential's power through Mesh authorization. Existing single-replica hot reload remains a deployment constraint; a multi-replica rollout needs coordinated job ownership and registry propagation.

**Getting to usable Slack agents.** Installation does not place the bot in all channels. An optional public-channel picker needs `channels:read` for discovery and `channels:join` for joining selected channels. Private channels require invitation by someone already authorized; do not request broad user permissions just to hide this step. [Channel discovery](https://docs.slack.dev/reference/methods/conversations.list/), [join API](https://docs.slack.dev/reference/methods/conversations.join/), [Slack installation guidance](https://slack.com/help/articles/360001537467-Guide-to-apps-in-Slack)

Retain current message subscriptions because Mesh's ingress consumes message events; adding `app_mention` alone would not make it work. Verify the bot's Messages tab supports replies, with explicit App Home manifest settings if needed. Add lifecycle handling for uninstall/revocation so connection status can become disconnected without waiting for a failed reply. Hostname repair should update all owned app URLs through the provisioning service, show a preview, and track each app's result; callback URLs need repair too.

**Two ways to improve further, without making the first release depend on them.** Slack now references manager apps that create and manage child apps. The creation API has enrollment/feature errors, and manager-scoped manifest access is restricted to apps that manager created. Public evidence does not establish that any self-hosted Mesh can simply request these scopes and become a manager. Treat Slack confirmation of eligibility, enrollment, ownership, limits, and token delivery as a separate investigation. [Manifest export](https://docs.slack.dev/reference/methods/apps.manifest.export/), [managed app permissions](https://docs.slack.dev/reference/methods/apps.managed.permissions.set/)

Enterprise `admin.apps.approve` now exposes `allow_child_auto_install`, which describes preapproving future child-app installs from a manager. It requires Enterprise administration and is not a universal replacement for installation/token issuance. A future approved Mesh manager could potentially remove the initial configuration-token paste and more per-agent approval work, but that is an eligibility-dependent product path. [Enterprise approval API](https://docs.slack.dev/reference/methods/admin.apps.approve/)

Slack's CLI also has a developer installation path that returns credentials. It demonstrates deeper automation is possible inside Slack tooling, but I did not find a public method contract establishing equivalent support for arbitrary Mesh-generated apps. Keep OAuth as the supported baseline until Slack confirms that route for this use case. [Official CLI install implementation](https://github.com/slackapi/slack-cli/blob/5ad9fbd03792f392af6c22fd1e2e420cfea8590c/internal/pkg/apps/install.go#L201)

A single shared Mesh Slack app would reduce installations but give the agents one underlying Slack bot identity. Per-message name/icon customization does not create distinct users, mentions, DMs, or memberships. That conflicts with Mesh's existing one-coworker-per-agent behavior, so it is not the recommended design. [Message customization API](https://docs.slack.dev/reference/methods/chat.postMessage/)

**Delivery plan and acceptance criteria.** These phases are implementation recommendations, not work already performed.

1. **Resolve the narrow API uncertainties first.** In a disposable authorized workspace, create a minimal HTTP app with a generated configuration token; rotate it; register the signed verification route; update its manifest; upload a known 512px PNG using that same token; complete OAuth and verify the bot's name/icon/DM experience. Repeat the meaningful permission cases on an approval-restricted workspace. Record raw error codes without secrets. Test one actual OpenRouter image response and confirm Ramp Router capability with its current catalog/support. Confirm app ownership/reconnection behavior. Outcome: a tested supported matrix and precise avatar fallback conditions.
2. **Ship the connection and lifecycle foundation.** Add encrypted provisioning grants/app records, refresh handling, job persistence, pending verification support, settings UI, and resumable OAuth. Preserve manual setup. Completion: a new agent reaches working Slack messaging without copying any per-agent credentials or editing Slack configuration pages.
3. **Ship identity creation and polished onboarding.** Add early provider readiness, editable generated persona, avatar storage/generation/upload, icon sync, resumable progress, and DM verification. Completion: ordinary users supply name/purpose, review identity, and approve Slack installation; generation failures remain recoverable.
4. **Finish operational behavior.** Add rename/avatar sync, manifest drift and hostname repair, installation revocation status, selected public-channel joining, cleanup, and audit visibility. Test second agents and existing manual connectors. Completion: reconfiguration and failed setup do not require recreating agents or leak credentials.
5. **Evaluate manager-app enrollment separately.** Ask Slack whether Mesh qualifies, whether this works for customer-hosted instances, and exactly how automatic child installation returns tokens. Only then decide whether an additional setup mode is worthwhile.

Key automated verification should cover rotated-token persistence/concurrency, duplicate starts, uncertain creation outcomes, pending signature authentication, callback forgery/replay/wrong-workspace cases, missing granted scopes, encrypted secret ownership, post-commit reload failures, and recovery after each external side effect. Extend the existing fake-Slack end-to-end harness for happy-path and interrupted onboarding. A real Slack smoke test remains necessary for token eligibility, URL verification timing, app presentation, and approval UX.

Measure setup completion rate, median user interactions and time to first successful conversation, OAuth abandonment, permission-related failures, avatar fallback frequency, and duplicate/orphan app count. A useful release bar is zero manual credential copies per new agent after the once-per-workspace setup, zero configuration-page edits on the verified automatic path, and successful recovery without duplicate apps.

The first phase is deliberately small enough to settle feasibility before a full build. The complete production experience is a multi-week control-plane and UX project, not just a manifest POST; estimate it after the spike resolves Slack token eligibility and the desired shared-provider billing behavior.


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