Back to Blog
Guide

Slack App Manifest Example: YAML and JSON That Worked, Tested

A Slack app manifest we used to create a working app on 1 October 2026, in YAML and JSON, plus the validation errors from broken versions and real output from apps.manifest.validate, export, create and update.

Slack Green Team
October 1, 2026
October 1, 2026
4 min read
Share:
slack api
developers
app manifest

A Slack app manifest is a YAML or JSON file that describes an app: its name, bot user, scopes, slash commands, events and settings. Paste it at api.slack.com/apps > Create New App > From a manifest, or send it to apps.manifest.create, and Slack builds the app from it. The manifest below created a working app on 1 October 2026: we installed it, used its bot token, ran its slash command over Socket Mode and opened modals with it.

The manifest, in YAML

display_information:
  name: api-lab-1001
  description: Throwaway test app for API tests, deleted after use
features:
  bot_user:
    display_name: api-lab
    always_online: false
  slash_commands:
    - command: /lab-modal
      description: Open a test modal
      should_escape: false
oauth_config:
  redirect_urls:
    - http://localhost:8765/callback
  scopes:
    bot:
      - chat:write
      - chat:write.public
      - channels:history
      - channels:read
      - channels:join
      - commands
      - incoming-webhook
    user:
      - chat:write
settings:
  interactivity:
    is_enabled: true
  org_deploy_enabled: false
  socket_mode_enabled: true
  token_rotation_enabled: false

The same manifest in JSON

{
  "display_information": {
    "name": "api-lab-1001",
    "description": "Throwaway test app for API tests, deleted after use"
  },
  "features": {
    "bot_user": {
      "display_name": "api-lab",
      "always_online": false
    },
    "slash_commands": [
      {
        "command": "/lab-modal",
        "description": "Open a test modal",
        "should_escape": false
      }
    ]
  },
  "oauth_config": {
    "redirect_urls": [
      "http://localhost:8765/callback"
    ],
    "scopes": {
      "bot": [
        "chat:write",
        "chat:write.public",
        "channels:history",
        "channels:read",
        "channels:join",
        "commands",
        "incoming-webhook"
      ],
      "user": [
        "chat:write"
      ]
    }
  },
  "settings": {
    "interactivity": {
      "is_enabled": true
    },
    "org_deploy_enabled": false,
    "socket_mode_enabled": true,
    "token_rotation_enabled": false
  }
}

What each part did in our app:

  • • bot_user gives the app a bot and an xoxb- token. Slash commands and bot scopes fail validation without it.
  • • slash_commands with no url works because socket_mode_enabled is true. Slack sends the command over the WebSocket instead.
  • • interactivity.is_enabled with no request_url is also allowed with Socket Mode. Our modals and their submissions arrived that way; see the expired_trigger_id test.
  • • redirect_urls lists where OAuth may send the user back. http://localhost is allowed; our redirect URL test covers what else is.
  • • scopes.user asks for a user token as well. The bot scopes are what most apps need.

The Socket Mode connection needs an app-level token with connections:write. A manifest cannot create it. Generate it under Basic Information > App-Level Tokens after the app exists.

Creating it in the web UI

The dialog has a JSON and a YAML tab. We pasted the JSON; clicking YAML translated it, and the YAML above is Slack's own translation. The editor checks the manifest as you type and disables Next while errors remain. With bot_user removed and a scope that does not exist, it showed:

Slack's Create from a manifest editor with error markers and the tooltip: Illegal bot scopes found chat:write.everything, Oauth requires bot_user

The dialog also said "We can't translate a manifest with errors", and the YAML tab stayed disabled. With the working manifest, Next led to a review step:

Slack's Review your app step listing the 7 bot scopes from the manifest

It listed the 7 bot scopes and left out the user scope, which was still in the app afterwards. Create made the app without installing it. We installed it in a separate step.

Never appear "away" on Slack again

Cloud-based. No downloads. Works 24/7 even when your laptop is off.

Validation errors we got

apps.manifest.validate returns the same checks as the editor, with a JSON pointer to each problem. For the broken manifest in the screenshot:

{
  "ok": false,
  "error": "invalid_manifest",
  "errors": [
    {
      "code": "illegal_bot_scopes",
      "message": "Illegal bot scopes found `chat:write.everything`",
      "pointer": "/oauth_config/scopes/bot"
    },
    {
      "code": "requires_bot_user",
      "message": "Oauth requires bot_user",
      "pointer": "/oauth_config/scopes/bot",
      "related_component": "bot_user"
    },
    {
      "code": "requires_bot_user",
      "message": "Slash Commands requires bot_user",
      "pointer": "/features/slash_commands",
      "related_component": "bot_user"
    }
  ]
}

Other changes to the working manifest, one at a time:

