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.
On this page
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_usergives the app a bot and anxoxb-token. Slash commands and bot scopes fail validation without it. - •
slash_commandswith nourlworks becausesocket_mode_enabledis true. Slack sends the command over the WebSocket instead. - •
interactivity.is_enabledwith norequest_urlis also allowed with Socket Mode. Our modals and their submissions arrived that way; see the expired_trigger_id test. - •
redirect_urlslists where OAuth may send the user back.http://localhostis allowed; our redirect URL test covers what else is. - •
scopes.userasks 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:
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:
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:
| Change | code | message |
|---|---|---|
Settings key socket_mode instead of socket_mode_enabled | failed_constraint | invalid additional property: socket_mode |
No display_information.name | missing_field | missing required field: name |
| App name of 36 characters | failed_constraint | must be less than 36 characters |
Command lab-modal without the slash | failed_constraint | input must match regex pattern: ^\/ |
| Socket Mode off, no URLs | four errors | Interactivty requires a Request URL; Slash Commands require URLs; and two more |
Redirect URL myapp://oauth/callback | invalid_redirect_urls | Invalid redirect URLs myapp://oauth/callback |
The same, with "pkce_enabled": true | none | ok: true |
The YAML text sent as manifest | none listed | invalid_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". Those request URLs receive signed requests; checking the signature is in verify Slack request signatures. The manifest API also accepts an event request_url without sending Slack's challenge to it; our URL verification test shows what the settings page says afterwards. Renaming an app later is an apps.manifest.update call; which of its three names changes where is in Slack app name change.
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.
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
How to See All Your DMs in Slack, Including the Hidden Ones
The DMs tab lists your direct messages with people. App DMs, Slackbot and DMs with deleted accounts sit elsewhere. We compared the tab with the full list the Slack API returned.
Slack Images Not Showing? 5 Causes We Reproduced
We uploaded images to a Slack test channel and then switched each image setting off, ran /collapse, and posted from a bot that was not in the channel. Five causes, each reproduced.
How to Message Yourself on Slack (and What It Notifies)
Your own name under Direct messages opens a private DM with yourself. We sent it a note, a file, a scheduled message and a reminder, and checked which ones raised a badge.