Changecodemessage
Settings key socket_mode instead of socket_mode_enabledfailed_constraintinvalid additional property: socket_mode
No display_information.namemissing_fieldmissing required field: name
App name of 36 charactersfailed_constraintmust be less than 36 characters
Command lab-modal without the slashfailed_constraintinput must match regex pattern: ^\/
Socket Mode off, no URLsfour errorsInteractivty requires a Request URL; Slash Commands require URLs; and two more
Redirect URL myapp://oauth/callbackinvalid_redirect_urlsInvalid redirect URLs myapp://oauth/callback
The same, with "pkce_enabled": truenoneok: true
The YAML text sent as manifestnone listedinvalid_manifest with no errors array

The last row matters if you script it: the API takes JSON only. Convert YAML first, for example with json.dumps(yaml.safe_load(text)) in Python. Note the spelling in the Socket Mode case; Slack's own message says "Interactivty requires a Request URL".

The manifest API

The apps.manifest.* methods need an app configuration token, not a bot or user token. A bot token got this from apps.manifest.export:

{
  "ok": false,
  "error": "missing_scope",
  "needed": "app_configurations:read",
  "provided": "chat:write,chat:write.public,channels:join,commands,channels:history,channels:read,incoming-webhook"
}

Generate a configuration token at the bottom of api.slack.com/apps, under "Your App Configuration Tokens", for one workspace. It came with a refresh token and expired after 12 hours: tooling.tokens.rotate with the refresh token returned a new pair, with exp 43,200 seconds after iat.

apps.manifest.export returned the live manifest of our app. Slack had added three settings we never wrote, pkce_enabled: false, app_level_token_rotation_enabled: false and is_mcp_enabled: false:

{
  "ok": true,
  "manifest": {
    "display_information": {
      "name": "api-lab-1001",
      "description": "Throwaway test app for API tests, deleted after use"
    },
    "features": {
      "bot_user": {
        "display_name": "api-lab",
        "always_online": false
      },
      "slash_commands": [
        {
          "command": "/lab-modal",
          "description": "Open a test modal",
          "should_escape": false
        }
      ]
    },
    "oauth_config": {
      "redirect_urls": [
        "http://localhost:8765/callback"
      ],
      "scopes": {
        "user": [
          "chat:write"
        ],
        "bot": [
          "chat:write",
          "chat:write.public",
          "channels:history",
          "channels:read",
          "channels:join",
          "commands",
          "incoming-webhook"
        ]
      },
      "pkce_enabled": false
    },
    "settings": {
      "event_subscriptions": {
        "bot_events": [
          "message.channels"
        ]
      },
      "interactivity": {
        "is_enabled": true
      },
      "org_deploy_enabled": false,
      "socket_mode_enabled": true,
      "token_rotation_enabled": false,
      "app_level_token_rotation_enabled": false,
      "is_mcp_enabled": false
    }
  }
}

apps.manifest.update replaces the whole manifest; send every section, not only the change. Adding a message.channels bot event returned:

{
  "ok": true,
  "app_id": "A0C5PB7LM9T",
  "permissions_updated": false
}

The event started arriving over Socket Mode without a reinstall, because the app already had channels:history. apps.manifest.create made our second app in one call and returned its credentials, which we replace here:

{
  "ok": true,
  "app_id": "A0C6P5BLCSU",
  "credentials": {
    "client_id": "11256386461409.12227181692912",
    "client_secret": "...",
    "verification_token": "...",
    "signing_secret": "..."
  },
  "oauth_authorize_url": "https://slack.com/oauth/v2/authorize?client_id=11256386461409.12227181692912&scope=chat:write&user_scope=chat:write",
  "team_id": "T0B7JBCDKC1",
  "team_domain": "slack-0yr1948"
}

FAQ

Can I delete an app with the API?

Yes. apps.manifest.delete with the configuration token and the app_id returned {"ok": true} for our second test app, and an export of the same app_id afterwards returned app_not_found. In the UI it is Basic Information > Delete App.

Is manifest.json the same as manifest.yaml?

Yes, the same fields in two formats. The web dialog converts between them; the API reads JSON only.

Always Active

Stop Jiggling Your Mouse.

Join hundreds of remote workers who never worry about their Slack status. Set it up once, stay green forever.

Related Articles

Guide

Slack OAuth Redirect URL on localhost: What Slack Accepts, Tested

We added localhost, 127.0.0.1, https, custom-scheme and tunnel redirect URLs to a Slack app on 1 October 2026, then ran the OAuth flow against a local server. What was accepted, how Slack matches the URL, and the PKCE rules.

Slack Green Team
Guide

Slack chat.delete API: Who Can Delete What, Tested

chat.delete removes a message by channel and ts. We deleted bot messages, a person's messages, a thread parent and an already deleted message with bot and user tokens, and list every response, including cant_delete_message and the tombstone a thread parent leaves.

Slack Green Team
Guide

Slack expired_trigger_id: How Long a trigger_id Lasts, Measured

We opened a Slack modal with views.open after delays from 0 to 5 seconds, 28 times, on 1 October 2026. The success rate per delay, the exact errors, and the Bolt code that avoids expired_trigger_id.

Slack Green Team