diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,8 @@
+# Changelog
+
+## [3.1.0] — 2026-10-28
+
+- Add the complete shared Block Kit contract with checked immutable values, native field records, closed roles and Aeson encoding.
+- Include strict JSON ingress, exact decimal bounds, composition/editing helpers and separate tested HTTP consumers. Reject unrepresentable numeric encodings and range-check bounded integers before exponent expansion.
+- Support GHC 9.6.7 through the tested 9.14 series, independent locked/lower/latest dependency graphs, compiled public API checks, source archives and native Haddock documentation.
+- Join the ten-language coordinated release with verified source/documentation artifacts and fresh Hackage consumers.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,7 @@
+Copyright 2019 Nicholas Lambourne
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,142 @@
+# slackblocks for Haskell
+
+Typed, immutable Slack Block Kit composition with checked construction and Aeson
+encoding. Requires GHC 9.6.7 or newer within the tested compiler range.
+
+The native API covers all 89 shared models, closed roles and enumerations in
+contract 1.4.0. Haskell joins the coordinated 3.1.0 release on
+[Hackage](https://hackage.haskell.org/package/slackblocks). Add
+`slackblocks >= 3.1 && < 3.2` to your application's Cabal `build-depends`,
+run `cabal update`, then `cabal build`. The source archive and native HTTP
+integration are checked on supported compiler endpoints.
+
+```haskell
+{-# LANGUAGE OverloadedStrings #-}
+
+import Data.Aeson (encode)
+import Slackblocks
+
+example = do
+  label <- buildPlainText (plainText "Open")
+  action <-
+    buildButtonElement
+      ((buttonElement label) {buttonElementActionId = Just "open"})
+  body <- buildMarkdownText (markdownText "*Hello*")
+  section <-
+    buildSectionBlock
+      ( sectionBlock
+          { sectionBlockText = Just (markdownTextAsText body),
+            sectionBlockAccessory = Just (buttonElementAsElement action)
+          }
+      )
+  buildMessagePayload
+    ( (messagePayload "C123")
+        { messagePayloadBlocks = Just [sectionBlockAsBlock section]
+        }
+    )
+
+-- Handle Left errors or send encode payload on Right payload.
+```
+
+Field records are construction inputs, not wire values: they have no `ToJSON`
+instance. Checked values have hidden constructors. Inspect a value, edit its
+fields, then rebuild to validate the change. Optional `Nothing` omits a field;
+explicit `Just False` and empty text remain present where the contract allows
+them. Block IDs are omitted unless supplied. Validation reports a category,
+structured path, constraint and explanation without throwing exceptions.
+
+`fromValue` and `decode` check JSON at the ingress boundary. `decode` also rejects
+duplicate object keys (including escaped spellings and opaque metadata), invalid
+UTF-8 and trailing input. Aeson's `FromJSON` instances validate models too, but
+cannot recover duplicate keys already collapsed by an external Aeson parser.
+The byte decoder also rejects numeric exponent overflow before Aeson can narrow
+an exponent to `Int`. Checked native values and JSON ingress reject numbers whose
+normalized encoding would overflow or fail to round-trip, including numbers in
+metadata and extensions. This is a
+`Scientific` representation boundary, not a Slack magnitude limit; decimal bounds
+supplied as strings keep their arbitrary-exponent support. Already parsed Aeson
+values cannot recover an original exponent that an external parser truncated.
+Integer fields with shared bounds are range-checked before expanding exponents,
+so oversized inputs fail without first allocating an enormous integer.
+Use `Slackblocks.decode` when accepting untrusted JSON bytes. Typed child roles
+reject unsupported elements, while metadata and explicit unmodeled extension
+fields preserve arbitrary JSON. Extensions cannot override modeled fields or
+the `type` discriminator. Unknown enum values and receiving-element style flags
+are rejected. JSON null is treated as absence for an optional field and as a
+missing value for a required field.
+
+Use `buildPlanTask (taskCardBlock id title TaskStatusPending)` for a pending task
+inside a plan. `buildTaskCardBlock` checks standalone cards and rejects pending.
+A `PlanTask` cannot be placed in a message's block list. Number-input bounds are
+decimal strings; `decimalText` converts `Scientific` values without going
+through a binary floating-point number. Exponents are compared without expanding
+them into potentially enormous integers.
+
+Run `cabal test all` from the package directory. Tests use local data and make no
+Slack requests. The package version is the coordinated 3.1.0 release;
+the independent shared contract version is 1.4.0.
+
+The conformance suite constructs all 113 valid fixtures through native APIs and
+independently checks their wire objects. It also checks every invalid input at
+JSON ingress, native invalid constructions where representable, and compiler
+rejections where the type system prevents construction. `python bin/check`
+adds generated-source drift, bundled-spec parity, compilation boundaries,
+`cabal check`, a source archive and a fresh archive consumer. Install the source
+tools separately with `cabal install ormolu-0.7.7.0 --ignore-project` and
+`cabal install hlint-3.8 --ignore-project` before running the full checker.
+
+The full checker requires deterministic formatting and generation, HLint,
+independent compiled export discovery and at least **95% handwritten expression
+coverage** across validation, wire handling, strict JSON, decimal comparison,
+style and helpers. HPC reports branches and declarations separately; derived
+`Eq`/`Show` declarations do not stand in for behavior tests. The two generated
+modules remain in native/conformance and compiled API checks, but are excluded
+from the handwritten percentage. All other production modules are discovered
+from source and compiled coverage receipts; missing modules fail the gate.
+Negative controls prove that unclassified exports and missing/zero coverage fail.
+Five randomized properties each run 500 cases with a recorded reproducible seed.
+
+`accordionSection` returns editable container fields; build each section and use
+`accordion` to check and preserve their order. `paginate` uses an explicit action
+ID prefix and page settings, adds previous/next controls and an optional indicator,
+and leaves expanded surface limits to checked payload construction. It does not
+send messages or store page state. `workflowFromURL`, named/hex/RGB colors and
+`blockKitBuilderURL` are pure helpers. Builder links accept checked payloads or an
+explicit `BuilderJSON` object; they do not make network requests.
+
+The supported compiler series are GHC 9.6 through 9.14. Compiler requirements
+belong to this package, not to unrelated Slack clients:
+
+| GHC | CI patch | Platforms | Checks |
+| --- | --- | --- | --- |
+| 9.6 | 9.6.7 (minimum) | Linux, macOS, Windows | Native, archive, transport; Linux dependency/quality gates |
+| 9.8 | 9.8.4 | Linux | Every native suite |
+| 9.10 | 9.10.3 | Linux | Every native suite |
+| 9.12 | 9.12.4 | Linux | Every native suite |
+| 9.14 | 9.14.1 | Linux, macOS, Windows | Native, archive, transport; Linux dependency gates |
+
+Cabal 3.16.1.0 is the pinned development tool. The package uses Cabal syntax 3.0
+and GHC2021. Tested lower dependency bounds are Aeson 2.2.0.0, bytestring
+0.11.5.4, text 2.0.2, containers 0.6.7, scientific 0.3.7.0 and vector 0.13.0.0.
+QuickCheck is test-only. Exact lower, committed locked and latest-within-bounds
+resolutions run independently; both Aeson ordered and unordered KeyMap modes
+execute every native suite. The library does not impose its development lock on
+downstream consumers. Resolution receipts record actual versions and flags.
+
+`python bin/package-check` extracts the actual source distribution outside the
+checkout, executes every bundled suite, builds Haddock, then builds two fresh
+consumers against the tarball. Neither consumer has a workspace override or
+access to the monorepo's spec. One checks the public API, encoding, validation
+and editing; the other checks real HTTP request bytes against a local server.
+Run `python bin/dependency-check locked`, `lower`, or `latest` to inspect a
+specific dependency profile; `--unordered-keymap` selects the alternative Aeson
+representation. `lower` uses GHC 9.6.7 and `locked` has endpoint compiler profiles.
+
+The separate same-commit quality job owns source tools, export discovery and HPC.
+The aggregate native status requires native, quality, dependency, intermediate
+compiler and sending jobs. The default local `python bin/check` executes all
+applicable profiles and archive/transport checks as well as the quality gates.
+
+For native sending, see [the runnable HTTP integration](integrations/README.md).
+HTTP and TLS packages remain outside the library's dependencies. This is an
+application example, not a credential store or an automatic retry service.
diff --git a/bin/api-audit b/bin/api-audit
new file mode 100644
--- /dev/null
+++ b/bin/api-audit
@@ -0,0 +1,91 @@
+#!/usr/bin/env python3
+"""Audit the compiled public API against a separately reviewed inventory."""
+import argparse
+import json
+import os
+from pathlib import Path
+import re
+import subprocess
+import tempfile
+
+ROOT = Path(__file__).resolve().parents[1]
+SYMBOL = re.compile(r"^(?:type )?(Slackblocks(?:\.[A-Za-z][A-Za-z0-9_']*)+)\s*::", re.M)
+
+
+def exposed_modules():
+    cabal = (ROOT / 'slackblocks.cabal').read_text(encoding='utf-8')
+    section = re.search(r'^  exposed-modules:(.*?)(?=^  [a-z][a-z-]*:)', cabal, re.M | re.S)
+    if not section:
+        raise ValueError('Cannot find exposed modules')
+    return section[1].split()
+
+
+def browse(modules, extra_file=None):
+    commands = ':module Prelude\n' + ''.join(f'putStrLn "AUDIT_MODULE:{module}"\n:browse! {module}\n' for module in modules) + ':quit\n'
+    build = os.environ.get('SLACKBLOCKS_HASKELL_BUILD', 'dist-newstyle')
+    command = ['cabal', f'--builddir={build}', 'exec', '--', 'ghci', '-ignore-dot-ghci', '-v0', '-package', 'slackblocks']
+    if extra_file:
+        command.append(str(extra_file))
+    result = subprocess.run(command, input=commands, cwd=ROOT, text=True, capture_output=True, check=True)
+    if 'error:' in result.stderr.lower():
+        raise ValueError(result.stderr)
+    pieces = re.split(r'^AUDIT_MODULE:(\S+)\s*$', result.stdout, flags=re.M)
+    actual = {}
+    for index in range(1, len(pieces), 2):
+        actual[pieces[index]] = sorted(set(SYMBOL.findall(pieces[index+1])))
+    if set(actual) != set(modules) or any(not values for values in actual.values()):
+        raise ValueError('GHC export discovery was incomplete')
+    return actual
+
+
+def verify_exports(actual, expected):
+    if set(actual) != set(expected):
+        raise ValueError(f'Unclassified or removed public modules: {set(actual) ^ set(expected)}')
+    for module in actual:
+        if actual[module] != sorted(expected[module]):
+            difference = set(actual[module]) ^ set(expected[module])
+            raise ValueError(f'Unclassified or removed public exports in {module}: {sorted(difference)}')
+
+
+def audit():
+    inventory = json.loads((ROOT / 'conformance/public-api.json').read_text(encoding='utf-8'))
+    actual = browse(exposed_modules())
+    verify_exports(actual, inventory['exports'])
+    public = set().union(*(set(values) for values in actual.values()))
+    model = json.loads((ROOT / 'test/spec/model.json').read_text(encoding='utf-8'))
+    coverage = json.loads((ROOT / 'test/spec/coverage.json').read_text(encoding='utf-8'))['capabilities']
+    manifest = json.loads((ROOT / 'test/spec/manifest.json').read_text(encoding='utf-8'))
+    fixtures = {entry['id'] for entry in manifest['fixtures']}
+    if set(inventory['models']) != {entry['name'] for entry in model['types']}:
+        raise ValueError('Independent native model map differs from shared contract')
+    covered = set()
+    for entry in inventory['models'].values():
+        qualified = entry['type']
+        module, name = qualified.rsplit('.',1)
+        required = [qualified, qualified+'Fields', module+'.build'+name, module+'.inspect'+name, module+'.'+name[0].lower()+name[1:]]
+        if not set(required) <= public or entry['capability'] not in coverage:
+            raise ValueError(f'Missing checked public model or capability: {entry}')
+        covered.add(entry['capability'])
+    if covered != set(coverage) or any(not ids or not set(ids) <= fixtures for ids in coverage.values()):
+        raise ValueError('Shared capabilities are not fully backed by native fixtures')
+    if not inventory['supporting_api'] or any(not entry['reason'] or not entry['tests'] for entry in inventory['supporting_api']):
+        raise ValueError('Supporting API requires explicit reasons and tests')
+    scratch = '/Volumes/Scratch' if Path('/Volumes/Repos').is_mount() else os.environ.get('RUNNER_TEMP')
+    with tempfile.TemporaryDirectory(prefix='haskell-api-probe-', dir=scratch) as directory:
+        probe = Path(directory) / 'Probe.hs'
+        probe.write_text('module Slackblocks.AuditProbe (unmappedPublicProducer) where\nimport Slackblocks\nimport qualified Data.Text as T\nunmappedPublicProducer :: Result PlainText\nunmappedPublicProducer = buildPlainText (plainText (T.pack "probe"))\n', encoding='utf-8')
+        discovered = browse(['Slackblocks.AuditProbe'], probe)
+        if discovered != {'Slackblocks.AuditProbe':['Slackblocks.AuditProbe.unmappedPublicProducer']}:
+            raise ValueError('Compiled negative-control producer was not discovered')
+        try:
+            verify_exports(actual | discovered, inventory['exports'])
+        except ValueError:
+            pass
+        else:
+            raise ValueError('API guard accepted an unclassified handwritten producer')
+    print(f'Compiled API: {len(public)} symbols, {len(inventory["models"])} models, {len(covered)} capabilities; unmapped-producer control rejected.')
+
+
+if __name__ == '__main__':
+    argparse.ArgumentParser(description=__doc__).parse_args()
+    audit()
diff --git a/bin/check b/bin/check
new file mode 100644
--- /dev/null
+++ b/bin/check
@@ -0,0 +1,81 @@
+#!/usr/bin/env python3
+"""Run the package checks, including public compilation boundaries."""
+import argparse
+import os
+from pathlib import Path
+import subprocess
+import tempfile
+import sys
+
+ROOT = Path(__file__).resolve().parents[1]
+
+
+def run(*args, **kwargs):
+    return subprocess.run(args, cwd=ROOT, check=True, **kwargs)
+
+
+def main():
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument('--ci-native', action='store_true', help='Native/archive lane; dedicated same-commit jobs own quality and additional dependency/transport profiles')
+    args = parser.parse_args()
+    scratch = os.environ.get("RUNNER_TEMP")
+    if Path("/Volumes/Repos").is_dir():
+        for volume in ("Repos", "Cache", "Scratch"):
+            if not Path("/Volumes", volume).is_mount():
+                raise SystemExit(f"Required external volume is unavailable: {volume}")
+        scratch = "/Volumes/Scratch"
+        os.environ.setdefault("CABAL_DIR", "/Volumes/Cache/slackblocks-haskell/cabal")
+        os.environ.setdefault("SLACKBLOCKS_HASKELL_BUILD", "/Volumes/Cache/slackblocks-haskell/build-native")
+        os.environ.setdefault("SLACKBLOCKS_HASKELL_COVERAGE", "/Volumes/Cache/slackblocks-haskell/build-coverage")
+    build = os.environ.get("SLACKBLOCKS_HASKELL_BUILD", "dist-newstyle")
+    cabal = ["cabal", f"--builddir={build}"]
+    if args.ci_native and os.environ.get('GITHUB_ACTIONS') != 'true':
+        raise SystemExit('--ci-native is reserved for the workflow with its dedicated quality job')
+    run(sys.executable, "generator/test_generate.py")
+    run(sys.executable, "bin/sync-spec", "--check")
+    if (ROOT / "conformance/skiplist.txt").read_text(encoding="utf-8").strip():
+        raise SystemExit("Haskell conformance skip list must be empty")
+    run(*cabal, "test", "all", "--test-show-details=direct")
+    if not args.ci_native:
+        run(sys.executable, "bin/quality")
+    run("cabal", "check")
+    with tempfile.TemporaryDirectory(prefix="slackblocks-haskell-", dir=scratch) as tmp:
+        for name, source, expected in [
+            ("pass", "import Slackblocks\nmain = print (buildPlainText (plainText mempty))\n", None),
+            ("unchecked", "import Slackblocks\nimport Data.Aeson\nmain = print (encode (plainText mempty))\n", "ToJSON PlainTextFields"),
+            ("private", "import Slackblocks\nmain = print (CheckedPlainText (plainText mempty))\n", "CheckedPlainText"),
+            ("wrong-role", "import Slackblocks\nf = buttonElement (markdownText mempty)\nmain = pure ()\n", "PlainText"),
+            ("context-invalid-element", "import Slackblocks\nf :: ButtonElement -> ContextBlockFields\nf button = contextBlock [buttonElementAsElement button]\nmain = pure ()\n", "ContextElement"),
+            ("input-invalid-element", "import Slackblocks\nf label block = inputBlock label (dividerBlockAsBlock block)\nmain = pure ()\n", "InputElement"),
+            ("workflow-button-missing-action-id", "import Slackblocks\nf label workflow = buildWorkflowButtonElement (workflowButtonElement label workflow)\nmain = pure ()\n", "WorkflowButtonElementFields"),
+            ("number-input-missing-decimal-flag", "import Slackblocks\nf = buildNumberInputElement numberInputElement\nmain = pure ()\n", "NumberInputElementFields"),
+            ("slack-icon-missing-name", "import Slackblocks\nf = buildSlackIcon slackIcon\nmain = pure ()\n", "SlackIconFields"),
+            ("plan-missing-tasks", "import Slackblocks\nf title = buildPlanBlock (planBlock title)\nmain = pure ()\n", "PlanBlockFields"),
+            ("task-card-missing-status", "import Slackblocks\nf identifier title = buildTaskCardBlock (taskCardBlock identifier title)\nmain = pure ()\n", "TaskCardBlockFields"),
+            ("attachment-missing-blocks", "import Slackblocks\nf = buildAttachment attachment\nmain = pure ()\n", "AttachmentFields"),
+            ("plan-task-as-block", "import Slackblocks\nf :: PlanTask -> Block\nf = taskCardBlockAsBlock\nmain = pure ()\n", "PlanTask"),
+        ]:
+            path = Path(tmp, name + ".hs")
+            path.write_text(source)
+            result = subprocess.run([*cabal, "exec", "--", "ghc", "-fno-code", "-package", "slackblocks", str(path)], cwd=ROOT, text=True, capture_output=True)
+            if expected is None:
+                if result.returncode:
+                    raise SystemExit(result.stdout + result.stderr)
+            elif not result.returncode or expected not in result.stderr:
+                raise SystemExit(f"Unexpected compiler result for {name}:\n{result.stdout}{result.stderr}")
+    run(sys.executable, 'bin/package-check')
+    if not args.ci_native:
+        compiler = subprocess.check_output(['ghc', '--numeric-version'], text=True).strip()
+        if compiler in ('9.6.7', '9.14.1'):
+            run(sys.executable, 'bin/dependency-check', 'locked')
+        if compiler == '9.6.7':
+            run(sys.executable, 'bin/dependency-check', 'lower')
+        run(sys.executable, 'bin/dependency-check', 'latest')
+        run(sys.executable, 'bin/dependency-check', 'latest', '--unordered-keymap')
+        for profile in ('locked', 'lower', 'latest') if compiler == '9.6.7' else ('latest',):
+            run(sys.executable, 'bin/http-check', '--profile', profile)
+    print("Package and public compilation boundaries passed.")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/bin/dependency-check b/bin/dependency-check
new file mode 100644
--- /dev/null
+++ b/bin/dependency-check
@@ -0,0 +1,63 @@
+#!/usr/bin/env python3
+"""Independently resolve and execute locked, exact-lower or latest dependencies."""
+import argparse
+import json
+import os
+from pathlib import Path
+import subprocess
+import tempfile
+
+ROOT = Path(__file__).resolve().parents[1]
+
+
+def main():
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument('profile', choices=['locked', 'lower', 'latest'])
+    parser.add_argument('--unordered-keymap', action='store_true')
+    args = parser.parse_args()
+    compiler = subprocess.check_output(['ghc', '--numeric-version'], text=True).strip()
+    policy = {}
+    if args.profile != 'latest':
+        filename = f'locked-{compiler}.json' if args.profile == 'locked' else 'lower.json'
+        policy = json.loads((ROOT / 'dependencies' / filename).read_text(encoding='utf-8'))
+        if policy['compiler'] != compiler:
+            raise SystemExit('Exact lower bounds must run on their declared minimum compiler')
+    scratch = os.environ.get('RUNNER_TEMP')
+    cache = Path(os.environ.get('SLACKBLOCKS_HASKELL_DEPENDENCIES', 'dist-dependencies')).resolve()
+    if Path('/Volumes/Repos').is_dir():
+        for volume in ('Repos', 'Cache', 'Scratch'):
+            if not Path('/Volumes', volume).is_mount():
+                raise SystemExit(f'Required external volume unavailable: {volume}')
+        scratch = '/Volumes/Scratch'
+        cache = Path('/Volumes/Cache/slackblocks-haskell/dependency-checks')
+    build = cache / compiler / (args.profile + ('-unordered' if args.unordered_keymap else '-ordered'))
+    with tempfile.TemporaryDirectory(prefix='haskell-resolution-', dir=scratch) as directory:
+        project = Path(directory)
+        text = f'packages: {ROOT.as_posix()}\ntests: True\n'
+        if 'index_state' in policy:
+            text += f'index-state: {policy["index_state"]}\n'
+        pins = policy.get('constraints', {})
+        if pins:
+            text += 'constraints: ' + ', '.join(f'any.{name} =={version}' for name, version in pins.items()) + '\n'
+        text += 'package aeson\n  flags: ' + ('-' if args.unordered_keymap else '+') + 'ordered-keymap\n'
+        (project / 'cabal.project').write_text(text, encoding='utf-8')
+        # A unique project and no inherited freeze file force an independent
+        # solver plan. Store/cache reuse does not replace execution of tests.
+        command = ['cabal', f'--project-dir={project}', f'--builddir={build}']
+        subprocess.run([*command, 'test', 'all', '--jobs=2', '--disable-optimization', '--test-show-details=direct'], cwd=ROOT, check=True)
+        plan = json.loads((build / 'cache/plan.json').read_text(encoding='utf-8'))
+        versions = {entry['pkg-name']: entry['pkg-version'] for entry in plan['install-plan']}
+        for name, version in pins.items():
+            if versions.get(name) != version:
+                raise SystemExit(f'{args.profile} did not actually resolve {name} =={version}')
+        aeson = [entry for entry in plan['install-plan'] if entry['pkg-name'] == 'aeson']
+        if len(aeson) != 1 or aeson[0]['flags']['ordered-keymap'] == args.unordered_keymap:
+            raise SystemExit('Aeson KeyMap variant was not actually selected')
+        subprocess.run([*command, 'freeze'], cwd=ROOT, check=True)
+        (build / 'resolution.project').write_text(text, encoding='utf-8')
+        (build / 'resolution.freeze').write_bytes((project / 'cabal.project.freeze').read_bytes())
+    print(f'{args.profile} GHC {compiler}: every native suite passed with verified dependency versions and KeyMap variant; receipts in {build}')
+
+
+if __name__ == '__main__':
+    main()
diff --git a/bin/http-check b/bin/http-check
new file mode 100644
--- /dev/null
+++ b/bin/http-check
@@ -0,0 +1,129 @@
+#!/usr/bin/env python3
+"""Exercise the actual Haskell HTTP client against a loopback server only."""
+import argparse
+import json
+import os
+from pathlib import Path
+import subprocess
+import tempfile
+import threading
+from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
+
+ROOT = Path(__file__).resolve().parents[1]
+
+
+def check(args, project, build):
+    cabal = ['cabal', f'--project-dir={project}', f'--builddir={build}']
+    subprocess.run([*cabal, 'build', 'transport-check', '--jobs=2'], cwd=args.project, check=True)
+    executable = subprocess.check_output([*cabal, 'list-bin', 'transport-check'], cwd=args.project, text=True).strip()
+    plan = json.loads((build / 'cache/plan.json').read_text(encoding='utf-8'))
+    versions = {entry['pkg-name']: entry['pkg-version'] for entry in plan['install-plan']}
+    for name, version in args.pins.items():
+        if versions.get(name) != version:
+            raise SystemExit(f'HTTP dependency did not resolve {name} =={version}')
+    subprocess.run([*cabal, 'freeze'], cwd=args.project, check=True)
+    (build / 'resolution.project').write_bytes((project / 'cabal.project').read_bytes())
+    (build / 'resolution.freeze').write_bytes((project / 'cabal.project.freeze').read_bytes())
+    exercise(executable, args.project)
+
+
+def main():
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument('--project', type=Path, default=ROOT / 'integrations')
+    parser.add_argument('--profile', choices=['locked', 'lower', 'latest'], default='latest')
+    args = parser.parse_args()
+    compiler = subprocess.check_output(['ghc', '--numeric-version'], text=True).strip()
+    policy = {}
+    if args.profile != 'latest':
+        filename = f'http-locked-{compiler}.json' if args.profile == 'locked' else 'http-lower.json'
+        policy = json.loads((ROOT / 'dependencies' / filename).read_text(encoding='utf-8'))
+        if policy['compiler'] != compiler:
+            raise SystemExit('HTTP lower dependency profile requires its minimum compiler')
+    args.pins = policy.get('constraints', {})
+    scratch = os.environ.get('RUNNER_TEMP')
+    base = Path(os.environ.get('SLACKBLOCKS_HASKELL_HTTP_BUILD', ROOT / 'dist-http')).resolve()
+    if Path('/Volumes/Repos').is_dir():
+        for volume in ('Repos', 'Cache', 'Scratch'):
+            if not Path('/Volumes', volume).is_mount():
+                raise SystemExit(f'Required external volume unavailable: {volume}')
+        scratch = '/Volumes/Scratch'
+        if 'SLACKBLOCKS_HASKELL_HTTP_BUILD' not in os.environ:
+            base = Path('/Volumes/Cache/slackblocks-haskell/http-checks')
+    build = base / compiler / args.profile
+    inputs = (args.project / 'cabal.project').read_text(encoding='utf-8').strip()
+    if not inputs.startswith('packages: ') or '\n' in inputs:
+        raise SystemExit('HTTP consumer must declare only its two explicit package locations')
+    packages = [(args.project / path).resolve() for path in inputs.removeprefix('packages: ').split()]
+    if len(packages) != 2:
+        raise SystemExit('HTTP consumer must resolve exactly its application and slackblocks')
+    with tempfile.TemporaryDirectory(prefix='haskell-http-', dir=scratch) as directory:
+        project = Path(directory)
+        text = 'packages: ' + ' '.join(path.as_posix() for path in packages) + '\n'
+        if 'index_state' in policy:
+            text += f'index-state: {policy["index_state"]}\n'
+        if args.pins:
+            text += 'constraints: ' + ', '.join(f'any.{name} =={version}' for name, version in args.pins.items()) + '\n'
+        (project / 'cabal.project').write_text(text, encoding='utf-8')
+        check(args, project, build)
+
+
+def exercise(executable, project):
+    heading = {'type': 'header', 'text': {'type': 'plain_text', 'text': 'Hello 😀'}}
+    metadata = {'opaque': {'type': 'unmodeled', 'integer': 9007199254740993, 'flag': False, 'empty': {}}}
+    message = {'channel': 'C_TEST', 'blocks': [heading], 'metadata': metadata,
+               'text': '', 'mrkdwn': True,
+               'attachments': [{'blocks': [heading], 'color': '#8800ff'}]}
+    webhook = {'blocks': [heading], 'metadata': metadata}
+    expected = {
+        '/chat.postMessage': (message, True, 200, b'{"ok":true}'),
+        '/webhook': (webhook, False, 200, b'ok'),
+        '/response': ({'blocks': [heading], 'text': '', 'mrkdwn': True, 'replace_original': False, 'response_type': 'in_channel'}, False, 200, b'ok'),
+        '/views.open': ({'trigger_id': 'trigger-test', 'view': {'type': 'modal', 'title': heading['text'], 'blocks': [heading]}}, True, 200, b'{"ok":true}'),
+        '/views.publish': ({'user_id': 'U_TEST', 'view': {'type': 'home', 'blocks': [heading]}}, True, 200, b'{"ok":true}'),
+        '/api-error': (message, True, 200, b'{"ok":false,"error":"invalid_blocks"}'),
+        '/malformed': (message, True, 200, b'not json'),
+        '/rate-limited': (message, True, 429, b'{"ok":false}'),
+        '/server-error': (message, True, 500, b'{"ok":false}'),
+        '/redirect': (message, True, 302, b''),
+        '/webhook-error': (webhook, False, 200, b'invalid_payload'),
+    }
+    seen, failures = [], []
+
+    class Handler(BaseHTTPRequestHandler):
+        def log_message(self, *args):
+            pass
+
+        def do_POST(self):
+            try:
+                payload, authenticated, status, response = expected[self.path]
+                body = self.rfile.read(int(self.headers['Content-Length']))
+                assert json.loads(body.decode('utf-8')) == payload, f'{self.path}: payload altered: {body!r}'
+                assert self.headers['Content-Type'] == 'application/json; charset=utf-8'
+                assert self.headers.get('Authorization') == ('Bearer test-token' if authenticated else None)
+                seen.append(self.path)
+            except Exception as error:
+                failures.append(str(error))
+                status, response = 400, b'bad request'
+            self.send_response(status)
+            self.send_header('Content-Length', str(len(response)))
+            if status == 302:
+                self.send_header('Location', '/must-not-follow')
+            self.end_headers()
+            self.wfile.write(response)
+
+    with ThreadingHTTPServer(('127.0.0.1', 0), Handler) as server:
+        thread = threading.Thread(target=server.serve_forever, daemon=True)
+        thread.start()
+        try:
+            result = subprocess.run([executable, f'http://127.0.0.1:{server.server_port}'], cwd=project, timeout=90)
+        finally:
+            server.shutdown()
+            thread.join()
+    if failures or sorted(seen) != sorted(expected):
+        raise SystemExit(f'HTTP wire parity failed: {failures}; observed paths: {seen}')
+    result.check_returncode()
+    print('Actual HTTP bytes: five payload surfaces, exact metadata/false/Unicode, authorization boundaries, six errors, no retries or redirects.')
+
+
+if __name__ == '__main__':
+    main()
diff --git a/bin/package-check b/bin/package-check
new file mode 100644
--- /dev/null
+++ b/bin/package-check
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+"""Check the real source archive, standalone tests, Haddock and fresh consumers."""
+import os
+from pathlib import Path
+import re
+import shutil
+import subprocess
+import sys
+import tarfile
+import tempfile
+
+ROOT = Path(__file__).resolve().parents[1]
+
+
+def main():
+    scratch = os.environ.get('RUNNER_TEMP')
+    if Path('/Volumes/Repos').is_dir():
+        for volume in ('Repos', 'Cache', 'Scratch'):
+            if not Path('/Volumes', volume).is_mount():
+                raise SystemExit(f'Required external volume unavailable: {volume}')
+        scratch = '/Volumes/Scratch'
+    version = re.search(r'^version: (\S+)$', (ROOT / 'slackblocks.cabal').read_text(), re.M)[1]
+    subprocess.run(['cabal', 'check'], cwd=ROOT, check=True)
+    with tempfile.TemporaryDirectory(prefix='haskell-archive-', dir=scratch) as directory:
+        temp = Path(directory)
+        subprocess.run(['cabal', 'sdist', f'--output-directory={temp}'], cwd=ROOT, check=True)
+        archive = temp / f'slackblocks-{version}.tar.gz'
+        with tarfile.open(archive) as bundle:
+            members = bundle.getmembers()
+            prefix = f'slackblocks-{version}/'
+            if any((not m.name.startswith(prefix) and not (m.isdir() and m.name == prefix.rstrip('/'))) or '..' in Path(m.name).parts or m.issym() or m.islnk() for m in members):
+                raise SystemExit('Unsafe source archive member')
+            files = {m.name[len(prefix):] for m in members if m.isfile()}
+            required = {'slackblocks.cabal', 'LICENSE', 'README.md', 'CHANGELOG.md', 'consumer/Main.hs'}
+            for folder in ('src', 'test', 'integrations/src'):
+                required.update(p.relative_to(ROOT).as_posix() for p in (ROOT / folder).rglob('*') if p.is_file())
+            if not required <= files:
+                raise SystemExit(f'Archive missing owned inputs: {required - files}')
+            if any(set(Path(name).parts) & {'.git', '.env', 'dist-newstyle', 'dist-coverage', '__pycache__'} for name in files):
+                raise SystemExit('Build cache or private state leaked into archive')
+            bundle.extractall(temp, filter='data')
+        extracted = temp / f'slackblocks-{version}'
+        if (temp / 'spec').exists() or (extracted / '.git').exists():
+            raise SystemExit('Consumer unexpectedly has a monorepo')
+        (extracted / 'cabal.project').write_text('packages: .\ntests: True\n', encoding='utf-8')
+        # These tests use only the data installed by this actual sdist.
+        subprocess.run(['cabal', 'test', 'all', '--jobs=2', '--test-show-details=direct'], cwd=extracted, check=True)
+        subprocess.run(['cabal', 'haddock', 'lib:slackblocks', '--haddock-html'], cwd=extracted, check=True)
+        docs = list(extracted.glob('dist-newstyle/build/*/*/slackblocks-*/doc/html/slackblocks/Slackblocks-Models.html'))
+        if len(docs) != 1 or 'buildPlainText' not in docs[0].read_text(encoding='utf-8'):
+            raise SystemExit('Haddock did not document the actual compiled API')
+        consumer = temp / 'consumer'
+        consumer.mkdir()
+        shutil.copy2(extracted / 'consumer/Main.hs', consumer / 'Main.hs')
+        (consumer / 'consumer.cabal').write_text(f'''cabal-version: 3.0
+name: archive-consumer
+version: 0.0.0
+build-type: Simple
+executable archive-consumer
+  main-is: Main.hs
+  default-language: GHC2021
+  build-depends: base, slackblocks =={version}, aeson, containers
+''', encoding='utf-8')
+        (consumer / 'cabal.project').write_text(f'packages: . ../{archive.name}\n', encoding='utf-8')
+        subprocess.run(['cabal', 'run', 'archive-consumer', '--jobs=2'], cwd=consumer, check=True)
+        integration = temp / 'transport'
+        shutil.copytree(extracted / 'integrations', integration)
+        (integration / 'cabal.project').write_text(f'packages: . ../{archive.name}\n', encoding='utf-8')
+        environment = dict(os.environ, SLACKBLOCKS_HASKELL_HTTP_BUILD=str(temp / 'transport-build'))
+        subprocess.run([sys.executable, str(extracted / 'bin/http-check'), '--project', str(integration)], env=environment, check=True)
+        print(f'Archive {archive.name}: {len(files)} files; standalone suites, Haddock and two independently resolved artifact consumers passed.')
+
+
+if __name__ == '__main__':
+    main()
diff --git a/bin/quality b/bin/quality
new file mode 100644
--- /dev/null
+++ b/bin/quality
@@ -0,0 +1,106 @@
+#!/usr/bin/env python3
+"""Run all source, compiled API and handwritten coverage gates."""
+import os
+from pathlib import Path
+import re
+import subprocess
+import sys
+import xml.etree.ElementTree as ET
+
+ROOT = Path(__file__).resolve().parents[1]
+GENERATED = {"Slackblocks.Models", "Slackblocks.Internal.Schema"}
+
+
+def run(*args, **kwargs):
+    return subprocess.run(args, cwd=ROOT, check=True, **kwargs)
+
+
+def coverage(xml, expected):
+    tree = ET.fromstring(xml)
+    modules = {}
+    for module in tree.iter("module"):
+        name = module.attrib["name"].split("/")[-1]
+        if name in GENERATED:
+            continue
+        expressions = module.find("exprs")
+        if expressions is None:
+            raise ValueError(f"Missing expression coverage for {name}")
+        if name in modules:
+            raise ValueError(f"Duplicate module receipt: {name}")
+        used, total = int(expressions.attrib["count"]), int(expressions.attrib["boxes"])
+        if not 0 <= used <= total:
+            raise ValueError(f"Invalid coverage counts: {name}")
+        modules[name] = (used, total)
+    if set(modules) != expected:
+        raise ValueError(f"Missing/unexpected handwritten modules: {set(modules) ^ expected}")
+    used = sum(value[0] for value in modules.values())
+    total = sum(value[1] for value in modules.values())
+    if not total or used * 100 < total * 95:
+        raise ValueError(f"Handwritten expression coverage below 95%: {used}/{total}")
+    print(f"Handwritten expressions: {used}/{total} ({100 * used / total:.2f}%)")
+    return modules
+
+
+def main():
+    for command, expected in (("ormolu", "ormolu 0.7.7.0"), ("hlint", "HLint v3.8")):
+        actual = run(command, "--version", capture_output=True, text=True).stdout.splitlines()[0]
+        if actual.split(",", 1)[0] != expected:
+            raise SystemExit(f"Expected {expected}; found {actual}")
+    run(sys.executable, "generator/generate.py", "--check")
+    sources = [p for folder in ('src', 'test', 'consumer', 'integrations/src') for p in sorted((ROOT / folder).rglob('*.hs'))]
+    for source in sources:
+        run("ormolu", "--mode", "check", str(source))
+    # Generated code is checked by formatting, deterministic regeneration and
+    # GHC warnings. HLint reviews every handwritten production/test module.
+    handwritten = []
+    for source in sources:
+        module = ".".join(source.relative_to(ROOT / "src").with_suffix("").parts) if source.is_relative_to(ROOT / "src") else None
+        if module not in GENERATED:
+            handwritten.append(str(source))
+    run("hlint", *handwritten)
+    run(sys.executable, "bin/api-audit")
+    build = Path(os.environ.get("SLACKBLOCKS_HASKELL_COVERAGE", "dist-coverage")).resolve()
+    for tix in build.rglob("*.tix"):
+        tix.unlink()  # Only this checker's dedicated coverage build tree.
+    run("cabal", f"--builddir={build}", "test", "all", "--enable-coverage", "--disable-optimization", "--test-show-details=direct")
+    cabal = (ROOT / "slackblocks.cabal").read_text(encoding="utf-8")
+    suites = set(re.findall(r"^test-suite (\S+)", cabal, re.M))
+    tixes = sorted(build.glob("build/*/*/slackblocks-*/t/*/noopt/hpc/vanilla/tix/*.tix"))
+    if {p.stem for p in tixes} != suites or len(tixes) != len(suites):
+        raise SystemExit("Incomplete or ambiguous native coverage suite inventory")
+    mix = list(build.glob("build/*/*/slackblocks-*/noopt/build/extra-compilation-artifacts/hpc/vanilla/mix"))
+    if len(mix) != 1:
+        raise SystemExit("Ambiguous library coverage artifacts")
+    expected = {".".join(p.relative_to(ROOT / "src").with_suffix("").parts) for p in (ROOT / "src").rglob("*.hs")} - GENERATED
+    # A re-export-only module has no executable ticks, but must still have a
+    # compiled Mix record. Any added code is automatically included by HPC.
+    compiled = {p.stem for p in mix[0].rglob("*.mix")}
+    if not expected <= compiled:
+        raise SystemExit(f"Uninstrumented production modules: {expected - compiled}")
+    empty = {p.stem for p in mix[0].rglob("*.mix") if p.read_text(encoding="utf-8").rstrip().endswith(" []")}
+    expected -= empty
+    output = build / "quality"
+    output.mkdir(exist_ok=True)
+    combined = output / "combined.tix"
+    run("hpc", "sum", "--union", "--exclude=Main", "--exclude=Constructions", "--exclude=Paths_slackblocks", f"--output={combined}", *(str(p) for p in tixes))
+    report = ["hpc", "report", str(combined), f"--hpcdir={mix[0]}", "--per-module", "--xml-output"]
+    xml = run(*report, capture_output=True, text=True).stdout
+    (output / "coverage.xml").write_text(xml, encoding="utf-8")
+    coverage(xml, expected)
+    # Mutate actual compiled receipts, not a fabricated percentage, and prove
+    # the gate fails when execution disappears or a module is omitted.
+    tree = ET.fromstring(xml)
+    for exprs in tree.iter("exprs"):
+        exprs.set("count", "0")
+    for candidate, scope in ((ET.tostring(tree, encoding="unicode"), expected), (xml, expected | {"Slackblocks.Unmeasured"})):
+        try:
+            coverage(candidate, scope)
+        except ValueError:
+            pass
+        else:
+            raise SystemExit("Coverage guard accepted its negative control")
+    print("Source formatting, lint, compiled API, 95% handwritten coverage and negative controls passed.")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/bin/sync-spec b/bin/sync-spec
new file mode 100644
--- /dev/null
+++ b/bin/sync-spec
@@ -0,0 +1,26 @@
+#!/usr/bin/env python3
+"""Copy only shared test inputs; native constructions are independently owned."""
+import argparse
+from pathlib import Path
+
+root = Path(__file__).resolve().parents[2]
+destination = root / "haskell/test/spec"
+sources = [root / "spec" / name for name in
+           ("manifest.json", "model.json", "limits.json", "coverage.json", "vocabulary.json")]
+sources += sorted((root / "spec/fixtures").rglob("*.json"))
+parser = argparse.ArgumentParser()
+parser.add_argument("--check", action="store_true")
+args = parser.parse_args()
+expected = {destination / source.relative_to(root / "spec"): source.read_bytes()
+            for source in sources}
+stale = set(destination.rglob("*.json")) - expected.keys()
+if stale:
+    raise SystemExit(f"Unexpected shared test inputs: {sorted(stale)}")
+for path, content in expected.items():
+    if args.check:
+        if not path.exists() or path.read_bytes() != content:
+            raise SystemExit(f"Shared input is out of date: {path}")
+    else:
+        path.parent.mkdir(parents=True, exist_ok=True)
+        path.write_bytes(content)
+print(f"Shared test inputs verified: {len(expected)} files")
diff --git a/conformance/public-api.json b/conformance/public-api.json
new file mode 100644
--- /dev/null
+++ b/conformance/public-api.json
@@ -0,0 +1,2829 @@
+{
+  "exports": {
+    "Slackblocks": [
+      "Slackblocks.Helpers.Black",
+      "Slackblocks.Helpers.Blue",
+      "Slackblocks.Helpers.BuilderBlocks",
+      "Slackblocks.Helpers.BuilderHome",
+      "Slackblocks.Helpers.BuilderJSON",
+      "Slackblocks.Helpers.BuilderMessage",
+      "Slackblocks.Helpers.BuilderModal",
+      "Slackblocks.Helpers.BuilderPayload",
+      "Slackblocks.Helpers.BuilderResponse",
+      "Slackblocks.Helpers.BuilderWebhook",
+      "Slackblocks.Helpers.Color",
+      "Slackblocks.Helpers.Danger",
+      "Slackblocks.Helpers.Good",
+      "Slackblocks.Helpers.Green",
+      "Slackblocks.Helpers.NamedColor",
+      "Slackblocks.Helpers.Orange",
+      "Slackblocks.Helpers.PaginatorOptions",
+      "Slackblocks.Helpers.Purple",
+      "Slackblocks.Helpers.Red",
+      "Slackblocks.Helpers.Warning",
+      "Slackblocks.Helpers.Yellow",
+      "Slackblocks.Helpers.accordion",
+      "Slackblocks.Helpers.accordionSection",
+      "Slackblocks.Helpers.blockKitBuilderURL",
+      "Slackblocks.Helpers.colorText",
+      "Slackblocks.Helpers.hexColor",
+      "Slackblocks.Helpers.namedColor",
+      "Slackblocks.Helpers.paginate",
+      "Slackblocks.Helpers.paginator",
+      "Slackblocks.Helpers.paginatorActionIdPrefix",
+      "Slackblocks.Helpers.paginatorBlockId",
+      "Slackblocks.Helpers.paginatorNextText",
+      "Slackblocks.Helpers.paginatorPage",
+      "Slackblocks.Helpers.paginatorPageSize",
+      "Slackblocks.Helpers.paginatorPreviousText",
+      "Slackblocks.Helpers.paginatorShowPageIndicator",
+      "Slackblocks.Helpers.rgbColor",
+      "Slackblocks.Helpers.workflowFromURL",
+      "Slackblocks.Json.FromSlackJSON",
+      "Slackblocks.Json.decimalText",
+      "Slackblocks.Json.decode",
+      "Slackblocks.Json.fromSlackJSON",
+      "Slackblocks.Json.fromValue",
+      "Slackblocks.Models.ActionsBlock",
+      "Slackblocks.Models.ActionsBlockFields",
+      "Slackblocks.Models.AlertBlock",
+      "Slackblocks.Models.AlertBlockFields",
+      "Slackblocks.Models.AlertLevel",
+      "Slackblocks.Models.AlertLevelDefault",
+      "Slackblocks.Models.AlertLevelError",
+      "Slackblocks.Models.AlertLevelInfo",
+      "Slackblocks.Models.AlertLevelSuccess",
+      "Slackblocks.Models.AlertLevelWarning",
+      "Slackblocks.Models.AreaChart",
+      "Slackblocks.Models.AreaChartFields",
+      "Slackblocks.Models.Attachment",
+      "Slackblocks.Models.AttachmentFields",
+      "Slackblocks.Models.AxisConfig",
+      "Slackblocks.Models.AxisConfigFields",
+      "Slackblocks.Models.BarChart",
+      "Slackblocks.Models.BarChartFields",
+      "Slackblocks.Models.Block",
+      "Slackblocks.Models.ButtonElement",
+      "Slackblocks.Models.ButtonElementFields",
+      "Slackblocks.Models.ButtonStyle",
+      "Slackblocks.Models.ButtonStyleDanger",
+      "Slackblocks.Models.ButtonStylePrimary",
+      "Slackblocks.Models.CardBlock",
+      "Slackblocks.Models.CardBlockFields",
+      "Slackblocks.Models.CarouselBlock",
+      "Slackblocks.Models.CarouselBlockFields",
+      "Slackblocks.Models.ChannelMultiSelectElement",
+      "Slackblocks.Models.ChannelMultiSelectElementFields",
+      "Slackblocks.Models.ChannelSelectElement",
+      "Slackblocks.Models.ChannelSelectElementFields",
+      "Slackblocks.Models.Chart",
+      "Slackblocks.Models.ChartSegment",
+      "Slackblocks.Models.ChartSegmentFields",
+      "Slackblocks.Models.CheckboxesElement",
+      "Slackblocks.Models.CheckboxesElementFields",
+      "Slackblocks.Models.ColumnAlign",
+      "Slackblocks.Models.ColumnAlignCenter",
+      "Slackblocks.Models.ColumnAlignLeft",
+      "Slackblocks.Models.ColumnAlignRight",
+      "Slackblocks.Models.ColumnSettings",
+      "Slackblocks.Models.ColumnSettingsFields",
+      "Slackblocks.Models.ConfirmationDialogue",
+      "Slackblocks.Models.ConfirmationDialogueFields",
+      "Slackblocks.Models.ContainerBlock",
+      "Slackblocks.Models.ContainerBlockFields",
+      "Slackblocks.Models.ContainerWidth",
+      "Slackblocks.Models.ContainerWidthFull",
+      "Slackblocks.Models.ContainerWidthNarrow",
+      "Slackblocks.Models.ContainerWidthStandard",
+      "Slackblocks.Models.ContainerWidthWide",
+      "Slackblocks.Models.ContextActionsBlock",
+      "Slackblocks.Models.ContextActionsBlockFields",
+      "Slackblocks.Models.ContextActionsElement",
+      "Slackblocks.Models.ContextBlock",
+      "Slackblocks.Models.ContextBlockFields",
+      "Slackblocks.Models.ContextElement",
+      "Slackblocks.Models.ConversationFilter",
+      "Slackblocks.Models.ConversationFilterFields",
+      "Slackblocks.Models.ConversationMultiSelectElement",
+      "Slackblocks.Models.ConversationMultiSelectElementFields",
+      "Slackblocks.Models.ConversationSelectElement",
+      "Slackblocks.Models.ConversationSelectElementFields",
+      "Slackblocks.Models.DataPoint",
+      "Slackblocks.Models.DataPointFields",
+      "Slackblocks.Models.DataSeries",
+      "Slackblocks.Models.DataSeriesFields",
+      "Slackblocks.Models.DataTableBlock",
+      "Slackblocks.Models.DataTableBlockFields",
+      "Slackblocks.Models.DataTableCell",
+      "Slackblocks.Models.DataVisualizationBlock",
+      "Slackblocks.Models.DataVisualizationBlockFields",
+      "Slackblocks.Models.DatePickerElement",
+      "Slackblocks.Models.DatePickerElementFields",
+      "Slackblocks.Models.DateTimePickerElement",
+      "Slackblocks.Models.DateTimePickerElementFields",
+      "Slackblocks.Models.DispatchActionConfiguration",
+      "Slackblocks.Models.DispatchActionConfigurationFields",
+      "Slackblocks.Models.DividerBlock",
+      "Slackblocks.Models.DividerBlockFields",
+      "Slackblocks.Models.Element",
+      "Slackblocks.Models.EmailInputElement",
+      "Slackblocks.Models.EmailInputElementFields",
+      "Slackblocks.Models.ExternalMultiSelectElement",
+      "Slackblocks.Models.ExternalMultiSelectElementFields",
+      "Slackblocks.Models.ExternalSelectElement",
+      "Slackblocks.Models.ExternalSelectElementFields",
+      "Slackblocks.Models.FeedbackButton",
+      "Slackblocks.Models.FeedbackButtonFields",
+      "Slackblocks.Models.FeedbackButtonsElement",
+      "Slackblocks.Models.FeedbackButtonsElementFields",
+      "Slackblocks.Models.FileBlock",
+      "Slackblocks.Models.FileBlockFields",
+      "Slackblocks.Models.FileInputElement",
+      "Slackblocks.Models.FileInputElementFields",
+      "Slackblocks.Models.HeaderBlock",
+      "Slackblocks.Models.HeaderBlockFields",
+      "Slackblocks.Models.HomeTabView",
+      "Slackblocks.Models.HomeTabViewFields",
+      "Slackblocks.Models.IconButtonElement",
+      "Slackblocks.Models.IconButtonElementFields",
+      "Slackblocks.Models.IconButtonIcon",
+      "Slackblocks.Models.IconButtonIconTrash",
+      "Slackblocks.Models.ImageBlock",
+      "Slackblocks.Models.ImageBlockFields",
+      "Slackblocks.Models.ImageElement",
+      "Slackblocks.Models.ImageElementFields",
+      "Slackblocks.Models.InputBlock",
+      "Slackblocks.Models.InputBlockFields",
+      "Slackblocks.Models.InputElement",
+      "Slackblocks.Models.InputParameter",
+      "Slackblocks.Models.InputParameterFields",
+      "Slackblocks.Models.LineChart",
+      "Slackblocks.Models.LineChartFields",
+      "Slackblocks.Models.MarkdownBlock",
+      "Slackblocks.Models.MarkdownBlockFields",
+      "Slackblocks.Models.MarkdownText",
+      "Slackblocks.Models.MarkdownTextFields",
+      "Slackblocks.Models.MessagePayload",
+      "Slackblocks.Models.MessagePayloadFields",
+      "Slackblocks.Models.MessageResponse",
+      "Slackblocks.Models.MessageResponseFields",
+      "Slackblocks.Models.ModalView",
+      "Slackblocks.Models.ModalViewFields",
+      "Slackblocks.Models.NumberInputElement",
+      "Slackblocks.Models.NumberInputElementFields",
+      "Slackblocks.Models.OverflowElement",
+      "Slackblocks.Models.OverflowElementFields",
+      "Slackblocks.Models.PieChart",
+      "Slackblocks.Models.PieChartFields",
+      "Slackblocks.Models.PlainText",
+      "Slackblocks.Models.PlainTextFields",
+      "Slackblocks.Models.PlainTextInputElement",
+      "Slackblocks.Models.PlainTextInputElementFields",
+      "Slackblocks.Models.PlanBlock",
+      "Slackblocks.Models.PlanBlockFields",
+      "Slackblocks.Models.PlanTask",
+      "Slackblocks.Models.RadioButtonsElement",
+      "Slackblocks.Models.RadioButtonsElementFields",
+      "Slackblocks.Models.RawNumber",
+      "Slackblocks.Models.RawNumberFields",
+      "Slackblocks.Models.RawText",
+      "Slackblocks.Models.RawTextFields",
+      "Slackblocks.Models.ResponseType",
+      "Slackblocks.Models.ResponseTypeEphemeral",
+      "Slackblocks.Models.ResponseTypeInChannel",
+      "Slackblocks.Models.RichTextBlock",
+      "Slackblocks.Models.RichTextBlockElement",
+      "Slackblocks.Models.RichTextBlockFields",
+      "Slackblocks.Models.RichTextChannel",
+      "Slackblocks.Models.RichTextChannelFields",
+      "Slackblocks.Models.RichTextCodeBlock",
+      "Slackblocks.Models.RichTextCodeBlockFields",
+      "Slackblocks.Models.RichTextEmoji",
+      "Slackblocks.Models.RichTextEmojiFields",
+      "Slackblocks.Models.RichTextInputElement",
+      "Slackblocks.Models.RichTextInputElementFields",
+      "Slackblocks.Models.RichTextLink",
+      "Slackblocks.Models.RichTextLinkFields",
+      "Slackblocks.Models.RichTextList",
+      "Slackblocks.Models.RichTextListFields",
+      "Slackblocks.Models.RichTextListStyle",
+      "Slackblocks.Models.RichTextListStyleBullet",
+      "Slackblocks.Models.RichTextListStyleOrdered",
+      "Slackblocks.Models.RichTextQuote",
+      "Slackblocks.Models.RichTextQuoteFields",
+      "Slackblocks.Models.RichTextSection",
+      "Slackblocks.Models.RichTextSectionElement",
+      "Slackblocks.Models.RichTextSectionFields",
+      "Slackblocks.Models.RichTextText",
+      "Slackblocks.Models.RichTextTextFields",
+      "Slackblocks.Models.RichTextUser",
+      "Slackblocks.Models.RichTextUserFields",
+      "Slackblocks.Models.RichTextUserGroup",
+      "Slackblocks.Models.RichTextUserGroupFields",
+      "Slackblocks.Models.SectionBlock",
+      "Slackblocks.Models.SectionBlockFields",
+      "Slackblocks.Models.SelectOption",
+      "Slackblocks.Models.SelectOptionFields",
+      "Slackblocks.Models.SelectOptionGroup",
+      "Slackblocks.Models.SelectOptionGroupFields",
+      "Slackblocks.Models.SlackFile",
+      "Slackblocks.Models.SlackFileFields",
+      "Slackblocks.Models.SlackIcon",
+      "Slackblocks.Models.SlackIconFields",
+      "Slackblocks.Models.StaticMultiSelectElement",
+      "Slackblocks.Models.StaticMultiSelectElementFields",
+      "Slackblocks.Models.StaticSelectElement",
+      "Slackblocks.Models.StaticSelectElementFields",
+      "Slackblocks.Models.TableBlock",
+      "Slackblocks.Models.TableBlockFields",
+      "Slackblocks.Models.TableCell",
+      "Slackblocks.Models.TaskCardBlock",
+      "Slackblocks.Models.TaskCardBlockFields",
+      "Slackblocks.Models.TaskStatus",
+      "Slackblocks.Models.TaskStatusComplete",
+      "Slackblocks.Models.TaskStatusError",
+      "Slackblocks.Models.TaskStatusInProgress",
+      "Slackblocks.Models.TaskStatusPending",
+      "Slackblocks.Models.TextObject",
+      "Slackblocks.Models.TimePickerElement",
+      "Slackblocks.Models.TimePickerElementFields",
+      "Slackblocks.Models.Trigger",
+      "Slackblocks.Models.TriggerFields",
+      "Slackblocks.Models.UrlInputElement",
+      "Slackblocks.Models.UrlInputElementFields",
+      "Slackblocks.Models.UrlSource",
+      "Slackblocks.Models.UrlSourceFields",
+      "Slackblocks.Models.UserMultiSelectElement",
+      "Slackblocks.Models.UserMultiSelectElementFields",
+      "Slackblocks.Models.UserSelectElement",
+      "Slackblocks.Models.UserSelectElementFields",
+      "Slackblocks.Models.VideoBlock",
+      "Slackblocks.Models.VideoBlockFields",
+      "Slackblocks.Models.WebhookMessage",
+      "Slackblocks.Models.WebhookMessageFields",
+      "Slackblocks.Models.Workflow",
+      "Slackblocks.Models.WorkflowButtonElement",
+      "Slackblocks.Models.WorkflowButtonElementFields",
+      "Slackblocks.Models.WorkflowFields",
+      "Slackblocks.Models.actionsBlock",
+      "Slackblocks.Models.actionsBlockAsBlock",
+      "Slackblocks.Models.actionsBlockBlockId",
+      "Slackblocks.Models.actionsBlockElements",
+      "Slackblocks.Models.actionsBlockExtensions",
+      "Slackblocks.Models.alertBlock",
+      "Slackblocks.Models.alertBlockAsBlock",
+      "Slackblocks.Models.alertBlockBlockId",
+      "Slackblocks.Models.alertBlockExtensions",
+      "Slackblocks.Models.alertBlockLevel",
+      "Slackblocks.Models.alertBlockText",
+      "Slackblocks.Models.areaChart",
+      "Slackblocks.Models.areaChartAsChart",
+      "Slackblocks.Models.areaChartAxisConfig",
+      "Slackblocks.Models.areaChartExtensions",
+      "Slackblocks.Models.areaChartSeries",
+      "Slackblocks.Models.attachment",
+      "Slackblocks.Models.attachmentBlocks",
+      "Slackblocks.Models.attachmentColor",
+      "Slackblocks.Models.attachmentExtensions",
+      "Slackblocks.Models.attachmentFallback",
+      "Slackblocks.Models.axisConfig",
+      "Slackblocks.Models.axisConfigCategories",
+      "Slackblocks.Models.axisConfigExtensions",
+      "Slackblocks.Models.axisConfigXLabel",
+      "Slackblocks.Models.axisConfigYLabel",
+      "Slackblocks.Models.barChart",
+      "Slackblocks.Models.barChartAsChart",
+      "Slackblocks.Models.barChartAxisConfig",
+      "Slackblocks.Models.barChartExtensions",
+      "Slackblocks.Models.barChartSeries",
+      "Slackblocks.Models.buildActionsBlock",
+      "Slackblocks.Models.buildAlertBlock",
+      "Slackblocks.Models.buildAreaChart",
+      "Slackblocks.Models.buildAttachment",
+      "Slackblocks.Models.buildAxisConfig",
+      "Slackblocks.Models.buildBarChart",
+      "Slackblocks.Models.buildButtonElement",
+      "Slackblocks.Models.buildCardBlock",
+      "Slackblocks.Models.buildCarouselBlock",
+      "Slackblocks.Models.buildChannelMultiSelectElement",
+      "Slackblocks.Models.buildChannelSelectElement",
+      "Slackblocks.Models.buildChartSegment",
+      "Slackblocks.Models.buildCheckboxesElement",
+      "Slackblocks.Models.buildColumnSettings",
+      "Slackblocks.Models.buildConfirmationDialogue",
+      "Slackblocks.Models.buildContainerBlock",
+      "Slackblocks.Models.buildContextActionsBlock",
+      "Slackblocks.Models.buildContextBlock",
+      "Slackblocks.Models.buildConversationFilter",
+      "Slackblocks.Models.buildConversationMultiSelectElement",
+      "Slackblocks.Models.buildConversationSelectElement",
+      "Slackblocks.Models.buildDataPoint",
+      "Slackblocks.Models.buildDataSeries",
+      "Slackblocks.Models.buildDataTableBlock",
+      "Slackblocks.Models.buildDataVisualizationBlock",
+      "Slackblocks.Models.buildDatePickerElement",
+      "Slackblocks.Models.buildDateTimePickerElement",
+      "Slackblocks.Models.buildDispatchActionConfiguration",
+      "Slackblocks.Models.buildDividerBlock",
+      "Slackblocks.Models.buildEmailInputElement",
+      "Slackblocks.Models.buildExternalMultiSelectElement",
+      "Slackblocks.Models.buildExternalSelectElement",
+      "Slackblocks.Models.buildFeedbackButton",
+      "Slackblocks.Models.buildFeedbackButtonsElement",
+      "Slackblocks.Models.buildFileBlock",
+      "Slackblocks.Models.buildFileInputElement",
+      "Slackblocks.Models.buildHeaderBlock",
+      "Slackblocks.Models.buildHomeTabView",
+      "Slackblocks.Models.buildIconButtonElement",
+      "Slackblocks.Models.buildImageBlock",
+      "Slackblocks.Models.buildImageElement",
+      "Slackblocks.Models.buildInputBlock",
+      "Slackblocks.Models.buildInputParameter",
+      "Slackblocks.Models.buildLineChart",
+      "Slackblocks.Models.buildMarkdownBlock",
+      "Slackblocks.Models.buildMarkdownText",
+      "Slackblocks.Models.buildMessagePayload",
+      "Slackblocks.Models.buildMessageResponse",
+      "Slackblocks.Models.buildModalView",
+      "Slackblocks.Models.buildNumberInputElement",
+      "Slackblocks.Models.buildOverflowElement",
+      "Slackblocks.Models.buildPieChart",
+      "Slackblocks.Models.buildPlainText",
+      "Slackblocks.Models.buildPlainTextInputElement",
+      "Slackblocks.Models.buildPlanBlock",
+      "Slackblocks.Models.buildPlanTask",
+      "Slackblocks.Models.buildRadioButtonsElement",
+      "Slackblocks.Models.buildRawNumber",
+      "Slackblocks.Models.buildRawText",
+      "Slackblocks.Models.buildRichTextBlock",
+      "Slackblocks.Models.buildRichTextChannel",
+      "Slackblocks.Models.buildRichTextCodeBlock",
+      "Slackblocks.Models.buildRichTextEmoji",
+      "Slackblocks.Models.buildRichTextInputElement",
+      "Slackblocks.Models.buildRichTextLink",
+      "Slackblocks.Models.buildRichTextList",
+      "Slackblocks.Models.buildRichTextQuote",
+      "Slackblocks.Models.buildRichTextSection",
+      "Slackblocks.Models.buildRichTextText",
+      "Slackblocks.Models.buildRichTextUser",
+      "Slackblocks.Models.buildRichTextUserGroup",
+      "Slackblocks.Models.buildSectionBlock",
+      "Slackblocks.Models.buildSelectOption",
+      "Slackblocks.Models.buildSelectOptionGroup",
+      "Slackblocks.Models.buildSlackFile",
+      "Slackblocks.Models.buildSlackIcon",
+      "Slackblocks.Models.buildStaticMultiSelectElement",
+      "Slackblocks.Models.buildStaticSelectElement",
+      "Slackblocks.Models.buildTableBlock",
+      "Slackblocks.Models.buildTaskCardBlock",
+      "Slackblocks.Models.buildTimePickerElement",
+      "Slackblocks.Models.buildTrigger",
+      "Slackblocks.Models.buildUrlInputElement",
+      "Slackblocks.Models.buildUrlSource",
+      "Slackblocks.Models.buildUserMultiSelectElement",
+      "Slackblocks.Models.buildUserSelectElement",
+      "Slackblocks.Models.buildVideoBlock",
+      "Slackblocks.Models.buildWebhookMessage",
+      "Slackblocks.Models.buildWorkflow",
+      "Slackblocks.Models.buildWorkflowButtonElement",
+      "Slackblocks.Models.buttonElement",
+      "Slackblocks.Models.buttonElementAccessibilityLabel",
+      "Slackblocks.Models.buttonElementActionId",
+      "Slackblocks.Models.buttonElementAgentPrompt",
+      "Slackblocks.Models.buttonElementAgentPromptDisplay",
+      "Slackblocks.Models.buttonElementAsElement",
+      "Slackblocks.Models.buttonElementConfirm",
+      "Slackblocks.Models.buttonElementExtensions",
+      "Slackblocks.Models.buttonElementStyle",
+      "Slackblocks.Models.buttonElementText",
+      "Slackblocks.Models.buttonElementUrl",
+      "Slackblocks.Models.buttonElementValue",
+      "Slackblocks.Models.cardBlock",
+      "Slackblocks.Models.cardBlockActions",
+      "Slackblocks.Models.cardBlockAsBlock",
+      "Slackblocks.Models.cardBlockBlockId",
+      "Slackblocks.Models.cardBlockBody",
+      "Slackblocks.Models.cardBlockExtensions",
+      "Slackblocks.Models.cardBlockHeroImage",
+      "Slackblocks.Models.cardBlockIcon",
+      "Slackblocks.Models.cardBlockSlackIcon",
+      "Slackblocks.Models.cardBlockSubtext",
+      "Slackblocks.Models.cardBlockSubtitle",
+      "Slackblocks.Models.cardBlockTitle",
+      "Slackblocks.Models.carouselBlock",
+      "Slackblocks.Models.carouselBlockAsBlock",
+      "Slackblocks.Models.carouselBlockBlockId",
+      "Slackblocks.Models.carouselBlockElements",
+      "Slackblocks.Models.carouselBlockExtensions",
+      "Slackblocks.Models.channelMultiSelectElement",
+      "Slackblocks.Models.channelMultiSelectElementActionId",
+      "Slackblocks.Models.channelMultiSelectElementAsElement",
+      "Slackblocks.Models.channelMultiSelectElementAsInputElement",
+      "Slackblocks.Models.channelMultiSelectElementConfirm",
+      "Slackblocks.Models.channelMultiSelectElementExtensions",
+      "Slackblocks.Models.channelMultiSelectElementFocusOnLoad",
+      "Slackblocks.Models.channelMultiSelectElementInitialChannels",
+      "Slackblocks.Models.channelMultiSelectElementMaxSelectedItems",
+      "Slackblocks.Models.channelMultiSelectElementPlaceholder",
+      "Slackblocks.Models.channelSelectElement",
+      "Slackblocks.Models.channelSelectElementActionId",
+      "Slackblocks.Models.channelSelectElementAsElement",
+      "Slackblocks.Models.channelSelectElementAsInputElement",
+      "Slackblocks.Models.channelSelectElementConfirm",
+      "Slackblocks.Models.channelSelectElementExtensions",
+      "Slackblocks.Models.channelSelectElementFocusOnLoad",
+      "Slackblocks.Models.channelSelectElementInitialChannel",
+      "Slackblocks.Models.channelSelectElementPlaceholder",
+      "Slackblocks.Models.channelSelectElementResponseUrlEnabled",
+      "Slackblocks.Models.chartSegment",
+      "Slackblocks.Models.chartSegmentExtensions",
+      "Slackblocks.Models.chartSegmentLabel",
+      "Slackblocks.Models.chartSegmentValue",
+      "Slackblocks.Models.checkboxesElement",
+      "Slackblocks.Models.checkboxesElementActionId",
+      "Slackblocks.Models.checkboxesElementAsElement",
+      "Slackblocks.Models.checkboxesElementAsInputElement",
+      "Slackblocks.Models.checkboxesElementConfirm",
+      "Slackblocks.Models.checkboxesElementExtensions",
+      "Slackblocks.Models.checkboxesElementFocusOnLoad",
+      "Slackblocks.Models.checkboxesElementInitialOptions",
+      "Slackblocks.Models.checkboxesElementOptions",
+      "Slackblocks.Models.columnSettings",
+      "Slackblocks.Models.columnSettingsAlign",
+      "Slackblocks.Models.columnSettingsExtensions",
+      "Slackblocks.Models.columnSettingsIsWrapped",
+      "Slackblocks.Models.confirmationDialogue",
+      "Slackblocks.Models.confirmationDialogueConfirm",
+      "Slackblocks.Models.confirmationDialogueDeny",
+      "Slackblocks.Models.confirmationDialogueExtensions",
+      "Slackblocks.Models.confirmationDialogueStyle",
+      "Slackblocks.Models.confirmationDialogueText",
+      "Slackblocks.Models.confirmationDialogueTitle",
+      "Slackblocks.Models.containerBlock",
+      "Slackblocks.Models.containerBlockAsBlock",
+      "Slackblocks.Models.containerBlockBlockId",
+      "Slackblocks.Models.containerBlockChildBlocks",
+      "Slackblocks.Models.containerBlockDefaultCollapsed",
+      "Slackblocks.Models.containerBlockExtensions",
+      "Slackblocks.Models.containerBlockHasHeaderDivider",
+      "Slackblocks.Models.containerBlockIcon",
+      "Slackblocks.Models.containerBlockIsCollapsible",
+      "Slackblocks.Models.containerBlockRichTextTitle",
+      "Slackblocks.Models.containerBlockSubtitle",
+      "Slackblocks.Models.containerBlockTitle",
+      "Slackblocks.Models.containerBlockWidth",
+      "Slackblocks.Models.contextActionsBlock",
+      "Slackblocks.Models.contextActionsBlockAsBlock",
+      "Slackblocks.Models.contextActionsBlockBlockId",
+      "Slackblocks.Models.contextActionsBlockElements",
+      "Slackblocks.Models.contextActionsBlockExtensions",
+      "Slackblocks.Models.contextBlock",
+      "Slackblocks.Models.contextBlockAsBlock",
+      "Slackblocks.Models.contextBlockBlockId",
+      "Slackblocks.Models.contextBlockElements",
+      "Slackblocks.Models.contextBlockExtensions",
+      "Slackblocks.Models.conversationFilter",
+      "Slackblocks.Models.conversationFilterExcludeBotUsers",
+      "Slackblocks.Models.conversationFilterExcludeExternalSharedChannels",
+      "Slackblocks.Models.conversationFilterExtensions",
+      "Slackblocks.Models.conversationFilterInclude",
+      "Slackblocks.Models.conversationMultiSelectElement",
+      "Slackblocks.Models.conversationMultiSelectElementActionId",
+      "Slackblocks.Models.conversationMultiSelectElementAsElement",
+      "Slackblocks.Models.conversationMultiSelectElementAsInputElement",
+      "Slackblocks.Models.conversationMultiSelectElementConfirm",
+      "Slackblocks.Models.conversationMultiSelectElementDefaultToCurrentConversation",
+      "Slackblocks.Models.conversationMultiSelectElementExtensions",
+      "Slackblocks.Models.conversationMultiSelectElementFilter",
+      "Slackblocks.Models.conversationMultiSelectElementFocusOnLoad",
+      "Slackblocks.Models.conversationMultiSelectElementInitialConversations",
+      "Slackblocks.Models.conversationMultiSelectElementMaxSelectedItems",
+      "Slackblocks.Models.conversationMultiSelectElementPlaceholder",
+      "Slackblocks.Models.conversationSelectElement",
+      "Slackblocks.Models.conversationSelectElementActionId",
+      "Slackblocks.Models.conversationSelectElementAsElement",
+      "Slackblocks.Models.conversationSelectElementAsInputElement",
+      "Slackblocks.Models.conversationSelectElementConfirm",
+      "Slackblocks.Models.conversationSelectElementDefaultToCurrentConversation",
+      "Slackblocks.Models.conversationSelectElementExtensions",
+      "Slackblocks.Models.conversationSelectElementFilter",
+      "Slackblocks.Models.conversationSelectElementFocusOnLoad",
+      "Slackblocks.Models.conversationSelectElementInitialConversation",
+      "Slackblocks.Models.conversationSelectElementPlaceholder",
+      "Slackblocks.Models.conversationSelectElementResponseUrlEnabled",
+      "Slackblocks.Models.dataPoint",
+      "Slackblocks.Models.dataPointExtensions",
+      "Slackblocks.Models.dataPointLabel",
+      "Slackblocks.Models.dataPointValue",
+      "Slackblocks.Models.dataSeries",
+      "Slackblocks.Models.dataSeriesData",
+      "Slackblocks.Models.dataSeriesExtensions",
+      "Slackblocks.Models.dataSeriesName",
+      "Slackblocks.Models.dataTableBlock",
+      "Slackblocks.Models.dataTableBlockAsBlock",
+      "Slackblocks.Models.dataTableBlockBlockId",
+      "Slackblocks.Models.dataTableBlockCaption",
+      "Slackblocks.Models.dataTableBlockExtensions",
+      "Slackblocks.Models.dataTableBlockPageSize",
+      "Slackblocks.Models.dataTableBlockRowHeaderColumnIndex",
+      "Slackblocks.Models.dataTableBlockRows",
+      "Slackblocks.Models.dataVisualizationBlock",
+      "Slackblocks.Models.dataVisualizationBlockAsBlock",
+      "Slackblocks.Models.dataVisualizationBlockBlockId",
+      "Slackblocks.Models.dataVisualizationBlockChart",
+      "Slackblocks.Models.dataVisualizationBlockExtensions",
+      "Slackblocks.Models.dataVisualizationBlockTitle",
+      "Slackblocks.Models.datePickerElement",
+      "Slackblocks.Models.datePickerElementActionId",
+      "Slackblocks.Models.datePickerElementAsElement",
+      "Slackblocks.Models.datePickerElementAsInputElement",
+      "Slackblocks.Models.datePickerElementConfirm",
+      "Slackblocks.Models.datePickerElementExtensions",
+      "Slackblocks.Models.datePickerElementFocusOnLoad",
+      "Slackblocks.Models.datePickerElementInitialDate",
+      "Slackblocks.Models.datePickerElementPlaceholder",
+      "Slackblocks.Models.dateTimePickerElement",
+      "Slackblocks.Models.dateTimePickerElementActionId",
+      "Slackblocks.Models.dateTimePickerElementAsElement",
+      "Slackblocks.Models.dateTimePickerElementAsInputElement",
+      "Slackblocks.Models.dateTimePickerElementConfirm",
+      "Slackblocks.Models.dateTimePickerElementExtensions",
+      "Slackblocks.Models.dateTimePickerElementFocusOnLoad",
+      "Slackblocks.Models.dateTimePickerElementInitialDateTime",
+      "Slackblocks.Models.dispatchActionConfiguration",
+      "Slackblocks.Models.dispatchActionConfigurationExtensions",
+      "Slackblocks.Models.dispatchActionConfigurationTriggerActionsOn",
+      "Slackblocks.Models.dividerBlock",
+      "Slackblocks.Models.dividerBlockAsBlock",
+      "Slackblocks.Models.dividerBlockBlockId",
+      "Slackblocks.Models.dividerBlockExtensions",
+      "Slackblocks.Models.emailInputElement",
+      "Slackblocks.Models.emailInputElementActionId",
+      "Slackblocks.Models.emailInputElementAsElement",
+      "Slackblocks.Models.emailInputElementAsInputElement",
+      "Slackblocks.Models.emailInputElementDispatchActionConfig",
+      "Slackblocks.Models.emailInputElementExtensions",
+      "Slackblocks.Models.emailInputElementFocusOnLoad",
+      "Slackblocks.Models.emailInputElementInitialValue",
+      "Slackblocks.Models.emailInputElementPlaceholder",
+      "Slackblocks.Models.externalMultiSelectElement",
+      "Slackblocks.Models.externalMultiSelectElementActionId",
+      "Slackblocks.Models.externalMultiSelectElementAsElement",
+      "Slackblocks.Models.externalMultiSelectElementAsInputElement",
+      "Slackblocks.Models.externalMultiSelectElementConfirm",
+      "Slackblocks.Models.externalMultiSelectElementExtensions",
+      "Slackblocks.Models.externalMultiSelectElementFocusOnLoad",
+      "Slackblocks.Models.externalMultiSelectElementInitialOptions",
+      "Slackblocks.Models.externalMultiSelectElementMaxSelectedItems",
+      "Slackblocks.Models.externalMultiSelectElementMinQueryLength",
+      "Slackblocks.Models.externalMultiSelectElementPlaceholder",
+      "Slackblocks.Models.externalSelectElement",
+      "Slackblocks.Models.externalSelectElementActionId",
+      "Slackblocks.Models.externalSelectElementAsElement",
+      "Slackblocks.Models.externalSelectElementAsInputElement",
+      "Slackblocks.Models.externalSelectElementConfirm",
+      "Slackblocks.Models.externalSelectElementExtensions",
+      "Slackblocks.Models.externalSelectElementFocusOnLoad",
+      "Slackblocks.Models.externalSelectElementInitialOption",
+      "Slackblocks.Models.externalSelectElementMinQueryLength",
+      "Slackblocks.Models.externalSelectElementPlaceholder",
+      "Slackblocks.Models.feedbackButton",
+      "Slackblocks.Models.feedbackButtonAccessibilityLabel",
+      "Slackblocks.Models.feedbackButtonExtensions",
+      "Slackblocks.Models.feedbackButtonText",
+      "Slackblocks.Models.feedbackButtonValue",
+      "Slackblocks.Models.feedbackButtonsElement",
+      "Slackblocks.Models.feedbackButtonsElementActionId",
+      "Slackblocks.Models.feedbackButtonsElementAsContextActionsElement",
+      "Slackblocks.Models.feedbackButtonsElementAsElement",
+      "Slackblocks.Models.feedbackButtonsElementExtensions",
+      "Slackblocks.Models.feedbackButtonsElementNegativeButton",
+      "Slackblocks.Models.feedbackButtonsElementPositiveButton",
+      "Slackblocks.Models.fileBlock",
+      "Slackblocks.Models.fileBlockAsBlock",
+      "Slackblocks.Models.fileBlockBlockId",
+      "Slackblocks.Models.fileBlockExtensions",
+      "Slackblocks.Models.fileBlockExternalId",
+      "Slackblocks.Models.fileBlockSource",
+      "Slackblocks.Models.fileInputElement",
+      "Slackblocks.Models.fileInputElementActionId",
+      "Slackblocks.Models.fileInputElementAsElement",
+      "Slackblocks.Models.fileInputElementAsInputElement",
+      "Slackblocks.Models.fileInputElementExtensions",
+      "Slackblocks.Models.fileInputElementFiletypes",
+      "Slackblocks.Models.fileInputElementMaxFiles",
+      "Slackblocks.Models.headerBlock",
+      "Slackblocks.Models.headerBlockAsBlock",
+      "Slackblocks.Models.headerBlockBlockId",
+      "Slackblocks.Models.headerBlockExtensions",
+      "Slackblocks.Models.headerBlockText",
+      "Slackblocks.Models.homeTabView",
+      "Slackblocks.Models.homeTabViewBlocks",
+      "Slackblocks.Models.homeTabViewCallbackId",
+      "Slackblocks.Models.homeTabViewExtensions",
+      "Slackblocks.Models.homeTabViewExternalId",
+      "Slackblocks.Models.homeTabViewPrivateMetadata",
+      "Slackblocks.Models.iconButtonElement",
+      "Slackblocks.Models.iconButtonElementAccessibilityLabel",
+      "Slackblocks.Models.iconButtonElementActionId",
+      "Slackblocks.Models.iconButtonElementAsContextActionsElement",
+      "Slackblocks.Models.iconButtonElementAsElement",
+      "Slackblocks.Models.iconButtonElementConfirm",
+      "Slackblocks.Models.iconButtonElementExtensions",
+      "Slackblocks.Models.iconButtonElementIcon",
+      "Slackblocks.Models.iconButtonElementText",
+      "Slackblocks.Models.iconButtonElementValue",
+      "Slackblocks.Models.iconButtonElementVisibleToUserIds",
+      "Slackblocks.Models.imageBlock",
+      "Slackblocks.Models.imageBlockAltText",
+      "Slackblocks.Models.imageBlockAsBlock",
+      "Slackblocks.Models.imageBlockBlockId",
+      "Slackblocks.Models.imageBlockExtensions",
+      "Slackblocks.Models.imageBlockImageUrl",
+      "Slackblocks.Models.imageBlockSlackFile",
+      "Slackblocks.Models.imageBlockTitle",
+      "Slackblocks.Models.imageElement",
+      "Slackblocks.Models.imageElementAltText",
+      "Slackblocks.Models.imageElementAsContextElement",
+      "Slackblocks.Models.imageElementAsElement",
+      "Slackblocks.Models.imageElementExtensions",
+      "Slackblocks.Models.imageElementImageUrl",
+      "Slackblocks.Models.imageElementSlackFile",
+      "Slackblocks.Models.inputBlock",
+      "Slackblocks.Models.inputBlockAsBlock",
+      "Slackblocks.Models.inputBlockBlockId",
+      "Slackblocks.Models.inputBlockDispatchAction",
+      "Slackblocks.Models.inputBlockElement",
+      "Slackblocks.Models.inputBlockExtensions",
+      "Slackblocks.Models.inputBlockHint",
+      "Slackblocks.Models.inputBlockLabel",
+      "Slackblocks.Models.inputBlockOptional",
+      "Slackblocks.Models.inputParameter",
+      "Slackblocks.Models.inputParameterExtensions",
+      "Slackblocks.Models.inputParameterName",
+      "Slackblocks.Models.inputParameterValue",
+      "Slackblocks.Models.inspectActionsBlock",
+      "Slackblocks.Models.inspectAlertBlock",
+      "Slackblocks.Models.inspectAreaChart",
+      "Slackblocks.Models.inspectAttachment",
+      "Slackblocks.Models.inspectAxisConfig",
+      "Slackblocks.Models.inspectBarChart",
+      "Slackblocks.Models.inspectButtonElement",
+      "Slackblocks.Models.inspectCardBlock",
+      "Slackblocks.Models.inspectCarouselBlock",
+      "Slackblocks.Models.inspectChannelMultiSelectElement",
+      "Slackblocks.Models.inspectChannelSelectElement",
+      "Slackblocks.Models.inspectChartSegment",
+      "Slackblocks.Models.inspectCheckboxesElement",
+      "Slackblocks.Models.inspectColumnSettings",
+      "Slackblocks.Models.inspectConfirmationDialogue",
+      "Slackblocks.Models.inspectContainerBlock",
+      "Slackblocks.Models.inspectContextActionsBlock",
+      "Slackblocks.Models.inspectContextBlock",
+      "Slackblocks.Models.inspectConversationFilter",
+      "Slackblocks.Models.inspectConversationMultiSelectElement",
+      "Slackblocks.Models.inspectConversationSelectElement",
+      "Slackblocks.Models.inspectDataPoint",
+      "Slackblocks.Models.inspectDataSeries",
+      "Slackblocks.Models.inspectDataTableBlock",
+      "Slackblocks.Models.inspectDataVisualizationBlock",
+      "Slackblocks.Models.inspectDatePickerElement",
+      "Slackblocks.Models.inspectDateTimePickerElement",
+      "Slackblocks.Models.inspectDispatchActionConfiguration",
+      "Slackblocks.Models.inspectDividerBlock",
+      "Slackblocks.Models.inspectEmailInputElement",
+      "Slackblocks.Models.inspectExternalMultiSelectElement",
+      "Slackblocks.Models.inspectExternalSelectElement",
+      "Slackblocks.Models.inspectFeedbackButton",
+      "Slackblocks.Models.inspectFeedbackButtonsElement",
+      "Slackblocks.Models.inspectFileBlock",
+      "Slackblocks.Models.inspectFileInputElement",
+      "Slackblocks.Models.inspectHeaderBlock",
+      "Slackblocks.Models.inspectHomeTabView",
+      "Slackblocks.Models.inspectIconButtonElement",
+      "Slackblocks.Models.inspectImageBlock",
+      "Slackblocks.Models.inspectImageElement",
+      "Slackblocks.Models.inspectInputBlock",
+      "Slackblocks.Models.inspectInputParameter",
+      "Slackblocks.Models.inspectLineChart",
+      "Slackblocks.Models.inspectMarkdownBlock",
+      "Slackblocks.Models.inspectMarkdownText",
+      "Slackblocks.Models.inspectMessagePayload",
+      "Slackblocks.Models.inspectMessageResponse",
+      "Slackblocks.Models.inspectModalView",
+      "Slackblocks.Models.inspectNumberInputElement",
+      "Slackblocks.Models.inspectOverflowElement",
+      "Slackblocks.Models.inspectPieChart",
+      "Slackblocks.Models.inspectPlainText",
+      "Slackblocks.Models.inspectPlainTextInputElement",
+      "Slackblocks.Models.inspectPlanBlock",
+      "Slackblocks.Models.inspectPlanTask",
+      "Slackblocks.Models.inspectRadioButtonsElement",
+      "Slackblocks.Models.inspectRawNumber",
+      "Slackblocks.Models.inspectRawText",
+      "Slackblocks.Models.inspectRichTextBlock",
+      "Slackblocks.Models.inspectRichTextChannel",
+      "Slackblocks.Models.inspectRichTextCodeBlock",
+      "Slackblocks.Models.inspectRichTextEmoji",
+      "Slackblocks.Models.inspectRichTextInputElement",
+      "Slackblocks.Models.inspectRichTextLink",
+      "Slackblocks.Models.inspectRichTextList",
+      "Slackblocks.Models.inspectRichTextQuote",
+      "Slackblocks.Models.inspectRichTextSection",
+      "Slackblocks.Models.inspectRichTextText",
+      "Slackblocks.Models.inspectRichTextUser",
+      "Slackblocks.Models.inspectRichTextUserGroup",
+      "Slackblocks.Models.inspectSectionBlock",
+      "Slackblocks.Models.inspectSelectOption",
+      "Slackblocks.Models.inspectSelectOptionGroup",
+      "Slackblocks.Models.inspectSlackFile",
+      "Slackblocks.Models.inspectSlackIcon",
+      "Slackblocks.Models.inspectStaticMultiSelectElement",
+      "Slackblocks.Models.inspectStaticSelectElement",
+      "Slackblocks.Models.inspectTableBlock",
+      "Slackblocks.Models.inspectTaskCardBlock",
+      "Slackblocks.Models.inspectTimePickerElement",
+      "Slackblocks.Models.inspectTrigger",
+      "Slackblocks.Models.inspectUrlInputElement",
+      "Slackblocks.Models.inspectUrlSource",
+      "Slackblocks.Models.inspectUserMultiSelectElement",
+      "Slackblocks.Models.inspectUserSelectElement",
+      "Slackblocks.Models.inspectVideoBlock",
+      "Slackblocks.Models.inspectWebhookMessage",
+      "Slackblocks.Models.inspectWorkflow",
+      "Slackblocks.Models.inspectWorkflowButtonElement",
+      "Slackblocks.Models.lineChart",
+      "Slackblocks.Models.lineChartAsChart",
+      "Slackblocks.Models.lineChartAxisConfig",
+      "Slackblocks.Models.lineChartExtensions",
+      "Slackblocks.Models.lineChartSeries",
+      "Slackblocks.Models.markdownBlock",
+      "Slackblocks.Models.markdownBlockAsBlock",
+      "Slackblocks.Models.markdownBlockBlockId",
+      "Slackblocks.Models.markdownBlockExtensions",
+      "Slackblocks.Models.markdownBlockText",
+      "Slackblocks.Models.markdownText",
+      "Slackblocks.Models.markdownTextAsContextElement",
+      "Slackblocks.Models.markdownTextAsText",
+      "Slackblocks.Models.markdownTextExtensions",
+      "Slackblocks.Models.markdownTextText",
+      "Slackblocks.Models.markdownTextVerbatim",
+      "Slackblocks.Models.matchBlockActionsBlock",
+      "Slackblocks.Models.matchBlockAlertBlock",
+      "Slackblocks.Models.matchBlockCardBlock",
+      "Slackblocks.Models.matchBlockCarouselBlock",
+      "Slackblocks.Models.matchBlockContainerBlock",
+      "Slackblocks.Models.matchBlockContextActionsBlock",
+      "Slackblocks.Models.matchBlockContextBlock",
+      "Slackblocks.Models.matchBlockDataTableBlock",
+      "Slackblocks.Models.matchBlockDataVisualizationBlock",
+      "Slackblocks.Models.matchBlockDividerBlock",
+      "Slackblocks.Models.matchBlockFileBlock",
+      "Slackblocks.Models.matchBlockHeaderBlock",
+      "Slackblocks.Models.matchBlockImageBlock",
+      "Slackblocks.Models.matchBlockInputBlock",
+      "Slackblocks.Models.matchBlockMarkdownBlock",
+      "Slackblocks.Models.matchBlockPlanBlock",
+      "Slackblocks.Models.matchBlockRichTextBlock",
+      "Slackblocks.Models.matchBlockSectionBlock",
+      "Slackblocks.Models.matchBlockTableBlock",
+      "Slackblocks.Models.matchBlockTaskCardBlock",
+      "Slackblocks.Models.matchBlockVideoBlock",
+      "Slackblocks.Models.matchChartAreaChart",
+      "Slackblocks.Models.matchChartBarChart",
+      "Slackblocks.Models.matchChartLineChart",
+      "Slackblocks.Models.matchChartPieChart",
+      "Slackblocks.Models.matchContextActionsElementFeedbackButtonsElement",
+      "Slackblocks.Models.matchContextActionsElementIconButtonElement",
+      "Slackblocks.Models.matchContextElementImageElement",
+      "Slackblocks.Models.matchContextElementMarkdownText",
+      "Slackblocks.Models.matchContextElementPlainText",
+      "Slackblocks.Models.matchDataTableCellRawNumber",
+      "Slackblocks.Models.matchDataTableCellRawText",
+      "Slackblocks.Models.matchDataTableCellRichTextBlock",
+      "Slackblocks.Models.matchElementButtonElement",
+      "Slackblocks.Models.matchElementChannelMultiSelectElement",
+      "Slackblocks.Models.matchElementChannelSelectElement",
+      "Slackblocks.Models.matchElementCheckboxesElement",
+      "Slackblocks.Models.matchElementConversationMultiSelectElement",
+      "Slackblocks.Models.matchElementConversationSelectElement",
+      "Slackblocks.Models.matchElementDatePickerElement",
+      "Slackblocks.Models.matchElementDateTimePickerElement",
+      "Slackblocks.Models.matchElementEmailInputElement",
+      "Slackblocks.Models.matchElementExternalMultiSelectElement",
+      "Slackblocks.Models.matchElementExternalSelectElement",
+      "Slackblocks.Models.matchElementFeedbackButtonsElement",
+      "Slackblocks.Models.matchElementFileInputElement",
+      "Slackblocks.Models.matchElementIconButtonElement",
+      "Slackblocks.Models.matchElementImageElement",
+      "Slackblocks.Models.matchElementNumberInputElement",
+      "Slackblocks.Models.matchElementOverflowElement",
+      "Slackblocks.Models.matchElementPlainTextInputElement",
+      "Slackblocks.Models.matchElementRadioButtonsElement",
+      "Slackblocks.Models.matchElementRichTextInputElement",
+      "Slackblocks.Models.matchElementStaticMultiSelectElement",
+      "Slackblocks.Models.matchElementStaticSelectElement",
+      "Slackblocks.Models.matchElementTimePickerElement",
+      "Slackblocks.Models.matchElementUrlInputElement",
+      "Slackblocks.Models.matchElementUserMultiSelectElement",
+      "Slackblocks.Models.matchElementUserSelectElement",
+      "Slackblocks.Models.matchElementWorkflowButtonElement",
+      "Slackblocks.Models.matchInputElementChannelMultiSelectElement",
+      "Slackblocks.Models.matchInputElementChannelSelectElement",
+      "Slackblocks.Models.matchInputElementCheckboxesElement",
+      "Slackblocks.Models.matchInputElementConversationMultiSelectElement",
+      "Slackblocks.Models.matchInputElementConversationSelectElement",
+      "Slackblocks.Models.matchInputElementDatePickerElement",
+      "Slackblocks.Models.matchInputElementDateTimePickerElement",
+      "Slackblocks.Models.matchInputElementEmailInputElement",
+      "Slackblocks.Models.matchInputElementExternalMultiSelectElement",
+      "Slackblocks.Models.matchInputElementExternalSelectElement",
+      "Slackblocks.Models.matchInputElementFileInputElement",
+      "Slackblocks.Models.matchInputElementNumberInputElement",
+      "Slackblocks.Models.matchInputElementPlainTextInputElement",
+      "Slackblocks.Models.matchInputElementRadioButtonsElement",
+      "Slackblocks.Models.matchInputElementRichTextInputElement",
+      "Slackblocks.Models.matchInputElementStaticMultiSelectElement",
+      "Slackblocks.Models.matchInputElementStaticSelectElement",
+      "Slackblocks.Models.matchInputElementTimePickerElement",
+      "Slackblocks.Models.matchInputElementUrlInputElement",
+      "Slackblocks.Models.matchInputElementUserMultiSelectElement",
+      "Slackblocks.Models.matchInputElementUserSelectElement",
+      "Slackblocks.Models.matchRichTextBlockElementRichTextCodeBlock",
+      "Slackblocks.Models.matchRichTextBlockElementRichTextList",
+      "Slackblocks.Models.matchRichTextBlockElementRichTextQuote",
+      "Slackblocks.Models.matchRichTextBlockElementRichTextSection",
+      "Slackblocks.Models.matchRichTextSectionElementRichTextChannel",
+      "Slackblocks.Models.matchRichTextSectionElementRichTextEmoji",
+      "Slackblocks.Models.matchRichTextSectionElementRichTextLink",
+      "Slackblocks.Models.matchRichTextSectionElementRichTextText",
+      "Slackblocks.Models.matchRichTextSectionElementRichTextUser",
+      "Slackblocks.Models.matchRichTextSectionElementRichTextUserGroup",
+      "Slackblocks.Models.matchTableCellRawText",
+      "Slackblocks.Models.matchTableCellRichTextBlock",
+      "Slackblocks.Models.matchTextObjectMarkdownText",
+      "Slackblocks.Models.matchTextObjectPlainText",
+      "Slackblocks.Models.messagePayload",
+      "Slackblocks.Models.messagePayloadAttachments",
+      "Slackblocks.Models.messagePayloadBlocks",
+      "Slackblocks.Models.messagePayloadChannel",
+      "Slackblocks.Models.messagePayloadExtensions",
+      "Slackblocks.Models.messagePayloadMetadata",
+      "Slackblocks.Models.messagePayloadMrkdwn",
+      "Slackblocks.Models.messagePayloadText",
+      "Slackblocks.Models.messagePayloadUnfurlLinks",
+      "Slackblocks.Models.messagePayloadUnfurlMedia",
+      "Slackblocks.Models.messageResponse",
+      "Slackblocks.Models.messageResponseAttachments",
+      "Slackblocks.Models.messageResponseBlocks",
+      "Slackblocks.Models.messageResponseExtensions",
+      "Slackblocks.Models.messageResponseMrkdwn",
+      "Slackblocks.Models.messageResponseReplaceOriginal",
+      "Slackblocks.Models.messageResponseResponseType",
+      "Slackblocks.Models.messageResponseText",
+      "Slackblocks.Models.modalView",
+      "Slackblocks.Models.modalViewBlocks",
+      "Slackblocks.Models.modalViewCallbackId",
+      "Slackblocks.Models.modalViewClearOnClose",
+      "Slackblocks.Models.modalViewClose",
+      "Slackblocks.Models.modalViewExtensions",
+      "Slackblocks.Models.modalViewExternalId",
+      "Slackblocks.Models.modalViewNotifyOnClose",
+      "Slackblocks.Models.modalViewPrivateMetadata",
+      "Slackblocks.Models.modalViewSubmit",
+      "Slackblocks.Models.modalViewSubmitDisabled",
+      "Slackblocks.Models.modalViewTitle",
+      "Slackblocks.Models.numberInputElement",
+      "Slackblocks.Models.numberInputElementActionId",
+      "Slackblocks.Models.numberInputElementAsElement",
+      "Slackblocks.Models.numberInputElementAsInputElement",
+      "Slackblocks.Models.numberInputElementDispatchActionConfig",
+      "Slackblocks.Models.numberInputElementExtensions",
+      "Slackblocks.Models.numberInputElementFocusOnLoad",
+      "Slackblocks.Models.numberInputElementInitialValue",
+      "Slackblocks.Models.numberInputElementIsDecimalAllowed",
+      "Slackblocks.Models.numberInputElementMaxValue",
+      "Slackblocks.Models.numberInputElementMinValue",
+      "Slackblocks.Models.numberInputElementPlaceholder",
+      "Slackblocks.Models.overflowElement",
+      "Slackblocks.Models.overflowElementActionId",
+      "Slackblocks.Models.overflowElementAsElement",
+      "Slackblocks.Models.overflowElementConfirm",
+      "Slackblocks.Models.overflowElementExtensions",
+      "Slackblocks.Models.overflowElementOptions",
+      "Slackblocks.Models.packageVersion",
+      "Slackblocks.Models.pieChart",
+      "Slackblocks.Models.pieChartAsChart",
+      "Slackblocks.Models.pieChartExtensions",
+      "Slackblocks.Models.pieChartSegments",
+      "Slackblocks.Models.plainText",
+      "Slackblocks.Models.plainTextAsContextElement",
+      "Slackblocks.Models.plainTextAsText",
+      "Slackblocks.Models.plainTextEmoji",
+      "Slackblocks.Models.plainTextExtensions",
+      "Slackblocks.Models.plainTextInputElement",
+      "Slackblocks.Models.plainTextInputElementActionId",
+      "Slackblocks.Models.plainTextInputElementAsElement",
+      "Slackblocks.Models.plainTextInputElementAsInputElement",
+      "Slackblocks.Models.plainTextInputElementDispatchActionConfig",
+      "Slackblocks.Models.plainTextInputElementExtensions",
+      "Slackblocks.Models.plainTextInputElementFocusOnLoad",
+      "Slackblocks.Models.plainTextInputElementInitialValue",
+      "Slackblocks.Models.plainTextInputElementMaxLength",
+      "Slackblocks.Models.plainTextInputElementMinLength",
+      "Slackblocks.Models.plainTextInputElementMultiline",
+      "Slackblocks.Models.plainTextInputElementPlaceholder",
+      "Slackblocks.Models.plainTextText",
+      "Slackblocks.Models.planBlock",
+      "Slackblocks.Models.planBlockAsBlock",
+      "Slackblocks.Models.planBlockBlockId",
+      "Slackblocks.Models.planBlockExtensions",
+      "Slackblocks.Models.planBlockTasks",
+      "Slackblocks.Models.planBlockTitle",
+      "Slackblocks.Models.radioButtonsElement",
+      "Slackblocks.Models.radioButtonsElementActionId",
+      "Slackblocks.Models.radioButtonsElementAsElement",
+      "Slackblocks.Models.radioButtonsElementAsInputElement",
+      "Slackblocks.Models.radioButtonsElementConfirm",
+      "Slackblocks.Models.radioButtonsElementExtensions",
+      "Slackblocks.Models.radioButtonsElementFocusOnLoad",
+      "Slackblocks.Models.radioButtonsElementInitialOption",
+      "Slackblocks.Models.radioButtonsElementOptions",
+      "Slackblocks.Models.rawNumber",
+      "Slackblocks.Models.rawNumberAsDataTableCell",
+      "Slackblocks.Models.rawNumberExtensions",
+      "Slackblocks.Models.rawNumberText",
+      "Slackblocks.Models.rawNumberValue",
+      "Slackblocks.Models.rawText",
+      "Slackblocks.Models.rawTextAsDataTableCell",
+      "Slackblocks.Models.rawTextAsTableCell",
+      "Slackblocks.Models.rawTextExtensions",
+      "Slackblocks.Models.rawTextText",
+      "Slackblocks.Models.richTextBlock",
+      "Slackblocks.Models.richTextBlockAsBlock",
+      "Slackblocks.Models.richTextBlockAsDataTableCell",
+      "Slackblocks.Models.richTextBlockAsTableCell",
+      "Slackblocks.Models.richTextBlockBlockId",
+      "Slackblocks.Models.richTextBlockElements",
+      "Slackblocks.Models.richTextBlockExtensions",
+      "Slackblocks.Models.richTextChannel",
+      "Slackblocks.Models.richTextChannelAsRichTextSectionElement",
+      "Slackblocks.Models.richTextChannelChannelId",
+      "Slackblocks.Models.richTextChannelExtensions",
+      "Slackblocks.Models.richTextChannelStyle",
+      "Slackblocks.Models.richTextCodeBlock",
+      "Slackblocks.Models.richTextCodeBlockAsRichTextBlockElement",
+      "Slackblocks.Models.richTextCodeBlockBorder",
+      "Slackblocks.Models.richTextCodeBlockElements",
+      "Slackblocks.Models.richTextCodeBlockExtensions",
+      "Slackblocks.Models.richTextEmoji",
+      "Slackblocks.Models.richTextEmojiAsRichTextSectionElement",
+      "Slackblocks.Models.richTextEmojiExtensions",
+      "Slackblocks.Models.richTextEmojiName",
+      "Slackblocks.Models.richTextEmojiSkinTone",
+      "Slackblocks.Models.richTextInputElement",
+      "Slackblocks.Models.richTextInputElementActionId",
+      "Slackblocks.Models.richTextInputElementAsElement",
+      "Slackblocks.Models.richTextInputElementAsInputElement",
+      "Slackblocks.Models.richTextInputElementDispatchActionConfig",
+      "Slackblocks.Models.richTextInputElementExtensions",
+      "Slackblocks.Models.richTextInputElementFocusOnLoad",
+      "Slackblocks.Models.richTextInputElementInitialValue",
+      "Slackblocks.Models.richTextInputElementMaxLines",
+      "Slackblocks.Models.richTextInputElementMinLines",
+      "Slackblocks.Models.richTextInputElementPlaceholder",
+      "Slackblocks.Models.richTextLink",
+      "Slackblocks.Models.richTextLinkAsRichTextSectionElement",
+      "Slackblocks.Models.richTextLinkExtensions",
+      "Slackblocks.Models.richTextLinkStyle",
+      "Slackblocks.Models.richTextLinkText",
+      "Slackblocks.Models.richTextLinkUnsafe",
+      "Slackblocks.Models.richTextLinkUrl",
+      "Slackblocks.Models.richTextList",
+      "Slackblocks.Models.richTextListAsRichTextBlockElement",
+      "Slackblocks.Models.richTextListBorder",
+      "Slackblocks.Models.richTextListElements",
+      "Slackblocks.Models.richTextListExtensions",
+      "Slackblocks.Models.richTextListIndent",
+      "Slackblocks.Models.richTextListOffset",
+      "Slackblocks.Models.richTextListStyle",
+      "Slackblocks.Models.richTextQuote",
+      "Slackblocks.Models.richTextQuoteAsRichTextBlockElement",
+      "Slackblocks.Models.richTextQuoteBorder",
+      "Slackblocks.Models.richTextQuoteElements",
+      "Slackblocks.Models.richTextQuoteExtensions",
+      "Slackblocks.Models.richTextSection",
+      "Slackblocks.Models.richTextSectionAsRichTextBlockElement",
+      "Slackblocks.Models.richTextSectionElements",
+      "Slackblocks.Models.richTextSectionExtensions",
+      "Slackblocks.Models.richTextText",
+      "Slackblocks.Models.richTextTextAsRichTextSectionElement",
+      "Slackblocks.Models.richTextTextExtensions",
+      "Slackblocks.Models.richTextTextStyle",
+      "Slackblocks.Models.richTextTextText",
+      "Slackblocks.Models.richTextUser",
+      "Slackblocks.Models.richTextUserAsRichTextSectionElement",
+      "Slackblocks.Models.richTextUserExtensions",
+      "Slackblocks.Models.richTextUserGroup",
+      "Slackblocks.Models.richTextUserGroupAsRichTextSectionElement",
+      "Slackblocks.Models.richTextUserGroupExtensions",
+      "Slackblocks.Models.richTextUserGroupStyle",
+      "Slackblocks.Models.richTextUserGroupUserGroupId",
+      "Slackblocks.Models.richTextUserStyle",
+      "Slackblocks.Models.richTextUserUserId",
+      "Slackblocks.Models.sectionBlock",
+      "Slackblocks.Models.sectionBlockAccessory",
+      "Slackblocks.Models.sectionBlockAsBlock",
+      "Slackblocks.Models.sectionBlockBlockId",
+      "Slackblocks.Models.sectionBlockExtensions",
+      "Slackblocks.Models.sectionBlockFields",
+      "Slackblocks.Models.sectionBlockText",
+      "Slackblocks.Models.selectOption",
+      "Slackblocks.Models.selectOptionDescription",
+      "Slackblocks.Models.selectOptionExtensions",
+      "Slackblocks.Models.selectOptionGroup",
+      "Slackblocks.Models.selectOptionGroupExtensions",
+      "Slackblocks.Models.selectOptionGroupLabel",
+      "Slackblocks.Models.selectOptionGroupOptions",
+      "Slackblocks.Models.selectOptionText",
+      "Slackblocks.Models.selectOptionUrl",
+      "Slackblocks.Models.selectOptionValue",
+      "Slackblocks.Models.slackFile",
+      "Slackblocks.Models.slackFileExtensions",
+      "Slackblocks.Models.slackFileId",
+      "Slackblocks.Models.slackFileUrl",
+      "Slackblocks.Models.slackIcon",
+      "Slackblocks.Models.slackIconExtensions",
+      "Slackblocks.Models.slackIconName",
+      "Slackblocks.Models.specVersion",
+      "Slackblocks.Models.staticMultiSelectElement",
+      "Slackblocks.Models.staticMultiSelectElementActionId",
+      "Slackblocks.Models.staticMultiSelectElementAsElement",
+      "Slackblocks.Models.staticMultiSelectElementAsInputElement",
+      "Slackblocks.Models.staticMultiSelectElementConfirm",
+      "Slackblocks.Models.staticMultiSelectElementExtensions",
+      "Slackblocks.Models.staticMultiSelectElementFocusOnLoad",
+      "Slackblocks.Models.staticMultiSelectElementInitialOptions",
+      "Slackblocks.Models.staticMultiSelectElementMaxSelectedItems",
+      "Slackblocks.Models.staticMultiSelectElementOptionGroups",
+      "Slackblocks.Models.staticMultiSelectElementOptions",
+      "Slackblocks.Models.staticMultiSelectElementPlaceholder",
+      "Slackblocks.Models.staticSelectElement",
+      "Slackblocks.Models.staticSelectElementActionId",
+      "Slackblocks.Models.staticSelectElementAsElement",
+      "Slackblocks.Models.staticSelectElementAsInputElement",
+      "Slackblocks.Models.staticSelectElementConfirm",
+      "Slackblocks.Models.staticSelectElementExtensions",
+      "Slackblocks.Models.staticSelectElementFocusOnLoad",
+      "Slackblocks.Models.staticSelectElementInitialOption",
+      "Slackblocks.Models.staticSelectElementOptionGroups",
+      "Slackblocks.Models.staticSelectElementOptions",
+      "Slackblocks.Models.staticSelectElementPlaceholder",
+      "Slackblocks.Models.tableBlock",
+      "Slackblocks.Models.tableBlockAsBlock",
+      "Slackblocks.Models.tableBlockBlockId",
+      "Slackblocks.Models.tableBlockColumnSettings",
+      "Slackblocks.Models.tableBlockExtensions",
+      "Slackblocks.Models.tableBlockRows",
+      "Slackblocks.Models.taskCardBlock",
+      "Slackblocks.Models.taskCardBlockAsBlock",
+      "Slackblocks.Models.taskCardBlockBlockId",
+      "Slackblocks.Models.taskCardBlockDetails",
+      "Slackblocks.Models.taskCardBlockExtensions",
+      "Slackblocks.Models.taskCardBlockOutput",
+      "Slackblocks.Models.taskCardBlockSources",
+      "Slackblocks.Models.taskCardBlockStatus",
+      "Slackblocks.Models.taskCardBlockTaskId",
+      "Slackblocks.Models.taskCardBlockTitle",
+      "Slackblocks.Models.timePickerElement",
+      "Slackblocks.Models.timePickerElementActionId",
+      "Slackblocks.Models.timePickerElementAsElement",
+      "Slackblocks.Models.timePickerElementAsInputElement",
+      "Slackblocks.Models.timePickerElementConfirm",
+      "Slackblocks.Models.timePickerElementExtensions",
+      "Slackblocks.Models.timePickerElementFocusOnLoad",
+      "Slackblocks.Models.timePickerElementInitialTime",
+      "Slackblocks.Models.timePickerElementPlaceholder",
+      "Slackblocks.Models.timePickerElementTimezone",
+      "Slackblocks.Models.trigger",
+      "Slackblocks.Models.triggerCustomizableInputParameters",
+      "Slackblocks.Models.triggerExtensions",
+      "Slackblocks.Models.triggerUrl",
+      "Slackblocks.Models.urlInputElement",
+      "Slackblocks.Models.urlInputElementActionId",
+      "Slackblocks.Models.urlInputElementAsElement",
+      "Slackblocks.Models.urlInputElementAsInputElement",
+      "Slackblocks.Models.urlInputElementDispatchActionConfig",
+      "Slackblocks.Models.urlInputElementExtensions",
+      "Slackblocks.Models.urlInputElementFocusOnLoad",
+      "Slackblocks.Models.urlInputElementInitialValue",
+      "Slackblocks.Models.urlInputElementPlaceholder",
+      "Slackblocks.Models.urlSource",
+      "Slackblocks.Models.urlSourceExtensions",
+      "Slackblocks.Models.urlSourceText",
+      "Slackblocks.Models.urlSourceUrl",
+      "Slackblocks.Models.userMultiSelectElement",
+      "Slackblocks.Models.userMultiSelectElementActionId",
+      "Slackblocks.Models.userMultiSelectElementAsElement",
+      "Slackblocks.Models.userMultiSelectElementAsInputElement",
+      "Slackblocks.Models.userMultiSelectElementConfirm",
+      "Slackblocks.Models.userMultiSelectElementExtensions",
+      "Slackblocks.Models.userMultiSelectElementFocusOnLoad",
+      "Slackblocks.Models.userMultiSelectElementInitialUsers",
+      "Slackblocks.Models.userMultiSelectElementMaxSelectedItems",
+      "Slackblocks.Models.userMultiSelectElementPlaceholder",
+      "Slackblocks.Models.userSelectElement",
+      "Slackblocks.Models.userSelectElementActionId",
+      "Slackblocks.Models.userSelectElementAsElement",
+      "Slackblocks.Models.userSelectElementAsInputElement",
+      "Slackblocks.Models.userSelectElementConfirm",
+      "Slackblocks.Models.userSelectElementExtensions",
+      "Slackblocks.Models.userSelectElementFocusOnLoad",
+      "Slackblocks.Models.userSelectElementInitialUser",
+      "Slackblocks.Models.userSelectElementPlaceholder",
+      "Slackblocks.Models.videoBlock",
+      "Slackblocks.Models.videoBlockAltText",
+      "Slackblocks.Models.videoBlockAsBlock",
+      "Slackblocks.Models.videoBlockAuthorName",
+      "Slackblocks.Models.videoBlockBlockId",
+      "Slackblocks.Models.videoBlockDescription",
+      "Slackblocks.Models.videoBlockExtensions",
+      "Slackblocks.Models.videoBlockProviderIconUrl",
+      "Slackblocks.Models.videoBlockProviderName",
+      "Slackblocks.Models.videoBlockThumbnailUrl",
+      "Slackblocks.Models.videoBlockTitle",
+      "Slackblocks.Models.videoBlockTitleUrl",
+      "Slackblocks.Models.videoBlockVideoUrl",
+      "Slackblocks.Models.webhookMessage",
+      "Slackblocks.Models.webhookMessageAttachments",
+      "Slackblocks.Models.webhookMessageBlocks",
+      "Slackblocks.Models.webhookMessageDeleteOriginal",
+      "Slackblocks.Models.webhookMessageExtensions",
+      "Slackblocks.Models.webhookMessageMetadata",
+      "Slackblocks.Models.webhookMessageReplaceOriginal",
+      "Slackblocks.Models.webhookMessageResponseType",
+      "Slackblocks.Models.webhookMessageText",
+      "Slackblocks.Models.webhookMessageUnfurlLinks",
+      "Slackblocks.Models.webhookMessageUnfurlMedia",
+      "Slackblocks.Models.workflow",
+      "Slackblocks.Models.workflowButtonElement",
+      "Slackblocks.Models.workflowButtonElementAccessibilityLabel",
+      "Slackblocks.Models.workflowButtonElementActionId",
+      "Slackblocks.Models.workflowButtonElementAsElement",
+      "Slackblocks.Models.workflowButtonElementConfirm",
+      "Slackblocks.Models.workflowButtonElementExtensions",
+      "Slackblocks.Models.workflowButtonElementStyle",
+      "Slackblocks.Models.workflowButtonElementText",
+      "Slackblocks.Models.workflowButtonElementWorkflow",
+      "Slackblocks.Models.workflowExtensions",
+      "Slackblocks.Models.workflowTrigger",
+      "Slackblocks.Style.RichTextStyle",
+      "Slackblocks.Style.richTextStyle",
+      "Slackblocks.Style.styleBold",
+      "Slackblocks.Style.styleClientHighlight",
+      "Slackblocks.Style.styleCode",
+      "Slackblocks.Style.styleHighlight",
+      "Slackblocks.Style.styleItalic",
+      "Slackblocks.Style.styleStrike",
+      "Slackblocks.Style.styleUnlink",
+      "Slackblocks.Validation.ErrorCategory",
+      "Slackblocks.Validation.Field",
+      "Slackblocks.Validation.Index",
+      "Slackblocks.Validation.InvalidUsage",
+      "Slackblocks.Validation.LengthExceeded",
+      "Slackblocks.Validation.MissingRequired",
+      "Slackblocks.Validation.MutuallyExclusive",
+      "Slackblocks.Validation.OutOfRange",
+      "Slackblocks.Validation.PathPiece",
+      "Slackblocks.Validation.Result",
+      "Slackblocks.Validation.TypeMismatch",
+      "Slackblocks.Validation.ValidationError",
+      "Slackblocks.Validation.atField",
+      "Slackblocks.Validation.atIndex",
+      "Slackblocks.Validation.categoryName",
+      "Slackblocks.Validation.errorCategory",
+      "Slackblocks.Validation.errorConstraint",
+      "Slackblocks.Validation.errorMessage",
+      "Slackblocks.Validation.errorPath",
+      "Slackblocks.Validation.failure",
+      "Slackblocks.Validation.renderPath"
+    ],
+    "Slackblocks.Validation": [
+      "Slackblocks.Validation.ErrorCategory",
+      "Slackblocks.Validation.Field",
+      "Slackblocks.Validation.Index",
+      "Slackblocks.Validation.InvalidUsage",
+      "Slackblocks.Validation.LengthExceeded",
+      "Slackblocks.Validation.MissingRequired",
+      "Slackblocks.Validation.MutuallyExclusive",
+      "Slackblocks.Validation.OutOfRange",
+      "Slackblocks.Validation.PathPiece",
+      "Slackblocks.Validation.Result",
+      "Slackblocks.Validation.TypeMismatch",
+      "Slackblocks.Validation.ValidationError",
+      "Slackblocks.Validation.atField",
+      "Slackblocks.Validation.atIndex",
+      "Slackblocks.Validation.categoryName",
+      "Slackblocks.Validation.errorCategory",
+      "Slackblocks.Validation.errorConstraint",
+      "Slackblocks.Validation.errorMessage",
+      "Slackblocks.Validation.errorPath",
+      "Slackblocks.Validation.failure",
+      "Slackblocks.Validation.renderPath"
+    ],
+    "Slackblocks.Models": [
+      "Slackblocks.Models.ActionsBlock",
+      "Slackblocks.Models.ActionsBlockFields",
+      "Slackblocks.Models.AlertBlock",
+      "Slackblocks.Models.AlertBlockFields",
+      "Slackblocks.Models.AlertLevel",
+      "Slackblocks.Models.AlertLevelDefault",
+      "Slackblocks.Models.AlertLevelError",
+      "Slackblocks.Models.AlertLevelInfo",
+      "Slackblocks.Models.AlertLevelSuccess",
+      "Slackblocks.Models.AlertLevelWarning",
+      "Slackblocks.Models.AreaChart",
+      "Slackblocks.Models.AreaChartFields",
+      "Slackblocks.Models.Attachment",
+      "Slackblocks.Models.AttachmentFields",
+      "Slackblocks.Models.AxisConfig",
+      "Slackblocks.Models.AxisConfigFields",
+      "Slackblocks.Models.BarChart",
+      "Slackblocks.Models.BarChartFields",
+      "Slackblocks.Models.Block",
+      "Slackblocks.Models.ButtonElement",
+      "Slackblocks.Models.ButtonElementFields",
+      "Slackblocks.Models.ButtonStyle",
+      "Slackblocks.Models.ButtonStyleDanger",
+      "Slackblocks.Models.ButtonStylePrimary",
+      "Slackblocks.Models.CardBlock",
+      "Slackblocks.Models.CardBlockFields",
+      "Slackblocks.Models.CarouselBlock",
+      "Slackblocks.Models.CarouselBlockFields",
+      "Slackblocks.Models.ChannelMultiSelectElement",
+      "Slackblocks.Models.ChannelMultiSelectElementFields",
+      "Slackblocks.Models.ChannelSelectElement",
+      "Slackblocks.Models.ChannelSelectElementFields",
+      "Slackblocks.Models.Chart",
+      "Slackblocks.Models.ChartSegment",
+      "Slackblocks.Models.ChartSegmentFields",
+      "Slackblocks.Models.CheckboxesElement",
+      "Slackblocks.Models.CheckboxesElementFields",
+      "Slackblocks.Models.ColumnAlign",
+      "Slackblocks.Models.ColumnAlignCenter",
+      "Slackblocks.Models.ColumnAlignLeft",
+      "Slackblocks.Models.ColumnAlignRight",
+      "Slackblocks.Models.ColumnSettings",
+      "Slackblocks.Models.ColumnSettingsFields",
+      "Slackblocks.Models.ConfirmationDialogue",
+      "Slackblocks.Models.ConfirmationDialogueFields",
+      "Slackblocks.Models.ContainerBlock",
+      "Slackblocks.Models.ContainerBlockFields",
+      "Slackblocks.Models.ContainerWidth",
+      "Slackblocks.Models.ContainerWidthFull",
+      "Slackblocks.Models.ContainerWidthNarrow",
+      "Slackblocks.Models.ContainerWidthStandard",
+      "Slackblocks.Models.ContainerWidthWide",
+      "Slackblocks.Models.ContextActionsBlock",
+      "Slackblocks.Models.ContextActionsBlockFields",
+      "Slackblocks.Models.ContextActionsElement",
+      "Slackblocks.Models.ContextBlock",
+      "Slackblocks.Models.ContextBlockFields",
+      "Slackblocks.Models.ContextElement",
+      "Slackblocks.Models.ConversationFilter",
+      "Slackblocks.Models.ConversationFilterFields",
+      "Slackblocks.Models.ConversationMultiSelectElement",
+      "Slackblocks.Models.ConversationMultiSelectElementFields",
+      "Slackblocks.Models.ConversationSelectElement",
+      "Slackblocks.Models.ConversationSelectElementFields",
+      "Slackblocks.Models.DataPoint",
+      "Slackblocks.Models.DataPointFields",
+      "Slackblocks.Models.DataSeries",
+      "Slackblocks.Models.DataSeriesFields",
+      "Slackblocks.Models.DataTableBlock",
+      "Slackblocks.Models.DataTableBlockFields",
+      "Slackblocks.Models.DataTableCell",
+      "Slackblocks.Models.DataVisualizationBlock",
+      "Slackblocks.Models.DataVisualizationBlockFields",
+      "Slackblocks.Models.DatePickerElement",
+      "Slackblocks.Models.DatePickerElementFields",
+      "Slackblocks.Models.DateTimePickerElement",
+      "Slackblocks.Models.DateTimePickerElementFields",
+      "Slackblocks.Models.DispatchActionConfiguration",
+      "Slackblocks.Models.DispatchActionConfigurationFields",
+      "Slackblocks.Models.DividerBlock",
+      "Slackblocks.Models.DividerBlockFields",
+      "Slackblocks.Models.Element",
+      "Slackblocks.Models.EmailInputElement",
+      "Slackblocks.Models.EmailInputElementFields",
+      "Slackblocks.Models.ExternalMultiSelectElement",
+      "Slackblocks.Models.ExternalMultiSelectElementFields",
+      "Slackblocks.Models.ExternalSelectElement",
+      "Slackblocks.Models.ExternalSelectElementFields",
+      "Slackblocks.Models.FeedbackButton",
+      "Slackblocks.Models.FeedbackButtonFields",
+      "Slackblocks.Models.FeedbackButtonsElement",
+      "Slackblocks.Models.FeedbackButtonsElementFields",
+      "Slackblocks.Models.FileBlock",
+      "Slackblocks.Models.FileBlockFields",
+      "Slackblocks.Models.FileInputElement",
+      "Slackblocks.Models.FileInputElementFields",
+      "Slackblocks.Models.HeaderBlock",
+      "Slackblocks.Models.HeaderBlockFields",
+      "Slackblocks.Models.HomeTabView",
+      "Slackblocks.Models.HomeTabViewFields",
+      "Slackblocks.Models.IconButtonElement",
+      "Slackblocks.Models.IconButtonElementFields",
+      "Slackblocks.Models.IconButtonIcon",
+      "Slackblocks.Models.IconButtonIconTrash",
+      "Slackblocks.Models.ImageBlock",
+      "Slackblocks.Models.ImageBlockFields",
+      "Slackblocks.Models.ImageElement",
+      "Slackblocks.Models.ImageElementFields",
+      "Slackblocks.Models.InputBlock",
+      "Slackblocks.Models.InputBlockFields",
+      "Slackblocks.Models.InputElement",
+      "Slackblocks.Models.InputParameter",
+      "Slackblocks.Models.InputParameterFields",
+      "Slackblocks.Models.LineChart",
+      "Slackblocks.Models.LineChartFields",
+      "Slackblocks.Models.MarkdownBlock",
+      "Slackblocks.Models.MarkdownBlockFields",
+      "Slackblocks.Models.MarkdownText",
+      "Slackblocks.Models.MarkdownTextFields",
+      "Slackblocks.Models.MessagePayload",
+      "Slackblocks.Models.MessagePayloadFields",
+      "Slackblocks.Models.MessageResponse",
+      "Slackblocks.Models.MessageResponseFields",
+      "Slackblocks.Models.ModalView",
+      "Slackblocks.Models.ModalViewFields",
+      "Slackblocks.Models.NumberInputElement",
+      "Slackblocks.Models.NumberInputElementFields",
+      "Slackblocks.Models.OverflowElement",
+      "Slackblocks.Models.OverflowElementFields",
+      "Slackblocks.Models.PieChart",
+      "Slackblocks.Models.PieChartFields",
+      "Slackblocks.Models.PlainText",
+      "Slackblocks.Models.PlainTextFields",
+      "Slackblocks.Models.PlainTextInputElement",
+      "Slackblocks.Models.PlainTextInputElementFields",
+      "Slackblocks.Models.PlanBlock",
+      "Slackblocks.Models.PlanBlockFields",
+      "Slackblocks.Models.PlanTask",
+      "Slackblocks.Models.RadioButtonsElement",
+      "Slackblocks.Models.RadioButtonsElementFields",
+      "Slackblocks.Models.RawNumber",
+      "Slackblocks.Models.RawNumberFields",
+      "Slackblocks.Models.RawText",
+      "Slackblocks.Models.RawTextFields",
+      "Slackblocks.Models.ResponseType",
+      "Slackblocks.Models.ResponseTypeEphemeral",
+      "Slackblocks.Models.ResponseTypeInChannel",
+      "Slackblocks.Models.RichTextBlock",
+      "Slackblocks.Models.RichTextBlockElement",
+      "Slackblocks.Models.RichTextBlockFields",
+      "Slackblocks.Models.RichTextChannel",
+      "Slackblocks.Models.RichTextChannelFields",
+      "Slackblocks.Models.RichTextCodeBlock",
+      "Slackblocks.Models.RichTextCodeBlockFields",
+      "Slackblocks.Models.RichTextEmoji",
+      "Slackblocks.Models.RichTextEmojiFields",
+      "Slackblocks.Models.RichTextInputElement",
+      "Slackblocks.Models.RichTextInputElementFields",
+      "Slackblocks.Models.RichTextLink",
+      "Slackblocks.Models.RichTextLinkFields",
+      "Slackblocks.Models.RichTextList",
+      "Slackblocks.Models.RichTextListFields",
+      "Slackblocks.Models.RichTextListStyle",
+      "Slackblocks.Models.RichTextListStyleBullet",
+      "Slackblocks.Models.RichTextListStyleOrdered",
+      "Slackblocks.Models.RichTextQuote",
+      "Slackblocks.Models.RichTextQuoteFields",
+      "Slackblocks.Models.RichTextSection",
+      "Slackblocks.Models.RichTextSectionElement",
+      "Slackblocks.Models.RichTextSectionFields",
+      "Slackblocks.Models.RichTextText",
+      "Slackblocks.Models.RichTextTextFields",
+      "Slackblocks.Models.RichTextUser",
+      "Slackblocks.Models.RichTextUserFields",
+      "Slackblocks.Models.RichTextUserGroup",
+      "Slackblocks.Models.RichTextUserGroupFields",
+      "Slackblocks.Models.SectionBlock",
+      "Slackblocks.Models.SectionBlockFields",
+      "Slackblocks.Models.SelectOption",
+      "Slackblocks.Models.SelectOptionFields",
+      "Slackblocks.Models.SelectOptionGroup",
+      "Slackblocks.Models.SelectOptionGroupFields",
+      "Slackblocks.Models.SlackFile",
+      "Slackblocks.Models.SlackFileFields",
+      "Slackblocks.Models.SlackIcon",
+      "Slackblocks.Models.SlackIconFields",
+      "Slackblocks.Models.StaticMultiSelectElement",
+      "Slackblocks.Models.StaticMultiSelectElementFields",
+      "Slackblocks.Models.StaticSelectElement",
+      "Slackblocks.Models.StaticSelectElementFields",
+      "Slackblocks.Models.TableBlock",
+      "Slackblocks.Models.TableBlockFields",
+      "Slackblocks.Models.TableCell",
+      "Slackblocks.Models.TaskCardBlock",
+      "Slackblocks.Models.TaskCardBlockFields",
+      "Slackblocks.Models.TaskStatus",
+      "Slackblocks.Models.TaskStatusComplete",
+      "Slackblocks.Models.TaskStatusError",
+      "Slackblocks.Models.TaskStatusInProgress",
+      "Slackblocks.Models.TaskStatusPending",
+      "Slackblocks.Models.TextObject",
+      "Slackblocks.Models.TimePickerElement",
+      "Slackblocks.Models.TimePickerElementFields",
+      "Slackblocks.Models.Trigger",
+      "Slackblocks.Models.TriggerFields",
+      "Slackblocks.Models.UrlInputElement",
+      "Slackblocks.Models.UrlInputElementFields",
+      "Slackblocks.Models.UrlSource",
+      "Slackblocks.Models.UrlSourceFields",
+      "Slackblocks.Models.UserMultiSelectElement",
+      "Slackblocks.Models.UserMultiSelectElementFields",
+      "Slackblocks.Models.UserSelectElement",
+      "Slackblocks.Models.UserSelectElementFields",
+      "Slackblocks.Models.VideoBlock",
+      "Slackblocks.Models.VideoBlockFields",
+      "Slackblocks.Models.WebhookMessage",
+      "Slackblocks.Models.WebhookMessageFields",
+      "Slackblocks.Models.Workflow",
+      "Slackblocks.Models.WorkflowButtonElement",
+      "Slackblocks.Models.WorkflowButtonElementFields",
+      "Slackblocks.Models.WorkflowFields",
+      "Slackblocks.Models.actionsBlock",
+      "Slackblocks.Models.actionsBlockAsBlock",
+      "Slackblocks.Models.actionsBlockBlockId",
+      "Slackblocks.Models.actionsBlockElements",
+      "Slackblocks.Models.actionsBlockExtensions",
+      "Slackblocks.Models.alertBlock",
+      "Slackblocks.Models.alertBlockAsBlock",
+      "Slackblocks.Models.alertBlockBlockId",
+      "Slackblocks.Models.alertBlockExtensions",
+      "Slackblocks.Models.alertBlockLevel",
+      "Slackblocks.Models.alertBlockText",
+      "Slackblocks.Models.areaChart",
+      "Slackblocks.Models.areaChartAsChart",
+      "Slackblocks.Models.areaChartAxisConfig",
+      "Slackblocks.Models.areaChartExtensions",
+      "Slackblocks.Models.areaChartSeries",
+      "Slackblocks.Models.attachment",
+      "Slackblocks.Models.attachmentBlocks",
+      "Slackblocks.Models.attachmentColor",
+      "Slackblocks.Models.attachmentExtensions",
+      "Slackblocks.Models.attachmentFallback",
+      "Slackblocks.Models.axisConfig",
+      "Slackblocks.Models.axisConfigCategories",
+      "Slackblocks.Models.axisConfigExtensions",
+      "Slackblocks.Models.axisConfigXLabel",
+      "Slackblocks.Models.axisConfigYLabel",
+      "Slackblocks.Models.barChart",
+      "Slackblocks.Models.barChartAsChart",
+      "Slackblocks.Models.barChartAxisConfig",
+      "Slackblocks.Models.barChartExtensions",
+      "Slackblocks.Models.barChartSeries",
+      "Slackblocks.Models.buildActionsBlock",
+      "Slackblocks.Models.buildAlertBlock",
+      "Slackblocks.Models.buildAreaChart",
+      "Slackblocks.Models.buildAttachment",
+      "Slackblocks.Models.buildAxisConfig",
+      "Slackblocks.Models.buildBarChart",
+      "Slackblocks.Models.buildButtonElement",
+      "Slackblocks.Models.buildCardBlock",
+      "Slackblocks.Models.buildCarouselBlock",
+      "Slackblocks.Models.buildChannelMultiSelectElement",
+      "Slackblocks.Models.buildChannelSelectElement",
+      "Slackblocks.Models.buildChartSegment",
+      "Slackblocks.Models.buildCheckboxesElement",
+      "Slackblocks.Models.buildColumnSettings",
+      "Slackblocks.Models.buildConfirmationDialogue",
+      "Slackblocks.Models.buildContainerBlock",
+      "Slackblocks.Models.buildContextActionsBlock",
+      "Slackblocks.Models.buildContextBlock",
+      "Slackblocks.Models.buildConversationFilter",
+      "Slackblocks.Models.buildConversationMultiSelectElement",
+      "Slackblocks.Models.buildConversationSelectElement",
+      "Slackblocks.Models.buildDataPoint",
+      "Slackblocks.Models.buildDataSeries",
+      "Slackblocks.Models.buildDataTableBlock",
+      "Slackblocks.Models.buildDataVisualizationBlock",
+      "Slackblocks.Models.buildDatePickerElement",
+      "Slackblocks.Models.buildDateTimePickerElement",
+      "Slackblocks.Models.buildDispatchActionConfiguration",
+      "Slackblocks.Models.buildDividerBlock",
+      "Slackblocks.Models.buildEmailInputElement",
+      "Slackblocks.Models.buildExternalMultiSelectElement",
+      "Slackblocks.Models.buildExternalSelectElement",
+      "Slackblocks.Models.buildFeedbackButton",
+      "Slackblocks.Models.buildFeedbackButtonsElement",
+      "Slackblocks.Models.buildFileBlock",
+      "Slackblocks.Models.buildFileInputElement",
+      "Slackblocks.Models.buildHeaderBlock",
+      "Slackblocks.Models.buildHomeTabView",
+      "Slackblocks.Models.buildIconButtonElement",
+      "Slackblocks.Models.buildImageBlock",
+      "Slackblocks.Models.buildImageElement",
+      "Slackblocks.Models.buildInputBlock",
+      "Slackblocks.Models.buildInputParameter",
+      "Slackblocks.Models.buildLineChart",
+      "Slackblocks.Models.buildMarkdownBlock",
+      "Slackblocks.Models.buildMarkdownText",
+      "Slackblocks.Models.buildMessagePayload",
+      "Slackblocks.Models.buildMessageResponse",
+      "Slackblocks.Models.buildModalView",
+      "Slackblocks.Models.buildNumberInputElement",
+      "Slackblocks.Models.buildOverflowElement",
+      "Slackblocks.Models.buildPieChart",
+      "Slackblocks.Models.buildPlainText",
+      "Slackblocks.Models.buildPlainTextInputElement",
+      "Slackblocks.Models.buildPlanBlock",
+      "Slackblocks.Models.buildPlanTask",
+      "Slackblocks.Models.buildRadioButtonsElement",
+      "Slackblocks.Models.buildRawNumber",
+      "Slackblocks.Models.buildRawText",
+      "Slackblocks.Models.buildRichTextBlock",
+      "Slackblocks.Models.buildRichTextChannel",
+      "Slackblocks.Models.buildRichTextCodeBlock",
+      "Slackblocks.Models.buildRichTextEmoji",
+      "Slackblocks.Models.buildRichTextInputElement",
+      "Slackblocks.Models.buildRichTextLink",
+      "Slackblocks.Models.buildRichTextList",
+      "Slackblocks.Models.buildRichTextQuote",
+      "Slackblocks.Models.buildRichTextSection",
+      "Slackblocks.Models.buildRichTextText",
+      "Slackblocks.Models.buildRichTextUser",
+      "Slackblocks.Models.buildRichTextUserGroup",
+      "Slackblocks.Models.buildSectionBlock",
+      "Slackblocks.Models.buildSelectOption",
+      "Slackblocks.Models.buildSelectOptionGroup",
+      "Slackblocks.Models.buildSlackFile",
+      "Slackblocks.Models.buildSlackIcon",
+      "Slackblocks.Models.buildStaticMultiSelectElement",
+      "Slackblocks.Models.buildStaticSelectElement",
+      "Slackblocks.Models.buildTableBlock",
+      "Slackblocks.Models.buildTaskCardBlock",
+      "Slackblocks.Models.buildTimePickerElement",
+      "Slackblocks.Models.buildTrigger",
+      "Slackblocks.Models.buildUrlInputElement",
+      "Slackblocks.Models.buildUrlSource",
+      "Slackblocks.Models.buildUserMultiSelectElement",
+      "Slackblocks.Models.buildUserSelectElement",
+      "Slackblocks.Models.buildVideoBlock",
+      "Slackblocks.Models.buildWebhookMessage",
+      "Slackblocks.Models.buildWorkflow",
+      "Slackblocks.Models.buildWorkflowButtonElement",
+      "Slackblocks.Models.buttonElement",
+      "Slackblocks.Models.buttonElementAccessibilityLabel",
+      "Slackblocks.Models.buttonElementActionId",
+      "Slackblocks.Models.buttonElementAgentPrompt",
+      "Slackblocks.Models.buttonElementAgentPromptDisplay",
+      "Slackblocks.Models.buttonElementAsElement",
+      "Slackblocks.Models.buttonElementConfirm",
+      "Slackblocks.Models.buttonElementExtensions",
+      "Slackblocks.Models.buttonElementStyle",
+      "Slackblocks.Models.buttonElementText",
+      "Slackblocks.Models.buttonElementUrl",
+      "Slackblocks.Models.buttonElementValue",
+      "Slackblocks.Models.cardBlock",
+      "Slackblocks.Models.cardBlockActions",
+      "Slackblocks.Models.cardBlockAsBlock",
+      "Slackblocks.Models.cardBlockBlockId",
+      "Slackblocks.Models.cardBlockBody",
+      "Slackblocks.Models.cardBlockExtensions",
+      "Slackblocks.Models.cardBlockHeroImage",
+      "Slackblocks.Models.cardBlockIcon",
+      "Slackblocks.Models.cardBlockSlackIcon",
+      "Slackblocks.Models.cardBlockSubtext",
+      "Slackblocks.Models.cardBlockSubtitle",
+      "Slackblocks.Models.cardBlockTitle",
+      "Slackblocks.Models.carouselBlock",
+      "Slackblocks.Models.carouselBlockAsBlock",
+      "Slackblocks.Models.carouselBlockBlockId",
+      "Slackblocks.Models.carouselBlockElements",
+      "Slackblocks.Models.carouselBlockExtensions",
+      "Slackblocks.Models.channelMultiSelectElement",
+      "Slackblocks.Models.channelMultiSelectElementActionId",
+      "Slackblocks.Models.channelMultiSelectElementAsElement",
+      "Slackblocks.Models.channelMultiSelectElementAsInputElement",
+      "Slackblocks.Models.channelMultiSelectElementConfirm",
+      "Slackblocks.Models.channelMultiSelectElementExtensions",
+      "Slackblocks.Models.channelMultiSelectElementFocusOnLoad",
+      "Slackblocks.Models.channelMultiSelectElementInitialChannels",
+      "Slackblocks.Models.channelMultiSelectElementMaxSelectedItems",
+      "Slackblocks.Models.channelMultiSelectElementPlaceholder",
+      "Slackblocks.Models.channelSelectElement",
+      "Slackblocks.Models.channelSelectElementActionId",
+      "Slackblocks.Models.channelSelectElementAsElement",
+      "Slackblocks.Models.channelSelectElementAsInputElement",
+      "Slackblocks.Models.channelSelectElementConfirm",
+      "Slackblocks.Models.channelSelectElementExtensions",
+      "Slackblocks.Models.channelSelectElementFocusOnLoad",
+      "Slackblocks.Models.channelSelectElementInitialChannel",
+      "Slackblocks.Models.channelSelectElementPlaceholder",
+      "Slackblocks.Models.channelSelectElementResponseUrlEnabled",
+      "Slackblocks.Models.chartSegment",
+      "Slackblocks.Models.chartSegmentExtensions",
+      "Slackblocks.Models.chartSegmentLabel",
+      "Slackblocks.Models.chartSegmentValue",
+      "Slackblocks.Models.checkboxesElement",
+      "Slackblocks.Models.checkboxesElementActionId",
+      "Slackblocks.Models.checkboxesElementAsElement",
+      "Slackblocks.Models.checkboxesElementAsInputElement",
+      "Slackblocks.Models.checkboxesElementConfirm",
+      "Slackblocks.Models.checkboxesElementExtensions",
+      "Slackblocks.Models.checkboxesElementFocusOnLoad",
+      "Slackblocks.Models.checkboxesElementInitialOptions",
+      "Slackblocks.Models.checkboxesElementOptions",
+      "Slackblocks.Models.columnSettings",
+      "Slackblocks.Models.columnSettingsAlign",
+      "Slackblocks.Models.columnSettingsExtensions",
+      "Slackblocks.Models.columnSettingsIsWrapped",
+      "Slackblocks.Models.confirmationDialogue",
+      "Slackblocks.Models.confirmationDialogueConfirm",
+      "Slackblocks.Models.confirmationDialogueDeny",
+      "Slackblocks.Models.confirmationDialogueExtensions",
+      "Slackblocks.Models.confirmationDialogueStyle",
+      "Slackblocks.Models.confirmationDialogueText",
+      "Slackblocks.Models.confirmationDialogueTitle",
+      "Slackblocks.Models.containerBlock",
+      "Slackblocks.Models.containerBlockAsBlock",
+      "Slackblocks.Models.containerBlockBlockId",
+      "Slackblocks.Models.containerBlockChildBlocks",
+      "Slackblocks.Models.containerBlockDefaultCollapsed",
+      "Slackblocks.Models.containerBlockExtensions",
+      "Slackblocks.Models.containerBlockHasHeaderDivider",
+      "Slackblocks.Models.containerBlockIcon",
+      "Slackblocks.Models.containerBlockIsCollapsible",
+      "Slackblocks.Models.containerBlockRichTextTitle",
+      "Slackblocks.Models.containerBlockSubtitle",
+      "Slackblocks.Models.containerBlockTitle",
+      "Slackblocks.Models.containerBlockWidth",
+      "Slackblocks.Models.contextActionsBlock",
+      "Slackblocks.Models.contextActionsBlockAsBlock",
+      "Slackblocks.Models.contextActionsBlockBlockId",
+      "Slackblocks.Models.contextActionsBlockElements",
+      "Slackblocks.Models.contextActionsBlockExtensions",
+      "Slackblocks.Models.contextBlock",
+      "Slackblocks.Models.contextBlockAsBlock",
+      "Slackblocks.Models.contextBlockBlockId",
+      "Slackblocks.Models.contextBlockElements",
+      "Slackblocks.Models.contextBlockExtensions",
+      "Slackblocks.Models.conversationFilter",
+      "Slackblocks.Models.conversationFilterExcludeBotUsers",
+      "Slackblocks.Models.conversationFilterExcludeExternalSharedChannels",
+      "Slackblocks.Models.conversationFilterExtensions",
+      "Slackblocks.Models.conversationFilterInclude",
+      "Slackblocks.Models.conversationMultiSelectElement",
+      "Slackblocks.Models.conversationMultiSelectElementActionId",
+      "Slackblocks.Models.conversationMultiSelectElementAsElement",
+      "Slackblocks.Models.conversationMultiSelectElementAsInputElement",
+      "Slackblocks.Models.conversationMultiSelectElementConfirm",
+      "Slackblocks.Models.conversationMultiSelectElementDefaultToCurrentConversation",
+      "Slackblocks.Models.conversationMultiSelectElementExtensions",
+      "Slackblocks.Models.conversationMultiSelectElementFilter",
+      "Slackblocks.Models.conversationMultiSelectElementFocusOnLoad",
+      "Slackblocks.Models.conversationMultiSelectElementInitialConversations",
+      "Slackblocks.Models.conversationMultiSelectElementMaxSelectedItems",
+      "Slackblocks.Models.conversationMultiSelectElementPlaceholder",
+      "Slackblocks.Models.conversationSelectElement",
+      "Slackblocks.Models.conversationSelectElementActionId",
+      "Slackblocks.Models.conversationSelectElementAsElement",
+      "Slackblocks.Models.conversationSelectElementAsInputElement",
+      "Slackblocks.Models.conversationSelectElementConfirm",
+      "Slackblocks.Models.conversationSelectElementDefaultToCurrentConversation",
+      "Slackblocks.Models.conversationSelectElementExtensions",
+      "Slackblocks.Models.conversationSelectElementFilter",
+      "Slackblocks.Models.conversationSelectElementFocusOnLoad",
+      "Slackblocks.Models.conversationSelectElementInitialConversation",
+      "Slackblocks.Models.conversationSelectElementPlaceholder",
+      "Slackblocks.Models.conversationSelectElementResponseUrlEnabled",
+      "Slackblocks.Models.dataPoint",
+      "Slackblocks.Models.dataPointExtensions",
+      "Slackblocks.Models.dataPointLabel",
+      "Slackblocks.Models.dataPointValue",
+      "Slackblocks.Models.dataSeries",
+      "Slackblocks.Models.dataSeriesData",
+      "Slackblocks.Models.dataSeriesExtensions",
+      "Slackblocks.Models.dataSeriesName",
+      "Slackblocks.Models.dataTableBlock",
+      "Slackblocks.Models.dataTableBlockAsBlock",
+      "Slackblocks.Models.dataTableBlockBlockId",
+      "Slackblocks.Models.dataTableBlockCaption",
+      "Slackblocks.Models.dataTableBlockExtensions",
+      "Slackblocks.Models.dataTableBlockPageSize",
+      "Slackblocks.Models.dataTableBlockRowHeaderColumnIndex",
+      "Slackblocks.Models.dataTableBlockRows",
+      "Slackblocks.Models.dataVisualizationBlock",
+      "Slackblocks.Models.dataVisualizationBlockAsBlock",
+      "Slackblocks.Models.dataVisualizationBlockBlockId",
+      "Slackblocks.Models.dataVisualizationBlockChart",
+      "Slackblocks.Models.dataVisualizationBlockExtensions",
+      "Slackblocks.Models.dataVisualizationBlockTitle",
+      "Slackblocks.Models.datePickerElement",
+      "Slackblocks.Models.datePickerElementActionId",
+      "Slackblocks.Models.datePickerElementAsElement",
+      "Slackblocks.Models.datePickerElementAsInputElement",
+      "Slackblocks.Models.datePickerElementConfirm",
+      "Slackblocks.Models.datePickerElementExtensions",
+      "Slackblocks.Models.datePickerElementFocusOnLoad",
+      "Slackblocks.Models.datePickerElementInitialDate",
+      "Slackblocks.Models.datePickerElementPlaceholder",
+      "Slackblocks.Models.dateTimePickerElement",
+      "Slackblocks.Models.dateTimePickerElementActionId",
+      "Slackblocks.Models.dateTimePickerElementAsElement",
+      "Slackblocks.Models.dateTimePickerElementAsInputElement",
+      "Slackblocks.Models.dateTimePickerElementConfirm",
+      "Slackblocks.Models.dateTimePickerElementExtensions",
+      "Slackblocks.Models.dateTimePickerElementFocusOnLoad",
+      "Slackblocks.Models.dateTimePickerElementInitialDateTime",
+      "Slackblocks.Models.dispatchActionConfiguration",
+      "Slackblocks.Models.dispatchActionConfigurationExtensions",
+      "Slackblocks.Models.dispatchActionConfigurationTriggerActionsOn",
+      "Slackblocks.Models.dividerBlock",
+      "Slackblocks.Models.dividerBlockAsBlock",
+      "Slackblocks.Models.dividerBlockBlockId",
+      "Slackblocks.Models.dividerBlockExtensions",
+      "Slackblocks.Models.emailInputElement",
+      "Slackblocks.Models.emailInputElementActionId",
+      "Slackblocks.Models.emailInputElementAsElement",
+      "Slackblocks.Models.emailInputElementAsInputElement",
+      "Slackblocks.Models.emailInputElementDispatchActionConfig",
+      "Slackblocks.Models.emailInputElementExtensions",
+      "Slackblocks.Models.emailInputElementFocusOnLoad",
+      "Slackblocks.Models.emailInputElementInitialValue",
+      "Slackblocks.Models.emailInputElementPlaceholder",
+      "Slackblocks.Models.externalMultiSelectElement",
+      "Slackblocks.Models.externalMultiSelectElementActionId",
+      "Slackblocks.Models.externalMultiSelectElementAsElement",
+      "Slackblocks.Models.externalMultiSelectElementAsInputElement",
+      "Slackblocks.Models.externalMultiSelectElementConfirm",
+      "Slackblocks.Models.externalMultiSelectElementExtensions",
+      "Slackblocks.Models.externalMultiSelectElementFocusOnLoad",
+      "Slackblocks.Models.externalMultiSelectElementInitialOptions",
+      "Slackblocks.Models.externalMultiSelectElementMaxSelectedItems",
+      "Slackblocks.Models.externalMultiSelectElementMinQueryLength",
+      "Slackblocks.Models.externalMultiSelectElementPlaceholder",
+      "Slackblocks.Models.externalSelectElement",
+      "Slackblocks.Models.externalSelectElementActionId",
+      "Slackblocks.Models.externalSelectElementAsElement",
+      "Slackblocks.Models.externalSelectElementAsInputElement",
+      "Slackblocks.Models.externalSelectElementConfirm",
+      "Slackblocks.Models.externalSelectElementExtensions",
+      "Slackblocks.Models.externalSelectElementFocusOnLoad",
+      "Slackblocks.Models.externalSelectElementInitialOption",
+      "Slackblocks.Models.externalSelectElementMinQueryLength",
+      "Slackblocks.Models.externalSelectElementPlaceholder",
+      "Slackblocks.Models.feedbackButton",
+      "Slackblocks.Models.feedbackButtonAccessibilityLabel",
+      "Slackblocks.Models.feedbackButtonExtensions",
+      "Slackblocks.Models.feedbackButtonText",
+      "Slackblocks.Models.feedbackButtonValue",
+      "Slackblocks.Models.feedbackButtonsElement",
+      "Slackblocks.Models.feedbackButtonsElementActionId",
+      "Slackblocks.Models.feedbackButtonsElementAsContextActionsElement",
+      "Slackblocks.Models.feedbackButtonsElementAsElement",
+      "Slackblocks.Models.feedbackButtonsElementExtensions",
+      "Slackblocks.Models.feedbackButtonsElementNegativeButton",
+      "Slackblocks.Models.feedbackButtonsElementPositiveButton",
+      "Slackblocks.Models.fileBlock",
+      "Slackblocks.Models.fileBlockAsBlock",
+      "Slackblocks.Models.fileBlockBlockId",
+      "Slackblocks.Models.fileBlockExtensions",
+      "Slackblocks.Models.fileBlockExternalId",
+      "Slackblocks.Models.fileBlockSource",
+      "Slackblocks.Models.fileInputElement",
+      "Slackblocks.Models.fileInputElementActionId",
+      "Slackblocks.Models.fileInputElementAsElement",
+      "Slackblocks.Models.fileInputElementAsInputElement",
+      "Slackblocks.Models.fileInputElementExtensions",
+      "Slackblocks.Models.fileInputElementFiletypes",
+      "Slackblocks.Models.fileInputElementMaxFiles",
+      "Slackblocks.Models.headerBlock",
+      "Slackblocks.Models.headerBlockAsBlock",
+      "Slackblocks.Models.headerBlockBlockId",
+      "Slackblocks.Models.headerBlockExtensions",
+      "Slackblocks.Models.headerBlockText",
+      "Slackblocks.Models.homeTabView",
+      "Slackblocks.Models.homeTabViewBlocks",
+      "Slackblocks.Models.homeTabViewCallbackId",
+      "Slackblocks.Models.homeTabViewExtensions",
+      "Slackblocks.Models.homeTabViewExternalId",
+      "Slackblocks.Models.homeTabViewPrivateMetadata",
+      "Slackblocks.Models.iconButtonElement",
+      "Slackblocks.Models.iconButtonElementAccessibilityLabel",
+      "Slackblocks.Models.iconButtonElementActionId",
+      "Slackblocks.Models.iconButtonElementAsContextActionsElement",
+      "Slackblocks.Models.iconButtonElementAsElement",
+      "Slackblocks.Models.iconButtonElementConfirm",
+      "Slackblocks.Models.iconButtonElementExtensions",
+      "Slackblocks.Models.iconButtonElementIcon",
+      "Slackblocks.Models.iconButtonElementText",
+      "Slackblocks.Models.iconButtonElementValue",
+      "Slackblocks.Models.iconButtonElementVisibleToUserIds",
+      "Slackblocks.Models.imageBlock",
+      "Slackblocks.Models.imageBlockAltText",
+      "Slackblocks.Models.imageBlockAsBlock",
+      "Slackblocks.Models.imageBlockBlockId",
+      "Slackblocks.Models.imageBlockExtensions",
+      "Slackblocks.Models.imageBlockImageUrl",
+      "Slackblocks.Models.imageBlockSlackFile",
+      "Slackblocks.Models.imageBlockTitle",
+      "Slackblocks.Models.imageElement",
+      "Slackblocks.Models.imageElementAltText",
+      "Slackblocks.Models.imageElementAsContextElement",
+      "Slackblocks.Models.imageElementAsElement",
+      "Slackblocks.Models.imageElementExtensions",
+      "Slackblocks.Models.imageElementImageUrl",
+      "Slackblocks.Models.imageElementSlackFile",
+      "Slackblocks.Models.inputBlock",
+      "Slackblocks.Models.inputBlockAsBlock",
+      "Slackblocks.Models.inputBlockBlockId",
+      "Slackblocks.Models.inputBlockDispatchAction",
+      "Slackblocks.Models.inputBlockElement",
+      "Slackblocks.Models.inputBlockExtensions",
+      "Slackblocks.Models.inputBlockHint",
+      "Slackblocks.Models.inputBlockLabel",
+      "Slackblocks.Models.inputBlockOptional",
+      "Slackblocks.Models.inputParameter",
+      "Slackblocks.Models.inputParameterExtensions",
+      "Slackblocks.Models.inputParameterName",
+      "Slackblocks.Models.inputParameterValue",
+      "Slackblocks.Models.inspectActionsBlock",
+      "Slackblocks.Models.inspectAlertBlock",
+      "Slackblocks.Models.inspectAreaChart",
+      "Slackblocks.Models.inspectAttachment",
+      "Slackblocks.Models.inspectAxisConfig",
+      "Slackblocks.Models.inspectBarChart",
+      "Slackblocks.Models.inspectButtonElement",
+      "Slackblocks.Models.inspectCardBlock",
+      "Slackblocks.Models.inspectCarouselBlock",
+      "Slackblocks.Models.inspectChannelMultiSelectElement",
+      "Slackblocks.Models.inspectChannelSelectElement",
+      "Slackblocks.Models.inspectChartSegment",
+      "Slackblocks.Models.inspectCheckboxesElement",
+      "Slackblocks.Models.inspectColumnSettings",
+      "Slackblocks.Models.inspectConfirmationDialogue",
+      "Slackblocks.Models.inspectContainerBlock",
+      "Slackblocks.Models.inspectContextActionsBlock",
+      "Slackblocks.Models.inspectContextBlock",
+      "Slackblocks.Models.inspectConversationFilter",
+      "Slackblocks.Models.inspectConversationMultiSelectElement",
+      "Slackblocks.Models.inspectConversationSelectElement",
+      "Slackblocks.Models.inspectDataPoint",
+      "Slackblocks.Models.inspectDataSeries",
+      "Slackblocks.Models.inspectDataTableBlock",
+      "Slackblocks.Models.inspectDataVisualizationBlock",
+      "Slackblocks.Models.inspectDatePickerElement",
+      "Slackblocks.Models.inspectDateTimePickerElement",
+      "Slackblocks.Models.inspectDispatchActionConfiguration",
+      "Slackblocks.Models.inspectDividerBlock",
+      "Slackblocks.Models.inspectEmailInputElement",
+      "Slackblocks.Models.inspectExternalMultiSelectElement",
+      "Slackblocks.Models.inspectExternalSelectElement",
+      "Slackblocks.Models.inspectFeedbackButton",
+      "Slackblocks.Models.inspectFeedbackButtonsElement",
+      "Slackblocks.Models.inspectFileBlock",
+      "Slackblocks.Models.inspectFileInputElement",
+      "Slackblocks.Models.inspectHeaderBlock",
+      "Slackblocks.Models.inspectHomeTabView",
+      "Slackblocks.Models.inspectIconButtonElement",
+      "Slackblocks.Models.inspectImageBlock",
+      "Slackblocks.Models.inspectImageElement",
+      "Slackblocks.Models.inspectInputBlock",
+      "Slackblocks.Models.inspectInputParameter",
+      "Slackblocks.Models.inspectLineChart",
+      "Slackblocks.Models.inspectMarkdownBlock",
+      "Slackblocks.Models.inspectMarkdownText",
+      "Slackblocks.Models.inspectMessagePayload",
+      "Slackblocks.Models.inspectMessageResponse",
+      "Slackblocks.Models.inspectModalView",
+      "Slackblocks.Models.inspectNumberInputElement",
+      "Slackblocks.Models.inspectOverflowElement",
+      "Slackblocks.Models.inspectPieChart",
+      "Slackblocks.Models.inspectPlainText",
+      "Slackblocks.Models.inspectPlainTextInputElement",
+      "Slackblocks.Models.inspectPlanBlock",
+      "Slackblocks.Models.inspectPlanTask",
+      "Slackblocks.Models.inspectRadioButtonsElement",
+      "Slackblocks.Models.inspectRawNumber",
+      "Slackblocks.Models.inspectRawText",
+      "Slackblocks.Models.inspectRichTextBlock",
+      "Slackblocks.Models.inspectRichTextChannel",
+      "Slackblocks.Models.inspectRichTextCodeBlock",
+      "Slackblocks.Models.inspectRichTextEmoji",
+      "Slackblocks.Models.inspectRichTextInputElement",
+      "Slackblocks.Models.inspectRichTextLink",
+      "Slackblocks.Models.inspectRichTextList",
+      "Slackblocks.Models.inspectRichTextQuote",
+      "Slackblocks.Models.inspectRichTextSection",
+      "Slackblocks.Models.inspectRichTextText",
+      "Slackblocks.Models.inspectRichTextUser",
+      "Slackblocks.Models.inspectRichTextUserGroup",
+      "Slackblocks.Models.inspectSectionBlock",
+      "Slackblocks.Models.inspectSelectOption",
+      "Slackblocks.Models.inspectSelectOptionGroup",
+      "Slackblocks.Models.inspectSlackFile",
+      "Slackblocks.Models.inspectSlackIcon",
+      "Slackblocks.Models.inspectStaticMultiSelectElement",
+      "Slackblocks.Models.inspectStaticSelectElement",
+      "Slackblocks.Models.inspectTableBlock",
+      "Slackblocks.Models.inspectTaskCardBlock",
+      "Slackblocks.Models.inspectTimePickerElement",
+      "Slackblocks.Models.inspectTrigger",
+      "Slackblocks.Models.inspectUrlInputElement",
+      "Slackblocks.Models.inspectUrlSource",
+      "Slackblocks.Models.inspectUserMultiSelectElement",
+      "Slackblocks.Models.inspectUserSelectElement",
+      "Slackblocks.Models.inspectVideoBlock",
+      "Slackblocks.Models.inspectWebhookMessage",
+      "Slackblocks.Models.inspectWorkflow",
+      "Slackblocks.Models.inspectWorkflowButtonElement",
+      "Slackblocks.Models.lineChart",
+      "Slackblocks.Models.lineChartAsChart",
+      "Slackblocks.Models.lineChartAxisConfig",
+      "Slackblocks.Models.lineChartExtensions",
+      "Slackblocks.Models.lineChartSeries",
+      "Slackblocks.Models.markdownBlock",
+      "Slackblocks.Models.markdownBlockAsBlock",
+      "Slackblocks.Models.markdownBlockBlockId",
+      "Slackblocks.Models.markdownBlockExtensions",
+      "Slackblocks.Models.markdownBlockText",
+      "Slackblocks.Models.markdownText",
+      "Slackblocks.Models.markdownTextAsContextElement",
+      "Slackblocks.Models.markdownTextAsText",
+      "Slackblocks.Models.markdownTextExtensions",
+      "Slackblocks.Models.markdownTextText",
+      "Slackblocks.Models.markdownTextVerbatim",
+      "Slackblocks.Models.matchBlockActionsBlock",
+      "Slackblocks.Models.matchBlockAlertBlock",
+      "Slackblocks.Models.matchBlockCardBlock",
+      "Slackblocks.Models.matchBlockCarouselBlock",
+      "Slackblocks.Models.matchBlockContainerBlock",
+      "Slackblocks.Models.matchBlockContextActionsBlock",
+      "Slackblocks.Models.matchBlockContextBlock",
+      "Slackblocks.Models.matchBlockDataTableBlock",
+      "Slackblocks.Models.matchBlockDataVisualizationBlock",
+      "Slackblocks.Models.matchBlockDividerBlock",
+      "Slackblocks.Models.matchBlockFileBlock",
+      "Slackblocks.Models.matchBlockHeaderBlock",
+      "Slackblocks.Models.matchBlockImageBlock",
+      "Slackblocks.Models.matchBlockInputBlock",
+      "Slackblocks.Models.matchBlockMarkdownBlock",
+      "Slackblocks.Models.matchBlockPlanBlock",
+      "Slackblocks.Models.matchBlockRichTextBlock",
+      "Slackblocks.Models.matchBlockSectionBlock",
+      "Slackblocks.Models.matchBlockTableBlock",
+      "Slackblocks.Models.matchBlockTaskCardBlock",
+      "Slackblocks.Models.matchBlockVideoBlock",
+      "Slackblocks.Models.matchChartAreaChart",
+      "Slackblocks.Models.matchChartBarChart",
+      "Slackblocks.Models.matchChartLineChart",
+      "Slackblocks.Models.matchChartPieChart",
+      "Slackblocks.Models.matchContextActionsElementFeedbackButtonsElement",
+      "Slackblocks.Models.matchContextActionsElementIconButtonElement",
+      "Slackblocks.Models.matchContextElementImageElement",
+      "Slackblocks.Models.matchContextElementMarkdownText",
+      "Slackblocks.Models.matchContextElementPlainText",
+      "Slackblocks.Models.matchDataTableCellRawNumber",
+      "Slackblocks.Models.matchDataTableCellRawText",
+      "Slackblocks.Models.matchDataTableCellRichTextBlock",
+      "Slackblocks.Models.matchElementButtonElement",
+      "Slackblocks.Models.matchElementChannelMultiSelectElement",
+      "Slackblocks.Models.matchElementChannelSelectElement",
+      "Slackblocks.Models.matchElementCheckboxesElement",
+      "Slackblocks.Models.matchElementConversationMultiSelectElement",
+      "Slackblocks.Models.matchElementConversationSelectElement",
+      "Slackblocks.Models.matchElementDatePickerElement",
+      "Slackblocks.Models.matchElementDateTimePickerElement",
+      "Slackblocks.Models.matchElementEmailInputElement",
+      "Slackblocks.Models.matchElementExternalMultiSelectElement",
+      "Slackblocks.Models.matchElementExternalSelectElement",
+      "Slackblocks.Models.matchElementFeedbackButtonsElement",
+      "Slackblocks.Models.matchElementFileInputElement",
+      "Slackblocks.Models.matchElementIconButtonElement",
+      "Slackblocks.Models.matchElementImageElement",
+      "Slackblocks.Models.matchElementNumberInputElement",
+      "Slackblocks.Models.matchElementOverflowElement",
+      "Slackblocks.Models.matchElementPlainTextInputElement",
+      "Slackblocks.Models.matchElementRadioButtonsElement",
+      "Slackblocks.Models.matchElementRichTextInputElement",
+      "Slackblocks.Models.matchElementStaticMultiSelectElement",
+      "Slackblocks.Models.matchElementStaticSelectElement",
+      "Slackblocks.Models.matchElementTimePickerElement",
+      "Slackblocks.Models.matchElementUrlInputElement",
+      "Slackblocks.Models.matchElementUserMultiSelectElement",
+      "Slackblocks.Models.matchElementUserSelectElement",
+      "Slackblocks.Models.matchElementWorkflowButtonElement",
+      "Slackblocks.Models.matchInputElementChannelMultiSelectElement",
+      "Slackblocks.Models.matchInputElementChannelSelectElement",
+      "Slackblocks.Models.matchInputElementCheckboxesElement",
+      "Slackblocks.Models.matchInputElementConversationMultiSelectElement",
+      "Slackblocks.Models.matchInputElementConversationSelectElement",
+      "Slackblocks.Models.matchInputElementDatePickerElement",
+      "Slackblocks.Models.matchInputElementDateTimePickerElement",
+      "Slackblocks.Models.matchInputElementEmailInputElement",
+      "Slackblocks.Models.matchInputElementExternalMultiSelectElement",
+      "Slackblocks.Models.matchInputElementExternalSelectElement",
+      "Slackblocks.Models.matchInputElementFileInputElement",
+      "Slackblocks.Models.matchInputElementNumberInputElement",
+      "Slackblocks.Models.matchInputElementPlainTextInputElement",
+      "Slackblocks.Models.matchInputElementRadioButtonsElement",
+      "Slackblocks.Models.matchInputElementRichTextInputElement",
+      "Slackblocks.Models.matchInputElementStaticMultiSelectElement",
+      "Slackblocks.Models.matchInputElementStaticSelectElement",
+      "Slackblocks.Models.matchInputElementTimePickerElement",
+      "Slackblocks.Models.matchInputElementUrlInputElement",
+      "Slackblocks.Models.matchInputElementUserMultiSelectElement",
+      "Slackblocks.Models.matchInputElementUserSelectElement",
+      "Slackblocks.Models.matchRichTextBlockElementRichTextCodeBlock",
+      "Slackblocks.Models.matchRichTextBlockElementRichTextList",
+      "Slackblocks.Models.matchRichTextBlockElementRichTextQuote",
+      "Slackblocks.Models.matchRichTextBlockElementRichTextSection",
+      "Slackblocks.Models.matchRichTextSectionElementRichTextChannel",
+      "Slackblocks.Models.matchRichTextSectionElementRichTextEmoji",
+      "Slackblocks.Models.matchRichTextSectionElementRichTextLink",
+      "Slackblocks.Models.matchRichTextSectionElementRichTextText",
+      "Slackblocks.Models.matchRichTextSectionElementRichTextUser",
+      "Slackblocks.Models.matchRichTextSectionElementRichTextUserGroup",
+      "Slackblocks.Models.matchTableCellRawText",
+      "Slackblocks.Models.matchTableCellRichTextBlock",
+      "Slackblocks.Models.matchTextObjectMarkdownText",
+      "Slackblocks.Models.matchTextObjectPlainText",
+      "Slackblocks.Models.messagePayload",
+      "Slackblocks.Models.messagePayloadAttachments",
+      "Slackblocks.Models.messagePayloadBlocks",
+      "Slackblocks.Models.messagePayloadChannel",
+      "Slackblocks.Models.messagePayloadExtensions",
+      "Slackblocks.Models.messagePayloadMetadata",
+      "Slackblocks.Models.messagePayloadMrkdwn",
+      "Slackblocks.Models.messagePayloadText",
+      "Slackblocks.Models.messagePayloadUnfurlLinks",
+      "Slackblocks.Models.messagePayloadUnfurlMedia",
+      "Slackblocks.Models.messageResponse",
+      "Slackblocks.Models.messageResponseAttachments",
+      "Slackblocks.Models.messageResponseBlocks",
+      "Slackblocks.Models.messageResponseExtensions",
+      "Slackblocks.Models.messageResponseMrkdwn",
+      "Slackblocks.Models.messageResponseReplaceOriginal",
+      "Slackblocks.Models.messageResponseResponseType",
+      "Slackblocks.Models.messageResponseText",
+      "Slackblocks.Models.modalView",
+      "Slackblocks.Models.modalViewBlocks",
+      "Slackblocks.Models.modalViewCallbackId",
+      "Slackblocks.Models.modalViewClearOnClose",
+      "Slackblocks.Models.modalViewClose",
+      "Slackblocks.Models.modalViewExtensions",
+      "Slackblocks.Models.modalViewExternalId",
+      "Slackblocks.Models.modalViewNotifyOnClose",
+      "Slackblocks.Models.modalViewPrivateMetadata",
+      "Slackblocks.Models.modalViewSubmit",
+      "Slackblocks.Models.modalViewSubmitDisabled",
+      "Slackblocks.Models.modalViewTitle",
+      "Slackblocks.Models.numberInputElement",
+      "Slackblocks.Models.numberInputElementActionId",
+      "Slackblocks.Models.numberInputElementAsElement",
+      "Slackblocks.Models.numberInputElementAsInputElement",
+      "Slackblocks.Models.numberInputElementDispatchActionConfig",
+      "Slackblocks.Models.numberInputElementExtensions",
+      "Slackblocks.Models.numberInputElementFocusOnLoad",
+      "Slackblocks.Models.numberInputElementInitialValue",
+      "Slackblocks.Models.numberInputElementIsDecimalAllowed",
+      "Slackblocks.Models.numberInputElementMaxValue",
+      "Slackblocks.Models.numberInputElementMinValue",
+      "Slackblocks.Models.numberInputElementPlaceholder",
+      "Slackblocks.Models.overflowElement",
+      "Slackblocks.Models.overflowElementActionId",
+      "Slackblocks.Models.overflowElementAsElement",
+      "Slackblocks.Models.overflowElementConfirm",
+      "Slackblocks.Models.overflowElementExtensions",
+      "Slackblocks.Models.overflowElementOptions",
+      "Slackblocks.Models.packageVersion",
+      "Slackblocks.Models.pieChart",
+      "Slackblocks.Models.pieChartAsChart",
+      "Slackblocks.Models.pieChartExtensions",
+      "Slackblocks.Models.pieChartSegments",
+      "Slackblocks.Models.plainText",
+      "Slackblocks.Models.plainTextAsContextElement",
+      "Slackblocks.Models.plainTextAsText",
+      "Slackblocks.Models.plainTextEmoji",
+      "Slackblocks.Models.plainTextExtensions",
+      "Slackblocks.Models.plainTextInputElement",
+      "Slackblocks.Models.plainTextInputElementActionId",
+      "Slackblocks.Models.plainTextInputElementAsElement",
+      "Slackblocks.Models.plainTextInputElementAsInputElement",
+      "Slackblocks.Models.plainTextInputElementDispatchActionConfig",
+      "Slackblocks.Models.plainTextInputElementExtensions",
+      "Slackblocks.Models.plainTextInputElementFocusOnLoad",
+      "Slackblocks.Models.plainTextInputElementInitialValue",
+      "Slackblocks.Models.plainTextInputElementMaxLength",
+      "Slackblocks.Models.plainTextInputElementMinLength",
+      "Slackblocks.Models.plainTextInputElementMultiline",
+      "Slackblocks.Models.plainTextInputElementPlaceholder",
+      "Slackblocks.Models.plainTextText",
+      "Slackblocks.Models.planBlock",
+      "Slackblocks.Models.planBlockAsBlock",
+      "Slackblocks.Models.planBlockBlockId",
+      "Slackblocks.Models.planBlockExtensions",
+      "Slackblocks.Models.planBlockTasks",
+      "Slackblocks.Models.planBlockTitle",
+      "Slackblocks.Models.radioButtonsElement",
+      "Slackblocks.Models.radioButtonsElementActionId",
+      "Slackblocks.Models.radioButtonsElementAsElement",
+      "Slackblocks.Models.radioButtonsElementAsInputElement",
+      "Slackblocks.Models.radioButtonsElementConfirm",
+      "Slackblocks.Models.radioButtonsElementExtensions",
+      "Slackblocks.Models.radioButtonsElementFocusOnLoad",
+      "Slackblocks.Models.radioButtonsElementInitialOption",
+      "Slackblocks.Models.radioButtonsElementOptions",
+      "Slackblocks.Models.rawNumber",
+      "Slackblocks.Models.rawNumberAsDataTableCell",
+      "Slackblocks.Models.rawNumberExtensions",
+      "Slackblocks.Models.rawNumberText",
+      "Slackblocks.Models.rawNumberValue",
+      "Slackblocks.Models.rawText",
+      "Slackblocks.Models.rawTextAsDataTableCell",
+      "Slackblocks.Models.rawTextAsTableCell",
+      "Slackblocks.Models.rawTextExtensions",
+      "Slackblocks.Models.rawTextText",
+      "Slackblocks.Models.richTextBlock",
+      "Slackblocks.Models.richTextBlockAsBlock",
+      "Slackblocks.Models.richTextBlockAsDataTableCell",
+      "Slackblocks.Models.richTextBlockAsTableCell",
+      "Slackblocks.Models.richTextBlockBlockId",
+      "Slackblocks.Models.richTextBlockElements",
+      "Slackblocks.Models.richTextBlockExtensions",
+      "Slackblocks.Models.richTextChannel",
+      "Slackblocks.Models.richTextChannelAsRichTextSectionElement",
+      "Slackblocks.Models.richTextChannelChannelId",
+      "Slackblocks.Models.richTextChannelExtensions",
+      "Slackblocks.Models.richTextChannelStyle",
+      "Slackblocks.Models.richTextCodeBlock",
+      "Slackblocks.Models.richTextCodeBlockAsRichTextBlockElement",
+      "Slackblocks.Models.richTextCodeBlockBorder",
+      "Slackblocks.Models.richTextCodeBlockElements",
+      "Slackblocks.Models.richTextCodeBlockExtensions",
+      "Slackblocks.Models.richTextEmoji",
+      "Slackblocks.Models.richTextEmojiAsRichTextSectionElement",
+      "Slackblocks.Models.richTextEmojiExtensions",
+      "Slackblocks.Models.richTextEmojiName",
+      "Slackblocks.Models.richTextEmojiSkinTone",
+      "Slackblocks.Models.richTextInputElement",
+      "Slackblocks.Models.richTextInputElementActionId",
+      "Slackblocks.Models.richTextInputElementAsElement",
+      "Slackblocks.Models.richTextInputElementAsInputElement",
+      "Slackblocks.Models.richTextInputElementDispatchActionConfig",
+      "Slackblocks.Models.richTextInputElementExtensions",
+      "Slackblocks.Models.richTextInputElementFocusOnLoad",
+      "Slackblocks.Models.richTextInputElementInitialValue",
+      "Slackblocks.Models.richTextInputElementMaxLines",
+      "Slackblocks.Models.richTextInputElementMinLines",
+      "Slackblocks.Models.richTextInputElementPlaceholder",
+      "Slackblocks.Models.richTextLink",
+      "Slackblocks.Models.richTextLinkAsRichTextSectionElement",
+      "Slackblocks.Models.richTextLinkExtensions",
+      "Slackblocks.Models.richTextLinkStyle",
+      "Slackblocks.Models.richTextLinkText",
+      "Slackblocks.Models.richTextLinkUnsafe",
+      "Slackblocks.Models.richTextLinkUrl",
+      "Slackblocks.Models.richTextList",
+      "Slackblocks.Models.richTextListAsRichTextBlockElement",
+      "Slackblocks.Models.richTextListBorder",
+      "Slackblocks.Models.richTextListElements",
+      "Slackblocks.Models.richTextListExtensions",
+      "Slackblocks.Models.richTextListIndent",
+      "Slackblocks.Models.richTextListOffset",
+      "Slackblocks.Models.richTextListStyle",
+      "Slackblocks.Models.richTextQuote",
+      "Slackblocks.Models.richTextQuoteAsRichTextBlockElement",
+      "Slackblocks.Models.richTextQuoteBorder",
+      "Slackblocks.Models.richTextQuoteElements",
+      "Slackblocks.Models.richTextQuoteExtensions",
+      "Slackblocks.Models.richTextSection",
+      "Slackblocks.Models.richTextSectionAsRichTextBlockElement",
+      "Slackblocks.Models.richTextSectionElements",
+      "Slackblocks.Models.richTextSectionExtensions",
+      "Slackblocks.Models.richTextText",
+      "Slackblocks.Models.richTextTextAsRichTextSectionElement",
+      "Slackblocks.Models.richTextTextExtensions",
+      "Slackblocks.Models.richTextTextStyle",
+      "Slackblocks.Models.richTextTextText",
+      "Slackblocks.Models.richTextUser",
+      "Slackblocks.Models.richTextUserAsRichTextSectionElement",
+      "Slackblocks.Models.richTextUserExtensions",
+      "Slackblocks.Models.richTextUserGroup",
+      "Slackblocks.Models.richTextUserGroupAsRichTextSectionElement",
+      "Slackblocks.Models.richTextUserGroupExtensions",
+      "Slackblocks.Models.richTextUserGroupStyle",
+      "Slackblocks.Models.richTextUserGroupUserGroupId",
+      "Slackblocks.Models.richTextUserStyle",
+      "Slackblocks.Models.richTextUserUserId",
+      "Slackblocks.Models.sectionBlock",
+      "Slackblocks.Models.sectionBlockAccessory",
+      "Slackblocks.Models.sectionBlockAsBlock",
+      "Slackblocks.Models.sectionBlockBlockId",
+      "Slackblocks.Models.sectionBlockExtensions",
+      "Slackblocks.Models.sectionBlockFields",
+      "Slackblocks.Models.sectionBlockText",
+      "Slackblocks.Models.selectOption",
+      "Slackblocks.Models.selectOptionDescription",
+      "Slackblocks.Models.selectOptionExtensions",
+      "Slackblocks.Models.selectOptionGroup",
+      "Slackblocks.Models.selectOptionGroupExtensions",
+      "Slackblocks.Models.selectOptionGroupLabel",
+      "Slackblocks.Models.selectOptionGroupOptions",
+      "Slackblocks.Models.selectOptionText",
+      "Slackblocks.Models.selectOptionUrl",
+      "Slackblocks.Models.selectOptionValue",
+      "Slackblocks.Models.slackFile",
+      "Slackblocks.Models.slackFileExtensions",
+      "Slackblocks.Models.slackFileId",
+      "Slackblocks.Models.slackFileUrl",
+      "Slackblocks.Models.slackIcon",
+      "Slackblocks.Models.slackIconExtensions",
+      "Slackblocks.Models.slackIconName",
+      "Slackblocks.Models.specVersion",
+      "Slackblocks.Models.staticMultiSelectElement",
+      "Slackblocks.Models.staticMultiSelectElementActionId",
+      "Slackblocks.Models.staticMultiSelectElementAsElement",
+      "Slackblocks.Models.staticMultiSelectElementAsInputElement",
+      "Slackblocks.Models.staticMultiSelectElementConfirm",
+      "Slackblocks.Models.staticMultiSelectElementExtensions",
+      "Slackblocks.Models.staticMultiSelectElementFocusOnLoad",
+      "Slackblocks.Models.staticMultiSelectElementInitialOptions",
+      "Slackblocks.Models.staticMultiSelectElementMaxSelectedItems",
+      "Slackblocks.Models.staticMultiSelectElementOptionGroups",
+      "Slackblocks.Models.staticMultiSelectElementOptions",
+      "Slackblocks.Models.staticMultiSelectElementPlaceholder",
+      "Slackblocks.Models.staticSelectElement",
+      "Slackblocks.Models.staticSelectElementActionId",
+      "Slackblocks.Models.staticSelectElementAsElement",
+      "Slackblocks.Models.staticSelectElementAsInputElement",
+      "Slackblocks.Models.staticSelectElementConfirm",
+      "Slackblocks.Models.staticSelectElementExtensions",
+      "Slackblocks.Models.staticSelectElementFocusOnLoad",
+      "Slackblocks.Models.staticSelectElementInitialOption",
+      "Slackblocks.Models.staticSelectElementOptionGroups",
+      "Slackblocks.Models.staticSelectElementOptions",
+      "Slackblocks.Models.staticSelectElementPlaceholder",
+      "Slackblocks.Models.tableBlock",
+      "Slackblocks.Models.tableBlockAsBlock",
+      "Slackblocks.Models.tableBlockBlockId",
+      "Slackblocks.Models.tableBlockColumnSettings",
+      "Slackblocks.Models.tableBlockExtensions",
+      "Slackblocks.Models.tableBlockRows",
+      "Slackblocks.Models.taskCardBlock",
+      "Slackblocks.Models.taskCardBlockAsBlock",
+      "Slackblocks.Models.taskCardBlockBlockId",
+      "Slackblocks.Models.taskCardBlockDetails",
+      "Slackblocks.Models.taskCardBlockExtensions",
+      "Slackblocks.Models.taskCardBlockOutput",
+      "Slackblocks.Models.taskCardBlockSources",
+      "Slackblocks.Models.taskCardBlockStatus",
+      "Slackblocks.Models.taskCardBlockTaskId",
+      "Slackblocks.Models.taskCardBlockTitle",
+      "Slackblocks.Models.timePickerElement",
+      "Slackblocks.Models.timePickerElementActionId",
+      "Slackblocks.Models.timePickerElementAsElement",
+      "Slackblocks.Models.timePickerElementAsInputElement",
+      "Slackblocks.Models.timePickerElementConfirm",
+      "Slackblocks.Models.timePickerElementExtensions",
+      "Slackblocks.Models.timePickerElementFocusOnLoad",
+      "Slackblocks.Models.timePickerElementInitialTime",
+      "Slackblocks.Models.timePickerElementPlaceholder",
+      "Slackblocks.Models.timePickerElementTimezone",
+      "Slackblocks.Models.trigger",
+      "Slackblocks.Models.triggerCustomizableInputParameters",
+      "Slackblocks.Models.triggerExtensions",
+      "Slackblocks.Models.triggerUrl",
+      "Slackblocks.Models.urlInputElement",
+      "Slackblocks.Models.urlInputElementActionId",
+      "Slackblocks.Models.urlInputElementAsElement",
+      "Slackblocks.Models.urlInputElementAsInputElement",
+      "Slackblocks.Models.urlInputElementDispatchActionConfig",
+      "Slackblocks.Models.urlInputElementExtensions",
+      "Slackblocks.Models.urlInputElementFocusOnLoad",
+      "Slackblocks.Models.urlInputElementInitialValue",
+      "Slackblocks.Models.urlInputElementPlaceholder",
+      "Slackblocks.Models.urlSource",
+      "Slackblocks.Models.urlSourceExtensions",
+      "Slackblocks.Models.urlSourceText",
+      "Slackblocks.Models.urlSourceUrl",
+      "Slackblocks.Models.userMultiSelectElement",
+      "Slackblocks.Models.userMultiSelectElementActionId",
+      "Slackblocks.Models.userMultiSelectElementAsElement",
+      "Slackblocks.Models.userMultiSelectElementAsInputElement",
+      "Slackblocks.Models.userMultiSelectElementConfirm",
+      "Slackblocks.Models.userMultiSelectElementExtensions",
+      "Slackblocks.Models.userMultiSelectElementFocusOnLoad",
+      "Slackblocks.Models.userMultiSelectElementInitialUsers",
+      "Slackblocks.Models.userMultiSelectElementMaxSelectedItems",
+      "Slackblocks.Models.userMultiSelectElementPlaceholder",
+      "Slackblocks.Models.userSelectElement",
+      "Slackblocks.Models.userSelectElementActionId",
+      "Slackblocks.Models.userSelectElementAsElement",
+      "Slackblocks.Models.userSelectElementAsInputElement",
+      "Slackblocks.Models.userSelectElementConfirm",
+      "Slackblocks.Models.userSelectElementExtensions",
+      "Slackblocks.Models.userSelectElementFocusOnLoad",
+      "Slackblocks.Models.userSelectElementInitialUser",
+      "Slackblocks.Models.userSelectElementPlaceholder",
+      "Slackblocks.Models.videoBlock",
+      "Slackblocks.Models.videoBlockAltText",
+      "Slackblocks.Models.videoBlockAsBlock",
+      "Slackblocks.Models.videoBlockAuthorName",
+      "Slackblocks.Models.videoBlockBlockId",
+      "Slackblocks.Models.videoBlockDescription",
+      "Slackblocks.Models.videoBlockExtensions",
+      "Slackblocks.Models.videoBlockProviderIconUrl",
+      "Slackblocks.Models.videoBlockProviderName",
+      "Slackblocks.Models.videoBlockThumbnailUrl",
+      "Slackblocks.Models.videoBlockTitle",
+      "Slackblocks.Models.videoBlockTitleUrl",
+      "Slackblocks.Models.videoBlockVideoUrl",
+      "Slackblocks.Models.webhookMessage",
+      "Slackblocks.Models.webhookMessageAttachments",
+      "Slackblocks.Models.webhookMessageBlocks",
+      "Slackblocks.Models.webhookMessageDeleteOriginal",
+      "Slackblocks.Models.webhookMessageExtensions",
+      "Slackblocks.Models.webhookMessageMetadata",
+      "Slackblocks.Models.webhookMessageReplaceOriginal",
+      "Slackblocks.Models.webhookMessageResponseType",
+      "Slackblocks.Models.webhookMessageText",
+      "Slackblocks.Models.webhookMessageUnfurlLinks",
+      "Slackblocks.Models.webhookMessageUnfurlMedia",
+      "Slackblocks.Models.workflow",
+      "Slackblocks.Models.workflowButtonElement",
+      "Slackblocks.Models.workflowButtonElementAccessibilityLabel",
+      "Slackblocks.Models.workflowButtonElementActionId",
+      "Slackblocks.Models.workflowButtonElementAsElement",
+      "Slackblocks.Models.workflowButtonElementConfirm",
+      "Slackblocks.Models.workflowButtonElementExtensions",
+      "Slackblocks.Models.workflowButtonElementStyle",
+      "Slackblocks.Models.workflowButtonElementText",
+      "Slackblocks.Models.workflowButtonElementWorkflow",
+      "Slackblocks.Models.workflowExtensions",
+      "Slackblocks.Models.workflowTrigger"
+    ],
+    "Slackblocks.Json": [
+      "Slackblocks.Json.FromSlackJSON",
+      "Slackblocks.Json.decimalText",
+      "Slackblocks.Json.decode",
+      "Slackblocks.Json.fromSlackJSON",
+      "Slackblocks.Json.fromValue"
+    ],
+    "Slackblocks.Style": [
+      "Slackblocks.Style.RichTextStyle",
+      "Slackblocks.Style.richTextStyle",
+      "Slackblocks.Style.styleBold",
+      "Slackblocks.Style.styleClientHighlight",
+      "Slackblocks.Style.styleCode",
+      "Slackblocks.Style.styleHighlight",
+      "Slackblocks.Style.styleItalic",
+      "Slackblocks.Style.styleStrike",
+      "Slackblocks.Style.styleUnlink"
+    ],
+    "Slackblocks.Helpers": [
+      "Slackblocks.Helpers.Black",
+      "Slackblocks.Helpers.Blue",
+      "Slackblocks.Helpers.BuilderBlocks",
+      "Slackblocks.Helpers.BuilderHome",
+      "Slackblocks.Helpers.BuilderJSON",
+      "Slackblocks.Helpers.BuilderMessage",
+      "Slackblocks.Helpers.BuilderModal",
+      "Slackblocks.Helpers.BuilderPayload",
+      "Slackblocks.Helpers.BuilderResponse",
+      "Slackblocks.Helpers.BuilderWebhook",
+      "Slackblocks.Helpers.Color",
+      "Slackblocks.Helpers.Danger",
+      "Slackblocks.Helpers.Good",
+      "Slackblocks.Helpers.Green",
+      "Slackblocks.Helpers.NamedColor",
+      "Slackblocks.Helpers.Orange",
+      "Slackblocks.Helpers.PaginatorOptions",
+      "Slackblocks.Helpers.Purple",
+      "Slackblocks.Helpers.Red",
+      "Slackblocks.Helpers.Warning",
+      "Slackblocks.Helpers.Yellow",
+      "Slackblocks.Helpers.accordion",
+      "Slackblocks.Helpers.accordionSection",
+      "Slackblocks.Helpers.blockKitBuilderURL",
+      "Slackblocks.Helpers.colorText",
+      "Slackblocks.Helpers.hexColor",
+      "Slackblocks.Helpers.namedColor",
+      "Slackblocks.Helpers.paginate",
+      "Slackblocks.Helpers.paginator",
+      "Slackblocks.Helpers.paginatorActionIdPrefix",
+      "Slackblocks.Helpers.paginatorBlockId",
+      "Slackblocks.Helpers.paginatorNextText",
+      "Slackblocks.Helpers.paginatorPage",
+      "Slackblocks.Helpers.paginatorPageSize",
+      "Slackblocks.Helpers.paginatorPreviousText",
+      "Slackblocks.Helpers.paginatorShowPageIndicator",
+      "Slackblocks.Helpers.rgbColor",
+      "Slackblocks.Helpers.workflowFromURL"
+    ]
+  },
+  "models": {
+    "ActionsBlock": {
+      "type": "Slackblocks.Models.ActionsBlock",
+      "capability": "blocks.actions"
+    },
+    "AlertBlock": {
+      "type": "Slackblocks.Models.AlertBlock",
+      "capability": "blocks.alert"
+    },
+    "AreaChart": {
+      "type": "Slackblocks.Models.AreaChart",
+      "capability": "blocks.data_visualization"
+    },
+    "Attachment": {
+      "type": "Slackblocks.Models.Attachment",
+      "capability": "messages.attachment"
+    },
+    "AxisConfig": {
+      "type": "Slackblocks.Models.AxisConfig",
+      "capability": "objects.axis_config"
+    },
+    "BarChart": {
+      "type": "Slackblocks.Models.BarChart",
+      "capability": "blocks.data_visualization"
+    },
+    "ButtonElement": {
+      "type": "Slackblocks.Models.ButtonElement",
+      "capability": "elements.button"
+    },
+    "CardBlock": {
+      "type": "Slackblocks.Models.CardBlock",
+      "capability": "blocks.card"
+    },
+    "CarouselBlock": {
+      "type": "Slackblocks.Models.CarouselBlock",
+      "capability": "blocks.carousel"
+    },
+    "ChannelMultiSelectElement": {
+      "type": "Slackblocks.Models.ChannelMultiSelectElement",
+      "capability": "elements.multi_select_channels"
+    },
+    "ChannelSelectElement": {
+      "type": "Slackblocks.Models.ChannelSelectElement",
+      "capability": "elements.select_channels"
+    },
+    "ChartSegment": {
+      "type": "Slackblocks.Models.ChartSegment",
+      "capability": "objects.chart_segment"
+    },
+    "CheckboxesElement": {
+      "type": "Slackblocks.Models.CheckboxesElement",
+      "capability": "elements.checkboxes"
+    },
+    "ColumnSettings": {
+      "type": "Slackblocks.Models.ColumnSettings",
+      "capability": "objects.column_settings"
+    },
+    "Confirmation": {
+      "type": "Slackblocks.Models.ConfirmationDialogue",
+      "capability": "objects.confirmation"
+    },
+    "ContainerBlock": {
+      "type": "Slackblocks.Models.ContainerBlock",
+      "capability": "blocks.container"
+    },
+    "ContextActionsBlock": {
+      "type": "Slackblocks.Models.ContextActionsBlock",
+      "capability": "blocks.context_actions"
+    },
+    "ContextBlock": {
+      "type": "Slackblocks.Models.ContextBlock",
+      "capability": "blocks.context"
+    },
+    "ConversationFilter": {
+      "type": "Slackblocks.Models.ConversationFilter",
+      "capability": "objects.conversation_filter"
+    },
+    "ConversationMultiSelectElement": {
+      "type": "Slackblocks.Models.ConversationMultiSelectElement",
+      "capability": "elements.multi_select_conversations"
+    },
+    "ConversationSelectElement": {
+      "type": "Slackblocks.Models.ConversationSelectElement",
+      "capability": "elements.select_conversations"
+    },
+    "DataPoint": {
+      "type": "Slackblocks.Models.DataPoint",
+      "capability": "objects.data_point"
+    },
+    "DataSeries": {
+      "type": "Slackblocks.Models.DataSeries",
+      "capability": "objects.data_series"
+    },
+    "DataTableBlock": {
+      "type": "Slackblocks.Models.DataTableBlock",
+      "capability": "blocks.data_table"
+    },
+    "DataVisualizationBlock": {
+      "type": "Slackblocks.Models.DataVisualizationBlock",
+      "capability": "blocks.data_visualization"
+    },
+    "DatePickerElement": {
+      "type": "Slackblocks.Models.DatePickerElement",
+      "capability": "elements.date_picker"
+    },
+    "DateTimePickerElement": {
+      "type": "Slackblocks.Models.DateTimePickerElement",
+      "capability": "elements.datetime_picker"
+    },
+    "DispatchActionConfiguration": {
+      "type": "Slackblocks.Models.DispatchActionConfiguration",
+      "capability": "objects.dispatch_action_configuration"
+    },
+    "DividerBlock": {
+      "type": "Slackblocks.Models.DividerBlock",
+      "capability": "blocks.divider"
+    },
+    "EmailInputElement": {
+      "type": "Slackblocks.Models.EmailInputElement",
+      "capability": "elements.email_input"
+    },
+    "ExternalMultiSelectElement": {
+      "type": "Slackblocks.Models.ExternalMultiSelectElement",
+      "capability": "elements.multi_select_external"
+    },
+    "ExternalSelectElement": {
+      "type": "Slackblocks.Models.ExternalSelectElement",
+      "capability": "elements.select_external"
+    },
+    "FeedbackButton": {
+      "type": "Slackblocks.Models.FeedbackButton",
+      "capability": "objects.feedback_button"
+    },
+    "FeedbackButtonsElement": {
+      "type": "Slackblocks.Models.FeedbackButtonsElement",
+      "capability": "elements.feedback_buttons"
+    },
+    "FileBlock": {
+      "type": "Slackblocks.Models.FileBlock",
+      "capability": "blocks.file"
+    },
+    "FileInputElement": {
+      "type": "Slackblocks.Models.FileInputElement",
+      "capability": "elements.file_input"
+    },
+    "HeaderBlock": {
+      "type": "Slackblocks.Models.HeaderBlock",
+      "capability": "blocks.header"
+    },
+    "HomeTabView": {
+      "type": "Slackblocks.Models.HomeTabView",
+      "capability": "views.home_tab"
+    },
+    "IconButtonElement": {
+      "type": "Slackblocks.Models.IconButtonElement",
+      "capability": "elements.icon_button"
+    },
+    "ImageBlock": {
+      "type": "Slackblocks.Models.ImageBlock",
+      "capability": "blocks.image"
+    },
+    "ImageElement": {
+      "type": "Slackblocks.Models.ImageElement",
+      "capability": "elements.image"
+    },
+    "InputBlock": {
+      "type": "Slackblocks.Models.InputBlock",
+      "capability": "blocks.input"
+    },
+    "InputParameter": {
+      "type": "Slackblocks.Models.InputParameter",
+      "capability": "objects.input_parameter"
+    },
+    "LineChart": {
+      "type": "Slackblocks.Models.LineChart",
+      "capability": "blocks.data_visualization"
+    },
+    "MarkdownBlock": {
+      "type": "Slackblocks.Models.MarkdownBlock",
+      "capability": "blocks.markdown"
+    },
+    "MarkdownText": {
+      "type": "Slackblocks.Models.MarkdownText",
+      "capability": "objects.markdown_text"
+    },
+    "MessagePayload": {
+      "type": "Slackblocks.Models.MessagePayload",
+      "capability": "messages.message"
+    },
+    "MessageResponse": {
+      "type": "Slackblocks.Models.MessageResponse",
+      "capability": "messages.message_response"
+    },
+    "ModalView": {
+      "type": "Slackblocks.Models.ModalView",
+      "capability": "views.modal"
+    },
+    "NumberInputElement": {
+      "type": "Slackblocks.Models.NumberInputElement",
+      "capability": "elements.number_input"
+    },
+    "Option": {
+      "type": "Slackblocks.Models.SelectOption",
+      "capability": "objects.option"
+    },
+    "OptionGroup": {
+      "type": "Slackblocks.Models.SelectOptionGroup",
+      "capability": "objects.option_group"
+    },
+    "OverflowElement": {
+      "type": "Slackblocks.Models.OverflowElement",
+      "capability": "elements.overflow"
+    },
+    "PieChart": {
+      "type": "Slackblocks.Models.PieChart",
+      "capability": "blocks.data_visualization"
+    },
+    "PlainText": {
+      "type": "Slackblocks.Models.PlainText",
+      "capability": "objects.plain_text"
+    },
+    "PlainTextInputElement": {
+      "type": "Slackblocks.Models.PlainTextInputElement",
+      "capability": "elements.plain_text_input"
+    },
+    "PlanBlock": {
+      "type": "Slackblocks.Models.PlanBlock",
+      "capability": "blocks.plan"
+    },
+    "RadioButtonsElement": {
+      "type": "Slackblocks.Models.RadioButtonsElement",
+      "capability": "elements.radio_buttons"
+    },
+    "RawNumber": {
+      "type": "Slackblocks.Models.RawNumber",
+      "capability": "objects.raw_number"
+    },
+    "RawText": {
+      "type": "Slackblocks.Models.RawText",
+      "capability": "objects.raw_text"
+    },
+    "RichTextBlock": {
+      "type": "Slackblocks.Models.RichTextBlock",
+      "capability": "blocks.rich_text"
+    },
+    "RichTextChannel": {
+      "type": "Slackblocks.Models.RichTextChannel",
+      "capability": "rich_text.channel"
+    },
+    "RichTextCodeBlock": {
+      "type": "Slackblocks.Models.RichTextCodeBlock",
+      "capability": "rich_text.code_block"
+    },
+    "RichTextEmoji": {
+      "type": "Slackblocks.Models.RichTextEmoji",
+      "capability": "rich_text.emoji"
+    },
+    "RichTextInputElement": {
+      "type": "Slackblocks.Models.RichTextInputElement",
+      "capability": "elements.rich_text_input"
+    },
+    "RichTextLink": {
+      "type": "Slackblocks.Models.RichTextLink",
+      "capability": "rich_text.link"
+    },
+    "RichTextList": {
+      "type": "Slackblocks.Models.RichTextList",
+      "capability": "rich_text.list"
+    },
+    "RichTextQuote": {
+      "type": "Slackblocks.Models.RichTextQuote",
+      "capability": "rich_text.quote"
+    },
+    "RichTextSection": {
+      "type": "Slackblocks.Models.RichTextSection",
+      "capability": "rich_text.section"
+    },
+    "RichTextText": {
+      "type": "Slackblocks.Models.RichTextText",
+      "capability": "rich_text.text"
+    },
+    "RichTextUser": {
+      "type": "Slackblocks.Models.RichTextUser",
+      "capability": "rich_text.user"
+    },
+    "RichTextUserGroup": {
+      "type": "Slackblocks.Models.RichTextUserGroup",
+      "capability": "rich_text.user_group"
+    },
+    "SectionBlock": {
+      "type": "Slackblocks.Models.SectionBlock",
+      "capability": "blocks.section"
+    },
+    "SlackFile": {
+      "type": "Slackblocks.Models.SlackFile",
+      "capability": "objects.slack_file"
+    },
+    "SlackIcon": {
+      "type": "Slackblocks.Models.SlackIcon",
+      "capability": "objects.slack_icon"
+    },
+    "StaticMultiSelectElement": {
+      "type": "Slackblocks.Models.StaticMultiSelectElement",
+      "capability": "elements.multi_select_static"
+    },
+    "StaticSelectElement": {
+      "type": "Slackblocks.Models.StaticSelectElement",
+      "capability": "elements.select_static"
+    },
+    "TableBlock": {
+      "type": "Slackblocks.Models.TableBlock",
+      "capability": "blocks.table"
+    },
+    "TaskCardBlock": {
+      "type": "Slackblocks.Models.TaskCardBlock",
+      "capability": "blocks.task_card"
+    },
+    "TimePickerElement": {
+      "type": "Slackblocks.Models.TimePickerElement",
+      "capability": "elements.time_picker"
+    },
+    "Trigger": {
+      "type": "Slackblocks.Models.Trigger",
+      "capability": "objects.trigger"
+    },
+    "UrlInputElement": {
+      "type": "Slackblocks.Models.UrlInputElement",
+      "capability": "elements.url_input"
+    },
+    "UrlSource": {
+      "type": "Slackblocks.Models.UrlSource",
+      "capability": "elements.url_source"
+    },
+    "UserMultiSelectElement": {
+      "type": "Slackblocks.Models.UserMultiSelectElement",
+      "capability": "elements.multi_select_users"
+    },
+    "UserSelectElement": {
+      "type": "Slackblocks.Models.UserSelectElement",
+      "capability": "elements.select_users"
+    },
+    "VideoBlock": {
+      "type": "Slackblocks.Models.VideoBlock",
+      "capability": "blocks.video"
+    },
+    "WebhookMessage": {
+      "type": "Slackblocks.Models.WebhookMessage",
+      "capability": "messages.webhook_message"
+    },
+    "Workflow": {
+      "type": "Slackblocks.Models.Workflow",
+      "capability": "objects.workflow"
+    },
+    "WorkflowButtonElement": {
+      "type": "Slackblocks.Models.WorkflowButtonElement",
+      "capability": "elements.workflow_button"
+    }
+  },
+  "supporting_api": [
+    {
+      "api": "Slackblocks.Validation",
+      "reason": "Error categories, structured paths and checked results; no payload producers.",
+      "tests": [
+        "Boundaries.hs",
+        "Helpers.hs"
+      ]
+    },
+    {
+      "api": "Slackblocks.Json",
+      "reason": "Checked JSON ingress and exact decimal conversion; every modeled decoder is exercised by conformance.",
+      "tests": [
+        "Conformance.hs",
+        "Boundaries.hs",
+        "Helpers.hs"
+      ]
+    },
+    {
+      "api": "Slackblocks.Style",
+      "reason": "Style vocabulary support checked by each receiving native element.",
+      "tests": [
+        "Constructions.hs",
+        "Boundaries.hs"
+      ]
+    },
+    {
+      "api": "Slackblocks.Helpers",
+      "reason": "Pure accordion, pagination, workflow, color and Builder support outside the shared model inventory.",
+      "tests": [
+        "Helpers.hs"
+      ]
+    },
+    {
+      "api": "Slackblocks.Models roles/enums/PlanTask",
+      "reason": "Closed union conversions, matchers and enums support mapped models; PlanTask encodes only in plan context.",
+      "tests": [
+        "Constructions.hs",
+        "Boundaries.hs",
+        "bin/check compiler controls"
+      ]
+    }
+  ]
+}
diff --git a/conformance/skiplist.txt b/conformance/skiplist.txt
new file mode 100644
--- /dev/null
+++ b/conformance/skiplist.txt
diff --git a/consumer/Main.hs b/consumer/Main.hs
new file mode 100644
--- /dev/null
+++ b/consumer/Main.hs
@@ -0,0 +1,42 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main (main) where
+
+import Control.Monad (unless)
+import Data.Aeson (Value (..), encode, object, toJSON, (.=))
+import qualified Data.Map.Strict as Map
+import Slackblocks
+import qualified Slackblocks.Helpers as Helpers
+import qualified Slackblocks.Json as Json
+import qualified Slackblocks.Models as Models
+import qualified Slackblocks.Style as Style
+import qualified Slackblocks.Validation as Validation
+
+accept :: (Show e) => Either e a -> IO a
+accept = either (fail . show) pure
+
+main :: IO ()
+main = do
+  label <- accept (Models.buildPlainText (Models.plainText "Archive consumer 😀"))
+  heading <- accept (buildHeaderBlock (headerBlock label))
+  let fields =
+        (messagePayload "C_ARCHIVE")
+          { messagePayloadBlocks = Just [headerBlockAsBlock heading],
+            messagePayloadMetadata = Just (Map.singleton "opaque" (object ["integer" .= (9007199254740993 :: Integer), "false" .= False, "type" .= String "future"]))
+          }
+  payload <- accept (buildMessagePayload fields)
+  decoded <- accept (Json.decode (encode payload) :: Validation.Result MessagePayload)
+  unless (payload == decoded) (fail "Archive consumer lost JSON values")
+  edited <- accept (buildMessagePayload ((inspectMessagePayload payload) {messagePayloadText = Just "edited"}))
+  unless (messagePayloadText (inspectMessagePayload edited) == Just "edited") (fail "Checked editing failed")
+  case buildPlainText (plainText "") of
+    Left _ -> pure ()
+    Right _ -> fail "Invalid construction escaped validation"
+  case Json.decode "{\"type\":\"plain_text\",\"text\":\"a\",\"text\":\"b\"}" :: Validation.Result PlainText of
+    Left _ -> pure ()
+    Right _ -> fail "Duplicate keys escaped strict ingress"
+  let style = Style.richTextStyle {Style.styleBold = Just False}
+  unless (toJSON style == object ["bold" .= False]) (fail "Explicit style false disappeared")
+  color <- accept (Helpers.hexColor "#8800ff")
+  unless (Helpers.colorText color == "#8800ff") (fail "Native color helper failed")
+  putStrLn "External archive consumer: all public modules, typed payloads, exact opaque values, checked editing and invalid ingress passed."
diff --git a/dependencies/http-locked-9.6.7.json b/dependencies/http-locked-9.6.7.json
new file mode 100644
--- /dev/null
+++ b/dependencies/http-locked-9.6.7.json
@@ -0,0 +1,100 @@
+{
+  "compiler": "9.6.7",
+  "constraints": {
+    "OneTuple": "0.4.3",
+    "QuickCheck": "2.19.0.0",
+    "StateVar": "1.2.2",
+    "aeson": "2.3.2.0",
+    "ansi-terminal": "1.1.5",
+    "ansi-terminal-types": "1.1.3",
+    "appar": "0.1.8",
+    "assoc": "1.1.1",
+    "async": "2.2.6",
+    "base-orphans": "0.9.4",
+    "base16": "2.0",
+    "base16-bytestring": "1.0.2.0",
+    "base64": "2.1",
+    "base64-bytestring": "1.2.1.0",
+    "basement": "0.0.16",
+    "bifunctors": "5.6.3",
+    "blaze-builder": "0.4.4.1",
+    "byteorder": "1.0.4",
+    "case-insensitive": "1.2.1.0",
+    "cborg": "0.2.10.0",
+    "cereal": "0.5.8.3",
+    "character-ps": "0.1",
+    "colour": "2.3.7",
+    "comonad": "5.0.10",
+    "contravariant": "1.5.6",
+    "cookie": "0.5.1",
+    "crypton": "1.0.6",
+    "crypton-asn1-encoding": "0.10.0",
+    "crypton-asn1-parse": "0.10.0",
+    "crypton-asn1-types": "0.4.1",
+    "crypton-connection": "0.4.5",
+    "crypton-pem": "0.3.0",
+    "crypton-socks": "0.6.2",
+    "crypton-x509": "1.8.0",
+    "crypton-x509-store": "1.8.0",
+    "crypton-x509-system": "1.8.0",
+    "crypton-x509-validation": "1.8.0",
+    "data-default": "0.8.0.2",
+    "data-default-class": "0.2.0.0",
+    "data-fix": "0.3.4",
+    "distributive": "0.6.3",
+    "dlist": "1.0",
+    "ech-config": "0.0.1",
+    "half": "0.3.3",
+    "hashable": "1.5.1.0",
+    "hpke": "0.0.0",
+    "hsc2hs": "0.68.10",
+    "http-client": "0.7.19",
+    "http-client-tls": "0.3.6.4",
+    "http-types": "0.12.6",
+    "indexed-traversable": "0.1.5",
+    "indexed-traversable-instances": "0.1.2.1",
+    "integer-conversion": "0.1.1",
+    "integer-logarithms": "1.0.5",
+    "iproute": "1.7.15",
+    "memory": "0.18.0",
+    "mime-types": "0.1.2.2",
+    "network": "3.2.9.0",
+    "network-byte-order": "0.1.8",
+    "network-uri": "2.6.4.2",
+    "old-locale": "1.0.0.7",
+    "old-time": "1.1.1.0",
+    "optparse-applicative": "0.19.0.0",
+    "os-string": "2.0.11",
+    "prettyprinter": "1.7.2",
+    "prettyprinter-ansi-terminal": "1.1.4",
+    "primitive": "0.9.1.0",
+    "random": "1.3.1",
+    "scientific": "0.3.10.0",
+    "semialign": "1.4",
+    "semigroupoids": "6.0.2",
+    "serialise": "0.2.6.1",
+    "splitmix": "0.1.3.2",
+    "streaming-commons": "0.2.3.1",
+    "strict": "0.5.1",
+    "tagged": "0.8.11",
+    "tasty": "1.5.5",
+    "terminal-size": "0.3.4",
+    "text-iso8601": "0.2.0.0",
+    "text-short": "0.1.6.1",
+    "th-abstraction": "0.7.2.0",
+    "th-compat": "0.1.7",
+    "these": "1.2.1",
+    "time-compat": "1.9.9",
+    "time-hourglass": "0.3.0",
+    "tls": "2.2.2",
+    "transformers-compat": "0.8",
+    "unix-time": "0.4.17",
+    "unordered-containers": "0.2.21",
+    "uuid-types": "1.0.6.1",
+    "vector": "0.13.2.0",
+    "vector-stream": "0.1.0.1",
+    "witherable": "0.5",
+    "zlib": "0.7.1.1"
+  },
+  "index_state": "2026-10-25T05:16:07Z"
+}
diff --git a/dependencies/http-lower.json b/dependencies/http-lower.json
new file mode 100644
--- /dev/null
+++ b/dependencies/http-lower.json
@@ -0,0 +1,14 @@
+{
+  "compiler": "9.6.7",
+  "constraints": {
+    "aeson": "2.2.0.0",
+    "bytestring": "0.11.5.4",
+    "containers": "0.6.7",
+    "http-client": "0.7.17",
+    "http-client-tls": "0.3.6.3",
+    "http-types": "0.12.4",
+    "scientific": "0.3.7.0",
+    "text": "2.0.2",
+    "vector": "0.13.0.0"
+  }
+}
diff --git a/dependencies/locked-9.14.1.json b/dependencies/locked-9.14.1.json
new file mode 100644
--- /dev/null
+++ b/dependencies/locked-9.14.1.json
@@ -0,0 +1,54 @@
+{
+  "index_state": "2026-10-25T05:16:07Z",
+  "compiler": "9.14.1",
+  "constraints": {
+    "OneTuple": "0.4.3",
+    "QuickCheck": "2.16.0.0",
+    "StateVar": "1.2.2",
+    "aeson": "2.3.2.0",
+    "ansi-terminal": "1.1.5",
+    "ansi-terminal-types": "1.1.3",
+    "assoc": "1.1.1",
+    "base-orphans": "0.9.4",
+    "bifunctors": "5.6.3",
+    "character-ps": "0.1",
+    "colour": "2.3.7",
+    "comonad": "5.0.10",
+    "contravariant": "1.5.6",
+    "data-fix": "0.3.4",
+    "distributive": "0.6.3",
+    "dlist": "1.0",
+    "hashable": "1.5.1.0",
+    "hsc2hs": "0.68.10",
+    "indexed-traversable": "0.1.5",
+    "indexed-traversable-instances": "0.1.2.1",
+    "integer-conversion": "0.1.1",
+    "integer-logarithms": "1.0.5",
+    "network-uri": "2.6.4.2",
+    "optparse-applicative": "0.19.0.0",
+    "prettyprinter": "1.7.2",
+    "prettyprinter-ansi-terminal": "1.1.4",
+    "primitive": "0.9.1.0",
+    "random": "1.3.1",
+    "scientific": "0.3.10.0",
+    "semialign": "1.4",
+    "semigroupoids": "6.0.2",
+    "splitmix": "0.1.3.2",
+    "strict": "0.5.1",
+    "tagged": "0.8.11",
+    "tasty": "1.5.5",
+    "terminal-size": "0.3.4",
+    "text-iso8601": "0.2.0.0",
+    "text-short": "0.1.6.1",
+    "th-abstraction": "0.7.2.0",
+    "th-compat": "0.1.7",
+    "these": "1.2.1",
+    "time-compat": "1.9.9",
+    "transformers-compat": "0.8",
+    "unordered-containers": "0.2.21",
+    "uuid-types": "1.0.6.1",
+    "vector": "0.13.2.0",
+    "vector-stream": "0.1.0.1",
+    "witherable": "0.5"
+  }
+}
diff --git a/dependencies/locked-9.6.7.json b/dependencies/locked-9.6.7.json
new file mode 100644
--- /dev/null
+++ b/dependencies/locked-9.6.7.json
@@ -0,0 +1,55 @@
+{
+  "index_state": "2026-10-25T05:16:07Z",
+  "compiler": "9.6.7",
+  "constraints": {
+    "OneTuple": "0.4.3",
+    "QuickCheck": "2.16.0.0",
+    "StateVar": "1.2.2",
+    "aeson": "2.3.2.0",
+    "ansi-terminal": "1.1.5",
+    "ansi-terminal-types": "1.1.3",
+    "assoc": "1.1.1",
+    "base-orphans": "0.9.4",
+    "bifunctors": "5.6.3",
+    "character-ps": "0.1",
+    "colour": "2.3.7",
+    "comonad": "5.0.10",
+    "contravariant": "1.5.6",
+    "data-fix": "0.3.4",
+    "distributive": "0.6.3",
+    "dlist": "1.0",
+    "hashable": "1.5.1.0",
+    "hsc2hs": "0.68.10",
+    "indexed-traversable": "0.1.5",
+    "indexed-traversable-instances": "0.1.2.1",
+    "integer-conversion": "0.1.1",
+    "integer-logarithms": "1.0.5",
+    "network-uri": "2.6.4.2",
+    "optparse-applicative": "0.19.0.0",
+    "os-string": "2.0.11",
+    "prettyprinter": "1.7.2",
+    "prettyprinter-ansi-terminal": "1.1.4",
+    "primitive": "0.9.1.0",
+    "random": "1.3.1",
+    "scientific": "0.3.10.0",
+    "semialign": "1.4",
+    "semigroupoids": "6.0.2",
+    "splitmix": "0.1.3.2",
+    "strict": "0.5.1",
+    "tagged": "0.8.11",
+    "tasty": "1.5.5",
+    "terminal-size": "0.3.4",
+    "text-iso8601": "0.2.0.0",
+    "text-short": "0.1.6.1",
+    "th-abstraction": "0.7.2.0",
+    "th-compat": "0.1.7",
+    "these": "1.2.1",
+    "time-compat": "1.9.9",
+    "transformers-compat": "0.8",
+    "unordered-containers": "0.2.21",
+    "uuid-types": "1.0.6.1",
+    "vector": "0.13.2.0",
+    "vector-stream": "0.1.0.1",
+    "witherable": "0.5"
+  }
+}
diff --git a/dependencies/lower.json b/dependencies/lower.json
new file mode 100644
--- /dev/null
+++ b/dependencies/lower.json
@@ -0,0 +1,12 @@
+{
+  "compiler": "9.6.7",
+  "constraints": {
+    "aeson": "2.2.0.0",
+    "bytestring": "0.11.5.4",
+    "text": "2.0.2",
+    "containers": "0.6.7",
+    "scientific": "0.3.7.0",
+    "vector": "0.13.0.0",
+    "QuickCheck": "2.14.3"
+  }
+}
diff --git a/generator/generate.py b/generator/generate.py
new file mode 100644
--- /dev/null
+++ b/generator/generate.py
@@ -0,0 +1,254 @@
+#!/usr/bin/env python3
+"""Generate Haskell's native model API directly from the shared contract."""
+import argparse
+import json
+from pathlib import Path
+import subprocess
+
+ROOT = Path(__file__).resolve().parents[2]
+PACKAGE = ROOT / "haskell"
+NAMES = {"Text": "TextObject", "Option": "SelectOption", "OptionGroup": "SelectOptionGroup", "Confirmation": "ConfirmationDialogue"}
+
+
+def name(value):
+    return NAMES.get(value, value)
+
+
+def lower(value):
+    return value[0].lower() + value[1:]
+
+
+def camel(value):
+    return "".join(part[:1].upper() + part[1:].lower() for part in value.split("_"))
+
+
+def string(value):
+    # Haskell string literals use decimal Unicode escapes rather than JSON's \u.
+    return '"' + ''.join('\\"' if c == '"' else '\\\\' if c == '\\' else '\\n' if c == '\n' else c if 32 <= ord(c) < 127 else '\\' + str(ord(c)) + '\\&' for c in value) + '"'
+
+
+def hslist(values):
+    return "[" + ", ".join(values) + "]"
+
+
+def native(field):
+    kind = field["kind"]
+    if kind == "string": return "Text"
+    if kind == "boolean": return "Bool"
+    if kind in {"int", "long"}: return "Integer"
+    if kind in {"number", "double"}: return "Scientific"
+    if kind in {"object", "text", "enum"}: return name(field["type"])
+    if kind == "list" and field["type"] == "TaskCardBlock": return "[PlanTask]"
+    if kind in {"list", "textList"}: return "[" + name(field["type"]) + "]"
+    if kind == "rows": return "[[" + name(field["type"]) + "]]"
+    if kind == "stringList": return "[Text]"
+    if kind == "map": return "Map Text Value"
+    if kind == "style": return "RichTextStyle"
+    raise ValueError(f"Unknown field kind {kind}")
+
+
+def fieldname(t, f):
+    return lower(name(t["name"])) + f["method"][:1].upper() + f["method"][1:]
+
+
+def default(t, f, enums):
+    value = t.get("defaults", {}).get(f["wire"])
+    if value is None: return "Nothing"
+    if f["kind"] == "enum": value = name(f["type"]) + camel(value)
+    elif isinstance(value, bool): value = str(value)
+    elif isinstance(value, str): value = string(value)
+    else: value = str(value)
+    return "Just (" + value + ")"
+
+
+def resolve(model):
+    names = [name(x['name']) for key in ('types', 'enums', 'interfaces') for x in model[key]]
+    if len(names) != len(set(names)): raise ValueError("Type name collision")
+    for enum in model['enums']:
+        constructors = [camel(c['wire']) for c in enum['constants']]
+        if not constructors or len(constructors) != len(set(constructors)):
+            raise ValueError("Enum constructor collision or empty enum")
+    roles = {r['name']: [] for r in model['interfaces']}
+    for t in model['types']:
+        fields = [fieldname(t, f) for f in t['fields']]
+        if len(fields) != len(set(fields)): raise ValueError("Field name collision")
+        for f in t['fields']:
+            native(f)
+            if f.get('type') and name(f['type']) not in names: raise ValueError("Unknown field type")
+        memberships = set(t['implements'])
+        if t['package'] == 'block': memberships.add('Block')
+        if t['package'] == 'element': memberships.add('Element')
+        for r in sorted(memberships): roles[r].append(t)
+    for role, members in roles.items():
+        tags = [t['wireType'] for t in members]
+        if len(set(tags)) != len(tags): raise ValueError(f"Ambiguous role {role}")
+    return {r: ts for r, ts in roles.items() if ts}
+
+
+def generate(model):
+    roles = resolve(model)
+    exports = ['packageVersion', 'specVersion']
+    lines = []
+    enums = {e['name']: e for e in model['enums']}
+    for e in model['enums']:
+        n = name(e['name'])
+        exports.append(n + '(..)')
+        cs = [(n + camel(c['wire']), c['wire']) for c in e['constants']]
+        lines += ['-- | ' + e['description'], 'data ' + n + '\n  = ' + '\n  | '.join(c + ' -- ^ Slack wire value: @' + w + '@.' for c, w in cs) + '\n  deriving (Eq, Ord, Show, Enum, Bounded)', f'instance ToJSON {n} where']
+        lines += [f'  toJSON {c} = String {string(w)}' for c, w in cs]
+        lines += [f'instance FromSlackJSON {n} where', '  fromSlackJSON value = case value of']
+        lines += [f'    String {string(w)} -> Right {c}' for c, w in cs]
+        lines += ['    _ -> failure TypeMismatch [] "enum" "unknown enumeration value"', f'instance FromJSON {n} where parseJSON = checkedParser', '']
+    for t in model['types']:
+        n = name(t['name']); factory = lower(n); fs = t['fields']; required = [f for f in fs if f.get('required')]
+        exports += [n, n+'Fields(..)', factory, 'build'+n, 'inspect'+n]
+        lines += ['-- | ' + t['description'], '--', '-- <' + t['docUrl'] + '>']
+        lines += ['-- ' + r for r in t['rules']]
+        lines += [f'newtype {n} = Checked{n} {n}Fields deriving (Eq, Show)', f'-- | Editable construction input. Call \'build{n}\' before encoding.', f'data {n}Fields = {n}Fields']
+        fielddecls = [(fieldname(t,f), native(f) if f.get('required') else 'Maybe ('+native(f)+')',f['description'] + (' Required.' if f.get('required') else ' Optional; Nothing omits the field. Factory default: @' + default(t, f, enums) + '@.') + (' Shared limit: @' + f['limits'] + '@.' if f.get('limits') else '')) for f in fs]
+        fielddecls.append((factory+'Extensions', 'Map Text Value', 'Unmodeled extension fields. Reserved and modeled keys are rejected.'))
+        for i,(fn,typ,desc) in enumerate(fielddecls):
+            lines += [('  { ' if i==0 else '  , ') + fn + ' :: ' + typ + ' -- ^ ' + desc]
+        lines += ['  } deriving (Eq, Show)', f'-- | Start editable fields for {n}; required arguments follow the signature.', '-- Optional fields start absent except the explicit shared defaults; no block ID is invented.', f'-- Finish with \'build{n}\' to validate before JSON encoding.', f'{factory} :: ' + ' -> '.join([native(f) for f in required]+[n+'Fields'])]
+        args = ['arg'+str(i) for i in range(len(required))]
+        values = []
+        for f in fs:
+            values.append(args[required.index(f)] if f.get('required') else default(t,f,enums))
+        values.append('Map.empty')
+        lines += [factory+' '+ ' '.join(args) + ' = '+n+'Fields '+' '.join('('+v+')' for v in values),
+                  f'-- | Inspect immutable construction fields. Rebuild edits with \'build{n}\' to revalidate.', f'inspect{n} :: {n} -> {n}Fields',f'inspect{n} (Checked{n} fields) = fields',
+                  f'-- | Validate fields, extensions and receiving context. Return structured errors or a checked {n}.', f'build{n} :: {n}Fields -> Result {n}',f'build{n} fields = do',
+                  f'  checkExtensions {hslist(string(f["wire"]) for f in fs)} ({factory}Extensions fields)',
+                  f'  validateFields {string(t["name"])} (wire{n} fields)',f'  pure (Checked{n} fields)',
+                  f'wire{n} :: {n}Fields -> Value',f'wire{n} fields = object $']
+        pieces = ['['+'"type" .= ('+string(t['wireType'])+' :: Text)'+']'] if t['wireType'] else ['[]']
+        for f in fs:
+            value = fieldname(t,f)+' fields'
+            if t['name']=='PlanBlock' and f['wire']=='tasks': value='map (stripTag . toJSON) ('+value+')'
+            if f.get('required'): piece='['+string(f['wire'])+' .= ('+value+')]'
+            else: piece='optional '+string(f['wire'])+' ('+value+')'
+            pieces.append(piece)
+        pieces.append('extensionPairs ('+factory+'Extensions fields)')
+        if t['name'] == 'TaskCardBlock':
+            position = lines.index('  pure (CheckedTaskCardBlock fields)')
+            lines.insert(position, '  standaloneTask (wireTaskCardBlock fields)')
+        lines += ['    '+ ' <> '.join(pieces),f'instance ToJSON {n} where toJSON (Checked{n} fields) = wire{n} fields']
+        if t['name'] == 'TaskCardBlock':
+            lines += ['instance FromSlackJSON TaskCardBlock where fromSlackJSON = decodeTaskCard buildTaskCardBlock',
+                      'decodeTaskCard :: (TaskCardBlockFields -> Result a) -> Value -> Result a',
+                      'decodeTaskCard constructor value = do']
+        else:
+            lines += [f'instance FromSlackJSON {n} where', '  fromSlackJSON value = do']
+        lines += [f'    fields <- wireObject {string(t["wireType"])} value']
+        if any(f['kind'] in {'int', 'long'} and f.get('limits') for f in fs):
+            lines += [f'    checkIntegerBounds {string(t["name"])} (Object fields)']
+        for f in fs:
+            method = 'required' if f.get('required') else 'optionalField'
+            expression = f'{method} {string(f["wire"])} fields'
+            if f['kind'] in {'text','textList'} or f.get('coerce') == 'decimal_string':
+                expression = f'{method} {string(f["wire"])} (coerceField {string(f["wire"])} {string(f.get("coerce","plain_text"))} fields)'
+            if t['name']=='PlanBlock' and f['wire']=='tasks':
+                expression = f'{method} "tasks" (restoreTasks fields)'
+            lines += [f'    parsed{f["method"][:1].upper()+f["method"][1:]} <- {expression}']
+        builder = 'constructor' if t['name'] == 'TaskCardBlock' else 'build'+n
+        lines += [f'    {builder} ({n}Fields '+' '.join('parsed'+f['method'][:1].upper()+f['method'][1:] for f in fs)+' (remaining '+hslist(string(f['wire']) for f in fs)+' fields))',
+                  f'instance FromJSON {n} where parseJSON = checkedParser','']
+    exports += ['PlanTask', 'buildPlanTask', 'inspectPlanTask']
+    task_fields = next(t['fields'] for t in model['types'] if t['name'] == 'TaskCardBlock')
+    lines += ['-- | A task in a plan. Unlike a standalone task card, it may be pending.',
+              'newtype PlanTask = CheckedPlanTask TaskCardBlock deriving (Eq, Show)',
+              '-- | Check a task for a plan, where pending is allowed. This value cannot enter the Block role.', 'buildPlanTask :: TaskCardBlockFields -> Result PlanTask',
+              'buildPlanTask fields = do',
+              '  checkExtensions '+hslist(string(f['wire']) for f in task_fields)+' (taskCardBlockExtensions fields)',
+              '  validateFields "TaskCardBlock" (wireTaskCardBlock fields)',
+              '  pure (CheckedPlanTask (CheckedTaskCardBlock fields))',
+              '-- | Inspect a plan task; call buildPlanTask after editing its fields.', 'inspectPlanTask :: PlanTask -> TaskCardBlockFields',
+              'inspectPlanTask (CheckedPlanTask task) = inspectTaskCardBlock task',
+              'instance ToJSON PlanTask where toJSON (CheckedPlanTask task) = stripTag (toJSON task)',
+              'instance FromSlackJSON PlanTask where fromSlackJSON = decodeTaskCard buildPlanTask . restoreTask',
+              'instance FromJSON PlanTask where parseJSON = checkedParser', '']
+    for role, members in roles.items():
+        n=name(role); exports.append(n)
+        lines += ['-- | Closed role: only checked members may enter this position.',f'data {n} = '+' | '.join(n+name(t['name'])+' '+name(t['name']) for t in members)+' deriving (Eq, Show)',f'instance ToJSON {n} where']
+        lines += ['  toJSON ('+n+name(t['name'])+' value) = toJSON value' for t in members]
+        lines += [f'instance FromSlackJSON {n} where','  fromSlackJSON value = do','    tag <- wireTag value','    case tag of']
+        for t in members:
+            tn=name(t['name']); fn=lower(tn)+'As'+role
+            check = role=='Block' and t['name']=='TaskCardBlock'
+            lines += ['      '+string(t['wireType'])+' -> '+ ('fromSlackJSON value >>= '+fn if check else n+tn+' <$> fromSlackJSON value')]
+        lines += ['      _ -> failure TypeMismatch [Field "type"] "role" "unsupported child type"',f'instance FromJSON {n} where parseJSON = checkedParser']
+        for t in members:
+            tn=name(t['name']); fn=lower(tn)+'As'+role; match='match'+n+tn
+            exports += [fn,match]
+            lines += [f'-- | Place a checked {tn} in the {n} role. The receiving parent retains its contextual validation.']
+            if role=='Block' and t['name']=='TaskCardBlock':
+                lines += [fn+' :: '+tn+' -> Result '+n,fn+' value = do','  standaloneTask (toJSON value)','  pure ('+n+tn+' value)']
+            else: lines += [fn+' :: '+tn+' -> '+n,fn+' = '+n+tn]
+            lines += [f'-- | Return the {tn} member when present, otherwise Nothing; no unchecked cast.', match+' :: '+n+' -> Maybe '+tn,match+' ('+n+tn+' value) = Just value']
+            if len(members)>1: lines.append(match+' _ = Nothing')
+        lines.append('')
+    header = ['-- Generated by haskell/generator/generate.py. Edit the shared contract instead.', '-- | Native checked models, construction fields and closed role conversions.', 'module Slackblocks.Models', '  ( '+ '\n  , '.join(exports)+'\n  ) where',
+              'import Data.Aeson (FromJSON(..), ToJSON(..), Value(..), object, (.=))','import Data.Map.Strict (Map)','import qualified Data.Map.Strict as Map',
+              'import Data.Scientific (Scientific)','import Data.Text (Text)','import Slackblocks.Validation','import Slackblocks.Json (FromSlackJSON(..))','import Slackblocks.Internal.Wire',
+              'import Slackblocks.Internal.Rules (validateFields, checkIntegerBounds, standaloneTask)','import Slackblocks.Style',
+              '-- | Coordinated package version, distinct from the shared contract.', 'packageVersion :: Text', '-- | Shared Block Kit contract version implemented by this package.', 'specVersion :: Text','packageVersion = "3.1.0"', 'specVersion = '+string(json.loads((ROOT/'spec/manifest.json').read_text(encoding="utf-8"))['spec_version']),'']
+    return '\n'.join(header+lines)+'\n'
+
+
+def limits_flat(values, prefix=''):
+    out=[]
+    for k,v in values.items():
+        key=prefix+'.'+k if prefix else k
+        if isinstance(v,dict): out.extend(limits_flat(v,key))
+        elif type(v) is int: out.append((key,v))
+        else: raise ValueError('Non-integral limit '+key)
+    return out
+
+
+def schema(model, limits, vocab):
+    lines=['-- Generated shared limits, vocabulary and local field constraints.', 'module Slackblocks.Internal.Schema (fieldConstraints, limits, icons, surfaceBlocks) where',
+           'import Data.Text (Text)', 'fieldConstraints :: Text -> [(Text, Text, Text, [(Text, Integer)], [Text])]', 'fieldConstraints owner = case owner of']
+    for t in model['types']:
+        constraints=[]
+        for f in t['fields']:
+            prefix=f.get('limits','')
+            if t['name'] in {'PlainText','MarkdownText'} and f['wire']=='text': prefix='text'
+            bounds=limits
+            if prefix:
+                for part in prefix.split('.'): bounds=bounds[part]
+                supported = {'min_length', 'max_length', 'min_items', 'max_items', 'min', 'max', 'exclusive_min', 'exclusive_max', 'item_max_length'}
+                if any(type(v) is not int or k not in supported for k,v in bounds.items()): raise ValueError('Invalid bound '+prefix)
+            else: bounds={}
+            flags=f.get('flags',[])
+            if bounds or flags:
+                constraints.append('('+', '.join([string(f['wire']),string(f['kind']),string(prefix),hslist('('+string(k)+', '+str(v)+')' for k,v in bounds.items()),hslist(string(x) for x in flags)])+')')
+        lines.append('  '+string(t['name'])+' -> '+hslist(constraints))
+    lines += ['  _ -> []','limits :: [(Text, Integer)]','limits = '+hslist('('+string(k)+', '+str(v)+')' for k,v in limits_flat(limits)),
+              'icons :: [Text]','icons = '+hslist(string(x) for x in vocab['slack_icon_names']), 'surfaceBlocks :: Text -> [Text]','surfaceBlocks surface = case surface of']
+    lines += ['  '+string(k)+' -> '+hslist(string(v) for v in vs) for k,vs in vocab['surface_block_types'].items()]
+    lines += ['  _ -> []']
+    return '\n'.join(lines)+'\n'
+
+
+def main():
+    parser=argparse.ArgumentParser();parser.add_argument('--check',action='store_true');args=parser.parse_args()
+    model=json.loads((ROOT/'spec/model.json').read_text(encoding="utf-8"));limits=json.loads((ROOT/'spec/limits.json').read_text(encoding="utf-8"));vocab=json.loads((ROOT/'spec/vocabulary.json').read_text(encoding="utf-8"))
+    outputs={PACKAGE/'src/Slackblocks/Models.hs':generate(model),PACKAGE/'src/Slackblocks/Internal/Schema.hs':schema(model,limits,vocab)}
+    for path in (PACKAGE/'src').rglob('*.hs'):
+        if path not in outputs and path.read_text(encoding="utf-8").startswith(('-- Generated by haskell/generator/', '-- Generated shared limits')):
+            raise SystemExit('Obsolete generated output: '+str(path))
+    stale=[]
+    version = subprocess.check_output(['ormolu', '--version'], text=True)
+    if version.splitlines()[0] != 'ormolu 0.7.7.0':
+        raise SystemExit('Generation requires ormolu 0.7.7.0')
+    for path,value in outputs.items():
+        value = subprocess.run(['ormolu', '--stdin-input-file', str(path)], input=value,
+                               text=True, capture_output=True, check=True).stdout
+        if args.check:
+            if not path.is_file() or path.read_text(encoding="utf-8")!=value: stale.append(str(path.relative_to(ROOT)))
+        else:
+            path.parent.mkdir(parents=True,exist_ok=True);path.write_text(value, encoding="utf-8")
+    if stale: raise SystemExit('Generated output differs: '+', '.join(stale))
+
+
+if __name__=='__main__': main()
diff --git a/generator/test_generate.py b/generator/test_generate.py
new file mode 100644
--- /dev/null
+++ b/generator/test_generate.py
@@ -0,0 +1,57 @@
+"""Fail closed when shared metadata outgrows the native generator."""
+import copy
+import json
+import unittest
+import generate
+
+
+class GeneratorContracts(unittest.TestCase):
+    def setUp(self):
+        self.model = json.loads((generate.ROOT / "spec/model.json").read_text(encoding="utf-8"))
+
+    def test_every_native_model_resolves(self):
+        roles = generate.resolve(self.model)
+        self.assertEqual(set(roles), {r['name'] for r in self.model['interfaces']})
+
+    def test_unknown_field_kind(self):
+        self.model['types'][0]['fields'][0]['kind'] = 'future-unimplemented-kind'
+        with self.assertRaisesRegex(ValueError, 'Unknown field kind'):
+            generate.resolve(self.model)
+
+    def test_unknown_referenced_type(self):
+        field = next(f for t in self.model['types'] for f in t['fields'] if 'type' in f)
+        field['type'] = 'FutureModel'
+        with self.assertRaisesRegex(ValueError, 'Unknown field type'):
+            generate.resolve(self.model)
+
+    def test_native_type_collision(self):
+        self.model['types'].append(copy.deepcopy(self.model['types'][0]))
+        with self.assertRaisesRegex(ValueError, 'Type name collision'):
+            generate.resolve(self.model)
+
+    def test_native_field_collision(self):
+        self.model['types'][0]['fields'].append(copy.deepcopy(self.model['types'][0]['fields'][0]))
+        with self.assertRaisesRegex(ValueError, 'Field name collision'):
+            generate.resolve(self.model)
+
+    def test_role_discriminator_collision(self):
+        blocks = [t for t in self.model['types'] if t['package'] == 'block']
+        blocks[1]['wireType'] = blocks[0]['wireType']
+        with self.assertRaisesRegex(ValueError, 'Ambiguous role'):
+            generate.resolve(self.model)
+
+    def test_enum_constructor_collision(self):
+        self.model['enums'][0]['constants'].append(copy.deepcopy(self.model['enums'][0]['constants'][0]))
+        with self.assertRaisesRegex(ValueError, 'Enum constructor collision'):
+            generate.resolve(self.model)
+
+    def test_new_constraint_cannot_be_silently_ignored(self):
+        limits = json.loads((generate.ROOT / 'spec/limits.json').read_text(encoding="utf-8"))
+        vocabulary = json.loads((generate.ROOT / 'spec/vocabulary.json').read_text(encoding="utf-8"))
+        limits['text']['future_rule'] = 1
+        with self.assertRaisesRegex(ValueError, 'Invalid bound'):
+            generate.schema(self.model, limits, vocabulary)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/integrations/README.md b/integrations/README.md
new file mode 100644
--- /dev/null
+++ b/integrations/README.md
@@ -0,0 +1,37 @@
+# Native Haskell sending
+
+`src/Send.hs` is the actual example transport exercised by `python bin/http-check`.
+It takes any checked `ToJSON` payload and an application-owned `http-client`
+manager. The executable in `src/Main.hs` constructs messages, webhooks, response
+URL replies, modal views and App Home views through the native Slackblocks API.
+It runs only against the loopback endpoint supplied by the checker.
+
+Use `postPayload manager SlackAPI endpoint (Just token) payload` for Slack Web API
+calls and `postPayload manager Webhook webhookURL Nothing payload` for incoming
+webhooks or response URLs. Create production managers with `tlsManagerSettings`.
+Do not log credentials or webhook URLs. `views.open` and `views.publish` require
+their API envelope (`trigger_id`/`user_id` and `view`) around a checked view.
+
+The example requires `http-client >=0.7.17 && <0.8`,
+`http-client-tls >=0.3.6.3 && <0.4` and `http-types >=0.12.4 && <0.13`.
+The minimum compiler is the library's GHC 9.6.7. CI independently resolves and
+executes exact-lower, locked and latest transport graphs, with latest also on
+GHC 9.14.1. `python bin/http-check --profile lower` selects the minimum graph;
+`locked` selects the recorded GHC 9.6.7 graph; the default is `latest`.
+
+The loopback server asserts complete JSON payload equality, arbitrary-precision
+metadata, Unicode, explicit false, content type and authentication boundaries.
+HTTP 429/500, redirects, malformed success bodies, `ok:false` and webhook errors
+must fail. There are no automatic retries, so an ambiguous response cannot cause
+an unbounded replay. Applications can apply their own bounded retry/deduplication
+policy. These tests exercise a real HTTP client, but do not claim live Slack or
+TLS-server acceptance.
+
+The current [slack-web 2.3.0.0 source](https://hackage.haskell.org/package/slack-web-2.3.0.0/src/src/Web/Slack/Chat.hs)
+offers text-encoded blocks/attachments in `PostMsgReq`, but has no message
+metadata field. Its [current dependency bounds](https://hackage.haskell.org/package/slack-web-2.3.0.0/slack-web.cabal)
+also require `base <4.22` and `aeson <2.3`, excluding this library's current
+GHC 9.14 lane. The older `slack-api` targets Slack's RTM API. Neither provides
+an intact, current-range adapter for this package, so the supported sending path
+uses native JSON over HTTP rather than a conversion that drops fields. This
+does not change either package's own support policy.
diff --git a/integrations/src/Main.hs b/integrations/src/Main.hs
new file mode 100644
--- /dev/null
+++ b/integrations/src/Main.hs
@@ -0,0 +1,41 @@
+module Main (main) where
+
+import Control.Exception (IOException, try)
+import Control.Monad (forM_, unless)
+import Data.Aeson (Value (..), object, (.=))
+import Data.Map.Strict qualified as Map
+import Network.HTTP.Client (newManager)
+import Network.HTTP.Client.TLS (tlsManagerSettings)
+import Send
+import Slackblocks
+import System.Environment (getArgs)
+
+accept :: (Show e) => Either e a -> IO a
+accept = either (fail . show) pure
+
+main :: IO ()
+main = do
+  args <- getArgs
+  endpoint <- case args of [value] -> pure value; _ -> fail "Expected local acceptance endpoint"
+  manager <- newManager tlsManagerSettings
+  title <- accept (buildPlainText (plainText "Hello 😀"))
+  heading <- accept (buildHeaderBlock (headerBlock title))
+  let blocks = [headerBlockAsBlock heading]
+      metadata = Map.singleton "opaque" (object ["type" .= String "unmodeled", "integer" .= (9007199254740993 :: Integer), "flag" .= False, "empty" .= object []])
+  attachmentValue <- accept (buildAttachment ((attachment blocks) {attachmentColor = Just "#8800ff"}))
+  message <- accept (buildMessagePayload ((messagePayload "C_TEST") {messagePayloadBlocks = Just blocks, messagePayloadMetadata = Just metadata, messagePayloadAttachments = Just [attachmentValue]}))
+  webhook <- accept (buildWebhookMessage (webhookMessage {webhookMessageBlocks = Just blocks, webhookMessageMetadata = Just metadata}))
+  response <- accept (buildMessageResponse (messageResponse {messageResponseBlocks = Just blocks, messageResponseReplaceOriginal = Just False}))
+  view <- accept (buildModalView (modalView title blocks))
+  home <- accept (buildHomeTabView (homeTabView blocks))
+  postPayload manager SlackAPI (endpoint <> "/chat.postMessage") (Just "test-token") message
+  postPayload manager Webhook (endpoint <> "/webhook") Nothing webhook
+  postPayload manager Webhook (endpoint <> "/response") Nothing response
+  postPayload manager SlackAPI (endpoint <> "/views.open") (Just "test-token") (object ["trigger_id" .= String "trigger-test", "view" .= view])
+  postPayload manager SlackAPI (endpoint <> "/views.publish") (Just "test-token") (object ["user_id" .= String "U_TEST", "view" .= home])
+  forM_ ["/api-error", "/malformed", "/rate-limited", "/server-error", "/redirect"] $ \path -> do
+    result <- try (postPayload manager SlackAPI (endpoint <> path) (Just "test-token") message) :: IO (Either IOException ())
+    unless (either (const True) (const False) result) (fail ("Expected failure for " <> path))
+  result <- try (postPayload manager Webhook (endpoint <> "/webhook-error") Nothing webhook) :: IO (Either IOException ())
+  unless (either (const True) (const False) result) (fail "Expected webhook failure")
+  putStrLn "Five intact payload surfaces and six HTTP/API error responses checked."
diff --git a/integrations/src/Send.hs b/integrations/src/Send.hs
new file mode 100644
--- /dev/null
+++ b/integrations/src/Send.hs
@@ -0,0 +1,36 @@
+-- | Example application transport. The core slackblocks package has no HTTP
+-- dependency; applications own credentials, endpoints, retries and routing.
+module Send (ResponseKind (..), postPayload) where
+
+import Control.Monad (unless)
+import Data.Aeson (ToJSON, Value (..), eitherDecode, encode)
+import Data.Aeson.KeyMap qualified as KM
+import Data.ByteString qualified as B
+import Network.HTTP.Client
+import Network.HTTP.Types.Status (statusCode)
+
+data ResponseKind = SlackAPI | Webhook
+
+-- | Send an intact JSON object. API calls use a bearer token; webhook/response
+-- URLs use Nothing. Do not log either credentials or credential-bearing URLs.
+postPayload :: (ToJSON a) => Manager -> ResponseKind -> String -> Maybe B.ByteString -> a -> IO ()
+postPayload manager kind endpoint token payload = do
+  baseRequest <- parseRequest endpoint
+  let request =
+        baseRequest
+          { method = "POST",
+            requestHeaders =
+              [("Content-Type", "application/json; charset=utf-8")]
+                <> maybe [] (\value -> [("Authorization", "Bearer " <> value)]) token,
+            requestBody = RequestBodyLBS (encode payload),
+            redirectCount = 0,
+            checkResponse = \_ _ -> pure ()
+          }
+  response <- httpLbs request manager
+  let code = statusCode (responseStatus response)
+  unless (code >= 200 && code < 300) (fail ("Slack HTTP status " <> show code))
+  case kind of
+    Webhook -> unless (responseBody response == "ok") (fail "Unexpected webhook response")
+    SlackAPI -> case eitherDecode (responseBody response) of
+      Right (Object result) | KM.lookup "ok" result == Just (Bool True) -> pure ()
+      _ -> fail "Slack API did not confirm success"
diff --git a/integrations/transport.cabal b/integrations/transport.cabal
new file mode 100644
--- /dev/null
+++ b/integrations/transport.cabal
@@ -0,0 +1,24 @@
+cabal-version: 3.0
+name: slackblocks-transport-check
+version: 0.0.0
+build-type: Simple
+license: MIT
+synopsis: Isolated HTTP consumer checks for slackblocks
+
+executable transport-check
+  main-is: Main.hs
+  other-modules: Send
+  hs-source-dirs: src
+  default-language: GHC2021
+  default-extensions: OverloadedStrings
+  ghc-options: -Wall -Wcompat
+  build-depends:
+    base >=4.18 && <4.23,
+    slackblocks ==3.1.0,
+    aeson >=2.2 && <2.4,
+    bytestring >=0.11.5.4 && <0.13,
+    containers >=0.6.7 && <0.9,
+    text >=2.0.2 && <2.2,
+    http-client >=0.7.17 && <0.8,
+    http-client-tls >=0.3.6.3 && <0.4,
+    http-types >=0.12.4 && <0.13
diff --git a/slackblocks.cabal b/slackblocks.cabal
new file mode 100644
--- /dev/null
+++ b/slackblocks.cabal
@@ -0,0 +1,112 @@
+cabal-version: 3.0
+name: slackblocks
+version: 3.1.0
+synopsis: Typed and validated Slack Block Kit composition
+description: Immutable Block Kit values, checked construction, and Aeson encoding.
+homepage: https://github.com/nicklambourne/slackblocks
+bug-reports: https://github.com/nicklambourne/slackblocks/issues
+license: MIT
+license-file: LICENSE
+author: Nicholas Lambourne
+maintainer: dev@ndl.au
+category: Web
+build-type: Simple
+tested-with: GHC ==9.6.7 || ==9.8.4 || ==9.10.3 || ==9.12.4 || ==9.14.1
+extra-doc-files: README.md CHANGELOG.md integrations/README.md
+data-files: test/spec/**/*.json
+extra-source-files: generator/*.py bin/check bin/sync-spec bin/api-audit bin/quality
+                    conformance/skiplist.txt conformance/public-api.json
+                    bin/package-check bin/http-check bin/dependency-check
+                    dependencies/*.json consumer/Main.hs
+                    integrations/transport.cabal integrations/src/*.hs
+
+source-repository head
+  type: git
+  location: https://github.com/nicklambourne/slackblocks.git
+  subdir: haskell
+
+common language
+  default-language: GHC2021
+  default-extensions: OverloadedStrings
+  ghc-options: -Wall -Wcompat
+
+library
+  import: language
+  hs-source-dirs: src
+  exposed-modules: Slackblocks Slackblocks.Validation Slackblocks.Models
+                   Slackblocks.Json Slackblocks.Style Slackblocks.Helpers
+  other-modules: Slackblocks.Internal.Wire Slackblocks.Internal.Rules
+                 Slackblocks.Internal.Schema Slackblocks.Internal.Decimal
+                 Slackblocks.Internal.JsonSyntax
+  build-depends:
+    base >=4.18 && <4.23,
+    aeson >=2.2 && <2.4,
+    bytestring >=0.11.5.4 && <0.13,
+    text >=2.0.2 && <2.2,
+    containers >=0.6.7 && <0.9,
+    scientific >=0.3.7 && <0.4,
+    vector >=0.13 && <0.14
+
+test-suite foundation
+  import: language
+  type: exitcode-stdio-1.0
+  hs-source-dirs: test
+  main-is: Foundation.hs
+  build-depends:
+    base >=4.18 && <4.23,
+    slackblocks,
+    aeson >=2.2 && <2.4,
+    text >=2.0.2 && <2.2
+
+test-suite conformance
+  import: language
+  type: exitcode-stdio-1.0
+  hs-source-dirs: test
+  main-is: Conformance.hs
+  other-modules: Constructions Paths_slackblocks
+  autogen-modules: Paths_slackblocks
+  build-depends:
+    base >=4.18 && <4.23,
+    slackblocks,
+    aeson >=2.2 && <2.4,
+    containers >=0.6.7 && <0.9,
+    text >=2.0.2 && <2.2
+
+test-suite boundaries
+  import: language
+  type: exitcode-stdio-1.0
+  hs-source-dirs: test
+  main-is: Boundaries.hs
+  build-depends:
+    base >=4.18 && <4.23,
+    slackblocks,
+    aeson >=2.2 && <2.4,
+    containers >=0.6.7 && <0.9,
+    text >=2.0.2 && <2.2
+
+test-suite helpers
+  import: language
+  type: exitcode-stdio-1.0
+  hs-source-dirs: test
+  main-is: Helpers.hs
+  build-depends:
+    base >=4.18 && <4.23,
+    slackblocks,
+    aeson >=2.2 && <2.4,
+    bytestring >=0.11.5.4 && <0.13,
+    containers >=0.6.7 && <0.9,
+    text >=2.0.2 && <2.2,
+    QuickCheck >=2.14.3 && <2.17
+
+test-suite adversarial
+  import: language
+  type: exitcode-stdio-1.0
+  hs-source-dirs: test
+  main-is: Adversarial.hs
+  build-depends:
+    base >=4.18 && <4.23,
+    slackblocks,
+    aeson >=2.2 && <2.4,
+    bytestring >=0.11.5.4 && <0.13,
+    containers >=0.6.7 && <0.9,
+    text >=2.0.2 && <2.2
diff --git a/src/Slackblocks.hs b/src/Slackblocks.hs
new file mode 100644
--- /dev/null
+++ b/src/Slackblocks.hs
@@ -0,0 +1,16 @@
+-- | Typed, immutable Slack Block Kit values. Build editable field records with
+-- checked builders before encoding. All record updates must be rebuilt.
+module Slackblocks
+  ( module Slackblocks.Models,
+    module Slackblocks.Validation,
+    module Slackblocks.Style,
+    module Slackblocks.Json,
+    module Slackblocks.Helpers,
+  )
+where
+
+import Slackblocks.Helpers
+import Slackblocks.Json
+import Slackblocks.Models
+import Slackblocks.Style
+import Slackblocks.Validation
diff --git a/src/Slackblocks/Helpers.hs b/src/Slackblocks/Helpers.hs
new file mode 100644
--- /dev/null
+++ b/src/Slackblocks/Helpers.hs
@@ -0,0 +1,239 @@
+-- | Pure composition conveniences. These produce ordinary checked Block Kit
+-- values; sending, interaction routing and pagination state remain application-owned.
+module Slackblocks.Helpers
+  ( accordionSection,
+    accordion,
+    PaginatorOptions (..),
+    paginator,
+    paginate,
+    workflowFromURL,
+    NamedColor (..),
+    Color,
+    namedColor,
+    hexColor,
+    rgbColor,
+    colorText,
+    BuilderPayload (..),
+    blockKitBuilderURL,
+  )
+where
+
+import Control.Monad (unless, when)
+import Data.Aeson (ToJSON (..), Value, encode, object, (.=))
+import Data.ByteString qualified as B
+import Data.ByteString.Lazy qualified as L
+import Data.Char (isAscii, isHexDigit, toUpper)
+import Data.Map.Strict (Map)
+import Data.Maybe (fromMaybe)
+import Data.Text (Text)
+import Data.Text qualified as T
+import Data.Text.Encoding qualified as TE
+import Numeric (showHex)
+import Slackblocks.Models
+import Slackblocks.Validation
+
+-- | Construction input for a collapsible container, initially collapsed. Edit
+-- ordinary container fields, then call 'buildContainerBlock'. No ID is invented.
+accordionSection :: PlainText -> [Block] -> ContainerBlockFields
+accordionSection title blocks =
+  (containerBlock blocks)
+    { containerBlockTitle = Just title,
+      containerBlockWidth = Just ContainerWidthStandard,
+      containerBlockIsCollapsible = Just True,
+      containerBlockDefaultCollapsed = Just True,
+      containerBlockHasHeaderDivider = Just False
+    }
+
+-- | Expand checked collapsible sections, preserving their order.
+accordion :: [ContainerBlock] -> Result [Block]
+accordion sections = do
+  when (null sections) $ failure MissingRequired [Field "sections"] "accordion.sections" "expected at least one section"
+  mapM_
+    ( \(index, section) ->
+        unless (containerBlockIsCollapsible (inspectContainerBlock section) == Just True) $
+          failure TypeMismatch [Field "sections", Index index] "accordion.section" "expected a collapsible container"
+    )
+    (zip [0 ..] sections)
+  pure (map containerBlockAsBlock sections)
+
+-- | Page numbers start at one. Navigation adds up to two blocks; the receiving
+-- message or view checks total counts after expansion. No fields encode directly.
+data PaginatorOptions = PaginatorOptions
+  { -- | Nonempty prefix; navigation action IDs append .previous or .next.
+    paginatorActionIdPrefix :: Text,
+    -- | One-based requested page; defaults to 1.
+    paginatorPage :: Integer,
+    -- | Positive number of content blocks per page; defaults to 5.
+    paginatorPageSize :: Integer,
+    -- | Previous-button label; defaults to Previous.
+    paginatorPreviousText :: Text,
+    -- | Next-button label; defaults to Next.
+    paginatorNextText :: Text,
+    -- | Include a context block with the page number; defaults to True.
+    paginatorShowPageIndicator :: Bool,
+    -- | Optional actions-block ID; absent by default.
+    paginatorBlockId :: Maybe Text
+  }
+  deriving (Eq, Show)
+
+-- | Defaults: first page, five content blocks, Previous/Next and page indicator.
+paginator :: Text -> PaginatorOptions
+paginator prefix = PaginatorOptions prefix 1 5 "Previous" "Next" True Nothing
+
+-- | Select a one-based page, add navigation and optionally its indicator. Reject empty content, invalid page/size and empty action prefix.
+paginate :: PaginatorOptions -> [Block] -> Result [Block]
+paginate options blocks = do
+  when (null blocks) $ failure MissingRequired [Field "blocks"] "paginator.blocks" "expected content"
+  when (T.null prefix) $ failure MissingRequired [Field "action_id_prefix"] "paginator.prefix" "expected a nonempty prefix"
+  when (page < 1) $ failure OutOfRange [Field "page"] "paginator.page" "expected positive page"
+  when (size < 1) $ failure OutOfRange [Field "page_size"] "paginator.page_size" "expected positive page size"
+  let total = fromIntegral (length blocks)
+      pages = (total - 1) `div` size + 1
+      content = take (fromInteger (min size total)) $ drop (fromInteger (min ((page - 1) * size) total)) blocks
+  when (page > pages) $ failure OutOfRange [Field "page"] "paginator.page" "page exceeds page count"
+  if pages == 1
+    then pure content
+    else do
+      previous <- if page > 1 then (: []) <$> button (paginatorPreviousText options) ".previous" (page - 1) else pure []
+      next <- if page < pages then (: []) <$> button (paginatorNextText options) ".next" (page + 1) else pure []
+      controls <- buildActionsBlock ((actionsBlock (previous <> next)) {actionsBlockBlockId = paginatorBlockId options})
+      indicator <-
+        if paginatorShowPageIndicator options
+          then do
+            label <- buildMarkdownText (markdownText ("Page " <> decimal page <> " of " <> decimal pages))
+            context <- buildContextBlock (contextBlock [markdownTextAsContextElement label])
+            pure [contextBlockAsBlock context]
+          else pure []
+      pure (content <> indicator <> [actionsBlockAsBlock controls])
+  where
+    prefix = paginatorActionIdPrefix options
+    page = paginatorPage options
+    size = paginatorPageSize options
+    decimal = T.pack . show
+    button title suffix destination = do
+      label <- buildPlainText (plainText title)
+      action <-
+        buildButtonElement
+          ( (buttonElement label)
+              { buttonElementActionId = Just (prefix <> suffix),
+                buttonElementValue = Just (decimal destination)
+              }
+          )
+      pure (buttonElementAsElement action)
+
+-- | Build a workflow's trigger and optional customizable parameters. This does
+-- not call Slack or add restrictions beyond the shared trigger contract.
+workflowFromURL :: Text -> [InputParameter] -> Result Workflow
+workflowFromURL url parameters = do
+  link <- buildTrigger ((trigger url) {triggerCustomizableInputParameters = if null parameters then Nothing else Just parameters})
+  buildWorkflow (workflow link)
+
+-- | Named attachment colors, including Slack semantic good, warning and danger values.
+data NamedColor
+  = -- | Slack attachment color @good@.
+    Good
+  | -- | Slack attachment color @warning@.
+    Warning
+  | -- | Slack attachment color @danger@.
+    Danger
+  | -- | Slack attachment color @#ff0000@.
+    Red
+  | -- | Slack attachment color @#0000ff@.
+    Blue
+  | -- | Slack attachment color @#ffff00@.
+    Yellow
+  | -- | Slack attachment color @#00ff00@.
+    Green
+  | -- | Slack attachment color @#ff8800@.
+    Orange
+  | -- | Slack attachment color @#8800ff@.
+    Purple
+  | -- | Slack attachment color @#000000@.
+    Black
+  deriving (Eq, Ord, Show, Enum, Bounded)
+
+-- | Valid attachment color. Use 'colorText' with an attachment's color field.
+newtype Color = Color Text deriving (Eq, Ord, Show)
+
+-- | Return the exact Slack color string for a checked color.
+colorText :: Color -> Text
+colorText (Color value) = value
+
+-- | Resolve a named attachment color without network access.
+namedColor :: NamedColor -> Color
+namedColor color = Color $ case color of
+  Good -> "good"
+  Warning -> "warning"
+  Danger -> "danger"
+  Red -> "#ff0000"
+  Blue -> "#0000ff"
+  Yellow -> "#ffff00"
+  Green -> "#00ff00"
+  Orange -> "#ff8800"
+  Purple -> "#8800ff"
+  Black -> "#000000"
+
+-- | Six ASCII hexadecimal digits, with optional #. Letter case is preserved.
+hexColor :: Text -> Result Color
+hexColor input =
+  let value = fromMaybe input (T.stripPrefix "#" input)
+   in if T.length value == 6 && T.all (\c -> isAscii c && isHexDigit c) value
+        then Right (Color ("#" <> value))
+        else failure TypeMismatch [Field "color"] "color.hex" "expected six hexadecimal digits"
+
+-- | Construct a lowercase hexadecimal color; each exact integer channel must be in 0..255.
+rgbColor :: Integer -> Integer -> Integer -> Result Color
+rgbColor red green blue = do
+  mapM_
+    ( \(index, value) ->
+        when (value < 0 || value > 255) $
+          failure OutOfRange [Field "color", Index index] "color.rgb" "channel must be in 0..255"
+    )
+    (zip [0 ..] channels)
+  pure (Color ("#" <> T.concat [T.justifyRight 2 '0' (T.pack (showHex channel "")) | channel <- channels]))
+  where
+    channels = [red, green, blue]
+
+-- | Explicit Builder input shapes. 'BuilderJSON' is a deliberate opaque-object
+-- escape hatch; it is not validation or typed support for a Block Kit feature.
+data BuilderPayload
+  = -- | A block list wrapped in a blocks object.
+    BuilderBlocks [Block]
+  | -- | A complete checked Web API message.
+    BuilderMessage MessagePayload
+  | -- | A checked incoming-webhook payload.
+    BuilderWebhook WebhookMessage
+  | -- | A checked interaction response.
+    BuilderResponse MessageResponse
+  | -- | A checked modal view.
+    BuilderModal ModalView
+  | -- | A checked App Home view.
+    BuilderHome HomeTabView
+  | -- | An explicit unchecked application object; modeled support does not rely on this escape hatch.
+    BuilderJSON (Map Text Value)
+  deriving (Eq, Show)
+
+instance ToJSON BuilderPayload where
+  toJSON (BuilderBlocks blocks) = object ["blocks" .= blocks]
+  toJSON (BuilderMessage value) = toJSON value
+  toJSON (BuilderWebhook value) = toJSON value
+  toJSON (BuilderResponse value) = toJSON value
+  toJSON (BuilderModal value) = toJSON value
+  toJSON (BuilderHome value) = toJSON value
+  toJSON (BuilderJSON value) = toJSON value
+
+-- | Percent-encode UTF-8 JSON and an optional team as a single path segment.
+-- Makes no network request; rendering in Slack is a separate acceptance step.
+blockKitBuilderURL :: BuilderPayload -> Maybe Text -> Text
+blockKitBuilderURL payload team =
+  "https://app.slack.com/block-kit-builder/"
+    <> maybe "" (escape . TE.encodeUtf8) team
+    <> "#"
+    <> escape (L.toStrict (encode payload))
+  where
+    escape = T.concat . map byte . B.unpack
+    byte value
+      | value >= 65 && value <= 90 || value >= 97 && value <= 122 || value >= 48 && value <= 57 || value `elem` [45, 46, 95, 126] = T.singleton (toEnum (fromIntegral value))
+      | otherwise = "%" <> T.pack (map toUpper (pad (showHex value "")))
+    pad [one] = ['0', one]
+    pad pair = pair
diff --git a/src/Slackblocks/Internal/Decimal.hs b/src/Slackblocks/Internal/Decimal.hs
new file mode 100644
--- /dev/null
+++ b/src/Slackblocks/Internal/Decimal.hs
@@ -0,0 +1,53 @@
+-- | Compare finite decimal strings without expanding their exponents.
+module Slackblocks.Internal.Decimal (decimalParts, compareDecimal) where
+
+import Data.Char (isAscii, isDigit)
+import Data.Text (Text)
+import Data.Text qualified as T
+import Text.Read (readMaybe)
+
+asciiDigits :: Text -> Bool
+asciiDigits text = not (T.null text) && T.all (\c -> isAscii c && isDigit c) text
+
+unsigned :: Text -> Text
+unsigned value = case T.uncons value of
+  Just (c, rest) | c == '+' || c == '-' -> rest
+  _ -> value
+
+signedInteger :: Text -> Maybe Integer
+signedInteger text
+  | asciiDigits (unsigned text) = readMaybe (T.unpack (if T.isPrefixOf "+" text then T.drop 1 text else text))
+  | otherwise = Nothing
+
+decimalParts :: Text -> Maybe (Int, Text, Integer)
+decimalParts value = do
+  (mantissa, exponentValue) <- case T.split (\c -> c == 'e' || c == 'E') value of
+    [m] -> Just (m, 0)
+    [m, e] -> (m,) <$> signedInteger e
+    _ -> Nothing
+  let pieces = T.splitOn "." (unsigned mantissa)
+  fraction <- case pieces of
+    [_] -> Just 0
+    [_, f] -> Just (T.length f)
+    _ -> Nothing
+  let combined = T.concat pieces
+  if not (asciiDigits combined)
+    then Nothing
+    else do
+      let digits = T.dropWhile (== '0') combined
+          sign
+            | T.null digits = 0
+            | T.isPrefixOf "-" mantissa = -1
+            | otherwise = 1
+          rank = exponentValue + fromIntegral (T.length digits) - fromIntegral fraction
+      pure (sign, digits, rank)
+
+compareDecimal :: Text -> Text -> Maybe Ordering
+compareDecimal left right = do
+  (a, x, rankX) <- decimalParts left
+  (b, y, rankY) <- decimalParts right
+  let width = max (T.length x) (T.length y)
+      magnitude = compare rankX rankY <> compare (T.justifyLeft width '0' x) (T.justifyLeft width '0' y)
+  pure $ if a /= b then compare a b else if a == 0 then EQ else if a < 0 then invert magnitude else magnitude
+  where
+    invert LT = GT; invert GT = LT; invert EQ = EQ
diff --git a/src/Slackblocks/Internal/JsonSyntax.hs b/src/Slackblocks/Internal/JsonSyntax.hs
new file mode 100644
--- /dev/null
+++ b/src/Slackblocks/Internal/JsonSyntax.hs
@@ -0,0 +1,105 @@
+-- | Preserve Aeson's exact numbers and UTF-8 checks while rejecting duplicate
+-- object keys before they can be discarded by a KeyMap, including in metadata.
+module Slackblocks.Internal.JsonSyntax (parseJSONBytes, checkJSONNumbers, encodableScientific) where
+
+import Control.Monad (unless, when)
+import Data.Aeson (Value (..))
+import Data.Aeson.Decoding.ByteString.Lazy (lbsToTokens)
+import Data.Aeson.Decoding.Tokens qualified as Token
+import Data.Aeson.Key qualified as Key
+import Data.Aeson.KeyMap qualified as KM
+import Data.ByteString.Lazy qualified as B
+import Data.ByteString.Lazy.Char8 qualified as C
+import Data.Char (isDigit)
+import Data.Scientific (Scientific, base10Exponent, coefficient)
+import Data.Text qualified as T
+import Data.Vector qualified as V
+import Slackblocks.Validation
+
+syntaxFailure :: [PathPiece] -> String -> Result a
+syntaxFailure path message = failure TypeMismatch path "json" (T.pack message)
+
+parseJSONBytes :: B.ByteString -> Result Value
+parseJSONBytes bytes = do
+  checkExponents bytes
+  (value, rest) <- valueToken [] (lbsToTokens bytes)
+  checkJSONNumbers value
+  if B.all (`elem` [9, 10, 13, 32]) rest
+    then Right value
+    else syntaxFailure [] "trailing input after JSON value"
+
+-- Scientific's formatter normalizes the coefficient using machine-sized
+-- exponent arithmetic. Check both its printed exponent and the exponent that
+-- parsing the printed fractional digits needs, without expanding any powers.
+encodableScientific :: Scientific -> Bool
+encodableScientific number
+  | coefficient number == 0 = True
+  | otherwise = printed <= toInteger (maxBound :: Int) && parsed >= toInteger (minBound :: Int)
+  where
+    digits = show (abs (coefficient number))
+    significant = reverse (dropWhile (== '0') (reverse digits))
+    printed = toInteger (base10Exponent number) + toInteger (length digits) - 1
+    parsed = printed - toInteger (max 1 (length significant - 1))
+
+-- Metadata stays opaque to Block Kit rules, but every number still has to
+-- survive the public Aeson encoder. This also covers native extension Values.
+checkJSONNumbers :: Value -> Result ()
+checkJSONNumbers (Number number) =
+  unless (encodableScientific number) $
+    failure TypeMismatch [] "json.number.exponent" "JSON number cannot round-trip through Scientific's encoder"
+checkJSONNumbers (Object fields) = mapM_ (\(key, value) -> atField (Key.toText key) (checkJSONNumbers value)) (KM.toList fields)
+checkJSONNumbers (Array values) = mapM_ (\(index, value) -> atIndex index (checkJSONNumbers value)) (zip [0 ..] (V.toList values))
+checkJSONNumbers _ = Right ()
+
+-- Aeson's lexer narrows exponents to Int without an overflow check. Inspect
+-- numeric lexemes first, including metadata, before their spelling is lost.
+-- This is a representation guard, not a second JSON grammar: Aeson still
+-- validates syntax/UTF-8 below. Skip quoted strings and escaped quotes intact.
+checkExponents :: B.ByteString -> Result ()
+checkExponents bytes = case C.uncons bytes of
+  Nothing -> Right ()
+  Just ('"', rest) -> quoted rest
+  Just (c, _) | isDigit c || c == '-' -> do
+    let (number, rest) = C.span (\x -> isDigit x || x `elem` (".eE+-" :: String)) bytes
+        (mantissa, exponentPart) = C.break (\x -> x == 'e' || x == 'E') number
+        fraction = C.drop 1 (snd (C.break (== '.') mantissa))
+    case C.readInteger (C.drop 1 exponentPart) of
+      Just (exponentValue, trailing) | B.null trailing -> do
+        let adjusted = exponentValue - toInteger (B.length fraction)
+        when (adjusted < toInteger (minBound :: Int) || adjusted > toInteger (maxBound :: Int)) $
+          failure TypeMismatch [] "json.number.exponent" "JSON number exponent is outside Scientific's representable range"
+      _ -> Right ()
+    checkExponents rest
+  Just (_, rest) -> checkExponents (C.dropWhile (\c -> c /= '"' && c /= '-' && not (isDigit c)) rest)
+  where
+    quoted input = case C.uncons (C.dropWhile (\c -> c /= '"' && c /= '\\') input) of
+      Nothing -> Right () -- The JSON lexer reports the unterminated string.
+      Just ('"', rest) -> checkExponents rest
+      Just (_, rest) -> quoted (C.drop 1 rest)
+
+valueToken :: [PathPiece] -> Token.Tokens k String -> Result (Value, k)
+valueToken path tokens = case tokens of
+  Token.TkLit literal rest -> Right (case literal of Token.LitNull -> Null; Token.LitTrue -> Bool True; Token.LitFalse -> Bool False, rest)
+  Token.TkText value rest -> Right (String value, rest)
+  Token.TkNumber value rest -> Right (Number (case value of Token.NumInteger n -> fromInteger n; Token.NumDecimal n -> n; Token.NumScientific n -> n), rest)
+  Token.TkArrayOpen items -> arrayTokens path 0 [] items
+  Token.TkRecordOpen fields -> objectTokens path KM.empty fields
+  Token.TkErr message -> syntaxFailure path message
+
+arrayTokens :: [PathPiece] -> Int -> [Value] -> Token.TkArray k String -> Result (Value, k)
+arrayTokens path index values tokens = case tokens of
+  Token.TkItem item -> do
+    (value, rest) <- valueToken (path <> [Index index]) item
+    arrayTokens path (index + 1) (value : values) rest
+  Token.TkArrayEnd rest -> Right (Array (V.fromList (reverse values)), rest)
+  Token.TkArrayErr message -> syntaxFailure path message
+
+objectTokens :: [PathPiece] -> KM.KeyMap Value -> Token.TkRecord k String -> Result (Value, k)
+objectTokens path fields tokens = case tokens of
+  Token.TkPair key item -> do
+    let childPath = path <> [Field (Key.toText key)]
+    when (KM.member key fields) $ failure TypeMismatch childPath "json.duplicate_key" "duplicate object key"
+    (value, rest) <- valueToken childPath item
+    objectTokens path (KM.insert key value fields) rest
+  Token.TkRecordEnd rest -> Right (Object fields, rest)
+  Token.TkRecordErr message -> syntaxFailure path message
diff --git a/src/Slackblocks/Internal/Rules.hs b/src/Slackblocks/Internal/Rules.hs
new file mode 100644
--- /dev/null
+++ b/src/Slackblocks/Internal/Rules.hs
@@ -0,0 +1,253 @@
+-- | Handwritten relationships and receiving-context validation. Only modeled
+-- child positions are traversed; arbitrary JSON metadata remains opaque.
+module Slackblocks.Internal.Rules (validateFields, checkIntegerBounds, standaloneTask) where
+
+import Control.Monad (unless, when)
+import Data.Aeson (Value (..))
+import Data.Aeson.Key qualified as K
+import Data.Aeson.KeyMap qualified as KM
+import Data.Char (isAscii, isAsciiUpper, isDigit, isHexDigit)
+import Data.List (nub)
+import Data.Maybe (isJust)
+import Data.Scientific (isInteger)
+import Data.Text (Text)
+import Data.Text qualified as T
+import Data.Vector qualified as V
+import Slackblocks.Internal.Decimal
+import Slackblocks.Internal.JsonSyntax (checkJSONNumbers)
+import Slackblocks.Internal.Schema
+import Slackblocks.Validation
+
+at :: Text -> Value -> Value
+at key (Object fields) = KM.lookup (K.fromText key) fields `orValue` Null
+  where
+    orValue (Just value) _ = value; orValue Nothing fallback = fallback
+at _ _ = Null
+
+has :: Text -> Value -> Bool
+has key (Object fields) = KM.member (K.fromText key) fields
+has _ _ = False
+
+array :: Value -> [Value]
+array (Array values) = V.toList values
+array _ = []
+
+text :: Value -> Text
+text (String value) = value
+text _ = ""
+
+count :: Value -> Integer
+count = fromIntegral . T.length . text
+
+limit :: Text -> Integer
+limit key = case lookup key limits of
+  Just value -> value
+  Nothing -> error ("internal generated limit missing: " <> T.unpack key)
+
+check :: ErrorCategory -> [PathPiece] -> Text -> Bool -> Result ()
+check category path constraint ok = unless ok (failure category path constraint "constraint violated")
+
+-- Reject bounded integer fields while they are still compact Scientific values.
+-- Expanding 1e1000000000 into Integer just to compare it with 3000 can exhaust
+-- memory. Fractions still reach the ordinary integer type check below.
+checkIntegerBounds :: Text -> Value -> Result ()
+checkIntegerBounds owner value = mapM_ field (fieldConstraints owner)
+  where
+    field (key, kind, prefix, bounds, _) = when (kind `elem` ["int", "long"]) $ case at key value of
+      number@(Number x) -> do
+        atField key (checkJSONNumbers number)
+        when (isInteger x) $ mapM_ (bound key prefix x) bounds
+      _ -> pure ()
+    bound key prefix x (suffix, n) = case suffix of
+      "min" -> check OutOfRange [Field key] (prefix <> ".min") (x >= fromInteger n)
+      "max" -> check OutOfRange [Field key] (prefix <> ".max") (x <= fromInteger n)
+      _ -> pure ()
+
+validateFields :: Text -> Value -> Result ()
+validateFields owner value = do
+  checkJSONNumbers value
+  mapM_ field (fieldConstraints owner)
+  rules owner value
+  where
+    field (key, kind, prefix, bounds, flags) = when (has key value && at key value /= Null) $ do
+      let v = at key value
+          measure = case kind of
+            "string" -> count v
+            "text" -> count (at "text" v)
+            _ -> fromIntegral (length (array v))
+          path = [Field key]
+          bound (suffix, n) =
+            let constraint = prefix <> "." <> suffix
+                lengthCheck predicate = check LengthExceeded path constraint (predicate measure n)
+                numberCheck predicate = case v of
+                  Number x -> check OutOfRange path constraint (predicate x (fromInteger n))
+                  _ -> failure TypeMismatch path constraint "expected a number"
+             in case suffix of
+                  "min_length" -> lengthCheck (>=)
+                  "max_length" -> lengthCheck (<=)
+                  "min_items" -> lengthCheck (>=)
+                  "max_items" -> lengthCheck (<=)
+                  "min" -> numberCheck (>=)
+                  "max" -> numberCheck (<=)
+                  "exclusive_min" -> numberCheck (>)
+                  "exclusive_max" -> numberCheck (<)
+                  "item_max_length" -> mapM_ (\(i, item) -> check LengthExceeded (path <> [Index i]) constraint ((if kind == "textList" then count (at "text" item) else count item) <= n)) (zip [0 ..] (array v))
+                  _ -> failure InvalidUsage path constraint "unknown internal constraint"
+      mapM_ bound bounds
+      unless (null flags) $ case v of
+        Object style -> mapM_ (\key' -> check TypeMismatch (path <> [Field (K.toText key')]) "style.flags" (K.toText key' `elem` flags)) (KM.keys style)
+        _ -> failure TypeMismatch path "style" "expected a style object"
+
+rules :: Text -> Value -> Result ()
+rules owner value = case owner of
+  "ButtonElement" -> check MissingRequired [Field "agent_prompt"] "button.agent_prompt_display.requires_agent_prompt" (not (present "agent_prompt_display") || present "agent_prompt")
+  "SlackIcon" -> check TypeMismatch [Field "name"] "slack_icon_names" (text (get "name") `elem` icons)
+  "ExternalMultiSelectElement" -> check InvalidUsage [Field "focus_on_load"] "external_multi_select.focus_on_load" (not (present "focus_on_load"))
+  "SectionBlock" -> do
+    check MissingRequired [] "section.content" (present "text" || not (null (array (get "fields"))))
+    check MissingRequired [Field "fields"] "section.fields" (not (present "fields") || not (null (array (get "fields"))))
+  "StaticSelectElement" -> options
+  "StaticMultiSelectElement" -> options
+  "CardBlock" -> do
+    check MissingRequired [] "card.content" (any present ["hero_image", "title", "actions", "body"])
+    check MutuallyExclusive [] "card.icon" (not (present "icon" && present "slack_icon"))
+  "ContainerBlock" -> do
+    check MissingRequired [] "container.title" (present "title" || present "rich_text_title")
+    check InvalidUsage [Field "default_collapsed"] "container.default_collapsed" (get "default_collapsed" /= Bool True || get "is_collapsible" == Bool True)
+    check InvalidUsage [Field "has_header_divider"] "container.has_header_divider" (get "has_header_divider" /= Bool True || get "is_collapsible" /= Bool True)
+  "ImageBlock" -> image
+  "ImageElement" -> image
+  "SlackFile" -> do
+    check MutuallyExclusive [] "slack_file.source" (present "id" /= present "url")
+    when (present "id") $
+      let identifier = text (get "id")
+       in check TypeMismatch [Field "id"] "slack_file.id" (T.length identifier >= 9 && T.isPrefixOf "F" identifier && T.all (\c -> isAsciiUpper c || (isAscii c && isDigit c)) (T.drop 1 identifier))
+  "ConversationFilter" -> do
+    check MissingRequired [] "conversation_filter.content" (any present ["include", "exclude_external_shared_channels", "exclude_bot_users"])
+    indexed "include" (\item -> check TypeMismatch [] "conversation_filter.include" (text item `elem` ["im", "mpim", "private", "public"]))
+  "DispatchActionConfiguration" -> check MissingRequired [Field "trigger_actions_on"] "dispatch_action_configuration.trigger_actions_on" (present "trigger_actions_on")
+  "DatePickerElement" -> when (present "initial_date") $ check TypeMismatch [Field "initial_date"] "date_picker.initial_date" (dateShape (text (get "initial_date")))
+  "TimePickerElement" -> when (present "initial_time") $ check TypeMismatch [Field "initial_time"] "time_picker.initial_time" (timeShape (text (get "initial_time")))
+  "PlainTextInputElement" -> case (get "min_length", get "max_length") of
+    (Number lo, Number hi) -> check OutOfRange [Field "min_length"] "plain_text_input.bounds" (lo <= hi)
+    _ -> Right ()
+  "NumberInputElement" -> do
+    mapM_ (\key -> when (present key) (check TypeMismatch [Field key] "number_input.decimal" (isJust (decimalParts (text (get key)))))) ["min_value", "max_value"]
+    when (present "min_value" && present "max_value") $ check OutOfRange [] "number_input.bounds" (compareDecimal (text (get "min_value")) (text (get "max_value")) /= Just GT)
+  "TableBlock" -> indexed "rows" (\row -> check LengthExceeded [] "table.columns.max_items" (fromIntegral (length (array row)) <= limit "table.columns.max_items"))
+  "DataTableBlock" -> dataTable value
+  "AxisConfig" -> do
+    indexed "categories" (\label -> check LengthExceeded [] "data_visualization.category_label.max_length" (count label <= limit "data_visualization.category_label.max_length"))
+    check InvalidUsage [Field "categories"] "data_visualization.categories.unique" (unique (array (get "categories")))
+  "ChartSegment" -> check OutOfRange [Field "value"] "data_visualization.segment.value.exclusive_min" (case get "value" of Number number -> number > fromInteger (limit "data_visualization.segment.value.exclusive_min"); _ -> False)
+  "LineChart" -> chart value
+  "BarChart" -> chart value
+  "AreaChart" -> chart value
+  "PlanBlock" -> check InvalidUsage [Field "tasks"] "plan.tasks.unique" (unique (map (at "task_id") (array (get "tasks"))))
+  "Attachment" -> do
+    when (present "color") $
+      let color = text (get "color")
+       in check TypeMismatch [Field "color"] "attachment.color" (color `elem` ["good", "warning", "danger"] || (T.length color == 7 && T.isPrefixOf "#" color && T.all (\c -> isAscii c && isHexDigit c) (T.drop 1 color)))
+    surface "message" [Field "blocks"] (array (get "blocks"))
+  "MessagePayload" -> message value
+  "MessageResponse" -> message value
+  "WebhookMessage" -> message value
+  "ModalView" -> do
+    surface "modal" [Field "blocks"] (array (get "blocks"))
+    check MissingRequired [Field "submit"] "view.submit.required" (present "submit" || not (any ((== String "input") . at "type") (array (get "blocks"))))
+  "HomeTabView" -> surface "home" [Field "blocks"] (array (get "blocks"))
+  _ -> Right ()
+  where
+    get key = at key value
+    present key = has key value
+    options = check MutuallyExclusive [] "select.options_or_groups" (not (present "options" && present "option_groups"))
+    image = do
+      check MissingRequired [] "image.source" (present "image_url" || present "slack_file")
+      check MutuallyExclusive [] "image.source" (not (present "image_url" && present "slack_file"))
+    indexed key f = mapM_ (\(i, item) -> atField key (atIndex i (f item))) (zip [0 ..] (array (get key)))
+
+unique :: (Eq a) => [a] -> Bool
+unique values = length (nub values) == length values
+
+dateShape :: Text -> Bool
+dateShape value = T.length value == 10 && and [if i `elem` [4, 7] then c == '-' else isAscii c && isDigit c | (i, c) <- zip [0 :: Int ..] (T.unpack value)]
+
+timeShape :: Text -> Bool
+timeShape value = case T.unpack value of
+  [a, b, ':', c, d] -> all (\x -> isAscii x && isDigit x) [a, b, c, d] && [a, b] <= "23" && [c, d] <= "59"
+  _ -> False
+
+standaloneTask :: Value -> Result ()
+standaloneTask value
+  | at "type" value == String "task_card" = check TypeMismatch [Field "status"] "task_card.pending" (at "status" value /= String "pending")
+  | at "type" value == String "container" = mapM_ (\(i, child) -> atField "child_blocks" (atIndex i (standaloneTask child))) (zip [0 ..] (array (at "child_blocks" value)))
+  | otherwise = Right ()
+
+surface :: Text -> [PathPiece] -> [Value] -> Result ()
+surface name path =
+  mapM_
+    ( \(i, block) -> do
+        check TypeMismatch (path <> [Index i, Field "type"]) "surface_block_types" (text (at "type" block) `elem` surfaceBlocks name)
+        either (Left . fmap (\e -> e {errorPath = path <> [Index i] <> errorPath e})) Right (standaloneTask block)
+    )
+    . zip [0 ..]
+
+-- Traverse only modeled nested blocks. Metadata values with a "type" key do
+-- not count as blocks, charts, markdown or tables.
+descendants :: Value -> [Value]
+descendants block = block : if at "type" block == String "container" then concatMap descendants (array (at "child_blocks" block)) else []
+
+message :: Value -> Result ()
+message value = do
+  let blocks = array (at "blocks" value)
+      attached = concatMap (array . at "blocks") (array (at "attachments" value))
+      nested = concatMap descendants (blocks <> attached)
+      charts = filter ((== String "data_visualization") . at "type") nested
+      markdown = sum [count (at "text" b) | b <- nested, at "type" b == String "markdown"]
+      tables = sum [tableCharacters b | b <- nested, at "type" b == String "data_table"]
+  surface "message" [Field "blocks"] blocks
+  check LengthExceeded [] "message.data_visualizations.max_items" (fromIntegral (length charts) <= limit "message.data_visualizations.max_items")
+  check LengthExceeded [] "markdown.total_text.max_length" (markdown <= limit "markdown.total_text.max_length")
+  check LengthExceeded [] "data_table.total_content.max_length" (tables <= limit "data_table.total_content.max_length")
+
+tableCharacters :: Value -> Integer
+tableCharacters value = sum (map cellCharacters (concatMap array (array (at "rows" value))))
+  where
+    cellCharacters cell
+      | at "type" cell `elem` [String "raw_text", String "raw_number"] = count (at "text" cell)
+      | at "type" cell == String "rich_text" = sum (map richCharacters (array (at "elements" cell)))
+      | otherwise = 0
+    richCharacters element
+      | at "type" element == String "rich_text_list" = sum (map richCharacters (array (at "elements" element)))
+      | otherwise = sum [count (at "text" item) | item <- array (at "elements" element), at "type" item `elem` [String "text", String "link"]]
+
+dataTable :: Value -> Result ()
+dataTable value = do
+  let rows = map array (array (at "rows" value))
+      width = case rows of [] -> 0; first : _ -> length first
+  mapM_
+    ( \(i, row) -> do
+        check LengthExceeded [Field "rows", Index i] "data_table.columns.max_items" (not (null row) && fromIntegral (length row) <= limit "data_table.columns.max_items")
+        check InvalidUsage [Field "rows", Index i] "data_table.rectangular" (length row == width)
+        mapM_
+          ( \(j, cell) -> do
+              check TypeMismatch [Field "rows", Index i, Index j] "data_table.header" (i /= 0 || at "type" cell /= String "rich_text")
+              when (at "type" cell `elem` [String "raw_text", String "raw_number"]) $ check LengthExceeded [Field "rows", Index i, Index j, Field "text"] "data_table.cell.min_length" (count (at "text" cell) > 0)
+          )
+          (zip [0 ..] row)
+    )
+    (zip [0 ..] rows)
+  check InvalidUsage [Field "column_settings"] "data_table.column_settings" (not (has "column_settings" value))
+  check LengthExceeded [Field "rows"] "data_table.content.max_length" (tableCharacters value <= limit "data_table.content.max_length")
+
+chart :: Value -> Result ()
+chart value = do
+  let categories = array (at "categories" (at "axis_config" value))
+      series = array (at "series" value)
+  mapM_
+    ( \(i, entry) ->
+        let points = array (at "data" entry)
+         in check InvalidUsage [Field "series", Index i, Field "data"] "data_visualization.series.categories" (length points == length categories && all (\label -> length (filter ((== label) . at "label") points) == 1) categories)
+    )
+    (zip [0 ..] series)
+  check InvalidUsage [Field "series"] "data_visualization.series.unique" (unique (map (at "name") series))
diff --git a/src/Slackblocks/Internal/Schema.hs b/src/Slackblocks/Internal/Schema.hs
new file mode 100644
--- /dev/null
+++ b/src/Slackblocks/Internal/Schema.hs
@@ -0,0 +1,110 @@
+-- Generated shared limits, vocabulary and local field constraints.
+module Slackblocks.Internal.Schema (fieldConstraints, limits, icons, surfaceBlocks) where
+
+import Data.Text (Text)
+
+fieldConstraints :: Text -> [(Text, Text, Text, [(Text, Integer)], [Text])]
+fieldConstraints owner = case owner of
+  "ActionsBlock" -> [("elements", "list", "actions.elements", [("max_items", 25)], []), ("block_id", "string", "block_id", [("max_length", 255)], [])]
+  "AlertBlock" -> [("text", "text", "alert.text", [("max_length", 200)], []), ("block_id", "string", "block_id", [("max_length", 255)], [])]
+  "CardBlock" -> [("title", "text", "card.title", [("max_length", 150)], []), ("subtitle", "text", "card.subtitle", [("max_length", 150)], []), ("body", "text", "card.body", [("max_length", 200)], []), ("actions", "list", "card.actions", [("max_items", 3)], []), ("subtext", "text", "card.subtext", [("max_length", 200)], []), ("block_id", "string", "block_id", [("max_length", 255)], [])]
+  "CarouselBlock" -> [("elements", "list", "carousel.elements", [("min_items", 1), ("max_items", 10)], []), ("block_id", "string", "block_id", [("max_length", 255)], [])]
+  "ContainerBlock" -> [("child_blocks", "list", "container.child_blocks", [("min_items", 1), ("max_items", 10)], []), ("title", "text", "container.title", [("max_length", 150)], []), ("subtitle", "text", "container.subtitle", [("max_length", 150)], []), ("block_id", "string", "block_id", [("max_length", 255)], [])]
+  "ContextActionsBlock" -> [("elements", "list", "context_actions.elements", [("max_items", 5)], []), ("block_id", "string", "block_id", [("max_length", 255)], [])]
+  "ContextBlock" -> [("elements", "list", "context.elements", [("max_items", 10)], []), ("block_id", "string", "block_id", [("max_length", 255)], [])]
+  "DataTableBlock" -> [("rows", "rows", "data_table.rows", [("min_items", 2), ("max_items", 201)], []), ("page_size", "int", "data_table.page_size", [("min", 1), ("max", 100)], []), ("row_header_column_index", "int", "data_table.row_header_column_index", [("min", 0)], []), ("block_id", "string", "block_id", [("max_length", 255)], [])]
+  "DataVisualizationBlock" -> [("title", "string", "data_visualization.title", [("max_length", 50)], []), ("block_id", "string", "block_id", [("max_length", 255)], [])]
+  "DividerBlock" -> [("block_id", "string", "block_id", [("max_length", 255)], [])]
+  "FileBlock" -> [("block_id", "string", "block_id", [("max_length", 255)], [])]
+  "HeaderBlock" -> [("text", "text", "header.text", [("max_length", 150)], []), ("block_id", "string", "block_id", [("max_length", 255)], [])]
+  "ImageBlock" -> [("image_url", "string", "image.image_url", [("max_length", 3000)], []), ("alt_text", "string", "image.alt_text", [("max_length", 2000)], []), ("title", "text", "image.title", [("max_length", 2000)], []), ("block_id", "string", "block_id", [("max_length", 255)], [])]
+  "InputBlock" -> [("label", "text", "input.label", [("max_length", 2000)], []), ("block_id", "string", "block_id", [("max_length", 255)], []), ("hint", "text", "input.hint", [("max_length", 2000)], [])]
+  "MarkdownBlock" -> [("text", "string", "markdown.text", [("max_length", 12000)], []), ("block_id", "string", "block_id", [("max_length", 255)], [])]
+  "PlanBlock" -> [("tasks", "list", "plan.tasks", [("max_items", 50)], []), ("block_id", "string", "block_id", [("max_length", 255)], [])]
+  "RichTextBlock" -> [("block_id", "string", "block_id", [("max_length", 255)], [])]
+  "SectionBlock" -> [("text", "text", "section.text", [("max_length", 3000)], []), ("fields", "textList", "section.fields", [("max_items", 10), ("item_max_length", 2000)], []), ("block_id", "string", "block_id", [("max_length", 255)], [])]
+  "TableBlock" -> [("rows", "rows", "table.rows", [("max_items", 100)], []), ("column_settings", "list", "table.column_settings", [("max_items", 20)], []), ("block_id", "string", "block_id", [("max_length", 255)], [])]
+  "TaskCardBlock" -> [("block_id", "string", "block_id", [("max_length", 255)], [])]
+  "VideoBlock" -> [("alt_text", "string", "video.alt_text", [("max_length", 2000)], []), ("thumbnail_url", "string", "video.thumbnail_url", [("max_length", 3000)], []), ("title", "text", "video.title", [("max_length", 200)], []), ("video_url", "string", "video.video_url", [("max_length", 3000)], []), ("block_id", "string", "block_id", [("max_length", 255)], []), ("author_name", "string", "video.author_name", [("max_length", 50)], []), ("description", "text", "video.description", [("max_length", 200)], []), ("provider_icon_url", "string", "video.provider_icon_url", [("max_length", 2000)], []), ("provider_name", "string", "video.provider_name", [("max_length", 50)], []), ("title_url", "string", "video.title_url", [("max_length", 2000)], [])]
+  "ButtonElement" -> [("text", "text", "button.text", [("max_length", 75)], []), ("action_id", "string", "action_id", [("max_length", 255)], []), ("url", "string", "button.url", [("max_length", 3000)], []), ("value", "string", "button.value", [("max_length", 2000)], []), ("accessibility_label", "string", "button.accessibility_label", [("max_length", 75)], []), ("agent_prompt", "string", "button.agent_prompt", [("max_length", 4000)], [])]
+  "ChannelMultiSelectElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("max_selected_items", "int", "multi_select.max_selected_items", [("min", 1)], []), ("placeholder", "text", "select.placeholder", [("max_length", 150)], [])]
+  "ChannelSelectElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("placeholder", "text", "select.placeholder", [("max_length", 150)], [])]
+  "CheckboxesElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("options", "list", "checkboxes.options", [("min_items", 1), ("max_items", 10)], [])]
+  "ConversationMultiSelectElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("max_selected_items", "int", "multi_select.max_selected_items", [("min", 1)], []), ("placeholder", "text", "select.placeholder", [("max_length", 150)], [])]
+  "ConversationSelectElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("placeholder", "text", "select.placeholder", [("max_length", 150)], [])]
+  "DatePickerElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("placeholder", "text", "date_picker.placeholder", [("max_length", 150)], [])]
+  "DateTimePickerElement" -> [("action_id", "string", "action_id", [("max_length", 255)], [])]
+  "EmailInputElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("placeholder", "text", "email_input.placeholder", [("max_length", 150)], [])]
+  "ExternalMultiSelectElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("min_query_length", "int", "select.min_query_length", [("min", 0)], []), ("max_selected_items", "int", "multi_select.max_selected_items", [("min", 1)], []), ("placeholder", "text", "select.placeholder", [("max_length", 150)], [])]
+  "ExternalSelectElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("min_query_length", "int", "select.min_query_length", [("min", 0)], []), ("placeholder", "text", "select.placeholder", [("max_length", 150)], [])]
+  "FeedbackButtonsElement" -> [("action_id", "string", "action_id", [("max_length", 255)], [])]
+  "FileInputElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("max_files", "int", "file_input.max_files", [("min", 1), ("max", 10)], [])]
+  "IconButtonElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("value", "string", "icon_button.value", [("max_length", 2000)], []), ("accessibility_label", "string", "icon_button.accessibility_label", [("max_length", 75)], []), ("visible_to_user_ids", "stringList", "icon_button.visible_to_user_ids", [("max_items", 10)], [])]
+  "ImageElement" -> [("alt_text", "string", "image_element.alt_text", [("max_length", 2000)], []), ("image_url", "string", "image_element.image_url", [("max_length", 3000)], [])]
+  "NumberInputElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("placeholder", "text", "number_input.placeholder", [("max_length", 150)], [])]
+  "OverflowElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("options", "list", "overflow.options", [("min_items", 1), ("max_items", 5)], [])]
+  "PlainTextInputElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("min_length", "int", "plain_text_input.min_length", [("min", 0), ("max", 3000)], []), ("max_length", "int", "plain_text_input.max_length", [("min", 1), ("max", 3000)], []), ("placeholder", "text", "plain_text_input.placeholder", [("max_length", 150)], [])]
+  "RadioButtonsElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("options", "list", "radio_buttons.options", [("min_items", 1), ("max_items", 10)], [])]
+  "RichTextInputElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("placeholder", "text", "rich_text_input.placeholder", [("max_length", 150)], []), ("min_lines", "int", "rich_text_input.min_lines", [("min", 1), ("max", 100)], []), ("max_lines", "int", "rich_text_input.max_lines", [("min", 1), ("max", 100)], [])]
+  "StaticMultiSelectElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("options", "list", "select.options", [("min_items", 1), ("max_items", 100)], []), ("option_groups", "list", "select.option_groups", [("min_items", 1), ("max_items", 100)], []), ("max_selected_items", "int", "multi_select.max_selected_items", [("min", 1)], []), ("placeholder", "text", "select.placeholder", [("max_length", 150)], [])]
+  "StaticSelectElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("options", "list", "select.options", [("min_items", 1), ("max_items", 100)], []), ("option_groups", "list", "select.option_groups", [("min_items", 1), ("max_items", 100)], []), ("placeholder", "text", "select.placeholder", [("max_length", 150)], [])]
+  "TimePickerElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("placeholder", "text", "time_picker.placeholder", [("max_length", 150)], [])]
+  "UrlInputElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("placeholder", "text", "url_input.placeholder", [("max_length", 150)], [])]
+  "UserMultiSelectElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("max_selected_items", "int", "multi_select.max_selected_items", [("min", 1)], []), ("placeholder", "text", "select.placeholder", [("max_length", 150)], [])]
+  "UserSelectElement" -> [("action_id", "string", "action_id", [("max_length", 255)], []), ("placeholder", "text", "select.placeholder", [("max_length", 150)], [])]
+  "WorkflowButtonElement" -> [("text", "text", "workflow_button.text", [("max_length", 75)], []), ("action_id", "string", "action_id", [("max_length", 255)], []), ("accessibility_label", "string", "workflow_button.accessibility_label", [("max_length", 75)], [])]
+  "AreaChart" -> [("series", "list", "data_visualization.series", [("min_items", 1), ("max_items", 12)], [])]
+  "AxisConfig" -> [("categories", "stringList", "data_visualization.categories", [("min_items", 1), ("max_items", 20)], []), ("x_label", "string", "data_visualization.axis_label", [("max_length", 50)], []), ("y_label", "string", "data_visualization.axis_label", [("max_length", 50)], [])]
+  "BarChart" -> [("series", "list", "data_visualization.series", [("min_items", 1), ("max_items", 12)], [])]
+  "ChartSegment" -> [("label", "string", "data_visualization.segment.label", [("max_length", 20)], [])]
+  "ColumnSettings" -> []
+  "Confirmation" -> [("title", "text", "confirmation.title", [("max_length", 100)], []), ("text", "text", "confirmation.text", [("max_length", 300)], []), ("confirm", "text", "confirmation.confirm", [("max_length", 30)], []), ("deny", "text", "confirmation.deny", [("max_length", 30)], [])]
+  "ConversationFilter" -> [("include", "stringList", "conversation_filter.include", [("min_items", 1)], [])]
+  "DataPoint" -> [("label", "string", "data_visualization.point_label", [("max_length", 20)], [])]
+  "DataSeries" -> [("name", "string", "data_visualization.series_name", [("max_length", 20)], []), ("data", "list", "data_visualization.data", [("min_items", 1), ("max_items", 20)], [])]
+  "DispatchActionConfiguration" -> [("trigger_actions_on", "stringList", "dispatch_action_configuration.trigger_actions_on", [("min_items", 1), ("max_items", 2)], [])]
+  "FeedbackButton" -> [("text", "text", "feedback_button.text", [("max_length", 75)], []), ("value", "string", "feedback_button.value", [("max_length", 2000)], []), ("accessibility_label", "string", "feedback_button.accessibility_label", [("max_length", 75)], [])]
+  "InputParameter" -> []
+  "LineChart" -> [("series", "list", "data_visualization.series", [("min_items", 1), ("max_items", 12)], [])]
+  "MarkdownText" -> [("text", "string", "text", [("min_length", 1), ("max_length", 3000)], [])]
+  "Option" -> [("text", "text", "option.text", [("max_length", 75)], []), ("value", "string", "option.value", [("max_length", 150)], []), ("description", "text", "option.description", [("max_length", 75)], []), ("url", "string", "option.url", [("max_length", 3000)], [])]
+  "OptionGroup" -> [("label", "text", "option_group.label", [("max_length", 75)], []), ("options", "list", "option_group.options", [("min_items", 1), ("max_items", 100)], [])]
+  "PieChart" -> [("segments", "list", "data_visualization.segments", [("min_items", 1), ("max_items", 12)], [])]
+  "PlainText" -> [("text", "string", "text", [("min_length", 1), ("max_length", 3000)], [])]
+  "RawNumber" -> []
+  "RawText" -> []
+  "RichTextChannel" -> [("style", "style", "", [], ["bold", "italic", "strike", "highlight", "client_highlight", "unlink"])]
+  "RichTextCodeBlock" -> [("border", "int", "rich_text_preformatted.border", [("min", 0), ("max", 1)], [])]
+  "RichTextEmoji" -> []
+  "RichTextLink" -> [("style", "style", "", [], ["bold", "italic", "strike", "code"])]
+  "RichTextList" -> [("indent", "int", "rich_text_list.indent", [("min", 0), ("max", 8)], []), ("offset", "int", "rich_text_list.offset", [("min", 0)], []), ("border", "int", "rich_text_list.border", [("min", 0), ("max", 1)], [])]
+  "RichTextQuote" -> [("border", "int", "rich_text_quote.border", [("min", 0), ("max", 1)], [])]
+  "RichTextSection" -> []
+  "RichTextText" -> [("style", "style", "", [], ["bold", "italic", "strike", "code"])]
+  "RichTextUser" -> [("style", "style", "", [], ["bold", "italic", "strike", "highlight", "client_highlight", "unlink"])]
+  "RichTextUserGroup" -> [("style", "style", "", [], ["bold", "italic", "strike", "highlight", "client_highlight", "unlink"])]
+  "SlackFile" -> []
+  "SlackIcon" -> []
+  "Trigger" -> []
+  "UrlSource" -> []
+  "Workflow" -> []
+  "Attachment" -> []
+  "HomeTabView" -> [("blocks", "list", "view.blocks", [("max_items", 100)], []), ("private_metadata", "string", "view.private_metadata", [("max_length", 3000)], []), ("callback_id", "string", "view.callback_id", [("max_length", 255)], []), ("external_id", "string", "view.external_id", [("max_length", 255)], [])]
+  "MessagePayload" -> [("channel", "string", "message.channel", [("min_length", 1)], []), ("blocks", "list", "message.blocks", [("max_items", 50)], []), ("attachments", "list", "message.attachments", [("max_items", 100)], [])]
+  "MessageResponse" -> [("blocks", "list", "message.blocks", [("max_items", 50)], []), ("attachments", "list", "message.attachments", [("max_items", 100)], [])]
+  "ModalView" -> [("title", "text", "view.title", [("max_length", 24)], []), ("blocks", "list", "view.blocks", [("max_items", 100)], []), ("close", "text", "view.close", [("max_length", 24)], []), ("submit", "text", "view.submit", [("max_length", 24)], []), ("private_metadata", "string", "view.private_metadata", [("max_length", 3000)], []), ("callback_id", "string", "view.callback_id", [("max_length", 255)], []), ("external_id", "string", "view.external_id", [("max_length", 255)], [])]
+  "WebhookMessage" -> [("blocks", "list", "message.blocks", [("max_items", 50)], []), ("attachments", "list", "message.attachments", [("max_items", 100)], [])]
+  _ -> []
+
+limits :: [(Text, Integer)]
+limits = [("text.min_length", 1), ("text.max_length", 3000), ("action_id.max_length", 255), ("block_id.max_length", 255), ("button.text.max_length", 75), ("button.url.max_length", 3000), ("button.value.max_length", 2000), ("button.accessibility_label.max_length", 75), ("button.agent_prompt.max_length", 4000), ("confirmation.title.max_length", 100), ("confirmation.text.max_length", 300), ("confirmation.confirm.max_length", 30), ("confirmation.deny.max_length", 30), ("option.text.max_length", 75), ("option.value.max_length", 150), ("option.description.max_length", 75), ("option.url.max_length", 3000), ("option_group.label.max_length", 75), ("option_group.options.min_items", 1), ("option_group.options.max_items", 100), ("select.placeholder.max_length", 150), ("select.options.min_items", 1), ("select.options.max_items", 100), ("select.option_groups.min_items", 1), ("select.option_groups.max_items", 100), ("select.min_query_length.min", 0), ("overflow.options.min_items", 1), ("overflow.options.max_items", 5), ("checkboxes.options.min_items", 1), ("checkboxes.options.max_items", 10), ("radio_buttons.options.min_items", 1), ("radio_buttons.options.max_items", 10), ("file_input.max_files.min", 1), ("file_input.max_files.max", 10), ("plain_text_input.min_length.min", 0), ("plain_text_input.min_length.max", 3000), ("plain_text_input.max_length.min", 1), ("plain_text_input.max_length.max", 3000), ("plain_text_input.placeholder.max_length", 150), ("email_input.placeholder.max_length", 150), ("url_input.placeholder.max_length", 150), ("number_input.placeholder.max_length", 150), ("date_picker.placeholder.max_length", 150), ("time_picker.placeholder.max_length", 150), ("rich_text_input.placeholder.max_length", 150), ("rich_text_input.min_lines.min", 1), ("rich_text_input.min_lines.max", 100), ("rich_text_input.max_lines.min", 1), ("rich_text_input.max_lines.max", 100), ("multi_select.max_selected_items.min", 1), ("conversation_filter.include.min_items", 1), ("dispatch_action_configuration.trigger_actions_on.min_items", 1), ("dispatch_action_configuration.trigger_actions_on.max_items", 2), ("actions.elements.max_items", 25), ("alert.text.max_length", 200), ("card.title.max_length", 150), ("card.subtitle.max_length", 150), ("card.body.max_length", 200), ("card.actions.max_items", 3), ("card.subtext.max_length", 200), ("carousel.elements.min_items", 1), ("carousel.elements.max_items", 10), ("container.title.max_length", 150), ("container.subtitle.max_length", 150), ("container.child_blocks.min_items", 1), ("container.child_blocks.max_items", 10), ("context.elements.max_items", 10), ("context_actions.elements.max_items", 5), ("feedback_button.text.max_length", 75), ("feedback_button.value.max_length", 2000), ("feedback_button.accessibility_label.max_length", 75), ("icon_button.value.max_length", 2000), ("icon_button.accessibility_label.max_length", 75), ("icon_button.visible_to_user_ids.max_items", 10), ("workflow_button.text.max_length", 75), ("workflow_button.accessibility_label.max_length", 75), ("table.rows.max_items", 100), ("table.columns.max_items", 20), ("table.column_settings.max_items", 20), ("data_table.rows.min_items", 2), ("data_table.rows.max_items", 201), ("data_table.columns.min_items", 1), ("data_table.columns.max_items", 20), ("data_table.page_size.min", 1), ("data_table.page_size.max", 100), ("data_table.cell_text.min_length", 1), ("data_table.content.max_length", 20000), ("data_table.total_content.max_length", 20000), ("data_table.row_header_column_index.min", 0), ("data_visualization.title.max_length", 50), ("data_visualization.segments.min_items", 1), ("data_visualization.segments.max_items", 12), ("data_visualization.segment.label.max_length", 20), ("data_visualization.segment.value.exclusive_min", 0), ("data_visualization.series.min_items", 1), ("data_visualization.series.max_items", 12), ("data_visualization.series_name.max_length", 20), ("data_visualization.data.min_items", 1), ("data_visualization.data.max_items", 20), ("data_visualization.point_label.max_length", 20), ("data_visualization.categories.min_items", 1), ("data_visualization.categories.max_items", 20), ("data_visualization.category_label.max_length", 20), ("data_visualization.axis_label.max_length", 50), ("header.text.max_length", 150), ("image.image_url.max_length", 3000), ("image.alt_text.max_length", 2000), ("image.title.max_length", 2000), ("image_element.image_url.max_length", 3000), ("image_element.alt_text.max_length", 2000), ("input.label.max_length", 2000), ("input.hint.max_length", 2000), ("markdown.text.max_length", 12000), ("markdown.total_text.max_length", 12000), ("plan.tasks.max_items", 50), ("rich_text_list.indent.min", 0), ("rich_text_list.indent.max", 8), ("rich_text_list.offset.min", 0), ("rich_text_list.border.min", 0), ("rich_text_list.border.max", 1), ("rich_text_quote.border.min", 0), ("rich_text_quote.border.max", 1), ("rich_text_preformatted.border.min", 0), ("rich_text_preformatted.border.max", 1), ("section.text.max_length", 3000), ("section.fields.max_items", 10), ("section.fields.item_max_length", 2000), ("video.alt_text.max_length", 2000), ("video.title.max_length", 200), ("video.author_name.max_length", 50), ("video.description.max_length", 200), ("video.provider_name.max_length", 50), ("video.thumbnail_url.max_length", 3000), ("video.video_url.max_length", 3000), ("video.title_url.max_length", 2000), ("video.provider_icon_url.max_length", 2000), ("message.data_visualizations.max_items", 2), ("message.channel.min_length", 1), ("message.blocks.max_items", 50), ("message.attachments.max_items", 100), ("view.blocks.max_items", 100), ("view.private_metadata.max_length", 3000), ("view.callback_id.max_length", 255), ("view.external_id.max_length", 255), ("view.title.max_length", 24), ("view.close.max_length", 24), ("view.submit.max_length", 24)]
+
+icons :: [Text]
+icons = ["archive", "book", "bookmark", "bot", "bug", "calendar", "call", "caret-left", "caret-right", "check", "clipboard", "code", "comment", "compass", "copy", "cube", "download", "edit", "email", "eye-closed", "eye-open", "file", "flag", "folder", "gear", "globe", "heart", "help", "image", "info", "key", "lightbulb", "link", "map", "mobile", "new-window", "pin", "plus", "refine", "refresh", "rocket", "save", "screen", "share", "sparkle", "star", "star-filled", "tag", "thumbs-down", "thumbs-up", "trash", "upload", "user", "warning"]
+
+surfaceBlocks :: Text -> [Text]
+surfaceBlocks surface = case surface of
+  "home" -> ["actions", "card", "carousel", "container", "context", "data_table", "data_visualization", "divider", "header", "image", "input", "rich_text", "section", "table", "video"]
+  "message" -> ["actions", "card", "carousel", "container", "context", "context_actions", "data_table", "data_visualization", "divider", "file", "header", "image", "input", "markdown", "plan", "rich_text", "section", "table", "task_card", "video"]
+  "modal" -> ["actions", "alert", "card", "context", "divider", "header", "image", "input", "rich_text", "section", "video"]
+  _ -> []
diff --git a/src/Slackblocks/Internal/Wire.hs b/src/Slackblocks/Internal/Wire.hs
new file mode 100644
--- /dev/null
+++ b/src/Slackblocks/Internal/Wire.hs
@@ -0,0 +1,99 @@
+module Slackblocks.Internal.Wire
+  ( optional,
+    extensionPairs,
+    checkExtensions,
+    checkedParser,
+    wireObject,
+    wireTag,
+    required,
+    optionalField,
+    remaining,
+    coerceField,
+    stripTag,
+    restoreTask,
+    restoreTasks,
+  )
+where
+
+import Data.Aeson (ToJSON (..), Value (..), object, (.=))
+import Data.Aeson.Key qualified as Key
+import Data.Aeson.KeyMap qualified as KM
+import Data.Aeson.Types (Pair, Parser)
+import Data.Bifunctor (first)
+import Data.Map.Strict (Map)
+import Data.Map.Strict qualified as Map
+import Data.Text (Text)
+import Slackblocks.Json
+import Slackblocks.Validation
+
+optional :: (ToJSON a) => Text -> Maybe a -> [Pair]
+optional key = maybe [] (\value -> [Key.fromText key .= value])
+
+extensionPairs :: Map Text Value -> [Pair]
+extensionPairs = map (first Key.fromText) . Map.toList
+
+checkExtensions :: [Text] -> Map Text Value -> Result ()
+checkExtensions fields = mapM_ check . Map.keys
+  where
+    check key
+      | key == "" || key == "type" || key `elem` fields =
+          failure InvalidUsage [Field key] "extensions.reserved" "extension collides with a reserved field"
+      | otherwise = Right ()
+
+checkedParser :: (FromSlackJSON a) => Value -> Parser a
+checkedParser = either (fail . show) pure . fromSlackJSON
+
+wireObject :: Text -> Value -> Result (KM.KeyMap Value)
+wireObject tag (Object fields)
+  | tag == "" = Right fields
+  | KM.lookup "type" fields == Just (String tag) = Right (KM.delete "type" fields)
+  | otherwise = failure TypeMismatch [Field "type"] "type" "unexpected wire discriminator"
+wireObject _ _ = failure TypeMismatch [] "type" "expected an object"
+
+wireTag :: Value -> Result Text
+wireTag (Object fields) = required "type" fields
+wireTag _ = failure TypeMismatch [] "type" "expected an object with a discriminator"
+
+required :: (FromSlackJSON a) => Text -> KM.KeyMap Value -> Result a
+required key fields = case KM.lookup (Key.fromText key) fields of
+  Nothing -> absent
+  Just Null -> absent
+  Just value -> atField key (fromSlackJSON value)
+  where
+    absent = failure MissingRequired [Field key] "required" "required field is missing"
+
+optionalField :: (FromSlackJSON a) => Text -> KM.KeyMap Value -> Result (Maybe a)
+optionalField key fields = case KM.lookup (Key.fromText key) fields of
+  Nothing -> Right Nothing
+  Just Null -> Right Nothing
+  Just value -> Just <$> atField key (fromSlackJSON value)
+
+remaining :: [Text] -> KM.KeyMap Value -> Map Text Value
+remaining names =
+  Map.fromList
+    . map (first Key.toText)
+    . KM.toList
+    . KM.filterWithKey (\key _ -> Key.toText key `notElem` names)
+
+coerceField :: Text -> Text -> KM.KeyMap Value -> KM.KeyMap Value
+coerceField field tag = KM.mapWithKey (\key value -> if Key.toText key == field then coerce value else value)
+  where
+    coerce (Number value) | tag == "decimal_string" = String (decimalText value)
+    coerce value | tag == "decimal_string" = value
+    coerce (String value) = object ["type" .= tag, "text" .= value]
+    coerce (Array values) = Array (fmap coerce values)
+    coerce value = value
+
+stripTag :: Value -> Value
+stripTag (Object fields) = Object (KM.delete "type" fields)
+stripTag value = value
+
+restoreTasks :: KM.KeyMap Value -> KM.KeyMap Value
+restoreTasks = KM.mapWithKey (\key value -> if key == "tasks" then restore value else value)
+  where
+    restore (Array tasks) = Array (fmap restoreTask tasks)
+    restore value = value
+
+restoreTask :: Value -> Value
+restoreTask (Object fields) = Object (if KM.member "type" fields then fields else KM.insert "type" (String "task_card") fields)
+restoreTask value = value
diff --git a/src/Slackblocks/Json.hs b/src/Slackblocks/Json.hs
new file mode 100644
--- /dev/null
+++ b/src/Slackblocks/Json.hs
@@ -0,0 +1,67 @@
+{-# LANGUAGE FlexibleInstances #-}
+
+-- | Checked JSON ingress. Unknown modeled values are rejected; opaque metadata
+-- remains ordinary Aeson 'Value' and is never traversed as Block Kit content.
+module Slackblocks.Json (FromSlackJSON (..), decode, fromValue, decimalText) where
+
+import Data.Aeson (Value (..))
+import Data.Aeson.Key qualified as Key
+import Data.Aeson.KeyMap qualified as KM
+import Data.ByteString.Lazy qualified as B
+import Data.Map.Strict (Map)
+import Data.Map.Strict qualified as Map
+import Data.Scientific (FPFormat (Generic), Scientific, base10Exponent, coefficient, floatingOrInteger, formatScientific)
+import Data.Text (Text)
+import Data.Text qualified as T
+import Data.Vector qualified as V
+import Slackblocks.Internal.JsonSyntax (checkJSONNumbers, encodableScientific, parseJSONBytes)
+import Slackblocks.Validation
+
+-- | Validate an Aeson value as a supported modeled type. It is not an arbitrary Slack event parser.
+class FromSlackJSON a where
+  -- | Check nested fields and receiving context; return structured validation errors.
+  fromSlackJSON :: Value -> Result a
+
+-- | Validate an already parsed value. Use decode to retain duplicate-key and UTF-8 checks on raw bytes.
+fromValue :: (FromSlackJSON a) => Value -> Result a
+fromValue = fromSlackJSON
+
+-- | Exact numeric convenience for a number-input bound. No binary floating-point
+-- conversion is performed; the wire representation remains a decimal string.
+decimalText :: Scientific -> Text
+decimalText value
+  | encodableScientific value = T.pack (formatScientific Generic Nothing value)
+  | otherwise = T.pack (show (coefficient value) <> "e" <> show (base10Exponent value))
+
+-- | Decode UTF-8 JSON bytes, rejecting duplicate keys, trailing input, numeric
+-- exponent overflow in Aeson's Scientific representation and invalid modeled values.
+decode :: (FromSlackJSON a) => B.ByteString -> Result a
+decode input = parseJSONBytes input >>= fromSlackJSON
+
+instance FromSlackJSON Text where
+  fromSlackJSON (String value) = Right value
+  fromSlackJSON _ = failure TypeMismatch [] "type" "expected text"
+
+instance FromSlackJSON Bool where
+  fromSlackJSON (Bool value) = Right value
+  fromSlackJSON _ = failure TypeMismatch [] "type" "expected a boolean"
+
+instance FromSlackJSON Integer where
+  fromSlackJSON (Number value) = case floatingOrInteger value :: Either Double Integer of
+    Right integer -> Right integer
+    Left _ -> failure TypeMismatch [] "type" "expected an integer"
+  fromSlackJSON _ = failure TypeMismatch [] "type" "expected an integer"
+
+instance FromSlackJSON Scientific where
+  fromSlackJSON value@(Number number) = checkJSONNumbers value >> Right number
+  fromSlackJSON _ = failure TypeMismatch [] "type" "expected a number"
+
+instance (FromSlackJSON a) => FromSlackJSON [a] where
+  fromSlackJSON (Array values) = traverse (\(index, value) -> atIndex index (fromSlackJSON value)) (zip [0 ..] (V.toList values))
+  fromSlackJSON _ = failure TypeMismatch [] "type" "expected an array"
+
+instance FromSlackJSON (Map Text Value) where
+  fromSlackJSON value@(Object fields) = do
+    checkJSONNumbers value
+    Right (Map.fromList [(Key.toText key, item) | (key, item) <- KM.toList fields])
+  fromSlackJSON _ = failure TypeMismatch [] "type" "expected an object"
diff --git a/src/Slackblocks/Models.hs b/src/Slackblocks/Models.hs
new file mode 100644
--- /dev/null
+++ b/src/Slackblocks/Models.hs
@@ -0,0 +1,7051 @@
+-- Generated by haskell/generator/generate.py. Edit the shared contract instead.
+
+-- | Native checked models, construction fields and closed role conversions.
+module Slackblocks.Models
+  ( packageVersion,
+    specVersion,
+    AlertLevel (..),
+    ContainerWidth (..),
+    TaskStatus (..),
+    ButtonStyle (..),
+    IconButtonIcon (..),
+    ColumnAlign (..),
+    RichTextListStyle (..),
+    ResponseType (..),
+    ActionsBlock,
+    ActionsBlockFields (..),
+    actionsBlock,
+    buildActionsBlock,
+    inspectActionsBlock,
+    AlertBlock,
+    AlertBlockFields (..),
+    alertBlock,
+    buildAlertBlock,
+    inspectAlertBlock,
+    CardBlock,
+    CardBlockFields (..),
+    cardBlock,
+    buildCardBlock,
+    inspectCardBlock,
+    CarouselBlock,
+    CarouselBlockFields (..),
+    carouselBlock,
+    buildCarouselBlock,
+    inspectCarouselBlock,
+    ContainerBlock,
+    ContainerBlockFields (..),
+    containerBlock,
+    buildContainerBlock,
+    inspectContainerBlock,
+    ContextActionsBlock,
+    ContextActionsBlockFields (..),
+    contextActionsBlock,
+    buildContextActionsBlock,
+    inspectContextActionsBlock,
+    ContextBlock,
+    ContextBlockFields (..),
+    contextBlock,
+    buildContextBlock,
+    inspectContextBlock,
+    DataTableBlock,
+    DataTableBlockFields (..),
+    dataTableBlock,
+    buildDataTableBlock,
+    inspectDataTableBlock,
+    DataVisualizationBlock,
+    DataVisualizationBlockFields (..),
+    dataVisualizationBlock,
+    buildDataVisualizationBlock,
+    inspectDataVisualizationBlock,
+    DividerBlock,
+    DividerBlockFields (..),
+    dividerBlock,
+    buildDividerBlock,
+    inspectDividerBlock,
+    FileBlock,
+    FileBlockFields (..),
+    fileBlock,
+    buildFileBlock,
+    inspectFileBlock,
+    HeaderBlock,
+    HeaderBlockFields (..),
+    headerBlock,
+    buildHeaderBlock,
+    inspectHeaderBlock,
+    ImageBlock,
+    ImageBlockFields (..),
+    imageBlock,
+    buildImageBlock,
+    inspectImageBlock,
+    InputBlock,
+    InputBlockFields (..),
+    inputBlock,
+    buildInputBlock,
+    inspectInputBlock,
+    MarkdownBlock,
+    MarkdownBlockFields (..),
+    markdownBlock,
+    buildMarkdownBlock,
+    inspectMarkdownBlock,
+    PlanBlock,
+    PlanBlockFields (..),
+    planBlock,
+    buildPlanBlock,
+    inspectPlanBlock,
+    RichTextBlock,
+    RichTextBlockFields (..),
+    richTextBlock,
+    buildRichTextBlock,
+    inspectRichTextBlock,
+    SectionBlock,
+    SectionBlockFields (..),
+    sectionBlock,
+    buildSectionBlock,
+    inspectSectionBlock,
+    TableBlock,
+    TableBlockFields (..),
+    tableBlock,
+    buildTableBlock,
+    inspectTableBlock,
+    TaskCardBlock,
+    TaskCardBlockFields (..),
+    taskCardBlock,
+    buildTaskCardBlock,
+    inspectTaskCardBlock,
+    VideoBlock,
+    VideoBlockFields (..),
+    videoBlock,
+    buildVideoBlock,
+    inspectVideoBlock,
+    ButtonElement,
+    ButtonElementFields (..),
+    buttonElement,
+    buildButtonElement,
+    inspectButtonElement,
+    ChannelMultiSelectElement,
+    ChannelMultiSelectElementFields (..),
+    channelMultiSelectElement,
+    buildChannelMultiSelectElement,
+    inspectChannelMultiSelectElement,
+    ChannelSelectElement,
+    ChannelSelectElementFields (..),
+    channelSelectElement,
+    buildChannelSelectElement,
+    inspectChannelSelectElement,
+    CheckboxesElement,
+    CheckboxesElementFields (..),
+    checkboxesElement,
+    buildCheckboxesElement,
+    inspectCheckboxesElement,
+    ConversationMultiSelectElement,
+    ConversationMultiSelectElementFields (..),
+    conversationMultiSelectElement,
+    buildConversationMultiSelectElement,
+    inspectConversationMultiSelectElement,
+    ConversationSelectElement,
+    ConversationSelectElementFields (..),
+    conversationSelectElement,
+    buildConversationSelectElement,
+    inspectConversationSelectElement,
+    DatePickerElement,
+    DatePickerElementFields (..),
+    datePickerElement,
+    buildDatePickerElement,
+    inspectDatePickerElement,
+    DateTimePickerElement,
+    DateTimePickerElementFields (..),
+    dateTimePickerElement,
+    buildDateTimePickerElement,
+    inspectDateTimePickerElement,
+    EmailInputElement,
+    EmailInputElementFields (..),
+    emailInputElement,
+    buildEmailInputElement,
+    inspectEmailInputElement,
+    ExternalMultiSelectElement,
+    ExternalMultiSelectElementFields (..),
+    externalMultiSelectElement,
+    buildExternalMultiSelectElement,
+    inspectExternalMultiSelectElement,
+    ExternalSelectElement,
+    ExternalSelectElementFields (..),
+    externalSelectElement,
+    buildExternalSelectElement,
+    inspectExternalSelectElement,
+    FeedbackButtonsElement,
+    FeedbackButtonsElementFields (..),
+    feedbackButtonsElement,
+    buildFeedbackButtonsElement,
+    inspectFeedbackButtonsElement,
+    FileInputElement,
+    FileInputElementFields (..),
+    fileInputElement,
+    buildFileInputElement,
+    inspectFileInputElement,
+    IconButtonElement,
+    IconButtonElementFields (..),
+    iconButtonElement,
+    buildIconButtonElement,
+    inspectIconButtonElement,
+    ImageElement,
+    ImageElementFields (..),
+    imageElement,
+    buildImageElement,
+    inspectImageElement,
+    NumberInputElement,
+    NumberInputElementFields (..),
+    numberInputElement,
+    buildNumberInputElement,
+    inspectNumberInputElement,
+    OverflowElement,
+    OverflowElementFields (..),
+    overflowElement,
+    buildOverflowElement,
+    inspectOverflowElement,
+    PlainTextInputElement,
+    PlainTextInputElementFields (..),
+    plainTextInputElement,
+    buildPlainTextInputElement,
+    inspectPlainTextInputElement,
+    RadioButtonsElement,
+    RadioButtonsElementFields (..),
+    radioButtonsElement,
+    buildRadioButtonsElement,
+    inspectRadioButtonsElement,
+    RichTextInputElement,
+    RichTextInputElementFields (..),
+    richTextInputElement,
+    buildRichTextInputElement,
+    inspectRichTextInputElement,
+    StaticMultiSelectElement,
+    StaticMultiSelectElementFields (..),
+    staticMultiSelectElement,
+    buildStaticMultiSelectElement,
+    inspectStaticMultiSelectElement,
+    StaticSelectElement,
+    StaticSelectElementFields (..),
+    staticSelectElement,
+    buildStaticSelectElement,
+    inspectStaticSelectElement,
+    TimePickerElement,
+    TimePickerElementFields (..),
+    timePickerElement,
+    buildTimePickerElement,
+    inspectTimePickerElement,
+    UrlInputElement,
+    UrlInputElementFields (..),
+    urlInputElement,
+    buildUrlInputElement,
+    inspectUrlInputElement,
+    UserMultiSelectElement,
+    UserMultiSelectElementFields (..),
+    userMultiSelectElement,
+    buildUserMultiSelectElement,
+    inspectUserMultiSelectElement,
+    UserSelectElement,
+    UserSelectElementFields (..),
+    userSelectElement,
+    buildUserSelectElement,
+    inspectUserSelectElement,
+    WorkflowButtonElement,
+    WorkflowButtonElementFields (..),
+    workflowButtonElement,
+    buildWorkflowButtonElement,
+    inspectWorkflowButtonElement,
+    AreaChart,
+    AreaChartFields (..),
+    areaChart,
+    buildAreaChart,
+    inspectAreaChart,
+    AxisConfig,
+    AxisConfigFields (..),
+    axisConfig,
+    buildAxisConfig,
+    inspectAxisConfig,
+    BarChart,
+    BarChartFields (..),
+    barChart,
+    buildBarChart,
+    inspectBarChart,
+    ChartSegment,
+    ChartSegmentFields (..),
+    chartSegment,
+    buildChartSegment,
+    inspectChartSegment,
+    ColumnSettings,
+    ColumnSettingsFields (..),
+    columnSettings,
+    buildColumnSettings,
+    inspectColumnSettings,
+    ConfirmationDialogue,
+    ConfirmationDialogueFields (..),
+    confirmationDialogue,
+    buildConfirmationDialogue,
+    inspectConfirmationDialogue,
+    ConversationFilter,
+    ConversationFilterFields (..),
+    conversationFilter,
+    buildConversationFilter,
+    inspectConversationFilter,
+    DataPoint,
+    DataPointFields (..),
+    dataPoint,
+    buildDataPoint,
+    inspectDataPoint,
+    DataSeries,
+    DataSeriesFields (..),
+    dataSeries,
+    buildDataSeries,
+    inspectDataSeries,
+    DispatchActionConfiguration,
+    DispatchActionConfigurationFields (..),
+    dispatchActionConfiguration,
+    buildDispatchActionConfiguration,
+    inspectDispatchActionConfiguration,
+    FeedbackButton,
+    FeedbackButtonFields (..),
+    feedbackButton,
+    buildFeedbackButton,
+    inspectFeedbackButton,
+    InputParameter,
+    InputParameterFields (..),
+    inputParameter,
+    buildInputParameter,
+    inspectInputParameter,
+    LineChart,
+    LineChartFields (..),
+    lineChart,
+    buildLineChart,
+    inspectLineChart,
+    MarkdownText,
+    MarkdownTextFields (..),
+    markdownText,
+    buildMarkdownText,
+    inspectMarkdownText,
+    SelectOption,
+    SelectOptionFields (..),
+    selectOption,
+    buildSelectOption,
+    inspectSelectOption,
+    SelectOptionGroup,
+    SelectOptionGroupFields (..),
+    selectOptionGroup,
+    buildSelectOptionGroup,
+    inspectSelectOptionGroup,
+    PieChart,
+    PieChartFields (..),
+    pieChart,
+    buildPieChart,
+    inspectPieChart,
+    PlainText,
+    PlainTextFields (..),
+    plainText,
+    buildPlainText,
+    inspectPlainText,
+    RawNumber,
+    RawNumberFields (..),
+    rawNumber,
+    buildRawNumber,
+    inspectRawNumber,
+    RawText,
+    RawTextFields (..),
+    rawText,
+    buildRawText,
+    inspectRawText,
+    RichTextChannel,
+    RichTextChannelFields (..),
+    richTextChannel,
+    buildRichTextChannel,
+    inspectRichTextChannel,
+    RichTextCodeBlock,
+    RichTextCodeBlockFields (..),
+    richTextCodeBlock,
+    buildRichTextCodeBlock,
+    inspectRichTextCodeBlock,
+    RichTextEmoji,
+    RichTextEmojiFields (..),
+    richTextEmoji,
+    buildRichTextEmoji,
+    inspectRichTextEmoji,
+    RichTextLink,
+    RichTextLinkFields (..),
+    richTextLink,
+    buildRichTextLink,
+    inspectRichTextLink,
+    RichTextList,
+    RichTextListFields (..),
+    richTextList,
+    buildRichTextList,
+    inspectRichTextList,
+    RichTextQuote,
+    RichTextQuoteFields (..),
+    richTextQuote,
+    buildRichTextQuote,
+    inspectRichTextQuote,
+    RichTextSection,
+    RichTextSectionFields (..),
+    richTextSection,
+    buildRichTextSection,
+    inspectRichTextSection,
+    RichTextText,
+    RichTextTextFields (..),
+    richTextText,
+    buildRichTextText,
+    inspectRichTextText,
+    RichTextUser,
+    RichTextUserFields (..),
+    richTextUser,
+    buildRichTextUser,
+    inspectRichTextUser,
+    RichTextUserGroup,
+    RichTextUserGroupFields (..),
+    richTextUserGroup,
+    buildRichTextUserGroup,
+    inspectRichTextUserGroup,
+    SlackFile,
+    SlackFileFields (..),
+    slackFile,
+    buildSlackFile,
+    inspectSlackFile,
+    SlackIcon,
+    SlackIconFields (..),
+    slackIcon,
+    buildSlackIcon,
+    inspectSlackIcon,
+    Trigger,
+    TriggerFields (..),
+    trigger,
+    buildTrigger,
+    inspectTrigger,
+    UrlSource,
+    UrlSourceFields (..),
+    urlSource,
+    buildUrlSource,
+    inspectUrlSource,
+    Workflow,
+    WorkflowFields (..),
+    workflow,
+    buildWorkflow,
+    inspectWorkflow,
+    Attachment,
+    AttachmentFields (..),
+    attachment,
+    buildAttachment,
+    inspectAttachment,
+    HomeTabView,
+    HomeTabViewFields (..),
+    homeTabView,
+    buildHomeTabView,
+    inspectHomeTabView,
+    MessagePayload,
+    MessagePayloadFields (..),
+    messagePayload,
+    buildMessagePayload,
+    inspectMessagePayload,
+    MessageResponse,
+    MessageResponseFields (..),
+    messageResponse,
+    buildMessageResponse,
+    inspectMessageResponse,
+    ModalView,
+    ModalViewFields (..),
+    modalView,
+    buildModalView,
+    inspectModalView,
+    WebhookMessage,
+    WebhookMessageFields (..),
+    webhookMessage,
+    buildWebhookMessage,
+    inspectWebhookMessage,
+    PlanTask,
+    buildPlanTask,
+    inspectPlanTask,
+    Block,
+    actionsBlockAsBlock,
+    matchBlockActionsBlock,
+    alertBlockAsBlock,
+    matchBlockAlertBlock,
+    cardBlockAsBlock,
+    matchBlockCardBlock,
+    carouselBlockAsBlock,
+    matchBlockCarouselBlock,
+    containerBlockAsBlock,
+    matchBlockContainerBlock,
+    contextActionsBlockAsBlock,
+    matchBlockContextActionsBlock,
+    contextBlockAsBlock,
+    matchBlockContextBlock,
+    dataTableBlockAsBlock,
+    matchBlockDataTableBlock,
+    dataVisualizationBlockAsBlock,
+    matchBlockDataVisualizationBlock,
+    dividerBlockAsBlock,
+    matchBlockDividerBlock,
+    fileBlockAsBlock,
+    matchBlockFileBlock,
+    headerBlockAsBlock,
+    matchBlockHeaderBlock,
+    imageBlockAsBlock,
+    matchBlockImageBlock,
+    inputBlockAsBlock,
+    matchBlockInputBlock,
+    markdownBlockAsBlock,
+    matchBlockMarkdownBlock,
+    planBlockAsBlock,
+    matchBlockPlanBlock,
+    richTextBlockAsBlock,
+    matchBlockRichTextBlock,
+    sectionBlockAsBlock,
+    matchBlockSectionBlock,
+    tableBlockAsBlock,
+    matchBlockTableBlock,
+    taskCardBlockAsBlock,
+    matchBlockTaskCardBlock,
+    videoBlockAsBlock,
+    matchBlockVideoBlock,
+    ContextElement,
+    imageElementAsContextElement,
+    matchContextElementImageElement,
+    markdownTextAsContextElement,
+    matchContextElementMarkdownText,
+    plainTextAsContextElement,
+    matchContextElementPlainText,
+    ContextActionsElement,
+    feedbackButtonsElementAsContextActionsElement,
+    matchContextActionsElementFeedbackButtonsElement,
+    iconButtonElementAsContextActionsElement,
+    matchContextActionsElementIconButtonElement,
+    TableCell,
+    richTextBlockAsTableCell,
+    matchTableCellRichTextBlock,
+    rawTextAsTableCell,
+    matchTableCellRawText,
+    DataTableCell,
+    richTextBlockAsDataTableCell,
+    matchDataTableCellRichTextBlock,
+    rawNumberAsDataTableCell,
+    matchDataTableCellRawNumber,
+    rawTextAsDataTableCell,
+    matchDataTableCellRawText,
+    Element,
+    buttonElementAsElement,
+    matchElementButtonElement,
+    channelMultiSelectElementAsElement,
+    matchElementChannelMultiSelectElement,
+    channelSelectElementAsElement,
+    matchElementChannelSelectElement,
+    checkboxesElementAsElement,
+    matchElementCheckboxesElement,
+    conversationMultiSelectElementAsElement,
+    matchElementConversationMultiSelectElement,
+    conversationSelectElementAsElement,
+    matchElementConversationSelectElement,
+    datePickerElementAsElement,
+    matchElementDatePickerElement,
+    dateTimePickerElementAsElement,
+    matchElementDateTimePickerElement,
+    emailInputElementAsElement,
+    matchElementEmailInputElement,
+    externalMultiSelectElementAsElement,
+    matchElementExternalMultiSelectElement,
+    externalSelectElementAsElement,
+    matchElementExternalSelectElement,
+    feedbackButtonsElementAsElement,
+    matchElementFeedbackButtonsElement,
+    fileInputElementAsElement,
+    matchElementFileInputElement,
+    iconButtonElementAsElement,
+    matchElementIconButtonElement,
+    imageElementAsElement,
+    matchElementImageElement,
+    numberInputElementAsElement,
+    matchElementNumberInputElement,
+    overflowElementAsElement,
+    matchElementOverflowElement,
+    plainTextInputElementAsElement,
+    matchElementPlainTextInputElement,
+    radioButtonsElementAsElement,
+    matchElementRadioButtonsElement,
+    richTextInputElementAsElement,
+    matchElementRichTextInputElement,
+    staticMultiSelectElementAsElement,
+    matchElementStaticMultiSelectElement,
+    staticSelectElementAsElement,
+    matchElementStaticSelectElement,
+    timePickerElementAsElement,
+    matchElementTimePickerElement,
+    urlInputElementAsElement,
+    matchElementUrlInputElement,
+    userMultiSelectElementAsElement,
+    matchElementUserMultiSelectElement,
+    userSelectElementAsElement,
+    matchElementUserSelectElement,
+    workflowButtonElementAsElement,
+    matchElementWorkflowButtonElement,
+    InputElement,
+    channelMultiSelectElementAsInputElement,
+    matchInputElementChannelMultiSelectElement,
+    channelSelectElementAsInputElement,
+    matchInputElementChannelSelectElement,
+    checkboxesElementAsInputElement,
+    matchInputElementCheckboxesElement,
+    conversationMultiSelectElementAsInputElement,
+    matchInputElementConversationMultiSelectElement,
+    conversationSelectElementAsInputElement,
+    matchInputElementConversationSelectElement,
+    datePickerElementAsInputElement,
+    matchInputElementDatePickerElement,
+    dateTimePickerElementAsInputElement,
+    matchInputElementDateTimePickerElement,
+    emailInputElementAsInputElement,
+    matchInputElementEmailInputElement,
+    externalMultiSelectElementAsInputElement,
+    matchInputElementExternalMultiSelectElement,
+    externalSelectElementAsInputElement,
+    matchInputElementExternalSelectElement,
+    fileInputElementAsInputElement,
+    matchInputElementFileInputElement,
+    numberInputElementAsInputElement,
+    matchInputElementNumberInputElement,
+    plainTextInputElementAsInputElement,
+    matchInputElementPlainTextInputElement,
+    radioButtonsElementAsInputElement,
+    matchInputElementRadioButtonsElement,
+    richTextInputElementAsInputElement,
+    matchInputElementRichTextInputElement,
+    staticMultiSelectElementAsInputElement,
+    matchInputElementStaticMultiSelectElement,
+    staticSelectElementAsInputElement,
+    matchInputElementStaticSelectElement,
+    timePickerElementAsInputElement,
+    matchInputElementTimePickerElement,
+    urlInputElementAsInputElement,
+    matchInputElementUrlInputElement,
+    userMultiSelectElementAsInputElement,
+    matchInputElementUserMultiSelectElement,
+    userSelectElementAsInputElement,
+    matchInputElementUserSelectElement,
+    TextObject,
+    markdownTextAsText,
+    matchTextObjectMarkdownText,
+    plainTextAsText,
+    matchTextObjectPlainText,
+    Chart,
+    areaChartAsChart,
+    matchChartAreaChart,
+    barChartAsChart,
+    matchChartBarChart,
+    lineChartAsChart,
+    matchChartLineChart,
+    pieChartAsChart,
+    matchChartPieChart,
+    RichTextBlockElement,
+    richTextCodeBlockAsRichTextBlockElement,
+    matchRichTextBlockElementRichTextCodeBlock,
+    richTextListAsRichTextBlockElement,
+    matchRichTextBlockElementRichTextList,
+    richTextQuoteAsRichTextBlockElement,
+    matchRichTextBlockElementRichTextQuote,
+    richTextSectionAsRichTextBlockElement,
+    matchRichTextBlockElementRichTextSection,
+    RichTextSectionElement,
+    richTextChannelAsRichTextSectionElement,
+    matchRichTextSectionElementRichTextChannel,
+    richTextEmojiAsRichTextSectionElement,
+    matchRichTextSectionElementRichTextEmoji,
+    richTextLinkAsRichTextSectionElement,
+    matchRichTextSectionElementRichTextLink,
+    richTextTextAsRichTextSectionElement,
+    matchRichTextSectionElementRichTextText,
+    richTextUserAsRichTextSectionElement,
+    matchRichTextSectionElementRichTextUser,
+    richTextUserGroupAsRichTextSectionElement,
+    matchRichTextSectionElementRichTextUserGroup,
+  )
+where
+
+import Data.Aeson (FromJSON (..), ToJSON (..), Value (..), object, (.=))
+import Data.Map.Strict (Map)
+import Data.Map.Strict qualified as Map
+import Data.Scientific (Scientific)
+import Data.Text (Text)
+import Slackblocks.Internal.Rules (checkIntegerBounds, standaloneTask, validateFields)
+import Slackblocks.Internal.Wire
+import Slackblocks.Json (FromSlackJSON (..))
+import Slackblocks.Style
+import Slackblocks.Validation
+
+-- | Coordinated package version, distinct from the shared contract.
+packageVersion :: Text
+
+-- | Shared Block Kit contract version implemented by this package.
+specVersion :: Text
+
+packageVersion = "3.1.0"
+
+specVersion = "1.4.0"
+
+-- | Severity levels supported by alert blocks.
+data AlertLevel
+  = -- | Slack wire value: @default@.
+    AlertLevelDefault
+  | -- | Slack wire value: @info@.
+    AlertLevelInfo
+  | -- | Slack wire value: @warning@.
+    AlertLevelWarning
+  | -- | Slack wire value: @error@.
+    AlertLevelError
+  | -- | Slack wire value: @success@.
+    AlertLevelSuccess
+  deriving (Eq, Ord, Show, Enum, Bounded)
+
+instance ToJSON AlertLevel where
+  toJSON AlertLevelDefault = String "default"
+  toJSON AlertLevelInfo = String "info"
+  toJSON AlertLevelWarning = String "warning"
+  toJSON AlertLevelError = String "error"
+  toJSON AlertLevelSuccess = String "success"
+
+instance FromSlackJSON AlertLevel where
+  fromSlackJSON value = case value of
+    String "default" -> Right AlertLevelDefault
+    String "info" -> Right AlertLevelInfo
+    String "warning" -> Right AlertLevelWarning
+    String "error" -> Right AlertLevelError
+    String "success" -> Right AlertLevelSuccess
+    _ -> failure TypeMismatch [] "enum" "unknown enumeration value"
+
+instance FromJSON AlertLevel where parseJSON = checkedParser
+
+-- | Widths supported by Slack container blocks.
+data ContainerWidth
+  = -- | Slack wire value: @narrow@.
+    ContainerWidthNarrow
+  | -- | Slack wire value: @standard@.
+    ContainerWidthStandard
+  | -- | Slack wire value: @wide@.
+    ContainerWidthWide
+  | -- | Slack wire value: @full@.
+    ContainerWidthFull
+  deriving (Eq, Ord, Show, Enum, Bounded)
+
+instance ToJSON ContainerWidth where
+  toJSON ContainerWidthNarrow = String "narrow"
+  toJSON ContainerWidthStandard = String "standard"
+  toJSON ContainerWidthWide = String "wide"
+  toJSON ContainerWidthFull = String "full"
+
+instance FromSlackJSON ContainerWidth where
+  fromSlackJSON value = case value of
+    String "narrow" -> Right ContainerWidthNarrow
+    String "standard" -> Right ContainerWidthStandard
+    String "wide" -> Right ContainerWidthWide
+    String "full" -> Right ContainerWidthFull
+    _ -> failure TypeMismatch [] "enum" "unknown enumeration value"
+
+instance FromJSON ContainerWidth where parseJSON = checkedParser
+
+-- | States supported by task card blocks.
+data TaskStatus
+  = -- | Slack wire value: @pending@.
+    TaskStatusPending
+  | -- | Slack wire value: @in_progress@.
+    TaskStatusInProgress
+  | -- | Slack wire value: @complete@.
+    TaskStatusComplete
+  | -- | Slack wire value: @error@.
+    TaskStatusError
+  deriving (Eq, Ord, Show, Enum, Bounded)
+
+instance ToJSON TaskStatus where
+  toJSON TaskStatusPending = String "pending"
+  toJSON TaskStatusInProgress = String "in_progress"
+  toJSON TaskStatusComplete = String "complete"
+  toJSON TaskStatusError = String "error"
+
+instance FromSlackJSON TaskStatus where
+  fromSlackJSON value = case value of
+    String "pending" -> Right TaskStatusPending
+    String "in_progress" -> Right TaskStatusInProgress
+    String "complete" -> Right TaskStatusComplete
+    String "error" -> Right TaskStatusError
+    _ -> failure TypeMismatch [] "enum" "unknown enumeration value"
+
+instance FromJSON TaskStatus where parseJSON = checkedParser
+
+-- | Visual emphasis for buttons and confirmation dialogs.
+data ButtonStyle
+  = -- | Slack wire value: @primary@.
+    ButtonStylePrimary
+  | -- | Slack wire value: @danger@.
+    ButtonStyleDanger
+  deriving (Eq, Ord, Show, Enum, Bounded)
+
+instance ToJSON ButtonStyle where
+  toJSON ButtonStylePrimary = String "primary"
+  toJSON ButtonStyleDanger = String "danger"
+
+instance FromSlackJSON ButtonStyle where
+  fromSlackJSON value = case value of
+    String "primary" -> Right ButtonStylePrimary
+    String "danger" -> Right ButtonStyleDanger
+    _ -> failure TypeMismatch [] "enum" "unknown enumeration value"
+
+instance FromJSON ButtonStyle where parseJSON = checkedParser
+
+-- | Icons supported by icon buttons.
+data IconButtonIcon
+  = -- | Slack wire value: @trash@.
+    IconButtonIconTrash
+  deriving (Eq, Ord, Show, Enum, Bounded)
+
+instance ToJSON IconButtonIcon where
+  toJSON IconButtonIconTrash = String "trash"
+
+instance FromSlackJSON IconButtonIcon where
+  fromSlackJSON value = case value of
+    String "trash" -> Right IconButtonIconTrash
+    _ -> failure TypeMismatch [] "enum" "unknown enumeration value"
+
+instance FromJSON IconButtonIcon where parseJSON = checkedParser
+
+-- | Horizontal alignment for table columns.
+data ColumnAlign
+  = -- | Slack wire value: @left@.
+    ColumnAlignLeft
+  | -- | Slack wire value: @center@.
+    ColumnAlignCenter
+  | -- | Slack wire value: @right@.
+    ColumnAlignRight
+  deriving (Eq, Ord, Show, Enum, Bounded)
+
+instance ToJSON ColumnAlign where
+  toJSON ColumnAlignLeft = String "left"
+  toJSON ColumnAlignCenter = String "center"
+  toJSON ColumnAlignRight = String "right"
+
+instance FromSlackJSON ColumnAlign where
+  fromSlackJSON value = case value of
+    String "left" -> Right ColumnAlignLeft
+    String "center" -> Right ColumnAlignCenter
+    String "right" -> Right ColumnAlignRight
+    _ -> failure TypeMismatch [] "enum" "unknown enumeration value"
+
+instance FromJSON ColumnAlign where parseJSON = checkedParser
+
+-- | Marker styles for rich text lists.
+data RichTextListStyle
+  = -- | Slack wire value: @bullet@.
+    RichTextListStyleBullet
+  | -- | Slack wire value: @ordered@.
+    RichTextListStyleOrdered
+  deriving (Eq, Ord, Show, Enum, Bounded)
+
+instance ToJSON RichTextListStyle where
+  toJSON RichTextListStyleBullet = String "bullet"
+  toJSON RichTextListStyleOrdered = String "ordered"
+
+instance FromSlackJSON RichTextListStyle where
+  fromSlackJSON value = case value of
+    String "bullet" -> Right RichTextListStyleBullet
+    String "ordered" -> Right RichTextListStyleOrdered
+    _ -> failure TypeMismatch [] "enum" "unknown enumeration value"
+
+instance FromJSON RichTextListStyle where parseJSON = checkedParser
+
+-- | Visibility of a slash-command or interaction response.
+data ResponseType
+  = -- | Slack wire value: @in_channel@.
+    ResponseTypeInChannel
+  | -- | Slack wire value: @ephemeral@.
+    ResponseTypeEphemeral
+  deriving (Eq, Ord, Show, Enum, Bounded)
+
+instance ToJSON ResponseType where
+  toJSON ResponseTypeInChannel = String "in_channel"
+  toJSON ResponseTypeEphemeral = String "ephemeral"
+
+instance FromSlackJSON ResponseType where
+  fromSlackJSON value = case value of
+    String "in_channel" -> Right ResponseTypeInChannel
+    String "ephemeral" -> Right ResponseTypeEphemeral
+    _ -> failure TypeMismatch [] "enum" "unknown enumeration value"
+
+instance FromJSON ResponseType where parseJSON = checkedParser
+
+-- | A block that holds interactive elements such as buttons and menus.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/actions-block>
+newtype ActionsBlock = CheckedActionsBlock ActionsBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildActionsBlock' before encoding.
+data ActionsBlockFields = ActionsBlockFields
+  { -- | Adds interactive elements in display order. Required. Shared limit: @actions.elements@.
+    actionsBlockElements :: [Element],
+    -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    actionsBlockBlockId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    actionsBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for ActionsBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildActionsBlock' to validate before JSON encoding.
+actionsBlock :: [Element] -> ActionsBlockFields
+actionsBlock arg0 = ActionsBlockFields (arg0) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildActionsBlock' to revalidate.
+inspectActionsBlock :: ActionsBlock -> ActionsBlockFields
+inspectActionsBlock (CheckedActionsBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked ActionsBlock.
+buildActionsBlock :: ActionsBlockFields -> Result ActionsBlock
+buildActionsBlock fields = do
+  checkExtensions ["elements", "block_id"] (actionsBlockExtensions fields)
+  validateFields "ActionsBlock" (wireActionsBlock fields)
+  pure (CheckedActionsBlock fields)
+
+wireActionsBlock :: ActionsBlockFields -> Value
+wireActionsBlock fields =
+  object $
+    ["type" .= ("actions" :: Text)] <> ["elements" .= (actionsBlockElements fields)] <> optional "block_id" (actionsBlockBlockId fields) <> extensionPairs (actionsBlockExtensions fields)
+
+instance ToJSON ActionsBlock where toJSON (CheckedActionsBlock fields) = wireActionsBlock fields
+
+instance FromSlackJSON ActionsBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "actions" value
+    parsedElements <- required "elements" fields
+    parsedBlockId <- optionalField "block_id" fields
+    buildActionsBlock (ActionsBlockFields parsedElements parsedBlockId (remaining ["elements", "block_id"] fields))
+
+instance FromJSON ActionsBlock where parseJSON = checkedParser
+
+-- | A severity-labelled alert, available in modals.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/alert-block>
+newtype AlertBlock = CheckedAlertBlock AlertBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildAlertBlock' before encoding.
+data AlertBlockFields = AlertBlockFields
+  { -- | Sets the alert message. Required. Shared limit: @alert.text@.
+    alertBlockText :: TextObject,
+    -- | Sets the alert severity, which controls its color and icon. Optional; Nothing omits the field. Factory default: @Nothing@.
+    alertBlockLevel :: Maybe (AlertLevel),
+    -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    alertBlockBlockId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    alertBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for AlertBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildAlertBlock' to validate before JSON encoding.
+alertBlock :: TextObject -> AlertBlockFields
+alertBlock arg0 = AlertBlockFields (arg0) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildAlertBlock' to revalidate.
+inspectAlertBlock :: AlertBlock -> AlertBlockFields
+inspectAlertBlock (CheckedAlertBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked AlertBlock.
+buildAlertBlock :: AlertBlockFields -> Result AlertBlock
+buildAlertBlock fields = do
+  checkExtensions ["text", "level", "block_id"] (alertBlockExtensions fields)
+  validateFields "AlertBlock" (wireAlertBlock fields)
+  pure (CheckedAlertBlock fields)
+
+wireAlertBlock :: AlertBlockFields -> Value
+wireAlertBlock fields =
+  object $
+    ["type" .= ("alert" :: Text)] <> ["text" .= (alertBlockText fields)] <> optional "level" (alertBlockLevel fields) <> optional "block_id" (alertBlockBlockId fields) <> extensionPairs (alertBlockExtensions fields)
+
+instance ToJSON AlertBlock where toJSON (CheckedAlertBlock fields) = wireAlertBlock fields
+
+instance FromSlackJSON AlertBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "alert" value
+    parsedText <- required "text" (coerceField "text" "mrkdwn" fields)
+    parsedLevel <- optionalField "level" fields
+    parsedBlockId <- optionalField "block_id" fields
+    buildAlertBlock (AlertBlockFields parsedText parsedLevel parsedBlockId (remaining ["text", "level", "block_id"] fields))
+
+instance FromJSON AlertBlock where parseJSON = checkedParser
+
+-- | A compact card with an image, text, and up to three buttons.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/card-block>
+-- Provide at least one of a hero image, title, actions, or body.
+-- An image icon and a Slack icon cannot be combined.
+newtype CardBlock = CheckedCardBlock CardBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildCardBlock' before encoding.
+data CardBlockFields = CardBlockFields
+  { -- | Sets the image displayed prominently at the top of the card. Optional; Nothing omits the field. Factory default: @Nothing@.
+    cardBlockHeroImage :: Maybe (ImageElement),
+    -- | Sets the image displayed beside the title. Optional; Nothing omits the field. Factory default: @Nothing@.
+    cardBlockIcon :: Maybe (ImageElement),
+    -- | Sets the card title. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @card.title@.
+    cardBlockTitle :: Maybe (TextObject),
+    -- | Sets the supporting text shown below the title. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @card.subtitle@.
+    cardBlockSubtitle :: Maybe (TextObject),
+    -- | Sets the main body text of the card. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @card.body@.
+    cardBlockBody :: Maybe (TextObject),
+    -- | Adds buttons displayed at the bottom of the card, in display order. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @card.actions@.
+    cardBlockActions :: Maybe ([ButtonElement]),
+    -- | Sets a Slack-provided icon displayed beside the title. Cannot be combined with an image icon. Optional; Nothing omits the field. Factory default: @Nothing@.
+    cardBlockSlackIcon :: Maybe (SlackIcon),
+    -- | Sets the small print shown at the bottom of the card. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @card.subtext@.
+    cardBlockSubtext :: Maybe (TextObject),
+    -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    cardBlockBlockId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    cardBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for CardBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildCardBlock' to validate before JSON encoding.
+cardBlock :: CardBlockFields
+cardBlock = CardBlockFields (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildCardBlock' to revalidate.
+inspectCardBlock :: CardBlock -> CardBlockFields
+inspectCardBlock (CheckedCardBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked CardBlock.
+buildCardBlock :: CardBlockFields -> Result CardBlock
+buildCardBlock fields = do
+  checkExtensions ["hero_image", "icon", "title", "subtitle", "body", "actions", "slack_icon", "subtext", "block_id"] (cardBlockExtensions fields)
+  validateFields "CardBlock" (wireCardBlock fields)
+  pure (CheckedCardBlock fields)
+
+wireCardBlock :: CardBlockFields -> Value
+wireCardBlock fields =
+  object $
+    ["type" .= ("card" :: Text)] <> optional "hero_image" (cardBlockHeroImage fields) <> optional "icon" (cardBlockIcon fields) <> optional "title" (cardBlockTitle fields) <> optional "subtitle" (cardBlockSubtitle fields) <> optional "body" (cardBlockBody fields) <> optional "actions" (cardBlockActions fields) <> optional "slack_icon" (cardBlockSlackIcon fields) <> optional "subtext" (cardBlockSubtext fields) <> optional "block_id" (cardBlockBlockId fields) <> extensionPairs (cardBlockExtensions fields)
+
+instance ToJSON CardBlock where toJSON (CheckedCardBlock fields) = wireCardBlock fields
+
+instance FromSlackJSON CardBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "card" value
+    parsedHeroImage <- optionalField "hero_image" fields
+    parsedIcon <- optionalField "icon" fields
+    parsedTitle <- optionalField "title" (coerceField "title" "mrkdwn" fields)
+    parsedSubtitle <- optionalField "subtitle" (coerceField "subtitle" "mrkdwn" fields)
+    parsedBody <- optionalField "body" (coerceField "body" "mrkdwn" fields)
+    parsedActions <- optionalField "actions" fields
+    parsedSlackIcon <- optionalField "slack_icon" fields
+    parsedSubtext <- optionalField "subtext" (coerceField "subtext" "mrkdwn" fields)
+    parsedBlockId <- optionalField "block_id" fields
+    buildCardBlock (CardBlockFields parsedHeroImage parsedIcon parsedTitle parsedSubtitle parsedBody parsedActions parsedSlackIcon parsedSubtext parsedBlockId (remaining ["hero_image", "icon", "title", "subtitle", "body", "actions", "slack_icon", "subtext", "block_id"] fields))
+
+instance FromJSON CardBlock where parseJSON = checkedParser
+
+-- | A horizontally scrolling set of cards, available in messages and App Home.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/carousel-block>
+newtype CarouselBlock = CheckedCarouselBlock CarouselBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildCarouselBlock' before encoding.
+data CarouselBlockFields = CarouselBlockFields
+  { -- | Adds the cards in display order. Required. Shared limit: @carousel.elements@.
+    carouselBlockElements :: [CardBlock],
+    -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    carouselBlockBlockId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    carouselBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for CarouselBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildCarouselBlock' to validate before JSON encoding.
+carouselBlock :: [CardBlock] -> CarouselBlockFields
+carouselBlock arg0 = CarouselBlockFields (arg0) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildCarouselBlock' to revalidate.
+inspectCarouselBlock :: CarouselBlock -> CarouselBlockFields
+inspectCarouselBlock (CheckedCarouselBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked CarouselBlock.
+buildCarouselBlock :: CarouselBlockFields -> Result CarouselBlock
+buildCarouselBlock fields = do
+  checkExtensions ["elements", "block_id"] (carouselBlockExtensions fields)
+  validateFields "CarouselBlock" (wireCarouselBlock fields)
+  pure (CheckedCarouselBlock fields)
+
+wireCarouselBlock :: CarouselBlockFields -> Value
+wireCarouselBlock fields =
+  object $
+    ["type" .= ("carousel" :: Text)] <> ["elements" .= (carouselBlockElements fields)] <> optional "block_id" (carouselBlockBlockId fields) <> extensionPairs (carouselBlockExtensions fields)
+
+instance ToJSON CarouselBlock where toJSON (CheckedCarouselBlock fields) = wireCarouselBlock fields
+
+instance FromSlackJSON CarouselBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "carousel" value
+    parsedElements <- required "elements" fields
+    parsedBlockId <- optionalField "block_id" fields
+    buildCarouselBlock (CarouselBlockFields parsedElements parsedBlockId (remaining ["elements", "block_id"] fields))
+
+instance FromJSON CarouselBlock where parseJSON = checkedParser
+
+-- | A titled group of blocks that can optionally collapse.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/container-block>
+-- Provide a title or a rich text title.
+newtype ContainerBlock = CheckedContainerBlock ContainerBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildContainerBlock' before encoding.
+data ContainerBlockFields = ContainerBlockFields
+  { -- | Adds the blocks grouped inside the container, in display order. Required. Shared limit: @container.child_blocks@.
+    containerBlockChildBlocks :: [Block],
+    -- | Sets the container title. Provide either a title or a rich text title. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @container.title@.
+    containerBlockTitle :: Maybe (PlainText),
+    -- | Sets a rich text block used as the title in place of a plain-text title. Optional; Nothing omits the field. Factory default: @Nothing@.
+    containerBlockRichTextTitle :: Maybe (RichTextBlock),
+    -- | Sets the supporting text shown below the title. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @container.subtitle@.
+    containerBlockSubtitle :: Maybe (TextObject),
+    -- | Sets the horizontal width of the container. Optional; Nothing omits the field. Factory default: @Nothing@.
+    containerBlockWidth :: Maybe (ContainerWidth),
+    -- | Sets the image displayed beside the title. Optional; Nothing omits the field. Factory default: @Nothing@.
+    containerBlockIcon :: Maybe (ImageElement),
+    -- | Sets whether users can collapse and expand the container. Optional; Nothing omits the field. Factory default: @Nothing@.
+    containerBlockIsCollapsible :: Maybe (Bool),
+    -- | Sets whether a collapsible container starts collapsed. Requires the container to be collapsible. Optional; Nothing omits the field. Factory default: @Nothing@.
+    containerBlockDefaultCollapsed :: Maybe (Bool),
+    -- | Sets whether Slack draws a divider below the container header. Only valid for non-collapsible containers. Optional; Nothing omits the field. Factory default: @Nothing@.
+    containerBlockHasHeaderDivider :: Maybe (Bool),
+    -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    containerBlockBlockId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    containerBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for ContainerBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildContainerBlock' to validate before JSON encoding.
+containerBlock :: [Block] -> ContainerBlockFields
+containerBlock arg0 = ContainerBlockFields (arg0) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildContainerBlock' to revalidate.
+inspectContainerBlock :: ContainerBlock -> ContainerBlockFields
+inspectContainerBlock (CheckedContainerBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked ContainerBlock.
+buildContainerBlock :: ContainerBlockFields -> Result ContainerBlock
+buildContainerBlock fields = do
+  checkExtensions ["child_blocks", "title", "rich_text_title", "subtitle", "width", "icon", "is_collapsible", "default_collapsed", "has_header_divider", "block_id"] (containerBlockExtensions fields)
+  validateFields "ContainerBlock" (wireContainerBlock fields)
+  pure (CheckedContainerBlock fields)
+
+wireContainerBlock :: ContainerBlockFields -> Value
+wireContainerBlock fields =
+  object $
+    ["type" .= ("container" :: Text)] <> ["child_blocks" .= (containerBlockChildBlocks fields)] <> optional "title" (containerBlockTitle fields) <> optional "rich_text_title" (containerBlockRichTextTitle fields) <> optional "subtitle" (containerBlockSubtitle fields) <> optional "width" (containerBlockWidth fields) <> optional "icon" (containerBlockIcon fields) <> optional "is_collapsible" (containerBlockIsCollapsible fields) <> optional "default_collapsed" (containerBlockDefaultCollapsed fields) <> optional "has_header_divider" (containerBlockHasHeaderDivider fields) <> optional "block_id" (containerBlockBlockId fields) <> extensionPairs (containerBlockExtensions fields)
+
+instance ToJSON ContainerBlock where toJSON (CheckedContainerBlock fields) = wireContainerBlock fields
+
+instance FromSlackJSON ContainerBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "container" value
+    parsedChildBlocks <- required "child_blocks" fields
+    parsedTitle <- optionalField "title" (coerceField "title" "plain_text" fields)
+    parsedRichTextTitle <- optionalField "rich_text_title" fields
+    parsedSubtitle <- optionalField "subtitle" (coerceField "subtitle" "mrkdwn" fields)
+    parsedWidth <- optionalField "width" fields
+    parsedIcon <- optionalField "icon" fields
+    parsedIsCollapsible <- optionalField "is_collapsible" fields
+    parsedDefaultCollapsed <- optionalField "default_collapsed" fields
+    parsedHasHeaderDivider <- optionalField "has_header_divider" fields
+    parsedBlockId <- optionalField "block_id" fields
+    buildContainerBlock (ContainerBlockFields parsedChildBlocks parsedTitle parsedRichTextTitle parsedSubtitle parsedWidth parsedIcon parsedIsCollapsible parsedDefaultCollapsed parsedHasHeaderDivider parsedBlockId (remaining ["child_blocks", "title", "rich_text_title", "subtitle", "width", "icon", "is_collapsible", "default_collapsed", "has_header_divider", "block_id"] fields))
+
+instance FromJSON ContainerBlock where parseJSON = checkedParser
+
+-- | A row of feedback or icon buttons, usually shown below AI-generated content.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/context-actions-block>
+newtype ContextActionsBlock = CheckedContextActionsBlock ContextActionsBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildContextActionsBlock' before encoding.
+data ContextActionsBlockFields = ContextActionsBlockFields
+  { -- | Adds feedback buttons or icon buttons in display order. Required. Shared limit: @context_actions.elements@.
+    contextActionsBlockElements :: [ContextActionsElement],
+    -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    contextActionsBlockBlockId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    contextActionsBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for ContextActionsBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildContextActionsBlock' to validate before JSON encoding.
+contextActionsBlock :: [ContextActionsElement] -> ContextActionsBlockFields
+contextActionsBlock arg0 = ContextActionsBlockFields (arg0) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildContextActionsBlock' to revalidate.
+inspectContextActionsBlock :: ContextActionsBlock -> ContextActionsBlockFields
+inspectContextActionsBlock (CheckedContextActionsBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked ContextActionsBlock.
+buildContextActionsBlock :: ContextActionsBlockFields -> Result ContextActionsBlock
+buildContextActionsBlock fields = do
+  checkExtensions ["elements", "block_id"] (contextActionsBlockExtensions fields)
+  validateFields "ContextActionsBlock" (wireContextActionsBlock fields)
+  pure (CheckedContextActionsBlock fields)
+
+wireContextActionsBlock :: ContextActionsBlockFields -> Value
+wireContextActionsBlock fields =
+  object $
+    ["type" .= ("context_actions" :: Text)] <> ["elements" .= (contextActionsBlockElements fields)] <> optional "block_id" (contextActionsBlockBlockId fields) <> extensionPairs (contextActionsBlockExtensions fields)
+
+instance ToJSON ContextActionsBlock where toJSON (CheckedContextActionsBlock fields) = wireContextActionsBlock fields
+
+instance FromSlackJSON ContextActionsBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "context_actions" value
+    parsedElements <- required "elements" fields
+    parsedBlockId <- optionalField "block_id" fields
+    buildContextActionsBlock (ContextActionsBlockFields parsedElements parsedBlockId (remaining ["elements", "block_id"] fields))
+
+instance FromJSON ContextActionsBlock where parseJSON = checkedParser
+
+-- | A block of small, secondary images and text.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/context-block>
+newtype ContextBlock = CheckedContextBlock ContextBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildContextBlock' before encoding.
+data ContextBlockFields = ContextBlockFields
+  { -- | Adds images and text in display order. Required. Shared limit: @context.elements@.
+    contextBlockElements :: [ContextElement],
+    -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    contextBlockBlockId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    contextBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for ContextBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildContextBlock' to validate before JSON encoding.
+contextBlock :: [ContextElement] -> ContextBlockFields
+contextBlock arg0 = ContextBlockFields (arg0) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildContextBlock' to revalidate.
+inspectContextBlock :: ContextBlock -> ContextBlockFields
+inspectContextBlock (CheckedContextBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked ContextBlock.
+buildContextBlock :: ContextBlockFields -> Result ContextBlock
+buildContextBlock fields = do
+  checkExtensions ["elements", "block_id"] (contextBlockExtensions fields)
+  validateFields "ContextBlock" (wireContextBlock fields)
+  pure (CheckedContextBlock fields)
+
+wireContextBlock :: ContextBlockFields -> Value
+wireContextBlock fields =
+  object $
+    ["type" .= ("context" :: Text)] <> ["elements" .= (contextBlockElements fields)] <> optional "block_id" (contextBlockBlockId fields) <> extensionPairs (contextBlockExtensions fields)
+
+instance ToJSON ContextBlock where toJSON (CheckedContextBlock fields) = wireContextBlock fields
+
+instance FromSlackJSON ContextBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "context" value
+    parsedElements <- required "elements" fields
+    parsedBlockId <- optionalField "block_id" fields
+    buildContextBlock (ContextBlockFields parsedElements parsedBlockId (remaining ["elements", "block_id"] fields))
+
+instance FromJSON ContextBlock where parseJSON = checkedParser
+
+-- | A sortable, paginated table of raw text, numbers, and rich text.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/data-table-block>
+-- The first row is the header row and cannot contain rich text.
+-- Every row must have the same number of cells, and all cell text together is limited to 20,000 characters.
+newtype DataTableBlock = CheckedDataTableBlock DataTableBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildDataTableBlock' before encoding.
+data DataTableBlockFields = DataTableBlockFields
+  { -- | Adds complete table rows in display order. Every row must have the same number of cells. Required. Shared limit: @data_table.rows@.
+    dataTableBlockRows :: [[DataTableCell]],
+    -- | Sets the caption that describes the table's contents. Required.
+    dataTableBlockCaption :: Text,
+    -- | Sets how many rows Slack shows per page. Optional; Nothing omits the field. Factory default: @Just (5)@. Shared limit: @data_table.page_size@.
+    dataTableBlockPageSize :: Maybe (Integer),
+    -- | Sets the zero-based index of the column whose cells act as row headers. Optional; Nothing omits the field. Factory default: @Just (0)@. Shared limit: @data_table.row_header_column_index@.
+    dataTableBlockRowHeaderColumnIndex :: Maybe (Integer),
+    -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    dataTableBlockBlockId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    dataTableBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for DataTableBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildDataTableBlock' to validate before JSON encoding.
+dataTableBlock :: [[DataTableCell]] -> Text -> DataTableBlockFields
+dataTableBlock arg0 arg1 = DataTableBlockFields (arg0) (arg1) (Just (5)) (Just (0)) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildDataTableBlock' to revalidate.
+inspectDataTableBlock :: DataTableBlock -> DataTableBlockFields
+inspectDataTableBlock (CheckedDataTableBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked DataTableBlock.
+buildDataTableBlock :: DataTableBlockFields -> Result DataTableBlock
+buildDataTableBlock fields = do
+  checkExtensions ["rows", "caption", "page_size", "row_header_column_index", "block_id"] (dataTableBlockExtensions fields)
+  validateFields "DataTableBlock" (wireDataTableBlock fields)
+  pure (CheckedDataTableBlock fields)
+
+wireDataTableBlock :: DataTableBlockFields -> Value
+wireDataTableBlock fields =
+  object $
+    ["type" .= ("data_table" :: Text)] <> ["rows" .= (dataTableBlockRows fields)] <> ["caption" .= (dataTableBlockCaption fields)] <> optional "page_size" (dataTableBlockPageSize fields) <> optional "row_header_column_index" (dataTableBlockRowHeaderColumnIndex fields) <> optional "block_id" (dataTableBlockBlockId fields) <> extensionPairs (dataTableBlockExtensions fields)
+
+instance ToJSON DataTableBlock where toJSON (CheckedDataTableBlock fields) = wireDataTableBlock fields
+
+instance FromSlackJSON DataTableBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "data_table" value
+    checkIntegerBounds "DataTableBlock" (Object fields)
+    parsedRows <- required "rows" fields
+    parsedCaption <- required "caption" fields
+    parsedPageSize <- optionalField "page_size" fields
+    parsedRowHeaderColumnIndex <- optionalField "row_header_column_index" fields
+    parsedBlockId <- optionalField "block_id" fields
+    buildDataTableBlock (DataTableBlockFields parsedRows parsedCaption parsedPageSize parsedRowHeaderColumnIndex parsedBlockId (remaining ["rows", "caption", "page_size", "row_header_column_index", "block_id"] fields))
+
+instance FromJSON DataTableBlock where parseJSON = checkedParser
+
+-- | A titled pie, bar, area, or line chart.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block>
+newtype DataVisualizationBlock = CheckedDataVisualizationBlock DataVisualizationBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildDataVisualizationBlock' before encoding.
+data DataVisualizationBlockFields = DataVisualizationBlockFields
+  { -- | Sets the chart title. Required. Shared limit: @data_visualization.title@.
+    dataVisualizationBlockTitle :: Text,
+    -- | Sets the chart to display. Required.
+    dataVisualizationBlockChart :: Chart,
+    -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    dataVisualizationBlockBlockId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    dataVisualizationBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for DataVisualizationBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildDataVisualizationBlock' to validate before JSON encoding.
+dataVisualizationBlock :: Text -> Chart -> DataVisualizationBlockFields
+dataVisualizationBlock arg0 arg1 = DataVisualizationBlockFields (arg0) (arg1) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildDataVisualizationBlock' to revalidate.
+inspectDataVisualizationBlock :: DataVisualizationBlock -> DataVisualizationBlockFields
+inspectDataVisualizationBlock (CheckedDataVisualizationBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked DataVisualizationBlock.
+buildDataVisualizationBlock :: DataVisualizationBlockFields -> Result DataVisualizationBlock
+buildDataVisualizationBlock fields = do
+  checkExtensions ["title", "chart", "block_id"] (dataVisualizationBlockExtensions fields)
+  validateFields "DataVisualizationBlock" (wireDataVisualizationBlock fields)
+  pure (CheckedDataVisualizationBlock fields)
+
+wireDataVisualizationBlock :: DataVisualizationBlockFields -> Value
+wireDataVisualizationBlock fields =
+  object $
+    ["type" .= ("data_visualization" :: Text)] <> ["title" .= (dataVisualizationBlockTitle fields)] <> ["chart" .= (dataVisualizationBlockChart fields)] <> optional "block_id" (dataVisualizationBlockBlockId fields) <> extensionPairs (dataVisualizationBlockExtensions fields)
+
+instance ToJSON DataVisualizationBlock where toJSON (CheckedDataVisualizationBlock fields) = wireDataVisualizationBlock fields
+
+instance FromSlackJSON DataVisualizationBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "data_visualization" value
+    parsedTitle <- required "title" fields
+    parsedChart <- required "chart" fields
+    parsedBlockId <- optionalField "block_id" fields
+    buildDataVisualizationBlock (DataVisualizationBlockFields parsedTitle parsedChart parsedBlockId (remaining ["title", "chart", "block_id"] fields))
+
+instance FromJSON DataVisualizationBlock where parseJSON = checkedParser
+
+-- | A horizontal rule that separates blocks.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/divider-block>
+newtype DividerBlock = CheckedDividerBlock DividerBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildDividerBlock' before encoding.
+data DividerBlockFields = DividerBlockFields
+  { -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    dividerBlockBlockId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    dividerBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for DividerBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildDividerBlock' to validate before JSON encoding.
+dividerBlock :: DividerBlockFields
+dividerBlock = DividerBlockFields (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildDividerBlock' to revalidate.
+inspectDividerBlock :: DividerBlock -> DividerBlockFields
+inspectDividerBlock (CheckedDividerBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked DividerBlock.
+buildDividerBlock :: DividerBlockFields -> Result DividerBlock
+buildDividerBlock fields = do
+  checkExtensions ["block_id"] (dividerBlockExtensions fields)
+  validateFields "DividerBlock" (wireDividerBlock fields)
+  pure (CheckedDividerBlock fields)
+
+wireDividerBlock :: DividerBlockFields -> Value
+wireDividerBlock fields =
+  object $
+    ["type" .= ("divider" :: Text)] <> optional "block_id" (dividerBlockBlockId fields) <> extensionPairs (dividerBlockExtensions fields)
+
+instance ToJSON DividerBlock where toJSON (CheckedDividerBlock fields) = wireDividerBlock fields
+
+instance FromSlackJSON DividerBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "divider" value
+    parsedBlockId <- optionalField "block_id" fields
+    buildDividerBlock (DividerBlockFields parsedBlockId (remaining ["block_id"] fields))
+
+instance FromJSON DividerBlock where parseJSON = checkedParser
+
+-- | A remote file previously added with the files.remote API.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/file-block>
+newtype FileBlock = CheckedFileBlock FileBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildFileBlock' before encoding.
+data FileBlockFields = FileBlockFields
+  { -- | Sets the external identifier of a remote file previously added with the files.remote API. Required.
+    fileBlockExternalId :: Text,
+    -- | Sets the file source. Slack currently supports only remote files. Required.
+    fileBlockSource :: Text,
+    -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    fileBlockBlockId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    fileBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for FileBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildFileBlock' to validate before JSON encoding.
+fileBlock :: Text -> Text -> FileBlockFields
+fileBlock arg0 arg1 = FileBlockFields (arg0) (arg1) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildFileBlock' to revalidate.
+inspectFileBlock :: FileBlock -> FileBlockFields
+inspectFileBlock (CheckedFileBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked FileBlock.
+buildFileBlock :: FileBlockFields -> Result FileBlock
+buildFileBlock fields = do
+  checkExtensions ["external_id", "source", "block_id"] (fileBlockExtensions fields)
+  validateFields "FileBlock" (wireFileBlock fields)
+  pure (CheckedFileBlock fields)
+
+wireFileBlock :: FileBlockFields -> Value
+wireFileBlock fields =
+  object $
+    ["type" .= ("file" :: Text)] <> ["external_id" .= (fileBlockExternalId fields)] <> ["source" .= (fileBlockSource fields)] <> optional "block_id" (fileBlockBlockId fields) <> extensionPairs (fileBlockExtensions fields)
+
+instance ToJSON FileBlock where toJSON (CheckedFileBlock fields) = wireFileBlock fields
+
+instance FromSlackJSON FileBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "file" value
+    parsedExternalId <- required "external_id" fields
+    parsedSource <- required "source" fields
+    parsedBlockId <- optionalField "block_id" fields
+    buildFileBlock (FileBlockFields parsedExternalId parsedSource parsedBlockId (remaining ["external_id", "source", "block_id"] fields))
+
+instance FromJSON FileBlock where parseJSON = checkedParser
+
+-- | Large, bold plain text that introduces a group of blocks.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/header-block>
+newtype HeaderBlock = CheckedHeaderBlock HeaderBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildHeaderBlock' before encoding.
+data HeaderBlockFields = HeaderBlockFields
+  { -- | Sets the header text, shown in a larger bold font. Required. Shared limit: @header.text@.
+    headerBlockText :: PlainText,
+    -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    headerBlockBlockId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    headerBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for HeaderBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildHeaderBlock' to validate before JSON encoding.
+headerBlock :: PlainText -> HeaderBlockFields
+headerBlock arg0 = HeaderBlockFields (arg0) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildHeaderBlock' to revalidate.
+inspectHeaderBlock :: HeaderBlock -> HeaderBlockFields
+inspectHeaderBlock (CheckedHeaderBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked HeaderBlock.
+buildHeaderBlock :: HeaderBlockFields -> Result HeaderBlock
+buildHeaderBlock fields = do
+  checkExtensions ["text", "block_id"] (headerBlockExtensions fields)
+  validateFields "HeaderBlock" (wireHeaderBlock fields)
+  pure (CheckedHeaderBlock fields)
+
+wireHeaderBlock :: HeaderBlockFields -> Value
+wireHeaderBlock fields =
+  object $
+    ["type" .= ("header" :: Text)] <> ["text" .= (headerBlockText fields)] <> optional "block_id" (headerBlockBlockId fields) <> extensionPairs (headerBlockExtensions fields)
+
+instance ToJSON HeaderBlock where toJSON (CheckedHeaderBlock fields) = wireHeaderBlock fields
+
+instance FromSlackJSON HeaderBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "header" value
+    parsedText <- required "text" (coerceField "text" "plain_text" fields)
+    parsedBlockId <- optionalField "block_id" fields
+    buildHeaderBlock (HeaderBlockFields parsedText parsedBlockId (remaining ["text", "block_id"] fields))
+
+instance FromJSON HeaderBlock where parseJSON = checkedParser
+
+-- | A standalone image with alternative text and an optional title.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/image-block>
+-- Provide exactly one of an image URL or a Slack file.
+newtype ImageBlock = CheckedImageBlock ImageBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildImageBlock' before encoding.
+data ImageBlockFields = ImageBlockFields
+  { -- | Sets the publicly accessible URL of the image. Cannot be combined with a Slack file. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @image.image_url@.
+    imageBlockImageUrl :: Maybe (Text),
+    -- | Sets a file hosted in Slack as the image source. Cannot be combined with an image URL. Optional; Nothing omits the field. Factory default: @Nothing@.
+    imageBlockSlackFile :: Maybe (SlackFile),
+    -- | Sets a plain-text summary of the image or video for assistive technology. Required. Shared limit: @image.alt_text@.
+    imageBlockAltText :: Text,
+    -- | Sets the title shown above the image. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @image.title@.
+    imageBlockTitle :: Maybe (PlainText),
+    -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    imageBlockBlockId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    imageBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for ImageBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildImageBlock' to validate before JSON encoding.
+imageBlock :: Text -> ImageBlockFields
+imageBlock arg0 = ImageBlockFields (Nothing) (Nothing) (arg0) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildImageBlock' to revalidate.
+inspectImageBlock :: ImageBlock -> ImageBlockFields
+inspectImageBlock (CheckedImageBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked ImageBlock.
+buildImageBlock :: ImageBlockFields -> Result ImageBlock
+buildImageBlock fields = do
+  checkExtensions ["image_url", "slack_file", "alt_text", "title", "block_id"] (imageBlockExtensions fields)
+  validateFields "ImageBlock" (wireImageBlock fields)
+  pure (CheckedImageBlock fields)
+
+wireImageBlock :: ImageBlockFields -> Value
+wireImageBlock fields =
+  object $
+    ["type" .= ("image" :: Text)] <> optional "image_url" (imageBlockImageUrl fields) <> optional "slack_file" (imageBlockSlackFile fields) <> ["alt_text" .= (imageBlockAltText fields)] <> optional "title" (imageBlockTitle fields) <> optional "block_id" (imageBlockBlockId fields) <> extensionPairs (imageBlockExtensions fields)
+
+instance ToJSON ImageBlock where toJSON (CheckedImageBlock fields) = wireImageBlock fields
+
+instance FromSlackJSON ImageBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "image" value
+    parsedImageUrl <- optionalField "image_url" fields
+    parsedSlackFile <- optionalField "slack_file" fields
+    parsedAltText <- required "alt_text" fields
+    parsedTitle <- optionalField "title" (coerceField "title" "plain_text" fields)
+    parsedBlockId <- optionalField "block_id" fields
+    buildImageBlock (ImageBlockFields parsedImageUrl parsedSlackFile parsedAltText parsedTitle parsedBlockId (remaining ["image_url", "slack_file", "alt_text", "title", "block_id"] fields))
+
+instance FromJSON ImageBlock where parseJSON = checkedParser
+
+-- | A labelled input that collects a value in a modal, App Home, or message.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/input-block>
+newtype InputBlock = CheckedInputBlock InputBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildInputBlock' before encoding.
+data InputBlockFields = InputBlockFields
+  { -- | Sets the label shown above the input. Required. Shared limit: @input.label@.
+    inputBlockLabel :: PlainText,
+    -- | Sets the input element that collects the user's value. Required.
+    inputBlockElement :: InputElement,
+    -- | Sets whether changing the element sends a block_actions payload immediately. Optional; Nothing omits the field. Factory default: @Nothing@.
+    inputBlockDispatchAction :: Maybe (Bool),
+    -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    inputBlockBlockId :: Maybe (Text),
+    -- | Sets helper text shown below the input. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @input.hint@.
+    inputBlockHint :: Maybe (PlainText),
+    -- | Sets whether the modal can be submitted when this input is empty. Optional; Nothing omits the field. Factory default: @Nothing@.
+    inputBlockOptional :: Maybe (Bool),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    inputBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for InputBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildInputBlock' to validate before JSON encoding.
+inputBlock :: PlainText -> InputElement -> InputBlockFields
+inputBlock arg0 arg1 = InputBlockFields (arg0) (arg1) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildInputBlock' to revalidate.
+inspectInputBlock :: InputBlock -> InputBlockFields
+inspectInputBlock (CheckedInputBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked InputBlock.
+buildInputBlock :: InputBlockFields -> Result InputBlock
+buildInputBlock fields = do
+  checkExtensions ["label", "element", "dispatch_action", "block_id", "hint", "optional"] (inputBlockExtensions fields)
+  validateFields "InputBlock" (wireInputBlock fields)
+  pure (CheckedInputBlock fields)
+
+wireInputBlock :: InputBlockFields -> Value
+wireInputBlock fields =
+  object $
+    ["type" .= ("input" :: Text)] <> ["label" .= (inputBlockLabel fields)] <> ["element" .= (inputBlockElement fields)] <> optional "dispatch_action" (inputBlockDispatchAction fields) <> optional "block_id" (inputBlockBlockId fields) <> optional "hint" (inputBlockHint fields) <> optional "optional" (inputBlockOptional fields) <> extensionPairs (inputBlockExtensions fields)
+
+instance ToJSON InputBlock where toJSON (CheckedInputBlock fields) = wireInputBlock fields
+
+instance FromSlackJSON InputBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "input" value
+    parsedLabel <- required "label" (coerceField "label" "plain_text" fields)
+    parsedElement <- required "element" fields
+    parsedDispatchAction <- optionalField "dispatch_action" fields
+    parsedBlockId <- optionalField "block_id" fields
+    parsedHint <- optionalField "hint" (coerceField "hint" "plain_text" fields)
+    parsedOptional <- optionalField "optional" fields
+    buildInputBlock (InputBlockFields parsedLabel parsedElement parsedDispatchAction parsedBlockId parsedHint parsedOptional (remaining ["label", "element", "dispatch_action", "block_id", "hint", "optional"] fields))
+
+instance FromJSON InputBlock where parseJSON = checkedParser
+
+-- | Standard markdown rendered by Slack, intended for AI-generated content.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/markdown-block>
+newtype MarkdownBlock = CheckedMarkdownBlock MarkdownBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildMarkdownBlock' before encoding.
+data MarkdownBlockFields = MarkdownBlockFields
+  { -- | Sets standard markdown content, which Slack renders directly. Required. Shared limit: @markdown.text@.
+    markdownBlockText :: Text,
+    -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    markdownBlockBlockId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    markdownBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for MarkdownBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildMarkdownBlock' to validate before JSON encoding.
+markdownBlock :: Text -> MarkdownBlockFields
+markdownBlock arg0 = MarkdownBlockFields (arg0) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildMarkdownBlock' to revalidate.
+inspectMarkdownBlock :: MarkdownBlock -> MarkdownBlockFields
+inspectMarkdownBlock (CheckedMarkdownBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked MarkdownBlock.
+buildMarkdownBlock :: MarkdownBlockFields -> Result MarkdownBlock
+buildMarkdownBlock fields = do
+  checkExtensions ["text", "block_id"] (markdownBlockExtensions fields)
+  validateFields "MarkdownBlock" (wireMarkdownBlock fields)
+  pure (CheckedMarkdownBlock fields)
+
+wireMarkdownBlock :: MarkdownBlockFields -> Value
+wireMarkdownBlock fields =
+  object $
+    ["type" .= ("markdown" :: Text)] <> ["text" .= (markdownBlockText fields)] <> optional "block_id" (markdownBlockBlockId fields) <> extensionPairs (markdownBlockExtensions fields)
+
+instance ToJSON MarkdownBlock where toJSON (CheckedMarkdownBlock fields) = wireMarkdownBlock fields
+
+instance FromSlackJSON MarkdownBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "markdown" value
+    parsedText <- required "text" fields
+    parsedBlockId <- optionalField "block_id" fields
+    buildMarkdownBlock (MarkdownBlockFields parsedText parsedBlockId (remaining ["text", "block_id"] fields))
+
+instance FromJSON MarkdownBlock where parseJSON = checkedParser
+
+-- | A titled sequence of task cards.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/plan-block>
+-- Task IDs must be unique within a plan.
+newtype PlanBlock = CheckedPlanBlock PlanBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildPlanBlock' before encoding.
+data PlanBlockFields = PlanBlockFields
+  { -- | Sets the plan title. Required.
+    planBlockTitle :: Text,
+    -- | Adds task cards in display order. Required. Shared limit: @plan.tasks@.
+    planBlockTasks :: [PlanTask],
+    -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    planBlockBlockId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    planBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for PlanBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildPlanBlock' to validate before JSON encoding.
+planBlock :: Text -> [PlanTask] -> PlanBlockFields
+planBlock arg0 arg1 = PlanBlockFields (arg0) (arg1) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildPlanBlock' to revalidate.
+inspectPlanBlock :: PlanBlock -> PlanBlockFields
+inspectPlanBlock (CheckedPlanBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked PlanBlock.
+buildPlanBlock :: PlanBlockFields -> Result PlanBlock
+buildPlanBlock fields = do
+  checkExtensions ["title", "tasks", "block_id"] (planBlockExtensions fields)
+  validateFields "PlanBlock" (wirePlanBlock fields)
+  pure (CheckedPlanBlock fields)
+
+wirePlanBlock :: PlanBlockFields -> Value
+wirePlanBlock fields =
+  object $
+    ["type" .= ("plan" :: Text)] <> ["title" .= (planBlockTitle fields)] <> ["tasks" .= (map (stripTag . toJSON) (planBlockTasks fields))] <> optional "block_id" (planBlockBlockId fields) <> extensionPairs (planBlockExtensions fields)
+
+instance ToJSON PlanBlock where toJSON (CheckedPlanBlock fields) = wirePlanBlock fields
+
+instance FromSlackJSON PlanBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "plan" value
+    parsedTitle <- required "title" fields
+    parsedTasks <- required "tasks" (restoreTasks fields)
+    parsedBlockId <- optionalField "block_id" fields
+    buildPlanBlock (PlanBlockFields parsedTitle parsedTasks parsedBlockId (remaining ["title", "tasks", "block_id"] fields))
+
+instance FromJSON PlanBlock where parseJSON = checkedParser
+
+-- | Formatted text built from sections, lists, preformatted blocks, and quotes.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/rich-text-block>
+newtype RichTextBlock = CheckedRichTextBlock RichTextBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildRichTextBlock' before encoding.
+data RichTextBlockFields = RichTextBlockFields
+  { -- | Adds sections, lists, preformatted blocks, and quotes in display order. Required.
+    richTextBlockElements :: [RichTextBlockElement],
+    -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    richTextBlockBlockId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    richTextBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for RichTextBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildRichTextBlock' to validate before JSON encoding.
+richTextBlock :: [RichTextBlockElement] -> RichTextBlockFields
+richTextBlock arg0 = RichTextBlockFields (arg0) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildRichTextBlock' to revalidate.
+inspectRichTextBlock :: RichTextBlock -> RichTextBlockFields
+inspectRichTextBlock (CheckedRichTextBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked RichTextBlock.
+buildRichTextBlock :: RichTextBlockFields -> Result RichTextBlock
+buildRichTextBlock fields = do
+  checkExtensions ["elements", "block_id"] (richTextBlockExtensions fields)
+  validateFields "RichTextBlock" (wireRichTextBlock fields)
+  pure (CheckedRichTextBlock fields)
+
+wireRichTextBlock :: RichTextBlockFields -> Value
+wireRichTextBlock fields =
+  object $
+    ["type" .= ("rich_text" :: Text)] <> ["elements" .= (richTextBlockElements fields)] <> optional "block_id" (richTextBlockBlockId fields) <> extensionPairs (richTextBlockExtensions fields)
+
+instance ToJSON RichTextBlock where toJSON (CheckedRichTextBlock fields) = wireRichTextBlock fields
+
+instance FromSlackJSON RichTextBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "rich_text" value
+    parsedElements <- required "elements" fields
+    parsedBlockId <- optionalField "block_id" fields
+    buildRichTextBlock (RichTextBlockFields parsedElements parsedBlockId (remaining ["elements", "block_id"] fields))
+
+instance FromJSON RichTextBlock where parseJSON = checkedParser
+
+-- | Text, a grid of fields, or both, with an optional accessory element.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/section-block>
+-- Provide text, fields, or both.
+newtype SectionBlock = CheckedSectionBlock SectionBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildSectionBlock' before encoding.
+data SectionBlockFields = SectionBlockFields
+  { -- | Sets the main text of the section. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @section.text@.
+    sectionBlockText :: Maybe (TextObject),
+    -- | Adds section fields, shown in a two-column grid, in display order. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @section.fields@.
+    sectionBlockFields :: Maybe ([TextObject]),
+    -- | Sets the element displayed beside the section text. Optional; Nothing omits the field. Factory default: @Nothing@.
+    sectionBlockAccessory :: Maybe (Element),
+    -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    sectionBlockBlockId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    sectionBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for SectionBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildSectionBlock' to validate before JSON encoding.
+sectionBlock :: SectionBlockFields
+sectionBlock = SectionBlockFields (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildSectionBlock' to revalidate.
+inspectSectionBlock :: SectionBlock -> SectionBlockFields
+inspectSectionBlock (CheckedSectionBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked SectionBlock.
+buildSectionBlock :: SectionBlockFields -> Result SectionBlock
+buildSectionBlock fields = do
+  checkExtensions ["text", "fields", "accessory", "block_id"] (sectionBlockExtensions fields)
+  validateFields "SectionBlock" (wireSectionBlock fields)
+  pure (CheckedSectionBlock fields)
+
+wireSectionBlock :: SectionBlockFields -> Value
+wireSectionBlock fields =
+  object $
+    ["type" .= ("section" :: Text)] <> optional "text" (sectionBlockText fields) <> optional "fields" (sectionBlockFields fields) <> optional "accessory" (sectionBlockAccessory fields) <> optional "block_id" (sectionBlockBlockId fields) <> extensionPairs (sectionBlockExtensions fields)
+
+instance ToJSON SectionBlock where toJSON (CheckedSectionBlock fields) = wireSectionBlock fields
+
+instance FromSlackJSON SectionBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "section" value
+    parsedText <- optionalField "text" (coerceField "text" "mrkdwn" fields)
+    parsedFields <- optionalField "fields" (coerceField "fields" "mrkdwn" fields)
+    parsedAccessory <- optionalField "accessory" fields
+    parsedBlockId <- optionalField "block_id" fields
+    buildSectionBlock (SectionBlockFields parsedText parsedFields parsedAccessory parsedBlockId (remaining ["text", "fields", "accessory", "block_id"] fields))
+
+instance FromJSON SectionBlock where parseJSON = checkedParser
+
+-- | A simple table of raw text and rich text cells.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/table-block>
+newtype TableBlock = CheckedTableBlock TableBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildTableBlock' before encoding.
+data TableBlockFields = TableBlockFields
+  { -- | Adds complete table rows in display order. Required. Shared limit: @table.rows@.
+    tableBlockRows :: [[TableCell]],
+    -- | Adds per-column settings in column order. Provide one entry for every column. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @table.column_settings@.
+    tableBlockColumnSettings :: Maybe ([ColumnSettings]),
+    -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    tableBlockBlockId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    tableBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for TableBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildTableBlock' to validate before JSON encoding.
+tableBlock :: [[TableCell]] -> TableBlockFields
+tableBlock arg0 = TableBlockFields (arg0) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildTableBlock' to revalidate.
+inspectTableBlock :: TableBlock -> TableBlockFields
+inspectTableBlock (CheckedTableBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked TableBlock.
+buildTableBlock :: TableBlockFields -> Result TableBlock
+buildTableBlock fields = do
+  checkExtensions ["rows", "column_settings", "block_id"] (tableBlockExtensions fields)
+  validateFields "TableBlock" (wireTableBlock fields)
+  pure (CheckedTableBlock fields)
+
+wireTableBlock :: TableBlockFields -> Value
+wireTableBlock fields =
+  object $
+    ["type" .= ("table" :: Text)] <> ["rows" .= (tableBlockRows fields)] <> optional "column_settings" (tableBlockColumnSettings fields) <> optional "block_id" (tableBlockBlockId fields) <> extensionPairs (tableBlockExtensions fields)
+
+instance ToJSON TableBlock where toJSON (CheckedTableBlock fields) = wireTableBlock fields
+
+instance FromSlackJSON TableBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "table" value
+    parsedRows <- required "rows" fields
+    parsedColumnSettings <- optionalField "column_settings" fields
+    parsedBlockId <- optionalField "block_id" fields
+    buildTableBlock (TableBlockFields parsedRows parsedColumnSettings parsedBlockId (remaining ["rows", "column_settings", "block_id"] fields))
+
+instance FromJSON TableBlock where parseJSON = checkedParser
+
+-- | One task with its status, details, output, and sources.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/task-card-block>
+-- A standalone task card's status cannot be pending; pending is only valid for plan tasks.
+newtype TaskCardBlock = CheckedTaskCardBlock TaskCardBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildTaskCardBlock' before encoding.
+data TaskCardBlockFields = TaskCardBlockFields
+  { -- | Sets the identifier of the task. Required.
+    taskCardBlockTaskId :: Text,
+    -- | Sets the task title. Required.
+    taskCardBlockTitle :: Text,
+    -- | Sets a rich text description of the task. Optional; Nothing omits the field. Factory default: @Nothing@.
+    taskCardBlockDetails :: Maybe (RichTextBlock),
+    -- | Sets rich text describing the task's output. Optional; Nothing omits the field. Factory default: @Nothing@.
+    taskCardBlockOutput :: Maybe (RichTextBlock),
+    -- | Adds links to the sources the task used, in display order. Optional; Nothing omits the field. Factory default: @Nothing@.
+    taskCardBlockSources :: Maybe ([UrlSource]),
+    -- | Sets the task's current state. Required.
+    taskCardBlockStatus :: TaskStatus,
+    -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    taskCardBlockBlockId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    taskCardBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for TaskCardBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildTaskCardBlock' to validate before JSON encoding.
+taskCardBlock :: Text -> Text -> TaskStatus -> TaskCardBlockFields
+taskCardBlock arg0 arg1 arg2 = TaskCardBlockFields (arg0) (arg1) (Nothing) (Nothing) (Nothing) (arg2) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildTaskCardBlock' to revalidate.
+inspectTaskCardBlock :: TaskCardBlock -> TaskCardBlockFields
+inspectTaskCardBlock (CheckedTaskCardBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked TaskCardBlock.
+buildTaskCardBlock :: TaskCardBlockFields -> Result TaskCardBlock
+buildTaskCardBlock fields = do
+  checkExtensions ["task_id", "title", "details", "output", "sources", "status", "block_id"] (taskCardBlockExtensions fields)
+  validateFields "TaskCardBlock" (wireTaskCardBlock fields)
+  standaloneTask (wireTaskCardBlock fields)
+  pure (CheckedTaskCardBlock fields)
+
+wireTaskCardBlock :: TaskCardBlockFields -> Value
+wireTaskCardBlock fields =
+  object $
+    ["type" .= ("task_card" :: Text)] <> ["task_id" .= (taskCardBlockTaskId fields)] <> ["title" .= (taskCardBlockTitle fields)] <> optional "details" (taskCardBlockDetails fields) <> optional "output" (taskCardBlockOutput fields) <> optional "sources" (taskCardBlockSources fields) <> ["status" .= (taskCardBlockStatus fields)] <> optional "block_id" (taskCardBlockBlockId fields) <> extensionPairs (taskCardBlockExtensions fields)
+
+instance ToJSON TaskCardBlock where toJSON (CheckedTaskCardBlock fields) = wireTaskCardBlock fields
+
+instance FromSlackJSON TaskCardBlock where fromSlackJSON = decodeTaskCard buildTaskCardBlock
+
+decodeTaskCard :: (TaskCardBlockFields -> Result a) -> Value -> Result a
+decodeTaskCard constructor value = do
+  fields <- wireObject "task_card" value
+  parsedTaskId <- required "task_id" fields
+  parsedTitle <- required "title" fields
+  parsedDetails <- optionalField "details" fields
+  parsedOutput <- optionalField "output" fields
+  parsedSources <- optionalField "sources" fields
+  parsedStatus <- required "status" fields
+  parsedBlockId <- optionalField "block_id" fields
+  constructor (TaskCardBlockFields parsedTaskId parsedTitle parsedDetails parsedOutput parsedSources parsedStatus parsedBlockId (remaining ["task_id", "title", "details", "output", "sources", "status", "block_id"] fields))
+
+instance FromJSON TaskCardBlock where parseJSON = checkedParser
+
+-- | An embedded video player.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/video-block>
+newtype VideoBlock = CheckedVideoBlock VideoBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildVideoBlock' before encoding.
+data VideoBlockFields = VideoBlockFields
+  { -- | Sets a plain-text summary of the image or video for assistive technology. Required. Shared limit: @video.alt_text@.
+    videoBlockAltText :: Text,
+    -- | Sets the URL of the image shown before the video plays. Required. Shared limit: @video.thumbnail_url@.
+    videoBlockThumbnailUrl :: Text,
+    -- | Sets the video title. Required. Shared limit: @video.title@.
+    videoBlockTitle :: PlainText,
+    -- | Sets the embeddable URL of the video. The domain must be listed in the app's unfurl domains. Required. Shared limit: @video.video_url@.
+    videoBlockVideoUrl :: Text,
+    -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.
+    videoBlockBlockId :: Maybe (Text),
+    -- | Sets the name of the video's author. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @video.author_name@.
+    videoBlockAuthorName :: Maybe (Text),
+    -- | Sets the video description. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @video.description@.
+    videoBlockDescription :: Maybe (PlainText),
+    -- | Sets the URL of the video provider's icon. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @video.provider_icon_url@.
+    videoBlockProviderIconUrl :: Maybe (Text),
+    -- | Sets the name of the video provider, such as YouTube. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @video.provider_name@.
+    videoBlockProviderName :: Maybe (Text),
+    -- | Sets the HTTPS URL opened when the title is clicked. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @video.title_url@.
+    videoBlockTitleUrl :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    videoBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for VideoBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildVideoBlock' to validate before JSON encoding.
+videoBlock :: Text -> Text -> PlainText -> Text -> VideoBlockFields
+videoBlock arg0 arg1 arg2 arg3 = VideoBlockFields (arg0) (arg1) (arg2) (arg3) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildVideoBlock' to revalidate.
+inspectVideoBlock :: VideoBlock -> VideoBlockFields
+inspectVideoBlock (CheckedVideoBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked VideoBlock.
+buildVideoBlock :: VideoBlockFields -> Result VideoBlock
+buildVideoBlock fields = do
+  checkExtensions ["alt_text", "thumbnail_url", "title", "video_url", "block_id", "author_name", "description", "provider_icon_url", "provider_name", "title_url"] (videoBlockExtensions fields)
+  validateFields "VideoBlock" (wireVideoBlock fields)
+  pure (CheckedVideoBlock fields)
+
+wireVideoBlock :: VideoBlockFields -> Value
+wireVideoBlock fields =
+  object $
+    ["type" .= ("video" :: Text)] <> ["alt_text" .= (videoBlockAltText fields)] <> ["thumbnail_url" .= (videoBlockThumbnailUrl fields)] <> ["title" .= (videoBlockTitle fields)] <> ["video_url" .= (videoBlockVideoUrl fields)] <> optional "block_id" (videoBlockBlockId fields) <> optional "author_name" (videoBlockAuthorName fields) <> optional "description" (videoBlockDescription fields) <> optional "provider_icon_url" (videoBlockProviderIconUrl fields) <> optional "provider_name" (videoBlockProviderName fields) <> optional "title_url" (videoBlockTitleUrl fields) <> extensionPairs (videoBlockExtensions fields)
+
+instance ToJSON VideoBlock where toJSON (CheckedVideoBlock fields) = wireVideoBlock fields
+
+instance FromSlackJSON VideoBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "video" value
+    parsedAltText <- required "alt_text" fields
+    parsedThumbnailUrl <- required "thumbnail_url" fields
+    parsedTitle <- required "title" (coerceField "title" "plain_text" fields)
+    parsedVideoUrl <- required "video_url" fields
+    parsedBlockId <- optionalField "block_id" fields
+    parsedAuthorName <- optionalField "author_name" fields
+    parsedDescription <- optionalField "description" (coerceField "description" "plain_text" fields)
+    parsedProviderIconUrl <- optionalField "provider_icon_url" fields
+    parsedProviderName <- optionalField "provider_name" fields
+    parsedTitleUrl <- optionalField "title_url" fields
+    buildVideoBlock (VideoBlockFields parsedAltText parsedThumbnailUrl parsedTitle parsedVideoUrl parsedBlockId parsedAuthorName parsedDescription parsedProviderIconUrl parsedProviderName parsedTitleUrl (remaining ["alt_text", "thumbnail_url", "title", "video_url", "block_id", "author_name", "description", "provider_icon_url", "provider_name", "title_url"] fields))
+
+instance FromJSON VideoBlock where parseJSON = checkedParser
+
+-- | An interactive button that sends a payload or opens a URL.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/button-element>
+-- A prompt display requires an agent prompt.
+newtype ButtonElement = CheckedButtonElement ButtonElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildButtonElement' before encoding.
+data ButtonElementFields = ButtonElementFields
+  { -- | Sets the button label. Required. Shared limit: @button.text@.
+    buttonElementText :: PlainText,
+    -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    buttonElementActionId :: Maybe (Text),
+    -- | Sets a URL opened in the user's browser when the button is clicked. Slack still sends an interaction payload. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @button.url@.
+    buttonElementUrl :: Maybe (Text),
+    -- | Sets the application-defined value sent in interaction payloads. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @button.value@.
+    buttonElementValue :: Maybe (Text),
+    -- | Sets the button's emphasis. Omit it for the default neutral style. Optional; Nothing omits the field. Factory default: @Nothing@.
+    buttonElementStyle :: Maybe (ButtonStyle),
+    -- | Sets a confirmation dialog shown before the action is sent. Optional; Nothing omits the field. Factory default: @Nothing@.
+    buttonElementConfirm :: Maybe (ConfirmationDialogue),
+    -- | Sets the label read by screen readers in place of the visible text. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @button.accessibility_label@.
+    buttonElementAccessibilityLabel :: Maybe (Text),
+    -- | Sets a prompt sent to Slackbot when AI is available to the clicking user. The app receives a block_actions payload in either case and must acknowledge it within three seconds. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @button.agent_prompt@.
+    buttonElementAgentPrompt :: Maybe (Text),
+    -- | Sets the collapsed Slackbot message text in place of the full prompt. Requires agent_prompt. Omit this field to let Slack use the button text; the full prompt remains expandable. Optional; Nothing omits the field. Factory default: @Nothing@.
+    buttonElementAgentPromptDisplay :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    buttonElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for ButtonElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildButtonElement' to validate before JSON encoding.
+buttonElement :: PlainText -> ButtonElementFields
+buttonElement arg0 = ButtonElementFields (arg0) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildButtonElement' to revalidate.
+inspectButtonElement :: ButtonElement -> ButtonElementFields
+inspectButtonElement (CheckedButtonElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked ButtonElement.
+buildButtonElement :: ButtonElementFields -> Result ButtonElement
+buildButtonElement fields = do
+  checkExtensions ["text", "action_id", "url", "value", "style", "confirm", "accessibility_label", "agent_prompt", "agent_prompt_display"] (buttonElementExtensions fields)
+  validateFields "ButtonElement" (wireButtonElement fields)
+  pure (CheckedButtonElement fields)
+
+wireButtonElement :: ButtonElementFields -> Value
+wireButtonElement fields =
+  object $
+    ["type" .= ("button" :: Text)] <> ["text" .= (buttonElementText fields)] <> optional "action_id" (buttonElementActionId fields) <> optional "url" (buttonElementUrl fields) <> optional "value" (buttonElementValue fields) <> optional "style" (buttonElementStyle fields) <> optional "confirm" (buttonElementConfirm fields) <> optional "accessibility_label" (buttonElementAccessibilityLabel fields) <> optional "agent_prompt" (buttonElementAgentPrompt fields) <> optional "agent_prompt_display" (buttonElementAgentPromptDisplay fields) <> extensionPairs (buttonElementExtensions fields)
+
+instance ToJSON ButtonElement where toJSON (CheckedButtonElement fields) = wireButtonElement fields
+
+instance FromSlackJSON ButtonElement where
+  fromSlackJSON value = do
+    fields <- wireObject "button" value
+    parsedText <- required "text" (coerceField "text" "plain_text" fields)
+    parsedActionId <- optionalField "action_id" fields
+    parsedUrl <- optionalField "url" fields
+    parsedValue <- optionalField "value" fields
+    parsedStyle <- optionalField "style" fields
+    parsedConfirm <- optionalField "confirm" fields
+    parsedAccessibilityLabel <- optionalField "accessibility_label" fields
+    parsedAgentPrompt <- optionalField "agent_prompt" fields
+    parsedAgentPromptDisplay <- optionalField "agent_prompt_display" fields
+    buildButtonElement (ButtonElementFields parsedText parsedActionId parsedUrl parsedValue parsedStyle parsedConfirm parsedAccessibilityLabel parsedAgentPrompt parsedAgentPromptDisplay (remaining ["text", "action_id", "url", "value", "style", "confirm", "accessibility_label", "agent_prompt", "agent_prompt_display"] fields))
+
+instance FromJSON ButtonElement where parseJSON = checkedParser
+
+-- | A menu for selecting multiple public channels.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element>
+newtype ChannelMultiSelectElement = CheckedChannelMultiSelectElement ChannelMultiSelectElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildChannelMultiSelectElement' before encoding.
+data ChannelMultiSelectElementFields = ChannelMultiSelectElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    channelMultiSelectElementActionId :: Maybe (Text),
+    -- | Adds channel IDs that are selected when the menu loads. Optional; Nothing omits the field. Factory default: @Nothing@.
+    channelMultiSelectElementInitialChannels :: Maybe ([Text]),
+    -- | Sets a confirmation dialog shown before the action is sent. Optional; Nothing omits the field. Factory default: @Nothing@.
+    channelMultiSelectElementConfirm :: Maybe (ConfirmationDialogue),
+    -- | Sets the maximum number of items a user can select. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @multi_select.max_selected_items@.
+    channelMultiSelectElementMaxSelectedItems :: Maybe (Integer),
+    -- | Sets whether the element receives focus when the view opens. Only one element per view may do so. Optional; Nothing omits the field. Factory default: @Nothing@.
+    channelMultiSelectElementFocusOnLoad :: Maybe (Bool),
+    -- | Sets the placeholder text shown before a value is chosen. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @select.placeholder@.
+    channelMultiSelectElementPlaceholder :: Maybe (PlainText),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    channelMultiSelectElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for ChannelMultiSelectElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildChannelMultiSelectElement' to validate before JSON encoding.
+channelMultiSelectElement :: ChannelMultiSelectElementFields
+channelMultiSelectElement = ChannelMultiSelectElementFields (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildChannelMultiSelectElement' to revalidate.
+inspectChannelMultiSelectElement :: ChannelMultiSelectElement -> ChannelMultiSelectElementFields
+inspectChannelMultiSelectElement (CheckedChannelMultiSelectElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked ChannelMultiSelectElement.
+buildChannelMultiSelectElement :: ChannelMultiSelectElementFields -> Result ChannelMultiSelectElement
+buildChannelMultiSelectElement fields = do
+  checkExtensions ["action_id", "initial_channels", "confirm", "max_selected_items", "focus_on_load", "placeholder"] (channelMultiSelectElementExtensions fields)
+  validateFields "ChannelMultiSelectElement" (wireChannelMultiSelectElement fields)
+  pure (CheckedChannelMultiSelectElement fields)
+
+wireChannelMultiSelectElement :: ChannelMultiSelectElementFields -> Value
+wireChannelMultiSelectElement fields =
+  object $
+    ["type" .= ("multi_channels_select" :: Text)] <> optional "action_id" (channelMultiSelectElementActionId fields) <> optional "initial_channels" (channelMultiSelectElementInitialChannels fields) <> optional "confirm" (channelMultiSelectElementConfirm fields) <> optional "max_selected_items" (channelMultiSelectElementMaxSelectedItems fields) <> optional "focus_on_load" (channelMultiSelectElementFocusOnLoad fields) <> optional "placeholder" (channelMultiSelectElementPlaceholder fields) <> extensionPairs (channelMultiSelectElementExtensions fields)
+
+instance ToJSON ChannelMultiSelectElement where toJSON (CheckedChannelMultiSelectElement fields) = wireChannelMultiSelectElement fields
+
+instance FromSlackJSON ChannelMultiSelectElement where
+  fromSlackJSON value = do
+    fields <- wireObject "multi_channels_select" value
+    checkIntegerBounds "ChannelMultiSelectElement" (Object fields)
+    parsedActionId <- optionalField "action_id" fields
+    parsedInitialChannels <- optionalField "initial_channels" fields
+    parsedConfirm <- optionalField "confirm" fields
+    parsedMaxSelectedItems <- optionalField "max_selected_items" fields
+    parsedFocusOnLoad <- optionalField "focus_on_load" fields
+    parsedPlaceholder <- optionalField "placeholder" (coerceField "placeholder" "plain_text" fields)
+    buildChannelMultiSelectElement (ChannelMultiSelectElementFields parsedActionId parsedInitialChannels parsedConfirm parsedMaxSelectedItems parsedFocusOnLoad parsedPlaceholder (remaining ["action_id", "initial_channels", "confirm", "max_selected_items", "focus_on_load", "placeholder"] fields))
+
+instance FromJSON ChannelMultiSelectElement where parseJSON = checkedParser
+
+-- | A menu for selecting one public channel.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element>
+newtype ChannelSelectElement = CheckedChannelSelectElement ChannelSelectElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildChannelSelectElement' before encoding.
+data ChannelSelectElementFields = ChannelSelectElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    channelSelectElementActionId :: Maybe (Text),
+    -- | Sets the channel ID selected when the menu loads. Optional; Nothing omits the field. Factory default: @Nothing@.
+    channelSelectElementInitialChannel :: Maybe (Text),
+    -- | Sets whether the selected conversation receives a response URL in the view submission payload. Only valid in modals. Optional; Nothing omits the field. Factory default: @Nothing@.
+    channelSelectElementResponseUrlEnabled :: Maybe (Bool),
+    -- | Sets a confirmation dialog shown before the action is sent. Optional; Nothing omits the field. Factory default: @Nothing@.
+    channelSelectElementConfirm :: Maybe (ConfirmationDialogue),
+    -- | Sets whether the element receives focus when the view opens. Only one element per view may do so. Optional; Nothing omits the field. Factory default: @Nothing@.
+    channelSelectElementFocusOnLoad :: Maybe (Bool),
+    -- | Sets the placeholder text shown before a value is chosen. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @select.placeholder@.
+    channelSelectElementPlaceholder :: Maybe (PlainText),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    channelSelectElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for ChannelSelectElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildChannelSelectElement' to validate before JSON encoding.
+channelSelectElement :: ChannelSelectElementFields
+channelSelectElement = ChannelSelectElementFields (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildChannelSelectElement' to revalidate.
+inspectChannelSelectElement :: ChannelSelectElement -> ChannelSelectElementFields
+inspectChannelSelectElement (CheckedChannelSelectElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked ChannelSelectElement.
+buildChannelSelectElement :: ChannelSelectElementFields -> Result ChannelSelectElement
+buildChannelSelectElement fields = do
+  checkExtensions ["action_id", "initial_channel", "response_url_enabled", "confirm", "focus_on_load", "placeholder"] (channelSelectElementExtensions fields)
+  validateFields "ChannelSelectElement" (wireChannelSelectElement fields)
+  pure (CheckedChannelSelectElement fields)
+
+wireChannelSelectElement :: ChannelSelectElementFields -> Value
+wireChannelSelectElement fields =
+  object $
+    ["type" .= ("channels_select" :: Text)] <> optional "action_id" (channelSelectElementActionId fields) <> optional "initial_channel" (channelSelectElementInitialChannel fields) <> optional "response_url_enabled" (channelSelectElementResponseUrlEnabled fields) <> optional "confirm" (channelSelectElementConfirm fields) <> optional "focus_on_load" (channelSelectElementFocusOnLoad fields) <> optional "placeholder" (channelSelectElementPlaceholder fields) <> extensionPairs (channelSelectElementExtensions fields)
+
+instance ToJSON ChannelSelectElement where toJSON (CheckedChannelSelectElement fields) = wireChannelSelectElement fields
+
+instance FromSlackJSON ChannelSelectElement where
+  fromSlackJSON value = do
+    fields <- wireObject "channels_select" value
+    parsedActionId <- optionalField "action_id" fields
+    parsedInitialChannel <- optionalField "initial_channel" fields
+    parsedResponseUrlEnabled <- optionalField "response_url_enabled" fields
+    parsedConfirm <- optionalField "confirm" fields
+    parsedFocusOnLoad <- optionalField "focus_on_load" fields
+    parsedPlaceholder <- optionalField "placeholder" (coerceField "placeholder" "plain_text" fields)
+    buildChannelSelectElement (ChannelSelectElementFields parsedActionId parsedInitialChannel parsedResponseUrlEnabled parsedConfirm parsedFocusOnLoad parsedPlaceholder (remaining ["action_id", "initial_channel", "response_url_enabled", "confirm", "focus_on_load", "placeholder"] fields))
+
+instance FromJSON ChannelSelectElement where parseJSON = checkedParser
+
+-- | A group of checkboxes.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/checkboxes-element>
+newtype CheckboxesElement = CheckedCheckboxesElement CheckboxesElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildCheckboxesElement' before encoding.
+data CheckboxesElementFields = CheckboxesElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    checkboxesElementActionId :: Maybe (Text),
+    -- | Adds selectable options in display order. Required. Shared limit: @checkboxes.options@.
+    checkboxesElementOptions :: [SelectOption],
+    -- | Adds options that are selected when the element loads. Each must match an option in the element. Optional; Nothing omits the field. Factory default: @Nothing@.
+    checkboxesElementInitialOptions :: Maybe ([SelectOption]),
+    -- | Sets a confirmation dialog shown before the action is sent. Optional; Nothing omits the field. Factory default: @Nothing@.
+    checkboxesElementConfirm :: Maybe (ConfirmationDialogue),
+    -- | Sets whether the element receives focus when the view opens. Only one element per view may do so. Optional; Nothing omits the field. Factory default: @Nothing@.
+    checkboxesElementFocusOnLoad :: Maybe (Bool),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    checkboxesElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for CheckboxesElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildCheckboxesElement' to validate before JSON encoding.
+checkboxesElement :: [SelectOption] -> CheckboxesElementFields
+checkboxesElement arg0 = CheckboxesElementFields (Nothing) (arg0) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildCheckboxesElement' to revalidate.
+inspectCheckboxesElement :: CheckboxesElement -> CheckboxesElementFields
+inspectCheckboxesElement (CheckedCheckboxesElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked CheckboxesElement.
+buildCheckboxesElement :: CheckboxesElementFields -> Result CheckboxesElement
+buildCheckboxesElement fields = do
+  checkExtensions ["action_id", "options", "initial_options", "confirm", "focus_on_load"] (checkboxesElementExtensions fields)
+  validateFields "CheckboxesElement" (wireCheckboxesElement fields)
+  pure (CheckedCheckboxesElement fields)
+
+wireCheckboxesElement :: CheckboxesElementFields -> Value
+wireCheckboxesElement fields =
+  object $
+    ["type" .= ("checkboxes" :: Text)] <> optional "action_id" (checkboxesElementActionId fields) <> ["options" .= (checkboxesElementOptions fields)] <> optional "initial_options" (checkboxesElementInitialOptions fields) <> optional "confirm" (checkboxesElementConfirm fields) <> optional "focus_on_load" (checkboxesElementFocusOnLoad fields) <> extensionPairs (checkboxesElementExtensions fields)
+
+instance ToJSON CheckboxesElement where toJSON (CheckedCheckboxesElement fields) = wireCheckboxesElement fields
+
+instance FromSlackJSON CheckboxesElement where
+  fromSlackJSON value = do
+    fields <- wireObject "checkboxes" value
+    parsedActionId <- optionalField "action_id" fields
+    parsedOptions <- required "options" fields
+    parsedInitialOptions <- optionalField "initial_options" fields
+    parsedConfirm <- optionalField "confirm" fields
+    parsedFocusOnLoad <- optionalField "focus_on_load" fields
+    buildCheckboxesElement (CheckboxesElementFields parsedActionId parsedOptions parsedInitialOptions parsedConfirm parsedFocusOnLoad (remaining ["action_id", "options", "initial_options", "confirm", "focus_on_load"] fields))
+
+instance FromJSON CheckboxesElement where parseJSON = checkedParser
+
+-- | A menu for selecting multiple conversations.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element>
+newtype ConversationMultiSelectElement = CheckedConversationMultiSelectElement ConversationMultiSelectElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildConversationMultiSelectElement' before encoding.
+data ConversationMultiSelectElementFields = ConversationMultiSelectElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    conversationMultiSelectElementActionId :: Maybe (Text),
+    -- | Adds conversation IDs that are selected when the menu loads. Optional; Nothing omits the field. Factory default: @Nothing@.
+    conversationMultiSelectElementInitialConversations :: Maybe ([Text]),
+    -- | Sets whether the menu pre-selects the conversation the user is viewing. Optional; Nothing omits the field. Factory default: @Nothing@.
+    conversationMultiSelectElementDefaultToCurrentConversation :: Maybe (Bool),
+    -- | Sets which conversation types the menu offers. Optional; Nothing omits the field. Factory default: @Nothing@.
+    conversationMultiSelectElementFilter :: Maybe (ConversationFilter),
+    -- | Sets a confirmation dialog shown before the action is sent. Optional; Nothing omits the field. Factory default: @Nothing@.
+    conversationMultiSelectElementConfirm :: Maybe (ConfirmationDialogue),
+    -- | Sets the maximum number of items a user can select. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @multi_select.max_selected_items@.
+    conversationMultiSelectElementMaxSelectedItems :: Maybe (Integer),
+    -- | Sets whether the element receives focus when the view opens. Only one element per view may do so. Optional; Nothing omits the field. Factory default: @Nothing@.
+    conversationMultiSelectElementFocusOnLoad :: Maybe (Bool),
+    -- | Sets the placeholder text shown before a value is chosen. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @select.placeholder@.
+    conversationMultiSelectElementPlaceholder :: Maybe (PlainText),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    conversationMultiSelectElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for ConversationMultiSelectElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildConversationMultiSelectElement' to validate before JSON encoding.
+conversationMultiSelectElement :: ConversationMultiSelectElementFields
+conversationMultiSelectElement = ConversationMultiSelectElementFields (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildConversationMultiSelectElement' to revalidate.
+inspectConversationMultiSelectElement :: ConversationMultiSelectElement -> ConversationMultiSelectElementFields
+inspectConversationMultiSelectElement (CheckedConversationMultiSelectElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked ConversationMultiSelectElement.
+buildConversationMultiSelectElement :: ConversationMultiSelectElementFields -> Result ConversationMultiSelectElement
+buildConversationMultiSelectElement fields = do
+  checkExtensions ["action_id", "initial_conversations", "default_to_current_conversation", "filter", "confirm", "max_selected_items", "focus_on_load", "placeholder"] (conversationMultiSelectElementExtensions fields)
+  validateFields "ConversationMultiSelectElement" (wireConversationMultiSelectElement fields)
+  pure (CheckedConversationMultiSelectElement fields)
+
+wireConversationMultiSelectElement :: ConversationMultiSelectElementFields -> Value
+wireConversationMultiSelectElement fields =
+  object $
+    ["type" .= ("multi_conversations_select" :: Text)] <> optional "action_id" (conversationMultiSelectElementActionId fields) <> optional "initial_conversations" (conversationMultiSelectElementInitialConversations fields) <> optional "default_to_current_conversation" (conversationMultiSelectElementDefaultToCurrentConversation fields) <> optional "filter" (conversationMultiSelectElementFilter fields) <> optional "confirm" (conversationMultiSelectElementConfirm fields) <> optional "max_selected_items" (conversationMultiSelectElementMaxSelectedItems fields) <> optional "focus_on_load" (conversationMultiSelectElementFocusOnLoad fields) <> optional "placeholder" (conversationMultiSelectElementPlaceholder fields) <> extensionPairs (conversationMultiSelectElementExtensions fields)
+
+instance ToJSON ConversationMultiSelectElement where toJSON (CheckedConversationMultiSelectElement fields) = wireConversationMultiSelectElement fields
+
+instance FromSlackJSON ConversationMultiSelectElement where
+  fromSlackJSON value = do
+    fields <- wireObject "multi_conversations_select" value
+    checkIntegerBounds "ConversationMultiSelectElement" (Object fields)
+    parsedActionId <- optionalField "action_id" fields
+    parsedInitialConversations <- optionalField "initial_conversations" fields
+    parsedDefaultToCurrentConversation <- optionalField "default_to_current_conversation" fields
+    parsedFilter <- optionalField "filter" fields
+    parsedConfirm <- optionalField "confirm" fields
+    parsedMaxSelectedItems <- optionalField "max_selected_items" fields
+    parsedFocusOnLoad <- optionalField "focus_on_load" fields
+    parsedPlaceholder <- optionalField "placeholder" (coerceField "placeholder" "plain_text" fields)
+    buildConversationMultiSelectElement (ConversationMultiSelectElementFields parsedActionId parsedInitialConversations parsedDefaultToCurrentConversation parsedFilter parsedConfirm parsedMaxSelectedItems parsedFocusOnLoad parsedPlaceholder (remaining ["action_id", "initial_conversations", "default_to_current_conversation", "filter", "confirm", "max_selected_items", "focus_on_load", "placeholder"] fields))
+
+instance FromJSON ConversationMultiSelectElement where parseJSON = checkedParser
+
+-- | A menu for selecting one conversation.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element>
+newtype ConversationSelectElement = CheckedConversationSelectElement ConversationSelectElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildConversationSelectElement' before encoding.
+data ConversationSelectElementFields = ConversationSelectElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    conversationSelectElementActionId :: Maybe (Text),
+    -- | Sets the conversation ID selected when the menu loads. Optional; Nothing omits the field. Factory default: @Nothing@.
+    conversationSelectElementInitialConversation :: Maybe (Text),
+    -- | Sets whether the menu pre-selects the conversation the user is viewing. Optional; Nothing omits the field. Factory default: @Nothing@.
+    conversationSelectElementDefaultToCurrentConversation :: Maybe (Bool),
+    -- | Sets which conversation types the menu offers. Optional; Nothing omits the field. Factory default: @Nothing@.
+    conversationSelectElementFilter :: Maybe (ConversationFilter),
+    -- | Sets whether the selected conversation receives a response URL in the view submission payload. Only valid in modals. Optional; Nothing omits the field. Factory default: @Nothing@.
+    conversationSelectElementResponseUrlEnabled :: Maybe (Bool),
+    -- | Sets a confirmation dialog shown before the action is sent. Optional; Nothing omits the field. Factory default: @Nothing@.
+    conversationSelectElementConfirm :: Maybe (ConfirmationDialogue),
+    -- | Sets whether the element receives focus when the view opens. Only one element per view may do so. Optional; Nothing omits the field. Factory default: @Nothing@.
+    conversationSelectElementFocusOnLoad :: Maybe (Bool),
+    -- | Sets the placeholder text shown before a value is chosen. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @select.placeholder@.
+    conversationSelectElementPlaceholder :: Maybe (PlainText),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    conversationSelectElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for ConversationSelectElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildConversationSelectElement' to validate before JSON encoding.
+conversationSelectElement :: ConversationSelectElementFields
+conversationSelectElement = ConversationSelectElementFields (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildConversationSelectElement' to revalidate.
+inspectConversationSelectElement :: ConversationSelectElement -> ConversationSelectElementFields
+inspectConversationSelectElement (CheckedConversationSelectElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked ConversationSelectElement.
+buildConversationSelectElement :: ConversationSelectElementFields -> Result ConversationSelectElement
+buildConversationSelectElement fields = do
+  checkExtensions ["action_id", "initial_conversation", "default_to_current_conversation", "filter", "response_url_enabled", "confirm", "focus_on_load", "placeholder"] (conversationSelectElementExtensions fields)
+  validateFields "ConversationSelectElement" (wireConversationSelectElement fields)
+  pure (CheckedConversationSelectElement fields)
+
+wireConversationSelectElement :: ConversationSelectElementFields -> Value
+wireConversationSelectElement fields =
+  object $
+    ["type" .= ("conversations_select" :: Text)] <> optional "action_id" (conversationSelectElementActionId fields) <> optional "initial_conversation" (conversationSelectElementInitialConversation fields) <> optional "default_to_current_conversation" (conversationSelectElementDefaultToCurrentConversation fields) <> optional "filter" (conversationSelectElementFilter fields) <> optional "response_url_enabled" (conversationSelectElementResponseUrlEnabled fields) <> optional "confirm" (conversationSelectElementConfirm fields) <> optional "focus_on_load" (conversationSelectElementFocusOnLoad fields) <> optional "placeholder" (conversationSelectElementPlaceholder fields) <> extensionPairs (conversationSelectElementExtensions fields)
+
+instance ToJSON ConversationSelectElement where toJSON (CheckedConversationSelectElement fields) = wireConversationSelectElement fields
+
+instance FromSlackJSON ConversationSelectElement where
+  fromSlackJSON value = do
+    fields <- wireObject "conversations_select" value
+    parsedActionId <- optionalField "action_id" fields
+    parsedInitialConversation <- optionalField "initial_conversation" fields
+    parsedDefaultToCurrentConversation <- optionalField "default_to_current_conversation" fields
+    parsedFilter <- optionalField "filter" fields
+    parsedResponseUrlEnabled <- optionalField "response_url_enabled" fields
+    parsedConfirm <- optionalField "confirm" fields
+    parsedFocusOnLoad <- optionalField "focus_on_load" fields
+    parsedPlaceholder <- optionalField "placeholder" (coerceField "placeholder" "plain_text" fields)
+    buildConversationSelectElement (ConversationSelectElementFields parsedActionId parsedInitialConversation parsedDefaultToCurrentConversation parsedFilter parsedResponseUrlEnabled parsedConfirm parsedFocusOnLoad parsedPlaceholder (remaining ["action_id", "initial_conversation", "default_to_current_conversation", "filter", "response_url_enabled", "confirm", "focus_on_load", "placeholder"] fields))
+
+instance FromJSON ConversationSelectElement where parseJSON = checkedParser
+
+-- | A calendar date picker.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/date-picker-element>
+-- initial_date must have the YYYY-MM-DD shape. Calendar validity is application-owned; some language date parsers additionally reject impossible calendar dates.
+newtype DatePickerElement = CheckedDatePickerElement DatePickerElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildDatePickerElement' before encoding.
+data DatePickerElementFields = DatePickerElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    datePickerElementActionId :: Maybe (Text),
+    -- | Sets the initially selected date, formatted as YYYY-MM-DD. Optional; Nothing omits the field. Factory default: @Nothing@.
+    datePickerElementInitialDate :: Maybe (Text),
+    -- | Sets a confirmation dialog shown before the action is sent. Optional; Nothing omits the field. Factory default: @Nothing@.
+    datePickerElementConfirm :: Maybe (ConfirmationDialogue),
+    -- | Sets whether the element receives focus when the view opens. Only one element per view may do so. Optional; Nothing omits the field. Factory default: @Nothing@.
+    datePickerElementFocusOnLoad :: Maybe (Bool),
+    -- | Sets the placeholder text shown before a value is chosen. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @date_picker.placeholder@.
+    datePickerElementPlaceholder :: Maybe (PlainText),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    datePickerElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for DatePickerElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildDatePickerElement' to validate before JSON encoding.
+datePickerElement :: DatePickerElementFields
+datePickerElement = DatePickerElementFields (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildDatePickerElement' to revalidate.
+inspectDatePickerElement :: DatePickerElement -> DatePickerElementFields
+inspectDatePickerElement (CheckedDatePickerElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked DatePickerElement.
+buildDatePickerElement :: DatePickerElementFields -> Result DatePickerElement
+buildDatePickerElement fields = do
+  checkExtensions ["action_id", "initial_date", "confirm", "focus_on_load", "placeholder"] (datePickerElementExtensions fields)
+  validateFields "DatePickerElement" (wireDatePickerElement fields)
+  pure (CheckedDatePickerElement fields)
+
+wireDatePickerElement :: DatePickerElementFields -> Value
+wireDatePickerElement fields =
+  object $
+    ["type" .= ("datepicker" :: Text)] <> optional "action_id" (datePickerElementActionId fields) <> optional "initial_date" (datePickerElementInitialDate fields) <> optional "confirm" (datePickerElementConfirm fields) <> optional "focus_on_load" (datePickerElementFocusOnLoad fields) <> optional "placeholder" (datePickerElementPlaceholder fields) <> extensionPairs (datePickerElementExtensions fields)
+
+instance ToJSON DatePickerElement where toJSON (CheckedDatePickerElement fields) = wireDatePickerElement fields
+
+instance FromSlackJSON DatePickerElement where
+  fromSlackJSON value = do
+    fields <- wireObject "datepicker" value
+    parsedActionId <- optionalField "action_id" fields
+    parsedInitialDate <- optionalField "initial_date" fields
+    parsedConfirm <- optionalField "confirm" fields
+    parsedFocusOnLoad <- optionalField "focus_on_load" fields
+    parsedPlaceholder <- optionalField "placeholder" (coerceField "placeholder" "plain_text" fields)
+    buildDatePickerElement (DatePickerElementFields parsedActionId parsedInitialDate parsedConfirm parsedFocusOnLoad parsedPlaceholder (remaining ["action_id", "initial_date", "confirm", "focus_on_load", "placeholder"] fields))
+
+instance FromJSON DatePickerElement where parseJSON = checkedParser
+
+-- | A combined date and time picker.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/datetime-picker-element>
+newtype DateTimePickerElement = CheckedDateTimePickerElement DateTimePickerElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildDateTimePickerElement' before encoding.
+data DateTimePickerElementFields = DateTimePickerElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    dateTimePickerElementActionId :: Maybe (Text),
+    -- | Sets the initially selected moment as a UNIX timestamp in seconds. Optional; Nothing omits the field. Factory default: @Nothing@.
+    dateTimePickerElementInitialDateTime :: Maybe (Integer),
+    -- | Sets a confirmation dialog shown before the action is sent. Optional; Nothing omits the field. Factory default: @Nothing@.
+    dateTimePickerElementConfirm :: Maybe (ConfirmationDialogue),
+    -- | Sets whether the element receives focus when the view opens. Only one element per view may do so. Optional; Nothing omits the field. Factory default: @Nothing@.
+    dateTimePickerElementFocusOnLoad :: Maybe (Bool),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    dateTimePickerElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for DateTimePickerElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildDateTimePickerElement' to validate before JSON encoding.
+dateTimePickerElement :: DateTimePickerElementFields
+dateTimePickerElement = DateTimePickerElementFields (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildDateTimePickerElement' to revalidate.
+inspectDateTimePickerElement :: DateTimePickerElement -> DateTimePickerElementFields
+inspectDateTimePickerElement (CheckedDateTimePickerElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked DateTimePickerElement.
+buildDateTimePickerElement :: DateTimePickerElementFields -> Result DateTimePickerElement
+buildDateTimePickerElement fields = do
+  checkExtensions ["action_id", "initial_date_time", "confirm", "focus_on_load"] (dateTimePickerElementExtensions fields)
+  validateFields "DateTimePickerElement" (wireDateTimePickerElement fields)
+  pure (CheckedDateTimePickerElement fields)
+
+wireDateTimePickerElement :: DateTimePickerElementFields -> Value
+wireDateTimePickerElement fields =
+  object $
+    ["type" .= ("datetimepicker" :: Text)] <> optional "action_id" (dateTimePickerElementActionId fields) <> optional "initial_date_time" (dateTimePickerElementInitialDateTime fields) <> optional "confirm" (dateTimePickerElementConfirm fields) <> optional "focus_on_load" (dateTimePickerElementFocusOnLoad fields) <> extensionPairs (dateTimePickerElementExtensions fields)
+
+instance ToJSON DateTimePickerElement where toJSON (CheckedDateTimePickerElement fields) = wireDateTimePickerElement fields
+
+instance FromSlackJSON DateTimePickerElement where
+  fromSlackJSON value = do
+    fields <- wireObject "datetimepicker" value
+    parsedActionId <- optionalField "action_id" fields
+    parsedInitialDateTime <- optionalField "initial_date_time" fields
+    parsedConfirm <- optionalField "confirm" fields
+    parsedFocusOnLoad <- optionalField "focus_on_load" fields
+    buildDateTimePickerElement (DateTimePickerElementFields parsedActionId parsedInitialDateTime parsedConfirm parsedFocusOnLoad (remaining ["action_id", "initial_date_time", "confirm", "focus_on_load"] fields))
+
+instance FromJSON DateTimePickerElement where parseJSON = checkedParser
+
+-- | A single-line input that accepts an email address.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/email-input-element>
+newtype EmailInputElement = CheckedEmailInputElement EmailInputElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildEmailInputElement' before encoding.
+data EmailInputElementFields = EmailInputElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    emailInputElementActionId :: Maybe (Text),
+    -- | Sets the value present when the input loads. Optional; Nothing omits the field. Factory default: @Nothing@.
+    emailInputElementInitialValue :: Maybe (Text),
+    -- | Sets which user interactions send a block_actions payload. Optional; Nothing omits the field. Factory default: @Nothing@.
+    emailInputElementDispatchActionConfig :: Maybe (DispatchActionConfiguration),
+    -- | Sets whether the element receives focus when the view opens. Only one element per view may do so. Optional; Nothing omits the field. Factory default: @Nothing@.
+    emailInputElementFocusOnLoad :: Maybe (Bool),
+    -- | Sets the placeholder text shown before a value is chosen. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @email_input.placeholder@.
+    emailInputElementPlaceholder :: Maybe (PlainText),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    emailInputElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for EmailInputElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildEmailInputElement' to validate before JSON encoding.
+emailInputElement :: EmailInputElementFields
+emailInputElement = EmailInputElementFields (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildEmailInputElement' to revalidate.
+inspectEmailInputElement :: EmailInputElement -> EmailInputElementFields
+inspectEmailInputElement (CheckedEmailInputElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked EmailInputElement.
+buildEmailInputElement :: EmailInputElementFields -> Result EmailInputElement
+buildEmailInputElement fields = do
+  checkExtensions ["action_id", "initial_value", "dispatch_action_config", "focus_on_load", "placeholder"] (emailInputElementExtensions fields)
+  validateFields "EmailInputElement" (wireEmailInputElement fields)
+  pure (CheckedEmailInputElement fields)
+
+wireEmailInputElement :: EmailInputElementFields -> Value
+wireEmailInputElement fields =
+  object $
+    ["type" .= ("email_text_input" :: Text)] <> optional "action_id" (emailInputElementActionId fields) <> optional "initial_value" (emailInputElementInitialValue fields) <> optional "dispatch_action_config" (emailInputElementDispatchActionConfig fields) <> optional "focus_on_load" (emailInputElementFocusOnLoad fields) <> optional "placeholder" (emailInputElementPlaceholder fields) <> extensionPairs (emailInputElementExtensions fields)
+
+instance ToJSON EmailInputElement where toJSON (CheckedEmailInputElement fields) = wireEmailInputElement fields
+
+instance FromSlackJSON EmailInputElement where
+  fromSlackJSON value = do
+    fields <- wireObject "email_text_input" value
+    parsedActionId <- optionalField "action_id" fields
+    parsedInitialValue <- optionalField "initial_value" fields
+    parsedDispatchActionConfig <- optionalField "dispatch_action_config" fields
+    parsedFocusOnLoad <- optionalField "focus_on_load" fields
+    parsedPlaceholder <- optionalField "placeholder" (coerceField "placeholder" "plain_text" fields)
+    buildEmailInputElement (EmailInputElementFields parsedActionId parsedInitialValue parsedDispatchActionConfig parsedFocusOnLoad parsedPlaceholder (remaining ["action_id", "initial_value", "dispatch_action_config", "focus_on_load", "placeholder"] fields))
+
+instance FromJSON EmailInputElement where parseJSON = checkedParser
+
+-- | A menu for selecting multiple options loaded from your app.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element>
+-- focus_on_load is unsupported and must be omitted, including explicit false.
+newtype ExternalMultiSelectElement = CheckedExternalMultiSelectElement ExternalMultiSelectElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildExternalMultiSelectElement' before encoding.
+data ExternalMultiSelectElementFields = ExternalMultiSelectElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    externalMultiSelectElementActionId :: Maybe (Text),
+    -- | Sets how many characters the user must type before Slack queries your options endpoint. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @select.min_query_length@.
+    externalMultiSelectElementMinQueryLength :: Maybe (Integer),
+    -- | Adds options that are selected when the element loads. Each must match an option in the element. Optional; Nothing omits the field. Factory default: @Nothing@.
+    externalMultiSelectElementInitialOptions :: Maybe ([SelectOption]),
+    -- | Sets a confirmation dialog shown before the action is sent. Optional; Nothing omits the field. Factory default: @Nothing@.
+    externalMultiSelectElementConfirm :: Maybe (ConfirmationDialogue),
+    -- | Sets the maximum number of items a user can select. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @multi_select.max_selected_items@.
+    externalMultiSelectElementMaxSelectedItems :: Maybe (Integer),
+    -- | Deprecated compatibility parameter. Slack external multi-select menus do not support focus_on_load; omit it. Explicit true or false is rejected. Optional; Nothing omits the field. Factory default: @Nothing@.
+    externalMultiSelectElementFocusOnLoad :: Maybe (Bool),
+    -- | Sets the placeholder text shown before a value is chosen. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @select.placeholder@.
+    externalMultiSelectElementPlaceholder :: Maybe (PlainText),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    externalMultiSelectElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for ExternalMultiSelectElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildExternalMultiSelectElement' to validate before JSON encoding.
+externalMultiSelectElement :: ExternalMultiSelectElementFields
+externalMultiSelectElement = ExternalMultiSelectElementFields (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildExternalMultiSelectElement' to revalidate.
+inspectExternalMultiSelectElement :: ExternalMultiSelectElement -> ExternalMultiSelectElementFields
+inspectExternalMultiSelectElement (CheckedExternalMultiSelectElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked ExternalMultiSelectElement.
+buildExternalMultiSelectElement :: ExternalMultiSelectElementFields -> Result ExternalMultiSelectElement
+buildExternalMultiSelectElement fields = do
+  checkExtensions ["action_id", "min_query_length", "initial_options", "confirm", "max_selected_items", "focus_on_load", "placeholder"] (externalMultiSelectElementExtensions fields)
+  validateFields "ExternalMultiSelectElement" (wireExternalMultiSelectElement fields)
+  pure (CheckedExternalMultiSelectElement fields)
+
+wireExternalMultiSelectElement :: ExternalMultiSelectElementFields -> Value
+wireExternalMultiSelectElement fields =
+  object $
+    ["type" .= ("multi_external_select" :: Text)] <> optional "action_id" (externalMultiSelectElementActionId fields) <> optional "min_query_length" (externalMultiSelectElementMinQueryLength fields) <> optional "initial_options" (externalMultiSelectElementInitialOptions fields) <> optional "confirm" (externalMultiSelectElementConfirm fields) <> optional "max_selected_items" (externalMultiSelectElementMaxSelectedItems fields) <> optional "focus_on_load" (externalMultiSelectElementFocusOnLoad fields) <> optional "placeholder" (externalMultiSelectElementPlaceholder fields) <> extensionPairs (externalMultiSelectElementExtensions fields)
+
+instance ToJSON ExternalMultiSelectElement where toJSON (CheckedExternalMultiSelectElement fields) = wireExternalMultiSelectElement fields
+
+instance FromSlackJSON ExternalMultiSelectElement where
+  fromSlackJSON value = do
+    fields <- wireObject "multi_external_select" value
+    checkIntegerBounds "ExternalMultiSelectElement" (Object fields)
+    parsedActionId <- optionalField "action_id" fields
+    parsedMinQueryLength <- optionalField "min_query_length" fields
+    parsedInitialOptions <- optionalField "initial_options" fields
+    parsedConfirm <- optionalField "confirm" fields
+    parsedMaxSelectedItems <- optionalField "max_selected_items" fields
+    parsedFocusOnLoad <- optionalField "focus_on_load" fields
+    parsedPlaceholder <- optionalField "placeholder" (coerceField "placeholder" "plain_text" fields)
+    buildExternalMultiSelectElement (ExternalMultiSelectElementFields parsedActionId parsedMinQueryLength parsedInitialOptions parsedConfirm parsedMaxSelectedItems parsedFocusOnLoad parsedPlaceholder (remaining ["action_id", "min_query_length", "initial_options", "confirm", "max_selected_items", "focus_on_load", "placeholder"] fields))
+
+instance FromJSON ExternalMultiSelectElement where parseJSON = checkedParser
+
+-- | A menu for selecting one option loaded from your app.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element>
+newtype ExternalSelectElement = CheckedExternalSelectElement ExternalSelectElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildExternalSelectElement' before encoding.
+data ExternalSelectElementFields = ExternalSelectElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    externalSelectElementActionId :: Maybe (Text),
+    -- | Sets how many characters the user must type before Slack queries your options endpoint. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @select.min_query_length@.
+    externalSelectElementMinQueryLength :: Maybe (Integer),
+    -- | Sets the option selected when the element loads. It must match an option in the element. Optional; Nothing omits the field. Factory default: @Nothing@.
+    externalSelectElementInitialOption :: Maybe (SelectOption),
+    -- | Sets a confirmation dialog shown before the action is sent. Optional; Nothing omits the field. Factory default: @Nothing@.
+    externalSelectElementConfirm :: Maybe (ConfirmationDialogue),
+    -- | Sets whether the element receives focus when the view opens. Only one element per view may do so. Optional; Nothing omits the field. Factory default: @Nothing@.
+    externalSelectElementFocusOnLoad :: Maybe (Bool),
+    -- | Sets the placeholder text shown before a value is chosen. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @select.placeholder@.
+    externalSelectElementPlaceholder :: Maybe (PlainText),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    externalSelectElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for ExternalSelectElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildExternalSelectElement' to validate before JSON encoding.
+externalSelectElement :: ExternalSelectElementFields
+externalSelectElement = ExternalSelectElementFields (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildExternalSelectElement' to revalidate.
+inspectExternalSelectElement :: ExternalSelectElement -> ExternalSelectElementFields
+inspectExternalSelectElement (CheckedExternalSelectElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked ExternalSelectElement.
+buildExternalSelectElement :: ExternalSelectElementFields -> Result ExternalSelectElement
+buildExternalSelectElement fields = do
+  checkExtensions ["action_id", "min_query_length", "initial_option", "confirm", "focus_on_load", "placeholder"] (externalSelectElementExtensions fields)
+  validateFields "ExternalSelectElement" (wireExternalSelectElement fields)
+  pure (CheckedExternalSelectElement fields)
+
+wireExternalSelectElement :: ExternalSelectElementFields -> Value
+wireExternalSelectElement fields =
+  object $
+    ["type" .= ("external_select" :: Text)] <> optional "action_id" (externalSelectElementActionId fields) <> optional "min_query_length" (externalSelectElementMinQueryLength fields) <> optional "initial_option" (externalSelectElementInitialOption fields) <> optional "confirm" (externalSelectElementConfirm fields) <> optional "focus_on_load" (externalSelectElementFocusOnLoad fields) <> optional "placeholder" (externalSelectElementPlaceholder fields) <> extensionPairs (externalSelectElementExtensions fields)
+
+instance ToJSON ExternalSelectElement where toJSON (CheckedExternalSelectElement fields) = wireExternalSelectElement fields
+
+instance FromSlackJSON ExternalSelectElement where
+  fromSlackJSON value = do
+    fields <- wireObject "external_select" value
+    checkIntegerBounds "ExternalSelectElement" (Object fields)
+    parsedActionId <- optionalField "action_id" fields
+    parsedMinQueryLength <- optionalField "min_query_length" fields
+    parsedInitialOption <- optionalField "initial_option" fields
+    parsedConfirm <- optionalField "confirm" fields
+    parsedFocusOnLoad <- optionalField "focus_on_load" fields
+    parsedPlaceholder <- optionalField "placeholder" (coerceField "placeholder" "plain_text" fields)
+    buildExternalSelectElement (ExternalSelectElementFields parsedActionId parsedMinQueryLength parsedInitialOption parsedConfirm parsedFocusOnLoad parsedPlaceholder (remaining ["action_id", "min_query_length", "initial_option", "confirm", "focus_on_load", "placeholder"] fields))
+
+instance FromJSON ExternalSelectElement where parseJSON = checkedParser
+
+-- | A pair of thumbs-up and thumbs-down feedback buttons.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/feedback-buttons-element>
+newtype FeedbackButtonsElement = CheckedFeedbackButtonsElement FeedbackButtonsElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildFeedbackButtonsElement' before encoding.
+data FeedbackButtonsElementFields = FeedbackButtonsElementFields
+  { -- | Sets the button for positive feedback. Required.
+    feedbackButtonsElementPositiveButton :: FeedbackButton,
+    -- | Sets the button for negative feedback. Required.
+    feedbackButtonsElementNegativeButton :: FeedbackButton,
+    -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    feedbackButtonsElementActionId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    feedbackButtonsElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for FeedbackButtonsElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildFeedbackButtonsElement' to validate before JSON encoding.
+feedbackButtonsElement :: FeedbackButton -> FeedbackButton -> FeedbackButtonsElementFields
+feedbackButtonsElement arg0 arg1 = FeedbackButtonsElementFields (arg0) (arg1) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildFeedbackButtonsElement' to revalidate.
+inspectFeedbackButtonsElement :: FeedbackButtonsElement -> FeedbackButtonsElementFields
+inspectFeedbackButtonsElement (CheckedFeedbackButtonsElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked FeedbackButtonsElement.
+buildFeedbackButtonsElement :: FeedbackButtonsElementFields -> Result FeedbackButtonsElement
+buildFeedbackButtonsElement fields = do
+  checkExtensions ["positive_button", "negative_button", "action_id"] (feedbackButtonsElementExtensions fields)
+  validateFields "FeedbackButtonsElement" (wireFeedbackButtonsElement fields)
+  pure (CheckedFeedbackButtonsElement fields)
+
+wireFeedbackButtonsElement :: FeedbackButtonsElementFields -> Value
+wireFeedbackButtonsElement fields =
+  object $
+    ["type" .= ("feedback_buttons" :: Text)] <> ["positive_button" .= (feedbackButtonsElementPositiveButton fields)] <> ["negative_button" .= (feedbackButtonsElementNegativeButton fields)] <> optional "action_id" (feedbackButtonsElementActionId fields) <> extensionPairs (feedbackButtonsElementExtensions fields)
+
+instance ToJSON FeedbackButtonsElement where toJSON (CheckedFeedbackButtonsElement fields) = wireFeedbackButtonsElement fields
+
+instance FromSlackJSON FeedbackButtonsElement where
+  fromSlackJSON value = do
+    fields <- wireObject "feedback_buttons" value
+    parsedPositiveButton <- required "positive_button" fields
+    parsedNegativeButton <- required "negative_button" fields
+    parsedActionId <- optionalField "action_id" fields
+    buildFeedbackButtonsElement (FeedbackButtonsElementFields parsedPositiveButton parsedNegativeButton parsedActionId (remaining ["positive_button", "negative_button", "action_id"] fields))
+
+instance FromJSON FeedbackButtonsElement where parseJSON = checkedParser
+
+-- | An input that lets users upload files.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/file-input-element>
+newtype FileInputElement = CheckedFileInputElement FileInputElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildFileInputElement' before encoding.
+data FileInputElementFields = FileInputElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    fileInputElementActionId :: Maybe (Text),
+    -- | Adds accepted file extensions, such as pdf or png. Optional; Nothing omits the field. Factory default: @Nothing@.
+    fileInputElementFiletypes :: Maybe ([Text]),
+    -- | Sets the maximum number of files a user can upload. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @file_input.max_files@.
+    fileInputElementMaxFiles :: Maybe (Integer),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    fileInputElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for FileInputElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildFileInputElement' to validate before JSON encoding.
+fileInputElement :: FileInputElementFields
+fileInputElement = FileInputElementFields (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildFileInputElement' to revalidate.
+inspectFileInputElement :: FileInputElement -> FileInputElementFields
+inspectFileInputElement (CheckedFileInputElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked FileInputElement.
+buildFileInputElement :: FileInputElementFields -> Result FileInputElement
+buildFileInputElement fields = do
+  checkExtensions ["action_id", "filetypes", "max_files"] (fileInputElementExtensions fields)
+  validateFields "FileInputElement" (wireFileInputElement fields)
+  pure (CheckedFileInputElement fields)
+
+wireFileInputElement :: FileInputElementFields -> Value
+wireFileInputElement fields =
+  object $
+    ["type" .= ("file_input" :: Text)] <> optional "action_id" (fileInputElementActionId fields) <> optional "filetypes" (fileInputElementFiletypes fields) <> optional "max_files" (fileInputElementMaxFiles fields) <> extensionPairs (fileInputElementExtensions fields)
+
+instance ToJSON FileInputElement where toJSON (CheckedFileInputElement fields) = wireFileInputElement fields
+
+instance FromSlackJSON FileInputElement where
+  fromSlackJSON value = do
+    fields <- wireObject "file_input" value
+    checkIntegerBounds "FileInputElement" (Object fields)
+    parsedActionId <- optionalField "action_id" fields
+    parsedFiletypes <- optionalField "filetypes" fields
+    parsedMaxFiles <- optionalField "max_files" fields
+    buildFileInputElement (FileInputElementFields parsedActionId parsedFiletypes parsedMaxFiles (remaining ["action_id", "filetypes", "max_files"] fields))
+
+instance FromJSON FileInputElement where parseJSON = checkedParser
+
+-- | A button that shows an icon instead of text.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/icon-button-element>
+newtype IconButtonElement = CheckedIconButtonElement IconButtonElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildIconButtonElement' before encoding.
+data IconButtonElementFields = IconButtonElementFields
+  { -- | Sets the text used as the button's accessible name. Required.
+    iconButtonElementText :: PlainText,
+    -- | Sets the icon shown on the button. Required.
+    iconButtonElementIcon :: IconButtonIcon,
+    -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    iconButtonElementActionId :: Maybe (Text),
+    -- | Sets the application-defined value sent in interaction payloads. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @icon_button.value@.
+    iconButtonElementValue :: Maybe (Text),
+    -- | Sets a confirmation dialog shown before the action is sent. Optional; Nothing omits the field. Factory default: @Nothing@.
+    iconButtonElementConfirm :: Maybe (ConfirmationDialogue),
+    -- | Sets the label read by screen readers in place of the visible text. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @icon_button.accessibility_label@.
+    iconButtonElementAccessibilityLabel :: Maybe (Text),
+    -- | Adds the IDs of users who can see this button. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @icon_button.visible_to_user_ids@.
+    iconButtonElementVisibleToUserIds :: Maybe ([Text]),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    iconButtonElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for IconButtonElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildIconButtonElement' to validate before JSON encoding.
+iconButtonElement :: PlainText -> IconButtonIcon -> IconButtonElementFields
+iconButtonElement arg0 arg1 = IconButtonElementFields (arg0) (arg1) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildIconButtonElement' to revalidate.
+inspectIconButtonElement :: IconButtonElement -> IconButtonElementFields
+inspectIconButtonElement (CheckedIconButtonElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked IconButtonElement.
+buildIconButtonElement :: IconButtonElementFields -> Result IconButtonElement
+buildIconButtonElement fields = do
+  checkExtensions ["text", "icon", "action_id", "value", "confirm", "accessibility_label", "visible_to_user_ids"] (iconButtonElementExtensions fields)
+  validateFields "IconButtonElement" (wireIconButtonElement fields)
+  pure (CheckedIconButtonElement fields)
+
+wireIconButtonElement :: IconButtonElementFields -> Value
+wireIconButtonElement fields =
+  object $
+    ["type" .= ("icon_button" :: Text)] <> ["text" .= (iconButtonElementText fields)] <> ["icon" .= (iconButtonElementIcon fields)] <> optional "action_id" (iconButtonElementActionId fields) <> optional "value" (iconButtonElementValue fields) <> optional "confirm" (iconButtonElementConfirm fields) <> optional "accessibility_label" (iconButtonElementAccessibilityLabel fields) <> optional "visible_to_user_ids" (iconButtonElementVisibleToUserIds fields) <> extensionPairs (iconButtonElementExtensions fields)
+
+instance ToJSON IconButtonElement where toJSON (CheckedIconButtonElement fields) = wireIconButtonElement fields
+
+instance FromSlackJSON IconButtonElement where
+  fromSlackJSON value = do
+    fields <- wireObject "icon_button" value
+    parsedText <- required "text" (coerceField "text" "plain_text" fields)
+    parsedIcon <- required "icon" fields
+    parsedActionId <- optionalField "action_id" fields
+    parsedValue <- optionalField "value" fields
+    parsedConfirm <- optionalField "confirm" fields
+    parsedAccessibilityLabel <- optionalField "accessibility_label" fields
+    parsedVisibleToUserIds <- optionalField "visible_to_user_ids" fields
+    buildIconButtonElement (IconButtonElementFields parsedText parsedIcon parsedActionId parsedValue parsedConfirm parsedAccessibilityLabel parsedVisibleToUserIds (remaining ["text", "icon", "action_id", "value", "confirm", "accessibility_label", "visible_to_user_ids"] fields))
+
+instance FromJSON IconButtonElement where parseJSON = checkedParser
+
+-- | An image displayed inside a section, context, or card.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/image-element>
+-- Provide exactly one of an image URL or a Slack file.
+newtype ImageElement = CheckedImageElement ImageElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildImageElement' before encoding.
+data ImageElementFields = ImageElementFields
+  { -- | Sets a plain-text summary of the image or video for assistive technology. Required. Shared limit: @image_element.alt_text@.
+    imageElementAltText :: Text,
+    -- | Sets the publicly accessible URL of the image. Cannot be combined with a Slack file. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @image_element.image_url@.
+    imageElementImageUrl :: Maybe (Text),
+    -- | Sets a file hosted in Slack as the image source. Cannot be combined with an image URL. Optional; Nothing omits the field. Factory default: @Nothing@.
+    imageElementSlackFile :: Maybe (SlackFile),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    imageElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for ImageElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildImageElement' to validate before JSON encoding.
+imageElement :: Text -> ImageElementFields
+imageElement arg0 = ImageElementFields (arg0) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildImageElement' to revalidate.
+inspectImageElement :: ImageElement -> ImageElementFields
+inspectImageElement (CheckedImageElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked ImageElement.
+buildImageElement :: ImageElementFields -> Result ImageElement
+buildImageElement fields = do
+  checkExtensions ["alt_text", "image_url", "slack_file"] (imageElementExtensions fields)
+  validateFields "ImageElement" (wireImageElement fields)
+  pure (CheckedImageElement fields)
+
+wireImageElement :: ImageElementFields -> Value
+wireImageElement fields =
+  object $
+    ["type" .= ("image" :: Text)] <> ["alt_text" .= (imageElementAltText fields)] <> optional "image_url" (imageElementImageUrl fields) <> optional "slack_file" (imageElementSlackFile fields) <> extensionPairs (imageElementExtensions fields)
+
+instance ToJSON ImageElement where toJSON (CheckedImageElement fields) = wireImageElement fields
+
+instance FromSlackJSON ImageElement where
+  fromSlackJSON value = do
+    fields <- wireObject "image" value
+    parsedAltText <- required "alt_text" fields
+    parsedImageUrl <- optionalField "image_url" fields
+    parsedSlackFile <- optionalField "slack_file" fields
+    buildImageElement (ImageElementFields parsedAltText parsedImageUrl parsedSlackFile (remaining ["alt_text", "image_url", "slack_file"] fields))
+
+instance FromJSON ImageElement where parseJSON = checkedParser
+
+-- | An input that accepts whole or decimal numbers.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/number-input-element>
+-- The minimum value cannot exceed the maximum value.
+newtype NumberInputElement = CheckedNumberInputElement NumberInputElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildNumberInputElement' before encoding.
+data NumberInputElementFields = NumberInputElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    numberInputElementActionId :: Maybe (Text),
+    -- | Sets whether the input accepts decimal numbers. Required.
+    numberInputElementIsDecimalAllowed :: Bool,
+    -- | Sets the number present when the input loads, as a string such as 42 or 3.5. Optional; Nothing omits the field. Factory default: @Nothing@.
+    numberInputElementInitialValue :: Maybe (Text),
+    -- | Sets the smallest accepted number. Sent as a finite numeric string; native numeric convenience arguments are converted to strings. Optional; Nothing omits the field. Factory default: @Nothing@.
+    numberInputElementMinValue :: Maybe (Text),
+    -- | Sets the largest accepted number. Sent as a finite numeric string; native numeric convenience arguments are converted to strings. Optional; Nothing omits the field. Factory default: @Nothing@.
+    numberInputElementMaxValue :: Maybe (Text),
+    -- | Sets which user interactions send a block_actions payload. Optional; Nothing omits the field. Factory default: @Nothing@.
+    numberInputElementDispatchActionConfig :: Maybe (DispatchActionConfiguration),
+    -- | Sets whether the element receives focus when the view opens. Only one element per view may do so. Optional; Nothing omits the field. Factory default: @Nothing@.
+    numberInputElementFocusOnLoad :: Maybe (Bool),
+    -- | Sets the placeholder text shown before a value is chosen. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @number_input.placeholder@.
+    numberInputElementPlaceholder :: Maybe (PlainText),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    numberInputElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for NumberInputElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildNumberInputElement' to validate before JSON encoding.
+numberInputElement :: Bool -> NumberInputElementFields
+numberInputElement arg0 = NumberInputElementFields (Nothing) (arg0) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildNumberInputElement' to revalidate.
+inspectNumberInputElement :: NumberInputElement -> NumberInputElementFields
+inspectNumberInputElement (CheckedNumberInputElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked NumberInputElement.
+buildNumberInputElement :: NumberInputElementFields -> Result NumberInputElement
+buildNumberInputElement fields = do
+  checkExtensions ["action_id", "is_decimal_allowed", "initial_value", "min_value", "max_value", "dispatch_action_config", "focus_on_load", "placeholder"] (numberInputElementExtensions fields)
+  validateFields "NumberInputElement" (wireNumberInputElement fields)
+  pure (CheckedNumberInputElement fields)
+
+wireNumberInputElement :: NumberInputElementFields -> Value
+wireNumberInputElement fields =
+  object $
+    ["type" .= ("number_input" :: Text)] <> optional "action_id" (numberInputElementActionId fields) <> ["is_decimal_allowed" .= (numberInputElementIsDecimalAllowed fields)] <> optional "initial_value" (numberInputElementInitialValue fields) <> optional "min_value" (numberInputElementMinValue fields) <> optional "max_value" (numberInputElementMaxValue fields) <> optional "dispatch_action_config" (numberInputElementDispatchActionConfig fields) <> optional "focus_on_load" (numberInputElementFocusOnLoad fields) <> optional "placeholder" (numberInputElementPlaceholder fields) <> extensionPairs (numberInputElementExtensions fields)
+
+instance ToJSON NumberInputElement where toJSON (CheckedNumberInputElement fields) = wireNumberInputElement fields
+
+instance FromSlackJSON NumberInputElement where
+  fromSlackJSON value = do
+    fields <- wireObject "number_input" value
+    parsedActionId <- optionalField "action_id" fields
+    parsedIsDecimalAllowed <- required "is_decimal_allowed" fields
+    parsedInitialValue <- optionalField "initial_value" fields
+    parsedMinValue <- optionalField "min_value" (coerceField "min_value" "decimal_string" fields)
+    parsedMaxValue <- optionalField "max_value" (coerceField "max_value" "decimal_string" fields)
+    parsedDispatchActionConfig <- optionalField "dispatch_action_config" fields
+    parsedFocusOnLoad <- optionalField "focus_on_load" fields
+    parsedPlaceholder <- optionalField "placeholder" (coerceField "placeholder" "plain_text" fields)
+    buildNumberInputElement (NumberInputElementFields parsedActionId parsedIsDecimalAllowed parsedInitialValue parsedMinValue parsedMaxValue parsedDispatchActionConfig parsedFocusOnLoad parsedPlaceholder (remaining ["action_id", "is_decimal_allowed", "initial_value", "min_value", "max_value", "dispatch_action_config", "focus_on_load", "placeholder"] fields))
+
+instance FromJSON NumberInputElement where parseJSON = checkedParser
+
+-- | A compact menu of up to five options.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/overflow-menu-element>
+newtype OverflowElement = CheckedOverflowElement OverflowElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildOverflowElement' before encoding.
+data OverflowElementFields = OverflowElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    overflowElementActionId :: Maybe (Text),
+    -- | Adds selectable options in display order. Required. Shared limit: @overflow.options@.
+    overflowElementOptions :: [SelectOption],
+    -- | Sets a confirmation dialog shown before the action is sent. Optional; Nothing omits the field. Factory default: @Nothing@.
+    overflowElementConfirm :: Maybe (ConfirmationDialogue),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    overflowElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for OverflowElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildOverflowElement' to validate before JSON encoding.
+overflowElement :: [SelectOption] -> OverflowElementFields
+overflowElement arg0 = OverflowElementFields (Nothing) (arg0) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildOverflowElement' to revalidate.
+inspectOverflowElement :: OverflowElement -> OverflowElementFields
+inspectOverflowElement (CheckedOverflowElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked OverflowElement.
+buildOverflowElement :: OverflowElementFields -> Result OverflowElement
+buildOverflowElement fields = do
+  checkExtensions ["action_id", "options", "confirm"] (overflowElementExtensions fields)
+  validateFields "OverflowElement" (wireOverflowElement fields)
+  pure (CheckedOverflowElement fields)
+
+wireOverflowElement :: OverflowElementFields -> Value
+wireOverflowElement fields =
+  object $
+    ["type" .= ("overflow" :: Text)] <> optional "action_id" (overflowElementActionId fields) <> ["options" .= (overflowElementOptions fields)] <> optional "confirm" (overflowElementConfirm fields) <> extensionPairs (overflowElementExtensions fields)
+
+instance ToJSON OverflowElement where toJSON (CheckedOverflowElement fields) = wireOverflowElement fields
+
+instance FromSlackJSON OverflowElement where
+  fromSlackJSON value = do
+    fields <- wireObject "overflow" value
+    parsedActionId <- optionalField "action_id" fields
+    parsedOptions <- required "options" fields
+    parsedConfirm <- optionalField "confirm" fields
+    buildOverflowElement (OverflowElementFields parsedActionId parsedOptions parsedConfirm (remaining ["action_id", "options", "confirm"] fields))
+
+instance FromJSON OverflowElement where parseJSON = checkedParser
+
+-- | A single-line or multi-line free-text input.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/plain-text-input-element>
+-- When both min_length and max_length are supplied, min_length must not exceed max_length; otherwise the input cannot be satisfied.
+newtype PlainTextInputElement = CheckedPlainTextInputElement PlainTextInputElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildPlainTextInputElement' before encoding.
+data PlainTextInputElementFields = PlainTextInputElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    plainTextInputElementActionId :: Maybe (Text),
+    -- | Sets the value present when the input loads. Optional; Nothing omits the field. Factory default: @Nothing@.
+    plainTextInputElementInitialValue :: Maybe (Text),
+    -- | Sets whether the input is a multi-line text area. Optional; Nothing omits the field. Factory default: @Nothing@.
+    plainTextInputElementMultiline :: Maybe (Bool),
+    -- | Sets the minimum number of characters the user must enter. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @plain_text_input.min_length@.
+    plainTextInputElementMinLength :: Maybe (Integer),
+    -- | Sets the maximum number of characters the user can enter. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @plain_text_input.max_length@.
+    plainTextInputElementMaxLength :: Maybe (Integer),
+    -- | Sets which user interactions send a block_actions payload. Optional; Nothing omits the field. Factory default: @Nothing@.
+    plainTextInputElementDispatchActionConfig :: Maybe (DispatchActionConfiguration),
+    -- | Sets whether the element receives focus when the view opens. Only one element per view may do so. Optional; Nothing omits the field. Factory default: @Nothing@.
+    plainTextInputElementFocusOnLoad :: Maybe (Bool),
+    -- | Sets the placeholder text shown before a value is chosen. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @plain_text_input.placeholder@.
+    plainTextInputElementPlaceholder :: Maybe (PlainText),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    plainTextInputElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for PlainTextInputElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildPlainTextInputElement' to validate before JSON encoding.
+plainTextInputElement :: PlainTextInputElementFields
+plainTextInputElement = PlainTextInputElementFields (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildPlainTextInputElement' to revalidate.
+inspectPlainTextInputElement :: PlainTextInputElement -> PlainTextInputElementFields
+inspectPlainTextInputElement (CheckedPlainTextInputElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked PlainTextInputElement.
+buildPlainTextInputElement :: PlainTextInputElementFields -> Result PlainTextInputElement
+buildPlainTextInputElement fields = do
+  checkExtensions ["action_id", "initial_value", "multiline", "min_length", "max_length", "dispatch_action_config", "focus_on_load", "placeholder"] (plainTextInputElementExtensions fields)
+  validateFields "PlainTextInputElement" (wirePlainTextInputElement fields)
+  pure (CheckedPlainTextInputElement fields)
+
+wirePlainTextInputElement :: PlainTextInputElementFields -> Value
+wirePlainTextInputElement fields =
+  object $
+    ["type" .= ("plain_text_input" :: Text)] <> optional "action_id" (plainTextInputElementActionId fields) <> optional "initial_value" (plainTextInputElementInitialValue fields) <> optional "multiline" (plainTextInputElementMultiline fields) <> optional "min_length" (plainTextInputElementMinLength fields) <> optional "max_length" (plainTextInputElementMaxLength fields) <> optional "dispatch_action_config" (plainTextInputElementDispatchActionConfig fields) <> optional "focus_on_load" (plainTextInputElementFocusOnLoad fields) <> optional "placeholder" (plainTextInputElementPlaceholder fields) <> extensionPairs (plainTextInputElementExtensions fields)
+
+instance ToJSON PlainTextInputElement where toJSON (CheckedPlainTextInputElement fields) = wirePlainTextInputElement fields
+
+instance FromSlackJSON PlainTextInputElement where
+  fromSlackJSON value = do
+    fields <- wireObject "plain_text_input" value
+    checkIntegerBounds "PlainTextInputElement" (Object fields)
+    parsedActionId <- optionalField "action_id" fields
+    parsedInitialValue <- optionalField "initial_value" fields
+    parsedMultiline <- optionalField "multiline" fields
+    parsedMinLength <- optionalField "min_length" fields
+    parsedMaxLength <- optionalField "max_length" fields
+    parsedDispatchActionConfig <- optionalField "dispatch_action_config" fields
+    parsedFocusOnLoad <- optionalField "focus_on_load" fields
+    parsedPlaceholder <- optionalField "placeholder" (coerceField "placeholder" "plain_text" fields)
+    buildPlainTextInputElement (PlainTextInputElementFields parsedActionId parsedInitialValue parsedMultiline parsedMinLength parsedMaxLength parsedDispatchActionConfig parsedFocusOnLoad parsedPlaceholder (remaining ["action_id", "initial_value", "multiline", "min_length", "max_length", "dispatch_action_config", "focus_on_load", "placeholder"] fields))
+
+instance FromJSON PlainTextInputElement where parseJSON = checkedParser
+
+-- | A group of radio buttons.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/radio-button-group-element>
+newtype RadioButtonsElement = CheckedRadioButtonsElement RadioButtonsElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildRadioButtonsElement' before encoding.
+data RadioButtonsElementFields = RadioButtonsElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    radioButtonsElementActionId :: Maybe (Text),
+    -- | Adds selectable options in display order. Required. Shared limit: @radio_buttons.options@.
+    radioButtonsElementOptions :: [SelectOption],
+    -- | Sets the option selected when the element loads. It must match an option in the element. Optional; Nothing omits the field. Factory default: @Nothing@.
+    radioButtonsElementInitialOption :: Maybe (SelectOption),
+    -- | Sets a confirmation dialog shown before the action is sent. Optional; Nothing omits the field. Factory default: @Nothing@.
+    radioButtonsElementConfirm :: Maybe (ConfirmationDialogue),
+    -- | Sets whether the element receives focus when the view opens. Only one element per view may do so. Optional; Nothing omits the field. Factory default: @Nothing@.
+    radioButtonsElementFocusOnLoad :: Maybe (Bool),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    radioButtonsElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for RadioButtonsElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildRadioButtonsElement' to validate before JSON encoding.
+radioButtonsElement :: [SelectOption] -> RadioButtonsElementFields
+radioButtonsElement arg0 = RadioButtonsElementFields (Nothing) (arg0) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildRadioButtonsElement' to revalidate.
+inspectRadioButtonsElement :: RadioButtonsElement -> RadioButtonsElementFields
+inspectRadioButtonsElement (CheckedRadioButtonsElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked RadioButtonsElement.
+buildRadioButtonsElement :: RadioButtonsElementFields -> Result RadioButtonsElement
+buildRadioButtonsElement fields = do
+  checkExtensions ["action_id", "options", "initial_option", "confirm", "focus_on_load"] (radioButtonsElementExtensions fields)
+  validateFields "RadioButtonsElement" (wireRadioButtonsElement fields)
+  pure (CheckedRadioButtonsElement fields)
+
+wireRadioButtonsElement :: RadioButtonsElementFields -> Value
+wireRadioButtonsElement fields =
+  object $
+    ["type" .= ("radio_buttons" :: Text)] <> optional "action_id" (radioButtonsElementActionId fields) <> ["options" .= (radioButtonsElementOptions fields)] <> optional "initial_option" (radioButtonsElementInitialOption fields) <> optional "confirm" (radioButtonsElementConfirm fields) <> optional "focus_on_load" (radioButtonsElementFocusOnLoad fields) <> extensionPairs (radioButtonsElementExtensions fields)
+
+instance ToJSON RadioButtonsElement where toJSON (CheckedRadioButtonsElement fields) = wireRadioButtonsElement fields
+
+instance FromSlackJSON RadioButtonsElement where
+  fromSlackJSON value = do
+    fields <- wireObject "radio_buttons" value
+    parsedActionId <- optionalField "action_id" fields
+    parsedOptions <- required "options" fields
+    parsedInitialOption <- optionalField "initial_option" fields
+    parsedConfirm <- optionalField "confirm" fields
+    parsedFocusOnLoad <- optionalField "focus_on_load" fields
+    buildRadioButtonsElement (RadioButtonsElementFields parsedActionId parsedOptions parsedInitialOption parsedConfirm parsedFocusOnLoad (remaining ["action_id", "options", "initial_option", "confirm", "focus_on_load"] fields))
+
+instance FromJSON RadioButtonsElement where parseJSON = checkedParser
+
+-- | An input that accepts formatted rich text.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/rich-text-input-element>
+newtype RichTextInputElement = CheckedRichTextInputElement RichTextInputElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildRichTextInputElement' before encoding.
+data RichTextInputElementFields = RichTextInputElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Required. Shared limit: @action_id@.
+    richTextInputElementActionId :: Text,
+    -- | Sets the rich text present when the input loads. Optional; Nothing omits the field. Factory default: @Nothing@.
+    richTextInputElementInitialValue :: Maybe (RichTextBlock),
+    -- | Sets which user interactions send a block_actions payload. Optional; Nothing omits the field. Factory default: @Nothing@.
+    richTextInputElementDispatchActionConfig :: Maybe (DispatchActionConfiguration),
+    -- | Sets whether the element receives focus when the view opens. Only one element per view may do so. Optional; Nothing omits the field. Factory default: @Nothing@.
+    richTextInputElementFocusOnLoad :: Maybe (Bool),
+    -- | Sets the placeholder text shown before a value is chosen. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @rich_text_input.placeholder@.
+    richTextInputElementPlaceholder :: Maybe (PlainText),
+    -- | Sets the minimum visible height of the input, in lines. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @rich_text_input.min_lines@.
+    richTextInputElementMinLines :: Maybe (Integer),
+    -- | Sets the maximum visible height of the input, in lines. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @rich_text_input.max_lines@.
+    richTextInputElementMaxLines :: Maybe (Integer),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    richTextInputElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for RichTextInputElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildRichTextInputElement' to validate before JSON encoding.
+richTextInputElement :: Text -> RichTextInputElementFields
+richTextInputElement arg0 = RichTextInputElementFields (arg0) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildRichTextInputElement' to revalidate.
+inspectRichTextInputElement :: RichTextInputElement -> RichTextInputElementFields
+inspectRichTextInputElement (CheckedRichTextInputElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked RichTextInputElement.
+buildRichTextInputElement :: RichTextInputElementFields -> Result RichTextInputElement
+buildRichTextInputElement fields = do
+  checkExtensions ["action_id", "initial_value", "dispatch_action_config", "focus_on_load", "placeholder", "min_lines", "max_lines"] (richTextInputElementExtensions fields)
+  validateFields "RichTextInputElement" (wireRichTextInputElement fields)
+  pure (CheckedRichTextInputElement fields)
+
+wireRichTextInputElement :: RichTextInputElementFields -> Value
+wireRichTextInputElement fields =
+  object $
+    ["type" .= ("rich_text_input" :: Text)] <> ["action_id" .= (richTextInputElementActionId fields)] <> optional "initial_value" (richTextInputElementInitialValue fields) <> optional "dispatch_action_config" (richTextInputElementDispatchActionConfig fields) <> optional "focus_on_load" (richTextInputElementFocusOnLoad fields) <> optional "placeholder" (richTextInputElementPlaceholder fields) <> optional "min_lines" (richTextInputElementMinLines fields) <> optional "max_lines" (richTextInputElementMaxLines fields) <> extensionPairs (richTextInputElementExtensions fields)
+
+instance ToJSON RichTextInputElement where toJSON (CheckedRichTextInputElement fields) = wireRichTextInputElement fields
+
+instance FromSlackJSON RichTextInputElement where
+  fromSlackJSON value = do
+    fields <- wireObject "rich_text_input" value
+    checkIntegerBounds "RichTextInputElement" (Object fields)
+    parsedActionId <- required "action_id" fields
+    parsedInitialValue <- optionalField "initial_value" fields
+    parsedDispatchActionConfig <- optionalField "dispatch_action_config" fields
+    parsedFocusOnLoad <- optionalField "focus_on_load" fields
+    parsedPlaceholder <- optionalField "placeholder" (coerceField "placeholder" "plain_text" fields)
+    parsedMinLines <- optionalField "min_lines" fields
+    parsedMaxLines <- optionalField "max_lines" fields
+    buildRichTextInputElement (RichTextInputElementFields parsedActionId parsedInitialValue parsedDispatchActionConfig parsedFocusOnLoad parsedPlaceholder parsedMinLines parsedMaxLines (remaining ["action_id", "initial_value", "dispatch_action_config", "focus_on_load", "placeholder", "min_lines", "max_lines"] fields))
+
+instance FromJSON RichTextInputElement where parseJSON = checkedParser
+
+-- | A menu for selecting multiple options defined in the payload.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element>
+-- Use options or option groups, not both.
+newtype StaticMultiSelectElement = CheckedStaticMultiSelectElement StaticMultiSelectElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildStaticMultiSelectElement' before encoding.
+data StaticMultiSelectElementFields = StaticMultiSelectElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    staticMultiSelectElementActionId :: Maybe (Text),
+    -- | Adds selectable options in display order. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @select.options@.
+    staticMultiSelectElementOptions :: Maybe ([SelectOption]),
+    -- | Adds labelled groups of options in display order. Cannot be combined with ungrouped options. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @select.option_groups@.
+    staticMultiSelectElementOptionGroups :: Maybe ([SelectOptionGroup]),
+    -- | Adds options that are selected when the element loads. Each must match an option in the element. Optional; Nothing omits the field. Factory default: @Nothing@.
+    staticMultiSelectElementInitialOptions :: Maybe ([SelectOption]),
+    -- | Sets a confirmation dialog shown before the action is sent. Optional; Nothing omits the field. Factory default: @Nothing@.
+    staticMultiSelectElementConfirm :: Maybe (ConfirmationDialogue),
+    -- | Sets the maximum number of items a user can select. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @multi_select.max_selected_items@.
+    staticMultiSelectElementMaxSelectedItems :: Maybe (Integer),
+    -- | Sets whether the element receives focus when the view opens. Only one element per view may do so. Optional; Nothing omits the field. Factory default: @Nothing@.
+    staticMultiSelectElementFocusOnLoad :: Maybe (Bool),
+    -- | Sets the placeholder text shown before a value is chosen. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @select.placeholder@.
+    staticMultiSelectElementPlaceholder :: Maybe (PlainText),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    staticMultiSelectElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for StaticMultiSelectElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildStaticMultiSelectElement' to validate before JSON encoding.
+staticMultiSelectElement :: StaticMultiSelectElementFields
+staticMultiSelectElement = StaticMultiSelectElementFields (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildStaticMultiSelectElement' to revalidate.
+inspectStaticMultiSelectElement :: StaticMultiSelectElement -> StaticMultiSelectElementFields
+inspectStaticMultiSelectElement (CheckedStaticMultiSelectElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked StaticMultiSelectElement.
+buildStaticMultiSelectElement :: StaticMultiSelectElementFields -> Result StaticMultiSelectElement
+buildStaticMultiSelectElement fields = do
+  checkExtensions ["action_id", "options", "option_groups", "initial_options", "confirm", "max_selected_items", "focus_on_load", "placeholder"] (staticMultiSelectElementExtensions fields)
+  validateFields "StaticMultiSelectElement" (wireStaticMultiSelectElement fields)
+  pure (CheckedStaticMultiSelectElement fields)
+
+wireStaticMultiSelectElement :: StaticMultiSelectElementFields -> Value
+wireStaticMultiSelectElement fields =
+  object $
+    ["type" .= ("multi_static_select" :: Text)] <> optional "action_id" (staticMultiSelectElementActionId fields) <> optional "options" (staticMultiSelectElementOptions fields) <> optional "option_groups" (staticMultiSelectElementOptionGroups fields) <> optional "initial_options" (staticMultiSelectElementInitialOptions fields) <> optional "confirm" (staticMultiSelectElementConfirm fields) <> optional "max_selected_items" (staticMultiSelectElementMaxSelectedItems fields) <> optional "focus_on_load" (staticMultiSelectElementFocusOnLoad fields) <> optional "placeholder" (staticMultiSelectElementPlaceholder fields) <> extensionPairs (staticMultiSelectElementExtensions fields)
+
+instance ToJSON StaticMultiSelectElement where toJSON (CheckedStaticMultiSelectElement fields) = wireStaticMultiSelectElement fields
+
+instance FromSlackJSON StaticMultiSelectElement where
+  fromSlackJSON value = do
+    fields <- wireObject "multi_static_select" value
+    checkIntegerBounds "StaticMultiSelectElement" (Object fields)
+    parsedActionId <- optionalField "action_id" fields
+    parsedOptions <- optionalField "options" fields
+    parsedOptionGroups <- optionalField "option_groups" fields
+    parsedInitialOptions <- optionalField "initial_options" fields
+    parsedConfirm <- optionalField "confirm" fields
+    parsedMaxSelectedItems <- optionalField "max_selected_items" fields
+    parsedFocusOnLoad <- optionalField "focus_on_load" fields
+    parsedPlaceholder <- optionalField "placeholder" (coerceField "placeholder" "plain_text" fields)
+    buildStaticMultiSelectElement (StaticMultiSelectElementFields parsedActionId parsedOptions parsedOptionGroups parsedInitialOptions parsedConfirm parsedMaxSelectedItems parsedFocusOnLoad parsedPlaceholder (remaining ["action_id", "options", "option_groups", "initial_options", "confirm", "max_selected_items", "focus_on_load", "placeholder"] fields))
+
+instance FromJSON StaticMultiSelectElement where parseJSON = checkedParser
+
+-- | A menu for selecting one option defined in the payload.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element>
+-- Use options or option groups, not both.
+newtype StaticSelectElement = CheckedStaticSelectElement StaticSelectElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildStaticSelectElement' before encoding.
+data StaticSelectElementFields = StaticSelectElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    staticSelectElementActionId :: Maybe (Text),
+    -- | Adds selectable options in display order. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @select.options@.
+    staticSelectElementOptions :: Maybe ([SelectOption]),
+    -- | Adds labelled groups of options in display order. Cannot be combined with ungrouped options. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @select.option_groups@.
+    staticSelectElementOptionGroups :: Maybe ([SelectOptionGroup]),
+    -- | Sets the option selected when the element loads. It must match an option in the element. Optional; Nothing omits the field. Factory default: @Nothing@.
+    staticSelectElementInitialOption :: Maybe (SelectOption),
+    -- | Sets a confirmation dialog shown before the action is sent. Optional; Nothing omits the field. Factory default: @Nothing@.
+    staticSelectElementConfirm :: Maybe (ConfirmationDialogue),
+    -- | Sets whether the element receives focus when the view opens. Only one element per view may do so. Optional; Nothing omits the field. Factory default: @Nothing@.
+    staticSelectElementFocusOnLoad :: Maybe (Bool),
+    -- | Sets the placeholder text shown before a value is chosen. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @select.placeholder@.
+    staticSelectElementPlaceholder :: Maybe (PlainText),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    staticSelectElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for StaticSelectElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildStaticSelectElement' to validate before JSON encoding.
+staticSelectElement :: StaticSelectElementFields
+staticSelectElement = StaticSelectElementFields (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildStaticSelectElement' to revalidate.
+inspectStaticSelectElement :: StaticSelectElement -> StaticSelectElementFields
+inspectStaticSelectElement (CheckedStaticSelectElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked StaticSelectElement.
+buildStaticSelectElement :: StaticSelectElementFields -> Result StaticSelectElement
+buildStaticSelectElement fields = do
+  checkExtensions ["action_id", "options", "option_groups", "initial_option", "confirm", "focus_on_load", "placeholder"] (staticSelectElementExtensions fields)
+  validateFields "StaticSelectElement" (wireStaticSelectElement fields)
+  pure (CheckedStaticSelectElement fields)
+
+wireStaticSelectElement :: StaticSelectElementFields -> Value
+wireStaticSelectElement fields =
+  object $
+    ["type" .= ("static_select" :: Text)] <> optional "action_id" (staticSelectElementActionId fields) <> optional "options" (staticSelectElementOptions fields) <> optional "option_groups" (staticSelectElementOptionGroups fields) <> optional "initial_option" (staticSelectElementInitialOption fields) <> optional "confirm" (staticSelectElementConfirm fields) <> optional "focus_on_load" (staticSelectElementFocusOnLoad fields) <> optional "placeholder" (staticSelectElementPlaceholder fields) <> extensionPairs (staticSelectElementExtensions fields)
+
+instance ToJSON StaticSelectElement where toJSON (CheckedStaticSelectElement fields) = wireStaticSelectElement fields
+
+instance FromSlackJSON StaticSelectElement where
+  fromSlackJSON value = do
+    fields <- wireObject "static_select" value
+    parsedActionId <- optionalField "action_id" fields
+    parsedOptions <- optionalField "options" fields
+    parsedOptionGroups <- optionalField "option_groups" fields
+    parsedInitialOption <- optionalField "initial_option" fields
+    parsedConfirm <- optionalField "confirm" fields
+    parsedFocusOnLoad <- optionalField "focus_on_load" fields
+    parsedPlaceholder <- optionalField "placeholder" (coerceField "placeholder" "plain_text" fields)
+    buildStaticSelectElement (StaticSelectElementFields parsedActionId parsedOptions parsedOptionGroups parsedInitialOption parsedConfirm parsedFocusOnLoad parsedPlaceholder (remaining ["action_id", "options", "option_groups", "initial_option", "confirm", "focus_on_load", "placeholder"] fields))
+
+instance FromJSON StaticSelectElement where parseJSON = checkedParser
+
+-- | A time-of-day picker.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/time-picker-element>
+-- initial_time must be HH:mm in 24-hour time, from 00:00 through 23:59.
+newtype TimePickerElement = CheckedTimePickerElement TimePickerElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildTimePickerElement' before encoding.
+data TimePickerElementFields = TimePickerElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    timePickerElementActionId :: Maybe (Text),
+    -- | Sets the initially selected time, formatted as HH:mm in 24-hour time. Optional; Nothing omits the field. Factory default: @Nothing@.
+    timePickerElementInitialTime :: Maybe (Text),
+    -- | Sets the IANA time zone used to display the time, such as Australia/Brisbane. Optional; Nothing omits the field. Factory default: @Nothing@.
+    timePickerElementTimezone :: Maybe (Text),
+    -- | Sets a confirmation dialog shown before the action is sent. Optional; Nothing omits the field. Factory default: @Nothing@.
+    timePickerElementConfirm :: Maybe (ConfirmationDialogue),
+    -- | Sets whether the element receives focus when the view opens. Only one element per view may do so. Optional; Nothing omits the field. Factory default: @Nothing@.
+    timePickerElementFocusOnLoad :: Maybe (Bool),
+    -- | Sets the placeholder text shown before a value is chosen. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @time_picker.placeholder@.
+    timePickerElementPlaceholder :: Maybe (PlainText),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    timePickerElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for TimePickerElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildTimePickerElement' to validate before JSON encoding.
+timePickerElement :: TimePickerElementFields
+timePickerElement = TimePickerElementFields (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildTimePickerElement' to revalidate.
+inspectTimePickerElement :: TimePickerElement -> TimePickerElementFields
+inspectTimePickerElement (CheckedTimePickerElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked TimePickerElement.
+buildTimePickerElement :: TimePickerElementFields -> Result TimePickerElement
+buildTimePickerElement fields = do
+  checkExtensions ["action_id", "initial_time", "timezone", "confirm", "focus_on_load", "placeholder"] (timePickerElementExtensions fields)
+  validateFields "TimePickerElement" (wireTimePickerElement fields)
+  pure (CheckedTimePickerElement fields)
+
+wireTimePickerElement :: TimePickerElementFields -> Value
+wireTimePickerElement fields =
+  object $
+    ["type" .= ("timepicker" :: Text)] <> optional "action_id" (timePickerElementActionId fields) <> optional "initial_time" (timePickerElementInitialTime fields) <> optional "timezone" (timePickerElementTimezone fields) <> optional "confirm" (timePickerElementConfirm fields) <> optional "focus_on_load" (timePickerElementFocusOnLoad fields) <> optional "placeholder" (timePickerElementPlaceholder fields) <> extensionPairs (timePickerElementExtensions fields)
+
+instance ToJSON TimePickerElement where toJSON (CheckedTimePickerElement fields) = wireTimePickerElement fields
+
+instance FromSlackJSON TimePickerElement where
+  fromSlackJSON value = do
+    fields <- wireObject "timepicker" value
+    parsedActionId <- optionalField "action_id" fields
+    parsedInitialTime <- optionalField "initial_time" fields
+    parsedTimezone <- optionalField "timezone" fields
+    parsedConfirm <- optionalField "confirm" fields
+    parsedFocusOnLoad <- optionalField "focus_on_load" fields
+    parsedPlaceholder <- optionalField "placeholder" (coerceField "placeholder" "plain_text" fields)
+    buildTimePickerElement (TimePickerElementFields parsedActionId parsedInitialTime parsedTimezone parsedConfirm parsedFocusOnLoad parsedPlaceholder (remaining ["action_id", "initial_time", "timezone", "confirm", "focus_on_load", "placeholder"] fields))
+
+instance FromJSON TimePickerElement where parseJSON = checkedParser
+
+-- | A single-line input that accepts a URL.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/url-input-element>
+newtype UrlInputElement = CheckedUrlInputElement UrlInputElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildUrlInputElement' before encoding.
+data UrlInputElementFields = UrlInputElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    urlInputElementActionId :: Maybe (Text),
+    -- | Sets the value present when the input loads. Optional; Nothing omits the field. Factory default: @Nothing@.
+    urlInputElementInitialValue :: Maybe (Text),
+    -- | Sets which user interactions send a block_actions payload. Optional; Nothing omits the field. Factory default: @Nothing@.
+    urlInputElementDispatchActionConfig :: Maybe (DispatchActionConfiguration),
+    -- | Sets whether the element receives focus when the view opens. Only one element per view may do so. Optional; Nothing omits the field. Factory default: @Nothing@.
+    urlInputElementFocusOnLoad :: Maybe (Bool),
+    -- | Sets the placeholder text shown before a value is chosen. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @url_input.placeholder@.
+    urlInputElementPlaceholder :: Maybe (PlainText),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    urlInputElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for UrlInputElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildUrlInputElement' to validate before JSON encoding.
+urlInputElement :: UrlInputElementFields
+urlInputElement = UrlInputElementFields (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildUrlInputElement' to revalidate.
+inspectUrlInputElement :: UrlInputElement -> UrlInputElementFields
+inspectUrlInputElement (CheckedUrlInputElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked UrlInputElement.
+buildUrlInputElement :: UrlInputElementFields -> Result UrlInputElement
+buildUrlInputElement fields = do
+  checkExtensions ["action_id", "initial_value", "dispatch_action_config", "focus_on_load", "placeholder"] (urlInputElementExtensions fields)
+  validateFields "UrlInputElement" (wireUrlInputElement fields)
+  pure (CheckedUrlInputElement fields)
+
+wireUrlInputElement :: UrlInputElementFields -> Value
+wireUrlInputElement fields =
+  object $
+    ["type" .= ("url_text_input" :: Text)] <> optional "action_id" (urlInputElementActionId fields) <> optional "initial_value" (urlInputElementInitialValue fields) <> optional "dispatch_action_config" (urlInputElementDispatchActionConfig fields) <> optional "focus_on_load" (urlInputElementFocusOnLoad fields) <> optional "placeholder" (urlInputElementPlaceholder fields) <> extensionPairs (urlInputElementExtensions fields)
+
+instance ToJSON UrlInputElement where toJSON (CheckedUrlInputElement fields) = wireUrlInputElement fields
+
+instance FromSlackJSON UrlInputElement where
+  fromSlackJSON value = do
+    fields <- wireObject "url_text_input" value
+    parsedActionId <- optionalField "action_id" fields
+    parsedInitialValue <- optionalField "initial_value" fields
+    parsedDispatchActionConfig <- optionalField "dispatch_action_config" fields
+    parsedFocusOnLoad <- optionalField "focus_on_load" fields
+    parsedPlaceholder <- optionalField "placeholder" (coerceField "placeholder" "plain_text" fields)
+    buildUrlInputElement (UrlInputElementFields parsedActionId parsedInitialValue parsedDispatchActionConfig parsedFocusOnLoad parsedPlaceholder (remaining ["action_id", "initial_value", "dispatch_action_config", "focus_on_load", "placeholder"] fields))
+
+instance FromJSON UrlInputElement where parseJSON = checkedParser
+
+-- | A menu for selecting multiple workspace users.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element>
+newtype UserMultiSelectElement = CheckedUserMultiSelectElement UserMultiSelectElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildUserMultiSelectElement' before encoding.
+data UserMultiSelectElementFields = UserMultiSelectElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    userMultiSelectElementActionId :: Maybe (Text),
+    -- | Adds user IDs that are selected when the menu loads. Optional; Nothing omits the field. Factory default: @Nothing@.
+    userMultiSelectElementInitialUsers :: Maybe ([Text]),
+    -- | Sets a confirmation dialog shown before the action is sent. Optional; Nothing omits the field. Factory default: @Nothing@.
+    userMultiSelectElementConfirm :: Maybe (ConfirmationDialogue),
+    -- | Sets the maximum number of items a user can select. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @multi_select.max_selected_items@.
+    userMultiSelectElementMaxSelectedItems :: Maybe (Integer),
+    -- | Sets whether the element receives focus when the view opens. Only one element per view may do so. Optional; Nothing omits the field. Factory default: @Nothing@.
+    userMultiSelectElementFocusOnLoad :: Maybe (Bool),
+    -- | Sets the placeholder text shown before a value is chosen. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @select.placeholder@.
+    userMultiSelectElementPlaceholder :: Maybe (PlainText),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    userMultiSelectElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for UserMultiSelectElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildUserMultiSelectElement' to validate before JSON encoding.
+userMultiSelectElement :: UserMultiSelectElementFields
+userMultiSelectElement = UserMultiSelectElementFields (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildUserMultiSelectElement' to revalidate.
+inspectUserMultiSelectElement :: UserMultiSelectElement -> UserMultiSelectElementFields
+inspectUserMultiSelectElement (CheckedUserMultiSelectElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked UserMultiSelectElement.
+buildUserMultiSelectElement :: UserMultiSelectElementFields -> Result UserMultiSelectElement
+buildUserMultiSelectElement fields = do
+  checkExtensions ["action_id", "initial_users", "confirm", "max_selected_items", "focus_on_load", "placeholder"] (userMultiSelectElementExtensions fields)
+  validateFields "UserMultiSelectElement" (wireUserMultiSelectElement fields)
+  pure (CheckedUserMultiSelectElement fields)
+
+wireUserMultiSelectElement :: UserMultiSelectElementFields -> Value
+wireUserMultiSelectElement fields =
+  object $
+    ["type" .= ("multi_users_select" :: Text)] <> optional "action_id" (userMultiSelectElementActionId fields) <> optional "initial_users" (userMultiSelectElementInitialUsers fields) <> optional "confirm" (userMultiSelectElementConfirm fields) <> optional "max_selected_items" (userMultiSelectElementMaxSelectedItems fields) <> optional "focus_on_load" (userMultiSelectElementFocusOnLoad fields) <> optional "placeholder" (userMultiSelectElementPlaceholder fields) <> extensionPairs (userMultiSelectElementExtensions fields)
+
+instance ToJSON UserMultiSelectElement where toJSON (CheckedUserMultiSelectElement fields) = wireUserMultiSelectElement fields
+
+instance FromSlackJSON UserMultiSelectElement where
+  fromSlackJSON value = do
+    fields <- wireObject "multi_users_select" value
+    checkIntegerBounds "UserMultiSelectElement" (Object fields)
+    parsedActionId <- optionalField "action_id" fields
+    parsedInitialUsers <- optionalField "initial_users" fields
+    parsedConfirm <- optionalField "confirm" fields
+    parsedMaxSelectedItems <- optionalField "max_selected_items" fields
+    parsedFocusOnLoad <- optionalField "focus_on_load" fields
+    parsedPlaceholder <- optionalField "placeholder" (coerceField "placeholder" "plain_text" fields)
+    buildUserMultiSelectElement (UserMultiSelectElementFields parsedActionId parsedInitialUsers parsedConfirm parsedMaxSelectedItems parsedFocusOnLoad parsedPlaceholder (remaining ["action_id", "initial_users", "confirm", "max_selected_items", "focus_on_load", "placeholder"] fields))
+
+instance FromJSON UserMultiSelectElement where parseJSON = checkedParser
+
+-- | A menu for selecting one workspace user.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element>
+newtype UserSelectElement = CheckedUserSelectElement UserSelectElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildUserSelectElement' before encoding.
+data UserSelectElementFields = UserSelectElementFields
+  { -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @action_id@.
+    userSelectElementActionId :: Maybe (Text),
+    -- | Sets the user ID selected when the menu loads. Optional; Nothing omits the field. Factory default: @Nothing@.
+    userSelectElementInitialUser :: Maybe (Text),
+    -- | Sets a confirmation dialog shown before the action is sent. Optional; Nothing omits the field. Factory default: @Nothing@.
+    userSelectElementConfirm :: Maybe (ConfirmationDialogue),
+    -- | Sets whether the element receives focus when the view opens. Only one element per view may do so. Optional; Nothing omits the field. Factory default: @Nothing@.
+    userSelectElementFocusOnLoad :: Maybe (Bool),
+    -- | Sets the placeholder text shown before a value is chosen. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @select.placeholder@.
+    userSelectElementPlaceholder :: Maybe (PlainText),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    userSelectElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for UserSelectElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildUserSelectElement' to validate before JSON encoding.
+userSelectElement :: UserSelectElementFields
+userSelectElement = UserSelectElementFields (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildUserSelectElement' to revalidate.
+inspectUserSelectElement :: UserSelectElement -> UserSelectElementFields
+inspectUserSelectElement (CheckedUserSelectElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked UserSelectElement.
+buildUserSelectElement :: UserSelectElementFields -> Result UserSelectElement
+buildUserSelectElement fields = do
+  checkExtensions ["action_id", "initial_user", "confirm", "focus_on_load", "placeholder"] (userSelectElementExtensions fields)
+  validateFields "UserSelectElement" (wireUserSelectElement fields)
+  pure (CheckedUserSelectElement fields)
+
+wireUserSelectElement :: UserSelectElementFields -> Value
+wireUserSelectElement fields =
+  object $
+    ["type" .= ("users_select" :: Text)] <> optional "action_id" (userSelectElementActionId fields) <> optional "initial_user" (userSelectElementInitialUser fields) <> optional "confirm" (userSelectElementConfirm fields) <> optional "focus_on_load" (userSelectElementFocusOnLoad fields) <> optional "placeholder" (userSelectElementPlaceholder fields) <> extensionPairs (userSelectElementExtensions fields)
+
+instance ToJSON UserSelectElement where toJSON (CheckedUserSelectElement fields) = wireUserSelectElement fields
+
+instance FromSlackJSON UserSelectElement where
+  fromSlackJSON value = do
+    fields <- wireObject "users_select" value
+    parsedActionId <- optionalField "action_id" fields
+    parsedInitialUser <- optionalField "initial_user" fields
+    parsedConfirm <- optionalField "confirm" fields
+    parsedFocusOnLoad <- optionalField "focus_on_load" fields
+    parsedPlaceholder <- optionalField "placeholder" (coerceField "placeholder" "plain_text" fields)
+    buildUserSelectElement (UserSelectElementFields parsedActionId parsedInitialUser parsedConfirm parsedFocusOnLoad parsedPlaceholder (remaining ["action_id", "initial_user", "confirm", "focus_on_load", "placeholder"] fields))
+
+instance FromJSON UserSelectElement where parseJSON = checkedParser
+
+-- | A button that starts a workflow through a link trigger.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/workflow-button-element>
+newtype WorkflowButtonElement = CheckedWorkflowButtonElement WorkflowButtonElementFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildWorkflowButtonElement' before encoding.
+data WorkflowButtonElementFields = WorkflowButtonElementFields
+  { -- | Sets the button label. Required. Shared limit: @workflow_button.text@.
+    workflowButtonElementText :: PlainText,
+    -- | Sets the workflow started when the button is clicked. Required.
+    workflowButtonElementWorkflow :: Workflow,
+    -- | Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block. Required. Shared limit: @action_id@.
+    workflowButtonElementActionId :: Text,
+    -- | Sets a confirmation dialog shown before the action is sent. Optional; Nothing omits the field. Factory default: @Nothing@.
+    workflowButtonElementConfirm :: Maybe (ConfirmationDialogue),
+    -- | Sets the button's emphasis. Omit it for the default neutral style. Optional; Nothing omits the field. Factory default: @Nothing@.
+    workflowButtonElementStyle :: Maybe (ButtonStyle),
+    -- | Sets the label read by screen readers in place of the visible text. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @workflow_button.accessibility_label@.
+    workflowButtonElementAccessibilityLabel :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    workflowButtonElementExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for WorkflowButtonElement; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildWorkflowButtonElement' to validate before JSON encoding.
+workflowButtonElement :: PlainText -> Workflow -> Text -> WorkflowButtonElementFields
+workflowButtonElement arg0 arg1 arg2 = WorkflowButtonElementFields (arg0) (arg1) (arg2) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildWorkflowButtonElement' to revalidate.
+inspectWorkflowButtonElement :: WorkflowButtonElement -> WorkflowButtonElementFields
+inspectWorkflowButtonElement (CheckedWorkflowButtonElement fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked WorkflowButtonElement.
+buildWorkflowButtonElement :: WorkflowButtonElementFields -> Result WorkflowButtonElement
+buildWorkflowButtonElement fields = do
+  checkExtensions ["text", "workflow", "action_id", "confirm", "style", "accessibility_label"] (workflowButtonElementExtensions fields)
+  validateFields "WorkflowButtonElement" (wireWorkflowButtonElement fields)
+  pure (CheckedWorkflowButtonElement fields)
+
+wireWorkflowButtonElement :: WorkflowButtonElementFields -> Value
+wireWorkflowButtonElement fields =
+  object $
+    ["type" .= ("workflow_button" :: Text)] <> ["text" .= (workflowButtonElementText fields)] <> ["workflow" .= (workflowButtonElementWorkflow fields)] <> ["action_id" .= (workflowButtonElementActionId fields)] <> optional "confirm" (workflowButtonElementConfirm fields) <> optional "style" (workflowButtonElementStyle fields) <> optional "accessibility_label" (workflowButtonElementAccessibilityLabel fields) <> extensionPairs (workflowButtonElementExtensions fields)
+
+instance ToJSON WorkflowButtonElement where toJSON (CheckedWorkflowButtonElement fields) = wireWorkflowButtonElement fields
+
+instance FromSlackJSON WorkflowButtonElement where
+  fromSlackJSON value = do
+    fields <- wireObject "workflow_button" value
+    parsedText <- required "text" (coerceField "text" "plain_text" fields)
+    parsedWorkflow <- required "workflow" fields
+    parsedActionId <- required "action_id" fields
+    parsedConfirm <- optionalField "confirm" fields
+    parsedStyle <- optionalField "style" fields
+    parsedAccessibilityLabel <- optionalField "accessibility_label" fields
+    buildWorkflowButtonElement (WorkflowButtonElementFields parsedText parsedWorkflow parsedActionId parsedConfirm parsedStyle parsedAccessibilityLabel (remaining ["text", "workflow", "action_id", "confirm", "style", "accessibility_label"] fields))
+
+instance FromJSON WorkflowButtonElement where parseJSON = checkedParser
+
+-- | An area chart for a data visualization block.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block>
+-- Every series needs exactly one data point for each axis category.
+newtype AreaChart = CheckedAreaChart AreaChartFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildAreaChart' before encoding.
+data AreaChartFields = AreaChartFields
+  { -- | Adds data series in display order. Series names must be unique. Required. Shared limit: @data_visualization.series@.
+    areaChartSeries :: [DataSeries],
+    -- | Sets the axis categories and labels. Every series needs exactly one point per category. Required.
+    areaChartAxisConfig :: AxisConfig,
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    areaChartExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for AreaChart; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildAreaChart' to validate before JSON encoding.
+areaChart :: [DataSeries] -> AxisConfig -> AreaChartFields
+areaChart arg0 arg1 = AreaChartFields (arg0) (arg1) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildAreaChart' to revalidate.
+inspectAreaChart :: AreaChart -> AreaChartFields
+inspectAreaChart (CheckedAreaChart fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked AreaChart.
+buildAreaChart :: AreaChartFields -> Result AreaChart
+buildAreaChart fields = do
+  checkExtensions ["series", "axis_config"] (areaChartExtensions fields)
+  validateFields "AreaChart" (wireAreaChart fields)
+  pure (CheckedAreaChart fields)
+
+wireAreaChart :: AreaChartFields -> Value
+wireAreaChart fields =
+  object $
+    ["type" .= ("area" :: Text)] <> ["series" .= (areaChartSeries fields)] <> ["axis_config" .= (areaChartAxisConfig fields)] <> extensionPairs (areaChartExtensions fields)
+
+instance ToJSON AreaChart where toJSON (CheckedAreaChart fields) = wireAreaChart fields
+
+instance FromSlackJSON AreaChart where
+  fromSlackJSON value = do
+    fields <- wireObject "area" value
+    parsedSeries <- required "series" fields
+    parsedAxisConfig <- required "axis_config" fields
+    buildAreaChart (AreaChartFields parsedSeries parsedAxisConfig (remaining ["series", "axis_config"] fields))
+
+instance FromJSON AreaChart where parseJSON = checkedParser
+
+-- | The categories and labels shared by bar, area, and line chart axes.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block>
+newtype AxisConfig = CheckedAxisConfig AxisConfigFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildAxisConfig' before encoding.
+data AxisConfigFields = AxisConfigFields
+  { -- | Adds axis categories in display order. Labels must be unique. Required. Shared limit: @data_visualization.categories@.
+    axisConfigCategories :: [Text],
+    -- | Sets the horizontal axis label. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @data_visualization.axis_label@.
+    axisConfigXLabel :: Maybe (Text),
+    -- | Sets the vertical axis label. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @data_visualization.axis_label@.
+    axisConfigYLabel :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    axisConfigExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for AxisConfig; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildAxisConfig' to validate before JSON encoding.
+axisConfig :: [Text] -> AxisConfigFields
+axisConfig arg0 = AxisConfigFields (arg0) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildAxisConfig' to revalidate.
+inspectAxisConfig :: AxisConfig -> AxisConfigFields
+inspectAxisConfig (CheckedAxisConfig fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked AxisConfig.
+buildAxisConfig :: AxisConfigFields -> Result AxisConfig
+buildAxisConfig fields = do
+  checkExtensions ["categories", "x_label", "y_label"] (axisConfigExtensions fields)
+  validateFields "AxisConfig" (wireAxisConfig fields)
+  pure (CheckedAxisConfig fields)
+
+wireAxisConfig :: AxisConfigFields -> Value
+wireAxisConfig fields =
+  object $
+    [] <> ["categories" .= (axisConfigCategories fields)] <> optional "x_label" (axisConfigXLabel fields) <> optional "y_label" (axisConfigYLabel fields) <> extensionPairs (axisConfigExtensions fields)
+
+instance ToJSON AxisConfig where toJSON (CheckedAxisConfig fields) = wireAxisConfig fields
+
+instance FromSlackJSON AxisConfig where
+  fromSlackJSON value = do
+    fields <- wireObject "" value
+    parsedCategories <- required "categories" fields
+    parsedXLabel <- optionalField "x_label" fields
+    parsedYLabel <- optionalField "y_label" fields
+    buildAxisConfig (AxisConfigFields parsedCategories parsedXLabel parsedYLabel (remaining ["categories", "x_label", "y_label"] fields))
+
+instance FromJSON AxisConfig where parseJSON = checkedParser
+
+-- | A bar chart for a data visualization block.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block>
+-- Every series needs exactly one data point for each axis category.
+newtype BarChart = CheckedBarChart BarChartFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildBarChart' before encoding.
+data BarChartFields = BarChartFields
+  { -- | Adds data series in display order. Series names must be unique. Required. Shared limit: @data_visualization.series@.
+    barChartSeries :: [DataSeries],
+    -- | Sets the axis categories and labels. Every series needs exactly one point per category. Required.
+    barChartAxisConfig :: AxisConfig,
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    barChartExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for BarChart; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildBarChart' to validate before JSON encoding.
+barChart :: [DataSeries] -> AxisConfig -> BarChartFields
+barChart arg0 arg1 = BarChartFields (arg0) (arg1) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildBarChart' to revalidate.
+inspectBarChart :: BarChart -> BarChartFields
+inspectBarChart (CheckedBarChart fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked BarChart.
+buildBarChart :: BarChartFields -> Result BarChart
+buildBarChart fields = do
+  checkExtensions ["series", "axis_config"] (barChartExtensions fields)
+  validateFields "BarChart" (wireBarChart fields)
+  pure (CheckedBarChart fields)
+
+wireBarChart :: BarChartFields -> Value
+wireBarChart fields =
+  object $
+    ["type" .= ("bar" :: Text)] <> ["series" .= (barChartSeries fields)] <> ["axis_config" .= (barChartAxisConfig fields)] <> extensionPairs (barChartExtensions fields)
+
+instance ToJSON BarChart where toJSON (CheckedBarChart fields) = wireBarChart fields
+
+instance FromSlackJSON BarChart where
+  fromSlackJSON value = do
+    fields <- wireObject "bar" value
+    parsedSeries <- required "series" fields
+    parsedAxisConfig <- required "axis_config" fields
+    buildBarChart (BarChartFields parsedSeries parsedAxisConfig (remaining ["series", "axis_config"] fields))
+
+instance FromJSON BarChart where parseJSON = checkedParser
+
+-- | One labelled segment of a pie chart.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block>
+newtype ChartSegment = CheckedChartSegment ChartSegmentFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildChartSegment' before encoding.
+data ChartSegmentFields = ChartSegmentFields
+  { -- | Sets the segment label. Required. Shared limit: @data_visualization.segment.label@.
+    chartSegmentLabel :: Text,
+    -- | Sets the segment's size. Must be greater than zero. Required.
+    chartSegmentValue :: Scientific,
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    chartSegmentExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for ChartSegment; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildChartSegment' to validate before JSON encoding.
+chartSegment :: Text -> Scientific -> ChartSegmentFields
+chartSegment arg0 arg1 = ChartSegmentFields (arg0) (arg1) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildChartSegment' to revalidate.
+inspectChartSegment :: ChartSegment -> ChartSegmentFields
+inspectChartSegment (CheckedChartSegment fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked ChartSegment.
+buildChartSegment :: ChartSegmentFields -> Result ChartSegment
+buildChartSegment fields = do
+  checkExtensions ["label", "value"] (chartSegmentExtensions fields)
+  validateFields "ChartSegment" (wireChartSegment fields)
+  pure (CheckedChartSegment fields)
+
+wireChartSegment :: ChartSegmentFields -> Value
+wireChartSegment fields =
+  object $
+    [] <> ["label" .= (chartSegmentLabel fields)] <> ["value" .= (chartSegmentValue fields)] <> extensionPairs (chartSegmentExtensions fields)
+
+instance ToJSON ChartSegment where toJSON (CheckedChartSegment fields) = wireChartSegment fields
+
+instance FromSlackJSON ChartSegment where
+  fromSlackJSON value = do
+    fields <- wireObject "" value
+    parsedLabel <- required "label" fields
+    parsedValue <- required "value" fields
+    buildChartSegment (ChartSegmentFields parsedLabel parsedValue (remaining ["label", "value"] fields))
+
+instance FromJSON ChartSegment where parseJSON = checkedParser
+
+-- | Alignment and wrapping settings for one table column.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/table-block>
+newtype ColumnSettings = CheckedColumnSettings ColumnSettingsFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildColumnSettings' before encoding.
+data ColumnSettingsFields = ColumnSettingsFields
+  { -- | Sets how cell content in the column is aligned. Optional; Nothing omits the field. Factory default: @Nothing@.
+    columnSettingsAlign :: Maybe (ColumnAlign),
+    -- | Sets whether long cell content wraps instead of being truncated. Optional; Nothing omits the field. Factory default: @Nothing@.
+    columnSettingsIsWrapped :: Maybe (Bool),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    columnSettingsExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for ColumnSettings; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildColumnSettings' to validate before JSON encoding.
+columnSettings :: ColumnSettingsFields
+columnSettings = ColumnSettingsFields (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildColumnSettings' to revalidate.
+inspectColumnSettings :: ColumnSettings -> ColumnSettingsFields
+inspectColumnSettings (CheckedColumnSettings fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked ColumnSettings.
+buildColumnSettings :: ColumnSettingsFields -> Result ColumnSettings
+buildColumnSettings fields = do
+  checkExtensions ["align", "is_wrapped"] (columnSettingsExtensions fields)
+  validateFields "ColumnSettings" (wireColumnSettings fields)
+  pure (CheckedColumnSettings fields)
+
+wireColumnSettings :: ColumnSettingsFields -> Value
+wireColumnSettings fields =
+  object $
+    [] <> optional "align" (columnSettingsAlign fields) <> optional "is_wrapped" (columnSettingsIsWrapped fields) <> extensionPairs (columnSettingsExtensions fields)
+
+instance ToJSON ColumnSettings where toJSON (CheckedColumnSettings fields) = wireColumnSettings fields
+
+instance FromSlackJSON ColumnSettings where
+  fromSlackJSON value = do
+    fields <- wireObject "" value
+    parsedAlign <- optionalField "align" fields
+    parsedIsWrapped <- optionalField "is_wrapped" fields
+    buildColumnSettings (ColumnSettingsFields parsedAlign parsedIsWrapped (remaining ["align", "is_wrapped"] fields))
+
+instance FromJSON ColumnSettings where parseJSON = checkedParser
+
+-- | A dialog that asks users to confirm an action before it is sent.
+--
+-- <https://docs.slack.dev/reference/block-kit/composition-objects/confirmation-dialog-object>
+newtype ConfirmationDialogue = CheckedConfirmationDialogue ConfirmationDialogueFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildConfirmationDialogue' before encoding.
+data ConfirmationDialogueFields = ConfirmationDialogueFields
+  { -- | Sets the dialog title. Required. Shared limit: @confirmation.title@.
+    confirmationDialogueTitle :: PlainText,
+    -- | Sets the explanatory text in the dialog body. Required. Shared limit: @confirmation.text@.
+    confirmationDialogueText :: TextObject,
+    -- | Sets the label of the button that confirms the action. Required. Shared limit: @confirmation.confirm@.
+    confirmationDialogueConfirm :: PlainText,
+    -- | Sets the label of the button that cancels the action. Required. Shared limit: @confirmation.deny@.
+    confirmationDialogueDeny :: PlainText,
+    -- | Sets the emphasis of the confirm button. Optional; Nothing omits the field. Factory default: @Nothing@.
+    confirmationDialogueStyle :: Maybe (ButtonStyle),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    confirmationDialogueExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for ConfirmationDialogue; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildConfirmationDialogue' to validate before JSON encoding.
+confirmationDialogue :: PlainText -> TextObject -> PlainText -> PlainText -> ConfirmationDialogueFields
+confirmationDialogue arg0 arg1 arg2 arg3 = ConfirmationDialogueFields (arg0) (arg1) (arg2) (arg3) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildConfirmationDialogue' to revalidate.
+inspectConfirmationDialogue :: ConfirmationDialogue -> ConfirmationDialogueFields
+inspectConfirmationDialogue (CheckedConfirmationDialogue fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked ConfirmationDialogue.
+buildConfirmationDialogue :: ConfirmationDialogueFields -> Result ConfirmationDialogue
+buildConfirmationDialogue fields = do
+  checkExtensions ["title", "text", "confirm", "deny", "style"] (confirmationDialogueExtensions fields)
+  validateFields "Confirmation" (wireConfirmationDialogue fields)
+  pure (CheckedConfirmationDialogue fields)
+
+wireConfirmationDialogue :: ConfirmationDialogueFields -> Value
+wireConfirmationDialogue fields =
+  object $
+    [] <> ["title" .= (confirmationDialogueTitle fields)] <> ["text" .= (confirmationDialogueText fields)] <> ["confirm" .= (confirmationDialogueConfirm fields)] <> ["deny" .= (confirmationDialogueDeny fields)] <> optional "style" (confirmationDialogueStyle fields) <> extensionPairs (confirmationDialogueExtensions fields)
+
+instance ToJSON ConfirmationDialogue where toJSON (CheckedConfirmationDialogue fields) = wireConfirmationDialogue fields
+
+instance FromSlackJSON ConfirmationDialogue where
+  fromSlackJSON value = do
+    fields <- wireObject "" value
+    parsedTitle <- required "title" (coerceField "title" "plain_text" fields)
+    parsedText <- required "text" (coerceField "text" "mrkdwn" fields)
+    parsedConfirm <- required "confirm" (coerceField "confirm" "plain_text" fields)
+    parsedDeny <- required "deny" (coerceField "deny" "plain_text" fields)
+    parsedStyle <- optionalField "style" fields
+    buildConfirmationDialogue (ConfirmationDialogueFields parsedTitle parsedText parsedConfirm parsedDeny parsedStyle (remaining ["title", "text", "confirm", "deny", "style"] fields))
+
+instance FromJSON ConfirmationDialogue where parseJSON = checkedParser
+
+-- | Limits which conversations a conversation menu offers.
+--
+-- <https://docs.slack.dev/reference/block-kit/composition-objects/conversation-filter-object>
+-- Set at least one filter.
+-- Include only im, mpim, private, and public.
+newtype ConversationFilter = CheckedConversationFilter ConversationFilterFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildConversationFilter' before encoding.
+data ConversationFilterFields = ConversationFilterFields
+  { -- | Adds conversation types to offer: im, mpim, private, or public. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @conversation_filter.include@.
+    conversationFilterInclude :: Maybe ([Text]),
+    -- | Sets whether externally shared channels are excluded. Optional; Nothing omits the field. Factory default: @Nothing@.
+    conversationFilterExcludeExternalSharedChannels :: Maybe (Bool),
+    -- | Sets whether bot users are excluded. Optional; Nothing omits the field. Factory default: @Nothing@.
+    conversationFilterExcludeBotUsers :: Maybe (Bool),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    conversationFilterExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for ConversationFilter; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildConversationFilter' to validate before JSON encoding.
+conversationFilter :: ConversationFilterFields
+conversationFilter = ConversationFilterFields (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildConversationFilter' to revalidate.
+inspectConversationFilter :: ConversationFilter -> ConversationFilterFields
+inspectConversationFilter (CheckedConversationFilter fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked ConversationFilter.
+buildConversationFilter :: ConversationFilterFields -> Result ConversationFilter
+buildConversationFilter fields = do
+  checkExtensions ["include", "exclude_external_shared_channels", "exclude_bot_users"] (conversationFilterExtensions fields)
+  validateFields "ConversationFilter" (wireConversationFilter fields)
+  pure (CheckedConversationFilter fields)
+
+wireConversationFilter :: ConversationFilterFields -> Value
+wireConversationFilter fields =
+  object $
+    [] <> optional "include" (conversationFilterInclude fields) <> optional "exclude_external_shared_channels" (conversationFilterExcludeExternalSharedChannels fields) <> optional "exclude_bot_users" (conversationFilterExcludeBotUsers fields) <> extensionPairs (conversationFilterExtensions fields)
+
+instance ToJSON ConversationFilter where toJSON (CheckedConversationFilter fields) = wireConversationFilter fields
+
+instance FromSlackJSON ConversationFilter where
+  fromSlackJSON value = do
+    fields <- wireObject "" value
+    parsedInclude <- optionalField "include" fields
+    parsedExcludeExternalSharedChannels <- optionalField "exclude_external_shared_channels" fields
+    parsedExcludeBotUsers <- optionalField "exclude_bot_users" fields
+    buildConversationFilter (ConversationFilterFields parsedInclude parsedExcludeExternalSharedChannels parsedExcludeBotUsers (remaining ["include", "exclude_external_shared_channels", "exclude_bot_users"] fields))
+
+instance FromJSON ConversationFilter where parseJSON = checkedParser
+
+-- | One value of a chart series, keyed by axis category.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block>
+newtype DataPoint = CheckedDataPoint DataPointFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildDataPoint' before encoding.
+data DataPointFields = DataPointFields
+  { -- | Sets the axis category this point belongs to. Required. Shared limit: @data_visualization.point_label@.
+    dataPointLabel :: Text,
+    -- | Sets the point's value. Required.
+    dataPointValue :: Scientific,
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    dataPointExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for DataPoint; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildDataPoint' to validate before JSON encoding.
+dataPoint :: Text -> Scientific -> DataPointFields
+dataPoint arg0 arg1 = DataPointFields (arg0) (arg1) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildDataPoint' to revalidate.
+inspectDataPoint :: DataPoint -> DataPointFields
+inspectDataPoint (CheckedDataPoint fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked DataPoint.
+buildDataPoint :: DataPointFields -> Result DataPoint
+buildDataPoint fields = do
+  checkExtensions ["label", "value"] (dataPointExtensions fields)
+  validateFields "DataPoint" (wireDataPoint fields)
+  pure (CheckedDataPoint fields)
+
+wireDataPoint :: DataPointFields -> Value
+wireDataPoint fields =
+  object $
+    [] <> ["label" .= (dataPointLabel fields)] <> ["value" .= (dataPointValue fields)] <> extensionPairs (dataPointExtensions fields)
+
+instance ToJSON DataPoint where toJSON (CheckedDataPoint fields) = wireDataPoint fields
+
+instance FromSlackJSON DataPoint where
+  fromSlackJSON value = do
+    fields <- wireObject "" value
+    parsedLabel <- required "label" fields
+    parsedValue <- required "value" fields
+    buildDataPoint (DataPointFields parsedLabel parsedValue (remaining ["label", "value"] fields))
+
+instance FromJSON DataPoint where parseJSON = checkedParser
+
+-- | A named series of chart data points.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block>
+newtype DataSeries = CheckedDataSeries DataSeriesFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildDataSeries' before encoding.
+data DataSeriesFields = DataSeriesFields
+  { -- | Sets the series name shown in the legend. Required. Shared limit: @data_visualization.series_name@.
+    dataSeriesName :: Text,
+    -- | Adds data points in axis-category order. Required. Shared limit: @data_visualization.data@.
+    dataSeriesData :: [DataPoint],
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    dataSeriesExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for DataSeries; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildDataSeries' to validate before JSON encoding.
+dataSeries :: Text -> [DataPoint] -> DataSeriesFields
+dataSeries arg0 arg1 = DataSeriesFields (arg0) (arg1) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildDataSeries' to revalidate.
+inspectDataSeries :: DataSeries -> DataSeriesFields
+inspectDataSeries (CheckedDataSeries fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked DataSeries.
+buildDataSeries :: DataSeriesFields -> Result DataSeries
+buildDataSeries fields = do
+  checkExtensions ["name", "data"] (dataSeriesExtensions fields)
+  validateFields "DataSeries" (wireDataSeries fields)
+  pure (CheckedDataSeries fields)
+
+wireDataSeries :: DataSeriesFields -> Value
+wireDataSeries fields =
+  object $
+    [] <> ["name" .= (dataSeriesName fields)] <> ["data" .= (dataSeriesData fields)] <> extensionPairs (dataSeriesExtensions fields)
+
+instance ToJSON DataSeries where toJSON (CheckedDataSeries fields) = wireDataSeries fields
+
+instance FromSlackJSON DataSeries where
+  fromSlackJSON value = do
+    fields <- wireObject "" value
+    parsedName <- required "name" fields
+    parsedData <- required "data" fields
+    buildDataSeries (DataSeriesFields parsedName parsedData (remaining ["name", "data"] fields))
+
+instance FromJSON DataSeries where parseJSON = checkedParser
+
+-- | Chooses which interactions with an input send a block_actions payload.
+--
+-- <https://docs.slack.dev/reference/block-kit/composition-objects/dispatch-action-configuration-object>
+newtype DispatchActionConfiguration = CheckedDispatchActionConfiguration DispatchActionConfigurationFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildDispatchActionConfiguration' before encoding.
+data DispatchActionConfigurationFields = DispatchActionConfigurationFields
+  { -- | Adds the interactions that send a payload: on_enter_pressed or on_character_entered. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @dispatch_action_configuration.trigger_actions_on@.
+    dispatchActionConfigurationTriggerActionsOn :: Maybe ([Text]),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    dispatchActionConfigurationExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for DispatchActionConfiguration; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildDispatchActionConfiguration' to validate before JSON encoding.
+dispatchActionConfiguration :: DispatchActionConfigurationFields
+dispatchActionConfiguration = DispatchActionConfigurationFields (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildDispatchActionConfiguration' to revalidate.
+inspectDispatchActionConfiguration :: DispatchActionConfiguration -> DispatchActionConfigurationFields
+inspectDispatchActionConfiguration (CheckedDispatchActionConfiguration fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked DispatchActionConfiguration.
+buildDispatchActionConfiguration :: DispatchActionConfigurationFields -> Result DispatchActionConfiguration
+buildDispatchActionConfiguration fields = do
+  checkExtensions ["trigger_actions_on"] (dispatchActionConfigurationExtensions fields)
+  validateFields "DispatchActionConfiguration" (wireDispatchActionConfiguration fields)
+  pure (CheckedDispatchActionConfiguration fields)
+
+wireDispatchActionConfiguration :: DispatchActionConfigurationFields -> Value
+wireDispatchActionConfiguration fields =
+  object $
+    [] <> optional "trigger_actions_on" (dispatchActionConfigurationTriggerActionsOn fields) <> extensionPairs (dispatchActionConfigurationExtensions fields)
+
+instance ToJSON DispatchActionConfiguration where toJSON (CheckedDispatchActionConfiguration fields) = wireDispatchActionConfiguration fields
+
+instance FromSlackJSON DispatchActionConfiguration where
+  fromSlackJSON value = do
+    fields <- wireObject "" value
+    parsedTriggerActionsOn <- optionalField "trigger_actions_on" fields
+    buildDispatchActionConfiguration (DispatchActionConfigurationFields parsedTriggerActionsOn (remaining ["trigger_actions_on"] fields))
+
+instance FromJSON DispatchActionConfiguration where parseJSON = checkedParser
+
+-- | One button of a feedback buttons element.
+--
+-- <https://docs.slack.dev/reference/block-kit/block-elements/feedback-buttons-element>
+newtype FeedbackButton = CheckedFeedbackButton FeedbackButtonFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildFeedbackButton' before encoding.
+data FeedbackButtonFields = FeedbackButtonFields
+  { -- | Sets the button label. Required. Shared limit: @feedback_button.text@.
+    feedbackButtonText :: PlainText,
+    -- | Sets the application-defined value sent in interaction payloads. Required. Shared limit: @feedback_button.value@.
+    feedbackButtonValue :: Text,
+    -- | Sets the label read by screen readers in place of the visible text. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @feedback_button.accessibility_label@.
+    feedbackButtonAccessibilityLabel :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    feedbackButtonExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for FeedbackButton; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildFeedbackButton' to validate before JSON encoding.
+feedbackButton :: PlainText -> Text -> FeedbackButtonFields
+feedbackButton arg0 arg1 = FeedbackButtonFields (arg0) (arg1) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildFeedbackButton' to revalidate.
+inspectFeedbackButton :: FeedbackButton -> FeedbackButtonFields
+inspectFeedbackButton (CheckedFeedbackButton fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked FeedbackButton.
+buildFeedbackButton :: FeedbackButtonFields -> Result FeedbackButton
+buildFeedbackButton fields = do
+  checkExtensions ["text", "value", "accessibility_label"] (feedbackButtonExtensions fields)
+  validateFields "FeedbackButton" (wireFeedbackButton fields)
+  pure (CheckedFeedbackButton fields)
+
+wireFeedbackButton :: FeedbackButtonFields -> Value
+wireFeedbackButton fields =
+  object $
+    [] <> ["text" .= (feedbackButtonText fields)] <> ["value" .= (feedbackButtonValue fields)] <> optional "accessibility_label" (feedbackButtonAccessibilityLabel fields) <> extensionPairs (feedbackButtonExtensions fields)
+
+instance ToJSON FeedbackButton where toJSON (CheckedFeedbackButton fields) = wireFeedbackButton fields
+
+instance FromSlackJSON FeedbackButton where
+  fromSlackJSON value = do
+    fields <- wireObject "" value
+    parsedText <- required "text" (coerceField "text" "plain_text" fields)
+    parsedValue <- required "value" fields
+    parsedAccessibilityLabel <- optionalField "accessibility_label" fields
+    buildFeedbackButton (FeedbackButtonFields parsedText parsedValue parsedAccessibilityLabel (remaining ["text", "value", "accessibility_label"] fields))
+
+instance FromJSON FeedbackButton where parseJSON = checkedParser
+
+-- | A named value passed to a workflow trigger.
+--
+-- <https://docs.slack.dev/reference/block-kit/composition-objects/trigger-object>
+newtype InputParameter = CheckedInputParameter InputParameterFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildInputParameter' before encoding.
+data InputParameterFields = InputParameterFields
+  { -- | Sets the workflow input parameter name. Required.
+    inputParameterName :: Text,
+    -- | Sets the value passed to the workflow input. Required.
+    inputParameterValue :: Text,
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    inputParameterExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for InputParameter; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildInputParameter' to validate before JSON encoding.
+inputParameter :: Text -> Text -> InputParameterFields
+inputParameter arg0 arg1 = InputParameterFields (arg0) (arg1) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildInputParameter' to revalidate.
+inspectInputParameter :: InputParameter -> InputParameterFields
+inspectInputParameter (CheckedInputParameter fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked InputParameter.
+buildInputParameter :: InputParameterFields -> Result InputParameter
+buildInputParameter fields = do
+  checkExtensions ["name", "value"] (inputParameterExtensions fields)
+  validateFields "InputParameter" (wireInputParameter fields)
+  pure (CheckedInputParameter fields)
+
+wireInputParameter :: InputParameterFields -> Value
+wireInputParameter fields =
+  object $
+    [] <> ["name" .= (inputParameterName fields)] <> ["value" .= (inputParameterValue fields)] <> extensionPairs (inputParameterExtensions fields)
+
+instance ToJSON InputParameter where toJSON (CheckedInputParameter fields) = wireInputParameter fields
+
+instance FromSlackJSON InputParameter where
+  fromSlackJSON value = do
+    fields <- wireObject "" value
+    parsedName <- required "name" fields
+    parsedValue <- required "value" fields
+    buildInputParameter (InputParameterFields parsedName parsedValue (remaining ["name", "value"] fields))
+
+instance FromJSON InputParameter where parseJSON = checkedParser
+
+-- | A line chart for a data visualization block.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block>
+-- Every series needs exactly one data point for each axis category.
+newtype LineChart = CheckedLineChart LineChartFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildLineChart' before encoding.
+data LineChartFields = LineChartFields
+  { -- | Adds data series in display order. Series names must be unique. Required. Shared limit: @data_visualization.series@.
+    lineChartSeries :: [DataSeries],
+    -- | Sets the axis categories and labels. Every series needs exactly one point per category. Required.
+    lineChartAxisConfig :: AxisConfig,
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    lineChartExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for LineChart; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildLineChart' to validate before JSON encoding.
+lineChart :: [DataSeries] -> AxisConfig -> LineChartFields
+lineChart arg0 arg1 = LineChartFields (arg0) (arg1) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildLineChart' to revalidate.
+inspectLineChart :: LineChart -> LineChartFields
+inspectLineChart (CheckedLineChart fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked LineChart.
+buildLineChart :: LineChartFields -> Result LineChart
+buildLineChart fields = do
+  checkExtensions ["series", "axis_config"] (lineChartExtensions fields)
+  validateFields "LineChart" (wireLineChart fields)
+  pure (CheckedLineChart fields)
+
+wireLineChart :: LineChartFields -> Value
+wireLineChart fields =
+  object $
+    ["type" .= ("line" :: Text)] <> ["series" .= (lineChartSeries fields)] <> ["axis_config" .= (lineChartAxisConfig fields)] <> extensionPairs (lineChartExtensions fields)
+
+instance ToJSON LineChart where toJSON (CheckedLineChart fields) = wireLineChart fields
+
+instance FromSlackJSON LineChart where
+  fromSlackJSON value = do
+    fields <- wireObject "line" value
+    parsedSeries <- required "series" fields
+    parsedAxisConfig <- required "axis_config" fields
+    buildLineChart (LineChartFields parsedSeries parsedAxisConfig (remaining ["series", "axis_config"] fields))
+
+instance FromJSON LineChart where parseJSON = checkedParser
+
+-- | A mrkdwn text composition object.
+--
+-- <https://docs.slack.dev/reference/block-kit/composition-objects/text-object>
+newtype MarkdownText = CheckedMarkdownText MarkdownTextFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildMarkdownText' before encoding.
+data MarkdownTextFields = MarkdownTextFields
+  { -- | Sets the mrkdwn-formatted text content. Required.
+    markdownTextText :: Text,
+    -- | Sets whether Slack leaves URLs, mentions, and channel names unlinked. Optional; Nothing omits the field. Factory default: @Nothing@.
+    markdownTextVerbatim :: Maybe (Bool),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    markdownTextExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for MarkdownText; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildMarkdownText' to validate before JSON encoding.
+markdownText :: Text -> MarkdownTextFields
+markdownText arg0 = MarkdownTextFields (arg0) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildMarkdownText' to revalidate.
+inspectMarkdownText :: MarkdownText -> MarkdownTextFields
+inspectMarkdownText (CheckedMarkdownText fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked MarkdownText.
+buildMarkdownText :: MarkdownTextFields -> Result MarkdownText
+buildMarkdownText fields = do
+  checkExtensions ["text", "verbatim"] (markdownTextExtensions fields)
+  validateFields "MarkdownText" (wireMarkdownText fields)
+  pure (CheckedMarkdownText fields)
+
+wireMarkdownText :: MarkdownTextFields -> Value
+wireMarkdownText fields =
+  object $
+    ["type" .= ("mrkdwn" :: Text)] <> ["text" .= (markdownTextText fields)] <> optional "verbatim" (markdownTextVerbatim fields) <> extensionPairs (markdownTextExtensions fields)
+
+instance ToJSON MarkdownText where toJSON (CheckedMarkdownText fields) = wireMarkdownText fields
+
+instance FromSlackJSON MarkdownText where
+  fromSlackJSON value = do
+    fields <- wireObject "mrkdwn" value
+    parsedText <- required "text" fields
+    parsedVerbatim <- optionalField "verbatim" fields
+    buildMarkdownText (MarkdownTextFields parsedText parsedVerbatim (remaining ["text", "verbatim"] fields))
+
+instance FromJSON MarkdownText where parseJSON = checkedParser
+
+-- | A selectable option for menus, checkboxes, radio buttons, and overflow menus.
+--
+-- <https://docs.slack.dev/reference/block-kit/composition-objects/option-object>
+newtype SelectOption = CheckedSelectOption SelectOptionFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildSelectOption' before encoding.
+data SelectOptionFields = SelectOptionFields
+  { -- | Sets the option label shown to users. Required. Shared limit: @option.text@.
+    selectOptionText :: TextObject,
+    -- | Sets the value sent in interaction payloads when this option is chosen. Required. Shared limit: @option.value@.
+    selectOptionValue :: Text,
+    -- | Sets supporting text shown below the label. Only rendered by checkboxes and radio buttons. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @option.description@.
+    selectOptionDescription :: Maybe (TextObject),
+    -- | Sets a URL loaded in the user's browser when an overflow menu option is chosen. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @option.url@.
+    selectOptionUrl :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    selectOptionExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for SelectOption; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildSelectOption' to validate before JSON encoding.
+selectOption :: TextObject -> Text -> SelectOptionFields
+selectOption arg0 arg1 = SelectOptionFields (arg0) (arg1) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildSelectOption' to revalidate.
+inspectSelectOption :: SelectOption -> SelectOptionFields
+inspectSelectOption (CheckedSelectOption fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked SelectOption.
+buildSelectOption :: SelectOptionFields -> Result SelectOption
+buildSelectOption fields = do
+  checkExtensions ["text", "value", "description", "url"] (selectOptionExtensions fields)
+  validateFields "Option" (wireSelectOption fields)
+  pure (CheckedSelectOption fields)
+
+wireSelectOption :: SelectOptionFields -> Value
+wireSelectOption fields =
+  object $
+    [] <> ["text" .= (selectOptionText fields)] <> ["value" .= (selectOptionValue fields)] <> optional "description" (selectOptionDescription fields) <> optional "url" (selectOptionUrl fields) <> extensionPairs (selectOptionExtensions fields)
+
+instance ToJSON SelectOption where toJSON (CheckedSelectOption fields) = wireSelectOption fields
+
+instance FromSlackJSON SelectOption where
+  fromSlackJSON value = do
+    fields <- wireObject "" value
+    parsedText <- required "text" (coerceField "text" "plain_text" fields)
+    parsedValue <- required "value" fields
+    parsedDescription <- optionalField "description" (coerceField "description" "plain_text" fields)
+    parsedUrl <- optionalField "url" fields
+    buildSelectOption (SelectOptionFields parsedText parsedValue parsedDescription parsedUrl (remaining ["text", "value", "description", "url"] fields))
+
+instance FromJSON SelectOption where parseJSON = checkedParser
+
+-- | A labelled group of options in a static select menu.
+--
+-- <https://docs.slack.dev/reference/block-kit/composition-objects/option-group-object>
+newtype SelectOptionGroup = CheckedSelectOptionGroup SelectOptionGroupFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildSelectOptionGroup' before encoding.
+data SelectOptionGroupFields = SelectOptionGroupFields
+  { -- | Sets the group heading. Required. Shared limit: @option_group.label@.
+    selectOptionGroupLabel :: PlainText,
+    -- | Adds selectable options in display order. Required. Shared limit: @option_group.options@.
+    selectOptionGroupOptions :: [SelectOption],
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    selectOptionGroupExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for SelectOptionGroup; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildSelectOptionGroup' to validate before JSON encoding.
+selectOptionGroup :: PlainText -> [SelectOption] -> SelectOptionGroupFields
+selectOptionGroup arg0 arg1 = SelectOptionGroupFields (arg0) (arg1) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildSelectOptionGroup' to revalidate.
+inspectSelectOptionGroup :: SelectOptionGroup -> SelectOptionGroupFields
+inspectSelectOptionGroup (CheckedSelectOptionGroup fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked SelectOptionGroup.
+buildSelectOptionGroup :: SelectOptionGroupFields -> Result SelectOptionGroup
+buildSelectOptionGroup fields = do
+  checkExtensions ["label", "options"] (selectOptionGroupExtensions fields)
+  validateFields "OptionGroup" (wireSelectOptionGroup fields)
+  pure (CheckedSelectOptionGroup fields)
+
+wireSelectOptionGroup :: SelectOptionGroupFields -> Value
+wireSelectOptionGroup fields =
+  object $
+    [] <> ["label" .= (selectOptionGroupLabel fields)] <> ["options" .= (selectOptionGroupOptions fields)] <> extensionPairs (selectOptionGroupExtensions fields)
+
+instance ToJSON SelectOptionGroup where toJSON (CheckedSelectOptionGroup fields) = wireSelectOptionGroup fields
+
+instance FromSlackJSON SelectOptionGroup where
+  fromSlackJSON value = do
+    fields <- wireObject "" value
+    parsedLabel <- required "label" (coerceField "label" "plain_text" fields)
+    parsedOptions <- required "options" fields
+    buildSelectOptionGroup (SelectOptionGroupFields parsedLabel parsedOptions (remaining ["label", "options"] fields))
+
+instance FromJSON SelectOptionGroup where parseJSON = checkedParser
+
+-- | A pie chart for a data visualization block.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block>
+newtype PieChart = CheckedPieChart PieChartFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildPieChart' before encoding.
+data PieChartFields = PieChartFields
+  { -- | Adds pie segments in display order. Required. Shared limit: @data_visualization.segments@.
+    pieChartSegments :: [ChartSegment],
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    pieChartExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for PieChart; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildPieChart' to validate before JSON encoding.
+pieChart :: [ChartSegment] -> PieChartFields
+pieChart arg0 = PieChartFields (arg0) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildPieChart' to revalidate.
+inspectPieChart :: PieChart -> PieChartFields
+inspectPieChart (CheckedPieChart fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked PieChart.
+buildPieChart :: PieChartFields -> Result PieChart
+buildPieChart fields = do
+  checkExtensions ["segments"] (pieChartExtensions fields)
+  validateFields "PieChart" (wirePieChart fields)
+  pure (CheckedPieChart fields)
+
+wirePieChart :: PieChartFields -> Value
+wirePieChart fields =
+  object $
+    ["type" .= ("pie" :: Text)] <> ["segments" .= (pieChartSegments fields)] <> extensionPairs (pieChartExtensions fields)
+
+instance ToJSON PieChart where toJSON (CheckedPieChart fields) = wirePieChart fields
+
+instance FromSlackJSON PieChart where
+  fromSlackJSON value = do
+    fields <- wireObject "pie" value
+    parsedSegments <- required "segments" fields
+    buildPieChart (PieChartFields parsedSegments (remaining ["segments"] fields))
+
+instance FromJSON PieChart where parseJSON = checkedParser
+
+-- | A plain-text composition object.
+--
+-- <https://docs.slack.dev/reference/block-kit/composition-objects/text-object>
+newtype PlainText = CheckedPlainText PlainTextFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildPlainText' before encoding.
+data PlainTextFields = PlainTextFields
+  { -- | Sets the text content. Required.
+    plainTextText :: Text,
+    -- | Sets whether emoji shortcodes such as :tada: are rendered as emoji. Optional; Nothing omits the field. Factory default: @Nothing@.
+    plainTextEmoji :: Maybe (Bool),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    plainTextExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for PlainText; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildPlainText' to validate before JSON encoding.
+plainText :: Text -> PlainTextFields
+plainText arg0 = PlainTextFields (arg0) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildPlainText' to revalidate.
+inspectPlainText :: PlainText -> PlainTextFields
+inspectPlainText (CheckedPlainText fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked PlainText.
+buildPlainText :: PlainTextFields -> Result PlainText
+buildPlainText fields = do
+  checkExtensions ["text", "emoji"] (plainTextExtensions fields)
+  validateFields "PlainText" (wirePlainText fields)
+  pure (CheckedPlainText fields)
+
+wirePlainText :: PlainTextFields -> Value
+wirePlainText fields =
+  object $
+    ["type" .= ("plain_text" :: Text)] <> ["text" .= (plainTextText fields)] <> optional "emoji" (plainTextEmoji fields) <> extensionPairs (plainTextExtensions fields)
+
+instance ToJSON PlainText where toJSON (CheckedPlainText fields) = wirePlainText fields
+
+instance FromSlackJSON PlainText where
+  fromSlackJSON value = do
+    fields <- wireObject "plain_text" value
+    parsedText <- required "text" fields
+    parsedEmoji <- optionalField "emoji" fields
+    buildPlainText (PlainTextFields parsedText parsedEmoji (remaining ["text", "emoji"] fields))
+
+instance FromJSON PlainText where parseJSON = checkedParser
+
+-- | A numeric data table cell with display text.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/data-table-block>
+newtype RawNumber = CheckedRawNumber RawNumberFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildRawNumber' before encoding.
+data RawNumberFields = RawNumberFields
+  { -- | Sets the numeric value used for sorting. Required.
+    rawNumberValue :: Scientific,
+    -- | Sets the formatted text displayed for the number, such as 1,234.5. Required.
+    rawNumberText :: Text,
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    rawNumberExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for RawNumber; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildRawNumber' to validate before JSON encoding.
+rawNumber :: Scientific -> Text -> RawNumberFields
+rawNumber arg0 arg1 = RawNumberFields (arg0) (arg1) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildRawNumber' to revalidate.
+inspectRawNumber :: RawNumber -> RawNumberFields
+inspectRawNumber (CheckedRawNumber fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked RawNumber.
+buildRawNumber :: RawNumberFields -> Result RawNumber
+buildRawNumber fields = do
+  checkExtensions ["value", "text"] (rawNumberExtensions fields)
+  validateFields "RawNumber" (wireRawNumber fields)
+  pure (CheckedRawNumber fields)
+
+wireRawNumber :: RawNumberFields -> Value
+wireRawNumber fields =
+  object $
+    ["type" .= ("raw_number" :: Text)] <> ["value" .= (rawNumberValue fields)] <> ["text" .= (rawNumberText fields)] <> extensionPairs (rawNumberExtensions fields)
+
+instance ToJSON RawNumber where toJSON (CheckedRawNumber fields) = wireRawNumber fields
+
+instance FromSlackJSON RawNumber where
+  fromSlackJSON value = do
+    fields <- wireObject "raw_number" value
+    parsedValue <- required "value" fields
+    parsedText <- required "text" fields
+    buildRawNumber (RawNumberFields parsedValue parsedText (remaining ["value", "text"] fields))
+
+instance FromJSON RawNumber where parseJSON = checkedParser
+
+-- | An unformatted table cell.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/table-block>
+newtype RawText = CheckedRawText RawTextFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildRawText' before encoding.
+data RawTextFields = RawTextFields
+  { -- | Sets the cell text. Required.
+    rawTextText :: Text,
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    rawTextExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for RawText; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildRawText' to validate before JSON encoding.
+rawText :: Text -> RawTextFields
+rawText arg0 = RawTextFields (arg0) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildRawText' to revalidate.
+inspectRawText :: RawText -> RawTextFields
+inspectRawText (CheckedRawText fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked RawText.
+buildRawText :: RawTextFields -> Result RawText
+buildRawText fields = do
+  checkExtensions ["text"] (rawTextExtensions fields)
+  validateFields "RawText" (wireRawText fields)
+  pure (CheckedRawText fields)
+
+wireRawText :: RawTextFields -> Value
+wireRawText fields =
+  object $
+    ["type" .= ("raw_text" :: Text)] <> ["text" .= (rawTextText fields)] <> extensionPairs (rawTextExtensions fields)
+
+instance ToJSON RawText where toJSON (CheckedRawText fields) = wireRawText fields
+
+instance FromSlackJSON RawText where
+  fromSlackJSON value = do
+    fields <- wireObject "raw_text" value
+    parsedText <- required "text" fields
+    buildRawText (RawTextFields parsedText (remaining ["text"] fields))
+
+instance FromJSON RawText where parseJSON = checkedParser
+
+-- | A channel mention inside rich text.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/rich-text-block>
+newtype RichTextChannel = CheckedRichTextChannel RichTextChannelFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildRichTextChannel' before encoding.
+data RichTextChannelFields = RichTextChannelFields
+  { -- | Sets the ID of the mentioned channel. Required.
+    richTextChannelChannelId :: Text,
+    -- | Sets the mention style. Optional; Nothing omits the field. Factory default: @Nothing@.
+    richTextChannelStyle :: Maybe (RichTextStyle),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    richTextChannelExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for RichTextChannel; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildRichTextChannel' to validate before JSON encoding.
+richTextChannel :: Text -> RichTextChannelFields
+richTextChannel arg0 = RichTextChannelFields (arg0) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildRichTextChannel' to revalidate.
+inspectRichTextChannel :: RichTextChannel -> RichTextChannelFields
+inspectRichTextChannel (CheckedRichTextChannel fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked RichTextChannel.
+buildRichTextChannel :: RichTextChannelFields -> Result RichTextChannel
+buildRichTextChannel fields = do
+  checkExtensions ["channel_id", "style"] (richTextChannelExtensions fields)
+  validateFields "RichTextChannel" (wireRichTextChannel fields)
+  pure (CheckedRichTextChannel fields)
+
+wireRichTextChannel :: RichTextChannelFields -> Value
+wireRichTextChannel fields =
+  object $
+    ["type" .= ("channel" :: Text)] <> ["channel_id" .= (richTextChannelChannelId fields)] <> optional "style" (richTextChannelStyle fields) <> extensionPairs (richTextChannelExtensions fields)
+
+instance ToJSON RichTextChannel where toJSON (CheckedRichTextChannel fields) = wireRichTextChannel fields
+
+instance FromSlackJSON RichTextChannel where
+  fromSlackJSON value = do
+    fields <- wireObject "channel" value
+    parsedChannelId <- required "channel_id" fields
+    parsedStyle <- optionalField "style" fields
+    buildRichTextChannel (RichTextChannelFields parsedChannelId parsedStyle (remaining ["channel_id", "style"] fields))
+
+instance FromJSON RichTextChannel where parseJSON = checkedParser
+
+-- | A preformatted code block inside rich text.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/rich-text-block>
+newtype RichTextCodeBlock = CheckedRichTextCodeBlock RichTextCodeBlockFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildRichTextCodeBlock' before encoding.
+data RichTextCodeBlockFields = RichTextCodeBlockFields
+  { -- | Adds inline rich text elements in display order. Required.
+    richTextCodeBlockElements :: [RichTextSectionElement],
+    -- | Sets the width of the left border, in pixels. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @rich_text_preformatted.border@.
+    richTextCodeBlockBorder :: Maybe (Integer),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    richTextCodeBlockExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for RichTextCodeBlock; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildRichTextCodeBlock' to validate before JSON encoding.
+richTextCodeBlock :: [RichTextSectionElement] -> RichTextCodeBlockFields
+richTextCodeBlock arg0 = RichTextCodeBlockFields (arg0) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildRichTextCodeBlock' to revalidate.
+inspectRichTextCodeBlock :: RichTextCodeBlock -> RichTextCodeBlockFields
+inspectRichTextCodeBlock (CheckedRichTextCodeBlock fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked RichTextCodeBlock.
+buildRichTextCodeBlock :: RichTextCodeBlockFields -> Result RichTextCodeBlock
+buildRichTextCodeBlock fields = do
+  checkExtensions ["elements", "border"] (richTextCodeBlockExtensions fields)
+  validateFields "RichTextCodeBlock" (wireRichTextCodeBlock fields)
+  pure (CheckedRichTextCodeBlock fields)
+
+wireRichTextCodeBlock :: RichTextCodeBlockFields -> Value
+wireRichTextCodeBlock fields =
+  object $
+    ["type" .= ("rich_text_preformatted" :: Text)] <> ["elements" .= (richTextCodeBlockElements fields)] <> optional "border" (richTextCodeBlockBorder fields) <> extensionPairs (richTextCodeBlockExtensions fields)
+
+instance ToJSON RichTextCodeBlock where toJSON (CheckedRichTextCodeBlock fields) = wireRichTextCodeBlock fields
+
+instance FromSlackJSON RichTextCodeBlock where
+  fromSlackJSON value = do
+    fields <- wireObject "rich_text_preformatted" value
+    checkIntegerBounds "RichTextCodeBlock" (Object fields)
+    parsedElements <- required "elements" fields
+    parsedBorder <- optionalField "border" fields
+    buildRichTextCodeBlock (RichTextCodeBlockFields parsedElements parsedBorder (remaining ["elements", "border"] fields))
+
+instance FromJSON RichTextCodeBlock where parseJSON = checkedParser
+
+-- | An emoji inside rich text.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/rich-text-block>
+newtype RichTextEmoji = CheckedRichTextEmoji RichTextEmojiFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildRichTextEmoji' before encoding.
+data RichTextEmojiFields = RichTextEmojiFields
+  { -- | Sets the emoji shortcode name, without colons. Required.
+    richTextEmojiName :: Text,
+    -- | Sets the emoji skin tone, from 1 to 6. Optional; Nothing omits the field. Factory default: @Nothing@.
+    richTextEmojiSkinTone :: Maybe (Integer),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    richTextEmojiExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for RichTextEmoji; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildRichTextEmoji' to validate before JSON encoding.
+richTextEmoji :: Text -> RichTextEmojiFields
+richTextEmoji arg0 = RichTextEmojiFields (arg0) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildRichTextEmoji' to revalidate.
+inspectRichTextEmoji :: RichTextEmoji -> RichTextEmojiFields
+inspectRichTextEmoji (CheckedRichTextEmoji fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked RichTextEmoji.
+buildRichTextEmoji :: RichTextEmojiFields -> Result RichTextEmoji
+buildRichTextEmoji fields = do
+  checkExtensions ["name", "skin_tone"] (richTextEmojiExtensions fields)
+  validateFields "RichTextEmoji" (wireRichTextEmoji fields)
+  pure (CheckedRichTextEmoji fields)
+
+wireRichTextEmoji :: RichTextEmojiFields -> Value
+wireRichTextEmoji fields =
+  object $
+    ["type" .= ("emoji" :: Text)] <> ["name" .= (richTextEmojiName fields)] <> optional "skin_tone" (richTextEmojiSkinTone fields) <> extensionPairs (richTextEmojiExtensions fields)
+
+instance ToJSON RichTextEmoji where toJSON (CheckedRichTextEmoji fields) = wireRichTextEmoji fields
+
+instance FromSlackJSON RichTextEmoji where
+  fromSlackJSON value = do
+    fields <- wireObject "emoji" value
+    parsedName <- required "name" fields
+    parsedSkinTone <- optionalField "skin_tone" fields
+    buildRichTextEmoji (RichTextEmojiFields parsedName parsedSkinTone (remaining ["name", "skin_tone"] fields))
+
+instance FromJSON RichTextEmoji where parseJSON = checkedParser
+
+-- | A hyperlink inside rich text.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/rich-text-block>
+newtype RichTextLink = CheckedRichTextLink RichTextLinkFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildRichTextLink' before encoding.
+data RichTextLinkFields = RichTextLinkFields
+  { -- | Sets the link target. Required.
+    richTextLinkUrl :: Text,
+    -- | Sets the visible link text. Slack shows the URL when omitted. Optional; Nothing omits the field. Factory default: @Nothing@.
+    richTextLinkText :: Maybe (Text),
+    -- | Sets the visual style. Optional; Nothing omits the field. Factory default: @Nothing@.
+    richTextLinkStyle :: Maybe (RichTextStyle),
+    -- | Sets whether Slack treats the link as unsafe. Optional; Nothing omits the field. Factory default: @Nothing@.
+    richTextLinkUnsafe :: Maybe (Bool),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    richTextLinkExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for RichTextLink; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildRichTextLink' to validate before JSON encoding.
+richTextLink :: Text -> RichTextLinkFields
+richTextLink arg0 = RichTextLinkFields (arg0) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildRichTextLink' to revalidate.
+inspectRichTextLink :: RichTextLink -> RichTextLinkFields
+inspectRichTextLink (CheckedRichTextLink fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked RichTextLink.
+buildRichTextLink :: RichTextLinkFields -> Result RichTextLink
+buildRichTextLink fields = do
+  checkExtensions ["url", "text", "style", "unsafe"] (richTextLinkExtensions fields)
+  validateFields "RichTextLink" (wireRichTextLink fields)
+  pure (CheckedRichTextLink fields)
+
+wireRichTextLink :: RichTextLinkFields -> Value
+wireRichTextLink fields =
+  object $
+    ["type" .= ("link" :: Text)] <> ["url" .= (richTextLinkUrl fields)] <> optional "text" (richTextLinkText fields) <> optional "style" (richTextLinkStyle fields) <> optional "unsafe" (richTextLinkUnsafe fields) <> extensionPairs (richTextLinkExtensions fields)
+
+instance ToJSON RichTextLink where toJSON (CheckedRichTextLink fields) = wireRichTextLink fields
+
+instance FromSlackJSON RichTextLink where
+  fromSlackJSON value = do
+    fields <- wireObject "link" value
+    parsedUrl <- required "url" fields
+    parsedText <- optionalField "text" fields
+    parsedStyle <- optionalField "style" fields
+    parsedUnsafe <- optionalField "unsafe" fields
+    buildRichTextLink (RichTextLinkFields parsedUrl parsedText parsedStyle parsedUnsafe (remaining ["url", "text", "style", "unsafe"] fields))
+
+instance FromJSON RichTextLink where parseJSON = checkedParser
+
+-- | A bulleted or numbered list inside rich text.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/rich-text-block>
+newtype RichTextList = CheckedRichTextList RichTextListFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildRichTextList' before encoding.
+data RichTextListFields = RichTextListFields
+  { -- | Sets whether the list is bulleted or numbered. Required.
+    richTextListStyle :: RichTextListStyle,
+    -- | Adds list items, one rich text section per item. Required.
+    richTextListElements :: [RichTextSection],
+    -- | Sets the list's indentation level. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @rich_text_list.indent@.
+    richTextListIndent :: Maybe (Integer),
+    -- | Sets the number of items to skip when numbering an ordered list. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @rich_text_list.offset@.
+    richTextListOffset :: Maybe (Integer),
+    -- | Sets the width of the left border, in pixels. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @rich_text_list.border@.
+    richTextListBorder :: Maybe (Integer),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    richTextListExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for RichTextList; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildRichTextList' to validate before JSON encoding.
+richTextList :: RichTextListStyle -> [RichTextSection] -> RichTextListFields
+richTextList arg0 arg1 = RichTextListFields (arg0) (arg1) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildRichTextList' to revalidate.
+inspectRichTextList :: RichTextList -> RichTextListFields
+inspectRichTextList (CheckedRichTextList fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked RichTextList.
+buildRichTextList :: RichTextListFields -> Result RichTextList
+buildRichTextList fields = do
+  checkExtensions ["style", "elements", "indent", "offset", "border"] (richTextListExtensions fields)
+  validateFields "RichTextList" (wireRichTextList fields)
+  pure (CheckedRichTextList fields)
+
+wireRichTextList :: RichTextListFields -> Value
+wireRichTextList fields =
+  object $
+    ["type" .= ("rich_text_list" :: Text)] <> ["style" .= (richTextListStyle fields)] <> ["elements" .= (richTextListElements fields)] <> optional "indent" (richTextListIndent fields) <> optional "offset" (richTextListOffset fields) <> optional "border" (richTextListBorder fields) <> extensionPairs (richTextListExtensions fields)
+
+instance ToJSON RichTextList where toJSON (CheckedRichTextList fields) = wireRichTextList fields
+
+instance FromSlackJSON RichTextList where
+  fromSlackJSON value = do
+    fields <- wireObject "rich_text_list" value
+    checkIntegerBounds "RichTextList" (Object fields)
+    parsedStyle <- required "style" fields
+    parsedElements <- required "elements" fields
+    parsedIndent <- optionalField "indent" fields
+    parsedOffset <- optionalField "offset" fields
+    parsedBorder <- optionalField "border" fields
+    buildRichTextList (RichTextListFields parsedStyle parsedElements parsedIndent parsedOffset parsedBorder (remaining ["style", "elements", "indent", "offset", "border"] fields))
+
+instance FromJSON RichTextList where parseJSON = checkedParser
+
+-- | A quotation inside rich text.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/rich-text-block>
+newtype RichTextQuote = CheckedRichTextQuote RichTextQuoteFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildRichTextQuote' before encoding.
+data RichTextQuoteFields = RichTextQuoteFields
+  { -- | Adds inline rich text elements in display order. Required.
+    richTextQuoteElements :: [RichTextSectionElement],
+    -- | Sets the width of the left border, in pixels. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @rich_text_quote.border@.
+    richTextQuoteBorder :: Maybe (Integer),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    richTextQuoteExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for RichTextQuote; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildRichTextQuote' to validate before JSON encoding.
+richTextQuote :: [RichTextSectionElement] -> RichTextQuoteFields
+richTextQuote arg0 = RichTextQuoteFields (arg0) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildRichTextQuote' to revalidate.
+inspectRichTextQuote :: RichTextQuote -> RichTextQuoteFields
+inspectRichTextQuote (CheckedRichTextQuote fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked RichTextQuote.
+buildRichTextQuote :: RichTextQuoteFields -> Result RichTextQuote
+buildRichTextQuote fields = do
+  checkExtensions ["elements", "border"] (richTextQuoteExtensions fields)
+  validateFields "RichTextQuote" (wireRichTextQuote fields)
+  pure (CheckedRichTextQuote fields)
+
+wireRichTextQuote :: RichTextQuoteFields -> Value
+wireRichTextQuote fields =
+  object $
+    ["type" .= ("rich_text_quote" :: Text)] <> ["elements" .= (richTextQuoteElements fields)] <> optional "border" (richTextQuoteBorder fields) <> extensionPairs (richTextQuoteExtensions fields)
+
+instance ToJSON RichTextQuote where toJSON (CheckedRichTextQuote fields) = wireRichTextQuote fields
+
+instance FromSlackJSON RichTextQuote where
+  fromSlackJSON value = do
+    fields <- wireObject "rich_text_quote" value
+    checkIntegerBounds "RichTextQuote" (Object fields)
+    parsedElements <- required "elements" fields
+    parsedBorder <- optionalField "border" fields
+    buildRichTextQuote (RichTextQuoteFields parsedElements parsedBorder (remaining ["elements", "border"] fields))
+
+instance FromJSON RichTextQuote where parseJSON = checkedParser
+
+-- | A paragraph of inline rich text elements.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/rich-text-block>
+newtype RichTextSection = CheckedRichTextSection RichTextSectionFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildRichTextSection' before encoding.
+data RichTextSectionFields = RichTextSectionFields
+  { -- | Adds inline rich text elements in display order. Required.
+    richTextSectionElements :: [RichTextSectionElement],
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    richTextSectionExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for RichTextSection; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildRichTextSection' to validate before JSON encoding.
+richTextSection :: [RichTextSectionElement] -> RichTextSectionFields
+richTextSection arg0 = RichTextSectionFields (arg0) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildRichTextSection' to revalidate.
+inspectRichTextSection :: RichTextSection -> RichTextSectionFields
+inspectRichTextSection (CheckedRichTextSection fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked RichTextSection.
+buildRichTextSection :: RichTextSectionFields -> Result RichTextSection
+buildRichTextSection fields = do
+  checkExtensions ["elements"] (richTextSectionExtensions fields)
+  validateFields "RichTextSection" (wireRichTextSection fields)
+  pure (CheckedRichTextSection fields)
+
+wireRichTextSection :: RichTextSectionFields -> Value
+wireRichTextSection fields =
+  object $
+    ["type" .= ("rich_text_section" :: Text)] <> ["elements" .= (richTextSectionElements fields)] <> extensionPairs (richTextSectionExtensions fields)
+
+instance ToJSON RichTextSection where toJSON (CheckedRichTextSection fields) = wireRichTextSection fields
+
+instance FromSlackJSON RichTextSection where
+  fromSlackJSON value = do
+    fields <- wireObject "rich_text_section" value
+    parsedElements <- required "elements" fields
+    buildRichTextSection (RichTextSectionFields parsedElements (remaining ["elements"] fields))
+
+instance FromJSON RichTextSection where parseJSON = checkedParser
+
+-- | A run of optionally styled text inside rich text.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/rich-text-block>
+newtype RichTextText = CheckedRichTextText RichTextTextFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildRichTextText' before encoding.
+data RichTextTextFields = RichTextTextFields
+  { -- | Sets the text content. Required.
+    richTextTextText :: Text,
+    -- | Sets the visual style. Optional; Nothing omits the field. Factory default: @Nothing@.
+    richTextTextStyle :: Maybe (RichTextStyle),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    richTextTextExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for RichTextText; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildRichTextText' to validate before JSON encoding.
+richTextText :: Text -> RichTextTextFields
+richTextText arg0 = RichTextTextFields (arg0) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildRichTextText' to revalidate.
+inspectRichTextText :: RichTextText -> RichTextTextFields
+inspectRichTextText (CheckedRichTextText fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked RichTextText.
+buildRichTextText :: RichTextTextFields -> Result RichTextText
+buildRichTextText fields = do
+  checkExtensions ["text", "style"] (richTextTextExtensions fields)
+  validateFields "RichTextText" (wireRichTextText fields)
+  pure (CheckedRichTextText fields)
+
+wireRichTextText :: RichTextTextFields -> Value
+wireRichTextText fields =
+  object $
+    ["type" .= ("text" :: Text)] <> ["text" .= (richTextTextText fields)] <> optional "style" (richTextTextStyle fields) <> extensionPairs (richTextTextExtensions fields)
+
+instance ToJSON RichTextText where toJSON (CheckedRichTextText fields) = wireRichTextText fields
+
+instance FromSlackJSON RichTextText where
+  fromSlackJSON value = do
+    fields <- wireObject "text" value
+    parsedText <- required "text" fields
+    parsedStyle <- optionalField "style" fields
+    buildRichTextText (RichTextTextFields parsedText parsedStyle (remaining ["text", "style"] fields))
+
+instance FromJSON RichTextText where parseJSON = checkedParser
+
+-- | A user mention inside rich text.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/rich-text-block>
+newtype RichTextUser = CheckedRichTextUser RichTextUserFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildRichTextUser' before encoding.
+data RichTextUserFields = RichTextUserFields
+  { -- | Sets the ID of the mentioned user. Required.
+    richTextUserUserId :: Text,
+    -- | Sets the visual style. Optional; Nothing omits the field. Factory default: @Nothing@.
+    richTextUserStyle :: Maybe (RichTextStyle),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    richTextUserExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for RichTextUser; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildRichTextUser' to validate before JSON encoding.
+richTextUser :: Text -> RichTextUserFields
+richTextUser arg0 = RichTextUserFields (arg0) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildRichTextUser' to revalidate.
+inspectRichTextUser :: RichTextUser -> RichTextUserFields
+inspectRichTextUser (CheckedRichTextUser fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked RichTextUser.
+buildRichTextUser :: RichTextUserFields -> Result RichTextUser
+buildRichTextUser fields = do
+  checkExtensions ["user_id", "style"] (richTextUserExtensions fields)
+  validateFields "RichTextUser" (wireRichTextUser fields)
+  pure (CheckedRichTextUser fields)
+
+wireRichTextUser :: RichTextUserFields -> Value
+wireRichTextUser fields =
+  object $
+    ["type" .= ("user" :: Text)] <> ["user_id" .= (richTextUserUserId fields)] <> optional "style" (richTextUserStyle fields) <> extensionPairs (richTextUserExtensions fields)
+
+instance ToJSON RichTextUser where toJSON (CheckedRichTextUser fields) = wireRichTextUser fields
+
+instance FromSlackJSON RichTextUser where
+  fromSlackJSON value = do
+    fields <- wireObject "user" value
+    parsedUserId <- required "user_id" fields
+    parsedStyle <- optionalField "style" fields
+    buildRichTextUser (RichTextUserFields parsedUserId parsedStyle (remaining ["user_id", "style"] fields))
+
+instance FromJSON RichTextUser where parseJSON = checkedParser
+
+-- | A user group mention inside rich text.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/rich-text-block>
+newtype RichTextUserGroup = CheckedRichTextUserGroup RichTextUserGroupFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildRichTextUserGroup' before encoding.
+data RichTextUserGroupFields = RichTextUserGroupFields
+  { -- | Sets the ID of the mentioned user group. Required.
+    richTextUserGroupUserGroupId :: Text,
+    -- | Sets the visual style. Optional; Nothing omits the field. Factory default: @Nothing@.
+    richTextUserGroupStyle :: Maybe (RichTextStyle),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    richTextUserGroupExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for RichTextUserGroup; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildRichTextUserGroup' to validate before JSON encoding.
+richTextUserGroup :: Text -> RichTextUserGroupFields
+richTextUserGroup arg0 = RichTextUserGroupFields (arg0) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildRichTextUserGroup' to revalidate.
+inspectRichTextUserGroup :: RichTextUserGroup -> RichTextUserGroupFields
+inspectRichTextUserGroup (CheckedRichTextUserGroup fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked RichTextUserGroup.
+buildRichTextUserGroup :: RichTextUserGroupFields -> Result RichTextUserGroup
+buildRichTextUserGroup fields = do
+  checkExtensions ["usergroup_id", "style"] (richTextUserGroupExtensions fields)
+  validateFields "RichTextUserGroup" (wireRichTextUserGroup fields)
+  pure (CheckedRichTextUserGroup fields)
+
+wireRichTextUserGroup :: RichTextUserGroupFields -> Value
+wireRichTextUserGroup fields =
+  object $
+    ["type" .= ("usergroup" :: Text)] <> ["usergroup_id" .= (richTextUserGroupUserGroupId fields)] <> optional "style" (richTextUserGroupStyle fields) <> extensionPairs (richTextUserGroupExtensions fields)
+
+instance ToJSON RichTextUserGroup where toJSON (CheckedRichTextUserGroup fields) = wireRichTextUserGroup fields
+
+instance FromSlackJSON RichTextUserGroup where
+  fromSlackJSON value = do
+    fields <- wireObject "usergroup" value
+    parsedUserGroupId <- required "usergroup_id" fields
+    parsedStyle <- optionalField "style" fields
+    buildRichTextUserGroup (RichTextUserGroupFields parsedUserGroupId parsedStyle (remaining ["usergroup_id", "style"] fields))
+
+instance FromJSON RichTextUserGroup where parseJSON = checkedParser
+
+-- | A reference to an image file hosted in Slack.
+--
+-- <https://docs.slack.dev/reference/block-kit/composition-objects/slack-file-object>
+-- Provide exactly one of an ID or a URL.
+-- An ID must match ^F[A-Z0-9]{8,}$.
+newtype SlackFile = CheckedSlackFile SlackFileFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildSlackFile' before encoding.
+data SlackFileFields = SlackFileFields
+  { -- | Sets the ID of a file previously uploaded to Slack. Cannot be combined with a URL. Optional; Nothing omits the field. Factory default: @Nothing@.
+    slackFileId :: Maybe (Text),
+    -- | Sets the URL of a file hosted in Slack. Cannot be combined with an ID. Optional; Nothing omits the field. Factory default: @Nothing@.
+    slackFileUrl :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    slackFileExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for SlackFile; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildSlackFile' to validate before JSON encoding.
+slackFile :: SlackFileFields
+slackFile = SlackFileFields (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildSlackFile' to revalidate.
+inspectSlackFile :: SlackFile -> SlackFileFields
+inspectSlackFile (CheckedSlackFile fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked SlackFile.
+buildSlackFile :: SlackFileFields -> Result SlackFile
+buildSlackFile fields = do
+  checkExtensions ["id", "url"] (slackFileExtensions fields)
+  validateFields "SlackFile" (wireSlackFile fields)
+  pure (CheckedSlackFile fields)
+
+wireSlackFile :: SlackFileFields -> Value
+wireSlackFile fields =
+  object $
+    [] <> optional "id" (slackFileId fields) <> optional "url" (slackFileUrl fields) <> extensionPairs (slackFileExtensions fields)
+
+instance ToJSON SlackFile where toJSON (CheckedSlackFile fields) = wireSlackFile fields
+
+instance FromSlackJSON SlackFile where
+  fromSlackJSON value = do
+    fields <- wireObject "" value
+    parsedId <- optionalField "id" fields
+    parsedUrl <- optionalField "url" fields
+    buildSlackFile (SlackFileFields parsedId parsedUrl (remaining ["id", "url"] fields))
+
+instance FromJSON SlackFile where parseJSON = checkedParser
+
+-- | A Slack-provided icon, referenced by name.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/card-block>
+newtype SlackIcon = CheckedSlackIcon SlackIconFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildSlackIcon' before encoding.
+data SlackIconFields = SlackIconFields
+  { -- | Sets the name of the Slack-provided icon, such as rocket. Required.
+    slackIconName :: Text,
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    slackIconExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for SlackIcon; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildSlackIcon' to validate before JSON encoding.
+slackIcon :: Text -> SlackIconFields
+slackIcon arg0 = SlackIconFields (arg0) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildSlackIcon' to revalidate.
+inspectSlackIcon :: SlackIcon -> SlackIconFields
+inspectSlackIcon (CheckedSlackIcon fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked SlackIcon.
+buildSlackIcon :: SlackIconFields -> Result SlackIcon
+buildSlackIcon fields = do
+  checkExtensions ["name"] (slackIconExtensions fields)
+  validateFields "SlackIcon" (wireSlackIcon fields)
+  pure (CheckedSlackIcon fields)
+
+wireSlackIcon :: SlackIconFields -> Value
+wireSlackIcon fields =
+  object $
+    ["type" .= ("icon" :: Text)] <> ["name" .= (slackIconName fields)] <> extensionPairs (slackIconExtensions fields)
+
+instance ToJSON SlackIcon where toJSON (CheckedSlackIcon fields) = wireSlackIcon fields
+
+instance FromSlackJSON SlackIcon where
+  fromSlackJSON value = do
+    fields <- wireObject "icon" value
+    parsedName <- required "name" fields
+    buildSlackIcon (SlackIconFields parsedName (remaining ["name"] fields))
+
+instance FromJSON SlackIcon where parseJSON = checkedParser
+
+-- | A link trigger that starts a workflow.
+--
+-- <https://docs.slack.dev/reference/block-kit/composition-objects/trigger-object>
+newtype Trigger = CheckedTrigger TriggerFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildTrigger' before encoding.
+data TriggerFields = TriggerFields
+  { -- | Sets the link trigger URL. Required.
+    triggerUrl :: Text,
+    -- | Adds the input values passed to the workflow's trigger. Optional; Nothing omits the field. Factory default: @Nothing@.
+    triggerCustomizableInputParameters :: Maybe ([InputParameter]),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    triggerExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for Trigger; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildTrigger' to validate before JSON encoding.
+trigger :: Text -> TriggerFields
+trigger arg0 = TriggerFields (arg0) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildTrigger' to revalidate.
+inspectTrigger :: Trigger -> TriggerFields
+inspectTrigger (CheckedTrigger fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked Trigger.
+buildTrigger :: TriggerFields -> Result Trigger
+buildTrigger fields = do
+  checkExtensions ["url", "customizable_input_parameters"] (triggerExtensions fields)
+  validateFields "Trigger" (wireTrigger fields)
+  pure (CheckedTrigger fields)
+
+wireTrigger :: TriggerFields -> Value
+wireTrigger fields =
+  object $
+    [] <> ["url" .= (triggerUrl fields)] <> optional "customizable_input_parameters" (triggerCustomizableInputParameters fields) <> extensionPairs (triggerExtensions fields)
+
+instance ToJSON Trigger where toJSON (CheckedTrigger fields) = wireTrigger fields
+
+instance FromSlackJSON Trigger where
+  fromSlackJSON value = do
+    fields <- wireObject "" value
+    parsedUrl <- required "url" fields
+    parsedCustomizableInputParameters <- optionalField "customizable_input_parameters" fields
+    buildTrigger (TriggerFields parsedUrl parsedCustomizableInputParameters (remaining ["url", "customizable_input_parameters"] fields))
+
+instance FromJSON Trigger where parseJSON = checkedParser
+
+-- | A linked source cited by a task card.
+--
+-- <https://docs.slack.dev/reference/block-kit/blocks/task-card-block>
+newtype UrlSource = CheckedUrlSource UrlSourceFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildUrlSource' before encoding.
+data UrlSourceFields = UrlSourceFields
+  { -- | Sets the source URL. Required.
+    urlSourceUrl :: Text,
+    -- | Sets the source link text. Required.
+    urlSourceText :: Text,
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    urlSourceExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for UrlSource; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildUrlSource' to validate before JSON encoding.
+urlSource :: Text -> Text -> UrlSourceFields
+urlSource arg0 arg1 = UrlSourceFields (arg0) (arg1) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildUrlSource' to revalidate.
+inspectUrlSource :: UrlSource -> UrlSourceFields
+inspectUrlSource (CheckedUrlSource fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked UrlSource.
+buildUrlSource :: UrlSourceFields -> Result UrlSource
+buildUrlSource fields = do
+  checkExtensions ["url", "text"] (urlSourceExtensions fields)
+  validateFields "UrlSource" (wireUrlSource fields)
+  pure (CheckedUrlSource fields)
+
+wireUrlSource :: UrlSourceFields -> Value
+wireUrlSource fields =
+  object $
+    ["type" .= ("url" :: Text)] <> ["url" .= (urlSourceUrl fields)] <> ["text" .= (urlSourceText fields)] <> extensionPairs (urlSourceExtensions fields)
+
+instance ToJSON UrlSource where toJSON (CheckedUrlSource fields) = wireUrlSource fields
+
+instance FromSlackJSON UrlSource where
+  fromSlackJSON value = do
+    fields <- wireObject "url" value
+    parsedUrl <- required "url" fields
+    parsedText <- required "text" fields
+    buildUrlSource (UrlSourceFields parsedUrl parsedText (remaining ["url", "text"] fields))
+
+instance FromJSON UrlSource where parseJSON = checkedParser
+
+-- | The workflow started by a workflow button.
+--
+-- <https://docs.slack.dev/reference/block-kit/composition-objects/workflow-object>
+newtype Workflow = CheckedWorkflow WorkflowFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildWorkflow' before encoding.
+data WorkflowFields = WorkflowFields
+  { -- | Sets the link trigger that starts the workflow. Required.
+    workflowTrigger :: Trigger,
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    workflowExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for Workflow; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildWorkflow' to validate before JSON encoding.
+workflow :: Trigger -> WorkflowFields
+workflow arg0 = WorkflowFields (arg0) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildWorkflow' to revalidate.
+inspectWorkflow :: Workflow -> WorkflowFields
+inspectWorkflow (CheckedWorkflow fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked Workflow.
+buildWorkflow :: WorkflowFields -> Result Workflow
+buildWorkflow fields = do
+  checkExtensions ["trigger"] (workflowExtensions fields)
+  validateFields "Workflow" (wireWorkflow fields)
+  pure (CheckedWorkflow fields)
+
+wireWorkflow :: WorkflowFields -> Value
+wireWorkflow fields =
+  object $
+    [] <> ["trigger" .= (workflowTrigger fields)] <> extensionPairs (workflowExtensions fields)
+
+instance ToJSON Workflow where toJSON (CheckedWorkflow fields) = wireWorkflow fields
+
+instance FromSlackJSON Workflow where
+  fromSlackJSON value = do
+    fields <- wireObject "" value
+    parsedTrigger <- required "trigger" fields
+    buildWorkflow (WorkflowFields parsedTrigger (remaining ["trigger"] fields))
+
+instance FromJSON Workflow where parseJSON = checkedParser
+
+-- | A legacy message attachment containing blocks and an optional color bar.
+--
+-- <https://docs.slack.dev/messaging/formatting-message-text#when-to-use-attachments>
+-- Only blocks supported in messages are accepted.
+newtype Attachment = CheckedAttachment AttachmentFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildAttachment' before encoding.
+data AttachmentFields = AttachmentFields
+  { -- | Adds the attachment's blocks in display order. Required.
+    attachmentBlocks :: [Block],
+    -- | Sets the attachment's left border color: a six-digit hex color or good, warning, or danger. Optional; Nothing omits the field. Factory default: @Nothing@.
+    attachmentColor :: Maybe (Text),
+    -- | Sets plain-text summary shown in clients that cannot display attachments. Optional; Nothing omits the field. Factory default: @Nothing@.
+    attachmentFallback :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    attachmentExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for Attachment; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildAttachment' to validate before JSON encoding.
+attachment :: [Block] -> AttachmentFields
+attachment arg0 = AttachmentFields (arg0) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildAttachment' to revalidate.
+inspectAttachment :: Attachment -> AttachmentFields
+inspectAttachment (CheckedAttachment fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked Attachment.
+buildAttachment :: AttachmentFields -> Result Attachment
+buildAttachment fields = do
+  checkExtensions ["blocks", "color", "fallback"] (attachmentExtensions fields)
+  validateFields "Attachment" (wireAttachment fields)
+  pure (CheckedAttachment fields)
+
+wireAttachment :: AttachmentFields -> Value
+wireAttachment fields =
+  object $
+    [] <> ["blocks" .= (attachmentBlocks fields)] <> optional "color" (attachmentColor fields) <> optional "fallback" (attachmentFallback fields) <> extensionPairs (attachmentExtensions fields)
+
+instance ToJSON Attachment where toJSON (CheckedAttachment fields) = wireAttachment fields
+
+instance FromSlackJSON Attachment where
+  fromSlackJSON value = do
+    fields <- wireObject "" value
+    parsedBlocks <- required "blocks" fields
+    parsedColor <- optionalField "color" fields
+    parsedFallback <- optionalField "fallback" fields
+    buildAttachment (AttachmentFields parsedBlocks parsedColor parsedFallback (remaining ["blocks", "color", "fallback"] fields))
+
+instance FromJSON Attachment where parseJSON = checkedParser
+
+-- | The payload for publishing an App Home tab with views.publish.
+--
+-- <https://docs.slack.dev/surfaces/app-home>
+-- Only blocks supported on the App Home tab are accepted.
+newtype HomeTabView = CheckedHomeTabView HomeTabViewFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildHomeTabView' before encoding.
+data HomeTabViewFields = HomeTabViewFields
+  { -- | Adds the blocks shown on the App Home tab, in display order. Required. Shared limit: @view.blocks@.
+    homeTabViewBlocks :: [Block],
+    -- | Sets application-defined data returned in view payloads. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @view.private_metadata@.
+    homeTabViewPrivateMetadata :: Maybe (Text),
+    -- | Sets an identifier returned in view payloads so your app can recognize the view. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @view.callback_id@.
+    homeTabViewCallbackId :: Maybe (Text),
+    -- | Sets a workspace-unique identifier you can use to update the view later. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @view.external_id@.
+    homeTabViewExternalId :: Maybe (Text),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    homeTabViewExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for HomeTabView; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildHomeTabView' to validate before JSON encoding.
+homeTabView :: [Block] -> HomeTabViewFields
+homeTabView arg0 = HomeTabViewFields (arg0) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildHomeTabView' to revalidate.
+inspectHomeTabView :: HomeTabView -> HomeTabViewFields
+inspectHomeTabView (CheckedHomeTabView fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked HomeTabView.
+buildHomeTabView :: HomeTabViewFields -> Result HomeTabView
+buildHomeTabView fields = do
+  checkExtensions ["blocks", "private_metadata", "callback_id", "external_id"] (homeTabViewExtensions fields)
+  validateFields "HomeTabView" (wireHomeTabView fields)
+  pure (CheckedHomeTabView fields)
+
+wireHomeTabView :: HomeTabViewFields -> Value
+wireHomeTabView fields =
+  object $
+    ["type" .= ("home" :: Text)] <> ["blocks" .= (homeTabViewBlocks fields)] <> optional "private_metadata" (homeTabViewPrivateMetadata fields) <> optional "callback_id" (homeTabViewCallbackId fields) <> optional "external_id" (homeTabViewExternalId fields) <> extensionPairs (homeTabViewExtensions fields)
+
+instance ToJSON HomeTabView where toJSON (CheckedHomeTabView fields) = wireHomeTabView fields
+
+instance FromSlackJSON HomeTabView where
+  fromSlackJSON value = do
+    fields <- wireObject "home" value
+    parsedBlocks <- required "blocks" fields
+    parsedPrivateMetadata <- optionalField "private_metadata" fields
+    parsedCallbackId <- optionalField "callback_id" fields
+    parsedExternalId <- optionalField "external_id" fields
+    buildHomeTabView (HomeTabViewFields parsedBlocks parsedPrivateMetadata parsedCallbackId parsedExternalId (remaining ["blocks", "private_metadata", "callback_id", "external_id"] fields))
+
+instance FromJSON HomeTabView where parseJSON = checkedParser
+
+-- | A complete payload for chat.postMessage and related Web API methods.
+--
+-- <https://docs.slack.dev/reference/methods/chat.postMessage>
+-- Only blocks supported in messages are accepted.
+-- Markdown block text is limited to 12,000 characters, and data table cell text to 20,000 characters, across the whole message.
+-- At most two data visualization blocks may be sent per message, across blocks and attachments after helper expansion (message.data_visualizations.max_items).
+newtype MessagePayload = CheckedMessagePayload MessagePayloadFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildMessagePayload' before encoding.
+data MessagePayloadFields = MessagePayloadFields
+  { -- | Sets the ID of the channel, private group, or conversation that receives the message. Required. Shared limit: @message.channel@.
+    messagePayloadChannel :: Text,
+    -- | Adds the message blocks in display order. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @message.blocks@.
+    messagePayloadBlocks :: Maybe ([Block]),
+    -- | Adds legacy attachments in display order. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @message.attachments@.
+    messagePayloadAttachments :: Maybe ([Attachment]),
+    -- | Sets the fallback text used in notifications and by clients that cannot display blocks. Optional; Nothing omits the field. Factory default: @Just ("")@.
+    messagePayloadText :: Maybe (Text),
+    -- | Sets whether Slack formats the top-level text as mrkdwn. Optional; Nothing omits the field. Factory default: @Just (True)@.
+    messagePayloadMrkdwn :: Maybe (Bool),
+    -- | Sets whether Slack unfurls text-based links. Optional; Nothing omits the field. Factory default: @Nothing@.
+    messagePayloadUnfurlLinks :: Maybe (Bool),
+    -- | Sets whether Slack unfurls media links. Optional; Nothing omits the field. Factory default: @Nothing@.
+    messagePayloadUnfurlMedia :: Maybe (Bool),
+    -- | Sets message metadata as a JSON-compatible map, such as event_type and event_payload. Optional; Nothing omits the field. Factory default: @Nothing@.
+    messagePayloadMetadata :: Maybe (Map Text Value),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    messagePayloadExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for MessagePayload; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildMessagePayload' to validate before JSON encoding.
+messagePayload :: Text -> MessagePayloadFields
+messagePayload arg0 = MessagePayloadFields (arg0) (Nothing) (Nothing) (Just ("")) (Just (True)) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildMessagePayload' to revalidate.
+inspectMessagePayload :: MessagePayload -> MessagePayloadFields
+inspectMessagePayload (CheckedMessagePayload fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked MessagePayload.
+buildMessagePayload :: MessagePayloadFields -> Result MessagePayload
+buildMessagePayload fields = do
+  checkExtensions ["channel", "blocks", "attachments", "text", "mrkdwn", "unfurl_links", "unfurl_media", "metadata"] (messagePayloadExtensions fields)
+  validateFields "MessagePayload" (wireMessagePayload fields)
+  pure (CheckedMessagePayload fields)
+
+wireMessagePayload :: MessagePayloadFields -> Value
+wireMessagePayload fields =
+  object $
+    [] <> ["channel" .= (messagePayloadChannel fields)] <> optional "blocks" (messagePayloadBlocks fields) <> optional "attachments" (messagePayloadAttachments fields) <> optional "text" (messagePayloadText fields) <> optional "mrkdwn" (messagePayloadMrkdwn fields) <> optional "unfurl_links" (messagePayloadUnfurlLinks fields) <> optional "unfurl_media" (messagePayloadUnfurlMedia fields) <> optional "metadata" (messagePayloadMetadata fields) <> extensionPairs (messagePayloadExtensions fields)
+
+instance ToJSON MessagePayload where toJSON (CheckedMessagePayload fields) = wireMessagePayload fields
+
+instance FromSlackJSON MessagePayload where
+  fromSlackJSON value = do
+    fields <- wireObject "" value
+    parsedChannel <- required "channel" fields
+    parsedBlocks <- optionalField "blocks" fields
+    parsedAttachments <- optionalField "attachments" fields
+    parsedText <- optionalField "text" fields
+    parsedMrkdwn <- optionalField "mrkdwn" fields
+    parsedUnfurlLinks <- optionalField "unfurl_links" fields
+    parsedUnfurlMedia <- optionalField "unfurl_media" fields
+    parsedMetadata <- optionalField "metadata" fields
+    buildMessagePayload (MessagePayloadFields parsedChannel parsedBlocks parsedAttachments parsedText parsedMrkdwn parsedUnfurlLinks parsedUnfurlMedia parsedMetadata (remaining ["channel", "blocks", "attachments", "text", "mrkdwn", "unfurl_links", "unfurl_media", "metadata"] fields))
+
+instance FromJSON MessagePayload where parseJSON = checkedParser
+
+-- | A response body for slash commands and interaction response URLs.
+--
+-- <https://docs.slack.dev/interactivity/handling-user-interaction#message_responses>
+-- Only blocks supported in messages are accepted.
+-- Markdown block text is limited to 12,000 characters, and data table cell text to 20,000 characters, across the whole message.
+-- At most two data visualization blocks may be sent per message, across blocks and attachments after helper expansion (message.data_visualizations.max_items).
+newtype MessageResponse = CheckedMessageResponse MessageResponseFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildMessageResponse' before encoding.
+data MessageResponseFields = MessageResponseFields
+  { -- | Adds the response blocks in display order. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @message.blocks@.
+    messageResponseBlocks :: Maybe ([Block]),
+    -- | Adds legacy attachments in display order. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @message.attachments@.
+    messageResponseAttachments :: Maybe ([Attachment]),
+    -- | Sets the fallback text used in notifications and by clients that cannot display blocks. Optional; Nothing omits the field. Factory default: @Just ("")@.
+    messageResponseText :: Maybe (Text),
+    -- | Sets whether Slack formats the top-level text as mrkdwn. Optional; Nothing omits the field. Factory default: @Just (True)@.
+    messageResponseMrkdwn :: Maybe (Bool),
+    -- | Sets whether the response replaces the message that triggered it. Optional; Nothing omits the field. Factory default: @Just (False)@.
+    messageResponseReplaceOriginal :: Maybe (Bool),
+    -- | Sets who can see the response. Optional; Nothing omits the field. Factory default: @Just (ResponseTypeInChannel)@.
+    messageResponseResponseType :: Maybe (ResponseType),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    messageResponseExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for MessageResponse; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildMessageResponse' to validate before JSON encoding.
+messageResponse :: MessageResponseFields
+messageResponse = MessageResponseFields (Nothing) (Nothing) (Just ("")) (Just (True)) (Just (False)) (Just (ResponseTypeInChannel)) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildMessageResponse' to revalidate.
+inspectMessageResponse :: MessageResponse -> MessageResponseFields
+inspectMessageResponse (CheckedMessageResponse fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked MessageResponse.
+buildMessageResponse :: MessageResponseFields -> Result MessageResponse
+buildMessageResponse fields = do
+  checkExtensions ["blocks", "attachments", "text", "mrkdwn", "replace_original", "response_type"] (messageResponseExtensions fields)
+  validateFields "MessageResponse" (wireMessageResponse fields)
+  pure (CheckedMessageResponse fields)
+
+wireMessageResponse :: MessageResponseFields -> Value
+wireMessageResponse fields =
+  object $
+    [] <> optional "blocks" (messageResponseBlocks fields) <> optional "attachments" (messageResponseAttachments fields) <> optional "text" (messageResponseText fields) <> optional "mrkdwn" (messageResponseMrkdwn fields) <> optional "replace_original" (messageResponseReplaceOriginal fields) <> optional "response_type" (messageResponseResponseType fields) <> extensionPairs (messageResponseExtensions fields)
+
+instance ToJSON MessageResponse where toJSON (CheckedMessageResponse fields) = wireMessageResponse fields
+
+instance FromSlackJSON MessageResponse where
+  fromSlackJSON value = do
+    fields <- wireObject "" value
+    parsedBlocks <- optionalField "blocks" fields
+    parsedAttachments <- optionalField "attachments" fields
+    parsedText <- optionalField "text" fields
+    parsedMrkdwn <- optionalField "mrkdwn" fields
+    parsedReplaceOriginal <- optionalField "replace_original" fields
+    parsedResponseType <- optionalField "response_type" fields
+    buildMessageResponse (MessageResponseFields parsedBlocks parsedAttachments parsedText parsedMrkdwn parsedReplaceOriginal parsedResponseType (remaining ["blocks", "attachments", "text", "mrkdwn", "replace_original", "response_type"] fields))
+
+instance FromJSON MessageResponse where parseJSON = checkedParser
+
+-- | The view payload for views.open, views.push, and views.update.
+--
+-- <https://docs.slack.dev/surfaces/modals>
+-- Only blocks supported in modals are accepted.
+-- A modal that contains an input block must have a submit label.
+newtype ModalView = CheckedModalView ModalViewFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildModalView' before encoding.
+data ModalViewFields = ModalViewFields
+  { -- | Sets the title in the modal's top bar. Required. Shared limit: @view.title@.
+    modalViewTitle :: PlainText,
+    -- | Adds the blocks shown in the modal, in display order. Required. Shared limit: @view.blocks@.
+    modalViewBlocks :: [Block],
+    -- | Sets the label of the button that closes the modal. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @view.close@.
+    modalViewClose :: Maybe (PlainText),
+    -- | Sets the label of the button that submits the modal. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @view.submit@.
+    modalViewSubmit :: Maybe (PlainText),
+    -- | Sets application-defined data returned in view payloads. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @view.private_metadata@.
+    modalViewPrivateMetadata :: Maybe (Text),
+    -- | Sets an identifier returned in view payloads so your app can recognize the view. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @view.callback_id@.
+    modalViewCallbackId :: Maybe (Text),
+    -- | Sets whether closing this modal closes every view in its stack. Optional; Nothing omits the field. Factory default: @Nothing@.
+    modalViewClearOnClose :: Maybe (Bool),
+    -- | Sets whether Slack sends a view_closed event when the user closes the modal. Optional; Nothing omits the field. Factory default: @Nothing@.
+    modalViewNotifyOnClose :: Maybe (Bool),
+    -- | Sets a workspace-unique identifier you can use to update the view later. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @view.external_id@.
+    modalViewExternalId :: Maybe (Text),
+    -- | Sets whether the submit button starts disabled in workflow configuration modals only. Omit this field for ordinary views.open/views.push modals, including when false. The general ModalView value cannot infer its receiving workflow context. Optional; Nothing omits the field. Factory default: @Nothing@.
+    modalViewSubmitDisabled :: Maybe (Bool),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    modalViewExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for ModalView; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildModalView' to validate before JSON encoding.
+modalView :: PlainText -> [Block] -> ModalViewFields
+modalView arg0 arg1 = ModalViewFields (arg0) (arg1) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildModalView' to revalidate.
+inspectModalView :: ModalView -> ModalViewFields
+inspectModalView (CheckedModalView fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked ModalView.
+buildModalView :: ModalViewFields -> Result ModalView
+buildModalView fields = do
+  checkExtensions ["title", "blocks", "close", "submit", "private_metadata", "callback_id", "clear_on_close", "notify_on_close", "external_id", "submit_disabled"] (modalViewExtensions fields)
+  validateFields "ModalView" (wireModalView fields)
+  pure (CheckedModalView fields)
+
+wireModalView :: ModalViewFields -> Value
+wireModalView fields =
+  object $
+    ["type" .= ("modal" :: Text)] <> ["title" .= (modalViewTitle fields)] <> ["blocks" .= (modalViewBlocks fields)] <> optional "close" (modalViewClose fields) <> optional "submit" (modalViewSubmit fields) <> optional "private_metadata" (modalViewPrivateMetadata fields) <> optional "callback_id" (modalViewCallbackId fields) <> optional "clear_on_close" (modalViewClearOnClose fields) <> optional "notify_on_close" (modalViewNotifyOnClose fields) <> optional "external_id" (modalViewExternalId fields) <> optional "submit_disabled" (modalViewSubmitDisabled fields) <> extensionPairs (modalViewExtensions fields)
+
+instance ToJSON ModalView where toJSON (CheckedModalView fields) = wireModalView fields
+
+instance FromSlackJSON ModalView where
+  fromSlackJSON value = do
+    fields <- wireObject "modal" value
+    parsedTitle <- required "title" (coerceField "title" "plain_text" fields)
+    parsedBlocks <- required "blocks" fields
+    parsedClose <- optionalField "close" (coerceField "close" "plain_text" fields)
+    parsedSubmit <- optionalField "submit" (coerceField "submit" "plain_text" fields)
+    parsedPrivateMetadata <- optionalField "private_metadata" fields
+    parsedCallbackId <- optionalField "callback_id" fields
+    parsedClearOnClose <- optionalField "clear_on_close" fields
+    parsedNotifyOnClose <- optionalField "notify_on_close" fields
+    parsedExternalId <- optionalField "external_id" fields
+    parsedSubmitDisabled <- optionalField "submit_disabled" fields
+    buildModalView (ModalViewFields parsedTitle parsedBlocks parsedClose parsedSubmit parsedPrivateMetadata parsedCallbackId parsedClearOnClose parsedNotifyOnClose parsedExternalId parsedSubmitDisabled (remaining ["title", "blocks", "close", "submit", "private_metadata", "callback_id", "clear_on_close", "notify_on_close", "external_id", "submit_disabled"] fields))
+
+instance FromJSON ModalView where parseJSON = checkedParser
+
+-- | A payload for incoming webhooks.
+--
+-- <https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks>
+-- Only blocks supported in messages are accepted.
+-- Markdown block text is limited to 12,000 characters, and data table cell text to 20,000 characters, across the whole message.
+-- At most two data visualization blocks may be sent per message, across blocks and attachments after helper expansion (message.data_visualizations.max_items).
+newtype WebhookMessage = CheckedWebhookMessage WebhookMessageFields deriving (Eq, Show)
+
+-- | Editable construction input. Call 'buildWebhookMessage' before encoding.
+data WebhookMessageFields = WebhookMessageFields
+  { -- | Adds the message blocks in display order. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @message.blocks@.
+    webhookMessageBlocks :: Maybe ([Block]),
+    -- | Adds legacy attachments in display order. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @message.attachments@.
+    webhookMessageAttachments :: Maybe ([Attachment]),
+    -- | Sets the fallback text used in notifications and by clients that cannot display blocks. Optional; Nothing omits the field. Factory default: @Nothing@.
+    webhookMessageText :: Maybe (Text),
+    -- | Sets who can see the response. Optional; Nothing omits the field. Factory default: @Nothing@.
+    webhookMessageResponseType :: Maybe (ResponseType),
+    -- | Sets whether the response replaces the message that triggered it. Optional; Nothing omits the field. Factory default: @Nothing@.
+    webhookMessageReplaceOriginal :: Maybe (Bool),
+    -- | Sets whether the response deletes the message that triggered it. Optional; Nothing omits the field. Factory default: @Nothing@.
+    webhookMessageDeleteOriginal :: Maybe (Bool),
+    -- | Sets whether Slack unfurls text-based links. Optional; Nothing omits the field. Factory default: @Nothing@.
+    webhookMessageUnfurlLinks :: Maybe (Bool),
+    -- | Sets whether Slack unfurls media links. Optional; Nothing omits the field. Factory default: @Nothing@.
+    webhookMessageUnfurlMedia :: Maybe (Bool),
+    -- | Sets message metadata as a JSON-compatible map, such as event_type and event_payload. Optional; Nothing omits the field. Factory default: @Nothing@.
+    webhookMessageMetadata :: Maybe (Map Text Value),
+    -- | Unmodeled extension fields. Reserved and modeled keys are rejected.
+    webhookMessageExtensions :: Map Text Value
+  }
+  deriving (Eq, Show)
+
+-- | Start editable fields for WebhookMessage; required arguments follow the signature.
+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.
+-- Finish with 'buildWebhookMessage' to validate before JSON encoding.
+webhookMessage :: WebhookMessageFields
+webhookMessage = WebhookMessageFields (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)
+
+-- | Inspect immutable construction fields. Rebuild edits with 'buildWebhookMessage' to revalidate.
+inspectWebhookMessage :: WebhookMessage -> WebhookMessageFields
+inspectWebhookMessage (CheckedWebhookMessage fields) = fields
+
+-- | Validate fields, extensions and receiving context. Return structured errors or a checked WebhookMessage.
+buildWebhookMessage :: WebhookMessageFields -> Result WebhookMessage
+buildWebhookMessage fields = do
+  checkExtensions ["blocks", "attachments", "text", "response_type", "replace_original", "delete_original", "unfurl_links", "unfurl_media", "metadata"] (webhookMessageExtensions fields)
+  validateFields "WebhookMessage" (wireWebhookMessage fields)
+  pure (CheckedWebhookMessage fields)
+
+wireWebhookMessage :: WebhookMessageFields -> Value
+wireWebhookMessage fields =
+  object $
+    [] <> optional "blocks" (webhookMessageBlocks fields) <> optional "attachments" (webhookMessageAttachments fields) <> optional "text" (webhookMessageText fields) <> optional "response_type" (webhookMessageResponseType fields) <> optional "replace_original" (webhookMessageReplaceOriginal fields) <> optional "delete_original" (webhookMessageDeleteOriginal fields) <> optional "unfurl_links" (webhookMessageUnfurlLinks fields) <> optional "unfurl_media" (webhookMessageUnfurlMedia fields) <> optional "metadata" (webhookMessageMetadata fields) <> extensionPairs (webhookMessageExtensions fields)
+
+instance ToJSON WebhookMessage where toJSON (CheckedWebhookMessage fields) = wireWebhookMessage fields
+
+instance FromSlackJSON WebhookMessage where
+  fromSlackJSON value = do
+    fields <- wireObject "" value
+    parsedBlocks <- optionalField "blocks" fields
+    parsedAttachments <- optionalField "attachments" fields
+    parsedText <- optionalField "text" fields
+    parsedResponseType <- optionalField "response_type" fields
+    parsedReplaceOriginal <- optionalField "replace_original" fields
+    parsedDeleteOriginal <- optionalField "delete_original" fields
+    parsedUnfurlLinks <- optionalField "unfurl_links" fields
+    parsedUnfurlMedia <- optionalField "unfurl_media" fields
+    parsedMetadata <- optionalField "metadata" fields
+    buildWebhookMessage (WebhookMessageFields parsedBlocks parsedAttachments parsedText parsedResponseType parsedReplaceOriginal parsedDeleteOriginal parsedUnfurlLinks parsedUnfurlMedia parsedMetadata (remaining ["blocks", "attachments", "text", "response_type", "replace_original", "delete_original", "unfurl_links", "unfurl_media", "metadata"] fields))
+
+instance FromJSON WebhookMessage where parseJSON = checkedParser
+
+-- | A task in a plan. Unlike a standalone task card, it may be pending.
+newtype PlanTask = CheckedPlanTask TaskCardBlock deriving (Eq, Show)
+
+-- | Check a task for a plan, where pending is allowed. This value cannot enter the Block role.
+buildPlanTask :: TaskCardBlockFields -> Result PlanTask
+buildPlanTask fields = do
+  checkExtensions ["task_id", "title", "details", "output", "sources", "status", "block_id"] (taskCardBlockExtensions fields)
+  validateFields "TaskCardBlock" (wireTaskCardBlock fields)
+  pure (CheckedPlanTask (CheckedTaskCardBlock fields))
+
+-- | Inspect a plan task; call buildPlanTask after editing its fields.
+inspectPlanTask :: PlanTask -> TaskCardBlockFields
+inspectPlanTask (CheckedPlanTask task) = inspectTaskCardBlock task
+
+instance ToJSON PlanTask where toJSON (CheckedPlanTask task) = stripTag (toJSON task)
+
+instance FromSlackJSON PlanTask where fromSlackJSON = decodeTaskCard buildPlanTask . restoreTask
+
+instance FromJSON PlanTask where parseJSON = checkedParser
+
+-- | Closed role: only checked members may enter this position.
+data Block = BlockActionsBlock ActionsBlock | BlockAlertBlock AlertBlock | BlockCardBlock CardBlock | BlockCarouselBlock CarouselBlock | BlockContainerBlock ContainerBlock | BlockContextActionsBlock ContextActionsBlock | BlockContextBlock ContextBlock | BlockDataTableBlock DataTableBlock | BlockDataVisualizationBlock DataVisualizationBlock | BlockDividerBlock DividerBlock | BlockFileBlock FileBlock | BlockHeaderBlock HeaderBlock | BlockImageBlock ImageBlock | BlockInputBlock InputBlock | BlockMarkdownBlock MarkdownBlock | BlockPlanBlock PlanBlock | BlockRichTextBlock RichTextBlock | BlockSectionBlock SectionBlock | BlockTableBlock TableBlock | BlockTaskCardBlock TaskCardBlock | BlockVideoBlock VideoBlock deriving (Eq, Show)
+
+instance ToJSON Block where
+  toJSON (BlockActionsBlock value) = toJSON value
+  toJSON (BlockAlertBlock value) = toJSON value
+  toJSON (BlockCardBlock value) = toJSON value
+  toJSON (BlockCarouselBlock value) = toJSON value
+  toJSON (BlockContainerBlock value) = toJSON value
+  toJSON (BlockContextActionsBlock value) = toJSON value
+  toJSON (BlockContextBlock value) = toJSON value
+  toJSON (BlockDataTableBlock value) = toJSON value
+  toJSON (BlockDataVisualizationBlock value) = toJSON value
+  toJSON (BlockDividerBlock value) = toJSON value
+  toJSON (BlockFileBlock value) = toJSON value
+  toJSON (BlockHeaderBlock value) = toJSON value
+  toJSON (BlockImageBlock value) = toJSON value
+  toJSON (BlockInputBlock value) = toJSON value
+  toJSON (BlockMarkdownBlock value) = toJSON value
+  toJSON (BlockPlanBlock value) = toJSON value
+  toJSON (BlockRichTextBlock value) = toJSON value
+  toJSON (BlockSectionBlock value) = toJSON value
+  toJSON (BlockTableBlock value) = toJSON value
+  toJSON (BlockTaskCardBlock value) = toJSON value
+  toJSON (BlockVideoBlock value) = toJSON value
+
+instance FromSlackJSON Block where
+  fromSlackJSON value = do
+    tag <- wireTag value
+    case tag of
+      "actions" -> BlockActionsBlock <$> fromSlackJSON value
+      "alert" -> BlockAlertBlock <$> fromSlackJSON value
+      "card" -> BlockCardBlock <$> fromSlackJSON value
+      "carousel" -> BlockCarouselBlock <$> fromSlackJSON value
+      "container" -> BlockContainerBlock <$> fromSlackJSON value
+      "context_actions" -> BlockContextActionsBlock <$> fromSlackJSON value
+      "context" -> BlockContextBlock <$> fromSlackJSON value
+      "data_table" -> BlockDataTableBlock <$> fromSlackJSON value
+      "data_visualization" -> BlockDataVisualizationBlock <$> fromSlackJSON value
+      "divider" -> BlockDividerBlock <$> fromSlackJSON value
+      "file" -> BlockFileBlock <$> fromSlackJSON value
+      "header" -> BlockHeaderBlock <$> fromSlackJSON value
+      "image" -> BlockImageBlock <$> fromSlackJSON value
+      "input" -> BlockInputBlock <$> fromSlackJSON value
+      "markdown" -> BlockMarkdownBlock <$> fromSlackJSON value
+      "plan" -> BlockPlanBlock <$> fromSlackJSON value
+      "rich_text" -> BlockRichTextBlock <$> fromSlackJSON value
+      "section" -> BlockSectionBlock <$> fromSlackJSON value
+      "table" -> BlockTableBlock <$> fromSlackJSON value
+      "task_card" -> fromSlackJSON value >>= taskCardBlockAsBlock
+      "video" -> BlockVideoBlock <$> fromSlackJSON value
+      _ -> failure TypeMismatch [Field "type"] "role" "unsupported child type"
+
+instance FromJSON Block where parseJSON = checkedParser
+
+-- | Place a checked ActionsBlock in the Block role. The receiving parent retains its contextual validation.
+actionsBlockAsBlock :: ActionsBlock -> Block
+actionsBlockAsBlock = BlockActionsBlock
+
+-- | Return the ActionsBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockActionsBlock :: Block -> Maybe ActionsBlock
+matchBlockActionsBlock (BlockActionsBlock value) = Just value
+matchBlockActionsBlock _ = Nothing
+
+-- | Place a checked AlertBlock in the Block role. The receiving parent retains its contextual validation.
+alertBlockAsBlock :: AlertBlock -> Block
+alertBlockAsBlock = BlockAlertBlock
+
+-- | Return the AlertBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockAlertBlock :: Block -> Maybe AlertBlock
+matchBlockAlertBlock (BlockAlertBlock value) = Just value
+matchBlockAlertBlock _ = Nothing
+
+-- | Place a checked CardBlock in the Block role. The receiving parent retains its contextual validation.
+cardBlockAsBlock :: CardBlock -> Block
+cardBlockAsBlock = BlockCardBlock
+
+-- | Return the CardBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockCardBlock :: Block -> Maybe CardBlock
+matchBlockCardBlock (BlockCardBlock value) = Just value
+matchBlockCardBlock _ = Nothing
+
+-- | Place a checked CarouselBlock in the Block role. The receiving parent retains its contextual validation.
+carouselBlockAsBlock :: CarouselBlock -> Block
+carouselBlockAsBlock = BlockCarouselBlock
+
+-- | Return the CarouselBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockCarouselBlock :: Block -> Maybe CarouselBlock
+matchBlockCarouselBlock (BlockCarouselBlock value) = Just value
+matchBlockCarouselBlock _ = Nothing
+
+-- | Place a checked ContainerBlock in the Block role. The receiving parent retains its contextual validation.
+containerBlockAsBlock :: ContainerBlock -> Block
+containerBlockAsBlock = BlockContainerBlock
+
+-- | Return the ContainerBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockContainerBlock :: Block -> Maybe ContainerBlock
+matchBlockContainerBlock (BlockContainerBlock value) = Just value
+matchBlockContainerBlock _ = Nothing
+
+-- | Place a checked ContextActionsBlock in the Block role. The receiving parent retains its contextual validation.
+contextActionsBlockAsBlock :: ContextActionsBlock -> Block
+contextActionsBlockAsBlock = BlockContextActionsBlock
+
+-- | Return the ContextActionsBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockContextActionsBlock :: Block -> Maybe ContextActionsBlock
+matchBlockContextActionsBlock (BlockContextActionsBlock value) = Just value
+matchBlockContextActionsBlock _ = Nothing
+
+-- | Place a checked ContextBlock in the Block role. The receiving parent retains its contextual validation.
+contextBlockAsBlock :: ContextBlock -> Block
+contextBlockAsBlock = BlockContextBlock
+
+-- | Return the ContextBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockContextBlock :: Block -> Maybe ContextBlock
+matchBlockContextBlock (BlockContextBlock value) = Just value
+matchBlockContextBlock _ = Nothing
+
+-- | Place a checked DataTableBlock in the Block role. The receiving parent retains its contextual validation.
+dataTableBlockAsBlock :: DataTableBlock -> Block
+dataTableBlockAsBlock = BlockDataTableBlock
+
+-- | Return the DataTableBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockDataTableBlock :: Block -> Maybe DataTableBlock
+matchBlockDataTableBlock (BlockDataTableBlock value) = Just value
+matchBlockDataTableBlock _ = Nothing
+
+-- | Place a checked DataVisualizationBlock in the Block role. The receiving parent retains its contextual validation.
+dataVisualizationBlockAsBlock :: DataVisualizationBlock -> Block
+dataVisualizationBlockAsBlock = BlockDataVisualizationBlock
+
+-- | Return the DataVisualizationBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockDataVisualizationBlock :: Block -> Maybe DataVisualizationBlock
+matchBlockDataVisualizationBlock (BlockDataVisualizationBlock value) = Just value
+matchBlockDataVisualizationBlock _ = Nothing
+
+-- | Place a checked DividerBlock in the Block role. The receiving parent retains its contextual validation.
+dividerBlockAsBlock :: DividerBlock -> Block
+dividerBlockAsBlock = BlockDividerBlock
+
+-- | Return the DividerBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockDividerBlock :: Block -> Maybe DividerBlock
+matchBlockDividerBlock (BlockDividerBlock value) = Just value
+matchBlockDividerBlock _ = Nothing
+
+-- | Place a checked FileBlock in the Block role. The receiving parent retains its contextual validation.
+fileBlockAsBlock :: FileBlock -> Block
+fileBlockAsBlock = BlockFileBlock
+
+-- | Return the FileBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockFileBlock :: Block -> Maybe FileBlock
+matchBlockFileBlock (BlockFileBlock value) = Just value
+matchBlockFileBlock _ = Nothing
+
+-- | Place a checked HeaderBlock in the Block role. The receiving parent retains its contextual validation.
+headerBlockAsBlock :: HeaderBlock -> Block
+headerBlockAsBlock = BlockHeaderBlock
+
+-- | Return the HeaderBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockHeaderBlock :: Block -> Maybe HeaderBlock
+matchBlockHeaderBlock (BlockHeaderBlock value) = Just value
+matchBlockHeaderBlock _ = Nothing
+
+-- | Place a checked ImageBlock in the Block role. The receiving parent retains its contextual validation.
+imageBlockAsBlock :: ImageBlock -> Block
+imageBlockAsBlock = BlockImageBlock
+
+-- | Return the ImageBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockImageBlock :: Block -> Maybe ImageBlock
+matchBlockImageBlock (BlockImageBlock value) = Just value
+matchBlockImageBlock _ = Nothing
+
+-- | Place a checked InputBlock in the Block role. The receiving parent retains its contextual validation.
+inputBlockAsBlock :: InputBlock -> Block
+inputBlockAsBlock = BlockInputBlock
+
+-- | Return the InputBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockInputBlock :: Block -> Maybe InputBlock
+matchBlockInputBlock (BlockInputBlock value) = Just value
+matchBlockInputBlock _ = Nothing
+
+-- | Place a checked MarkdownBlock in the Block role. The receiving parent retains its contextual validation.
+markdownBlockAsBlock :: MarkdownBlock -> Block
+markdownBlockAsBlock = BlockMarkdownBlock
+
+-- | Return the MarkdownBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockMarkdownBlock :: Block -> Maybe MarkdownBlock
+matchBlockMarkdownBlock (BlockMarkdownBlock value) = Just value
+matchBlockMarkdownBlock _ = Nothing
+
+-- | Place a checked PlanBlock in the Block role. The receiving parent retains its contextual validation.
+planBlockAsBlock :: PlanBlock -> Block
+planBlockAsBlock = BlockPlanBlock
+
+-- | Return the PlanBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockPlanBlock :: Block -> Maybe PlanBlock
+matchBlockPlanBlock (BlockPlanBlock value) = Just value
+matchBlockPlanBlock _ = Nothing
+
+-- | Place a checked RichTextBlock in the Block role. The receiving parent retains its contextual validation.
+richTextBlockAsBlock :: RichTextBlock -> Block
+richTextBlockAsBlock = BlockRichTextBlock
+
+-- | Return the RichTextBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockRichTextBlock :: Block -> Maybe RichTextBlock
+matchBlockRichTextBlock (BlockRichTextBlock value) = Just value
+matchBlockRichTextBlock _ = Nothing
+
+-- | Place a checked SectionBlock in the Block role. The receiving parent retains its contextual validation.
+sectionBlockAsBlock :: SectionBlock -> Block
+sectionBlockAsBlock = BlockSectionBlock
+
+-- | Return the SectionBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockSectionBlock :: Block -> Maybe SectionBlock
+matchBlockSectionBlock (BlockSectionBlock value) = Just value
+matchBlockSectionBlock _ = Nothing
+
+-- | Place a checked TableBlock in the Block role. The receiving parent retains its contextual validation.
+tableBlockAsBlock :: TableBlock -> Block
+tableBlockAsBlock = BlockTableBlock
+
+-- | Return the TableBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockTableBlock :: Block -> Maybe TableBlock
+matchBlockTableBlock (BlockTableBlock value) = Just value
+matchBlockTableBlock _ = Nothing
+
+-- | Place a checked TaskCardBlock in the Block role. The receiving parent retains its contextual validation.
+taskCardBlockAsBlock :: TaskCardBlock -> Result Block
+taskCardBlockAsBlock value = do
+  standaloneTask (toJSON value)
+  pure (BlockTaskCardBlock value)
+
+-- | Return the TaskCardBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockTaskCardBlock :: Block -> Maybe TaskCardBlock
+matchBlockTaskCardBlock (BlockTaskCardBlock value) = Just value
+matchBlockTaskCardBlock _ = Nothing
+
+-- | Place a checked VideoBlock in the Block role. The receiving parent retains its contextual validation.
+videoBlockAsBlock :: VideoBlock -> Block
+videoBlockAsBlock = BlockVideoBlock
+
+-- | Return the VideoBlock member when present, otherwise Nothing; no unchecked cast.
+matchBlockVideoBlock :: Block -> Maybe VideoBlock
+matchBlockVideoBlock (BlockVideoBlock value) = Just value
+matchBlockVideoBlock _ = Nothing
+
+-- | Closed role: only checked members may enter this position.
+data ContextElement = ContextElementImageElement ImageElement | ContextElementMarkdownText MarkdownText | ContextElementPlainText PlainText deriving (Eq, Show)
+
+instance ToJSON ContextElement where
+  toJSON (ContextElementImageElement value) = toJSON value
+  toJSON (ContextElementMarkdownText value) = toJSON value
+  toJSON (ContextElementPlainText value) = toJSON value
+
+instance FromSlackJSON ContextElement where
+  fromSlackJSON value = do
+    tag <- wireTag value
+    case tag of
+      "image" -> ContextElementImageElement <$> fromSlackJSON value
+      "mrkdwn" -> ContextElementMarkdownText <$> fromSlackJSON value
+      "plain_text" -> ContextElementPlainText <$> fromSlackJSON value
+      _ -> failure TypeMismatch [Field "type"] "role" "unsupported child type"
+
+instance FromJSON ContextElement where parseJSON = checkedParser
+
+-- | Place a checked ImageElement in the ContextElement role. The receiving parent retains its contextual validation.
+imageElementAsContextElement :: ImageElement -> ContextElement
+imageElementAsContextElement = ContextElementImageElement
+
+-- | Return the ImageElement member when present, otherwise Nothing; no unchecked cast.
+matchContextElementImageElement :: ContextElement -> Maybe ImageElement
+matchContextElementImageElement (ContextElementImageElement value) = Just value
+matchContextElementImageElement _ = Nothing
+
+-- | Place a checked MarkdownText in the ContextElement role. The receiving parent retains its contextual validation.
+markdownTextAsContextElement :: MarkdownText -> ContextElement
+markdownTextAsContextElement = ContextElementMarkdownText
+
+-- | Return the MarkdownText member when present, otherwise Nothing; no unchecked cast.
+matchContextElementMarkdownText :: ContextElement -> Maybe MarkdownText
+matchContextElementMarkdownText (ContextElementMarkdownText value) = Just value
+matchContextElementMarkdownText _ = Nothing
+
+-- | Place a checked PlainText in the ContextElement role. The receiving parent retains its contextual validation.
+plainTextAsContextElement :: PlainText -> ContextElement
+plainTextAsContextElement = ContextElementPlainText
+
+-- | Return the PlainText member when present, otherwise Nothing; no unchecked cast.
+matchContextElementPlainText :: ContextElement -> Maybe PlainText
+matchContextElementPlainText (ContextElementPlainText value) = Just value
+matchContextElementPlainText _ = Nothing
+
+-- | Closed role: only checked members may enter this position.
+data ContextActionsElement = ContextActionsElementFeedbackButtonsElement FeedbackButtonsElement | ContextActionsElementIconButtonElement IconButtonElement deriving (Eq, Show)
+
+instance ToJSON ContextActionsElement where
+  toJSON (ContextActionsElementFeedbackButtonsElement value) = toJSON value
+  toJSON (ContextActionsElementIconButtonElement value) = toJSON value
+
+instance FromSlackJSON ContextActionsElement where
+  fromSlackJSON value = do
+    tag <- wireTag value
+    case tag of
+      "feedback_buttons" -> ContextActionsElementFeedbackButtonsElement <$> fromSlackJSON value
+      "icon_button" -> ContextActionsElementIconButtonElement <$> fromSlackJSON value
+      _ -> failure TypeMismatch [Field "type"] "role" "unsupported child type"
+
+instance FromJSON ContextActionsElement where parseJSON = checkedParser
+
+-- | Place a checked FeedbackButtonsElement in the ContextActionsElement role. The receiving parent retains its contextual validation.
+feedbackButtonsElementAsContextActionsElement :: FeedbackButtonsElement -> ContextActionsElement
+feedbackButtonsElementAsContextActionsElement = ContextActionsElementFeedbackButtonsElement
+
+-- | Return the FeedbackButtonsElement member when present, otherwise Nothing; no unchecked cast.
+matchContextActionsElementFeedbackButtonsElement :: ContextActionsElement -> Maybe FeedbackButtonsElement
+matchContextActionsElementFeedbackButtonsElement (ContextActionsElementFeedbackButtonsElement value) = Just value
+matchContextActionsElementFeedbackButtonsElement _ = Nothing
+
+-- | Place a checked IconButtonElement in the ContextActionsElement role. The receiving parent retains its contextual validation.
+iconButtonElementAsContextActionsElement :: IconButtonElement -> ContextActionsElement
+iconButtonElementAsContextActionsElement = ContextActionsElementIconButtonElement
+
+-- | Return the IconButtonElement member when present, otherwise Nothing; no unchecked cast.
+matchContextActionsElementIconButtonElement :: ContextActionsElement -> Maybe IconButtonElement
+matchContextActionsElementIconButtonElement (ContextActionsElementIconButtonElement value) = Just value
+matchContextActionsElementIconButtonElement _ = Nothing
+
+-- | Closed role: only checked members may enter this position.
+data TableCell = TableCellRichTextBlock RichTextBlock | TableCellRawText RawText deriving (Eq, Show)
+
+instance ToJSON TableCell where
+  toJSON (TableCellRichTextBlock value) = toJSON value
+  toJSON (TableCellRawText value) = toJSON value
+
+instance FromSlackJSON TableCell where
+  fromSlackJSON value = do
+    tag <- wireTag value
+    case tag of
+      "rich_text" -> TableCellRichTextBlock <$> fromSlackJSON value
+      "raw_text" -> TableCellRawText <$> fromSlackJSON value
+      _ -> failure TypeMismatch [Field "type"] "role" "unsupported child type"
+
+instance FromJSON TableCell where parseJSON = checkedParser
+
+-- | Place a checked RichTextBlock in the TableCell role. The receiving parent retains its contextual validation.
+richTextBlockAsTableCell :: RichTextBlock -> TableCell
+richTextBlockAsTableCell = TableCellRichTextBlock
+
+-- | Return the RichTextBlock member when present, otherwise Nothing; no unchecked cast.
+matchTableCellRichTextBlock :: TableCell -> Maybe RichTextBlock
+matchTableCellRichTextBlock (TableCellRichTextBlock value) = Just value
+matchTableCellRichTextBlock _ = Nothing
+
+-- | Place a checked RawText in the TableCell role. The receiving parent retains its contextual validation.
+rawTextAsTableCell :: RawText -> TableCell
+rawTextAsTableCell = TableCellRawText
+
+-- | Return the RawText member when present, otherwise Nothing; no unchecked cast.
+matchTableCellRawText :: TableCell -> Maybe RawText
+matchTableCellRawText (TableCellRawText value) = Just value
+matchTableCellRawText _ = Nothing
+
+-- | Closed role: only checked members may enter this position.
+data DataTableCell = DataTableCellRichTextBlock RichTextBlock | DataTableCellRawNumber RawNumber | DataTableCellRawText RawText deriving (Eq, Show)
+
+instance ToJSON DataTableCell where
+  toJSON (DataTableCellRichTextBlock value) = toJSON value
+  toJSON (DataTableCellRawNumber value) = toJSON value
+  toJSON (DataTableCellRawText value) = toJSON value
+
+instance FromSlackJSON DataTableCell where
+  fromSlackJSON value = do
+    tag <- wireTag value
+    case tag of
+      "rich_text" -> DataTableCellRichTextBlock <$> fromSlackJSON value
+      "raw_number" -> DataTableCellRawNumber <$> fromSlackJSON value
+      "raw_text" -> DataTableCellRawText <$> fromSlackJSON value
+      _ -> failure TypeMismatch [Field "type"] "role" "unsupported child type"
+
+instance FromJSON DataTableCell where parseJSON = checkedParser
+
+-- | Place a checked RichTextBlock in the DataTableCell role. The receiving parent retains its contextual validation.
+richTextBlockAsDataTableCell :: RichTextBlock -> DataTableCell
+richTextBlockAsDataTableCell = DataTableCellRichTextBlock
+
+-- | Return the RichTextBlock member when present, otherwise Nothing; no unchecked cast.
+matchDataTableCellRichTextBlock :: DataTableCell -> Maybe RichTextBlock
+matchDataTableCellRichTextBlock (DataTableCellRichTextBlock value) = Just value
+matchDataTableCellRichTextBlock _ = Nothing
+
+-- | Place a checked RawNumber in the DataTableCell role. The receiving parent retains its contextual validation.
+rawNumberAsDataTableCell :: RawNumber -> DataTableCell
+rawNumberAsDataTableCell = DataTableCellRawNumber
+
+-- | Return the RawNumber member when present, otherwise Nothing; no unchecked cast.
+matchDataTableCellRawNumber :: DataTableCell -> Maybe RawNumber
+matchDataTableCellRawNumber (DataTableCellRawNumber value) = Just value
+matchDataTableCellRawNumber _ = Nothing
+
+-- | Place a checked RawText in the DataTableCell role. The receiving parent retains its contextual validation.
+rawTextAsDataTableCell :: RawText -> DataTableCell
+rawTextAsDataTableCell = DataTableCellRawText
+
+-- | Return the RawText member when present, otherwise Nothing; no unchecked cast.
+matchDataTableCellRawText :: DataTableCell -> Maybe RawText
+matchDataTableCellRawText (DataTableCellRawText value) = Just value
+matchDataTableCellRawText _ = Nothing
+
+-- | Closed role: only checked members may enter this position.
+data Element = ElementButtonElement ButtonElement | ElementChannelMultiSelectElement ChannelMultiSelectElement | ElementChannelSelectElement ChannelSelectElement | ElementCheckboxesElement CheckboxesElement | ElementConversationMultiSelectElement ConversationMultiSelectElement | ElementConversationSelectElement ConversationSelectElement | ElementDatePickerElement DatePickerElement | ElementDateTimePickerElement DateTimePickerElement | ElementEmailInputElement EmailInputElement | ElementExternalMultiSelectElement ExternalMultiSelectElement | ElementExternalSelectElement ExternalSelectElement | ElementFeedbackButtonsElement FeedbackButtonsElement | ElementFileInputElement FileInputElement | ElementIconButtonElement IconButtonElement | ElementImageElement ImageElement | ElementNumberInputElement NumberInputElement | ElementOverflowElement OverflowElement | ElementPlainTextInputElement PlainTextInputElement | ElementRadioButtonsElement RadioButtonsElement | ElementRichTextInputElement RichTextInputElement | ElementStaticMultiSelectElement StaticMultiSelectElement | ElementStaticSelectElement StaticSelectElement | ElementTimePickerElement TimePickerElement | ElementUrlInputElement UrlInputElement | ElementUserMultiSelectElement UserMultiSelectElement | ElementUserSelectElement UserSelectElement | ElementWorkflowButtonElement WorkflowButtonElement deriving (Eq, Show)
+
+instance ToJSON Element where
+  toJSON (ElementButtonElement value) = toJSON value
+  toJSON (ElementChannelMultiSelectElement value) = toJSON value
+  toJSON (ElementChannelSelectElement value) = toJSON value
+  toJSON (ElementCheckboxesElement value) = toJSON value
+  toJSON (ElementConversationMultiSelectElement value) = toJSON value
+  toJSON (ElementConversationSelectElement value) = toJSON value
+  toJSON (ElementDatePickerElement value) = toJSON value
+  toJSON (ElementDateTimePickerElement value) = toJSON value
+  toJSON (ElementEmailInputElement value) = toJSON value
+  toJSON (ElementExternalMultiSelectElement value) = toJSON value
+  toJSON (ElementExternalSelectElement value) = toJSON value
+  toJSON (ElementFeedbackButtonsElement value) = toJSON value
+  toJSON (ElementFileInputElement value) = toJSON value
+  toJSON (ElementIconButtonElement value) = toJSON value
+  toJSON (ElementImageElement value) = toJSON value
+  toJSON (ElementNumberInputElement value) = toJSON value
+  toJSON (ElementOverflowElement value) = toJSON value
+  toJSON (ElementPlainTextInputElement value) = toJSON value
+  toJSON (ElementRadioButtonsElement value) = toJSON value
+  toJSON (ElementRichTextInputElement value) = toJSON value
+  toJSON (ElementStaticMultiSelectElement value) = toJSON value
+  toJSON (ElementStaticSelectElement value) = toJSON value
+  toJSON (ElementTimePickerElement value) = toJSON value
+  toJSON (ElementUrlInputElement value) = toJSON value
+  toJSON (ElementUserMultiSelectElement value) = toJSON value
+  toJSON (ElementUserSelectElement value) = toJSON value
+  toJSON (ElementWorkflowButtonElement value) = toJSON value
+
+instance FromSlackJSON Element where
+  fromSlackJSON value = do
+    tag <- wireTag value
+    case tag of
+      "button" -> ElementButtonElement <$> fromSlackJSON value
+      "multi_channels_select" -> ElementChannelMultiSelectElement <$> fromSlackJSON value
+      "channels_select" -> ElementChannelSelectElement <$> fromSlackJSON value
+      "checkboxes" -> ElementCheckboxesElement <$> fromSlackJSON value
+      "multi_conversations_select" -> ElementConversationMultiSelectElement <$> fromSlackJSON value
+      "conversations_select" -> ElementConversationSelectElement <$> fromSlackJSON value
+      "datepicker" -> ElementDatePickerElement <$> fromSlackJSON value
+      "datetimepicker" -> ElementDateTimePickerElement <$> fromSlackJSON value
+      "email_text_input" -> ElementEmailInputElement <$> fromSlackJSON value
+      "multi_external_select" -> ElementExternalMultiSelectElement <$> fromSlackJSON value
+      "external_select" -> ElementExternalSelectElement <$> fromSlackJSON value
+      "feedback_buttons" -> ElementFeedbackButtonsElement <$> fromSlackJSON value
+      "file_input" -> ElementFileInputElement <$> fromSlackJSON value
+      "icon_button" -> ElementIconButtonElement <$> fromSlackJSON value
+      "image" -> ElementImageElement <$> fromSlackJSON value
+      "number_input" -> ElementNumberInputElement <$> fromSlackJSON value
+      "overflow" -> ElementOverflowElement <$> fromSlackJSON value
+      "plain_text_input" -> ElementPlainTextInputElement <$> fromSlackJSON value
+      "radio_buttons" -> ElementRadioButtonsElement <$> fromSlackJSON value
+      "rich_text_input" -> ElementRichTextInputElement <$> fromSlackJSON value
+      "multi_static_select" -> ElementStaticMultiSelectElement <$> fromSlackJSON value
+      "static_select" -> ElementStaticSelectElement <$> fromSlackJSON value
+      "timepicker" -> ElementTimePickerElement <$> fromSlackJSON value
+      "url_text_input" -> ElementUrlInputElement <$> fromSlackJSON value
+      "multi_users_select" -> ElementUserMultiSelectElement <$> fromSlackJSON value
+      "users_select" -> ElementUserSelectElement <$> fromSlackJSON value
+      "workflow_button" -> ElementWorkflowButtonElement <$> fromSlackJSON value
+      _ -> failure TypeMismatch [Field "type"] "role" "unsupported child type"
+
+instance FromJSON Element where parseJSON = checkedParser
+
+-- | Place a checked ButtonElement in the Element role. The receiving parent retains its contextual validation.
+buttonElementAsElement :: ButtonElement -> Element
+buttonElementAsElement = ElementButtonElement
+
+-- | Return the ButtonElement member when present, otherwise Nothing; no unchecked cast.
+matchElementButtonElement :: Element -> Maybe ButtonElement
+matchElementButtonElement (ElementButtonElement value) = Just value
+matchElementButtonElement _ = Nothing
+
+-- | Place a checked ChannelMultiSelectElement in the Element role. The receiving parent retains its contextual validation.
+channelMultiSelectElementAsElement :: ChannelMultiSelectElement -> Element
+channelMultiSelectElementAsElement = ElementChannelMultiSelectElement
+
+-- | Return the ChannelMultiSelectElement member when present, otherwise Nothing; no unchecked cast.
+matchElementChannelMultiSelectElement :: Element -> Maybe ChannelMultiSelectElement
+matchElementChannelMultiSelectElement (ElementChannelMultiSelectElement value) = Just value
+matchElementChannelMultiSelectElement _ = Nothing
+
+-- | Place a checked ChannelSelectElement in the Element role. The receiving parent retains its contextual validation.
+channelSelectElementAsElement :: ChannelSelectElement -> Element
+channelSelectElementAsElement = ElementChannelSelectElement
+
+-- | Return the ChannelSelectElement member when present, otherwise Nothing; no unchecked cast.
+matchElementChannelSelectElement :: Element -> Maybe ChannelSelectElement
+matchElementChannelSelectElement (ElementChannelSelectElement value) = Just value
+matchElementChannelSelectElement _ = Nothing
+
+-- | Place a checked CheckboxesElement in the Element role. The receiving parent retains its contextual validation.
+checkboxesElementAsElement :: CheckboxesElement -> Element
+checkboxesElementAsElement = ElementCheckboxesElement
+
+-- | Return the CheckboxesElement member when present, otherwise Nothing; no unchecked cast.
+matchElementCheckboxesElement :: Element -> Maybe CheckboxesElement
+matchElementCheckboxesElement (ElementCheckboxesElement value) = Just value
+matchElementCheckboxesElement _ = Nothing
+
+-- | Place a checked ConversationMultiSelectElement in the Element role. The receiving parent retains its contextual validation.
+conversationMultiSelectElementAsElement :: ConversationMultiSelectElement -> Element
+conversationMultiSelectElementAsElement = ElementConversationMultiSelectElement
+
+-- | Return the ConversationMultiSelectElement member when present, otherwise Nothing; no unchecked cast.
+matchElementConversationMultiSelectElement :: Element -> Maybe ConversationMultiSelectElement
+matchElementConversationMultiSelectElement (ElementConversationMultiSelectElement value) = Just value
+matchElementConversationMultiSelectElement _ = Nothing
+
+-- | Place a checked ConversationSelectElement in the Element role. The receiving parent retains its contextual validation.
+conversationSelectElementAsElement :: ConversationSelectElement -> Element
+conversationSelectElementAsElement = ElementConversationSelectElement
+
+-- | Return the ConversationSelectElement member when present, otherwise Nothing; no unchecked cast.
+matchElementConversationSelectElement :: Element -> Maybe ConversationSelectElement
+matchElementConversationSelectElement (ElementConversationSelectElement value) = Just value
+matchElementConversationSelectElement _ = Nothing
+
+-- | Place a checked DatePickerElement in the Element role. The receiving parent retains its contextual validation.
+datePickerElementAsElement :: DatePickerElement -> Element
+datePickerElementAsElement = ElementDatePickerElement
+
+-- | Return the DatePickerElement member when present, otherwise Nothing; no unchecked cast.
+matchElementDatePickerElement :: Element -> Maybe DatePickerElement
+matchElementDatePickerElement (ElementDatePickerElement value) = Just value
+matchElementDatePickerElement _ = Nothing
+
+-- | Place a checked DateTimePickerElement in the Element role. The receiving parent retains its contextual validation.
+dateTimePickerElementAsElement :: DateTimePickerElement -> Element
+dateTimePickerElementAsElement = ElementDateTimePickerElement
+
+-- | Return the DateTimePickerElement member when present, otherwise Nothing; no unchecked cast.
+matchElementDateTimePickerElement :: Element -> Maybe DateTimePickerElement
+matchElementDateTimePickerElement (ElementDateTimePickerElement value) = Just value
+matchElementDateTimePickerElement _ = Nothing
+
+-- | Place a checked EmailInputElement in the Element role. The receiving parent retains its contextual validation.
+emailInputElementAsElement :: EmailInputElement -> Element
+emailInputElementAsElement = ElementEmailInputElement
+
+-- | Return the EmailInputElement member when present, otherwise Nothing; no unchecked cast.
+matchElementEmailInputElement :: Element -> Maybe EmailInputElement
+matchElementEmailInputElement (ElementEmailInputElement value) = Just value
+matchElementEmailInputElement _ = Nothing
+
+-- | Place a checked ExternalMultiSelectElement in the Element role. The receiving parent retains its contextual validation.
+externalMultiSelectElementAsElement :: ExternalMultiSelectElement -> Element
+externalMultiSelectElementAsElement = ElementExternalMultiSelectElement
+
+-- | Return the ExternalMultiSelectElement member when present, otherwise Nothing; no unchecked cast.
+matchElementExternalMultiSelectElement :: Element -> Maybe ExternalMultiSelectElement
+matchElementExternalMultiSelectElement (ElementExternalMultiSelectElement value) = Just value
+matchElementExternalMultiSelectElement _ = Nothing
+
+-- | Place a checked ExternalSelectElement in the Element role. The receiving parent retains its contextual validation.
+externalSelectElementAsElement :: ExternalSelectElement -> Element
+externalSelectElementAsElement = ElementExternalSelectElement
+
+-- | Return the ExternalSelectElement member when present, otherwise Nothing; no unchecked cast.
+matchElementExternalSelectElement :: Element -> Maybe ExternalSelectElement
+matchElementExternalSelectElement (ElementExternalSelectElement value) = Just value
+matchElementExternalSelectElement _ = Nothing
+
+-- | Place a checked FeedbackButtonsElement in the Element role. The receiving parent retains its contextual validation.
+feedbackButtonsElementAsElement :: FeedbackButtonsElement -> Element
+feedbackButtonsElementAsElement = ElementFeedbackButtonsElement
+
+-- | Return the FeedbackButtonsElement member when present, otherwise Nothing; no unchecked cast.
+matchElementFeedbackButtonsElement :: Element -> Maybe FeedbackButtonsElement
+matchElementFeedbackButtonsElement (ElementFeedbackButtonsElement value) = Just value
+matchElementFeedbackButtonsElement _ = Nothing
+
+-- | Place a checked FileInputElement in the Element role. The receiving parent retains its contextual validation.
+fileInputElementAsElement :: FileInputElement -> Element
+fileInputElementAsElement = ElementFileInputElement
+
+-- | Return the FileInputElement member when present, otherwise Nothing; no unchecked cast.
+matchElementFileInputElement :: Element -> Maybe FileInputElement
+matchElementFileInputElement (ElementFileInputElement value) = Just value
+matchElementFileInputElement _ = Nothing
+
+-- | Place a checked IconButtonElement in the Element role. The receiving parent retains its contextual validation.
+iconButtonElementAsElement :: IconButtonElement -> Element
+iconButtonElementAsElement = ElementIconButtonElement
+
+-- | Return the IconButtonElement member when present, otherwise Nothing; no unchecked cast.
+matchElementIconButtonElement :: Element -> Maybe IconButtonElement
+matchElementIconButtonElement (ElementIconButtonElement value) = Just value
+matchElementIconButtonElement _ = Nothing
+
+-- | Place a checked ImageElement in the Element role. The receiving parent retains its contextual validation.
+imageElementAsElement :: ImageElement -> Element
+imageElementAsElement = ElementImageElement
+
+-- | Return the ImageElement member when present, otherwise Nothing; no unchecked cast.
+matchElementImageElement :: Element -> Maybe ImageElement
+matchElementImageElement (ElementImageElement value) = Just value
+matchElementImageElement _ = Nothing
+
+-- | Place a checked NumberInputElement in the Element role. The receiving parent retains its contextual validation.
+numberInputElementAsElement :: NumberInputElement -> Element
+numberInputElementAsElement = ElementNumberInputElement
+
+-- | Return the NumberInputElement member when present, otherwise Nothing; no unchecked cast.
+matchElementNumberInputElement :: Element -> Maybe NumberInputElement
+matchElementNumberInputElement (ElementNumberInputElement value) = Just value
+matchElementNumberInputElement _ = Nothing
+
+-- | Place a checked OverflowElement in the Element role. The receiving parent retains its contextual validation.
+overflowElementAsElement :: OverflowElement -> Element
+overflowElementAsElement = ElementOverflowElement
+
+-- | Return the OverflowElement member when present, otherwise Nothing; no unchecked cast.
+matchElementOverflowElement :: Element -> Maybe OverflowElement
+matchElementOverflowElement (ElementOverflowElement value) = Just value
+matchElementOverflowElement _ = Nothing
+
+-- | Place a checked PlainTextInputElement in the Element role. The receiving parent retains its contextual validation.
+plainTextInputElementAsElement :: PlainTextInputElement -> Element
+plainTextInputElementAsElement = ElementPlainTextInputElement
+
+-- | Return the PlainTextInputElement member when present, otherwise Nothing; no unchecked cast.
+matchElementPlainTextInputElement :: Element -> Maybe PlainTextInputElement
+matchElementPlainTextInputElement (ElementPlainTextInputElement value) = Just value
+matchElementPlainTextInputElement _ = Nothing
+
+-- | Place a checked RadioButtonsElement in the Element role. The receiving parent retains its contextual validation.
+radioButtonsElementAsElement :: RadioButtonsElement -> Element
+radioButtonsElementAsElement = ElementRadioButtonsElement
+
+-- | Return the RadioButtonsElement member when present, otherwise Nothing; no unchecked cast.
+matchElementRadioButtonsElement :: Element -> Maybe RadioButtonsElement
+matchElementRadioButtonsElement (ElementRadioButtonsElement value) = Just value
+matchElementRadioButtonsElement _ = Nothing
+
+-- | Place a checked RichTextInputElement in the Element role. The receiving parent retains its contextual validation.
+richTextInputElementAsElement :: RichTextInputElement -> Element
+richTextInputElementAsElement = ElementRichTextInputElement
+
+-- | Return the RichTextInputElement member when present, otherwise Nothing; no unchecked cast.
+matchElementRichTextInputElement :: Element -> Maybe RichTextInputElement
+matchElementRichTextInputElement (ElementRichTextInputElement value) = Just value
+matchElementRichTextInputElement _ = Nothing
+
+-- | Place a checked StaticMultiSelectElement in the Element role. The receiving parent retains its contextual validation.
+staticMultiSelectElementAsElement :: StaticMultiSelectElement -> Element
+staticMultiSelectElementAsElement = ElementStaticMultiSelectElement
+
+-- | Return the StaticMultiSelectElement member when present, otherwise Nothing; no unchecked cast.
+matchElementStaticMultiSelectElement :: Element -> Maybe StaticMultiSelectElement
+matchElementStaticMultiSelectElement (ElementStaticMultiSelectElement value) = Just value
+matchElementStaticMultiSelectElement _ = Nothing
+
+-- | Place a checked StaticSelectElement in the Element role. The receiving parent retains its contextual validation.
+staticSelectElementAsElement :: StaticSelectElement -> Element
+staticSelectElementAsElement = ElementStaticSelectElement
+
+-- | Return the StaticSelectElement member when present, otherwise Nothing; no unchecked cast.
+matchElementStaticSelectElement :: Element -> Maybe StaticSelectElement
+matchElementStaticSelectElement (ElementStaticSelectElement value) = Just value
+matchElementStaticSelectElement _ = Nothing
+
+-- | Place a checked TimePickerElement in the Element role. The receiving parent retains its contextual validation.
+timePickerElementAsElement :: TimePickerElement -> Element
+timePickerElementAsElement = ElementTimePickerElement
+
+-- | Return the TimePickerElement member when present, otherwise Nothing; no unchecked cast.
+matchElementTimePickerElement :: Element -> Maybe TimePickerElement
+matchElementTimePickerElement (ElementTimePickerElement value) = Just value
+matchElementTimePickerElement _ = Nothing
+
+-- | Place a checked UrlInputElement in the Element role. The receiving parent retains its contextual validation.
+urlInputElementAsElement :: UrlInputElement -> Element
+urlInputElementAsElement = ElementUrlInputElement
+
+-- | Return the UrlInputElement member when present, otherwise Nothing; no unchecked cast.
+matchElementUrlInputElement :: Element -> Maybe UrlInputElement
+matchElementUrlInputElement (ElementUrlInputElement value) = Just value
+matchElementUrlInputElement _ = Nothing
+
+-- | Place a checked UserMultiSelectElement in the Element role. The receiving parent retains its contextual validation.
+userMultiSelectElementAsElement :: UserMultiSelectElement -> Element
+userMultiSelectElementAsElement = ElementUserMultiSelectElement
+
+-- | Return the UserMultiSelectElement member when present, otherwise Nothing; no unchecked cast.
+matchElementUserMultiSelectElement :: Element -> Maybe UserMultiSelectElement
+matchElementUserMultiSelectElement (ElementUserMultiSelectElement value) = Just value
+matchElementUserMultiSelectElement _ = Nothing
+
+-- | Place a checked UserSelectElement in the Element role. The receiving parent retains its contextual validation.
+userSelectElementAsElement :: UserSelectElement -> Element
+userSelectElementAsElement = ElementUserSelectElement
+
+-- | Return the UserSelectElement member when present, otherwise Nothing; no unchecked cast.
+matchElementUserSelectElement :: Element -> Maybe UserSelectElement
+matchElementUserSelectElement (ElementUserSelectElement value) = Just value
+matchElementUserSelectElement _ = Nothing
+
+-- | Place a checked WorkflowButtonElement in the Element role. The receiving parent retains its contextual validation.
+workflowButtonElementAsElement :: WorkflowButtonElement -> Element
+workflowButtonElementAsElement = ElementWorkflowButtonElement
+
+-- | Return the WorkflowButtonElement member when present, otherwise Nothing; no unchecked cast.
+matchElementWorkflowButtonElement :: Element -> Maybe WorkflowButtonElement
+matchElementWorkflowButtonElement (ElementWorkflowButtonElement value) = Just value
+matchElementWorkflowButtonElement _ = Nothing
+
+-- | Closed role: only checked members may enter this position.
+data InputElement = InputElementChannelMultiSelectElement ChannelMultiSelectElement | InputElementChannelSelectElement ChannelSelectElement | InputElementCheckboxesElement CheckboxesElement | InputElementConversationMultiSelectElement ConversationMultiSelectElement | InputElementConversationSelectElement ConversationSelectElement | InputElementDatePickerElement DatePickerElement | InputElementDateTimePickerElement DateTimePickerElement | InputElementEmailInputElement EmailInputElement | InputElementExternalMultiSelectElement ExternalMultiSelectElement | InputElementExternalSelectElement ExternalSelectElement | InputElementFileInputElement FileInputElement | InputElementNumberInputElement NumberInputElement | InputElementPlainTextInputElement PlainTextInputElement | InputElementRadioButtonsElement RadioButtonsElement | InputElementRichTextInputElement RichTextInputElement | InputElementStaticMultiSelectElement StaticMultiSelectElement | InputElementStaticSelectElement StaticSelectElement | InputElementTimePickerElement TimePickerElement | InputElementUrlInputElement UrlInputElement | InputElementUserMultiSelectElement UserMultiSelectElement | InputElementUserSelectElement UserSelectElement deriving (Eq, Show)
+
+instance ToJSON InputElement where
+  toJSON (InputElementChannelMultiSelectElement value) = toJSON value
+  toJSON (InputElementChannelSelectElement value) = toJSON value
+  toJSON (InputElementCheckboxesElement value) = toJSON value
+  toJSON (InputElementConversationMultiSelectElement value) = toJSON value
+  toJSON (InputElementConversationSelectElement value) = toJSON value
+  toJSON (InputElementDatePickerElement value) = toJSON value
+  toJSON (InputElementDateTimePickerElement value) = toJSON value
+  toJSON (InputElementEmailInputElement value) = toJSON value
+  toJSON (InputElementExternalMultiSelectElement value) = toJSON value
+  toJSON (InputElementExternalSelectElement value) = toJSON value
+  toJSON (InputElementFileInputElement value) = toJSON value
+  toJSON (InputElementNumberInputElement value) = toJSON value
+  toJSON (InputElementPlainTextInputElement value) = toJSON value
+  toJSON (InputElementRadioButtonsElement value) = toJSON value
+  toJSON (InputElementRichTextInputElement value) = toJSON value
+  toJSON (InputElementStaticMultiSelectElement value) = toJSON value
+  toJSON (InputElementStaticSelectElement value) = toJSON value
+  toJSON (InputElementTimePickerElement value) = toJSON value
+  toJSON (InputElementUrlInputElement value) = toJSON value
+  toJSON (InputElementUserMultiSelectElement value) = toJSON value
+  toJSON (InputElementUserSelectElement value) = toJSON value
+
+instance FromSlackJSON InputElement where
+  fromSlackJSON value = do
+    tag <- wireTag value
+    case tag of
+      "multi_channels_select" -> InputElementChannelMultiSelectElement <$> fromSlackJSON value
+      "channels_select" -> InputElementChannelSelectElement <$> fromSlackJSON value
+      "checkboxes" -> InputElementCheckboxesElement <$> fromSlackJSON value
+      "multi_conversations_select" -> InputElementConversationMultiSelectElement <$> fromSlackJSON value
+      "conversations_select" -> InputElementConversationSelectElement <$> fromSlackJSON value
+      "datepicker" -> InputElementDatePickerElement <$> fromSlackJSON value
+      "datetimepicker" -> InputElementDateTimePickerElement <$> fromSlackJSON value
+      "email_text_input" -> InputElementEmailInputElement <$> fromSlackJSON value
+      "multi_external_select" -> InputElementExternalMultiSelectElement <$> fromSlackJSON value
+      "external_select" -> InputElementExternalSelectElement <$> fromSlackJSON value
+      "file_input" -> InputElementFileInputElement <$> fromSlackJSON value
+      "number_input" -> InputElementNumberInputElement <$> fromSlackJSON value
+      "plain_text_input" -> InputElementPlainTextInputElement <$> fromSlackJSON value
+      "radio_buttons" -> InputElementRadioButtonsElement <$> fromSlackJSON value
+      "rich_text_input" -> InputElementRichTextInputElement <$> fromSlackJSON value
+      "multi_static_select" -> InputElementStaticMultiSelectElement <$> fromSlackJSON value
+      "static_select" -> InputElementStaticSelectElement <$> fromSlackJSON value
+      "timepicker" -> InputElementTimePickerElement <$> fromSlackJSON value
+      "url_text_input" -> InputElementUrlInputElement <$> fromSlackJSON value
+      "multi_users_select" -> InputElementUserMultiSelectElement <$> fromSlackJSON value
+      "users_select" -> InputElementUserSelectElement <$> fromSlackJSON value
+      _ -> failure TypeMismatch [Field "type"] "role" "unsupported child type"
+
+instance FromJSON InputElement where parseJSON = checkedParser
+
+-- | Place a checked ChannelMultiSelectElement in the InputElement role. The receiving parent retains its contextual validation.
+channelMultiSelectElementAsInputElement :: ChannelMultiSelectElement -> InputElement
+channelMultiSelectElementAsInputElement = InputElementChannelMultiSelectElement
+
+-- | Return the ChannelMultiSelectElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementChannelMultiSelectElement :: InputElement -> Maybe ChannelMultiSelectElement
+matchInputElementChannelMultiSelectElement (InputElementChannelMultiSelectElement value) = Just value
+matchInputElementChannelMultiSelectElement _ = Nothing
+
+-- | Place a checked ChannelSelectElement in the InputElement role. The receiving parent retains its contextual validation.
+channelSelectElementAsInputElement :: ChannelSelectElement -> InputElement
+channelSelectElementAsInputElement = InputElementChannelSelectElement
+
+-- | Return the ChannelSelectElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementChannelSelectElement :: InputElement -> Maybe ChannelSelectElement
+matchInputElementChannelSelectElement (InputElementChannelSelectElement value) = Just value
+matchInputElementChannelSelectElement _ = Nothing
+
+-- | Place a checked CheckboxesElement in the InputElement role. The receiving parent retains its contextual validation.
+checkboxesElementAsInputElement :: CheckboxesElement -> InputElement
+checkboxesElementAsInputElement = InputElementCheckboxesElement
+
+-- | Return the CheckboxesElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementCheckboxesElement :: InputElement -> Maybe CheckboxesElement
+matchInputElementCheckboxesElement (InputElementCheckboxesElement value) = Just value
+matchInputElementCheckboxesElement _ = Nothing
+
+-- | Place a checked ConversationMultiSelectElement in the InputElement role. The receiving parent retains its contextual validation.
+conversationMultiSelectElementAsInputElement :: ConversationMultiSelectElement -> InputElement
+conversationMultiSelectElementAsInputElement = InputElementConversationMultiSelectElement
+
+-- | Return the ConversationMultiSelectElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementConversationMultiSelectElement :: InputElement -> Maybe ConversationMultiSelectElement
+matchInputElementConversationMultiSelectElement (InputElementConversationMultiSelectElement value) = Just value
+matchInputElementConversationMultiSelectElement _ = Nothing
+
+-- | Place a checked ConversationSelectElement in the InputElement role. The receiving parent retains its contextual validation.
+conversationSelectElementAsInputElement :: ConversationSelectElement -> InputElement
+conversationSelectElementAsInputElement = InputElementConversationSelectElement
+
+-- | Return the ConversationSelectElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementConversationSelectElement :: InputElement -> Maybe ConversationSelectElement
+matchInputElementConversationSelectElement (InputElementConversationSelectElement value) = Just value
+matchInputElementConversationSelectElement _ = Nothing
+
+-- | Place a checked DatePickerElement in the InputElement role. The receiving parent retains its contextual validation.
+datePickerElementAsInputElement :: DatePickerElement -> InputElement
+datePickerElementAsInputElement = InputElementDatePickerElement
+
+-- | Return the DatePickerElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementDatePickerElement :: InputElement -> Maybe DatePickerElement
+matchInputElementDatePickerElement (InputElementDatePickerElement value) = Just value
+matchInputElementDatePickerElement _ = Nothing
+
+-- | Place a checked DateTimePickerElement in the InputElement role. The receiving parent retains its contextual validation.
+dateTimePickerElementAsInputElement :: DateTimePickerElement -> InputElement
+dateTimePickerElementAsInputElement = InputElementDateTimePickerElement
+
+-- | Return the DateTimePickerElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementDateTimePickerElement :: InputElement -> Maybe DateTimePickerElement
+matchInputElementDateTimePickerElement (InputElementDateTimePickerElement value) = Just value
+matchInputElementDateTimePickerElement _ = Nothing
+
+-- | Place a checked EmailInputElement in the InputElement role. The receiving parent retains its contextual validation.
+emailInputElementAsInputElement :: EmailInputElement -> InputElement
+emailInputElementAsInputElement = InputElementEmailInputElement
+
+-- | Return the EmailInputElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementEmailInputElement :: InputElement -> Maybe EmailInputElement
+matchInputElementEmailInputElement (InputElementEmailInputElement value) = Just value
+matchInputElementEmailInputElement _ = Nothing
+
+-- | Place a checked ExternalMultiSelectElement in the InputElement role. The receiving parent retains its contextual validation.
+externalMultiSelectElementAsInputElement :: ExternalMultiSelectElement -> InputElement
+externalMultiSelectElementAsInputElement = InputElementExternalMultiSelectElement
+
+-- | Return the ExternalMultiSelectElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementExternalMultiSelectElement :: InputElement -> Maybe ExternalMultiSelectElement
+matchInputElementExternalMultiSelectElement (InputElementExternalMultiSelectElement value) = Just value
+matchInputElementExternalMultiSelectElement _ = Nothing
+
+-- | Place a checked ExternalSelectElement in the InputElement role. The receiving parent retains its contextual validation.
+externalSelectElementAsInputElement :: ExternalSelectElement -> InputElement
+externalSelectElementAsInputElement = InputElementExternalSelectElement
+
+-- | Return the ExternalSelectElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementExternalSelectElement :: InputElement -> Maybe ExternalSelectElement
+matchInputElementExternalSelectElement (InputElementExternalSelectElement value) = Just value
+matchInputElementExternalSelectElement _ = Nothing
+
+-- | Place a checked FileInputElement in the InputElement role. The receiving parent retains its contextual validation.
+fileInputElementAsInputElement :: FileInputElement -> InputElement
+fileInputElementAsInputElement = InputElementFileInputElement
+
+-- | Return the FileInputElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementFileInputElement :: InputElement -> Maybe FileInputElement
+matchInputElementFileInputElement (InputElementFileInputElement value) = Just value
+matchInputElementFileInputElement _ = Nothing
+
+-- | Place a checked NumberInputElement in the InputElement role. The receiving parent retains its contextual validation.
+numberInputElementAsInputElement :: NumberInputElement -> InputElement
+numberInputElementAsInputElement = InputElementNumberInputElement
+
+-- | Return the NumberInputElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementNumberInputElement :: InputElement -> Maybe NumberInputElement
+matchInputElementNumberInputElement (InputElementNumberInputElement value) = Just value
+matchInputElementNumberInputElement _ = Nothing
+
+-- | Place a checked PlainTextInputElement in the InputElement role. The receiving parent retains its contextual validation.
+plainTextInputElementAsInputElement :: PlainTextInputElement -> InputElement
+plainTextInputElementAsInputElement = InputElementPlainTextInputElement
+
+-- | Return the PlainTextInputElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementPlainTextInputElement :: InputElement -> Maybe PlainTextInputElement
+matchInputElementPlainTextInputElement (InputElementPlainTextInputElement value) = Just value
+matchInputElementPlainTextInputElement _ = Nothing
+
+-- | Place a checked RadioButtonsElement in the InputElement role. The receiving parent retains its contextual validation.
+radioButtonsElementAsInputElement :: RadioButtonsElement -> InputElement
+radioButtonsElementAsInputElement = InputElementRadioButtonsElement
+
+-- | Return the RadioButtonsElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementRadioButtonsElement :: InputElement -> Maybe RadioButtonsElement
+matchInputElementRadioButtonsElement (InputElementRadioButtonsElement value) = Just value
+matchInputElementRadioButtonsElement _ = Nothing
+
+-- | Place a checked RichTextInputElement in the InputElement role. The receiving parent retains its contextual validation.
+richTextInputElementAsInputElement :: RichTextInputElement -> InputElement
+richTextInputElementAsInputElement = InputElementRichTextInputElement
+
+-- | Return the RichTextInputElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementRichTextInputElement :: InputElement -> Maybe RichTextInputElement
+matchInputElementRichTextInputElement (InputElementRichTextInputElement value) = Just value
+matchInputElementRichTextInputElement _ = Nothing
+
+-- | Place a checked StaticMultiSelectElement in the InputElement role. The receiving parent retains its contextual validation.
+staticMultiSelectElementAsInputElement :: StaticMultiSelectElement -> InputElement
+staticMultiSelectElementAsInputElement = InputElementStaticMultiSelectElement
+
+-- | Return the StaticMultiSelectElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementStaticMultiSelectElement :: InputElement -> Maybe StaticMultiSelectElement
+matchInputElementStaticMultiSelectElement (InputElementStaticMultiSelectElement value) = Just value
+matchInputElementStaticMultiSelectElement _ = Nothing
+
+-- | Place a checked StaticSelectElement in the InputElement role. The receiving parent retains its contextual validation.
+staticSelectElementAsInputElement :: StaticSelectElement -> InputElement
+staticSelectElementAsInputElement = InputElementStaticSelectElement
+
+-- | Return the StaticSelectElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementStaticSelectElement :: InputElement -> Maybe StaticSelectElement
+matchInputElementStaticSelectElement (InputElementStaticSelectElement value) = Just value
+matchInputElementStaticSelectElement _ = Nothing
+
+-- | Place a checked TimePickerElement in the InputElement role. The receiving parent retains its contextual validation.
+timePickerElementAsInputElement :: TimePickerElement -> InputElement
+timePickerElementAsInputElement = InputElementTimePickerElement
+
+-- | Return the TimePickerElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementTimePickerElement :: InputElement -> Maybe TimePickerElement
+matchInputElementTimePickerElement (InputElementTimePickerElement value) = Just value
+matchInputElementTimePickerElement _ = Nothing
+
+-- | Place a checked UrlInputElement in the InputElement role. The receiving parent retains its contextual validation.
+urlInputElementAsInputElement :: UrlInputElement -> InputElement
+urlInputElementAsInputElement = InputElementUrlInputElement
+
+-- | Return the UrlInputElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementUrlInputElement :: InputElement -> Maybe UrlInputElement
+matchInputElementUrlInputElement (InputElementUrlInputElement value) = Just value
+matchInputElementUrlInputElement _ = Nothing
+
+-- | Place a checked UserMultiSelectElement in the InputElement role. The receiving parent retains its contextual validation.
+userMultiSelectElementAsInputElement :: UserMultiSelectElement -> InputElement
+userMultiSelectElementAsInputElement = InputElementUserMultiSelectElement
+
+-- | Return the UserMultiSelectElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementUserMultiSelectElement :: InputElement -> Maybe UserMultiSelectElement
+matchInputElementUserMultiSelectElement (InputElementUserMultiSelectElement value) = Just value
+matchInputElementUserMultiSelectElement _ = Nothing
+
+-- | Place a checked UserSelectElement in the InputElement role. The receiving parent retains its contextual validation.
+userSelectElementAsInputElement :: UserSelectElement -> InputElement
+userSelectElementAsInputElement = InputElementUserSelectElement
+
+-- | Return the UserSelectElement member when present, otherwise Nothing; no unchecked cast.
+matchInputElementUserSelectElement :: InputElement -> Maybe UserSelectElement
+matchInputElementUserSelectElement (InputElementUserSelectElement value) = Just value
+matchInputElementUserSelectElement _ = Nothing
+
+-- | Closed role: only checked members may enter this position.
+data TextObject = TextObjectMarkdownText MarkdownText | TextObjectPlainText PlainText deriving (Eq, Show)
+
+instance ToJSON TextObject where
+  toJSON (TextObjectMarkdownText value) = toJSON value
+  toJSON (TextObjectPlainText value) = toJSON value
+
+instance FromSlackJSON TextObject where
+  fromSlackJSON value = do
+    tag <- wireTag value
+    case tag of
+      "mrkdwn" -> TextObjectMarkdownText <$> fromSlackJSON value
+      "plain_text" -> TextObjectPlainText <$> fromSlackJSON value
+      _ -> failure TypeMismatch [Field "type"] "role" "unsupported child type"
+
+instance FromJSON TextObject where parseJSON = checkedParser
+
+-- | Place a checked MarkdownText in the TextObject role. The receiving parent retains its contextual validation.
+markdownTextAsText :: MarkdownText -> TextObject
+markdownTextAsText = TextObjectMarkdownText
+
+-- | Return the MarkdownText member when present, otherwise Nothing; no unchecked cast.
+matchTextObjectMarkdownText :: TextObject -> Maybe MarkdownText
+matchTextObjectMarkdownText (TextObjectMarkdownText value) = Just value
+matchTextObjectMarkdownText _ = Nothing
+
+-- | Place a checked PlainText in the TextObject role. The receiving parent retains its contextual validation.
+plainTextAsText :: PlainText -> TextObject
+plainTextAsText = TextObjectPlainText
+
+-- | Return the PlainText member when present, otherwise Nothing; no unchecked cast.
+matchTextObjectPlainText :: TextObject -> Maybe PlainText
+matchTextObjectPlainText (TextObjectPlainText value) = Just value
+matchTextObjectPlainText _ = Nothing
+
+-- | Closed role: only checked members may enter this position.
+data Chart = ChartAreaChart AreaChart | ChartBarChart BarChart | ChartLineChart LineChart | ChartPieChart PieChart deriving (Eq, Show)
+
+instance ToJSON Chart where
+  toJSON (ChartAreaChart value) = toJSON value
+  toJSON (ChartBarChart value) = toJSON value
+  toJSON (ChartLineChart value) = toJSON value
+  toJSON (ChartPieChart value) = toJSON value
+
+instance FromSlackJSON Chart where
+  fromSlackJSON value = do
+    tag <- wireTag value
+    case tag of
+      "area" -> ChartAreaChart <$> fromSlackJSON value
+      "bar" -> ChartBarChart <$> fromSlackJSON value
+      "line" -> ChartLineChart <$> fromSlackJSON value
+      "pie" -> ChartPieChart <$> fromSlackJSON value
+      _ -> failure TypeMismatch [Field "type"] "role" "unsupported child type"
+
+instance FromJSON Chart where parseJSON = checkedParser
+
+-- | Place a checked AreaChart in the Chart role. The receiving parent retains its contextual validation.
+areaChartAsChart :: AreaChart -> Chart
+areaChartAsChart = ChartAreaChart
+
+-- | Return the AreaChart member when present, otherwise Nothing; no unchecked cast.
+matchChartAreaChart :: Chart -> Maybe AreaChart
+matchChartAreaChart (ChartAreaChart value) = Just value
+matchChartAreaChart _ = Nothing
+
+-- | Place a checked BarChart in the Chart role. The receiving parent retains its contextual validation.
+barChartAsChart :: BarChart -> Chart
+barChartAsChart = ChartBarChart
+
+-- | Return the BarChart member when present, otherwise Nothing; no unchecked cast.
+matchChartBarChart :: Chart -> Maybe BarChart
+matchChartBarChart (ChartBarChart value) = Just value
+matchChartBarChart _ = Nothing
+
+-- | Place a checked LineChart in the Chart role. The receiving parent retains its contextual validation.
+lineChartAsChart :: LineChart -> Chart
+lineChartAsChart = ChartLineChart
+
+-- | Return the LineChart member when present, otherwise Nothing; no unchecked cast.
+matchChartLineChart :: Chart -> Maybe LineChart
+matchChartLineChart (ChartLineChart value) = Just value
+matchChartLineChart _ = Nothing
+
+-- | Place a checked PieChart in the Chart role. The receiving parent retains its contextual validation.
+pieChartAsChart :: PieChart -> Chart
+pieChartAsChart = ChartPieChart
+
+-- | Return the PieChart member when present, otherwise Nothing; no unchecked cast.
+matchChartPieChart :: Chart -> Maybe PieChart
+matchChartPieChart (ChartPieChart value) = Just value
+matchChartPieChart _ = Nothing
+
+-- | Closed role: only checked members may enter this position.
+data RichTextBlockElement = RichTextBlockElementRichTextCodeBlock RichTextCodeBlock | RichTextBlockElementRichTextList RichTextList | RichTextBlockElementRichTextQuote RichTextQuote | RichTextBlockElementRichTextSection RichTextSection deriving (Eq, Show)
+
+instance ToJSON RichTextBlockElement where
+  toJSON (RichTextBlockElementRichTextCodeBlock value) = toJSON value
+  toJSON (RichTextBlockElementRichTextList value) = toJSON value
+  toJSON (RichTextBlockElementRichTextQuote value) = toJSON value
+  toJSON (RichTextBlockElementRichTextSection value) = toJSON value
+
+instance FromSlackJSON RichTextBlockElement where
+  fromSlackJSON value = do
+    tag <- wireTag value
+    case tag of
+      "rich_text_preformatted" -> RichTextBlockElementRichTextCodeBlock <$> fromSlackJSON value
+      "rich_text_list" -> RichTextBlockElementRichTextList <$> fromSlackJSON value
+      "rich_text_quote" -> RichTextBlockElementRichTextQuote <$> fromSlackJSON value
+      "rich_text_section" -> RichTextBlockElementRichTextSection <$> fromSlackJSON value
+      _ -> failure TypeMismatch [Field "type"] "role" "unsupported child type"
+
+instance FromJSON RichTextBlockElement where parseJSON = checkedParser
+
+-- | Place a checked RichTextCodeBlock in the RichTextBlockElement role. The receiving parent retains its contextual validation.
+richTextCodeBlockAsRichTextBlockElement :: RichTextCodeBlock -> RichTextBlockElement
+richTextCodeBlockAsRichTextBlockElement = RichTextBlockElementRichTextCodeBlock
+
+-- | Return the RichTextCodeBlock member when present, otherwise Nothing; no unchecked cast.
+matchRichTextBlockElementRichTextCodeBlock :: RichTextBlockElement -> Maybe RichTextCodeBlock
+matchRichTextBlockElementRichTextCodeBlock (RichTextBlockElementRichTextCodeBlock value) = Just value
+matchRichTextBlockElementRichTextCodeBlock _ = Nothing
+
+-- | Place a checked RichTextList in the RichTextBlockElement role. The receiving parent retains its contextual validation.
+richTextListAsRichTextBlockElement :: RichTextList -> RichTextBlockElement
+richTextListAsRichTextBlockElement = RichTextBlockElementRichTextList
+
+-- | Return the RichTextList member when present, otherwise Nothing; no unchecked cast.
+matchRichTextBlockElementRichTextList :: RichTextBlockElement -> Maybe RichTextList
+matchRichTextBlockElementRichTextList (RichTextBlockElementRichTextList value) = Just value
+matchRichTextBlockElementRichTextList _ = Nothing
+
+-- | Place a checked RichTextQuote in the RichTextBlockElement role. The receiving parent retains its contextual validation.
+richTextQuoteAsRichTextBlockElement :: RichTextQuote -> RichTextBlockElement
+richTextQuoteAsRichTextBlockElement = RichTextBlockElementRichTextQuote
+
+-- | Return the RichTextQuote member when present, otherwise Nothing; no unchecked cast.
+matchRichTextBlockElementRichTextQuote :: RichTextBlockElement -> Maybe RichTextQuote
+matchRichTextBlockElementRichTextQuote (RichTextBlockElementRichTextQuote value) = Just value
+matchRichTextBlockElementRichTextQuote _ = Nothing
+
+-- | Place a checked RichTextSection in the RichTextBlockElement role. The receiving parent retains its contextual validation.
+richTextSectionAsRichTextBlockElement :: RichTextSection -> RichTextBlockElement
+richTextSectionAsRichTextBlockElement = RichTextBlockElementRichTextSection
+
+-- | Return the RichTextSection member when present, otherwise Nothing; no unchecked cast.
+matchRichTextBlockElementRichTextSection :: RichTextBlockElement -> Maybe RichTextSection
+matchRichTextBlockElementRichTextSection (RichTextBlockElementRichTextSection value) = Just value
+matchRichTextBlockElementRichTextSection _ = Nothing
+
+-- | Closed role: only checked members may enter this position.
+data RichTextSectionElement = RichTextSectionElementRichTextChannel RichTextChannel | RichTextSectionElementRichTextEmoji RichTextEmoji | RichTextSectionElementRichTextLink RichTextLink | RichTextSectionElementRichTextText RichTextText | RichTextSectionElementRichTextUser RichTextUser | RichTextSectionElementRichTextUserGroup RichTextUserGroup deriving (Eq, Show)
+
+instance ToJSON RichTextSectionElement where
+  toJSON (RichTextSectionElementRichTextChannel value) = toJSON value
+  toJSON (RichTextSectionElementRichTextEmoji value) = toJSON value
+  toJSON (RichTextSectionElementRichTextLink value) = toJSON value
+  toJSON (RichTextSectionElementRichTextText value) = toJSON value
+  toJSON (RichTextSectionElementRichTextUser value) = toJSON value
+  toJSON (RichTextSectionElementRichTextUserGroup value) = toJSON value
+
+instance FromSlackJSON RichTextSectionElement where
+  fromSlackJSON value = do
+    tag <- wireTag value
+    case tag of
+      "channel" -> RichTextSectionElementRichTextChannel <$> fromSlackJSON value
+      "emoji" -> RichTextSectionElementRichTextEmoji <$> fromSlackJSON value
+      "link" -> RichTextSectionElementRichTextLink <$> fromSlackJSON value
+      "text" -> RichTextSectionElementRichTextText <$> fromSlackJSON value
+      "user" -> RichTextSectionElementRichTextUser <$> fromSlackJSON value
+      "usergroup" -> RichTextSectionElementRichTextUserGroup <$> fromSlackJSON value
+      _ -> failure TypeMismatch [Field "type"] "role" "unsupported child type"
+
+instance FromJSON RichTextSectionElement where parseJSON = checkedParser
+
+-- | Place a checked RichTextChannel in the RichTextSectionElement role. The receiving parent retains its contextual validation.
+richTextChannelAsRichTextSectionElement :: RichTextChannel -> RichTextSectionElement
+richTextChannelAsRichTextSectionElement = RichTextSectionElementRichTextChannel
+
+-- | Return the RichTextChannel member when present, otherwise Nothing; no unchecked cast.
+matchRichTextSectionElementRichTextChannel :: RichTextSectionElement -> Maybe RichTextChannel
+matchRichTextSectionElementRichTextChannel (RichTextSectionElementRichTextChannel value) = Just value
+matchRichTextSectionElementRichTextChannel _ = Nothing
+
+-- | Place a checked RichTextEmoji in the RichTextSectionElement role. The receiving parent retains its contextual validation.
+richTextEmojiAsRichTextSectionElement :: RichTextEmoji -> RichTextSectionElement
+richTextEmojiAsRichTextSectionElement = RichTextSectionElementRichTextEmoji
+
+-- | Return the RichTextEmoji member when present, otherwise Nothing; no unchecked cast.
+matchRichTextSectionElementRichTextEmoji :: RichTextSectionElement -> Maybe RichTextEmoji
+matchRichTextSectionElementRichTextEmoji (RichTextSectionElementRichTextEmoji value) = Just value
+matchRichTextSectionElementRichTextEmoji _ = Nothing
+
+-- | Place a checked RichTextLink in the RichTextSectionElement role. The receiving parent retains its contextual validation.
+richTextLinkAsRichTextSectionElement :: RichTextLink -> RichTextSectionElement
+richTextLinkAsRichTextSectionElement = RichTextSectionElementRichTextLink
+
+-- | Return the RichTextLink member when present, otherwise Nothing; no unchecked cast.
+matchRichTextSectionElementRichTextLink :: RichTextSectionElement -> Maybe RichTextLink
+matchRichTextSectionElementRichTextLink (RichTextSectionElementRichTextLink value) = Just value
+matchRichTextSectionElementRichTextLink _ = Nothing
+
+-- | Place a checked RichTextText in the RichTextSectionElement role. The receiving parent retains its contextual validation.
+richTextTextAsRichTextSectionElement :: RichTextText -> RichTextSectionElement
+richTextTextAsRichTextSectionElement = RichTextSectionElementRichTextText
+
+-- | Return the RichTextText member when present, otherwise Nothing; no unchecked cast.
+matchRichTextSectionElementRichTextText :: RichTextSectionElement -> Maybe RichTextText
+matchRichTextSectionElementRichTextText (RichTextSectionElementRichTextText value) = Just value
+matchRichTextSectionElementRichTextText _ = Nothing
+
+-- | Place a checked RichTextUser in the RichTextSectionElement role. The receiving parent retains its contextual validation.
+richTextUserAsRichTextSectionElement :: RichTextUser -> RichTextSectionElement
+richTextUserAsRichTextSectionElement = RichTextSectionElementRichTextUser
+
+-- | Return the RichTextUser member when present, otherwise Nothing; no unchecked cast.
+matchRichTextSectionElementRichTextUser :: RichTextSectionElement -> Maybe RichTextUser
+matchRichTextSectionElementRichTextUser (RichTextSectionElementRichTextUser value) = Just value
+matchRichTextSectionElementRichTextUser _ = Nothing
+
+-- | Place a checked RichTextUserGroup in the RichTextSectionElement role. The receiving parent retains its contextual validation.
+richTextUserGroupAsRichTextSectionElement :: RichTextUserGroup -> RichTextSectionElement
+richTextUserGroupAsRichTextSectionElement = RichTextSectionElementRichTextUserGroup
+
+-- | Return the RichTextUserGroup member when present, otherwise Nothing; no unchecked cast.
+matchRichTextSectionElementRichTextUserGroup :: RichTextSectionElement -> Maybe RichTextUserGroup
+matchRichTextSectionElementRichTextUserGroup (RichTextSectionElementRichTextUserGroup value) = Just value
+matchRichTextSectionElementRichTextUserGroup _ = Nothing
diff --git a/src/Slackblocks/Style.hs b/src/Slackblocks/Style.hs
new file mode 100644
--- /dev/null
+++ b/src/Slackblocks/Style.hs
@@ -0,0 +1,61 @@
+-- | Optional rich-text flags checked by their receiving modeled element.
+module Slackblocks.Style (RichTextStyle (..), richTextStyle) where
+
+import Data.Aeson (FromJSON (..), ToJSON (..), object)
+import Data.Map.Strict qualified as Map
+import Slackblocks.Internal.Wire
+import Slackblocks.Json
+import Slackblocks.Validation
+
+-- | Style flags are optional: explicit false is preserved. Each receiving
+-- element checks its own allowed flags, including flags supplied as false.
+data RichTextStyle = RichTextStyle
+  { -- | Bold text. Nothing omits the flag.
+    styleBold :: Maybe Bool,
+    -- | Italic text. Nothing omits the flag.
+    styleItalic :: Maybe Bool,
+    -- | Strikethrough text. Nothing omits the flag.
+    styleStrike :: Maybe Bool,
+    -- | Inline code, where accepted by the receiving element. Nothing omits the flag.
+    styleCode :: Maybe Bool,
+    -- | Highlight a supported mention. Nothing omits the flag.
+    styleHighlight :: Maybe Bool,
+    -- | Client-specific highlighting on supported mentions. Nothing omits the flag.
+    styleClientHighlight :: Maybe Bool,
+    -- | Disable linking on a supported mention. Nothing omits the flag.
+    styleUnlink :: Maybe Bool
+  }
+  deriving (Eq, Show)
+
+-- | Empty style with every flag absent. Explicit Just False remains present; the receiving element validates allowed flags.
+richTextStyle :: RichTextStyle
+richTextStyle = RichTextStyle Nothing Nothing Nothing Nothing Nothing Nothing Nothing
+
+instance ToJSON RichTextStyle where
+  toJSON value =
+    object $
+      optional "bold" (styleBold value)
+        <> optional "italic" (styleItalic value)
+        <> optional "strike" (styleStrike value)
+        <> optional "code" (styleCode value)
+        <> optional "highlight" (styleHighlight value)
+        <> optional "client_highlight" (styleClientHighlight value)
+        <> optional "unlink" (styleUnlink value)
+
+instance FromSlackJSON RichTextStyle where
+  fromSlackJSON value = do
+    fields <- wireObject "" value
+    let unknown = remaining ["bold", "italic", "strike", "code", "highlight", "client_highlight", "unlink"] fields
+    case Map.keys unknown of
+      [] -> pure ()
+      key : _ -> failure TypeMismatch [Field key] "style.flags" "unknown style flag"
+    RichTextStyle
+      <$> optionalField "bold" fields
+      <*> optionalField "italic" fields
+      <*> optionalField "strike" fields
+      <*> optionalField "code" fields
+      <*> optionalField "highlight" fields
+      <*> optionalField "client_highlight" fields
+      <*> optionalField "unlink" fields
+
+instance FromJSON RichTextStyle where parseJSON = checkedParser
diff --git a/src/Slackblocks/Validation.hs b/src/Slackblocks/Validation.hs
new file mode 100644
--- /dev/null
+++ b/src/Slackblocks/Validation.hs
@@ -0,0 +1,90 @@
+-- | Stable validation categories and paths shared with the other implementations.
+module Slackblocks.Validation
+  ( ErrorCategory (..),
+    PathPiece (..),
+    ValidationError (..),
+    Result,
+    categoryName,
+    renderPath,
+    failure,
+    atField,
+    atIndex,
+  )
+where
+
+import Data.List.NonEmpty (NonEmpty (..))
+import Data.Text (Text)
+import Data.Text qualified as T
+
+-- | Normative shared categories; categoryName returns the stable wire spelling.
+data ErrorCategory
+  = -- | Text or collection exceeds a shared limit.
+    LengthExceeded
+  | -- | A number or count is outside the accepted range.
+    OutOfRange
+  | -- | Fields cannot be supplied together.
+    MutuallyExclusive
+  | -- | The value has an unsupported type or vocabulary.
+    TypeMismatch
+  | -- | A required value is absent.
+    MissingRequired
+  | -- | A combination or receiving context is unsupported.
+    InvalidUsage
+  deriving (Eq, Ord, Show, Enum, Bounded)
+
+-- | A field or zero-based array index in a validation path.
+data PathPiece
+  = -- | Object field name.
+    Field Text
+  | -- | Zero-based array position.
+    Index Int
+  deriving (Eq, Ord, Show)
+
+-- | One validation failure with a machine-readable category, path and constraint.
+data ValidationError = ValidationError
+  { -- | Normative shared category.
+    errorCategory :: ErrorCategory,
+    -- | Structured path from the root payload.
+    errorPath :: [PathPiece],
+    -- | Stable constraint identifier.
+    errorConstraint :: Text,
+    -- | Human-readable explanation; do not use prose as a compatibility key.
+    errorMessage :: Text
+  }
+  deriving (Eq, Show)
+
+-- | Checked construction result. Failure contains at least one validation error.
+type Result a = Either (NonEmpty ValidationError) a
+
+-- | Stable shared category spelling suitable for logging and matching.
+categoryName :: ErrorCategory -> Text
+categoryName LengthExceeded = "length-exceeded"
+categoryName OutOfRange = "out-of-range"
+categoryName MutuallyExclusive = "mutually-exclusive"
+categoryName TypeMismatch = "type-mismatch"
+categoryName MissingRequired = "missing-required"
+categoryName InvalidUsage = "invalid-usage"
+
+-- | Format a path starting at $, with dot fields and zero-based bracket indexes.
+renderPath :: [PathPiece] -> Text
+renderPath = ("$" <>) . T.concat . map render
+  where
+    render (Field name) = "." <> name
+    render (Index index) = "[" <> T.pack (show index) <> "]"
+
+-- | Create one failure without throwing an exception.
+failure :: ErrorCategory -> [PathPiece] -> Text -> Text -> Result a
+failure category path constraint message =
+  Left (ValidationError category path constraint message :| [])
+
+-- | Prefix every error path with a field; preserve successful values.
+atField :: Text -> Result a -> Result a
+atField name = either (Left . fmap prepend) Right
+  where
+    prepend err = err {errorPath = Field name : errorPath err}
+
+-- | Prefix every error path with a zero-based index; preserve successful values.
+atIndex :: Int -> Result a -> Result a
+atIndex index = either (Left . fmap prepend) Right
+  where
+    prepend err = err {errorPath = Index index : errorPath err}
diff --git a/test/Adversarial.hs b/test/Adversarial.hs
new file mode 100644
--- /dev/null
+++ b/test/Adversarial.hs
@@ -0,0 +1,70 @@
+module Main (main) where
+
+import Control.Exception (evaluate)
+import Control.Monad (forM_, unless)
+import Data.Aeson qualified as A
+import Data.ByteString.Lazy qualified as L
+import Data.List.NonEmpty qualified as NE
+import Data.Map.Strict qualified as Map
+import Data.Text qualified as T
+import Slackblocks
+
+assert :: String -> Bool -> IO ()
+assert label condition = unless condition (fail label)
+
+accept :: (Show e) => Either e a -> IO a
+accept = either (fail . show) pure
+
+reject :: (FromSlackJSON a) => String -> ErrorCategory -> L.ByteString -> (a -> ()) -> IO ()
+reject label category bytes witness = case decode bytes of
+  Right value -> witness value `seq` fail (label <> " accepted")
+  Left errors -> do
+    diagnosticLength <- evaluate (length (show errors))
+    assert (label <> ": diagnostics") (diagnosticLength > 0)
+    assert (label <> ": category " <> show errors) (any ((== category) . errorCategory) (NE.toList errors))
+
+main :: IO ()
+main = do
+  reject "untagged object cannot silently discard type" InvalidUsage "{\"channel\":\"C123\",\"type\":\"future\"}" (const () :: MessagePayload -> ())
+  reject "empty section fields with text" MissingRequired "{\"type\":\"section\",\"text\":\"ok\",\"fields\":[]}" (const () :: SectionBlock -> ())
+  reject "empty card" MissingRequired "{\"type\":\"card\"}" (const () :: CardBlock -> ())
+  reject "two card icon sources" MutuallyExclusive "{\"type\":\"card\",\"body\":\"body\",\"slack_icon\":{\"type\":\"icon\",\"name\":\"check\"},\"icon\":{\"type\":\"image\",\"image_url\":\"x\",\"alt_text\":\"x\"}}" (const () :: CardBlock -> ())
+  reject "container missing title" MissingRequired "{\"type\":\"container\",\"child_blocks\":[{\"type\":\"divider\"}]}" (const () :: ContainerBlock -> ())
+  reject "collapsed noncollapsible container" InvalidUsage "{\"type\":\"container\",\"title\":\"Title\",\"child_blocks\":[{\"type\":\"divider\"}],\"default_collapsed\":true}" (const () :: ContainerBlock -> ())
+  reject "collapsible header divider" InvalidUsage "{\"type\":\"container\",\"title\":\"Title\",\"child_blocks\":[{\"type\":\"divider\"}],\"is_collapsible\":true,\"has_header_divider\":true}" (const () :: ContainerBlock -> ())
+  reject "empty Slack file" MutuallyExclusive "{}" (const () :: SlackFile -> ())
+  reject "empty conversation filter" MissingRequired "{}" (const () :: ConversationFilter -> ())
+  reject "duplicate axis categories" InvalidUsage "{\"categories\":[\"a\",\"a\"]}" (const () :: AxisConfig -> ())
+  reject "bad attachment color" TypeMismatch "{\"color\":\"#GG0000\",\"blocks\":[{\"type\":\"divider\"}]}" (const () :: Attachment -> ())
+  reject "attachment surface" TypeMismatch "{\"blocks\":[{\"type\":\"alert\",\"text\":\"Alert\"}]}" (const () :: Attachment -> ())
+  reject "unequal table row widths" InvalidUsage "{\"type\":\"data_table\",\"caption\":\"Table\",\"rows\":[[{\"type\":\"raw_text\",\"text\":\"A\"}],[{\"type\":\"raw_text\",\"text\":\"B\"},{\"type\":\"raw_text\",\"text\":\"C\"}]]}" (const () :: DataTableBlock -> ())
+  reject "rich text table header" TypeMismatch "{\"type\":\"data_table\",\"caption\":\"Table\",\"rows\":[[{\"type\":\"rich_text\",\"elements\":[{\"type\":\"rich_text_section\",\"elements\":[{\"type\":\"text\",\"text\":\"header\"}]}]}],[{\"type\":\"raw_text\",\"text\":\"Body\"}]]}" (const () :: DataTableBlock -> ())
+  reject "table column settings extension" InvalidUsage "{\"type\":\"data_table\",\"caption\":\"Table\",\"column_settings\":[],\"rows\":[[{\"type\":\"raw_text\",\"text\":\"A\"}],[{\"type\":\"raw_text\",\"text\":\"Body\"}]]}" (const () :: DataTableBlock -> ())
+  reject "duplicate chart series names" InvalidUsage "{\"type\":\"line\",\"axis_config\":{\"categories\":[\"a\"]},\"series\":[{\"name\":\"s\",\"data\":[{\"label\":\"a\",\"value\":1}]},{\"name\":\"s\",\"data\":[{\"label\":\"a\",\"value\":2}]}]}" (const () :: LineChart -> ())
+  forM_ ["24:00", "23:60", "23:5x", "x3:59", "23-59"] $ \value ->
+    reject "invalid time" TypeMismatch (A.encode (A.object ["type" A..= A.String "timepicker", "initial_time" A..= (value :: T.Text)])) (const () :: TimePickerElement -> ())
+  forM_ [("-1", "1"), ("-1", "-1.0"), ("-2", "-1"), ("1e+2", "100"), ("0", "-0")] $ \(lo, hi) -> do
+    number <- accept $ buildNumberInputElement ((numberInputElement True) {numberInputElementMinValue = Just lo, numberInputElementMaxValue = Just hi})
+    assert "exact decimal bounds preserved" (numberInputElementMinValue (inspectNumberInputElement number) == Just lo)
+  reject "repeated exponent" TypeMismatch "{\"type\":\"number_input\",\"is_decimal_allowed\":true,\"min_value\":\"1e2e3\"}" (const () :: NumberInputElement -> ())
+  -- A nested rich-text table exercises content accounting through lists, links
+  -- and non-text items. Metadata that resembles content stays opaque.
+  let table = "{\"type\":\"data_table\",\"caption\":\"Table\",\"rows\":[[{\"type\":\"raw_text\",\"text\":\"Header\"}],[{\"type\":\"rich_text\",\"elements\":[{\"type\":\"rich_text_list\",\"style\":\"bullet\",\"elements\":[{\"type\":\"rich_text_section\",\"elements\":[{\"type\":\"text\",\"text\":\"Body\"},{\"type\":\"link\",\"url\":\"https://example.com\",\"text\":\"Link\"},{\"type\":\"emoji\",\"name\":\"wave\"}]}]}]}]]}"
+  checked <- accept (decode table :: Result DataTableBlock)
+  title <- accept (buildPlainText (plainText "Nested"))
+  inner <- accept (buildContainerBlock (accordionSection title [dataTableBlockAsBlock checked]))
+  outer <- accept (buildContainerBlock (accordionSection title [containerBlockAsBlock inner]))
+  message <- accept (buildMessagePayload ((messagePayload "C123") {messagePayloadBlocks = Just [containerBlockAsBlock outer]}))
+  assert "nested message roundtrip" (decode (A.encode message) == Right message)
+  richTitle <- accept (decode "{\"type\":\"rich_text\",\"elements\":[{\"type\":\"rich_text_section\",\"elements\":[{\"type\":\"text\",\"text\":\"Title\"}]}]}" :: Result RichTextBlock)
+  divider <- accept (buildDividerBlock dividerBlock)
+  _ <- accept (buildContainerBlock ((containerBlock [dividerBlockAsBlock divider]) {containerBlockRichTextTitle = Just richTitle}))
+  forM_ ["{\"exclude_external_shared_channels\":false}", "{\"exclude_bot_users\":false}"] $ \bytes -> do
+    value <- accept (decode bytes :: Result ConversationFilter)
+    assert "explicit false is content" (decode (A.encode value) == Right value)
+  -- Verify Aeson's standard instance adapters in both directions, separately
+  -- from the stricter duplicate-aware byte decoder.
+  extended <- accept (buildPlainText ((plainText "text") {plainTextExtensions = Map.singleton "future" (A.Bool False)}))
+  assert "extension roundtrip through Aeson" (A.eitherDecode (A.encode extended) == Right extended)
+  assert "Aeson checked failure" (case A.eitherDecode "{\"type\":\"plain_text\",\"text\":\"\"}" :: Either String PlainText of Left errorText -> not (null errorText); Right _ -> False)
+  putStrLn "Adversarial relationships, nested accounting, exact bounds and Aeson adapters passed."
diff --git a/test/Boundaries.hs b/test/Boundaries.hs
new file mode 100644
--- /dev/null
+++ b/test/Boundaries.hs
@@ -0,0 +1,85 @@
+module Main (main) where
+
+import Control.Exception (evaluate)
+import Control.Monad (forM_, unless)
+import Data.Aeson (ToJSON (..), Value (..), object, (.=))
+import Data.List.NonEmpty qualified as NE
+import Data.Map.Strict qualified as Map
+import Data.Text qualified as T
+import Slackblocks
+
+assert :: String -> Bool -> IO ()
+assert label condition = unless condition (fail label)
+
+accept :: (Show e) => Either e a -> IO a
+accept = either (fail . show) pure
+
+reject :: String -> ErrorCategory -> [PathPiece] -> Result a -> IO ()
+reject label category path value = case value of
+  Right _ -> fail (label <> ": unexpectedly accepted")
+  Left errors -> do
+    diagnosticLength <- evaluate (length (show errors))
+    assert (label <> ": empty diagnostics") (diagnosticLength > 0)
+    assert (label <> ": wrong category/path: " <> show errors) $
+      any (\e -> errorCategory e == category && errorPath e == path) (NE.toList errors)
+
+main :: IO ()
+main = do
+  let pending = taskCardBlock "task" "Waiting" TaskStatusPending
+      pendingWire = object ["type" .= String "task_card", "task_id" .= String "task", "title" .= String "Waiting", "status" .= String "pending"]
+  reject "standalone pending construction" TypeMismatch [Field "status"] (buildTaskCardBlock pending)
+  reject "standalone pending ingress" TypeMismatch [Field "status"] (fromValue pendingWire :: Result TaskCardBlock)
+  reject "standalone role ingress" TypeMismatch [Field "status"] (fromValue pendingWire :: Result Block)
+  task <- accept (buildPlanTask pending)
+  plan <- accept (buildPlanBlock (planBlock "Plan" [task]))
+  assert "pending task only has plan wire form" $
+    toJSON task
+      == object
+        ["task_id" .= String "task", "title" .= String "Waiting", "status" .= String "pending"]
+  accept (fromValue (toJSON plan) :: Result PlanBlock) >>= assert "plan roundtrip" . (== plan)
+  reject "duplicate plan task IDs" InvalidUsage [Field "tasks"] (buildPlanBlock (planBlock "Plan" [task, task]))
+  reject "reserved extension" InvalidUsage [Field "text"] $
+    buildPlainText ((plainText "safe") {plainTextExtensions = Map.singleton "text" (String "unchecked")})
+  reject
+    "unknown style"
+    TypeMismatch
+    [Field "mystery"]
+    (fromValue (object ["mystery" .= False]) :: Result RichTextStyle)
+  reject "style false still unsupported" TypeMismatch [Field "style", Field "highlight"] $
+    buildRichTextText ((richTextText "Text") {richTextTextStyle = Just (richTextStyle {styleHighlight = Just False})})
+  forM_ ["NaN", "Infinity", " 1", "1 ", "1e", "--1", "١", "1.2.3"] $ \bound ->
+    reject ("bad decimal " <> T.unpack bound) TypeMismatch [Field "min_value"] $
+      buildNumberInputElement ((numberInputElement True) {numberInputElementMinValue = Just bound})
+  forM_
+    [ ("0.100000000000000000000000000001", "0.100000000000000000000000000002"),
+      ("-1e99999999999999999999999", "-1e99999999999999999999998"),
+      ("-0", "0.0"),
+      ("+1.0e0", "1"),
+      (".5", "1.")
+    ]
+    $ \(lo, hi) -> do
+      value <-
+        accept $
+          buildNumberInputElement
+            ( (numberInputElement True)
+                { numberInputElementMinValue = Just lo,
+                  numberInputElementMaxValue = Just hi
+                }
+            )
+      accept (fromValue (toJSON value) :: Result NumberInputElement) >>= assert "exact decimal roundtrip" . (== value)
+  reject "nearby exact inverted decimals" OutOfRange [] $
+    buildNumberInputElement
+      ( (numberInputElement True)
+          { numberInputElementMinValue = Just "0.100000000000000000000000000002",
+            numberInputElementMaxValue = Just "0.100000000000000000000000000001"
+          }
+      )
+  let opaque = object ["type" .= String "data_visualization", "blocks" .= [pendingWire], "integer" .= (9007199254740993 :: Integer)]
+  webhook <- accept $ buildWebhookMessage (webhookMessage {webhookMessageMetadata = Just (Map.singleton "event_payload" opaque)})
+  accept (fromValue (toJSON webhook) :: Result WebhookMessage) >>= assert "metadata remains opaque and exact" . (== webhook)
+  reject
+    "nested path"
+    LengthExceeded
+    [Field "blocks", Index 0, Field "text", Field "text"]
+    (fromValue (object ["channel" .= String "C123", "blocks" .= [object ["type" .= String "header", "text" .= object ["type" .= String "plain_text", "text" .= String ""]]]]) :: Result MessagePayload)
+  putStrLn "Native/ingress context, exact-decimal, metadata, extension, style and error-path boundaries passed."
diff --git a/test/Conformance.hs b/test/Conformance.hs
new file mode 100644
--- /dev/null
+++ b/test/Conformance.hs
@@ -0,0 +1,104 @@
+module Main (main) where
+
+import Constructions (invalidCases, validCases)
+import Control.Monad (forM, unless)
+import Data.Aeson (FromJSON (..), Value (..), eitherDecodeFileStrict, withObject, (.:))
+import Data.Aeson.Key qualified as Key
+import Data.Aeson.KeyMap qualified as KM
+import Data.List.NonEmpty qualified as NE
+import Data.Set qualified as Set
+import Data.Text (Text)
+import Data.Text qualified as T
+import Paths_slackblocks (getDataFileName)
+import Slackblocks
+
+newtype Fixture = Fixture String
+
+instance FromJSON Fixture where
+  parseJSON = withObject "Fixture" (fmap Fixture . (.: "id"))
+
+newtype Manifest = Manifest [Fixture]
+
+instance FromJSON Manifest where
+  parseJSON = withObject "Manifest" (fmap Manifest . (.: "fixtures"))
+
+data Invalid = Invalid String Text Text
+
+instance FromJSON Invalid where
+  parseJSON = withObject "Invalid case" $ \o -> Invalid <$> o .: "id" <*> o .: "category" <*> o .: "constraint"
+
+newtype InvalidManifest = InvalidManifest [Invalid]
+
+instance FromJSON InvalidManifest where
+  parseJSON = withObject "Invalid manifest" (fmap InvalidManifest . (.: "cases"))
+
+readData :: (FromJSON a) => FilePath -> IO a
+readData name = do
+  path <- getDataFileName ("test/spec/" <> name)
+  result <- eitherDecodeFileStrict path
+  either fail pure result
+
+inventory :: String -> [String] -> [String] -> IO ()
+inventory name expected actual = do
+  unless
+    ( not (null expected)
+        && length actual == Set.size (Set.fromList actual)
+        && Set.fromList expected == Set.fromList actual
+    )
+    $ fail
+      ( name
+          <> " inventory mismatch: missing "
+          <> show (Set.fromList expected Set.\\ Set.fromList actual)
+          <> "; unexpected "
+          <> show (Set.fromList actual Set.\\ Set.fromList expected)
+      )
+
+checkValue :: String -> Value -> Result Value -> [String]
+checkValue name expected actual = case actual of
+  Left errors -> [name <> ": rejected: " <> show errors]
+  Right value
+    | value == expected -> []
+    | otherwise -> [name <> ": wire mismatch\nexpected " <> show expected <> "\nactual " <> show value]
+
+checkInvalid :: String -> Text -> Result () -> [String]
+checkInvalid name expected actual = case actual of
+  Right () -> [name <> ": accepted invalid value"]
+  Left errors
+    | any ((== expected) . categoryName . errorCategory) (NE.toList errors)
+        && all (\e -> not (T.null (errorConstraint e)) && not (T.null (errorMessage e)) && not (T.null (renderPath (errorPath e)))) errors ->
+        []
+    | otherwise -> [name <> ": expected " <> T.unpack expected <> "; got " <> show errors]
+
+main :: IO ()
+main = do
+  Manifest fixtures <- readData "manifest.json"
+  InvalidManifest invalid <- readData "fixtures/invalid/manifest.json"
+  inventory "valid" [name | Fixture name <- fixtures] [name | (name, _, _) <- validCases]
+  inventory "invalid" [name | Invalid name _ _ <- invalid] [name | (name, _, _) <- invalidCases]
+  limits <- readData "limits.json"
+  let scalarPaths prefix (Object fields) =
+        concat
+          [scalarPaths (if T.null prefix then Key.toText key else prefix <> "." <> Key.toText key) value | (key, value) <- KM.toList fields]
+      scalarPaths prefix (Number _) = [prefix]
+      scalarPaths _ _ = error "Non-numeric shared limit"
+      missing = Set.fromList (scalarPaths "" limits) Set.\\ Set.fromList [constraint | Invalid _ _ constraint <- invalid]
+  unless (Set.null missing) (fail ("Scalar limits missing invalid cases: " <> show missing))
+  validFailures <- forM validCases $ \(name, native, decoder) -> do
+    expected <- readData ("fixtures/valid/" <> name <> ".json")
+    pure (checkValue (name <> " native") expected native <> checkValue (name <> " ingress") expected (decoder expected))
+  invalidFailures <- forM invalidCases $ \(name, native, ingress) ->
+    case [category | Invalid identifier category _ <- invalid, identifier == name] of
+      [category] -> pure (maybe [] (checkInvalid (name <> " native") category) native <> checkInvalid (name <> " ingress") category ingress)
+      _ -> fail ("Invalid manifest category missing or duplicated: " <> name)
+  let failures = concat (validFailures <> invalidFailures)
+  mapM_ putStrLn failures
+  unless (null failures) (fail (show (length failures) <> " conformance checks failed"))
+  let nativeCount = length [() | (_, Just _, _) <- invalidCases]
+  putStrLn
+    ( show (length validCases)
+        <> " valid native/ingress pairs; "
+        <> show (length invalidCases)
+        <> " invalid ingress cases and "
+        <> show nativeCount
+        <> " native invalid constructions passed. Eight unrepresentable inputs have compiler rejection controls."
+    )
diff --git a/test/Constructions.hs b/test/Constructions.hs
new file mode 100644
--- /dev/null
+++ b/test/Constructions.hs
@@ -0,0 +1,2337 @@
+-- Independent native public-API examples; never regenerated by the model generator.
+module Constructions (validCases, invalidCases) where
+
+import Control.Monad (void)
+import Data.Aeson (ToJSON (..), Value (..), object, (.=))
+import Data.Map.Strict qualified as Map
+import Data.Text qualified as T
+import Slackblocks
+
+valid0 :: Result Value
+valid0 = do
+  v0 <- buildMarkdownText ((markdownText "I like pretty colours") {markdownTextVerbatim = Nothing})
+  v1 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v0), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "fake_block_id_0"})
+  v2 <- buildMarkdownText ((markdownText "I don't like pretty colours") {markdownTextVerbatim = Nothing})
+  v3 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v2), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "fake_block_id_1"})
+  v4 <- buildAttachment ((attachment [sectionBlockAsBlock v1, sectionBlockAsBlock v3]) {attachmentColor = Just "#8800ff", attachmentFallback = Nothing})
+  pure (toJSON v4)
+
+valid1 :: Result Value
+valid1 = do
+  v0 <- buildMarkdownText ((markdownText "I like pretty colours") {markdownTextVerbatim = Nothing})
+  v1 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v0), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "fake_block_id"})
+  v2 <- buildAttachment ((attachment [sectionBlockAsBlock v1]) {attachmentColor = Just "#000000", attachmentFallback = Just "Colours preference"})
+  pure (toJSON v2)
+
+valid2 :: Result Value
+valid2 = do
+  v0 <- buildMarkdownText ((markdownText "*a*") {markdownTextVerbatim = Nothing})
+  v1 <- buildPlainText ((plainText "*a*") {plainTextEmoji = Nothing})
+  v2 <- buildSelectOption ((selectOption (markdownTextAsText v0) "a") {selectOptionDescription = Just (plainTextAsText v1), selectOptionUrl = Nothing})
+  v3 <- buildMarkdownText ((markdownText "*b*") {markdownTextVerbatim = Nothing})
+  v4 <- buildPlainText ((plainText "*b*") {plainTextEmoji = Nothing})
+  v5 <- buildSelectOption ((selectOption (markdownTextAsText v3) "b") {selectOptionDescription = Just (plainTextAsText v4), selectOptionUrl = Nothing})
+  v6 <- buildMarkdownText ((markdownText "*c*") {markdownTextVerbatim = Nothing})
+  v7 <- buildPlainText ((plainText "*c*") {plainTextEmoji = Nothing})
+  v8 <- buildSelectOption ((selectOption (markdownTextAsText v6) "c") {selectOptionDescription = Just (plainTextAsText v7), selectOptionUrl = Nothing})
+  v9 <- buildCheckboxesElement ((checkboxesElement [v2, v5, v8]) {checkboxesElementActionId = Just "actionId-0", checkboxesElementInitialOptions = Nothing, checkboxesElementConfirm = Nothing, checkboxesElementFocusOnLoad = Nothing})
+  v10 <- buildActionsBlock ((actionsBlock [checkboxesElementAsElement v9]) {actionsBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v10)
+
+valid3 :: Result Value
+valid3 = do
+  v0 <- buildMarkdownText ((markdownText "The work is mysterious and important.") {markdownTextVerbatim = Nothing})
+  v1 <- buildAlertBlock ((alertBlock (markdownTextAsText v0)) {alertBlockLevel = Just AlertLevelInfo, alertBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v1)
+
+valid4 :: Result Value
+valid4 = do
+  v0 <- buildImageElement ((imageElement "Sample hero image") {imageElementImageUrl = Just "https://picsum.photos/400/300", imageElementSlackFile = Nothing})
+  v1 <- buildMarkdownText ((markdownText "Lumon Industries") {markdownTextVerbatim = Nothing})
+  v2 <- buildMarkdownText ((markdownText "Committed to work-life balance") {markdownTextVerbatim = Nothing})
+  v3 <- buildMarkdownText ((markdownText "Please enjoy each card equally.") {markdownTextVerbatim = Nothing})
+  v4 <- buildPlainText ((plainText "Action Button") {plainTextEmoji = Nothing})
+  v5 <- buildButtonElement ((buttonElement v4) {buttonElementActionId = Just "button_action", buttonElementUrl = Nothing, buttonElementValue = Nothing, buttonElementStyle = Nothing, buttonElementConfirm = Nothing, buttonElementAccessibilityLabel = Nothing, buttonElementAgentPrompt = Nothing, buttonElementAgentPromptDisplay = Nothing})
+  v6 <- buildSlackIcon (slackIcon "bot")
+  v7 <- buildMarkdownText ((markdownText "A card assembled by slackblocks.") {markdownTextVerbatim = Nothing})
+  v8 <- buildCardBlock (cardBlock {cardBlockHeroImage = Just v0, cardBlockIcon = Nothing, cardBlockTitle = Just (markdownTextAsText v1), cardBlockSubtitle = Just (markdownTextAsText v2), cardBlockBody = Just (markdownTextAsText v3), cardBlockActions = Just [v5], cardBlockSlackIcon = Just v6, cardBlockSubtext = Just (markdownTextAsText v7), cardBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v8)
+
+valid5 :: Result Value
+valid5 = do
+  v0 <- buildMarkdownText ((markdownText "First result") {markdownTextVerbatim = Nothing})
+  v1 <- buildCardBlock (cardBlock {cardBlockHeroImage = Nothing, cardBlockIcon = Nothing, cardBlockTitle = Just (markdownTextAsText v0), cardBlockSubtitle = Nothing, cardBlockBody = Nothing, cardBlockActions = Nothing, cardBlockSlackIcon = Nothing, cardBlockSubtext = Nothing, cardBlockBlockId = Just "card_1"})
+  v2 <- buildMarkdownText ((markdownText "Second result") {markdownTextVerbatim = Nothing})
+  v3 <- buildCardBlock (cardBlock {cardBlockHeroImage = Nothing, cardBlockIcon = Nothing, cardBlockTitle = Just (markdownTextAsText v2), cardBlockSubtitle = Nothing, cardBlockBody = Nothing, cardBlockActions = Nothing, cardBlockSlackIcon = Nothing, cardBlockSubtext = Nothing, cardBlockBlockId = Just "card_2"})
+  v4 <- buildCarouselBlock ((carouselBlock [v1, v3]) {carouselBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v4)
+
+valid6 :: Result Value
+valid6 = do
+  v0 <- buildMarkdownText ((markdownText "All systems operational.") {markdownTextVerbatim = Nothing})
+  v1 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v0), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "child_1"})
+  v2 <- buildPlainText ((plainText "Deployment summary") {plainTextEmoji = Nothing})
+  v3 <- buildMarkdownText ((markdownText "Production is healthy") {markdownTextVerbatim = Nothing})
+  v4 <- buildContainerBlock ((containerBlock [sectionBlockAsBlock v1]) {containerBlockTitle = Just v2, containerBlockRichTextTitle = Nothing, containerBlockSubtitle = Just (markdownTextAsText v3), containerBlockWidth = Just ContainerWidthStandard, containerBlockIcon = Nothing, containerBlockIsCollapsible = Just False, containerBlockDefaultCollapsed = Just False, containerBlockHasHeaderDivider = Just True, containerBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v4)
+
+valid7 :: Result Value
+valid7 = do
+  v0 <- buildPlainText ((plainText "Good") {plainTextEmoji = Nothing})
+  v1 <- buildFeedbackButton ((feedbackButton v0 "positive_feedback") {feedbackButtonAccessibilityLabel = Just "Mark this response as good"})
+  v2 <- buildPlainText ((plainText "Bad") {plainTextEmoji = Nothing})
+  v3 <- buildFeedbackButton ((feedbackButton v2 "negative_feedback") {feedbackButtonAccessibilityLabel = Just "Mark this response as bad"})
+  v4 <- buildFeedbackButtonsElement ((feedbackButtonsElement v1 v3) {feedbackButtonsElementActionId = Just "feedback_buttons_1"})
+  v5 <- buildContextActionsBlock ((contextActionsBlock [feedbackButtonsElementAsContextActionsElement v4]) {contextActionsBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v5)
+
+valid8 :: Result Value
+valid8 = do
+  v0 <- buildPlainText ((plainText "Delete") {plainTextEmoji = Nothing})
+  v1 <- buildIconButtonElement ((iconButtonElement v0 IconButtonIconTrash) {iconButtonElementActionId = Just "delete_button", iconButtonElementValue = Just "delete_item", iconButtonElementConfirm = Nothing, iconButtonElementAccessibilityLabel = Nothing, iconButtonElementVisibleToUserIds = Nothing})
+  v2 <- buildContextActionsBlock ((contextActionsBlock [iconButtonElementAsContextActionsElement v1]) {contextActionsBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v2)
+
+valid9 :: Result Value
+valid9 = do
+  v0 <- buildMarkdownText ((markdownText "Hello, world!") {markdownTextVerbatim = Nothing})
+  v1 <- buildContextBlock ((contextBlock [markdownTextAsContextElement v0]) {contextBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v1)
+
+valid10 :: Result Value
+valid10 = do
+  v0 <- buildRawText (rawText "Name")
+  v1 <- buildRawText (rawText "Score")
+  v2 <- buildRawText (rawText "Alice")
+  v3 <- buildRawNumber (rawNumber 42 "42")
+  v4 <- buildDataTableBlock ((dataTableBlock [[rawTextAsDataTableCell v0, rawTextAsDataTableCell v1], [rawTextAsDataTableCell v2, rawNumberAsDataTableCell v3]] "Team scores") {dataTableBlockPageSize = Just 5, dataTableBlockRowHeaderColumnIndex = Just 0, dataTableBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v4)
+
+valid11 :: Result Value
+valid11 = do
+  v0 <- buildDataPoint (dataPoint "Mon" 12000)
+  v1 <- buildDataPoint (dataPoint "Tue" 13500)
+  v2 <- buildDataSeries (dataSeries "Free Tier" [v0, v1])
+  v3 <- buildAxisConfig ((axisConfig ["Mon", "Tue"]) {axisConfigXLabel = Just "Day", axisConfigYLabel = Just "Users"})
+  v4 <- buildAreaChart (areaChart [v2] v3)
+  v5 <- buildDataVisualizationBlock ((dataVisualizationBlock "Daily Active Users" (areaChartAsChart v4)) {dataVisualizationBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v5)
+
+valid12 :: Result Value
+valid12 = do
+  v0 <- buildDataPoint (dataPoint "Pumpkin" 70)
+  v1 <- buildDataPoint (dataPoint "Blueberry" 90)
+  v2 <- buildDataSeries (dataSeries "Pies" [v0, v1])
+  v3 <- buildAxisConfig ((axisConfig ["Pumpkin", "Blueberry"]) {axisConfigXLabel = Just "Pies", axisConfigYLabel = Just "Tastiness"})
+  v4 <- buildBarChart (barChart [v2] v3)
+  v5 <- buildDataVisualizationBlock ((dataVisualizationBlock "Pies by Tastiness" (barChartAsChart v4)) {dataVisualizationBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v5)
+
+valid13 :: Result Value
+valid13 = do
+  v0 <- buildDataPoint (dataPoint "Week 1" 32000)
+  v1 <- buildDataPoint (dataPoint "Week 2" 35000)
+  v2 <- buildDataSeries (dataSeries "Website" [v0, v1])
+  v3 <- buildDataPoint (dataPoint "Week 1" 28000)
+  v4 <- buildDataPoint (dataPoint "Week 2" 31000)
+  v5 <- buildDataSeries (dataSeries "In-store" [v3, v4])
+  v6 <- buildAxisConfig ((axisConfig ["Week 1", "Week 2"]) {axisConfigXLabel = Just "Week", axisConfigYLabel = Just "Paper Sales (USD)"})
+  v7 <- buildLineChart (lineChart [v2, v5] v6)
+  v8 <- buildDataVisualizationBlock ((dataVisualizationBlock "Weekly Paper Sales" (lineChartAsChart v7)) {dataVisualizationBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v8)
+
+valid14 :: Result Value
+valid14 = do
+  v0 <- buildChartSegment (chartSegment "Kit Kat" 45)
+  v1 <- buildChartSegment (chartSegment "Twix" 28)
+  v2 <- buildChartSegment (chartSegment "Crunch" 18)
+  v3 <- buildChartSegment (chartSegment "Milky Way" 9)
+  v4 <- buildPieChart (pieChart [v0, v1, v2, v3])
+  v5 <- buildDataVisualizationBlock ((dataVisualizationBlock "My Favorite Candy Bars" (pieChartAsChart v4)) {dataVisualizationBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v5)
+
+valid15 :: Result Value
+valid15 = do
+  v0 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v0)
+
+valid16 :: Result Value
+valid16 = do
+  v0 <- buildFileBlock ((fileBlock "external_id" "remote") {fileBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v0)
+
+valid17 :: Result Value
+valid17 = do
+  v0 <- buildPlainText ((plainText (T.replicate 150 "😀")) {plainTextEmoji = Nothing})
+  v1 <- buildHeaderBlock ((headerBlock v0) {headerBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v1)
+
+valid18 :: Result Value
+valid18 = do
+  v0 <- buildPlainText ((plainText "AloHa!") {plainTextEmoji = Nothing})
+  v1 <- buildHeaderBlock ((headerBlock v0) {headerBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v1)
+
+valid19 :: Result Value
+valid19 = do
+  v0 <- buildPlainText ((plainText (T.replicate 150 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildHeaderBlock ((headerBlock v0) {headerBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v1)
+
+valid20 :: Result Value
+valid20 = do
+  v0 <- buildPlainText ((plainText "image1") {plainTextEmoji = Nothing})
+  v1 <- buildImageBlock ((imageBlock "image1") {imageBlockImageUrl = Just "https://api.slack.com/img/blocks/bkb_template_images/beagle.png", imageBlockSlackFile = Nothing, imageBlockTitle = Just v0, imageBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v1)
+
+valid21 :: Result Value
+valid21 = do
+  v0 <- buildSlackFile (slackFile {slackFileId = Nothing, slackFileUrl = Just "https://files.slack.com/files-pri/T0123456-F0123ABC456/kitten.png"})
+  v1 <- buildImageBlock ((imageBlock "An incredibly cute kitten.") {imageBlockImageUrl = Nothing, imageBlockSlackFile = Just v0, imageBlockTitle = Nothing, imageBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v1)
+
+valid22 :: Result Value
+valid22 = do
+  v0 <- buildPlainText ((plainText "Label") {plainTextEmoji = Just True})
+  v1 <- buildPlainTextInputElement (plainTextInputElement {plainTextInputElementActionId = Just "action", plainTextInputElementInitialValue = Nothing, plainTextInputElementMultiline = Nothing, plainTextInputElementMinLength = Nothing, plainTextInputElementMaxLength = Nothing, plainTextInputElementDispatchActionConfig = Nothing, plainTextInputElementFocusOnLoad = Nothing, plainTextInputElementPlaceholder = Nothing})
+  v2 <- buildPlainText ((plainText "Hint") {plainTextEmoji = Just True})
+  v3 <- buildInputBlock ((inputBlock v0 (plainTextInputElementAsInputElement v1)) {inputBlockDispatchAction = Nothing, inputBlockBlockId = Just "fake_block_id", inputBlockHint = Just v2, inputBlockOptional = Just True})
+  pure (toJSON v3)
+
+valid23 :: Result Value
+valid23 = do
+  v0 <- buildMarkdownBlock ((markdownBlock "**Hello**, _world_!") {markdownBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v0)
+
+valid24 :: Result Value
+valid24 = do
+  v0 <- buildMarkdownBlock ((markdownBlock "") {markdownBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v0)
+
+valid25 :: Result Value
+valid25 = do
+  v0 <- buildRichTextText ((richTextText "Profile data loaded") {richTextTextStyle = Nothing})
+  v1 <- buildRichTextSection (richTextSection [richTextTextAsRichTextSectionElement v0])
+  v2 <- buildRichTextBlock ((richTextBlock [richTextSectionAsRichTextBlockElement v1]) {richTextBlockBlockId = Just "plan_output"})
+  v3 <- buildPlanTask ((taskCardBlock "call_001" "Fetched user profile information" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Just v2, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v4 <- buildPlanTask ((taskCardBlock "call_002" "Checked user permissions" TaskStatusPending) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v5 <- buildPlanBlock ((planBlock "Thinking completed" [v3, v4]) {planBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v5)
+
+valid26 :: Result Value
+valid26 = do
+  v0 <- buildRichTextText ((richTextText "You 'bout to witness hip-hop in its most purest") {richTextTextStyle = Just ((richTextStyle {styleBold = Just True}))})
+  v1 <- buildRichTextText ((richTextText "Most rawest form, flow almost flawless") {richTextTextStyle = Just ((richTextStyle {styleStrike = Just True}))})
+  v2 <- buildRichTextText ((richTextText "Most hardest, most honest known artist") {richTextTextStyle = Just ((richTextStyle {styleItalic = Just True}))})
+  v3 <- buildRichTextSection (richTextSection [richTextTextAsRichTextSectionElement v0, richTextTextAsRichTextSectionElement v1, richTextTextAsRichTextSectionElement v2])
+  v4 <- buildRichTextBlock ((richTextBlock [richTextSectionAsRichTextBlockElement v3]) {richTextBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v4)
+
+valid27 :: Result Value
+valid27 = do
+  v0 <- buildMarkdownText ((markdownText "Hello") {markdownTextVerbatim = Nothing})
+  v1 <- buildMarkdownText ((markdownText "Are you") {markdownTextVerbatim = Nothing})
+  v2 <- buildPlainText ((plainText "There?") {plainTextEmoji = Just True})
+  v3 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v0), sectionBlockFields = Just [markdownTextAsText v1, plainTextAsText v2], sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v3)
+
+valid28 :: Result Value
+valid28 = do
+  v0 <- buildMarkdownText ((markdownText "Highly") {markdownTextVerbatim = Nothing})
+  v1 <- buildPlainText ((plainText "Strung") {plainTextEmoji = Just True})
+  v2 <- buildSectionBlock (sectionBlock {sectionBlockText = Nothing, sectionBlockFields = Just [markdownTextAsText v0, plainTextAsText v1], sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v2)
+
+valid29 :: Result Value
+valid29 = do
+  v0 <- buildMarkdownText ((markdownText "Test:") {markdownTextVerbatim = Nothing})
+  v1 <- buildPlainText ((plainText "foo") {plainTextEmoji = Nothing})
+  v2 <- buildMarkdownText ((markdownText "bar") {markdownTextVerbatim = Nothing})
+  v3 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v0), sectionBlockFields = Just [plainTextAsText v1, markdownTextAsText v2], sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v3)
+
+valid30 :: Result Value
+valid30 = do
+  v0 <- buildMarkdownText ((markdownText "Lowly") {markdownTextVerbatim = Nothing})
+  v1 <- buildSectionBlock (sectionBlock {sectionBlockText = Nothing, sectionBlockFields = Just [markdownTextAsText v0], sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v1)
+
+valid31 :: Result Value
+valid31 = do
+  v0 <- buildMarkdownText ((markdownText "Hello, world!") {markdownTextVerbatim = Nothing})
+  v1 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v0), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v1)
+
+valid32 :: Result Value
+valid32 = do
+  v0 <- buildRawText (rawText "Header A")
+  v1 <- buildRawText (rawText "Header B")
+  v2 <- buildRawText (rawText "Data 1A")
+  v3 <- buildRichTextLink ((richTextLink "https://slack.com") {richTextLinkText = Just "Data 1B", richTextLinkStyle = Nothing, richTextLinkUnsafe = Nothing})
+  v4 <- buildRichTextSection (richTextSection [richTextLinkAsRichTextSectionElement v3])
+  v5 <- buildRichTextBlock ((richTextBlock [richTextSectionAsRichTextBlockElement v4]) {richTextBlockBlockId = Nothing})
+  v6 <- buildRawText (rawText "Data 2A")
+  v7 <- buildRichTextLink ((richTextLink "https://slack.com") {richTextLinkText = Just "Data 2B", richTextLinkStyle = Nothing, richTextLinkUnsafe = Nothing})
+  v8 <- buildRichTextSection (richTextSection [richTextLinkAsRichTextSectionElement v7])
+  v9 <- buildRichTextBlock ((richTextBlock [richTextSectionAsRichTextBlockElement v8]) {richTextBlockBlockId = Nothing})
+  v10 <- buildColumnSettings (columnSettings {columnSettingsAlign = Nothing, columnSettingsIsWrapped = Just True})
+  v11 <- buildColumnSettings (columnSettings {columnSettingsAlign = Just ColumnAlignRight, columnSettingsIsWrapped = Nothing})
+  v12 <- buildTableBlock ((tableBlock [[rawTextAsTableCell v0, rawTextAsTableCell v1], [rawTextAsTableCell v2, richTextBlockAsTableCell v5], [rawTextAsTableCell v6, richTextBlockAsTableCell v9]]) {tableBlockColumnSettings = Just [v10, v11], tableBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v12)
+
+valid33 :: Result Value
+valid33 = do
+  v0 <- buildRawText (rawText "Header A")
+  v1 <- buildRawText (rawText "Header B")
+  v2 <- buildRawText (rawText "Only one cell")
+  v3 <- buildTableBlock ((tableBlock [[rawTextAsTableCell v0, rawTextAsTableCell v1], [rawTextAsTableCell v2]]) {tableBlockColumnSettings = Nothing, tableBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v3)
+
+valid34 :: Result Value
+valid34 = do
+  v0 <- buildRichTextText ((richTextText "Found weather data for Chicago from 2 sources") {richTextTextStyle = Nothing})
+  v1 <- buildRichTextSection (richTextSection [richTextTextAsRichTextSectionElement v0])
+  v2 <- buildRichTextBlock ((richTextBlock [richTextSectionAsRichTextBlockElement v1]) {richTextBlockBlockId = Just "task_output"})
+  v3 <- buildUrlSource (urlSource "https://weather.com/" "weather.com")
+  v4 <- buildUrlSource (urlSource "https://www.accuweather.com/" "accuweather.com")
+  v5 <- buildTaskCardBlock ((taskCardBlock "task_1" "Fetching weather data" TaskStatusInProgress) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Just v2, taskCardBlockSources = Just [v3, v4], taskCardBlockBlockId = Just "fake_block_id"})
+  pure (toJSON v5)
+
+valid35 :: Result Value
+valid35 = do
+  v0 <- buildPlainText ((plainText "Title") {plainTextEmoji = Nothing})
+  v1 <- buildVideoBlock ((videoBlock "alt" "https://example.com/t.png" v0 "https://example.com/v.mp4") {videoBlockBlockId = Just "b1", videoBlockAuthorName = Nothing, videoBlockDescription = Nothing, videoBlockProviderIconUrl = Nothing, videoBlockProviderName = Nothing, videoBlockTitleUrl = Nothing})
+  pure (toJSON v1)
+
+valid36 :: Result Value
+valid36 = do
+  v0 <- buildPlainText ((plainText "Getting Started") {plainTextEmoji = Nothing})
+  v1 <- buildPlainText ((plainText "A short intro") {plainTextEmoji = Nothing})
+  v2 <- buildVideoBlock ((videoBlock "How to use Slack" "https://example.com/thumb.png" v0 "https://example.com/video.mp4") {videoBlockBlockId = Just "video_1", videoBlockAuthorName = Just "Slack", videoBlockDescription = Just v1, videoBlockProviderIconUrl = Just "https://example.com/icon.png", videoBlockProviderName = Just "YouTube", videoBlockTitleUrl = Just "https://example.com"})
+  pure (toJSON v2)
+
+valid37 :: Result Value
+valid37 = do
+  v0 <- buildPlainText ((plainText "Summarize") {plainTextEmoji = Nothing})
+  v1 <- buildButtonElement ((buttonElement v0) {buttonElementActionId = Nothing, buttonElementUrl = Nothing, buttonElementValue = Nothing, buttonElementStyle = Nothing, buttonElementConfirm = Nothing, buttonElementAccessibilityLabel = Nothing, buttonElementAgentPrompt = Just "Summarize this incident.", buttonElementAgentPromptDisplay = Nothing})
+  pure (toJSON v1)
+
+valid38 :: Result Value
+valid38 = do
+  v0 <- buildPlainText ((plainText "Summarize") {plainTextEmoji = Nothing})
+  v1 <- buildButtonElement ((buttonElement v0) {buttonElementActionId = Nothing, buttonElementUrl = Nothing, buttonElementValue = Nothing, buttonElementStyle = Nothing, buttonElementConfirm = Nothing, buttonElementAccessibilityLabel = Nothing, buttonElementAgentPrompt = Just (T.replicate 4000 "😀"), buttonElementAgentPromptDisplay = Nothing})
+  pure (toJSON v1)
+
+valid39 :: Result Value
+valid39 = do
+  v0 <- buildPlainText ((plainText "Summarize") {plainTextEmoji = Nothing})
+  v1 <- buildButtonElement ((buttonElement v0) {buttonElementActionId = Just "summarize", buttonElementUrl = Nothing, buttonElementValue = Nothing, buttonElementStyle = Nothing, buttonElementConfirm = Nothing, buttonElementAccessibilityLabel = Nothing, buttonElementAgentPrompt = Just "Summarize this incident.", buttonElementAgentPromptDisplay = Just (T.replicate 80 "x")})
+  pure (toJSON v1)
+
+valid40 :: Result Value
+valid40 = do
+  v0 <- buildPlainText ((plainText "Summarize") {plainTextEmoji = Nothing})
+  v1 <- buildButtonElement ((buttonElement v0) {buttonElementActionId = Nothing, buttonElementUrl = Nothing, buttonElementValue = Nothing, buttonElementStyle = Nothing, buttonElementConfirm = Nothing, buttonElementAccessibilityLabel = Nothing, buttonElementAgentPrompt = Just "", buttonElementAgentPromptDisplay = Just ""})
+  pure (toJSON v1)
+
+valid41 :: Result Value
+valid41 = do
+  v0 <- buildPlainText ((plainText "Click Me") {plainTextEmoji = Nothing})
+  v1 <- buildButtonElement ((buttonElement v0) {buttonElementActionId = Just "button", buttonElementUrl = Nothing, buttonElementValue = Just "click_me", buttonElementStyle = Nothing, buttonElementConfirm = Nothing, buttonElementAccessibilityLabel = Nothing, buttonElementAgentPrompt = Nothing, buttonElementAgentPromptDisplay = Nothing})
+  pure (toJSON v1)
+
+valid42 :: Result Value
+valid42 = do
+  v0 <- buildPlainText ((plainText "Click Me") {plainTextEmoji = Nothing})
+  v1 <- buildButtonElement ((buttonElement v0) {buttonElementActionId = Nothing, buttonElementUrl = Nothing, buttonElementValue = Just "click_me", buttonElementStyle = Nothing, buttonElementConfirm = Nothing, buttonElementAccessibilityLabel = Nothing, buttonElementAgentPrompt = Nothing, buttonElementAgentPromptDisplay = Nothing})
+  pure (toJSON v1)
+
+valid43 :: Result Value
+valid43 = do
+  v0 <- buildPlainText ((plainText "Link!") {plainTextEmoji = Nothing})
+  v1 <- buildButtonElement ((buttonElement v0) {buttonElementActionId = Just "button", buttonElementUrl = Just "https://ndl.im/", buttonElementValue = Nothing, buttonElementStyle = Nothing, buttonElementConfirm = Nothing, buttonElementAccessibilityLabel = Nothing, buttonElementAgentPrompt = Nothing, buttonElementAgentPromptDisplay = Nothing})
+  pure (toJSON v1)
+
+valid44 :: Result Value
+valid44 = do
+  v0 <- buildPlainText ((plainText "Load") {plainTextEmoji = Nothing})
+  v1 <- buildButtonElement ((buttonElement v0) {buttonElementActionId = Just "button", buttonElementUrl = Nothing, buttonElementValue = Just "im_a_style_button", buttonElementStyle = Just ButtonStylePrimary, buttonElementConfirm = Nothing, buttonElementAccessibilityLabel = Nothing, buttonElementAgentPrompt = Nothing, buttonElementAgentPromptDisplay = Nothing})
+  pure (toJSON v1)
+
+valid45 :: Result Value
+valid45 = do
+  v0 <- buildPlainText ((plainText (T.replicate 75 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildButtonElement ((buttonElement v0) {buttonElementActionId = Just "button", buttonElementUrl = Nothing, buttonElementValue = Nothing, buttonElementStyle = Nothing, buttonElementConfirm = Nothing, buttonElementAccessibilityLabel = Nothing, buttonElementAgentPrompt = Nothing, buttonElementAgentPromptDisplay = Nothing})
+  pure (toJSON v1)
+
+valid46 :: Result Value
+valid46 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildSelectOption ((selectOption (plainTextAsText v0) "A") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v2 <- buildPlainText ((plainText "B") {plainTextEmoji = Nothing})
+  v3 <- buildSelectOption ((selectOption (plainTextAsText v2) "B") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v4 <- buildCheckboxesElement ((checkboxesElement [v1, v3]) {checkboxesElementActionId = Just "and...action", checkboxesElementInitialOptions = Just [v1], checkboxesElementConfirm = Nothing, checkboxesElementFocusOnLoad = Nothing})
+  pure (toJSON v4)
+
+valid47 :: Result Value
+valid47 = do
+  v0 <- buildPlainText ((plainText "Pick a date") {plainTextEmoji = Nothing})
+  v1 <- buildDatePickerElement (datePickerElement {datePickerElementActionId = Just "datepicker", datePickerElementInitialDate = Just "1970-01-01", datePickerElementConfirm = Nothing, datePickerElementFocusOnLoad = Nothing, datePickerElementPlaceholder = Just v0})
+  pure (toJSON v1)
+
+valid48 :: Result Value
+valid48 = do
+  v0 <- buildDateTimePickerElement (dateTimePickerElement {dateTimePickerElementActionId = Just "datetime_picker", dateTimePickerElementInitialDateTime = Just 1628633830, dateTimePickerElementConfirm = Nothing, dateTimePickerElementFocusOnLoad = Nothing})
+  pure (toJSON v0)
+
+valid49 :: Result Value
+valid49 = do
+  v0 <- buildPlainText ((plainText "Enter your email") {plainTextEmoji = Nothing})
+  v1 <- buildEmailInputElement (emailInputElement {emailInputElementActionId = Just "email_input", emailInputElementInitialValue = Nothing, emailInputElementDispatchActionConfig = Nothing, emailInputElementFocusOnLoad = Nothing, emailInputElementPlaceholder = Just v0})
+  pure (toJSON v1)
+
+valid50 :: Result Value
+valid50 = do
+  v0 <- buildFileInputElement (fileInputElement {fileInputElementActionId = Just "file_input_action_id_1", fileInputElementFiletypes = Just ["jpg", "png"], fileInputElementMaxFiles = Just 5})
+  pure (toJSON v0)
+
+valid51 :: Result Value
+valid51 = do
+  v0 <- buildImageElement ((imageElement "Logo for ndl.im") {imageElementImageUrl = Just "https://ndl.im/img/logo.png", imageElementSlackFile = Nothing})
+  pure (toJSON v0)
+
+valid52 :: Result Value
+valid52 = do
+  v0 <- buildSlackFile (slackFile {slackFileId = Just "F0123ABC456", slackFileUrl = Nothing})
+  v1 <- buildImageElement ((imageElement "An incredibly cute kitten.") {imageElementImageUrl = Nothing, imageElementSlackFile = Just v0})
+  pure (toJSON v1)
+
+valid53 :: Result Value
+valid53 = do
+  v0 <- buildSlackFile (slackFile {slackFileId = Nothing, slackFileUrl = Just "https://files.slack.com/files-pri/T0123456-F0123456/xyz.png"})
+  v1 <- buildImageElement ((imageElement "An incredibly cute kitten.") {imageElementImageUrl = Nothing, imageElementSlackFile = Just v0})
+  pure (toJSON v1)
+
+valid54 :: Result Value
+valid54 = do
+  v0 <- buildPlainText ((plainText "Select channels") {plainTextEmoji = Nothing})
+  v1 <- buildChannelMultiSelectElement (channelMultiSelectElement {channelMultiSelectElementActionId = Just "multi_channels_select", channelMultiSelectElementInitialChannels = Nothing, channelMultiSelectElementConfirm = Nothing, channelMultiSelectElementMaxSelectedItems = Nothing, channelMultiSelectElementFocusOnLoad = Nothing, channelMultiSelectElementPlaceholder = Just v0})
+  pure (toJSON v1)
+
+valid55 :: Result Value
+valid55 = do
+  v0 <- buildPlainText ((plainText "Select conversations") {plainTextEmoji = Nothing})
+  v1 <- buildConversationMultiSelectElement (conversationMultiSelectElement {conversationMultiSelectElementActionId = Just "multi_conversations_select", conversationMultiSelectElementInitialConversations = Nothing, conversationMultiSelectElementDefaultToCurrentConversation = Nothing, conversationMultiSelectElementFilter = Nothing, conversationMultiSelectElementConfirm = Nothing, conversationMultiSelectElementMaxSelectedItems = Nothing, conversationMultiSelectElementFocusOnLoad = Nothing, conversationMultiSelectElementPlaceholder = Just v0})
+  pure (toJSON v1)
+
+valid56 :: Result Value
+valid56 = do
+  v0 <- buildPlainText ((plainText "Select items") {plainTextEmoji = Nothing})
+  v1 <- buildExternalMultiSelectElement (externalMultiSelectElement {externalMultiSelectElementActionId = Just "multi_external_select", externalMultiSelectElementMinQueryLength = Just 3, externalMultiSelectElementInitialOptions = Nothing, externalMultiSelectElementConfirm = Nothing, externalMultiSelectElementMaxSelectedItems = Nothing, externalMultiSelectElementFocusOnLoad = Nothing, externalMultiSelectElementPlaceholder = Just v0})
+  pure (toJSON v1)
+
+valid57 :: Result Value
+valid57 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildSelectOption ((selectOption (plainTextAsText v0) "A") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v2 <- buildPlainText ((plainText "B") {plainTextEmoji = Nothing})
+  v3 <- buildSelectOption ((selectOption (plainTextAsText v2) "B") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v4 <- buildPlainText ((plainText "Select one or more") {plainTextEmoji = Nothing})
+  v5 <- buildStaticMultiSelectElement (staticMultiSelectElement {staticMultiSelectElementActionId = Just "multi_static_select", staticMultiSelectElementOptions = Just [v1, v3], staticMultiSelectElementOptionGroups = Nothing, staticMultiSelectElementInitialOptions = Nothing, staticMultiSelectElementConfirm = Nothing, staticMultiSelectElementMaxSelectedItems = Nothing, staticMultiSelectElementFocusOnLoad = Nothing, staticMultiSelectElementPlaceholder = Just v4})
+  pure (toJSON v5)
+
+valid58 :: Result Value
+valid58 = do
+  v0 <- buildPlainText ((plainText "Select one or more users") {plainTextEmoji = Nothing})
+  v1 <- buildUserMultiSelectElement (userMultiSelectElement {userMultiSelectElementActionId = Just "multi_users_select", userMultiSelectElementInitialUsers = Nothing, userMultiSelectElementConfirm = Nothing, userMultiSelectElementMaxSelectedItems = Nothing, userMultiSelectElementFocusOnLoad = Nothing, userMultiSelectElementPlaceholder = Just v0})
+  pure (toJSON v1)
+
+valid59 :: Result Value
+valid59 = do
+  v0 <- buildPlainText ((plainText "Select one or more users") {plainTextEmoji = Nothing})
+  v1 <- buildUserMultiSelectElement (userMultiSelectElement {userMultiSelectElementActionId = Just "multi_users_select", userMultiSelectElementInitialUsers = Just ["U064B5H1309", "U063JR973UP"], userMultiSelectElementConfirm = Nothing, userMultiSelectElementMaxSelectedItems = Nothing, userMultiSelectElementFocusOnLoad = Nothing, userMultiSelectElementPlaceholder = Just v0})
+  pure (toJSON v1)
+
+valid60 :: Result Value
+valid60 = do
+  v0 <- buildNumberInputElement ((numberInputElement False) {numberInputElementActionId = Just "number_input", numberInputElementInitialValue = Nothing, numberInputElementMinValue = Nothing, numberInputElementMaxValue = Nothing, numberInputElementDispatchActionConfig = Nothing, numberInputElementFocusOnLoad = Nothing, numberInputElementPlaceholder = Nothing})
+  pure (toJSON v0)
+
+valid61 :: Result Value
+valid61 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildSelectOption ((selectOption (plainTextAsText v0) "A") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v2 <- buildPlainText ((plainText "B") {plainTextEmoji = Nothing})
+  v3 <- buildSelectOption ((selectOption (plainTextAsText v2) "B") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v4 <- buildPlainText ((plainText "C") {plainTextEmoji = Nothing})
+  v5 <- buildSelectOption ((selectOption (plainTextAsText v4) "C") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v6 <- buildOverflowElement ((overflowElement [v1, v3, v5]) {overflowElementActionId = Just "overflow", overflowElementConfirm = Nothing})
+  pure (toJSON v6)
+
+valid62 :: Result Value
+valid62 = do
+  v0 <- buildPlainText ((plainText "Enter your plain text") {plainTextEmoji = Nothing})
+  v1 <- buildPlainTextInputElement (plainTextInputElement {plainTextInputElementActionId = Just "plaintext_input", plainTextInputElementInitialValue = Nothing, plainTextInputElementMultiline = Nothing, plainTextInputElementMinLength = Nothing, plainTextInputElementMaxLength = Nothing, plainTextInputElementDispatchActionConfig = Nothing, plainTextInputElementFocusOnLoad = Nothing, plainTextInputElementPlaceholder = Just v0})
+  pure (toJSON v1)
+
+valid63 :: Result Value
+valid63 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildSelectOption ((selectOption (plainTextAsText v0) "A") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v2 <- buildPlainText ((plainText "B") {plainTextEmoji = Nothing})
+  v3 <- buildSelectOption ((selectOption (plainTextAsText v2) "B") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v4 <- buildPlainText ((plainText "C") {plainTextEmoji = Nothing})
+  v5 <- buildSelectOption ((selectOption (plainTextAsText v4) "C") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v6 <- buildRadioButtonsElement ((radioButtonsElement [v1, v3, v5]) {radioButtonsElementActionId = Just "radio_buttons", radioButtonsElementInitialOption = Just v1, radioButtonsElementConfirm = Nothing, radioButtonsElementFocusOnLoad = Nothing})
+  pure (toJSON v6)
+
+valid64 :: Result Value
+valid64 = do
+  v0 <- buildRichTextText ((richTextText "I'm rich") {richTextTextStyle = Nothing})
+  v1 <- buildRichTextSection (richTextSection [richTextTextAsRichTextSectionElement v0])
+  v2 <- buildRichTextBlock ((richTextBlock [richTextSectionAsRichTextBlockElement v1]) {richTextBlockBlockId = Nothing})
+  v3 <- buildPlainText ((plainText "Hello") {plainTextEmoji = Nothing})
+  v4 <- buildRichTextInputElement ((richTextInputElement "action_id") {richTextInputElementInitialValue = Just v2, richTextInputElementDispatchActionConfig = Nothing, richTextInputElementFocusOnLoad = Just False, richTextInputElementPlaceholder = Just v3, richTextInputElementMinLines = Nothing, richTextInputElementMaxLines = Nothing})
+  pure (toJSON v4)
+
+valid65 :: Result Value
+valid65 = do
+  v0 <- buildPlainText ((plainText "Select a channel") {plainTextEmoji = Nothing})
+  v1 <- buildChannelSelectElement (channelSelectElement {channelSelectElementActionId = Just "channels_select", channelSelectElementInitialChannel = Nothing, channelSelectElementResponseUrlEnabled = Nothing, channelSelectElementConfirm = Nothing, channelSelectElementFocusOnLoad = Nothing, channelSelectElementPlaceholder = Just v0})
+  pure (toJSON v1)
+
+valid66 :: Result Value
+valid66 = do
+  v0 <- buildPlainText ((plainText "Select one conversation") {plainTextEmoji = Nothing})
+  v1 <- buildConversationSelectElement (conversationSelectElement {conversationSelectElementActionId = Just "conversations_select", conversationSelectElementInitialConversation = Nothing, conversationSelectElementDefaultToCurrentConversation = Nothing, conversationSelectElementFilter = Nothing, conversationSelectElementResponseUrlEnabled = Nothing, conversationSelectElementConfirm = Nothing, conversationSelectElementFocusOnLoad = Nothing, conversationSelectElementPlaceholder = Just v0})
+  pure (toJSON v1)
+
+valid67 :: Result Value
+valid67 = do
+  v0 <- buildPlainText ((plainText "Select one item") {plainTextEmoji = Nothing})
+  v1 <- buildExternalSelectElement (externalSelectElement {externalSelectElementActionId = Just "external_select", externalSelectElementMinQueryLength = Just 4, externalSelectElementInitialOption = Nothing, externalSelectElementConfirm = Nothing, externalSelectElementFocusOnLoad = Nothing, externalSelectElementPlaceholder = Just v0})
+  pure (toJSON v1)
+
+valid68 :: Result Value
+valid68 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildSelectOption ((selectOption (plainTextAsText v0) "A") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v2 <- buildPlainText ((plainText "B") {plainTextEmoji = Nothing})
+  v3 <- buildSelectOption ((selectOption (plainTextAsText v2) "B") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v4 <- buildPlainText ((plainText "C") {plainTextEmoji = Nothing})
+  v5 <- buildSelectOption ((selectOption (plainTextAsText v4) "C") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v6 <- buildPlainText ((plainText "Select one item") {plainTextEmoji = Nothing})
+  v7 <- buildStaticSelectElement (staticSelectElement {staticSelectElementActionId = Just "static_select", staticSelectElementOptions = Just [v1, v3, v5], staticSelectElementOptionGroups = Nothing, staticSelectElementInitialOption = Nothing, staticSelectElementConfirm = Nothing, staticSelectElementFocusOnLoad = Nothing, staticSelectElementPlaceholder = Just v6})
+  pure (toJSON v7)
+
+valid69 :: Result Value
+valid69 = do
+  v0 <- buildPlainText ((plainText "Select one user") {plainTextEmoji = Nothing})
+  v1 <- buildUserSelectElement (userSelectElement {userSelectElementActionId = Just "users_select", userSelectElementInitialUser = Nothing, userSelectElementConfirm = Nothing, userSelectElementFocusOnLoad = Nothing, userSelectElementPlaceholder = Just v0})
+  pure (toJSON v1)
+
+valid70 :: Result Value
+valid70 = do
+  v0 <- buildPlainText ((plainText "Select your time") {plainTextEmoji = Nothing})
+  v1 <- buildTimePickerElement (timePickerElement {timePickerElementActionId = Just "timepicker", timePickerElementInitialTime = Just "12:00", timePickerElementTimezone = Just "Australia/Sydney", timePickerElementConfirm = Nothing, timePickerElementFocusOnLoad = Nothing, timePickerElementPlaceholder = Just v0})
+  pure (toJSON v1)
+
+valid71 :: Result Value
+valid71 = do
+  v0 <- buildUrlInputElement (urlInputElement {urlInputElementActionId = Just "url_text_input", urlInputElementInitialValue = Nothing, urlInputElementDispatchActionConfig = Nothing, urlInputElementFocusOnLoad = Nothing, urlInputElementPlaceholder = Nothing})
+  pure (toJSON v0)
+
+valid72 :: Result Value
+valid72 = do
+  v0 <- buildUrlSource (urlSource "https://docs.slack.dev/" "Slack API docs")
+  pure (toJSON v0)
+
+valid73 :: Result Value
+valid73 = do
+  v0 <- buildPlainText ((plainText "Run Your Workflow") {plainTextEmoji = Nothing})
+  v1 <- buildInputParameter (inputParameter "name_a" "value_a")
+  v2 <- buildInputParameter (inputParameter "name_b" "value_b")
+  v3 <- buildTrigger ((trigger "https://slack.com/shortcuts/Ft012KXZK1MZ/8831723c452aac3e87c6d3219bebd44c") {triggerCustomizableInputParameters = Just [v1, v2]})
+  v4 <- buildWorkflow (workflow v3)
+  v5 <- buildWorkflowButtonElement ((workflowButtonElement v0 v4 "run_workflow") {workflowButtonElementConfirm = Nothing, workflowButtonElementStyle = Nothing, workflowButtonElementAccessibilityLabel = Nothing})
+  pure (toJSON v5)
+
+valid74 :: Result Value
+valid74 = do
+  v0 <- buildMarkdownText ((markdownText "Hello, world!") {markdownTextVerbatim = Nothing})
+  v1 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v0), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "fake_block_id"})
+  v2 <- buildMessagePayload ((messagePayload "#slackblocks") {messagePayloadBlocks = Just [sectionBlockAsBlock v1], messagePayloadAttachments = Nothing, messagePayloadText = Just "", messagePayloadMrkdwn = Just True, messagePayloadUnfurlLinks = Nothing, messagePayloadUnfurlMedia = Nothing, messagePayloadMetadata = Nothing})
+  pure (toJSON v2)
+
+valid75 :: Result Value
+valid75 = do
+  v0 <- buildMarkdownText ((markdownText "Hello, world!") {markdownTextVerbatim = Nothing})
+  v1 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v0), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "block1"})
+  v2 <- buildAttachment ((attachment [sectionBlockAsBlock v1]) {attachmentColor = Just "#000000", attachmentFallback = Nothing})
+  v3 <- buildMessagePayload ((messagePayload "#slackblocks") {messagePayloadBlocks = Nothing, messagePayloadAttachments = Just [v2], messagePayloadText = Just "", messagePayloadMrkdwn = Just True, messagePayloadUnfurlLinks = Nothing, messagePayloadUnfurlMedia = Nothing, messagePayloadMetadata = Nothing})
+  pure (toJSON v3)
+
+valid76 :: Result Value
+valid76 = do
+  v0 <- buildMarkdownText ((markdownText "Block, One") {markdownTextVerbatim = Nothing})
+  v1 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v0), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "fake_block1"})
+  v2 <- buildPlainText ((plainText " ") {plainTextEmoji = Nothing})
+  v3 <- buildImageBlock ((imageBlock "crash") {imageBlockImageUrl = Just "http://bit.ly/slack-block-test-image", imageBlockSlackFile = Nothing, imageBlockTitle = Just v2, imageBlockBlockId = Just "fake_block3"})
+  v4 <- buildAttachment ((attachment [sectionBlockAsBlock v1]) {attachmentColor = Just "#8800ff", attachmentFallback = Nothing})
+  v5 <- buildMarkdownText ((markdownText "Block, Two") {markdownTextVerbatim = Nothing})
+  v6 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v5), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "fake_block2"})
+  v7 <- buildAttachment ((attachment [sectionBlockAsBlock v6, imageBlockAsBlock v3]) {attachmentColor = Just "#ffff00", attachmentFallback = Nothing})
+  v8 <- buildMessagePayload ((messagePayload "#slackblocks") {messagePayloadBlocks = Just [sectionBlockAsBlock v1, imageBlockAsBlock v3], messagePayloadAttachments = Just [v4, v7], messagePayloadText = Just "", messagePayloadMrkdwn = Just True, messagePayloadUnfurlLinks = Nothing, messagePayloadUnfurlMedia = Nothing, messagePayloadMetadata = Nothing})
+  pure (toJSON v8)
+
+valid77 :: Result Value
+valid77 = do
+  v0 <- buildMarkdownText ((markdownText "Hello, world!") {markdownTextVerbatim = Nothing})
+  v1 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v0), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "fake_block_id"})
+  v2 <- buildMessageResponse (messageResponse {messageResponseBlocks = Just [sectionBlockAsBlock v1], messageResponseAttachments = Nothing, messageResponseText = Just "", messageResponseMrkdwn = Just True, messageResponseReplaceOriginal = Just False, messageResponseResponseType = Just ResponseTypeEphemeral})
+  pure (toJSON v2)
+
+valid78 :: Result Value
+valid78 = do
+  v0 <- buildMarkdownText ((markdownText "Hello, world!") {markdownTextVerbatim = Nothing})
+  v1 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v0), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "fake_block_id"})
+  v2 <- buildAttachment ((attachment [sectionBlockAsBlock v1]) {attachmentColor = Just "#ffff00", attachmentFallback = Nothing})
+  v3 <- buildMessagePayload ((messagePayload "#slackblocks") {messagePayloadBlocks = Nothing, messagePayloadAttachments = Just [v2], messagePayloadText = Just "", messagePayloadMrkdwn = Just True, messagePayloadUnfurlLinks = Nothing, messagePayloadUnfurlMedia = Nothing, messagePayloadMetadata = Nothing})
+  pure (toJSON v3)
+
+valid79 :: Result Value
+valid79 = do
+  v0 <- buildMarkdownText ((markdownText "Hello, world!") {markdownTextVerbatim = Nothing})
+  v1 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v0), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "fake_block_id"})
+  v2 <- buildMessagePayload ((messagePayload "#slackblocks") {messagePayloadBlocks = Just [sectionBlockAsBlock v1], messagePayloadAttachments = Nothing, messagePayloadText = Just "", messagePayloadMrkdwn = Just True, messagePayloadUnfurlLinks = Just False, messagePayloadUnfurlMedia = Just False, messagePayloadMetadata = Nothing})
+  pure (toJSON v2)
+
+valid80 :: Result Value
+valid80 = do
+  v0 <- buildMarkdownText ((markdownText "You wouldn't do ol' Hook in now, would you, lad?") {markdownTextVerbatim = Nothing})
+  v1 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v0), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "fake_block_id"})
+  v2 <- buildMarkdownText ((markdownText "Well, all right... if you... say you're a codfish.") {markdownTextVerbatim = Nothing})
+  v3 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v2), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "fake_block_id"})
+  v4 <- buildWebhookMessage (webhookMessage {webhookMessageBlocks = Just [sectionBlockAsBlock v1, sectionBlockAsBlock v3], webhookMessageAttachments = Nothing, webhookMessageText = Nothing, webhookMessageResponseType = Just ResponseTypeEphemeral, webhookMessageReplaceOriginal = Just True, webhookMessageDeleteOriginal = Nothing, webhookMessageUnfurlLinks = Just False, webhookMessageUnfurlMedia = Just False, webhookMessageMetadata = Just (Map.fromList [("sender", String "Walt"), ("event_payload", object ["type" .= String "markdown", "items" .= toJSON ([object ["type" .= String "data_table"], object ["type" .= String "markdown", "text" .= String (T.replicate 12001 "x")]] :: [Value])])])})
+  pure (toJSON v4)
+
+valid81 :: Result Value
+valid81 = do
+  v0 <- buildMarkdownText ((markdownText "I'm a codfish.") {markdownTextVerbatim = Nothing})
+  v1 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v0), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "fake_block_id"})
+  v2 <- buildMarkdownText ((markdownText "Louder!") {markdownTextVerbatim = Nothing})
+  v3 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v2), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "fake_block_id"})
+  v4 <- buildMarkdownText ((markdownText "I'M A CODFISH!") {markdownTextVerbatim = Nothing})
+  v5 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v4), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "fake_block_id"})
+  v6 <- buildAttachment ((attachment [sectionBlockAsBlock v5]) {attachmentColor = Nothing, attachmentFallback = Nothing})
+  v7 <- buildWebhookMessage (webhookMessage {webhookMessageBlocks = Just [sectionBlockAsBlock v1, sectionBlockAsBlock v3], webhookMessageAttachments = Just [v6], webhookMessageText = Nothing, webhookMessageResponseType = Just ResponseTypeInChannel, webhookMessageReplaceOriginal = Nothing, webhookMessageDeleteOriginal = Just True, webhookMessageUnfurlLinks = Just True, webhookMessageUnfurlMedia = Just True, webhookMessageMetadata = Just (Map.fromList [("sender", String "Walt")])})
+  pure (toJSON v7)
+
+valid82 :: Result Value
+valid82 = do
+  v0 <- buildPlainText ((plainText "Maybe?") {plainTextEmoji = Nothing})
+  v1 <- buildPlainText ((plainText "Would you like to play checkers?") {plainTextEmoji = Nothing})
+  v2 <- buildPlainText ((plainText "Yes") {plainTextEmoji = Nothing})
+  v3 <- buildPlainText ((plainText "Nope!") {plainTextEmoji = Nothing})
+  v4 <- buildConfirmationDialogue ((confirmationDialogue v0 (plainTextAsText v1) v2 v3) {confirmationDialogueStyle = Nothing})
+  pure (toJSON v4)
+
+valid83 :: Result Value
+valid83 = do
+  v0 <- buildConversationFilter (conversationFilter {conversationFilterInclude = Just ["public", "mpim"], conversationFilterExcludeExternalSharedChannels = Nothing, conversationFilterExcludeBotUsers = Just True})
+  pure (toJSON v0)
+
+valid84 :: Result Value
+valid84 = do
+  v0 <- buildDispatchActionConfiguration (dispatchActionConfiguration {dispatchActionConfigurationTriggerActionsOn = Just ["on_character_entered"]})
+  pure (toJSON v0)
+
+valid85 :: Result Value
+valid85 = do
+  v0 <- buildInputParameter (inputParameter "name" "value")
+  pure (toJSON v0)
+
+valid86 :: Result Value
+valid86 = do
+  v0 <- buildPlainText ((plainText "Canberra") {plainTextEmoji = Nothing})
+  v1 <- buildSelectOption ((selectOption (plainTextAsText v0) "canberra") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  pure (toJSON v1)
+
+valid87 :: Result Value
+valid87 = do
+  v0 <- buildPlainText ((plainText "Group A") {plainTextEmoji = Nothing})
+  v1 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v2 <- buildSelectOption ((selectOption (plainTextAsText v1) "A") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v3 <- buildPlainText ((plainText "B") {plainTextEmoji = Nothing})
+  v4 <- buildSelectOption ((selectOption (plainTextAsText v3) "B") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v5 <- buildPlainText ((plainText "C") {plainTextEmoji = Nothing})
+  v6 <- buildSelectOption ((selectOption (plainTextAsText v5) "C") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v7 <- buildSelectOptionGroup (selectOptionGroup v0 [v2, v4, v6])
+  pure (toJSON v7)
+
+valid88 :: Result Value
+valid88 = do
+  v0 <- buildPlainText ((plainText "At limit") {plainTextEmoji = Nothing})
+  v1 <- buildSelectOption ((selectOption (plainTextAsText v0) (T.replicate 150 "x")) {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  pure (toJSON v1)
+
+valid89 :: Result Value
+valid89 = do
+  v0 <- buildMarkdownText ((markdownText "hi") {markdownTextVerbatim = Nothing})
+  pure (toJSON v0)
+
+valid90 :: Result Value
+valid90 = do
+  v0 <- buildMarkdownText ((markdownText "hi") {markdownTextVerbatim = Just True})
+  pure (toJSON v0)
+
+valid91 :: Result Value
+valid91 = do
+  v0 <- buildPlainText ((plainText "hi") {plainTextEmoji = Nothing})
+  pure (toJSON v0)
+
+valid92 :: Result Value
+valid92 = do
+  v0 <- buildPlainText ((plainText "hi") {plainTextEmoji = Just True})
+  pure (toJSON v0)
+
+valid93 :: Result Value
+valid93 = do
+  v0 <- buildInputParameter (inputParameter "A" "A")
+  v1 <- buildInputParameter (inputParameter "B" "B")
+  v2 <- buildTrigger ((trigger "https://slack.com/shortcuts/Ft012KXZK1MZ/8831723c452aac3e87c6d3219bebd44c") {triggerCustomizableInputParameters = Just [v0, v1]})
+  pure (toJSON v2)
+
+valid94 :: Result Value
+valid94 = do
+  v0 <- buildInputParameter (inputParameter "A" "A")
+  v1 <- buildInputParameter (inputParameter "B" "B")
+  v2 <- buildTrigger ((trigger "https://slack.com/shortcuts/Ft012KXZK1MZ/8831723c452aac3e87c6d3219bebd44c") {triggerCustomizableInputParameters = Just [v0, v1]})
+  v3 <- buildWorkflow (workflow v2)
+  pure (toJSON v3)
+
+valid95 :: Result Value
+valid95 = do
+  v0 <- buildRichTextText ((richTextText "I am a bold rich text block!") {richTextTextStyle = Just ((richTextStyle {styleBold = Just True, styleItalic = Just True, styleStrike = Just False}))})
+  pure (toJSON v0)
+
+valid96 :: Result Value
+valid96 = do
+  v0 <- buildRichTextChannel ((richTextChannel "C0261C65XNY") {richTextChannelStyle = Just ((richTextStyle {styleBold = Just True, styleItalic = Just False, styleStrike = Just True, styleHighlight = Just True, styleClientHighlight = Just True, styleUnlink = Just False}))})
+  pure (toJSON v0)
+
+valid97 :: Result Value
+valid97 = do
+  v0 <- buildRichTextText ((richTextText "\ndef hello_world():\n    print('hello, world')") {richTextTextStyle = Nothing})
+  v1 <- buildRichTextCodeBlock ((richTextCodeBlock [richTextTextAsRichTextSectionElement v0]) {richTextCodeBlockBorder = Just 0})
+  pure (toJSON v1)
+
+valid98 :: Result Value
+valid98 = do
+  v0 <- buildRichTextEmoji ((richTextEmoji "wave") {richTextEmojiSkinTone = Nothing})
+  pure (toJSON v0)
+
+valid99 :: Result Value
+valid99 = do
+  v0 <- buildRichTextLink ((richTextLink "https://google.com/") {richTextLinkText = Just "Google", richTextLinkStyle = Just ((richTextStyle {styleBold = Just True, styleItalic = Just False, styleStrike = Just True, styleCode = Just True})), richTextLinkUnsafe = Just False})
+  pure (toJSON v0)
+
+valid100 :: Result Value
+valid100 = do
+  v0 <- buildRichTextText ((richTextText "Oh") {richTextTextStyle = Nothing})
+  v1 <- buildRichTextSection (richTextSection [richTextTextAsRichTextSectionElement v0])
+  v2 <- buildRichTextText ((richTextText "Hi") {richTextTextStyle = Nothing})
+  v3 <- buildRichTextSection (richTextSection [richTextTextAsRichTextSectionElement v2])
+  v4 <- buildRichTextText ((richTextText "Mark") {richTextTextStyle = Nothing})
+  v5 <- buildRichTextSection (richTextSection [richTextTextAsRichTextSectionElement v4])
+  v6 <- buildRichTextList ((richTextList RichTextListStyleBullet [v1, v3, v5]) {richTextListIndent = Just 0, richTextListOffset = Just 0, richTextListBorder = Just 1})
+  pure (toJSON v6)
+
+valid101 :: Result Value
+valid101 = do
+  v0 <- buildRichTextText ((richTextText "Oh") {richTextTextStyle = Nothing})
+  v1 <- buildRichTextSection (richTextSection [richTextTextAsRichTextSectionElement v0])
+  v2 <- buildRichTextText ((richTextText "Hi") {richTextTextStyle = Nothing})
+  v3 <- buildRichTextSection (richTextSection [richTextTextAsRichTextSectionElement v2])
+  v4 <- buildRichTextList ((richTextList RichTextListStyleOrdered [v1, v3]) {richTextListIndent = Just 1, richTextListOffset = Just 2, richTextListBorder = Just 1})
+  pure (toJSON v4)
+
+valid102 :: Result Value
+valid102 = do
+  v0 <- buildRichTextText ((richTextText "Great and good are seldom the same man") {richTextTextStyle = Nothing})
+  v1 <- buildRichTextQuote ((richTextQuote [richTextTextAsRichTextSectionElement v0]) {richTextQuoteBorder = Just 1})
+  pure (toJSON v1)
+
+valid103 :: Result Value
+valid103 = do
+  v0 <- buildRichTextText ((richTextText "The only true wisdom is in knowing you know nothing") {richTextTextStyle = Nothing})
+  v1 <- buildRichTextSection (richTextSection [richTextTextAsRichTextSectionElement v0])
+  pure (toJSON v1)
+
+valid104 :: Result Value
+valid104 = do
+  v0 <- buildRichTextUser ((richTextUser "DR36TNNLA") {richTextUserStyle = Just ((richTextStyle {styleBold = Just True, styleItalic = Just False, styleStrike = Just True, styleHighlight = Just True, styleClientHighlight = Just True, styleUnlink = Just False}))})
+  pure (toJSON v0)
+
+valid105 :: Result Value
+valid105 = do
+  v0 <- buildRichTextUserGroup ((richTextUserGroup "C01RGRU0RUK") {richTextUserGroupStyle = Just ((richTextStyle {styleBold = Just True, styleItalic = Just False, styleStrike = Just True, styleHighlight = Just True, styleClientHighlight = Just True, styleUnlink = Just False}))})
+  pure (toJSON v0)
+
+valid106 :: Result Value
+valid106 = do
+  v0 <- buildMarkdownText ((markdownText "Example Block") {markdownTextVerbatim = Nothing})
+  v1 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v0), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "fake_id"})
+  v2 <- buildHomeTabView ((homeTabView [sectionBlockAsBlock v1]) {homeTabViewPrivateMetadata = Nothing, homeTabViewCallbackId = Nothing, homeTabViewExternalId = Nothing})
+  pure (toJSON v2)
+
+valid107 :: Result Value
+valid107 = do
+  v0 <- buildPlainText ((plainText "Hello, world!") {plainTextEmoji = Nothing})
+  v1 <- buildMarkdownText ((markdownText "first section block") {markdownTextVerbatim = Nothing})
+  v2 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v1), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "1"})
+  v3 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "2"})
+  v4 <- buildMarkdownText ((markdownText "second section block") {markdownTextVerbatim = Nothing})
+  v5 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v4), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Just "3"})
+  v6 <- buildPlainText ((plainText "Close button") {plainTextEmoji = Nothing})
+  v7 <- buildPlainText ((plainText "Submit button") {plainTextEmoji = Nothing})
+  v8 <- buildModalView ((modalView v0 [sectionBlockAsBlock v2, dividerBlockAsBlock v3, sectionBlockAsBlock v5]) {modalViewClose = Just v6, modalViewSubmit = Just v7, modalViewPrivateMetadata = Nothing, modalViewCallbackId = Nothing, modalViewClearOnClose = Nothing, modalViewNotifyOnClose = Nothing, modalViewExternalId = Nothing, modalViewSubmitDisabled = Nothing})
+  pure (toJSON v8)
+
+valid108 :: Result Value
+valid108 = do
+  v0 <- buildPlainText ((plainText "Empty") {plainTextEmoji = Nothing})
+  v1 <- buildModalView ((modalView v0 []) {modalViewClose = Nothing, modalViewSubmit = Nothing, modalViewPrivateMetadata = Nothing, modalViewCallbackId = Nothing, modalViewClearOnClose = Nothing, modalViewNotifyOnClose = Nothing, modalViewExternalId = Nothing, modalViewSubmitDisabled = Nothing})
+  pure (toJSON v1)
+
+valid109 :: Result Value
+valid109 = do
+  v0 <- buildNumberInputElement ((numberInputElement True) {numberInputElementActionId = Just "bounds", numberInputElementInitialValue = Nothing, numberInputElementMinValue = Just "-1.5", numberInputElementMaxValue = Just "10.25", numberInputElementDispatchActionConfig = Nothing, numberInputElementFocusOnLoad = Nothing, numberInputElementPlaceholder = Nothing})
+  pure (toJSON v0)
+
+valid110 :: Result Value
+valid110 = do
+  v0 <- buildNumberInputElement ((numberInputElement True) {numberInputElementActionId = Nothing, numberInputElementInitialValue = Nothing, numberInputElementMinValue = Just "0.1000", numberInputElementMaxValue = Just "1e-1", numberInputElementDispatchActionConfig = Nothing, numberInputElementFocusOnLoad = Nothing, numberInputElementPlaceholder = Nothing})
+  pure (toJSON v0)
+
+valid111 :: Result Value
+valid111 = do
+  v0 <- buildNumberInputElement ((numberInputElement True) {numberInputElementActionId = Nothing, numberInputElementInitialValue = Nothing, numberInputElementMinValue = Just "1e309", numberInputElementMaxValue = Just "2e309", numberInputElementDispatchActionConfig = Nothing, numberInputElementFocusOnLoad = Nothing, numberInputElementPlaceholder = Nothing})
+  pure (toJSON v0)
+
+valid112 :: Result Value
+valid112 = do
+  v0 <- buildChartSegment (chartSegment "A" 1)
+  v1 <- buildPieChart (pieChart [v0])
+  v2 <- buildDataVisualizationBlock ((dataVisualizationBlock "Chart" (pieChartAsChart v1)) {dataVisualizationBlockBlockId = Just "chart_0"})
+  v3 <- buildDataVisualizationBlock ((dataVisualizationBlock "Chart" (pieChartAsChart v1)) {dataVisualizationBlockBlockId = Just "chart_1"})
+  v4 <- buildMessagePayload ((messagePayload "C123") {messagePayloadBlocks = Just [dataVisualizationBlockAsBlock v2, dataVisualizationBlockAsBlock v3], messagePayloadAttachments = Nothing, messagePayloadText = Just "", messagePayloadMrkdwn = Just True, messagePayloadUnfurlLinks = Nothing, messagePayloadUnfurlMedia = Nothing, messagePayloadMetadata = Nothing})
+  pure (toJSON v4)
+
+validCases :: [(String, Result Value, Value -> Result Value)]
+validCases =
+  [ ("attachments/attachment_multi_block", valid0, \value -> toJSON <$> (fromValue value :: Result Attachment)),
+    ("attachments/attachment_simple", valid1, \value -> toJSON <$> (fromValue value :: Result Attachment)),
+    ("blocks/actions_block_checkboxes", valid2, \value -> toJSON <$> (fromValue value :: Result ActionsBlock)),
+    ("blocks/alert_block", valid3, \value -> toJSON <$> (fromValue value :: Result AlertBlock)),
+    ("blocks/card_block", valid4, \value -> toJSON <$> (fromValue value :: Result CardBlock)),
+    ("blocks/carousel_block", valid5, \value -> toJSON <$> (fromValue value :: Result CarouselBlock)),
+    ("blocks/container_block", valid6, \value -> toJSON <$> (fromValue value :: Result ContainerBlock)),
+    ("blocks/context_actions_feedback_buttons", valid7, \value -> toJSON <$> (fromValue value :: Result ContextActionsBlock)),
+    ("blocks/context_actions_icon_button", valid8, \value -> toJSON <$> (fromValue value :: Result ContextActionsBlock)),
+    ("blocks/context_block_text_only", valid9, \value -> toJSON <$> (fromValue value :: Result ContextBlock)),
+    ("blocks/data_table_block", valid10, \value -> toJSON <$> (fromValue value :: Result DataTableBlock)),
+    ("blocks/data_visualization_area", valid11, \value -> toJSON <$> (fromValue value :: Result DataVisualizationBlock)),
+    ("blocks/data_visualization_bar", valid12, \value -> toJSON <$> (fromValue value :: Result DataVisualizationBlock)),
+    ("blocks/data_visualization_line", valid13, \value -> toJSON <$> (fromValue value :: Result DataVisualizationBlock)),
+    ("blocks/data_visualization_pie", valid14, \value -> toJSON <$> (fromValue value :: Result DataVisualizationBlock)),
+    ("blocks/divider_block_only", valid15, \value -> toJSON <$> (fromValue value :: Result DividerBlock)),
+    ("blocks/file_block_only", valid16, \value -> toJSON <$> (fromValue value :: Result FileBlock)),
+    ("blocks/header_block_emoji_at_limit", valid17, \value -> toJSON <$> (fromValue value :: Result HeaderBlock)),
+    ("blocks/header_block_only", valid18, \value -> toJSON <$> (fromValue value :: Result HeaderBlock)),
+    ("blocks/header_block_text_at_limit", valid19, \value -> toJSON <$> (fromValue value :: Result HeaderBlock)),
+    ("blocks/image_block_only", valid20, \value -> toJSON <$> (fromValue value :: Result ImageBlock)),
+    ("blocks/image_block_slack_file", valid21, \value -> toJSON <$> (fromValue value :: Result ImageBlock)),
+    ("blocks/input_block_only", valid22, \value -> toJSON <$> (fromValue value :: Result InputBlock)),
+    ("blocks/markdown_block_basic", valid23, \value -> toJSON <$> (fromValue value :: Result MarkdownBlock)),
+    ("blocks/markdown_empty", valid24, \value -> toJSON <$> (fromValue value :: Result MarkdownBlock)),
+    ("blocks/plan_block", valid25, \value -> toJSON <$> (fromValue value :: Result PlanBlock)),
+    ("blocks/rich_text_block_basic", valid26, \value -> toJSON <$> (fromValue value :: Result RichTextBlock)),
+    ("blocks/section_block_both_text_and_fields", valid27, \value -> toJSON <$> (fromValue value :: Result SectionBlock)),
+    ("blocks/section_block_empty_text_field_value", valid28, \value -> toJSON <$> (fromValue value :: Result SectionBlock)),
+    ("blocks/section_block_fields", valid29, \value -> toJSON <$> (fromValue value :: Result SectionBlock)),
+    ("blocks/section_block_single_field_value_coercion", valid30, \value -> toJSON <$> (fromValue value :: Result SectionBlock)),
+    ("blocks/section_block_text_only", valid31, \value -> toJSON <$> (fromValue value :: Result SectionBlock)),
+    ("blocks/table_block", valid32, \value -> toJSON <$> (fromValue value :: Result TableBlock)),
+    ("blocks/table_ragged_rows", valid33, \value -> toJSON <$> (fromValue value :: Result TableBlock)),
+    ("blocks/task_card_block", valid34, \value -> toJSON <$> (fromValue value :: Result TaskCardBlock)),
+    ("blocks/video_block_basic", valid35, \value -> toJSON <$> (fromValue value :: Result VideoBlock)),
+    ("blocks/video_block_full", valid36, \value -> toJSON <$> (fromValue value :: Result VideoBlock)),
+    ("elements/button_agent_prompt", valid37, \value -> toJSON <$> (fromValue value :: Result ButtonElement)),
+    ("elements/button_agent_prompt_at_limit", valid38, \value -> toJSON <$> (fromValue value :: Result ButtonElement)),
+    ("elements/button_agent_prompt_display", valid39, \value -> toJSON <$> (fromValue value :: Result ButtonElement)),
+    ("elements/button_agent_prompt_empty", valid40, \value -> toJSON <$> (fromValue value :: Result ButtonElement)),
+    ("elements/button_basic", valid41, \value -> toJSON <$> (fromValue value :: Result ButtonElement)),
+    ("elements/button_without_action_id", valid42, \value -> toJSON <$> (fromValue value :: Result ButtonElement)),
+    ("elements/button_link", valid43, \value -> toJSON <$> (fromValue value :: Result ButtonElement)),
+    ("elements/button_style", valid44, \value -> toJSON <$> (fromValue value :: Result ButtonElement)),
+    ("elements/button_text_at_limit", valid45, \value -> toJSON <$> (fromValue value :: Result ButtonElement)),
+    ("elements/checkbox_basic", valid46, \value -> toJSON <$> (fromValue value :: Result CheckboxesElement)),
+    ("elements/date_picker_basic", valid47, \value -> toJSON <$> (fromValue value :: Result DatePickerElement)),
+    ("elements/datetime_picker_basic", valid48, \value -> toJSON <$> (fromValue value :: Result DateTimePickerElement)),
+    ("elements/email_input_basic", valid49, \value -> toJSON <$> (fromValue value :: Result EmailInputElement)),
+    ("elements/file_input_basic", valid50, \value -> toJSON <$> (fromValue value :: Result FileInputElement)),
+    ("elements/image_basic", valid51, \value -> toJSON <$> (fromValue value :: Result ImageElement)),
+    ("elements/image_slack_file_id", valid52, \value -> toJSON <$> (fromValue value :: Result ImageElement)),
+    ("elements/image_slack_file_url", valid53, \value -> toJSON <$> (fromValue value :: Result ImageElement)),
+    ("elements/multi_select_channel", valid54, \value -> toJSON <$> (fromValue value :: Result ChannelMultiSelectElement)),
+    ("elements/multi_select_conversation", valid55, \value -> toJSON <$> (fromValue value :: Result ConversationMultiSelectElement)),
+    ("elements/multi_select_external", valid56, \value -> toJSON <$> (fromValue value :: Result ExternalMultiSelectElement)),
+    ("elements/multi_select_static", valid57, \value -> toJSON <$> (fromValue value :: Result StaticMultiSelectElement)),
+    ("elements/multi_select_user", valid58, \value -> toJSON <$> (fromValue value :: Result UserMultiSelectElement)),
+    ("elements/multi_select_user_with_initial_users", valid59, \value -> toJSON <$> (fromValue value :: Result UserMultiSelectElement)),
+    ("elements/number_input_basic", valid60, \value -> toJSON <$> (fromValue value :: Result NumberInputElement)),
+    ("elements/overflow_menu_basic", valid61, \value -> toJSON <$> (fromValue value :: Result OverflowElement)),
+    ("elements/plaintext_input_basic", valid62, \value -> toJSON <$> (fromValue value :: Result PlainTextInputElement)),
+    ("elements/radio_button_group_basic", valid63, \value -> toJSON <$> (fromValue value :: Result RadioButtonsElement)),
+    ("elements/rich_text_input_basic", valid64, \value -> toJSON <$> (fromValue value :: Result RichTextInputElement)),
+    ("elements/select_menu_channel", valid65, \value -> toJSON <$> (fromValue value :: Result ChannelSelectElement)),
+    ("elements/select_menu_conversation", valid66, \value -> toJSON <$> (fromValue value :: Result ConversationSelectElement)),
+    ("elements/select_menu_external", valid67, \value -> toJSON <$> (fromValue value :: Result ExternalSelectElement)),
+    ("elements/select_menu_static", valid68, \value -> toJSON <$> (fromValue value :: Result StaticSelectElement)),
+    ("elements/select_menu_user", valid69, \value -> toJSON <$> (fromValue value :: Result UserSelectElement)),
+    ("elements/timepicker_basic", valid70, \value -> toJSON <$> (fromValue value :: Result TimePickerElement)),
+    ("elements/url_input_basic", valid71, \value -> toJSON <$> (fromValue value :: Result UrlInputElement)),
+    ("elements/url_source_basic", valid72, \value -> toJSON <$> (fromValue value :: Result UrlSource)),
+    ("elements/workflow_button_basic", valid73, \value -> toJSON <$> (fromValue value :: Result WorkflowButtonElement)),
+    ("messages/message_basic", valid74, \value -> toJSON <$> (fromValue value :: Result MessagePayload)),
+    ("messages/message_basic_attachment", valid75, \value -> toJSON <$> (fromValue value :: Result MessagePayload)),
+    ("messages/message_compound", valid76, \value -> toJSON <$> (fromValue value :: Result MessagePayload)),
+    ("messages/message_response", valid77, \value -> toJSON <$> (fromValue value :: Result MessageResponse)),
+    ("messages/message_with_attachments", valid78, \value -> toJSON <$> (fromValue value :: Result MessagePayload)),
+    ("messages/message_with_optional_arguments", valid79, \value -> toJSON <$> (fromValue value :: Result MessagePayload)),
+    ("messages/webhook_message_basic", valid80, \value -> toJSON <$> (fromValue value :: Result WebhookMessage)),
+    ("messages/webhook_message_delete", valid81, \value -> toJSON <$> (fromValue value :: Result WebhookMessage)),
+    ("objects/confirmation_dialogue_basic", valid82, \value -> toJSON <$> (fromValue value :: Result ConfirmationDialogue)),
+    ("objects/conversation_filter_basic", valid83, \value -> toJSON <$> (fromValue value :: Result ConversationFilter)),
+    ("objects/dispatch_action_configuration_basic", valid84, \value -> toJSON <$> (fromValue value :: Result DispatchActionConfiguration)),
+    ("objects/input_parameter_basic", valid85, \value -> toJSON <$> (fromValue value :: Result InputParameter)),
+    ("objects/option_basic", valid86, \value -> toJSON <$> (fromValue value :: Result SelectOption)),
+    ("objects/option_group_basic", valid87, \value -> toJSON <$> (fromValue value :: Result SelectOptionGroup)),
+    ("objects/option_value_at_limit", valid88, \value -> toJSON <$> (fromValue value :: Result SelectOption)),
+    ("objects/text_markdown_basic", valid89, \value -> toJSON <$> (fromValue value :: Result MarkdownText)),
+    ("objects/text_markdown_verbatim", valid90, \value -> toJSON <$> (fromValue value :: Result MarkdownText)),
+    ("objects/text_plaintext_basic", valid91, \value -> toJSON <$> (fromValue value :: Result PlainText)),
+    ("objects/text_plaintext_emoji", valid92, \value -> toJSON <$> (fromValue value :: Result PlainText)),
+    ("objects/trigger_basic", valid93, \value -> toJSON <$> (fromValue value :: Result Trigger)),
+    ("objects/workflow_basic", valid94, \value -> toJSON <$> (fromValue value :: Result Workflow)),
+    ("rich_text/rich_text_basic", valid95, \value -> toJSON <$> (fromValue value :: Result RichTextText)),
+    ("rich_text/rich_text_channel_basic", valid96, \value -> toJSON <$> (fromValue value :: Result RichTextChannel)),
+    ("rich_text/rich_text_code_block_basic", valid97, \value -> toJSON <$> (fromValue value :: Result RichTextCodeBlock)),
+    ("rich_text/rich_text_emoji_basic", valid98, \value -> toJSON <$> (fromValue value :: Result RichTextEmoji)),
+    ("rich_text/rich_text_link_basic", valid99, \value -> toJSON <$> (fromValue value :: Result RichTextLink)),
+    ("rich_text/rich_text_list_basic", valid100, \value -> toJSON <$> (fromValue value :: Result RichTextList)),
+    ("rich_text/rich_text_list_ordered", valid101, \value -> toJSON <$> (fromValue value :: Result RichTextList)),
+    ("rich_text/rich_text_quote_basic", valid102, \value -> toJSON <$> (fromValue value :: Result RichTextQuote)),
+    ("rich_text/rich_text_section_basic", valid103, \value -> toJSON <$> (fromValue value :: Result RichTextSection)),
+    ("rich_text/rich_text_user_basic", valid104, \value -> toJSON <$> (fromValue value :: Result RichTextUser)),
+    ("rich_text/rich_text_user_group_basic", valid105, \value -> toJSON <$> (fromValue value :: Result RichTextUserGroup)),
+    ("views/hometab_view", valid106, \value -> toJSON <$> (fromValue value :: Result HomeTabView)),
+    ("views/modal_with_blocks", valid107, \value -> toJSON <$> (fromValue value :: Result ModalView)),
+    ("views/modal_without_blocks", valid108, \value -> toJSON <$> (fromValue value :: Result ModalView)),
+    ("elements/number_input_bounds", valid109, \value -> toJSON <$> (fromValue value :: Result NumberInputElement)),
+    ("elements/number_input_scientific_bounds", valid110, \value -> toJSON <$> (fromValue value :: Result NumberInputElement)),
+    ("elements/number_input_large_exponent", valid111, \value -> toJSON <$> (fromValue value :: Result NumberInputElement)),
+    ("messages/message_two_charts", valid112, \value -> toJSON <$> (fromValue value :: Result MessagePayload))
+  ]
+
+invalid0 :: Result ()
+invalid0 = do
+  v0 <- buildExternalSelectElement (externalSelectElement {externalSelectElementActionId = Nothing, externalSelectElementMinQueryLength = Just (-1), externalSelectElementInitialOption = Nothing, externalSelectElementConfirm = Nothing, externalSelectElementFocusOnLoad = Nothing, externalSelectElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid1 :: Result ()
+invalid1 = do
+  v0 <- buildExternalMultiSelectElement (externalMultiSelectElement {externalMultiSelectElementActionId = Nothing, externalMultiSelectElementMinQueryLength = Just (-1), externalMultiSelectElementInitialOptions = Nothing, externalMultiSelectElementConfirm = Nothing, externalMultiSelectElementMaxSelectedItems = Nothing, externalMultiSelectElementFocusOnLoad = Nothing, externalMultiSelectElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid2 :: Result ()
+invalid2 = do
+  v0 <- buildDatePickerElement (datePickerElement {datePickerElementActionId = Nothing, datePickerElementInitialDate = Just "February 29, 2028", datePickerElementConfirm = Nothing, datePickerElementFocusOnLoad = Nothing, datePickerElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid3 :: Result ()
+invalid3 = do
+  v0 <- buildTimePickerElement (timePickerElement {timePickerElementActionId = Nothing, timePickerElementInitialTime = Just "9:00", timePickerElementTimezone = Nothing, timePickerElementConfirm = Nothing, timePickerElementFocusOnLoad = Nothing, timePickerElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid4 :: Result ()
+invalid4 = do
+  v0 <- buildTimePickerElement (timePickerElement {timePickerElementActionId = Nothing, timePickerElementInitialTime = Just "25:99", timePickerElementTimezone = Nothing, timePickerElementConfirm = Nothing, timePickerElementFocusOnLoad = Nothing, timePickerElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid5 :: Result ()
+invalid5 = do
+  v0 <- buildStaticSelectElement (staticSelectElement {staticSelectElementActionId = Nothing, staticSelectElementOptions = Just [], staticSelectElementOptionGroups = Nothing, staticSelectElementInitialOption = Nothing, staticSelectElementConfirm = Nothing, staticSelectElementFocusOnLoad = Nothing, staticSelectElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid6 :: Result ()
+invalid6 = do
+  v0 <- buildStaticMultiSelectElement (staticMultiSelectElement {staticMultiSelectElementActionId = Nothing, staticMultiSelectElementOptions = Just [], staticMultiSelectElementOptionGroups = Nothing, staticMultiSelectElementInitialOptions = Nothing, staticMultiSelectElementConfirm = Nothing, staticMultiSelectElementMaxSelectedItems = Nothing, staticMultiSelectElementFocusOnLoad = Nothing, staticMultiSelectElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid7 :: Result ()
+invalid7 = do
+  v0 <- buildStaticSelectElement (staticSelectElement {staticSelectElementActionId = Nothing, staticSelectElementOptions = Nothing, staticSelectElementOptionGroups = Just [], staticSelectElementInitialOption = Nothing, staticSelectElementConfirm = Nothing, staticSelectElementFocusOnLoad = Nothing, staticSelectElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid8 :: Result ()
+invalid8 = do
+  v0 <- buildStaticMultiSelectElement (staticMultiSelectElement {staticMultiSelectElementActionId = Nothing, staticMultiSelectElementOptions = Nothing, staticMultiSelectElementOptionGroups = Just [], staticMultiSelectElementInitialOptions = Nothing, staticMultiSelectElementConfirm = Nothing, staticMultiSelectElementMaxSelectedItems = Nothing, staticMultiSelectElementFocusOnLoad = Nothing, staticMultiSelectElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid9 :: Result ()
+invalid9 = do
+  v0 <- buildPlainText ((plainText "") {plainTextEmoji = Nothing})
+  v0 `seq` pure ()
+
+invalid10 :: Result ()
+invalid10 = do
+  v0 <- buildPlainText ((plainText (T.replicate 3001 "x")) {plainTextEmoji = Nothing})
+  v0 `seq` pure ()
+
+invalid11 :: Result ()
+invalid11 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildButtonElement ((buttonElement v0) {buttonElementActionId = Nothing, buttonElementUrl = Nothing, buttonElementValue = Nothing, buttonElementStyle = Nothing, buttonElementConfirm = Nothing, buttonElementAccessibilityLabel = Nothing, buttonElementAgentPrompt = Just (T.replicate 4001 "😀"), buttonElementAgentPromptDisplay = Nothing})
+  v1 `seq` pure ()
+
+invalid12 :: Result ()
+invalid12 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildButtonElement ((buttonElement v0) {buttonElementActionId = Nothing, buttonElementUrl = Nothing, buttonElementValue = Nothing, buttonElementStyle = Nothing, buttonElementConfirm = Nothing, buttonElementAccessibilityLabel = Nothing, buttonElementAgentPrompt = Nothing, buttonElementAgentPromptDisplay = Just ""})
+  v1 `seq` pure ()
+
+invalid13 :: Result ()
+invalid13 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildButtonElement ((buttonElement v0) {buttonElementActionId = Just (T.replicate 256 "x"), buttonElementUrl = Nothing, buttonElementValue = Nothing, buttonElementStyle = Nothing, buttonElementConfirm = Nothing, buttonElementAccessibilityLabel = Nothing, buttonElementAgentPrompt = Nothing, buttonElementAgentPromptDisplay = Nothing})
+  v1 `seq` pure ()
+
+invalid14 :: Result ()
+invalid14 = do
+  v0 <- buildPlainText ((plainText (T.replicate 76 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildButtonElement ((buttonElement v0) {buttonElementActionId = Just "a", buttonElementUrl = Nothing, buttonElementValue = Nothing, buttonElementStyle = Nothing, buttonElementConfirm = Nothing, buttonElementAccessibilityLabel = Nothing, buttonElementAgentPrompt = Nothing, buttonElementAgentPromptDisplay = Nothing})
+  v1 `seq` pure ()
+
+invalid15 :: Result ()
+invalid15 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildButtonElement ((buttonElement v0) {buttonElementActionId = Just "a", buttonElementUrl = Just (T.replicate 3001 "x"), buttonElementValue = Nothing, buttonElementStyle = Nothing, buttonElementConfirm = Nothing, buttonElementAccessibilityLabel = Nothing, buttonElementAgentPrompt = Nothing, buttonElementAgentPromptDisplay = Nothing})
+  v1 `seq` pure ()
+
+invalid16 :: Result ()
+invalid16 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildButtonElement ((buttonElement v0) {buttonElementActionId = Just "a", buttonElementUrl = Nothing, buttonElementValue = Just (T.replicate 2001 "x"), buttonElementStyle = Nothing, buttonElementConfirm = Nothing, buttonElementAccessibilityLabel = Nothing, buttonElementAgentPrompt = Nothing, buttonElementAgentPromptDisplay = Nothing})
+  v1 `seq` pure ()
+
+invalid17 :: Result ()
+invalid17 = do
+  v0 <- buildPlainText ((plainText (T.replicate 101 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildMarkdownText ((markdownText "Text") {markdownTextVerbatim = Nothing})
+  v2 <- buildPlainText ((plainText "Yes") {plainTextEmoji = Nothing})
+  v3 <- buildPlainText ((plainText "No") {plainTextEmoji = Nothing})
+  v4 <- buildConfirmationDialogue ((confirmationDialogue v0 (markdownTextAsText v1) v2 v3) {confirmationDialogueStyle = Nothing})
+  v4 `seq` pure ()
+
+invalid18 :: Result ()
+invalid18 = do
+  v0 <- buildPlainText ((plainText "Title") {plainTextEmoji = Nothing})
+  v1 <- buildMarkdownText ((markdownText (T.replicate 301 "x")) {markdownTextVerbatim = Nothing})
+  v2 <- buildPlainText ((plainText "Yes") {plainTextEmoji = Nothing})
+  v3 <- buildPlainText ((plainText "No") {plainTextEmoji = Nothing})
+  v4 <- buildConfirmationDialogue ((confirmationDialogue v0 (markdownTextAsText v1) v2 v3) {confirmationDialogueStyle = Nothing})
+  v4 `seq` pure ()
+
+invalid19 :: Result ()
+invalid19 = do
+  v0 <- buildPlainText ((plainText "Title") {plainTextEmoji = Nothing})
+  v1 <- buildMarkdownText ((markdownText "Text") {markdownTextVerbatim = Nothing})
+  v2 <- buildPlainText ((plainText (T.replicate 31 "x")) {plainTextEmoji = Nothing})
+  v3 <- buildPlainText ((plainText "No") {plainTextEmoji = Nothing})
+  v4 <- buildConfirmationDialogue ((confirmationDialogue v0 (markdownTextAsText v1) v2 v3) {confirmationDialogueStyle = Nothing})
+  v4 `seq` pure ()
+
+invalid20 :: Result ()
+invalid20 = do
+  v0 <- buildPlainText ((plainText "Title") {plainTextEmoji = Nothing})
+  v1 <- buildMarkdownText ((markdownText "Text") {markdownTextVerbatim = Nothing})
+  v2 <- buildPlainText ((plainText "Yes") {plainTextEmoji = Nothing})
+  v3 <- buildPlainText ((plainText (T.replicate 31 "x")) {plainTextEmoji = Nothing})
+  v4 <- buildConfirmationDialogue ((confirmationDialogue v0 (markdownTextAsText v1) v2 v3) {confirmationDialogueStyle = Nothing})
+  v4 `seq` pure ()
+
+invalid21 :: Result ()
+invalid21 = do
+  v0 <- buildPlainText ((plainText (T.replicate 76 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildSelectOption ((selectOption (plainTextAsText v0) "a") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v1 `seq` pure ()
+
+invalid22 :: Result ()
+invalid22 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildSelectOption ((selectOption (plainTextAsText v0) (T.replicate 151 "x")) {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v1 `seq` pure ()
+
+invalid23 :: Result ()
+invalid23 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildSelectOption ((selectOption (plainTextAsText v0) "a") {selectOptionDescription = Nothing, selectOptionUrl = Just (T.replicate 3001 "🙂")})
+  v1 `seq` pure ()
+
+invalid24 :: Result ()
+invalid24 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildPlainText ((plainText (T.replicate 76 "x")) {plainTextEmoji = Nothing})
+  v2 <- buildSelectOption ((selectOption (plainTextAsText v0) "a") {selectOptionDescription = Just (plainTextAsText v1), selectOptionUrl = Nothing})
+  v2 `seq` pure ()
+
+invalid25 :: Result ()
+invalid25 = do
+  v0 <- buildPlainText ((plainText (T.replicate 76 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v2 <- buildSelectOption ((selectOption (plainTextAsText v1) "a") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v3 <- buildSelectOptionGroup (selectOptionGroup v0 [v2])
+  v3 `seq` pure ()
+
+invalid26 :: Result ()
+invalid26 = do
+  v0 <- buildPlainText ((plainText "Group") {plainTextEmoji = Nothing})
+  v1 <- buildSelectOptionGroup (selectOptionGroup v0 [])
+  v1 `seq` pure ()
+
+invalid27 :: Result ()
+invalid27 = do
+  v0 <- buildPlainText ((plainText "Group") {plainTextEmoji = Nothing})
+  v1 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v2 <- buildSelectOption ((selectOption (plainTextAsText v1) "a") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v3 <- buildSelectOptionGroup (selectOptionGroup v0 (replicate 101 v2))
+  v3 `seq` pure ()
+
+invalid28 :: Result ()
+invalid28 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildSelectOption ((selectOption (plainTextAsText v0) "a") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v2 <- buildPlainText ((plainText (T.replicate 151 "x")) {plainTextEmoji = Nothing})
+  v3 <- buildStaticSelectElement (staticSelectElement {staticSelectElementActionId = Just "a", staticSelectElementOptions = Just [v1], staticSelectElementOptionGroups = Nothing, staticSelectElementInitialOption = Nothing, staticSelectElementConfirm = Nothing, staticSelectElementFocusOnLoad = Nothing, staticSelectElementPlaceholder = Just v2})
+  v3 `seq` pure ()
+
+invalid29 :: Result ()
+invalid29 = do
+  v0 <- buildPlainText ((plainText (T.replicate 151 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildPlainTextInputElement (plainTextInputElement {plainTextInputElementActionId = Just "a", plainTextInputElementInitialValue = Nothing, plainTextInputElementMultiline = Nothing, plainTextInputElementMinLength = Nothing, plainTextInputElementMaxLength = Nothing, plainTextInputElementDispatchActionConfig = Nothing, plainTextInputElementFocusOnLoad = Nothing, plainTextInputElementPlaceholder = Just v0})
+  v1 `seq` pure ()
+
+invalid30 :: Result ()
+invalid30 = do
+  v0 <- buildPlainText ((plainText (T.replicate 151 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildEmailInputElement (emailInputElement {emailInputElementActionId = Just "a", emailInputElementInitialValue = Nothing, emailInputElementDispatchActionConfig = Nothing, emailInputElementFocusOnLoad = Nothing, emailInputElementPlaceholder = Just v0})
+  v1 `seq` pure ()
+
+invalid31 :: Result ()
+invalid31 = do
+  v0 <- buildPlainText ((plainText (T.replicate 151 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildUrlInputElement (urlInputElement {urlInputElementActionId = Just "a", urlInputElementInitialValue = Nothing, urlInputElementDispatchActionConfig = Nothing, urlInputElementFocusOnLoad = Nothing, urlInputElementPlaceholder = Just v0})
+  v1 `seq` pure ()
+
+invalid32 :: Result ()
+invalid32 = do
+  v0 <- buildPlainText ((plainText (T.replicate 151 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildNumberInputElement ((numberInputElement False) {numberInputElementActionId = Just "a", numberInputElementInitialValue = Nothing, numberInputElementMinValue = Nothing, numberInputElementMaxValue = Nothing, numberInputElementDispatchActionConfig = Nothing, numberInputElementFocusOnLoad = Nothing, numberInputElementPlaceholder = Just v0})
+  v1 `seq` pure ()
+
+invalid33 :: Result ()
+invalid33 = do
+  v0 <- buildPlainText ((plainText (T.replicate 151 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildDatePickerElement (datePickerElement {datePickerElementActionId = Just "a", datePickerElementInitialDate = Nothing, datePickerElementConfirm = Nothing, datePickerElementFocusOnLoad = Nothing, datePickerElementPlaceholder = Just v0})
+  v1 `seq` pure ()
+
+invalid34 :: Result ()
+invalid34 = do
+  v0 <- buildPlainText ((plainText (T.replicate 151 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildTimePickerElement (timePickerElement {timePickerElementActionId = Just "a", timePickerElementInitialTime = Nothing, timePickerElementTimezone = Nothing, timePickerElementConfirm = Nothing, timePickerElementFocusOnLoad = Nothing, timePickerElementPlaceholder = Just v0})
+  v1 `seq` pure ()
+
+invalid35 :: Result ()
+invalid35 = do
+  v0 <- buildPlainText ((plainText (T.replicate 151 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildRichTextInputElement ((richTextInputElement "a") {richTextInputElementInitialValue = Nothing, richTextInputElementDispatchActionConfig = Nothing, richTextInputElementFocusOnLoad = Nothing, richTextInputElementPlaceholder = Just v0, richTextInputElementMinLines = Nothing, richTextInputElementMaxLines = Nothing})
+  v1 `seq` pure ()
+
+invalid36 :: Result ()
+invalid36 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildSelectOption ((selectOption (plainTextAsText v0) "a") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v2 <- buildStaticSelectElement (staticSelectElement {staticSelectElementActionId = Just "a", staticSelectElementOptions = Just (replicate 101 v1), staticSelectElementOptionGroups = Nothing, staticSelectElementInitialOption = Nothing, staticSelectElementConfirm = Nothing, staticSelectElementFocusOnLoad = Nothing, staticSelectElementPlaceholder = Nothing})
+  v2 `seq` pure ()
+
+invalid37 :: Result ()
+invalid37 = do
+  v0 <- buildPlainText ((plainText "Group") {plainTextEmoji = Nothing})
+  v1 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v2 <- buildSelectOption ((selectOption (plainTextAsText v1) "a") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v3 <- buildSelectOptionGroup (selectOptionGroup v0 [v2])
+  v4 <- buildStaticSelectElement (staticSelectElement {staticSelectElementActionId = Just "a", staticSelectElementOptions = Nothing, staticSelectElementOptionGroups = Just (replicate 101 v3), staticSelectElementInitialOption = Nothing, staticSelectElementConfirm = Nothing, staticSelectElementFocusOnLoad = Nothing, staticSelectElementPlaceholder = Nothing})
+  v4 `seq` pure ()
+
+invalid38 :: Result ()
+invalid38 = do
+  v0 <- buildOverflowElement ((overflowElement []) {overflowElementActionId = Just "a", overflowElementConfirm = Nothing})
+  v0 `seq` pure ()
+
+invalid39 :: Result ()
+invalid39 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildSelectOption ((selectOption (plainTextAsText v0) "a") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v2 <- buildOverflowElement ((overflowElement (replicate 6 v1)) {overflowElementActionId = Just "a", overflowElementConfirm = Nothing})
+  v2 `seq` pure ()
+
+invalid40 :: Result ()
+invalid40 = do
+  v0 <- buildCheckboxesElement ((checkboxesElement []) {checkboxesElementActionId = Just "a", checkboxesElementInitialOptions = Nothing, checkboxesElementConfirm = Nothing, checkboxesElementFocusOnLoad = Nothing})
+  v0 `seq` pure ()
+
+invalid41 :: Result ()
+invalid41 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildSelectOption ((selectOption (plainTextAsText v0) "a") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v2 <- buildCheckboxesElement ((checkboxesElement (replicate 11 v1)) {checkboxesElementActionId = Just "a", checkboxesElementInitialOptions = Nothing, checkboxesElementConfirm = Nothing, checkboxesElementFocusOnLoad = Nothing})
+  v2 `seq` pure ()
+
+invalid42 :: Result ()
+invalid42 = do
+  v0 <- buildRadioButtonsElement ((radioButtonsElement []) {radioButtonsElementActionId = Just "a", radioButtonsElementInitialOption = Nothing, radioButtonsElementConfirm = Nothing, radioButtonsElementFocusOnLoad = Nothing})
+  v0 `seq` pure ()
+
+invalid43 :: Result ()
+invalid43 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildSelectOption ((selectOption (plainTextAsText v0) "a") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v2 <- buildRadioButtonsElement ((radioButtonsElement (replicate 11 v1)) {radioButtonsElementActionId = Just "a", radioButtonsElementInitialOption = Nothing, radioButtonsElementConfirm = Nothing, radioButtonsElementFocusOnLoad = Nothing})
+  v2 `seq` pure ()
+
+invalid44 :: Result ()
+invalid44 = do
+  v0 <- buildRawText (rawText "A")
+  v1 <- buildTableBlock ((tableBlock [[rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0], [rawTextAsTableCell v0]]) {tableBlockColumnSettings = Nothing, tableBlockBlockId = Nothing})
+  v1 `seq` pure ()
+
+invalid45 :: Result ()
+invalid45 = do
+  v0 <- buildRawText (rawText "A")
+  v1 <- buildTableBlock ((tableBlock [[rawTextAsTableCell v0, rawTextAsTableCell v0, rawTextAsTableCell v0, rawTextAsTableCell v0, rawTextAsTableCell v0, rawTextAsTableCell v0, rawTextAsTableCell v0, rawTextAsTableCell v0, rawTextAsTableCell v0, rawTextAsTableCell v0, rawTextAsTableCell v0, rawTextAsTableCell v0, rawTextAsTableCell v0, rawTextAsTableCell v0, rawTextAsTableCell v0, rawTextAsTableCell v0, rawTextAsTableCell v0, rawTextAsTableCell v0, rawTextAsTableCell v0, rawTextAsTableCell v0, rawTextAsTableCell v0]]) {tableBlockColumnSettings = Nothing, tableBlockBlockId = Nothing})
+  v1 `seq` pure ()
+
+invalid46 :: Result ()
+invalid46 = do
+  v0 <- buildFileInputElement (fileInputElement {fileInputElementActionId = Just "a", fileInputElementFiletypes = Nothing, fileInputElementMaxFiles = Just 0})
+  v0 `seq` pure ()
+
+invalid47 :: Result ()
+invalid47 = do
+  v0 <- buildFileInputElement (fileInputElement {fileInputElementActionId = Just "a", fileInputElementFiletypes = Nothing, fileInputElementMaxFiles = Just 11})
+  v0 `seq` pure ()
+
+invalid48 :: Result ()
+invalid48 = do
+  v0 <- buildDispatchActionConfiguration (dispatchActionConfiguration {dispatchActionConfigurationTriggerActionsOn = Just []})
+  v0 `seq` pure ()
+
+invalid49 :: Result ()
+invalid49 = do
+  v0 <- buildDispatchActionConfiguration (dispatchActionConfiguration {dispatchActionConfigurationTriggerActionsOn = Nothing})
+  v0 `seq` pure ()
+
+invalid50 :: Result ()
+invalid50 = do
+  v0 <- buildDispatchActionConfiguration (dispatchActionConfiguration {dispatchActionConfigurationTriggerActionsOn = Just ["on_enter_pressed", "on_character_entered", "on_enter_pressed"]})
+  v0 `seq` pure ()
+
+invalid51 :: Result ()
+invalid51 = do
+  v0 <- buildPlainTextInputElement (plainTextInputElement {plainTextInputElementActionId = Just "a", plainTextInputElementInitialValue = Nothing, plainTextInputElementMultiline = Nothing, plainTextInputElementMinLength = Nothing, plainTextInputElementMaxLength = Just 3001, plainTextInputElementDispatchActionConfig = Nothing, plainTextInputElementFocusOnLoad = Nothing, plainTextInputElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid52 :: Result ()
+invalid52 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildButtonElement ((buttonElement v0) {buttonElementActionId = Just "a", buttonElementUrl = Nothing, buttonElementValue = Nothing, buttonElementStyle = Nothing, buttonElementConfirm = Nothing, buttonElementAccessibilityLabel = Nothing, buttonElementAgentPrompt = Nothing, buttonElementAgentPromptDisplay = Nothing})
+  v2 <- buildActionsBlock ((actionsBlock (replicate 26 (buttonElementAsElement v1))) {actionsBlockBlockId = Nothing})
+  v2 `seq` pure ()
+
+invalid53 :: Result ()
+invalid53 = do
+  v0 <- buildMarkdownText ((markdownText "A") {markdownTextVerbatim = Nothing})
+  v1 <- buildContextBlock ((contextBlock (replicate 11 (markdownTextAsContextElement v0))) {contextBlockBlockId = Nothing})
+  v1 `seq` pure ()
+
+invalid54 :: Result ()
+invalid54 = do
+  v0 <- buildPlainText ((plainText (T.replicate 151 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildHeaderBlock ((headerBlock v0) {headerBlockBlockId = Nothing})
+  v1 `seq` pure ()
+
+invalid55 :: Result ()
+invalid55 = do
+  v0 <- buildImageBlock ((imageBlock "Alt") {imageBlockImageUrl = Just (T.replicate 3001 "x"), imageBlockSlackFile = Nothing, imageBlockTitle = Nothing, imageBlockBlockId = Nothing})
+  v0 `seq` pure ()
+
+invalid56 :: Result ()
+invalid56 = do
+  v0 <- buildImageBlock ((imageBlock (T.replicate 2001 "x")) {imageBlockImageUrl = Just "https://example.com/image.png", imageBlockSlackFile = Nothing, imageBlockTitle = Nothing, imageBlockBlockId = Nothing})
+  v0 `seq` pure ()
+
+invalid57 :: Result ()
+invalid57 = do
+  v0 <- buildPlainText ((plainText (T.replicate 2001 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildPlainTextInputElement (plainTextInputElement {plainTextInputElementActionId = Just "a", plainTextInputElementInitialValue = Nothing, plainTextInputElementMultiline = Nothing, plainTextInputElementMinLength = Nothing, plainTextInputElementMaxLength = Nothing, plainTextInputElementDispatchActionConfig = Nothing, plainTextInputElementFocusOnLoad = Nothing, plainTextInputElementPlaceholder = Nothing})
+  v2 <- buildInputBlock ((inputBlock v0 (plainTextInputElementAsInputElement v1)) {inputBlockDispatchAction = Nothing, inputBlockBlockId = Nothing, inputBlockHint = Nothing, inputBlockOptional = Nothing})
+  v2 `seq` pure ()
+
+invalid58 :: Result ()
+invalid58 = do
+  v0 <- buildPlainText ((plainText "Label") {plainTextEmoji = Nothing})
+  v1 <- buildPlainTextInputElement (plainTextInputElement {plainTextInputElementActionId = Just "a", plainTextInputElementInitialValue = Nothing, plainTextInputElementMultiline = Nothing, plainTextInputElementMinLength = Nothing, plainTextInputElementMaxLength = Nothing, plainTextInputElementDispatchActionConfig = Nothing, plainTextInputElementFocusOnLoad = Nothing, plainTextInputElementPlaceholder = Nothing})
+  v2 <- buildPlainText ((plainText (T.replicate 2001 "x")) {plainTextEmoji = Nothing})
+  v3 <- buildInputBlock ((inputBlock v0 (plainTextInputElementAsInputElement v1)) {inputBlockDispatchAction = Nothing, inputBlockBlockId = Nothing, inputBlockHint = Just v2, inputBlockOptional = Nothing})
+  v3 `seq` pure ()
+
+invalid59 :: Result ()
+invalid59 = do
+  v0 <- buildMarkdownBlock ((markdownBlock (T.replicate 12001 "x")) {markdownBlockBlockId = Nothing})
+  v0 `seq` pure ()
+
+invalid60 :: Result ()
+invalid60 = do
+  v0 <- buildMarkdownText ((markdownText (T.replicate 3001 "x")) {markdownTextVerbatim = Nothing})
+  v1 <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText v0), sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Nothing})
+  v1 `seq` pure ()
+
+invalid61 :: Result ()
+invalid61 = do
+  v0 <- buildMarkdownText ((markdownText "x") {markdownTextVerbatim = Nothing})
+  v1 <- buildSectionBlock (sectionBlock {sectionBlockText = Nothing, sectionBlockFields = Just (replicate 11 (markdownTextAsText v0)), sectionBlockAccessory = Nothing, sectionBlockBlockId = Nothing})
+  v1 `seq` pure ()
+
+invalid62 :: Result ()
+invalid62 = do
+  v0 <- buildMarkdownText ((markdownText (T.replicate 2001 "x")) {markdownTextVerbatim = Nothing})
+  v1 <- buildSectionBlock (sectionBlock {sectionBlockText = Nothing, sectionBlockFields = Just [markdownTextAsText v0], sectionBlockAccessory = Nothing, sectionBlockBlockId = Nothing})
+  v1 `seq` pure ()
+
+invalid63 :: Result ()
+invalid63 = do
+  v0 <- buildPlainText ((plainText "Title") {plainTextEmoji = Nothing})
+  v1 <- buildVideoBlock ((videoBlock (T.replicate 2001 "x") "https://example.com/thumbnail.png" v0 "https://example.com/video.mp4") {videoBlockBlockId = Nothing, videoBlockAuthorName = Nothing, videoBlockDescription = Nothing, videoBlockProviderIconUrl = Nothing, videoBlockProviderName = Nothing, videoBlockTitleUrl = Nothing})
+  v1 `seq` pure ()
+
+invalid64 :: Result ()
+invalid64 = do
+  v0 <- buildPlainText ((plainText (T.replicate 201 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildVideoBlock ((videoBlock "Video" "https://example.com/thumbnail.png" v0 "https://example.com/video.mp4") {videoBlockBlockId = Nothing, videoBlockAuthorName = Nothing, videoBlockDescription = Nothing, videoBlockProviderIconUrl = Nothing, videoBlockProviderName = Nothing, videoBlockTitleUrl = Nothing})
+  v1 `seq` pure ()
+
+invalid65 :: Result ()
+invalid65 = do
+  v0 <- buildPlainText ((plainText "Title") {plainTextEmoji = Nothing})
+  v1 <- buildVideoBlock ((videoBlock "Video" "https://example.com/thumbnail.png" v0 "https://example.com/video.mp4") {videoBlockBlockId = Nothing, videoBlockAuthorName = Just (T.replicate 51 "x"), videoBlockDescription = Nothing, videoBlockProviderIconUrl = Nothing, videoBlockProviderName = Nothing, videoBlockTitleUrl = Nothing})
+  v1 `seq` pure ()
+
+invalid66 :: Result ()
+invalid66 = do
+  v0 <- buildPlainText ((plainText "Title") {plainTextEmoji = Nothing})
+  v1 <- buildPlainText ((plainText (T.replicate 201 "x")) {plainTextEmoji = Nothing})
+  v2 <- buildVideoBlock ((videoBlock "Video" "https://example.com/thumbnail.png" v0 "https://example.com/video.mp4") {videoBlockBlockId = Nothing, videoBlockAuthorName = Nothing, videoBlockDescription = Just v1, videoBlockProviderIconUrl = Nothing, videoBlockProviderName = Nothing, videoBlockTitleUrl = Nothing})
+  v2 `seq` pure ()
+
+invalid67 :: Result ()
+invalid67 = do
+  v0 <- buildPlainText ((plainText "Title") {plainTextEmoji = Nothing})
+  v1 <- buildVideoBlock ((videoBlock "Video" "https://example.com/thumbnail.png" v0 "https://example.com/video.mp4") {videoBlockBlockId = Nothing, videoBlockAuthorName = Nothing, videoBlockDescription = Nothing, videoBlockProviderIconUrl = Nothing, videoBlockProviderName = Just (T.replicate 51 "x"), videoBlockTitleUrl = Nothing})
+  v1 `seq` pure ()
+
+invalid68 :: Result ()
+invalid68 = do
+  v0 <- buildMessagePayload ((messagePayload "") {messagePayloadBlocks = Nothing, messagePayloadAttachments = Nothing, messagePayloadText = Nothing, messagePayloadMrkdwn = Nothing, messagePayloadUnfurlLinks = Nothing, messagePayloadUnfurlMedia = Nothing, messagePayloadMetadata = Nothing})
+  v0 `seq` pure ()
+
+invalid69 :: Result ()
+invalid69 = do
+  v0 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Nothing})
+  v1 <- buildMessagePayload ((messagePayload "C123") {messagePayloadBlocks = Just (replicate 51 (dividerBlockAsBlock v0)), messagePayloadAttachments = Nothing, messagePayloadText = Nothing, messagePayloadMrkdwn = Nothing, messagePayloadUnfurlLinks = Nothing, messagePayloadUnfurlMedia = Nothing, messagePayloadMetadata = Nothing})
+  v1 `seq` pure ()
+
+invalid70 :: Result ()
+invalid70 = do
+  v0 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Nothing})
+  v1 <- buildAttachment ((attachment [dividerBlockAsBlock v0]) {attachmentColor = Nothing, attachmentFallback = Nothing})
+  v2 <- buildMessagePayload ((messagePayload "C123") {messagePayloadBlocks = Nothing, messagePayloadAttachments = Just (replicate 101 v1), messagePayloadText = Nothing, messagePayloadMrkdwn = Nothing, messagePayloadUnfurlLinks = Nothing, messagePayloadUnfurlMedia = Nothing, messagePayloadMetadata = Nothing})
+  v2 `seq` pure ()
+
+invalid71 :: Result ()
+invalid71 = do
+  v0 <- buildPlainText ((plainText "Modal only") {plainTextEmoji = Nothing})
+  v1 <- buildAlertBlock ((alertBlock (plainTextAsText v0)) {alertBlockLevel = Nothing, alertBlockBlockId = Nothing})
+  v2 <- buildMessagePayload ((messagePayload "C123") {messagePayloadBlocks = Just [alertBlockAsBlock v1], messagePayloadAttachments = Nothing, messagePayloadText = Nothing, messagePayloadMrkdwn = Nothing, messagePayloadUnfurlLinks = Nothing, messagePayloadUnfurlMedia = Nothing, messagePayloadMetadata = Nothing})
+  v2 `seq` pure ()
+
+invalid72 :: Result ()
+invalid72 = do
+  v0 <- buildPlainText ((plainText "Invalid") {plainTextEmoji = Nothing})
+  v1 <- buildMarkdownBlock ((markdownBlock "Message only") {markdownBlockBlockId = Nothing})
+  v2 <- buildModalView ((modalView v0 [markdownBlockAsBlock v1]) {modalViewClose = Nothing, modalViewSubmit = Nothing, modalViewPrivateMetadata = Nothing, modalViewCallbackId = Nothing, modalViewClearOnClose = Nothing, modalViewNotifyOnClose = Nothing, modalViewExternalId = Nothing, modalViewSubmitDisabled = Nothing})
+  v2 `seq` pure ()
+
+invalid73 :: Result ()
+invalid73 = do
+  v0 <- buildPlainText ((plainText "Modal only") {plainTextEmoji = Nothing})
+  v1 <- buildAlertBlock ((alertBlock (plainTextAsText v0)) {alertBlockLevel = Nothing, alertBlockBlockId = Nothing})
+  v2 <- buildHomeTabView ((homeTabView [alertBlockAsBlock v1]) {homeTabViewPrivateMetadata = Nothing, homeTabViewCallbackId = Nothing, homeTabViewExternalId = Nothing})
+  v2 `seq` pure ()
+
+invalid74 :: Result ()
+invalid74 = do
+  v0 <- buildPlainText ((plainText "Missing submit") {plainTextEmoji = Nothing})
+  v1 <- buildPlainText ((plainText "Name") {plainTextEmoji = Nothing})
+  v2 <- buildPlainTextInputElement (plainTextInputElement {plainTextInputElementActionId = Just "a", plainTextInputElementInitialValue = Nothing, plainTextInputElementMultiline = Nothing, plainTextInputElementMinLength = Nothing, plainTextInputElementMaxLength = Nothing, plainTextInputElementDispatchActionConfig = Nothing, plainTextInputElementFocusOnLoad = Nothing, plainTextInputElementPlaceholder = Nothing})
+  v3 <- buildInputBlock ((inputBlock v1 (plainTextInputElementAsInputElement v2)) {inputBlockDispatchAction = Nothing, inputBlockBlockId = Nothing, inputBlockHint = Nothing, inputBlockOptional = Nothing})
+  v4 <- buildModalView ((modalView v0 [inputBlockAsBlock v3]) {modalViewClose = Nothing, modalViewSubmit = Nothing, modalViewPrivateMetadata = Nothing, modalViewCallbackId = Nothing, modalViewClearOnClose = Nothing, modalViewNotifyOnClose = Nothing, modalViewExternalId = Nothing, modalViewSubmitDisabled = Nothing})
+  v4 `seq` pure ()
+
+invalid75 :: Result ()
+invalid75 = do
+  v0 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Nothing})
+  v1 <- buildHomeTabView ((homeTabView (replicate 101 (dividerBlockAsBlock v0))) {homeTabViewPrivateMetadata = Nothing, homeTabViewCallbackId = Nothing, homeTabViewExternalId = Nothing})
+  v1 `seq` pure ()
+
+invalid76 :: Result ()
+invalid76 = do
+  v0 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Nothing})
+  v1 <- buildHomeTabView ((homeTabView [dividerBlockAsBlock v0]) {homeTabViewPrivateMetadata = Just (T.replicate 3001 "x"), homeTabViewCallbackId = Nothing, homeTabViewExternalId = Nothing})
+  v1 `seq` pure ()
+
+invalid77 :: Result ()
+invalid77 = do
+  v0 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Nothing})
+  v1 <- buildHomeTabView ((homeTabView [dividerBlockAsBlock v0]) {homeTabViewPrivateMetadata = Nothing, homeTabViewCallbackId = Just (T.replicate 256 "x"), homeTabViewExternalId = Nothing})
+  v1 `seq` pure ()
+
+invalid78 :: Result ()
+invalid78 = do
+  v0 <- buildPlainText ((plainText (T.replicate 25 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Nothing})
+  v2 <- buildModalView ((modalView v0 [dividerBlockAsBlock v1]) {modalViewClose = Nothing, modalViewSubmit = Nothing, modalViewPrivateMetadata = Nothing, modalViewCallbackId = Nothing, modalViewClearOnClose = Nothing, modalViewNotifyOnClose = Nothing, modalViewExternalId = Nothing, modalViewSubmitDisabled = Nothing})
+  v2 `seq` pure ()
+
+invalid79 :: Result ()
+invalid79 = do
+  v0 <- buildPlainText ((plainText "Title") {plainTextEmoji = Nothing})
+  v1 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Nothing})
+  v2 <- buildPlainText ((plainText (T.replicate 25 "x")) {plainTextEmoji = Nothing})
+  v3 <- buildModalView ((modalView v0 [dividerBlockAsBlock v1]) {modalViewClose = Just v2, modalViewSubmit = Nothing, modalViewPrivateMetadata = Nothing, modalViewCallbackId = Nothing, modalViewClearOnClose = Nothing, modalViewNotifyOnClose = Nothing, modalViewExternalId = Nothing, modalViewSubmitDisabled = Nothing})
+  v3 `seq` pure ()
+
+invalid80 :: Result ()
+invalid80 = do
+  v0 <- buildPlainText ((plainText "Title") {plainTextEmoji = Nothing})
+  v1 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Nothing})
+  v2 <- buildPlainText ((plainText (T.replicate 25 "x")) {plainTextEmoji = Nothing})
+  v3 <- buildModalView ((modalView v0 [dividerBlockAsBlock v1]) {modalViewClose = Nothing, modalViewSubmit = Just v2, modalViewPrivateMetadata = Nothing, modalViewCallbackId = Nothing, modalViewClearOnClose = Nothing, modalViewNotifyOnClose = Nothing, modalViewExternalId = Nothing, modalViewSubmitDisabled = Nothing})
+  v3 `seq` pure ()
+
+invalid81 :: Result ()
+invalid81 = do
+  v0 <- buildSectionBlock (sectionBlock {sectionBlockText = Nothing, sectionBlockFields = Nothing, sectionBlockAccessory = Nothing, sectionBlockBlockId = Nothing})
+  v0 `seq` pure ()
+
+invalid82 :: Result ()
+invalid82 = do
+  v0 <- buildSectionBlock (sectionBlock {sectionBlockText = Nothing, sectionBlockFields = Just [], sectionBlockAccessory = Nothing, sectionBlockBlockId = Nothing})
+  v0 `seq` pure ()
+
+invalid83 :: Result ()
+invalid83 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildSelectOption ((selectOption (plainTextAsText v0) "a") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v2 <- buildPlainText ((plainText "B") {plainTextEmoji = Nothing})
+  v3 <- buildSelectOption ((selectOption (plainTextAsText v2) "b") {selectOptionDescription = Nothing, selectOptionUrl = Nothing})
+  v4 <- buildSelectOptionGroup (selectOptionGroup v0 [v3])
+  v5 <- buildStaticSelectElement (staticSelectElement {staticSelectElementActionId = Just "a", staticSelectElementOptions = Just [v1], staticSelectElementOptionGroups = Just [v4], staticSelectElementInitialOption = Nothing, staticSelectElementConfirm = Nothing, staticSelectElementFocusOnLoad = Nothing, staticSelectElementPlaceholder = Nothing})
+  v5 `seq` pure ()
+
+invalid84 :: Result ()
+invalid84 = do
+  v0 <- buildSlackFile (slackFile {slackFileId = Just "F0123ABC456", slackFileUrl = Nothing})
+  v1 <- buildImageElement ((imageElement "image") {imageElementImageUrl = Just "https://example.com/image.png", imageElementSlackFile = Just v0})
+  v1 `seq` pure ()
+
+invalid85 :: Result ()
+invalid85 = do
+  v0 <- buildNumberInputElement ((numberInputElement True) {numberInputElementActionId = Nothing, numberInputElementInitialValue = Nothing, numberInputElementMinValue = Just "9007199254740993", numberInputElementMaxValue = Just "9007199254740992", numberInputElementDispatchActionConfig = Nothing, numberInputElementFocusOnLoad = Nothing, numberInputElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid86 :: Result ()
+invalid86 = do
+  v0 <- buildNumberInputElement ((numberInputElement True) {numberInputElementActionId = Nothing, numberInputElementInitialValue = Nothing, numberInputElementMinValue = Just "0.10000000000000001", numberInputElementMaxValue = Just "0.1", numberInputElementDispatchActionConfig = Nothing, numberInputElementFocusOnLoad = Nothing, numberInputElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid87 :: Result ()
+invalid87 = do
+  v0 <- buildNumberInputElement ((numberInputElement True) {numberInputElementActionId = Nothing, numberInputElementInitialValue = Nothing, numberInputElementMinValue = Just "0x10", numberInputElementMaxValue = Nothing, numberInputElementDispatchActionConfig = Nothing, numberInputElementFocusOnLoad = Nothing, numberInputElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid88 :: Result ()
+invalid88 = do
+  v0 <- buildNumberInputElement ((numberInputElement True) {numberInputElementActionId = Nothing, numberInputElementInitialValue = Nothing, numberInputElementMinValue = Just "1_000", numberInputElementMaxValue = Nothing, numberInputElementDispatchActionConfig = Nothing, numberInputElementFocusOnLoad = Nothing, numberInputElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid89 :: Result ()
+invalid89 = do
+  v0 <- buildNumberInputElement ((numberInputElement True) {numberInputElementActionId = Nothing, numberInputElementInitialValue = Nothing, numberInputElementMinValue = Just " 1", numberInputElementMaxValue = Nothing, numberInputElementDispatchActionConfig = Nothing, numberInputElementFocusOnLoad = Nothing, numberInputElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid90 :: Result ()
+invalid90 = do
+  v0 <- buildNumberInputElement ((numberInputElement True) {numberInputElementActionId = Nothing, numberInputElementInitialValue = Nothing, numberInputElementMinValue = Just "1 ", numberInputElementMaxValue = Nothing, numberInputElementDispatchActionConfig = Nothing, numberInputElementFocusOnLoad = Nothing, numberInputElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid91 :: Result ()
+invalid91 = do
+  v0 <- buildNumberInputElement ((numberInputElement True) {numberInputElementActionId = Nothing, numberInputElementInitialValue = Nothing, numberInputElementMinValue = Just "NaN", numberInputElementMaxValue = Nothing, numberInputElementDispatchActionConfig = Nothing, numberInputElementFocusOnLoad = Nothing, numberInputElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid92 :: Result ()
+invalid92 = do
+  v0 <- buildNumberInputElement ((numberInputElement True) {numberInputElementActionId = Nothing, numberInputElementInitialValue = Nothing, numberInputElementMinValue = Just "Infinity", numberInputElementMaxValue = Nothing, numberInputElementDispatchActionConfig = Nothing, numberInputElementFocusOnLoad = Nothing, numberInputElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid93 :: Result ()
+invalid93 = do
+  v0 <- buildExternalMultiSelectElement (externalMultiSelectElement {externalMultiSelectElementActionId = Nothing, externalMultiSelectElementMinQueryLength = Nothing, externalMultiSelectElementInitialOptions = Nothing, externalMultiSelectElementConfirm = Nothing, externalMultiSelectElementMaxSelectedItems = Nothing, externalMultiSelectElementFocusOnLoad = Just False, externalMultiSelectElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid94 :: Result ()
+invalid94 = do
+  v0 <- buildExternalMultiSelectElement (externalMultiSelectElement {externalMultiSelectElementActionId = Nothing, externalMultiSelectElementMinQueryLength = Nothing, externalMultiSelectElementInitialOptions = Nothing, externalMultiSelectElementConfirm = Nothing, externalMultiSelectElementMaxSelectedItems = Nothing, externalMultiSelectElementFocusOnLoad = Just True, externalMultiSelectElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid95 :: Result ()
+invalid95 = do
+  v0 <- buildPlainTextInputElement (plainTextInputElement {plainTextInputElementActionId = Nothing, plainTextInputElementInitialValue = Nothing, plainTextInputElementMultiline = Nothing, plainTextInputElementMinLength = Just 20, plainTextInputElementMaxLength = Just 10, plainTextInputElementDispatchActionConfig = Nothing, plainTextInputElementFocusOnLoad = Nothing, plainTextInputElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid99 :: Result ()
+invalid99 = do
+  v0 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just (T.replicate 256 "x")})
+  v0 `seq` pure ()
+
+invalid100 :: Result ()
+invalid100 = do
+  v0 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v1 <- buildButtonElement ((buttonElement v0) {buttonElementActionId = Just "a", buttonElementUrl = Nothing, buttonElementValue = Nothing, buttonElementStyle = Nothing, buttonElementConfirm = Nothing, buttonElementAccessibilityLabel = Just (T.replicate 76 "x"), buttonElementAgentPrompt = Nothing, buttonElementAgentPromptDisplay = Nothing})
+  v1 `seq` pure ()
+
+invalid101 :: Result ()
+invalid101 = do
+  v0 <- buildPlainText ((plainText (T.replicate 76 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildTrigger ((trigger "https://slack.com/shortcuts/Ft0/abc") {triggerCustomizableInputParameters = Nothing})
+  v2 <- buildWorkflow (workflow v1)
+  v3 <- buildWorkflowButtonElement ((workflowButtonElement v0 v2 "a") {workflowButtonElementConfirm = Nothing, workflowButtonElementStyle = Nothing, workflowButtonElementAccessibilityLabel = Nothing})
+  v3 `seq` pure ()
+
+invalid102 :: Result ()
+invalid102 = do
+  v0 <- buildPlainText ((plainText "Run") {plainTextEmoji = Nothing})
+  v1 <- buildTrigger ((trigger "https://slack.com/shortcuts/Ft0/abc") {triggerCustomizableInputParameters = Nothing})
+  v2 <- buildWorkflow (workflow v1)
+  v3 <- buildWorkflowButtonElement ((workflowButtonElement v0 v2 "a") {workflowButtonElementConfirm = Nothing, workflowButtonElementStyle = Nothing, workflowButtonElementAccessibilityLabel = Just (T.replicate 76 "x")})
+  v3 `seq` pure ()
+
+invalid103 :: Result ()
+invalid103 = do
+  v0 <- buildPlainText ((plainText (T.replicate 201 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildAlertBlock ((alertBlock (plainTextAsText v0)) {alertBlockLevel = Nothing, alertBlockBlockId = Nothing})
+  v1 `seq` pure ()
+
+invalid104 :: Result ()
+invalid104 = do
+  v0 <- buildPlainText ((plainText (T.replicate 151 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildCardBlock (cardBlock {cardBlockHeroImage = Nothing, cardBlockIcon = Nothing, cardBlockTitle = Just (plainTextAsText v0), cardBlockSubtitle = Nothing, cardBlockBody = Nothing, cardBlockActions = Nothing, cardBlockSlackIcon = Nothing, cardBlockSubtext = Nothing, cardBlockBlockId = Nothing})
+  v1 `seq` pure ()
+
+invalid105 :: Result ()
+invalid105 = do
+  v0 <- buildPlainText ((plainText "Card") {plainTextEmoji = Nothing})
+  v1 <- buildPlainText ((plainText (T.replicate 151 "x")) {plainTextEmoji = Nothing})
+  v2 <- buildCardBlock (cardBlock {cardBlockHeroImage = Nothing, cardBlockIcon = Nothing, cardBlockTitle = Just (plainTextAsText v0), cardBlockSubtitle = Just (plainTextAsText v1), cardBlockBody = Nothing, cardBlockActions = Nothing, cardBlockSlackIcon = Nothing, cardBlockSubtext = Nothing, cardBlockBlockId = Nothing})
+  v2 `seq` pure ()
+
+invalid106 :: Result ()
+invalid106 = do
+  v0 <- buildPlainText ((plainText (T.replicate 201 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildCardBlock (cardBlock {cardBlockHeroImage = Nothing, cardBlockIcon = Nothing, cardBlockTitle = Nothing, cardBlockSubtitle = Nothing, cardBlockBody = Just (plainTextAsText v0), cardBlockActions = Nothing, cardBlockSlackIcon = Nothing, cardBlockSubtext = Nothing, cardBlockBlockId = Nothing})
+  v1 `seq` pure ()
+
+invalid107 :: Result ()
+invalid107 = do
+  v0 <- buildPlainText ((plainText "Card") {plainTextEmoji = Nothing})
+  v1 <- buildPlainText ((plainText "A") {plainTextEmoji = Nothing})
+  v2 <- buildButtonElement ((buttonElement v1) {buttonElementActionId = Just "a", buttonElementUrl = Nothing, buttonElementValue = Nothing, buttonElementStyle = Nothing, buttonElementConfirm = Nothing, buttonElementAccessibilityLabel = Nothing, buttonElementAgentPrompt = Nothing, buttonElementAgentPromptDisplay = Nothing})
+  v3 <- buildCardBlock (cardBlock {cardBlockHeroImage = Nothing, cardBlockIcon = Nothing, cardBlockTitle = Just (plainTextAsText v0), cardBlockSubtitle = Nothing, cardBlockBody = Nothing, cardBlockActions = Just (replicate 4 v2), cardBlockSlackIcon = Nothing, cardBlockSubtext = Nothing, cardBlockBlockId = Nothing})
+  v3 `seq` pure ()
+
+invalid108 :: Result ()
+invalid108 = do
+  v0 <- buildPlainText ((plainText "Card") {plainTextEmoji = Nothing})
+  v1 <- buildPlainText ((plainText (T.replicate 201 "x")) {plainTextEmoji = Nothing})
+  v2 <- buildCardBlock (cardBlock {cardBlockHeroImage = Nothing, cardBlockIcon = Nothing, cardBlockTitle = Just (plainTextAsText v0), cardBlockSubtitle = Nothing, cardBlockBody = Nothing, cardBlockActions = Nothing, cardBlockSlackIcon = Nothing, cardBlockSubtext = Just (plainTextAsText v1), cardBlockBlockId = Nothing})
+  v2 `seq` pure ()
+
+invalid109 :: Result ()
+invalid109 = do
+  v0 <- buildCarouselBlock ((carouselBlock []) {carouselBlockBlockId = Nothing})
+  v0 `seq` pure ()
+
+invalid110 :: Result ()
+invalid110 = do
+  v0 <- buildPlainText ((plainText "Card") {plainTextEmoji = Nothing})
+  v1 <- buildCardBlock (cardBlock {cardBlockHeroImage = Nothing, cardBlockIcon = Nothing, cardBlockTitle = Just (plainTextAsText v0), cardBlockSubtitle = Nothing, cardBlockBody = Nothing, cardBlockActions = Nothing, cardBlockSlackIcon = Nothing, cardBlockSubtext = Nothing, cardBlockBlockId = Nothing})
+  v2 <- buildCarouselBlock ((carouselBlock (replicate 11 v1)) {carouselBlockBlockId = Nothing})
+  v2 `seq` pure ()
+
+invalid111 :: Result ()
+invalid111 = do
+  v0 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Nothing})
+  v1 <- buildPlainText ((plainText (T.replicate 151 "x")) {plainTextEmoji = Nothing})
+  v2 <- buildContainerBlock ((containerBlock [dividerBlockAsBlock v0]) {containerBlockTitle = Just v1, containerBlockRichTextTitle = Nothing, containerBlockSubtitle = Nothing, containerBlockWidth = Nothing, containerBlockIcon = Nothing, containerBlockIsCollapsible = Nothing, containerBlockDefaultCollapsed = Nothing, containerBlockHasHeaderDivider = Nothing, containerBlockBlockId = Nothing})
+  v2 `seq` pure ()
+
+invalid112 :: Result ()
+invalid112 = do
+  v0 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Nothing})
+  v1 <- buildPlainText ((plainText "Container") {plainTextEmoji = Nothing})
+  v2 <- buildPlainText ((plainText (T.replicate 151 "x")) {plainTextEmoji = Nothing})
+  v3 <- buildContainerBlock ((containerBlock [dividerBlockAsBlock v0]) {containerBlockTitle = Just v1, containerBlockRichTextTitle = Nothing, containerBlockSubtitle = Just (plainTextAsText v2), containerBlockWidth = Nothing, containerBlockIcon = Nothing, containerBlockIsCollapsible = Nothing, containerBlockDefaultCollapsed = Nothing, containerBlockHasHeaderDivider = Nothing, containerBlockBlockId = Nothing})
+  v3 `seq` pure ()
+
+invalid113 :: Result ()
+invalid113 = do
+  v0 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Nothing})
+  v1 <- buildPlainText ((plainText "Container") {plainTextEmoji = Nothing})
+  v2 <- buildContainerBlock ((containerBlock (replicate 11 (dividerBlockAsBlock v0))) {containerBlockTitle = Just v1, containerBlockRichTextTitle = Nothing, containerBlockSubtitle = Nothing, containerBlockWidth = Nothing, containerBlockIcon = Nothing, containerBlockIsCollapsible = Nothing, containerBlockDefaultCollapsed = Nothing, containerBlockHasHeaderDivider = Nothing, containerBlockBlockId = Nothing})
+  v2 `seq` pure ()
+
+invalid114 :: Result ()
+invalid114 = do
+  v0 <- buildPlainText ((plainText "Delete") {plainTextEmoji = Nothing})
+  v1 <- buildIconButtonElement ((iconButtonElement v0 IconButtonIconTrash) {iconButtonElementActionId = Nothing, iconButtonElementValue = Nothing, iconButtonElementConfirm = Nothing, iconButtonElementAccessibilityLabel = Nothing, iconButtonElementVisibleToUserIds = Nothing})
+  v2 <- buildContextActionsBlock ((contextActionsBlock (replicate 6 (iconButtonElementAsContextActionsElement v1))) {contextActionsBlockBlockId = Nothing})
+  v2 `seq` pure ()
+
+invalid115 :: Result ()
+invalid115 = do
+  v0 <- buildPlainText ((plainText (T.replicate 76 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildFeedbackButton ((feedbackButton v0 "good") {feedbackButtonAccessibilityLabel = Nothing})
+  v2 <- buildPlainText ((plainText "Bad") {plainTextEmoji = Nothing})
+  v3 <- buildFeedbackButton ((feedbackButton v2 "bad") {feedbackButtonAccessibilityLabel = Nothing})
+  v4 <- buildFeedbackButtonsElement ((feedbackButtonsElement v1 v3) {feedbackButtonsElementActionId = Nothing})
+  v4 `seq` pure ()
+
+invalid116 :: Result ()
+invalid116 = do
+  v0 <- buildPlainText ((plainText "Good") {plainTextEmoji = Nothing})
+  v1 <- buildFeedbackButton ((feedbackButton v0 (T.replicate 2001 "x")) {feedbackButtonAccessibilityLabel = Nothing})
+  v2 <- buildPlainText ((plainText "Bad") {plainTextEmoji = Nothing})
+  v3 <- buildFeedbackButton ((feedbackButton v2 "bad") {feedbackButtonAccessibilityLabel = Nothing})
+  v4 <- buildFeedbackButtonsElement ((feedbackButtonsElement v1 v3) {feedbackButtonsElementActionId = Nothing})
+  v4 `seq` pure ()
+
+invalid117 :: Result ()
+invalid117 = do
+  v0 <- buildPlainText ((plainText "Good") {plainTextEmoji = Nothing})
+  v1 <- buildFeedbackButton ((feedbackButton v0 "good") {feedbackButtonAccessibilityLabel = Just (T.replicate 76 "x")})
+  v2 <- buildPlainText ((plainText "Bad") {plainTextEmoji = Nothing})
+  v3 <- buildFeedbackButton ((feedbackButton v2 "bad") {feedbackButtonAccessibilityLabel = Nothing})
+  v4 <- buildFeedbackButtonsElement ((feedbackButtonsElement v1 v3) {feedbackButtonsElementActionId = Nothing})
+  v4 `seq` pure ()
+
+invalid118 :: Result ()
+invalid118 = do
+  v0 <- buildPlainText ((plainText "Delete") {plainTextEmoji = Nothing})
+  v1 <- buildIconButtonElement ((iconButtonElement v0 IconButtonIconTrash) {iconButtonElementActionId = Nothing, iconButtonElementValue = Nothing, iconButtonElementConfirm = Nothing, iconButtonElementAccessibilityLabel = Nothing, iconButtonElementVisibleToUserIds = Just ["U0", "U1", "U2", "U3", "U4", "U5", "U6", "U7", "U8", "U9", "U10"]})
+  v1 `seq` pure ()
+
+invalid119 :: Result ()
+invalid119 = do
+  v0 <- buildPlainText ((plainText "Delete") {plainTextEmoji = Nothing})
+  v1 <- buildIconButtonElement ((iconButtonElement v0 IconButtonIconTrash) {iconButtonElementActionId = Nothing, iconButtonElementValue = Just (T.replicate 2001 "x"), iconButtonElementConfirm = Nothing, iconButtonElementAccessibilityLabel = Nothing, iconButtonElementVisibleToUserIds = Nothing})
+  v1 `seq` pure ()
+
+invalid120 :: Result ()
+invalid120 = do
+  v0 <- buildPlainText ((plainText "Delete") {plainTextEmoji = Nothing})
+  v1 <- buildIconButtonElement ((iconButtonElement v0 IconButtonIconTrash) {iconButtonElementActionId = Nothing, iconButtonElementValue = Nothing, iconButtonElementConfirm = Nothing, iconButtonElementAccessibilityLabel = Just (T.replicate 76 "x"), iconButtonElementVisibleToUserIds = Nothing})
+  v1 `seq` pure ()
+
+invalid121 :: Result ()
+invalid121 = do
+  v0 <- buildRawText (rawText "Name")
+  v1 <- buildDataTableBlock ((dataTableBlock [[rawTextAsDataTableCell v0]] "Names") {dataTableBlockPageSize = Nothing, dataTableBlockRowHeaderColumnIndex = Nothing, dataTableBlockBlockId = Nothing})
+  v1 `seq` pure ()
+
+invalid122 :: Result ()
+invalid122 = do
+  v0 <- buildRawText (rawText "A")
+  v1 <- buildDataTableBlock ((dataTableBlock [[rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0]] "Names") {dataTableBlockPageSize = Nothing, dataTableBlockRowHeaderColumnIndex = Nothing, dataTableBlockBlockId = Nothing})
+  v1 `seq` pure ()
+
+invalid123 :: Result ()
+invalid123 = do
+  v0 <- buildDataTableBlock ((dataTableBlock [[], []] "Empty") {dataTableBlockPageSize = Nothing, dataTableBlockRowHeaderColumnIndex = Nothing, dataTableBlockBlockId = Nothing})
+  v0 `seq` pure ()
+
+invalid124 :: Result ()
+invalid124 = do
+  v0 <- buildRawText (rawText "A")
+  v1 <- buildDataTableBlock ((dataTableBlock [[rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0], [rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0, rawTextAsDataTableCell v0]] "Wide") {dataTableBlockPageSize = Nothing, dataTableBlockRowHeaderColumnIndex = Nothing, dataTableBlockBlockId = Nothing})
+  v1 `seq` pure ()
+
+invalid125 :: Result ()
+invalid125 = do
+  v0 <- buildRawText (rawText "Name")
+  v1 <- buildRawText (rawText "Alice")
+  v2 <- buildDataTableBlock ((dataTableBlock [[rawTextAsDataTableCell v0], [rawTextAsDataTableCell v1]] "Names") {dataTableBlockPageSize = Just 0, dataTableBlockRowHeaderColumnIndex = Nothing, dataTableBlockBlockId = Nothing})
+  v2 `seq` pure ()
+
+invalid126 :: Result ()
+invalid126 = do
+  v0 <- buildRawText (rawText "Name")
+  v1 <- buildRawText (rawText "Alice")
+  v2 <- buildDataTableBlock ((dataTableBlock [[rawTextAsDataTableCell v0], [rawTextAsDataTableCell v1]] "Names") {dataTableBlockPageSize = Just 101, dataTableBlockRowHeaderColumnIndex = Nothing, dataTableBlockBlockId = Nothing})
+  v2 `seq` pure ()
+
+invalid127 :: Result ()
+invalid127 = do
+  v0 <- buildRawText (rawText "Name")
+  v1 <- buildRawText (rawText "")
+  v2 <- buildDataTableBlock ((dataTableBlock [[rawTextAsDataTableCell v0], [rawTextAsDataTableCell v1]] "Names") {dataTableBlockPageSize = Nothing, dataTableBlockRowHeaderColumnIndex = Nothing, dataTableBlockBlockId = Nothing})
+  v2 `seq` pure ()
+
+invalid128 :: Result ()
+invalid128 = do
+  v0 <- buildRawText (rawText "Name")
+  v1 <- buildRawText (rawText (T.replicate 20000 "x"))
+  v2 <- buildDataTableBlock ((dataTableBlock [[rawTextAsDataTableCell v0], [rawTextAsDataTableCell v1]] "Names") {dataTableBlockPageSize = Nothing, dataTableBlockRowHeaderColumnIndex = Nothing, dataTableBlockBlockId = Nothing})
+  v2 `seq` pure ()
+
+invalid129 :: Result ()
+invalid129 = do
+  v0 <- buildChartSegment (chartSegment "A" 1)
+  v1 <- buildPieChart (pieChart [v0])
+  v2 <- buildDataVisualizationBlock ((dataVisualizationBlock "Chart" (pieChartAsChart v1)) {dataVisualizationBlockBlockId = Nothing})
+  v3 <- buildMessagePayload ((messagePayload "C123") {messagePayloadBlocks = Just (replicate 3 (dataVisualizationBlockAsBlock v2)), messagePayloadAttachments = Nothing, messagePayloadText = Nothing, messagePayloadMrkdwn = Nothing, messagePayloadUnfurlLinks = Nothing, messagePayloadUnfurlMedia = Nothing, messagePayloadMetadata = Nothing})
+  v3 `seq` pure ()
+
+invalid130 :: Result ()
+invalid130 = do
+  v0 <- buildChartSegment (chartSegment "A" 1)
+  v1 <- buildPieChart (pieChart [v0])
+  v2 <- buildDataVisualizationBlock ((dataVisualizationBlock (T.replicate 51 "x") (pieChartAsChart v1)) {dataVisualizationBlockBlockId = Nothing})
+  v2 `seq` pure ()
+
+invalid131 :: Result ()
+invalid131 = do
+  v0 <- buildPieChart (pieChart [])
+  v0 `seq` pure ()
+
+invalid132 :: Result ()
+invalid132 = do
+  v0 <- buildChartSegment (chartSegment "S" 1)
+  v1 <- buildPieChart (pieChart (replicate 13 v0))
+  v1 `seq` pure ()
+
+invalid133 :: Result ()
+invalid133 = do
+  v0 <- buildChartSegment (chartSegment (T.replicate 21 "x") 1)
+  v0 `seq` pure ()
+
+invalid134 :: Result ()
+invalid134 = do
+  v0 <- buildChartSegment (chartSegment "A" 0)
+  v0 `seq` pure ()
+
+invalid135 :: Result ()
+invalid135 = do
+  v0 <- buildAxisConfig ((axisConfig ["A"]) {axisConfigXLabel = Nothing, axisConfigYLabel = Nothing})
+  v1 <- buildLineChart (lineChart [] v0)
+  v1 `seq` pure ()
+
+invalid136 :: Result ()
+invalid136 = do
+  v0 <- buildDataPoint (dataPoint "A" 1)
+  v1 <- buildDataPoint (dataPoint "A" 2)
+  v2 <- buildDataSeries (dataSeries "Series" [v0, v1])
+  v3 <- buildAxisConfig ((axisConfig ["A", "B"]) {axisConfigXLabel = Nothing, axisConfigYLabel = Nothing})
+  v4 <- buildLineChart (lineChart [v2] v3)
+  v4 `seq` pure ()
+
+invalid137 :: Result ()
+invalid137 = do
+  v0 <- buildDataPoint (dataPoint "A" 1)
+  v1 <- buildDataSeries (dataSeries "S0" [v0])
+  v2 <- buildDataSeries (dataSeries "S1" [v0])
+  v3 <- buildDataSeries (dataSeries "S2" [v0])
+  v4 <- buildDataSeries (dataSeries "S3" [v0])
+  v5 <- buildDataSeries (dataSeries "S4" [v0])
+  v6 <- buildDataSeries (dataSeries "S5" [v0])
+  v7 <- buildDataSeries (dataSeries "S6" [v0])
+  v8 <- buildDataSeries (dataSeries "S7" [v0])
+  v9 <- buildDataSeries (dataSeries "S8" [v0])
+  v10 <- buildDataSeries (dataSeries "S9" [v0])
+  v11 <- buildDataSeries (dataSeries "S10" [v0])
+  v12 <- buildDataSeries (dataSeries "S11" [v0])
+  v13 <- buildDataSeries (dataSeries "S12" [v0])
+  v14 <- buildAxisConfig ((axisConfig ["A"]) {axisConfigXLabel = Nothing, axisConfigYLabel = Nothing})
+  v15 <- buildLineChart (lineChart [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13] v14)
+  v15 `seq` pure ()
+
+invalid138 :: Result ()
+invalid138 = do
+  v0 <- buildDataPoint (dataPoint "A" 1)
+  v1 <- buildDataSeries (dataSeries (T.replicate 21 "x") [v0])
+  v1 `seq` pure ()
+
+invalid139 :: Result ()
+invalid139 = do
+  v0 <- buildDataSeries (dataSeries "Series" [])
+  v0 `seq` pure ()
+
+invalid140 :: Result ()
+invalid140 = do
+  v0 <- buildDataPoint (dataPoint "P0" 0)
+  v1 <- buildDataPoint (dataPoint "P1" 1)
+  v2 <- buildDataPoint (dataPoint "P2" 2)
+  v3 <- buildDataPoint (dataPoint "P3" 3)
+  v4 <- buildDataPoint (dataPoint "P4" 4)
+  v5 <- buildDataPoint (dataPoint "P5" 5)
+  v6 <- buildDataPoint (dataPoint "P6" 6)
+  v7 <- buildDataPoint (dataPoint "P7" 7)
+  v8 <- buildDataPoint (dataPoint "P8" 8)
+  v9 <- buildDataPoint (dataPoint "P9" 9)
+  v10 <- buildDataPoint (dataPoint "P10" 10)
+  v11 <- buildDataPoint (dataPoint "P11" 11)
+  v12 <- buildDataPoint (dataPoint "P12" 12)
+  v13 <- buildDataPoint (dataPoint "P13" 13)
+  v14 <- buildDataPoint (dataPoint "P14" 14)
+  v15 <- buildDataPoint (dataPoint "P15" 15)
+  v16 <- buildDataPoint (dataPoint "P16" 16)
+  v17 <- buildDataPoint (dataPoint "P17" 17)
+  v18 <- buildDataPoint (dataPoint "P18" 18)
+  v19 <- buildDataPoint (dataPoint "P19" 19)
+  v20 <- buildDataPoint (dataPoint "P20" 20)
+  v21 <- buildDataSeries (dataSeries "Series" [v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20])
+  v21 `seq` pure ()
+
+invalid141 :: Result ()
+invalid141 = do
+  v0 <- buildDataPoint (dataPoint (T.replicate 21 "x") 1)
+  v0 `seq` pure ()
+
+invalid142 :: Result ()
+invalid142 = do
+  v0 <- buildAxisConfig ((axisConfig []) {axisConfigXLabel = Nothing, axisConfigYLabel = Nothing})
+  v0 `seq` pure ()
+
+invalid143 :: Result ()
+invalid143 = do
+  v0 <- buildAxisConfig ((axisConfig ["X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7", "X8", "X9", "X10", "X11", "X12", "X13", "X14", "X15", "X16", "X17", "X18", "X19", "X20"]) {axisConfigXLabel = Nothing, axisConfigYLabel = Nothing})
+  v0 `seq` pure ()
+
+invalid144 :: Result ()
+invalid144 = do
+  v0 <- buildAxisConfig ((axisConfig [T.replicate 21 "x"]) {axisConfigXLabel = Nothing, axisConfigYLabel = Nothing})
+  v0 `seq` pure ()
+
+invalid145 :: Result ()
+invalid145 = do
+  v0 <- buildAxisConfig ((axisConfig ["A"]) {axisConfigXLabel = Just (T.replicate 51 "x"), axisConfigYLabel = Nothing})
+  v0 `seq` pure ()
+
+invalid146 :: Result ()
+invalid146 = do
+  v0 <- buildPlainTextInputElement (plainTextInputElement {plainTextInputElementActionId = Just "a", plainTextInputElementInitialValue = Nothing, plainTextInputElementMultiline = Nothing, plainTextInputElementMinLength = Just (-1), plainTextInputElementMaxLength = Nothing, plainTextInputElementDispatchActionConfig = Nothing, plainTextInputElementFocusOnLoad = Nothing, plainTextInputElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid147 :: Result ()
+invalid147 = do
+  v0 <- buildPlainTextInputElement (plainTextInputElement {plainTextInputElementActionId = Just "a", plainTextInputElementInitialValue = Nothing, plainTextInputElementMultiline = Nothing, plainTextInputElementMinLength = Just 3001, plainTextInputElementMaxLength = Nothing, plainTextInputElementDispatchActionConfig = Nothing, plainTextInputElementFocusOnLoad = Nothing, plainTextInputElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid148 :: Result ()
+invalid148 = do
+  v0 <- buildPlainTextInputElement (plainTextInputElement {plainTextInputElementActionId = Just "a", plainTextInputElementInitialValue = Nothing, plainTextInputElementMultiline = Nothing, plainTextInputElementMinLength = Nothing, plainTextInputElementMaxLength = Just 0, plainTextInputElementDispatchActionConfig = Nothing, plainTextInputElementFocusOnLoad = Nothing, plainTextInputElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid149 :: Result ()
+invalid149 = do
+  v0 <- buildRichTextInputElement ((richTextInputElement "a") {richTextInputElementInitialValue = Nothing, richTextInputElementDispatchActionConfig = Nothing, richTextInputElementFocusOnLoad = Nothing, richTextInputElementPlaceholder = Nothing, richTextInputElementMinLines = Just 0, richTextInputElementMaxLines = Nothing})
+  v0 `seq` pure ()
+
+invalid150 :: Result ()
+invalid150 = do
+  v0 <- buildRichTextInputElement ((richTextInputElement "a") {richTextInputElementInitialValue = Nothing, richTextInputElementDispatchActionConfig = Nothing, richTextInputElementFocusOnLoad = Nothing, richTextInputElementPlaceholder = Nothing, richTextInputElementMinLines = Just 101, richTextInputElementMaxLines = Nothing})
+  v0 `seq` pure ()
+
+invalid151 :: Result ()
+invalid151 = do
+  v0 <- buildRichTextInputElement ((richTextInputElement "a") {richTextInputElementInitialValue = Nothing, richTextInputElementDispatchActionConfig = Nothing, richTextInputElementFocusOnLoad = Nothing, richTextInputElementPlaceholder = Nothing, richTextInputElementMinLines = Nothing, richTextInputElementMaxLines = Just 0})
+  v0 `seq` pure ()
+
+invalid152 :: Result ()
+invalid152 = do
+  v0 <- buildRichTextInputElement ((richTextInputElement "a") {richTextInputElementInitialValue = Nothing, richTextInputElementDispatchActionConfig = Nothing, richTextInputElementFocusOnLoad = Nothing, richTextInputElementPlaceholder = Nothing, richTextInputElementMinLines = Nothing, richTextInputElementMaxLines = Just 101})
+  v0 `seq` pure ()
+
+invalid153 :: Result ()
+invalid153 = do
+  v0 <- buildUserMultiSelectElement (userMultiSelectElement {userMultiSelectElementActionId = Just "a", userMultiSelectElementInitialUsers = Nothing, userMultiSelectElementConfirm = Nothing, userMultiSelectElementMaxSelectedItems = Just 0, userMultiSelectElementFocusOnLoad = Nothing, userMultiSelectElementPlaceholder = Nothing})
+  v0 `seq` pure ()
+
+invalid154 :: Result ()
+invalid154 = do
+  v0 <- buildConversationFilter (conversationFilter {conversationFilterInclude = Just [], conversationFilterExcludeExternalSharedChannels = Nothing, conversationFilterExcludeBotUsers = Nothing})
+  v0 `seq` pure ()
+
+invalid155 :: Result ()
+invalid155 = do
+  v0 <- buildConversationFilter (conversationFilter {conversationFilterInclude = Just ["channel"], conversationFilterExcludeExternalSharedChannels = Nothing, conversationFilterExcludeBotUsers = Nothing})
+  v0 `seq` pure ()
+
+invalid159 :: Result ()
+invalid159 = do
+  v0 <- buildSlackFile (slackFile {slackFileId = Just "F0123456", slackFileUrl = Nothing})
+  v0 `seq` pure ()
+
+invalid160 :: Result ()
+invalid160 = do
+  v0 <- buildImageElement ((imageElement "Alt") {imageElementImageUrl = Just (T.replicate 3001 "x"), imageElementSlackFile = Nothing})
+  v0 `seq` pure ()
+
+invalid161 :: Result ()
+invalid161 = do
+  v0 <- buildImageElement ((imageElement (T.replicate 2001 "x")) {imageElementImageUrl = Just "https://example.com/image.png", imageElementSlackFile = Nothing})
+  v0 `seq` pure ()
+
+invalid162 :: Result ()
+invalid162 = do
+  v0 <- buildPlainText ((plainText (T.replicate 2001 "x")) {plainTextEmoji = Nothing})
+  v1 <- buildImageBlock ((imageBlock "Alt") {imageBlockImageUrl = Just "https://example.com/image.png", imageBlockSlackFile = Nothing, imageBlockTitle = Just v0, imageBlockBlockId = Nothing})
+  v1 `seq` pure ()
+
+invalid163 :: Result ()
+invalid163 = do
+  v0 <- buildSlackFile (slackFile {slackFileId = Just "F0123ABC456", slackFileUrl = Nothing})
+  v1 <- buildImageBlock ((imageBlock "Alt") {imageBlockImageUrl = Just "https://example.com/image.png", imageBlockSlackFile = Just v0, imageBlockTitle = Nothing, imageBlockBlockId = Nothing})
+  v1 `seq` pure ()
+
+invalid164 :: Result ()
+invalid164 = do
+  v0 <- buildImageBlock ((imageBlock "Alt") {imageBlockImageUrl = Nothing, imageBlockSlackFile = Nothing, imageBlockTitle = Nothing, imageBlockBlockId = Nothing})
+  v0 `seq` pure ()
+
+invalid165 :: Result ()
+invalid165 = do
+  v0 <- buildPlainText ((plainText "Container") {plainTextEmoji = Nothing})
+  v1 <- buildContainerBlock ((containerBlock []) {containerBlockTitle = Just v0, containerBlockRichTextTitle = Nothing, containerBlockSubtitle = Nothing, containerBlockWidth = Nothing, containerBlockIcon = Nothing, containerBlockIsCollapsible = Nothing, containerBlockDefaultCollapsed = Nothing, containerBlockHasHeaderDivider = Nothing, containerBlockBlockId = Nothing})
+  v1 `seq` pure ()
+
+invalid166 :: Result ()
+invalid166 = do
+  v0 <- buildRawText (rawText "A")
+  v1 <- buildColumnSettings (columnSettings {columnSettingsAlign = Nothing, columnSettingsIsWrapped = Just True})
+  v2 <- buildTableBlock ((tableBlock [[rawTextAsTableCell v0]]) {tableBlockColumnSettings = Just (replicate 21 v1), tableBlockBlockId = Nothing})
+  v2 `seq` pure ()
+
+invalid167 :: Result ()
+invalid167 = do
+  v0 <- buildRawText (rawText "Name")
+  v1 <- buildRawText (rawText "Alice")
+  v2 <- buildDataTableBlock ((dataTableBlock [[rawTextAsDataTableCell v0], [rawTextAsDataTableCell v1]] "Names") {dataTableBlockPageSize = Nothing, dataTableBlockRowHeaderColumnIndex = Just (-1), dataTableBlockBlockId = Nothing})
+  v2 `seq` pure ()
+
+invalid168 :: Result ()
+invalid168 = do
+  v0 <- buildRawText (rawText "Name")
+  v1 <- buildRawText (rawText (T.replicate 9997 "x"))
+  v2 <- buildDataTableBlock ((dataTableBlock [[rawTextAsDataTableCell v0], [rawTextAsDataTableCell v1]] "Names") {dataTableBlockPageSize = Nothing, dataTableBlockRowHeaderColumnIndex = Nothing, dataTableBlockBlockId = Nothing})
+  v3 <- buildMessagePayload ((messagePayload "C123") {messagePayloadBlocks = Just [dataTableBlockAsBlock v2, dataTableBlockAsBlock v2], messagePayloadAttachments = Nothing, messagePayloadText = Nothing, messagePayloadMrkdwn = Nothing, messagePayloadUnfurlLinks = Nothing, messagePayloadUnfurlMedia = Nothing, messagePayloadMetadata = Nothing})
+  v3 `seq` pure ()
+
+invalid169 :: Result ()
+invalid169 = do
+  v0 <- buildMarkdownBlock ((markdownBlock (T.replicate 6001 "x")) {markdownBlockBlockId = Nothing})
+  v1 <- buildMessagePayload ((messagePayload "C123") {messagePayloadBlocks = Just [markdownBlockAsBlock v0, markdownBlockAsBlock v0], messagePayloadAttachments = Nothing, messagePayloadText = Nothing, messagePayloadMrkdwn = Nothing, messagePayloadUnfurlLinks = Nothing, messagePayloadUnfurlMedia = Nothing, messagePayloadMetadata = Nothing})
+  v1 `seq` pure ()
+
+invalid171 :: Result ()
+invalid171 = do
+  v0 <- buildPlanTask ((taskCardBlock "task_0" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v1 <- buildPlanTask ((taskCardBlock "task_1" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v2 <- buildPlanTask ((taskCardBlock "task_2" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v3 <- buildPlanTask ((taskCardBlock "task_3" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v4 <- buildPlanTask ((taskCardBlock "task_4" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v5 <- buildPlanTask ((taskCardBlock "task_5" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v6 <- buildPlanTask ((taskCardBlock "task_6" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v7 <- buildPlanTask ((taskCardBlock "task_7" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v8 <- buildPlanTask ((taskCardBlock "task_8" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v9 <- buildPlanTask ((taskCardBlock "task_9" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v10 <- buildPlanTask ((taskCardBlock "task_10" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v11 <- buildPlanTask ((taskCardBlock "task_11" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v12 <- buildPlanTask ((taskCardBlock "task_12" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v13 <- buildPlanTask ((taskCardBlock "task_13" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v14 <- buildPlanTask ((taskCardBlock "task_14" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v15 <- buildPlanTask ((taskCardBlock "task_15" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v16 <- buildPlanTask ((taskCardBlock "task_16" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v17 <- buildPlanTask ((taskCardBlock "task_17" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v18 <- buildPlanTask ((taskCardBlock "task_18" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v19 <- buildPlanTask ((taskCardBlock "task_19" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v20 <- buildPlanTask ((taskCardBlock "task_20" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v21 <- buildPlanTask ((taskCardBlock "task_21" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v22 <- buildPlanTask ((taskCardBlock "task_22" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v23 <- buildPlanTask ((taskCardBlock "task_23" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v24 <- buildPlanTask ((taskCardBlock "task_24" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v25 <- buildPlanTask ((taskCardBlock "task_25" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v26 <- buildPlanTask ((taskCardBlock "task_26" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v27 <- buildPlanTask ((taskCardBlock "task_27" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v28 <- buildPlanTask ((taskCardBlock "task_28" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v29 <- buildPlanTask ((taskCardBlock "task_29" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v30 <- buildPlanTask ((taskCardBlock "task_30" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v31 <- buildPlanTask ((taskCardBlock "task_31" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v32 <- buildPlanTask ((taskCardBlock "task_32" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v33 <- buildPlanTask ((taskCardBlock "task_33" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v34 <- buildPlanTask ((taskCardBlock "task_34" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v35 <- buildPlanTask ((taskCardBlock "task_35" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v36 <- buildPlanTask ((taskCardBlock "task_36" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v37 <- buildPlanTask ((taskCardBlock "task_37" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v38 <- buildPlanTask ((taskCardBlock "task_38" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v39 <- buildPlanTask ((taskCardBlock "task_39" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v40 <- buildPlanTask ((taskCardBlock "task_40" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v41 <- buildPlanTask ((taskCardBlock "task_41" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v42 <- buildPlanTask ((taskCardBlock "task_42" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v43 <- buildPlanTask ((taskCardBlock "task_43" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v44 <- buildPlanTask ((taskCardBlock "task_44" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v45 <- buildPlanTask ((taskCardBlock "task_45" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v46 <- buildPlanTask ((taskCardBlock "task_46" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v47 <- buildPlanTask ((taskCardBlock "task_47" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v48 <- buildPlanTask ((taskCardBlock "task_48" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v49 <- buildPlanTask ((taskCardBlock "task_49" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v50 <- buildPlanTask ((taskCardBlock "task_50" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v51 <- buildPlanBlock ((planBlock "Plan" [v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50]) {planBlockBlockId = Nothing})
+  v51 `seq` pure ()
+
+invalid172 :: Result ()
+invalid172 = do
+  v0 <- buildPlanTask ((taskCardBlock "task" "Task" TaskStatusComplete) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v1 <- buildPlanBlock ((planBlock "Plan" [v0, v0]) {planBlockBlockId = Nothing})
+  v1 `seq` pure ()
+
+invalid174 :: Result ()
+invalid174 = do
+  v0 <- buildTaskCardBlock ((taskCardBlock "task" "Task" TaskStatusPending) {taskCardBlockDetails = Nothing, taskCardBlockOutput = Nothing, taskCardBlockSources = Nothing, taskCardBlockBlockId = Nothing})
+  v1 <- taskCardBlockAsBlock v0
+  v2 <- buildMessagePayload ((messagePayload "C123") {messagePayloadBlocks = Just [v1], messagePayloadAttachments = Nothing, messagePayloadText = Nothing, messagePayloadMrkdwn = Nothing, messagePayloadUnfurlLinks = Nothing, messagePayloadUnfurlMedia = Nothing, messagePayloadMetadata = Nothing})
+  v2 `seq` pure ()
+
+invalid175 :: Result ()
+invalid175 = do
+  v0 <- buildRichTextText ((richTextText "Item") {richTextTextStyle = Nothing})
+  v1 <- buildRichTextSection (richTextSection [richTextTextAsRichTextSectionElement v0])
+  v2 <- buildRichTextList ((richTextList RichTextListStyleBullet [v1]) {richTextListIndent = Just (-1), richTextListOffset = Nothing, richTextListBorder = Nothing})
+  v2 `seq` pure ()
+
+invalid176 :: Result ()
+invalid176 = do
+  v0 <- buildRichTextText ((richTextText "Item") {richTextTextStyle = Nothing})
+  v1 <- buildRichTextSection (richTextSection [richTextTextAsRichTextSectionElement v0])
+  v2 <- buildRichTextList ((richTextList RichTextListStyleBullet [v1]) {richTextListIndent = Just 9, richTextListOffset = Nothing, richTextListBorder = Nothing})
+  v2 `seq` pure ()
+
+invalid177 :: Result ()
+invalid177 = do
+  v0 <- buildRichTextText ((richTextText "Item") {richTextTextStyle = Nothing})
+  v1 <- buildRichTextSection (richTextSection [richTextTextAsRichTextSectionElement v0])
+  v2 <- buildRichTextList ((richTextList RichTextListStyleBullet [v1]) {richTextListIndent = Nothing, richTextListOffset = Just (-1), richTextListBorder = Nothing})
+  v2 `seq` pure ()
+
+invalid178 :: Result ()
+invalid178 = do
+  v0 <- buildRichTextText ((richTextText "Item") {richTextTextStyle = Nothing})
+  v1 <- buildRichTextSection (richTextSection [richTextTextAsRichTextSectionElement v0])
+  v2 <- buildRichTextList ((richTextList RichTextListStyleBullet [v1]) {richTextListIndent = Nothing, richTextListOffset = Nothing, richTextListBorder = Just (-1)})
+  v2 `seq` pure ()
+
+invalid179 :: Result ()
+invalid179 = do
+  v0 <- buildRichTextText ((richTextText "Item") {richTextTextStyle = Nothing})
+  v1 <- buildRichTextSection (richTextSection [richTextTextAsRichTextSectionElement v0])
+  v2 <- buildRichTextList ((richTextList RichTextListStyleBullet [v1]) {richTextListIndent = Nothing, richTextListOffset = Nothing, richTextListBorder = Just 2})
+  v2 `seq` pure ()
+
+invalid180 :: Result ()
+invalid180 = do
+  v0 <- buildRichTextText ((richTextText "Text") {richTextTextStyle = Nothing})
+  v1 <- buildRichTextQuote ((richTextQuote [richTextTextAsRichTextSectionElement v0]) {richTextQuoteBorder = Just (-1)})
+  v1 `seq` pure ()
+
+invalid181 :: Result ()
+invalid181 = do
+  v0 <- buildRichTextText ((richTextText "Text") {richTextTextStyle = Nothing})
+  v1 <- buildRichTextQuote ((richTextQuote [richTextTextAsRichTextSectionElement v0]) {richTextQuoteBorder = Just 2})
+  v1 `seq` pure ()
+
+invalid182 :: Result ()
+invalid182 = do
+  v0 <- buildRichTextText ((richTextText "Text") {richTextTextStyle = Nothing})
+  v1 <- buildRichTextCodeBlock ((richTextCodeBlock [richTextTextAsRichTextSectionElement v0]) {richTextCodeBlockBorder = Just (-1)})
+  v1 `seq` pure ()
+
+invalid183 :: Result ()
+invalid183 = do
+  v0 <- buildRichTextText ((richTextText "Text") {richTextTextStyle = Nothing})
+  v1 <- buildRichTextCodeBlock ((richTextCodeBlock [richTextTextAsRichTextSectionElement v0]) {richTextCodeBlockBorder = Just 2})
+  v1 `seq` pure ()
+
+invalid184 :: Result ()
+invalid184 = do
+  v0 <- buildPlainText ((plainText "Title") {plainTextEmoji = Nothing})
+  v1 <- buildVideoBlock ((videoBlock "Video" (T.replicate 3001 "x") v0 "https://example.com/video.mp4") {videoBlockBlockId = Nothing, videoBlockAuthorName = Nothing, videoBlockDescription = Nothing, videoBlockProviderIconUrl = Nothing, videoBlockProviderName = Nothing, videoBlockTitleUrl = Nothing})
+  v1 `seq` pure ()
+
+invalid185 :: Result ()
+invalid185 = do
+  v0 <- buildPlainText ((plainText "Title") {plainTextEmoji = Nothing})
+  v1 <- buildVideoBlock ((videoBlock "Video" "https://example.com/thumbnail.png" v0 (T.replicate 3001 "x")) {videoBlockBlockId = Nothing, videoBlockAuthorName = Nothing, videoBlockDescription = Nothing, videoBlockProviderIconUrl = Nothing, videoBlockProviderName = Nothing, videoBlockTitleUrl = Nothing})
+  v1 `seq` pure ()
+
+invalid186 :: Result ()
+invalid186 = do
+  v0 <- buildPlainText ((plainText "Title") {plainTextEmoji = Nothing})
+  v1 <- buildVideoBlock ((videoBlock "Video" "https://example.com/thumbnail.png" v0 "https://example.com/video.mp4") {videoBlockBlockId = Nothing, videoBlockAuthorName = Nothing, videoBlockDescription = Nothing, videoBlockProviderIconUrl = Nothing, videoBlockProviderName = Nothing, videoBlockTitleUrl = Just (T.replicate 2001 "x")})
+  v1 `seq` pure ()
+
+invalid187 :: Result ()
+invalid187 = do
+  v0 <- buildPlainText ((plainText "Title") {plainTextEmoji = Nothing})
+  v1 <- buildVideoBlock ((videoBlock "Video" "https://example.com/thumbnail.png" v0 "https://example.com/video.mp4") {videoBlockBlockId = Nothing, videoBlockAuthorName = Nothing, videoBlockDescription = Nothing, videoBlockProviderIconUrl = Just (T.replicate 2001 "x"), videoBlockProviderName = Nothing, videoBlockTitleUrl = Nothing})
+  v1 `seq` pure ()
+
+invalid188 :: Result ()
+invalid188 = do
+  v0 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Nothing})
+  v1 <- buildHomeTabView ((homeTabView [dividerBlockAsBlock v0]) {homeTabViewPrivateMetadata = Nothing, homeTabViewCallbackId = Nothing, homeTabViewExternalId = Just (T.replicate 256 "x")})
+  v1 `seq` pure ()
+
+invalidCases :: [(String, Maybe (Result ()), Result ())]
+invalidCases =
+  [ ("external-query-negative", Just invalid0, void (fromValue (object ["type" .= String "external_select", "min_query_length" .= Number (-1)]) :: Result ExternalSelectElement)),
+    ("external-multi-query-negative", Just invalid1, void (fromValue (object ["type" .= String "multi_external_select", "min_query_length" .= Number (-1)]) :: Result ExternalMultiSelectElement)),
+    ("date-picker-invalid-format", Just invalid2, void (fromValue (object ["type" .= String "datepicker", "initial_date" .= String "February 29, 2028"]) :: Result DatePickerElement)),
+    ("time-picker-invalid-format", Just invalid3, void (fromValue (object ["type" .= String "timepicker", "initial_time" .= String "9:00"]) :: Result TimePickerElement)),
+    ("time-picker-invalid-range", Just invalid4, void (fromValue (object ["type" .= String "timepicker", "initial_time" .= String "25:99"]) :: Result TimePickerElement)),
+    ("static-select-empty-options", Just invalid5, void (fromValue (object ["type" .= String "static_select", "options" .= toJSON ([] :: [Value])]) :: Result StaticSelectElement)),
+    ("static-multi-select-empty-options", Just invalid6, void (fromValue (object ["type" .= String "multi_static_select", "options" .= toJSON ([] :: [Value])]) :: Result StaticMultiSelectElement)),
+    ("static-select-empty-groups", Just invalid7, void (fromValue (object ["type" .= String "static_select", "option_groups" .= toJSON ([] :: [Value])]) :: Result StaticSelectElement)),
+    ("static-multi-select-empty-groups", Just invalid8, void (fromValue (object ["type" .= String "multi_static_select", "option_groups" .= toJSON ([] :: [Value])]) :: Result StaticMultiSelectElement)),
+    ("text-empty", Just invalid9, void (fromValue (object ["type" .= String "plain_text", "text" .= String ""]) :: Result PlainText)),
+    ("text-too-long", Just invalid10, void (fromValue (object ["type" .= String "plain_text", "text" .= String (T.replicate 3001 "x")]) :: Result PlainText)),
+    ("button-agent-prompt-too-long", Just invalid11, void (fromValue (object ["type" .= String "button", "text" .= object ["type" .= String "plain_text", "text" .= String "A"], "agent_prompt" .= String (T.replicate 4001 "😀")]) :: Result ButtonElement)),
+    ("button-agent-prompt-display-without-prompt", Just invalid12, void (fromValue (object ["type" .= String "button", "text" .= object ["type" .= String "plain_text", "text" .= String "A"], "agent_prompt_display" .= String ""]) :: Result ButtonElement)),
+    ("button-action-id-too-long", Just invalid13, void (fromValue (object ["type" .= String "button", "text" .= object ["type" .= String "plain_text", "text" .= String "A"], "action_id" .= String (T.replicate 256 "x")]) :: Result ButtonElement)),
+    ("button-text-too-long", Just invalid14, void (fromValue (object ["type" .= String "button", "text" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 76 "x")], "action_id" .= String "a"]) :: Result ButtonElement)),
+    ("button-url-too-long", Just invalid15, void (fromValue (object ["type" .= String "button", "text" .= object ["type" .= String "plain_text", "text" .= String "A"], "action_id" .= String "a", "url" .= String (T.replicate 3001 "x")]) :: Result ButtonElement)),
+    ("button-value-too-long", Just invalid16, void (fromValue (object ["type" .= String "button", "text" .= object ["type" .= String "plain_text", "text" .= String "A"], "action_id" .= String "a", "value" .= String (T.replicate 2001 "x")]) :: Result ButtonElement)),
+    ("confirmation-title-too-long", Just invalid17, void (fromValue (object ["title" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 101 "x")], "text" .= object ["type" .= String "mrkdwn", "text" .= String "Text"], "confirm" .= object ["type" .= String "plain_text", "text" .= String "Yes"], "deny" .= object ["type" .= String "plain_text", "text" .= String "No"]]) :: Result ConfirmationDialogue)),
+    ("confirmation-text-too-long", Just invalid18, void (fromValue (object ["title" .= object ["type" .= String "plain_text", "text" .= String "Title"], "text" .= object ["type" .= String "mrkdwn", "text" .= String (T.replicate 301 "x")], "confirm" .= object ["type" .= String "plain_text", "text" .= String "Yes"], "deny" .= object ["type" .= String "plain_text", "text" .= String "No"]]) :: Result ConfirmationDialogue)),
+    ("confirmation-confirm-too-long", Just invalid19, void (fromValue (object ["title" .= object ["type" .= String "plain_text", "text" .= String "Title"], "text" .= object ["type" .= String "mrkdwn", "text" .= String "Text"], "confirm" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 31 "x")], "deny" .= object ["type" .= String "plain_text", "text" .= String "No"]]) :: Result ConfirmationDialogue)),
+    ("confirmation-deny-too-long", Just invalid20, void (fromValue (object ["title" .= object ["type" .= String "plain_text", "text" .= String "Title"], "text" .= object ["type" .= String "mrkdwn", "text" .= String "Text"], "confirm" .= object ["type" .= String "plain_text", "text" .= String "Yes"], "deny" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 31 "x")]]) :: Result ConfirmationDialogue)),
+    ("option-text-too-long", Just invalid21, void (fromValue (object ["text" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 76 "x")], "value" .= String "a"]) :: Result SelectOption)),
+    ("option-value-too-long", Just invalid22, void (fromValue (object ["text" .= object ["type" .= String "plain_text", "text" .= String "A"], "value" .= String (T.replicate 151 "x")]) :: Result SelectOption)),
+    ("option-url-too-long", Just invalid23, void (fromValue (object ["text" .= object ["type" .= String "plain_text", "text" .= String "A"], "value" .= String "a", "url" .= String (T.replicate 3001 "🙂")]) :: Result SelectOption)),
+    ("option-description-too-long", Just invalid24, void (fromValue (object ["text" .= object ["type" .= String "plain_text", "text" .= String "A"], "value" .= String "a", "description" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 76 "x")]]) :: Result SelectOption)),
+    ("option-group-label-too-long", Just invalid25, void (fromValue (object ["label" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 76 "x")], "options" .= toJSON ([object ["text" .= object ["type" .= String "plain_text", "text" .= String "A"], "value" .= String "a"]] :: [Value])]) :: Result SelectOptionGroup)),
+    ("option-group-empty", Just invalid26, void (fromValue (object ["label" .= object ["type" .= String "plain_text", "text" .= String "Group"], "options" .= toJSON ([] :: [Value])]) :: Result SelectOptionGroup)),
+    ("option-group-too-many-options", Just invalid27, void (fromValue (object ["label" .= object ["type" .= String "plain_text", "text" .= String "Group"], "options" .= toJSON (replicate 101 (object ["text" .= object ["type" .= String "plain_text", "text" .= String "A"], "value" .= String "a"]))]) :: Result SelectOptionGroup)),
+    ("select-placeholder-too-long", Just invalid28, void (fromValue (object ["type" .= String "static_select", "action_id" .= String "a", "options" .= toJSON ([object ["text" .= object ["type" .= String "plain_text", "text" .= String "A"], "value" .= String "a"]] :: [Value]), "placeholder" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 151 "x")]]) :: Result StaticSelectElement)),
+    ("plain-text-input-placeholder-too-long", Just invalid29, void (fromValue (object ["type" .= String "plain_text_input", "action_id" .= String "a", "placeholder" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 151 "x")]]) :: Result PlainTextInputElement)),
+    ("email-input-placeholder-too-long", Just invalid30, void (fromValue (object ["type" .= String "email_text_input", "action_id" .= String "a", "placeholder" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 151 "x")]]) :: Result EmailInputElement)),
+    ("url-input-placeholder-too-long", Just invalid31, void (fromValue (object ["type" .= String "url_text_input", "action_id" .= String "a", "placeholder" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 151 "x")]]) :: Result UrlInputElement)),
+    ("number-input-placeholder-too-long", Just invalid32, void (fromValue (object ["type" .= String "number_input", "action_id" .= String "a", "is_decimal_allowed" .= Bool False, "placeholder" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 151 "x")]]) :: Result NumberInputElement)),
+    ("date-picker-placeholder-too-long", Just invalid33, void (fromValue (object ["type" .= String "datepicker", "action_id" .= String "a", "placeholder" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 151 "x")]]) :: Result DatePickerElement)),
+    ("time-picker-placeholder-too-long", Just invalid34, void (fromValue (object ["type" .= String "timepicker", "action_id" .= String "a", "placeholder" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 151 "x")]]) :: Result TimePickerElement)),
+    ("rich-text-input-placeholder-too-long", Just invalid35, void (fromValue (object ["type" .= String "rich_text_input", "action_id" .= String "a", "placeholder" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 151 "x")]]) :: Result RichTextInputElement)),
+    ("select-too-many-options", Just invalid36, void (fromValue (object ["type" .= String "static_select", "action_id" .= String "a", "options" .= toJSON (replicate 101 (object ["text" .= object ["type" .= String "plain_text", "text" .= String "A"], "value" .= String "a"]))]) :: Result StaticSelectElement)),
+    ("select-too-many-option-groups", Just invalid37, void (fromValue (object ["type" .= String "static_select", "action_id" .= String "a", "option_groups" .= toJSON (replicate 101 (object ["label" .= object ["type" .= String "plain_text", "text" .= String "Group"], "options" .= toJSON ([object ["text" .= object ["type" .= String "plain_text", "text" .= String "A"], "value" .= String "a"]] :: [Value])]))]) :: Result StaticSelectElement)),
+    ("overflow-empty", Just invalid38, void (fromValue (object ["type" .= String "overflow", "action_id" .= String "a", "options" .= toJSON ([] :: [Value])]) :: Result OverflowElement)),
+    ("overflow-too-many-options", Just invalid39, void (fromValue (object ["type" .= String "overflow", "action_id" .= String "a", "options" .= toJSON (replicate 6 (object ["text" .= object ["type" .= String "plain_text", "text" .= String "A"], "value" .= String "a"]))]) :: Result OverflowElement)),
+    ("checkboxes-empty", Just invalid40, void (fromValue (object ["type" .= String "checkboxes", "action_id" .= String "a", "options" .= toJSON ([] :: [Value])]) :: Result CheckboxesElement)),
+    ("checkboxes-too-many-options", Just invalid41, void (fromValue (object ["type" .= String "checkboxes", "action_id" .= String "a", "options" .= toJSON (replicate 11 (object ["text" .= object ["type" .= String "plain_text", "text" .= String "A"], "value" .= String "a"]))]) :: Result CheckboxesElement)),
+    ("radio-buttons-empty", Just invalid42, void (fromValue (object ["type" .= String "radio_buttons", "action_id" .= String "a", "options" .= toJSON ([] :: [Value])]) :: Result RadioButtonsElement)),
+    ("radio-buttons-too-many-options", Just invalid43, void (fromValue (object ["type" .= String "radio_buttons", "action_id" .= String "a", "options" .= toJSON (replicate 11 (object ["text" .= object ["type" .= String "plain_text", "text" .= String "A"], "value" .= String "a"]))]) :: Result RadioButtonsElement)),
+    ("table-too-many-rows", Just invalid44, void (fromValue (object ["type" .= String "table", "rows" .= toJSON (replicate 101 (toJSON ([object ["type" .= String "raw_text", "text" .= String "A"]] :: [Value])))]) :: Result TableBlock)),
+    ("table-too-many-columns", Just invalid45, void (fromValue (object ["type" .= String "table", "rows" .= toJSON ([toJSON (replicate 21 (object ["type" .= String "raw_text", "text" .= String "A"]))] :: [Value])]) :: Result TableBlock)),
+    ("file-input-max-files-too-small", Just invalid46, void (fromValue (object ["type" .= String "file_input", "action_id" .= String "a", "max_files" .= Number 0]) :: Result FileInputElement)),
+    ("file-input-max-files-too-large", Just invalid47, void (fromValue (object ["type" .= String "file_input", "action_id" .= String "a", "max_files" .= Number 11]) :: Result FileInputElement)),
+    ("dispatch-action-no-triggers", Just invalid48, void (fromValue (object ["trigger_actions_on" .= toJSON ([] :: [Value])]) :: Result DispatchActionConfiguration)),
+    ("dispatch-action-missing-triggers", Just invalid49, void (fromValue (object []) :: Result DispatchActionConfiguration)),
+    ("dispatch-action-too-many-triggers", Just invalid50, void (fromValue (object ["trigger_actions_on" .= toJSON ([String "on_enter_pressed", String "on_character_entered", String "on_enter_pressed"] :: [Value])]) :: Result DispatchActionConfiguration)),
+    ("plain-text-input-max-length-too-large", Just invalid51, void (fromValue (object ["type" .= String "plain_text_input", "action_id" .= String "a", "max_length" .= Number 3001]) :: Result PlainTextInputElement)),
+    ("actions-too-many-elements", Just invalid52, void (fromValue (object ["type" .= String "actions", "elements" .= toJSON (replicate 26 (object ["type" .= String "button", "text" .= object ["type" .= String "plain_text", "text" .= String "A"], "action_id" .= String "a"]))]) :: Result ActionsBlock)),
+    ("context-too-many-elements", Just invalid53, void (fromValue (object ["type" .= String "context", "elements" .= toJSON (replicate 11 (object ["type" .= String "mrkdwn", "text" .= String "A"]))]) :: Result ContextBlock)),
+    ("header-text-too-long", Just invalid54, void (fromValue (object ["type" .= String "header", "text" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 151 "x")]]) :: Result HeaderBlock)),
+    ("image-url-too-long", Just invalid55, void (fromValue (object ["type" .= String "image", "image_url" .= String (T.replicate 3001 "x"), "alt_text" .= String "Alt"]) :: Result ImageBlock)),
+    ("image-alt-text-too-long", Just invalid56, void (fromValue (object ["type" .= String "image", "image_url" .= String "https://example.com/image.png", "alt_text" .= String (T.replicate 2001 "x")]) :: Result ImageBlock)),
+    ("input-label-too-long", Just invalid57, void (fromValue (object ["type" .= String "input", "label" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 2001 "x")], "element" .= object ["type" .= String "plain_text_input", "action_id" .= String "a"]]) :: Result InputBlock)),
+    ("input-hint-too-long", Just invalid58, void (fromValue (object ["type" .= String "input", "label" .= object ["type" .= String "plain_text", "text" .= String "Label"], "hint" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 2001 "x")], "element" .= object ["type" .= String "plain_text_input", "action_id" .= String "a"]]) :: Result InputBlock)),
+    ("markdown-too-long", Just invalid59, void (fromValue (object ["type" .= String "markdown", "text" .= String (T.replicate 12001 "x")]) :: Result MarkdownBlock)),
+    ("section-text-too-long", Just invalid60, void (fromValue (object ["type" .= String "section", "text" .= object ["type" .= String "mrkdwn", "text" .= String (T.replicate 3001 "x")]]) :: Result SectionBlock)),
+    ("section-too-many-fields", Just invalid61, void (fromValue (object ["type" .= String "section", "fields" .= toJSON (replicate 11 (object ["type" .= String "mrkdwn", "text" .= String "x"]))]) :: Result SectionBlock)),
+    ("section-field-too-long", Just invalid62, void (fromValue (object ["type" .= String "section", "fields" .= toJSON ([object ["type" .= String "mrkdwn", "text" .= String (T.replicate 2001 "x")]] :: [Value])]) :: Result SectionBlock)),
+    ("video-alt-text-too-long", Just invalid63, void (fromValue (object ["type" .= String "video", "alt_text" .= String (T.replicate 2001 "x"), "thumbnail_url" .= String "https://example.com/thumbnail.png", "title" .= object ["type" .= String "plain_text", "text" .= String "Title"], "video_url" .= String "https://example.com/video.mp4"]) :: Result VideoBlock)),
+    ("video-title-too-long", Just invalid64, void (fromValue (object ["type" .= String "video", "alt_text" .= String "Video", "thumbnail_url" .= String "https://example.com/thumbnail.png", "title" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 201 "x")], "video_url" .= String "https://example.com/video.mp4"]) :: Result VideoBlock)),
+    ("video-author-name-too-long", Just invalid65, void (fromValue (object ["type" .= String "video", "alt_text" .= String "Video", "thumbnail_url" .= String "https://example.com/thumbnail.png", "title" .= object ["type" .= String "plain_text", "text" .= String "Title"], "video_url" .= String "https://example.com/video.mp4", "author_name" .= String (T.replicate 51 "x")]) :: Result VideoBlock)),
+    ("video-description-too-long", Just invalid66, void (fromValue (object ["type" .= String "video", "alt_text" .= String "Video", "thumbnail_url" .= String "https://example.com/thumbnail.png", "title" .= object ["type" .= String "plain_text", "text" .= String "Title"], "video_url" .= String "https://example.com/video.mp4", "description" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 201 "x")]]) :: Result VideoBlock)),
+    ("video-provider-name-too-long", Just invalid67, void (fromValue (object ["type" .= String "video", "alt_text" .= String "Video", "thumbnail_url" .= String "https://example.com/thumbnail.png", "title" .= object ["type" .= String "plain_text", "text" .= String "Title"], "video_url" .= String "https://example.com/video.mp4", "provider_name" .= String (T.replicate 51 "x")]) :: Result VideoBlock)),
+    ("message-channel-empty", Just invalid68, void (fromValue (object ["channel" .= String ""]) :: Result MessagePayload)),
+    ("message-too-many-blocks", Just invalid69, void (fromValue (object ["channel" .= String "C123", "blocks" .= toJSON (replicate 51 (object ["type" .= String "divider"]))]) :: Result MessagePayload)),
+    ("message-too-many-attachments", Just invalid70, void (fromValue (object ["channel" .= String "C123", "attachments" .= toJSON (replicate 101 (object ["blocks" .= toJSON ([object ["type" .= String "divider"]] :: [Value])]))]) :: Result MessagePayload)),
+    ("message-invalid-block-surface", Just invalid71, void (fromValue (object ["channel" .= String "C123", "blocks" .= toJSON ([object ["type" .= String "alert", "text" .= object ["type" .= String "plain_text", "text" .= String "Modal only"]]] :: [Value])]) :: Result MessagePayload)),
+    ("modal-invalid-block-surface", Just invalid72, void (fromValue (object ["type" .= String "modal", "title" .= object ["type" .= String "plain_text", "text" .= String "Invalid"], "blocks" .= toJSON ([object ["type" .= String "markdown", "text" .= String "Message only"]] :: [Value])]) :: Result ModalView)),
+    ("home-invalid-block-surface", Just invalid73, void (fromValue (object ["type" .= String "home", "blocks" .= toJSON ([object ["type" .= String "alert", "text" .= object ["type" .= String "plain_text", "text" .= String "Modal only"]]] :: [Value])]) :: Result HomeTabView)),
+    ("modal-input-requires-submit", Just invalid74, void (fromValue (object ["type" .= String "modal", "title" .= object ["type" .= String "plain_text", "text" .= String "Missing submit"], "blocks" .= toJSON ([object ["type" .= String "input", "label" .= object ["type" .= String "plain_text", "text" .= String "Name"], "element" .= object ["type" .= String "plain_text_input", "action_id" .= String "a"]]] :: [Value])]) :: Result ModalView)),
+    ("view-too-many-blocks", Just invalid75, void (fromValue (object ["type" .= String "home", "blocks" .= toJSON (replicate 101 (object ["type" .= String "divider"]))]) :: Result HomeTabView)),
+    ("view-private-metadata-too-long", Just invalid76, void (fromValue (object ["type" .= String "home", "blocks" .= toJSON ([object ["type" .= String "divider"]] :: [Value]), "private_metadata" .= String (T.replicate 3001 "x")]) :: Result HomeTabView)),
+    ("view-callback-id-too-long", Just invalid77, void (fromValue (object ["type" .= String "home", "blocks" .= toJSON ([object ["type" .= String "divider"]] :: [Value]), "callback_id" .= String (T.replicate 256 "x")]) :: Result HomeTabView)),
+    ("view-title-too-long", Just invalid78, void (fromValue (object ["type" .= String "modal", "title" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 25 "x")], "blocks" .= toJSON ([object ["type" .= String "divider"]] :: [Value])]) :: Result ModalView)),
+    ("view-close-too-long", Just invalid79, void (fromValue (object ["type" .= String "modal", "title" .= object ["type" .= String "plain_text", "text" .= String "Title"], "close" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 25 "x")], "blocks" .= toJSON ([object ["type" .= String "divider"]] :: [Value])]) :: Result ModalView)),
+    ("view-submit-too-long", Just invalid80, void (fromValue (object ["type" .= String "modal", "title" .= object ["type" .= String "plain_text", "text" .= String "Title"], "submit" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 25 "x")], "blocks" .= toJSON ([object ["type" .= String "divider"]] :: [Value])]) :: Result ModalView)),
+    ("section-missing-content", Just invalid81, void (fromValue (object ["type" .= String "section"]) :: Result SectionBlock)),
+    ("section-empty-fields", Just invalid82, void (fromValue (object ["type" .= String "section", "fields" .= toJSON ([] :: [Value])]) :: Result SectionBlock)),
+    ("static-select-options-and-groups", Just invalid83, void (fromValue (object ["type" .= String "static_select", "action_id" .= String "a", "options" .= toJSON ([object ["text" .= object ["type" .= String "plain_text", "text" .= String "A"], "value" .= String "a"]] :: [Value]), "option_groups" .= toJSON ([object ["label" .= object ["type" .= String "plain_text", "text" .= String "A"], "options" .= toJSON ([object ["text" .= object ["type" .= String "plain_text", "text" .= String "B"], "value" .= String "b"]] :: [Value])]] :: [Value])]) :: Result StaticSelectElement)),
+    ("image-url-and-slack-file", Just invalid84, void (fromValue (object ["type" .= String "image", "alt_text" .= String "image", "image_url" .= String "https://example.com/image.png", "slack_file" .= object ["id" .= String "F0123ABC456"]]) :: Result ImageElement)),
+    ("number-bound-inverted-large-integer", Just invalid85, void (fromValue (object ["type" .= String "number_input", "is_decimal_allowed" .= Bool True, "min_value" .= String "9007199254740993", "max_value" .= String "9007199254740992"]) :: Result NumberInputElement)),
+    ("number-bound-inverted-long-fraction", Just invalid86, void (fromValue (object ["type" .= String "number_input", "is_decimal_allowed" .= Bool True, "min_value" .= String "0.10000000000000001", "max_value" .= String "0.1"]) :: Result NumberInputElement)),
+    ("number-bound-hex", Just invalid87, void (fromValue (object ["type" .= String "number_input", "is_decimal_allowed" .= Bool True, "min_value" .= String "0x10"]) :: Result NumberInputElement)),
+    ("number-bound-underscore", Just invalid88, void (fromValue (object ["type" .= String "number_input", "is_decimal_allowed" .= Bool True, "min_value" .= String "1_000"]) :: Result NumberInputElement)),
+    ("number-bound-leading-space", Just invalid89, void (fromValue (object ["type" .= String "number_input", "is_decimal_allowed" .= Bool True, "min_value" .= String " 1"]) :: Result NumberInputElement)),
+    ("number-bound-trailing-space", Just invalid90, void (fromValue (object ["type" .= String "number_input", "is_decimal_allowed" .= Bool True, "min_value" .= String "1 "]) :: Result NumberInputElement)),
+    ("number-bound-nan", Just invalid91, void (fromValue (object ["type" .= String "number_input", "is_decimal_allowed" .= Bool True, "min_value" .= String "NaN"]) :: Result NumberInputElement)),
+    ("number-bound-infinity", Just invalid92, void (fromValue (object ["type" .= String "number_input", "is_decimal_allowed" .= Bool True, "min_value" .= String "Infinity"]) :: Result NumberInputElement)),
+    ("external-multi-focus-false", Just invalid93, void (fromValue (object ["type" .= String "multi_external_select", "focus_on_load" .= Bool False]) :: Result ExternalMultiSelectElement)),
+    ("external-multi-focus-true", Just invalid94, void (fromValue (object ["type" .= String "multi_external_select", "focus_on_load" .= Bool True]) :: Result ExternalMultiSelectElement)),
+    ("plain-text-input-inverted-range", Just invalid95, void (fromValue (object ["type" .= String "plain_text_input", "min_length" .= Number 20, "max_length" .= Number 10]) :: Result PlainTextInputElement)),
+    ("number-input-inverted-range", Just (void $ buildNumberInputElement ((numberInputElement True) {numberInputElementMinValue = Just (decimalText 2), numberInputElementMaxValue = Just (decimalText 1)})), void (fromValue (object ["type" .= String "number_input", "action_id" .= String "a", "is_decimal_allowed" .= Bool True, "min_value" .= Number 2, "max_value" .= Number 1]) :: Result NumberInputElement)),
+    ("context-invalid-element", Nothing, void (fromValue (object ["type" .= String "context", "elements" .= toJSON ([object ["type" .= String "divider"]] :: [Value])]) :: Result ContextBlock)),
+    ("input-invalid-element", Nothing, void (fromValue (object ["type" .= String "input", "label" .= object ["type" .= String "plain_text", "text" .= String "Label"], "element" .= object ["type" .= String "button", "text" .= object ["type" .= String "plain_text", "text" .= String "A"], "action_id" .= String "a"]]) :: Result InputBlock)),
+    ("block-id-too-long", Just invalid99, void (fromValue (object ["type" .= String "divider", "block_id" .= String (T.replicate 256 "x")]) :: Result DividerBlock)),
+    ("button-accessibility-label-too-long", Just invalid100, void (fromValue (object ["type" .= String "button", "text" .= object ["type" .= String "plain_text", "text" .= String "A"], "action_id" .= String "a", "accessibility_label" .= String (T.replicate 76 "x")]) :: Result ButtonElement)),
+    ("workflow-button-text-too-long", Just invalid101, void (fromValue (object ["type" .= String "workflow_button", "text" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 76 "x")], "workflow" .= object ["trigger" .= object ["url" .= String "https://slack.com/shortcuts/Ft0/abc"]], "action_id" .= String "a"]) :: Result WorkflowButtonElement)),
+    ("workflow-button-accessibility-label-too-long", Just invalid102, void (fromValue (object ["type" .= String "workflow_button", "text" .= object ["type" .= String "plain_text", "text" .= String "Run"], "workflow" .= object ["trigger" .= object ["url" .= String "https://slack.com/shortcuts/Ft0/abc"]], "action_id" .= String "a", "accessibility_label" .= String (T.replicate 76 "x")]) :: Result WorkflowButtonElement)),
+    ("alert-text-too-long", Just invalid103, void (fromValue (object ["type" .= String "alert", "text" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 201 "x")]]) :: Result AlertBlock)),
+    ("card-title-too-long", Just invalid104, void (fromValue (object ["type" .= String "card", "title" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 151 "x")]]) :: Result CardBlock)),
+    ("card-subtitle-too-long", Just invalid105, void (fromValue (object ["type" .= String "card", "title" .= object ["type" .= String "plain_text", "text" .= String "Card"], "subtitle" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 151 "x")]]) :: Result CardBlock)),
+    ("card-body-too-long", Just invalid106, void (fromValue (object ["type" .= String "card", "body" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 201 "x")]]) :: Result CardBlock)),
+    ("card-too-many-actions", Just invalid107, void (fromValue (object ["type" .= String "card", "title" .= object ["type" .= String "plain_text", "text" .= String "Card"], "actions" .= toJSON (replicate 4 (object ["type" .= String "button", "text" .= object ["type" .= String "plain_text", "text" .= String "A"], "action_id" .= String "a"]))]) :: Result CardBlock)),
+    ("card-subtext-too-long", Just invalid108, void (fromValue (object ["type" .= String "card", "title" .= object ["type" .= String "plain_text", "text" .= String "Card"], "subtext" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 201 "x")]]) :: Result CardBlock)),
+    ("carousel-empty", Just invalid109, void (fromValue (object ["type" .= String "carousel", "elements" .= toJSON ([] :: [Value])]) :: Result CarouselBlock)),
+    ("carousel-too-many-cards", Just invalid110, void (fromValue (object ["type" .= String "carousel", "elements" .= toJSON (replicate 11 (object ["type" .= String "card", "title" .= object ["type" .= String "plain_text", "text" .= String "Card"]]))]) :: Result CarouselBlock)),
+    ("container-title-too-long", Just invalid111, void (fromValue (object ["type" .= String "container", "title" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 151 "x")], "child_blocks" .= toJSON ([object ["type" .= String "divider"]] :: [Value])]) :: Result ContainerBlock)),
+    ("container-subtitle-too-long", Just invalid112, void (fromValue (object ["type" .= String "container", "title" .= object ["type" .= String "plain_text", "text" .= String "Container"], "subtitle" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 151 "x")], "child_blocks" .= toJSON ([object ["type" .= String "divider"]] :: [Value])]) :: Result ContainerBlock)),
+    ("container-too-many-child-blocks", Just invalid113, void (fromValue (object ["type" .= String "container", "title" .= object ["type" .= String "plain_text", "text" .= String "Container"], "child_blocks" .= toJSON (replicate 11 (object ["type" .= String "divider"]))]) :: Result ContainerBlock)),
+    ("context-actions-too-many-elements", Just invalid114, void (fromValue (object ["type" .= String "context_actions", "elements" .= toJSON (replicate 6 (object ["type" .= String "icon_button", "text" .= object ["type" .= String "plain_text", "text" .= String "Delete"], "icon" .= String "trash"]))]) :: Result ContextActionsBlock)),
+    ("feedback-button-text-too-long", Just invalid115, void (fromValue (object ["type" .= String "feedback_buttons", "positive_button" .= object ["text" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 76 "x")], "value" .= String "good"], "negative_button" .= object ["text" .= object ["type" .= String "plain_text", "text" .= String "Bad"], "value" .= String "bad"]]) :: Result FeedbackButtonsElement)),
+    ("feedback-button-value-too-long", Just invalid116, void (fromValue (object ["type" .= String "feedback_buttons", "positive_button" .= object ["text" .= object ["type" .= String "plain_text", "text" .= String "Good"], "value" .= String (T.replicate 2001 "x")], "negative_button" .= object ["text" .= object ["type" .= String "plain_text", "text" .= String "Bad"], "value" .= String "bad"]]) :: Result FeedbackButtonsElement)),
+    ("feedback-button-accessibility-label-too-long", Just invalid117, void (fromValue (object ["type" .= String "feedback_buttons", "positive_button" .= object ["text" .= object ["type" .= String "plain_text", "text" .= String "Good"], "value" .= String "good", "accessibility_label" .= String (T.replicate 76 "x")], "negative_button" .= object ["text" .= object ["type" .= String "plain_text", "text" .= String "Bad"], "value" .= String "bad"]]) :: Result FeedbackButtonsElement)),
+    ("icon-button-too-many-visible-users", Just invalid118, void (fromValue (object ["type" .= String "icon_button", "text" .= object ["type" .= String "plain_text", "text" .= String "Delete"], "icon" .= String "trash", "visible_to_user_ids" .= toJSON ([String "U0", String "U1", String "U2", String "U3", String "U4", String "U5", String "U6", String "U7", String "U8", String "U9", String "U10"] :: [Value])]) :: Result IconButtonElement)),
+    ("icon-button-value-too-long", Just invalid119, void (fromValue (object ["type" .= String "icon_button", "text" .= object ["type" .= String "plain_text", "text" .= String "Delete"], "icon" .= String "trash", "value" .= String (T.replicate 2001 "x")]) :: Result IconButtonElement)),
+    ("icon-button-accessibility-label-too-long", Just invalid120, void (fromValue (object ["type" .= String "icon_button", "text" .= object ["type" .= String "plain_text", "text" .= String "Delete"], "icon" .= String "trash", "accessibility_label" .= String (T.replicate 76 "x")]) :: Result IconButtonElement)),
+    ("data-table-too-few-rows", Just invalid121, void (fromValue (object ["type" .= String "data_table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "Name"]] :: [Value])] :: [Value]), "caption" .= String "Names"]) :: Result DataTableBlock)),
+    ("data-table-too-many-rows", Just invalid122, void (fromValue (object ["type" .= String "data_table", "rows" .= toJSON (replicate 202 (toJSON ([object ["type" .= String "raw_text", "text" .= String "A"]] :: [Value]))), "caption" .= String "Names"]) :: Result DataTableBlock)),
+    ("data-table-too-few-columns", Just invalid123, void (fromValue (object ["type" .= String "data_table", "rows" .= toJSON ([toJSON ([] :: [Value]), toJSON ([] :: [Value])] :: [Value]), "caption" .= String "Empty"]) :: Result DataTableBlock)),
+    ("data-table-too-many-columns", Just invalid124, void (fromValue (object ["type" .= String "data_table", "rows" .= toJSON ([toJSON (replicate 21 (object ["type" .= String "raw_text", "text" .= String "A"])), toJSON (replicate 21 (object ["type" .= String "raw_text", "text" .= String "A"]))] :: [Value]), "caption" .= String "Wide"]) :: Result DataTableBlock)),
+    ("data-table-page-size-too-small", Just invalid125, void (fromValue (object ["type" .= String "data_table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "Name"]] :: [Value]), toJSON ([object ["type" .= String "raw_text", "text" .= String "Alice"]] :: [Value])] :: [Value]), "caption" .= String "Names", "page_size" .= Number 0]) :: Result DataTableBlock)),
+    ("data-table-page-size-too-large", Just invalid126, void (fromValue (object ["type" .= String "data_table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "Name"]] :: [Value]), toJSON ([object ["type" .= String "raw_text", "text" .= String "Alice"]] :: [Value])] :: [Value]), "caption" .= String "Names", "page_size" .= Number 101]) :: Result DataTableBlock)),
+    ("data-table-cell-text-empty", Just invalid127, void (fromValue (object ["type" .= String "data_table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "Name"]] :: [Value]), toJSON ([object ["type" .= String "raw_text", "text" .= String ""]] :: [Value])] :: [Value]), "caption" .= String "Names"]) :: Result DataTableBlock)),
+    ("data-table-content-too-long", Just invalid128, void (fromValue (object ["type" .= String "data_table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "Name"]] :: [Value]), toJSON ([object ["type" .= String "raw_text", "text" .= String (T.replicate 20000 "x")]] :: [Value])] :: [Value]), "caption" .= String "Names"]) :: Result DataTableBlock)),
+    ("message-too-many-charts", Just invalid129, void (fromValue (object ["channel" .= String "C123", "blocks" .= toJSON (replicate 3 (object ["type" .= String "data_visualization", "title" .= String "Chart", "chart" .= object ["type" .= String "pie", "segments" .= toJSON ([object ["label" .= String "A", "value" .= Number 1]] :: [Value])]]))]) :: Result MessagePayload)),
+    ("data-visualization-title-too-long", Just invalid130, void (fromValue (object ["type" .= String "data_visualization", "title" .= String (T.replicate 51 "x"), "chart" .= object ["type" .= String "pie", "segments" .= toJSON ([object ["label" .= String "A", "value" .= Number 1]] :: [Value])]]) :: Result DataVisualizationBlock)),
+    ("pie-chart-empty", Just invalid131, void (fromValue (object ["type" .= String "pie", "segments" .= toJSON ([] :: [Value])]) :: Result PieChart)),
+    ("pie-chart-too-many-segments", Just invalid132, void (fromValue (object ["type" .= String "pie", "segments" .= toJSON (replicate 13 (object ["label" .= String "S", "value" .= Number 1]))]) :: Result PieChart)),
+    ("chart-segment-label-too-long", Just invalid133, void (fromValue (object ["label" .= String (T.replicate 21 "x"), "value" .= Number 1]) :: Result ChartSegment)),
+    ("chart-segment-value-not-positive", Just invalid134, void (fromValue (object ["label" .= String "A", "value" .= Number 0]) :: Result ChartSegment)),
+    ("chart-series-empty", Just invalid135, void (fromValue (object ["type" .= String "line", "axis_config" .= object ["categories" .= toJSON ([String "A"] :: [Value])], "series" .= toJSON ([] :: [Value])]) :: Result LineChart)),
+    ("chart-duplicate-point-labels", Just invalid136, void (fromValue (object ["type" .= String "line", "axis_config" .= object ["categories" .= toJSON ([String "A", String "B"] :: [Value])], "series" .= toJSON ([object ["name" .= String "Series", "data" .= toJSON ([object ["label" .= String "A", "value" .= Number 1], object ["label" .= String "A", "value" .= Number 2]] :: [Value])]] :: [Value])]) :: Result LineChart)),
+    ("chart-too-many-series", Just invalid137, void (fromValue (object ["type" .= String "line", "series" .= toJSON ([object ["name" .= String "S0", "data" .= toJSON ([object ["label" .= String "A", "value" .= Number 1]] :: [Value])], object ["name" .= String "S1", "data" .= toJSON ([object ["label" .= String "A", "value" .= Number 1]] :: [Value])], object ["name" .= String "S2", "data" .= toJSON ([object ["label" .= String "A", "value" .= Number 1]] :: [Value])], object ["name" .= String "S3", "data" .= toJSON ([object ["label" .= String "A", "value" .= Number 1]] :: [Value])], object ["name" .= String "S4", "data" .= toJSON ([object ["label" .= String "A", "value" .= Number 1]] :: [Value])], object ["name" .= String "S5", "data" .= toJSON ([object ["label" .= String "A", "value" .= Number 1]] :: [Value])], object ["name" .= String "S6", "data" .= toJSON ([object ["label" .= String "A", "value" .= Number 1]] :: [Value])], object ["name" .= String "S7", "data" .= toJSON ([object ["label" .= String "A", "value" .= Number 1]] :: [Value])], object ["name" .= String "S8", "data" .= toJSON ([object ["label" .= String "A", "value" .= Number 1]] :: [Value])], object ["name" .= String "S9", "data" .= toJSON ([object ["label" .= String "A", "value" .= Number 1]] :: [Value])], object ["name" .= String "S10", "data" .= toJSON ([object ["label" .= String "A", "value" .= Number 1]] :: [Value])], object ["name" .= String "S11", "data" .= toJSON ([object ["label" .= String "A", "value" .= Number 1]] :: [Value])], object ["name" .= String "S12", "data" .= toJSON ([object ["label" .= String "A", "value" .= Number 1]] :: [Value])]] :: [Value]), "axis_config" .= object ["categories" .= toJSON ([String "A"] :: [Value])]]) :: Result LineChart)),
+    ("data-series-name-too-long", Just invalid138, void (fromValue (object ["name" .= String (T.replicate 21 "x"), "data" .= toJSON ([object ["label" .= String "A", "value" .= Number 1]] :: [Value])]) :: Result DataSeries)),
+    ("data-series-empty", Just invalid139, void (fromValue (object ["name" .= String "Series", "data" .= toJSON ([] :: [Value])]) :: Result DataSeries)),
+    ("data-series-too-many-points", Just invalid140, void (fromValue (object ["name" .= String "Series", "data" .= toJSON ([object ["label" .= String "P0", "value" .= Number 0], object ["label" .= String "P1", "value" .= Number 1], object ["label" .= String "P2", "value" .= Number 2], object ["label" .= String "P3", "value" .= Number 3], object ["label" .= String "P4", "value" .= Number 4], object ["label" .= String "P5", "value" .= Number 5], object ["label" .= String "P6", "value" .= Number 6], object ["label" .= String "P7", "value" .= Number 7], object ["label" .= String "P8", "value" .= Number 8], object ["label" .= String "P9", "value" .= Number 9], object ["label" .= String "P10", "value" .= Number 10], object ["label" .= String "P11", "value" .= Number 11], object ["label" .= String "P12", "value" .= Number 12], object ["label" .= String "P13", "value" .= Number 13], object ["label" .= String "P14", "value" .= Number 14], object ["label" .= String "P15", "value" .= Number 15], object ["label" .= String "P16", "value" .= Number 16], object ["label" .= String "P17", "value" .= Number 17], object ["label" .= String "P18", "value" .= Number 18], object ["label" .= String "P19", "value" .= Number 19], object ["label" .= String "P20", "value" .= Number 20]] :: [Value])]) :: Result DataSeries)),
+    ("data-point-label-too-long", Just invalid141, void (fromValue (object ["label" .= String (T.replicate 21 "x"), "value" .= Number 1]) :: Result DataPoint)),
+    ("axis-categories-empty", Just invalid142, void (fromValue (object ["categories" .= toJSON ([] :: [Value])]) :: Result AxisConfig)),
+    ("axis-too-many-categories", Just invalid143, void (fromValue (object ["categories" .= toJSON ([String "X0", String "X1", String "X2", String "X3", String "X4", String "X5", String "X6", String "X7", String "X8", String "X9", String "X10", String "X11", String "X12", String "X13", String "X14", String "X15", String "X16", String "X17", String "X18", String "X19", String "X20"] :: [Value])]) :: Result AxisConfig)),
+    ("axis-category-label-too-long", Just invalid144, void (fromValue (object ["categories" .= toJSON ([String (T.replicate 21 "x")] :: [Value])]) :: Result AxisConfig)),
+    ("axis-label-too-long", Just invalid145, void (fromValue (object ["categories" .= toJSON ([String "A"] :: [Value]), "x_label" .= String (T.replicate 51 "x")]) :: Result AxisConfig)),
+    ("plain-text-input-min-length-negative", Just invalid146, void (fromValue (object ["type" .= String "plain_text_input", "action_id" .= String "a", "min_length" .= Number (-1)]) :: Result PlainTextInputElement)),
+    ("plain-text-input-min-length-too-large", Just invalid147, void (fromValue (object ["type" .= String "plain_text_input", "action_id" .= String "a", "min_length" .= Number 3001]) :: Result PlainTextInputElement)),
+    ("plain-text-input-max-length-too-small", Just invalid148, void (fromValue (object ["type" .= String "plain_text_input", "action_id" .= String "a", "max_length" .= Number 0]) :: Result PlainTextInputElement)),
+    ("rich-text-input-min-lines-too-small", Just invalid149, void (fromValue (object ["type" .= String "rich_text_input", "action_id" .= String "a", "min_lines" .= Number 0]) :: Result RichTextInputElement)),
+    ("rich-text-input-min-lines-too-large", Just invalid150, void (fromValue (object ["type" .= String "rich_text_input", "action_id" .= String "a", "min_lines" .= Number 101]) :: Result RichTextInputElement)),
+    ("rich-text-input-max-lines-too-small", Just invalid151, void (fromValue (object ["type" .= String "rich_text_input", "action_id" .= String "a", "max_lines" .= Number 0]) :: Result RichTextInputElement)),
+    ("rich-text-input-max-lines-too-large", Just invalid152, void (fromValue (object ["type" .= String "rich_text_input", "action_id" .= String "a", "max_lines" .= Number 101]) :: Result RichTextInputElement)),
+    ("multi-select-max-selected-items-too-small", Just invalid153, void (fromValue (object ["type" .= String "multi_users_select", "action_id" .= String "a", "max_selected_items" .= Number 0]) :: Result UserMultiSelectElement)),
+    ("conversation-filter-include-empty", Just invalid154, void (fromValue (object ["include" .= toJSON ([] :: [Value])]) :: Result ConversationFilter)),
+    ("conversation-filter-unknown-include", Just invalid155, void (fromValue (object ["include" .= toJSON ([String "channel"] :: [Value])]) :: Result ConversationFilter)),
+    ("workflow-button-missing-action-id", Nothing, void (fromValue (object ["type" .= String "workflow_button", "text" .= object ["type" .= String "plain_text", "text" .= String "Run"], "workflow" .= object ["trigger" .= object ["url" .= String "https://slack.com/shortcuts/Ft0/abc"]]]) :: Result WorkflowButtonElement)),
+    ("number-input-missing-decimal-flag", Nothing, void (fromValue (object ["type" .= String "number_input", "action_id" .= String "a"]) :: Result NumberInputElement)),
+    ("slack-icon-missing-name", Nothing, void (fromValue (object ["type" .= String "icon"]) :: Result SlackIcon)),
+    ("slack-file-id-malformed", Just invalid159, void (fromValue (object ["id" .= String "F0123456"]) :: Result SlackFile)),
+    ("image-element-url-too-long", Just invalid160, void (fromValue (object ["type" .= String "image", "image_url" .= String (T.replicate 3001 "x"), "alt_text" .= String "Alt"]) :: Result ImageElement)),
+    ("image-element-alt-text-too-long", Just invalid161, void (fromValue (object ["type" .= String "image", "image_url" .= String "https://example.com/image.png", "alt_text" .= String (T.replicate 2001 "x")]) :: Result ImageElement)),
+    ("image-block-title-too-long", Just invalid162, void (fromValue (object ["type" .= String "image", "image_url" .= String "https://example.com/image.png", "alt_text" .= String "Alt", "title" .= object ["type" .= String "plain_text", "text" .= String (T.replicate 2001 "x")]]) :: Result ImageBlock)),
+    ("image-block-url-and-slack-file", Just invalid163, void (fromValue (object ["type" .= String "image", "image_url" .= String "https://example.com/image.png", "slack_file" .= object ["id" .= String "F0123ABC456"], "alt_text" .= String "Alt"]) :: Result ImageBlock)),
+    ("image-block-missing-source", Just invalid164, void (fromValue (object ["type" .= String "image", "alt_text" .= String "Alt"]) :: Result ImageBlock)),
+    ("container-no-child-blocks", Just invalid165, void (fromValue (object ["type" .= String "container", "title" .= object ["type" .= String "plain_text", "text" .= String "Container"], "child_blocks" .= toJSON ([] :: [Value])]) :: Result ContainerBlock)),
+    ("table-too-many-column-settings", Just invalid166, void (fromValue (object ["type" .= String "table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "A"]] :: [Value])] :: [Value]), "column_settings" .= toJSON (replicate 21 (object ["is_wrapped" .= Bool True]))]) :: Result TableBlock)),
+    ("data-table-row-header-index-negative", Just invalid167, void (fromValue (object ["type" .= String "data_table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "Name"]] :: [Value]), toJSON ([object ["type" .= String "raw_text", "text" .= String "Alice"]] :: [Value])] :: [Value]), "caption" .= String "Names", "row_header_column_index" .= Number (-1)]) :: Result DataTableBlock)),
+    ("data-table-total-content-too-long", Just invalid168, void (fromValue (object ["channel" .= String "C123", "blocks" .= toJSON ([object ["type" .= String "data_table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "Name"]] :: [Value]), toJSON ([object ["type" .= String "raw_text", "text" .= String (T.replicate 9997 "x")]] :: [Value])] :: [Value]), "caption" .= String "Names"], object ["type" .= String "data_table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "Name"]] :: [Value]), toJSON ([object ["type" .= String "raw_text", "text" .= String (T.replicate 9997 "x")]] :: [Value])] :: [Value]), "caption" .= String "Names"]] :: [Value])]) :: Result MessagePayload)),
+    ("markdown-total-too-long", Just invalid169, void (fromValue (object ["channel" .= String "C123", "blocks" .= toJSON ([object ["type" .= String "markdown", "text" .= String (T.replicate 6001 "x")], object ["type" .= String "markdown", "text" .= String (T.replicate 6001 "x")]] :: [Value])]) :: Result MessagePayload)),
+    ("plan-missing-tasks", Nothing, void (fromValue (object ["type" .= String "plan", "title" .= String "Plan"]) :: Result PlanBlock)),
+    ("plan-too-many-tasks", Just invalid171, void (fromValue (object ["type" .= String "plan", "title" .= String "Plan", "tasks" .= toJSON ([object ["task_id" .= String "task_0", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_1", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_2", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_3", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_4", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_5", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_6", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_7", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_8", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_9", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_10", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_11", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_12", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_13", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_14", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_15", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_16", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_17", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_18", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_19", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_20", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_21", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_22", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_23", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_24", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_25", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_26", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_27", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_28", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_29", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_30", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_31", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_32", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_33", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_34", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_35", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_36", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_37", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_38", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_39", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_40", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_41", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_42", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_43", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_44", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_45", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_46", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_47", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_48", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_49", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task_50", "title" .= String "Task", "status" .= String "complete"]] :: [Value])]) :: Result PlanBlock)),
+    ("plan-duplicate-task-ids", Just invalid172, void (fromValue (object ["type" .= String "plan", "title" .= String "Plan", "tasks" .= toJSON ([object ["task_id" .= String "task", "title" .= String "Task", "status" .= String "complete"], object ["task_id" .= String "task", "title" .= String "Task", "status" .= String "complete"]] :: [Value])]) :: Result PlanBlock)),
+    ("task-card-missing-status", Nothing, void (fromValue (object ["type" .= String "task_card", "task_id" .= String "task", "title" .= String "Task"]) :: Result TaskCardBlock)),
+    ("task-card-pending-status", Just invalid174, void (fromValue (object ["channel" .= String "C123", "blocks" .= toJSON ([object ["type" .= String "task_card", "task_id" .= String "task", "title" .= String "Task", "status" .= String "pending"]] :: [Value])]) :: Result MessagePayload)),
+    ("rich-text-list-indent-negative", Just invalid175, void (fromValue (object ["type" .= String "rich_text_list", "style" .= String "bullet", "elements" .= toJSON ([object ["type" .= String "rich_text_section", "elements" .= toJSON ([object ["type" .= String "text", "text" .= String "Item"]] :: [Value])]] :: [Value]), "indent" .= Number (-1)]) :: Result RichTextList)),
+    ("rich-text-list-indent-too-large", Just invalid176, void (fromValue (object ["type" .= String "rich_text_list", "style" .= String "bullet", "elements" .= toJSON ([object ["type" .= String "rich_text_section", "elements" .= toJSON ([object ["type" .= String "text", "text" .= String "Item"]] :: [Value])]] :: [Value]), "indent" .= Number 9]) :: Result RichTextList)),
+    ("rich-text-list-offset-negative", Just invalid177, void (fromValue (object ["type" .= String "rich_text_list", "style" .= String "bullet", "elements" .= toJSON ([object ["type" .= String "rich_text_section", "elements" .= toJSON ([object ["type" .= String "text", "text" .= String "Item"]] :: [Value])]] :: [Value]), "offset" .= Number (-1)]) :: Result RichTextList)),
+    ("rich-text-list-border-negative", Just invalid178, void (fromValue (object ["type" .= String "rich_text_list", "style" .= String "bullet", "elements" .= toJSON ([object ["type" .= String "rich_text_section", "elements" .= toJSON ([object ["type" .= String "text", "text" .= String "Item"]] :: [Value])]] :: [Value]), "border" .= Number (-1)]) :: Result RichTextList)),
+    ("rich-text-list-border-too-large", Just invalid179, void (fromValue (object ["type" .= String "rich_text_list", "style" .= String "bullet", "elements" .= toJSON ([object ["type" .= String "rich_text_section", "elements" .= toJSON ([object ["type" .= String "text", "text" .= String "Item"]] :: [Value])]] :: [Value]), "border" .= Number 2]) :: Result RichTextList)),
+    ("rich-text-quote-border-negative", Just invalid180, void (fromValue (object ["type" .= String "rich_text_quote", "elements" .= toJSON ([object ["type" .= String "text", "text" .= String "Text"]] :: [Value]), "border" .= Number (-1)]) :: Result RichTextQuote)),
+    ("rich-text-quote-border-too-large", Just invalid181, void (fromValue (object ["type" .= String "rich_text_quote", "elements" .= toJSON ([object ["type" .= String "text", "text" .= String "Text"]] :: [Value]), "border" .= Number 2]) :: Result RichTextQuote)),
+    ("rich-text-preformatted-border-negative", Just invalid182, void (fromValue (object ["type" .= String "rich_text_preformatted", "elements" .= toJSON ([object ["type" .= String "text", "text" .= String "Text"]] :: [Value]), "border" .= Number (-1)]) :: Result RichTextCodeBlock)),
+    ("rich-text-preformatted-border-too-large", Just invalid183, void (fromValue (object ["type" .= String "rich_text_preformatted", "elements" .= toJSON ([object ["type" .= String "text", "text" .= String "Text"]] :: [Value]), "border" .= Number 2]) :: Result RichTextCodeBlock)),
+    ("video-thumbnail-url-too-long", Just invalid184, void (fromValue (object ["type" .= String "video", "alt_text" .= String "Video", "thumbnail_url" .= String (T.replicate 3001 "x"), "title" .= object ["type" .= String "plain_text", "text" .= String "Title"], "video_url" .= String "https://example.com/video.mp4"]) :: Result VideoBlock)),
+    ("video-url-too-long", Just invalid185, void (fromValue (object ["type" .= String "video", "alt_text" .= String "Video", "thumbnail_url" .= String "https://example.com/thumbnail.png", "title" .= object ["type" .= String "plain_text", "text" .= String "Title"], "video_url" .= String (T.replicate 3001 "x")]) :: Result VideoBlock)),
+    ("video-title-url-too-long", Just invalid186, void (fromValue (object ["type" .= String "video", "alt_text" .= String "Video", "thumbnail_url" .= String "https://example.com/thumbnail.png", "title" .= object ["type" .= String "plain_text", "text" .= String "Title"], "video_url" .= String "https://example.com/video.mp4", "title_url" .= String (T.replicate 2001 "x")]) :: Result VideoBlock)),
+    ("video-provider-icon-url-too-long", Just invalid187, void (fromValue (object ["type" .= String "video", "alt_text" .= String "Video", "thumbnail_url" .= String "https://example.com/thumbnail.png", "title" .= object ["type" .= String "plain_text", "text" .= String "Title"], "video_url" .= String "https://example.com/video.mp4", "provider_icon_url" .= String (T.replicate 2001 "x")]) :: Result VideoBlock)),
+    ("view-external-id-too-long", Just invalid188, void (fromValue (object ["type" .= String "home", "blocks" .= toJSON ([object ["type" .= String "divider"]] :: [Value]), "external_id" .= String (T.replicate 256 "x")]) :: Result HomeTabView)),
+    ("attachment-missing-blocks", Nothing, void (fromValue (object ["fallback" .= String "Summary"]) :: Result Attachment))
+  ]
diff --git a/test/Foundation.hs b/test/Foundation.hs
new file mode 100644
--- /dev/null
+++ b/test/Foundation.hs
@@ -0,0 +1,53 @@
+module Main (main) where
+
+import Control.Monad (unless)
+import Data.Aeson (ToJSON (..), object, (.=))
+import Data.Text qualified as T
+import Slackblocks
+
+assert :: String -> Bool -> IO ()
+assert label ok = unless ok (fail label)
+
+main :: IO ()
+main = do
+  let payload = do
+        label <- buildPlainText ((plainText "Open") {plainTextEmoji = Just False})
+        action <- buildButtonElement ((buttonElement label) {buttonElementActionId = Just "open"})
+        text <- buildMarkdownText (markdownText "*Hello*")
+        section <- buildSectionBlock (sectionBlock {sectionBlockText = Just (markdownTextAsText text), sectionBlockAccessory = Just (buttonElementAsElement action)})
+        buildMessagePayload ((messagePayload "C123") {messagePayloadBlocks = Just [sectionBlockAsBlock section]})
+  message <- either (fail . show) pure payload
+  assert "typed complete payload" $
+    toJSON message
+      == object
+        [ "channel" .= ("C123" :: T.Text),
+          "text" .= ("" :: T.Text),
+          "mrkdwn" .= True,
+          "blocks"
+            .= [ object
+                   [ "type" .= ("section" :: T.Text),
+                     "text" .= object ["type" .= ("mrkdwn" :: T.Text), "text" .= ("*Hello*" :: T.Text)],
+                     "accessory"
+                       .= object
+                         [ "type" .= ("button" :: T.Text),
+                           "action_id" .= ("open" :: T.Text),
+                           "text" .= object ["type" .= ("plain_text" :: T.Text), "text" .= ("Open" :: T.Text), "emoji" .= False]
+                         ]
+                   ]
+               ]
+        ]
+  let empty = buildPlainText (plainText "")
+  assert "empty text rejected" $ either (all ((== LengthExceeded) . errorCategory)) (const False) empty
+  boundary <- either (fail . show) pure $ buildPlainText (plainText (T.replicate 3000 "😀"))
+  assert "Unicode counts code points" $ plainTextText (inspectPlainText boundary) == T.replicate 3000 "😀"
+  assert "Unicode over-limit rejected" $ either (const True) (const False) $ buildPlainText (plainText (T.replicate 3001 "😀"))
+  assert "checked editing" $ either (const True) (const False) $ buildPlainText ((inspectPlainText boundary) {plainTextText = ""})
+  label <- either (fail . show) pure $ buildPlainText (plainText "Ask")
+  assert "display requires prompt" $
+    either (all ((== MissingRequired) . errorCategory)) (const False) $
+      buildButtonElement ((buttonElement label) {buttonElementAgentPromptDisplay = Just "Summary"})
+  assert "empty prompt is present" $
+    either (const False) (const True) $
+      buildButtonElement ((buttonElement label) {buttonElementAgentPrompt = Just "", buttonElementAgentPromptDisplay = Just "Summary"})
+  assert "spec identity" $ specVersion == "1.4.0"
+  putStrLn "Foundation construction, wire, Unicode, editing and prompt controls passed."
diff --git a/test/Helpers.hs b/test/Helpers.hs
new file mode 100644
--- /dev/null
+++ b/test/Helpers.hs
@@ -0,0 +1,218 @@
+{-# LANGUAGE LambdaCase #-}
+
+module Main (main) where
+
+import Control.Exception (evaluate)
+import Control.Monad (forM_, unless)
+import Data.Aeson (ToJSON (..), Value (..), encode, object, (.=))
+import Data.Aeson qualified as A
+import Data.Aeson.KeyMap qualified as KM
+import Data.ByteString.Lazy qualified as L
+import Data.ByteString.Lazy.Char8 qualified as C
+import Data.Map.Strict qualified as Map
+import Data.Maybe (isNothing)
+import Data.Text qualified as T
+import Slackblocks
+import Test.QuickCheck hiding (Result, label)
+import Test.QuickCheck.Random (mkQCGen)
+
+assert :: String -> Bool -> IO ()
+assert label condition = unless condition (fail label)
+
+accept :: (Show e) => Either e a -> IO a
+accept = either (fail . show) pure
+
+reject :: String -> Result a -> IO ()
+reject label = either report (const (fail (label <> " accepted")))
+  where
+    report errors = do
+      diagnosticLength <- evaluate (length (show errors))
+      assert (label <> ": empty diagnostics") (diagnosticLength > 0)
+
+rejectExponent :: String -> Result a -> IO ()
+rejectExponent label = either report (const (fail (label <> " accepted")))
+  where
+    report errors =
+      assert (label <> ": wrong diagnostic") $
+        all (\e -> errorCategory e == TypeMismatch && errorConstraint e == "json.number.exponent") errors
+
+textValue :: Gen T.Text
+textValue = T.pack <$> listOf1 (elements ['a', 'Z', '0', 'é', '中', '😀', '\x301', '\n', '"', '\\'])
+
+propertyRun :: (Testable prop) => String -> prop -> IO ()
+propertyRun name test = do
+  putStrLn name
+  result <- quickCheckWithResult stdArgs {maxSuccess = 500, replay = Just (mkQCGen 20261025, 0)} test
+  unless (isSuccess result) (fail ("Property failed: " <> name))
+
+main :: IO ()
+main = do
+  divider <- accept (buildDividerBlock dividerBlock)
+  let blocks = replicate 12 (dividerBlockAsBlock divider)
+  title <- accept (buildPlainText (plainText "Section"))
+  section <- accept (buildContainerBlock (accordionSection title [dividerBlockAsBlock divider]))
+  accept (accordion [section, section]) >>= assert "accordion preserves order" . (== replicate 2 (containerBlockAsBlock section))
+  reject "empty accordion" (accordion [])
+  regular <- accept $ buildContainerBlock ((containerBlock [dividerBlockAsBlock divider]) {containerBlockTitle = Just title})
+  reject "noncollapsible accordion" (accordion [regular])
+  first <- accept (paginate (paginator "list") blocks)
+  middle <- accept (paginate ((paginator "list") {paginatorPage = 2}) blocks)
+  lastPage <- accept (paginate ((paginator "list") {paginatorPage = 3, paginatorShowPageIndicator = False}) blocks)
+  assert "page sizes include controls" (length first == 7 && length middle == 7 && length lastPage == 3)
+  let actionValues page = case matchBlockActionsBlock (last page) of
+        Nothing -> []
+        Just actions ->
+          [ (buttonElementActionId fields, buttonElementValue fields)
+          | element <- actionsBlockElements (inspectActionsBlock actions),
+            Just button <- [matchElementButtonElement element],
+            let fields = inspectButtonElement button
+          ]
+  assert "first navigation" (actionValues first == [(Just "list.next", Just "2")])
+  assert "middle navigation" (actionValues middle == [(Just "list.previous", Just "1"), (Just "list.next", Just "3")])
+  assert "last navigation" (actionValues lastPage == [(Just "list.previous", Just "2")])
+  reject "empty pagination" (paginate (paginator "list") [])
+  reject "empty prefix" (paginate (paginator "") blocks)
+  reject "page zero" (paginate ((paginator "list") {paginatorPage = 0}) blocks)
+  reject "size zero" (paginate ((paginator "list") {paginatorPageSize = 0}) blocks)
+  reject "past end" (paginate ((paginator "list") {paginatorPage = 4}) blocks)
+  reject "oversized navigation ID" (paginate (paginator (T.replicate 255 "x")) blocks)
+  accept (paginate ((paginator "list") {paginatorPageSize = 10 ^ (100 :: Int)}) blocks) >>= assert "huge size has no Int overflow" . (== blocks)
+  reject "expanded message count" $ do
+    page <- paginate ((paginator "list") {paginatorPageSize = 50}) (replicate 51 (dividerBlockAsBlock divider))
+    buildMessagePayload ((messagePayload "C123") {messagePayloadBlocks = Just page})
+  parameter <- accept (buildInputParameter (inputParameter "ticket" "42"))
+  flow <- accept (workflowFromURL "https://example.com/trigger" [parameter])
+  assert "workflow has typed parameters" (triggerCustomizableInputParameters (inspectTrigger (workflowTrigger (inspectWorkflow flow))) == Just [parameter])
+  flowEmpty <- accept (workflowFromURL "https://example.com/trigger" [])
+  assert "empty workflow parameters omitted" (isNothing (triggerCustomizableInputParameters (inspectTrigger (workflowTrigger (inspectWorkflow flowEmpty)))))
+  accept (hexColor "A0B1c2") >>= assert "hex keeps case" . (== either (error . show) id (hexColor "#A0B1c2"))
+  assert "named palette" (map (colorText . namedColor) [minBound .. maxBound] == ["good", "warning", "danger", "#ff0000", "#0000ff", "#ffff00", "#00ff00", "#ff8800", "#8800ff", "#000000"])
+  reject "bad hex" (hexColor "#gg0000")
+  reject "short hex" (hexColor "fff")
+  reject "negative RGB" (rgbColor (-1) 0 0)
+  reject "large RGB" (rgbColor 0 0 256)
+  assert "Builder blocks wrapped and escaped" $
+    blockKitBuilderURL (BuilderBlocks [dividerBlockAsBlock divider]) (Just "T/a#")
+      == "https://app.slack.com/block-kit-builder/T%2Fa%23#%7B%22blocks%22%3A%5B%7B%22type%22%3A%22divider%22%7D%5D%7D"
+  assert "Builder Unicode encoded as UTF8" ("%F0%9F%98%80" `T.isInfixOf` blockKitBuilderURL (BuilderJSON (Map.singleton "emoji" (String "😀"))) Nothing)
+  assert "Builder unreserved segment" ("T-a._~#" `T.isInfixOf` blockKitBuilderURL (BuilderJSON Map.empty) (Just "T-a._~"))
+  msg <- accept (buildMessagePayload (messagePayload "C123"))
+  web <- accept (buildWebhookMessage webhookMessage)
+  response <- accept (buildMessageResponse messageResponse)
+  modalTitle <- accept (buildPlainText (plainText "View"))
+  modal <- accept (buildModalView (modalView modalTitle []))
+  home <- accept (buildHomeTabView (homeTabView [dividerBlockAsBlock divider]))
+  assert "Builder message" (toJSON (BuilderMessage msg) == toJSON msg)
+  assert "Builder webhook" (toJSON (BuilderWebhook web) == toJSON web)
+  assert "Builder response" (toJSON (BuilderResponse response) == toJSON response)
+  assert "Builder modal" (toJSON (BuilderModal modal) == toJSON modal)
+  assert "Builder home" (toJSON (BuilderHome home) == toJSON home)
+  let duplicate = "{\"type\":\"plain_text\",\"text\":\"one\",\"te\\u0078t\":\"two\"}"
+  reject "escaped duplicate key" (decode duplicate :: Result PlainText)
+  reject "nested duplicate metadata" (decode "{\"metadata\":{\"key\":1,\"key\":2}}" :: Result WebhookMessage)
+  reject "trailing JSON" (decode "{\"type\":\"divider\"} false" :: Result DividerBlock)
+  reject "invalid UTF8" (decode (L.pack [34, 255, 34]) :: Result PlainText)
+  reject "invalid JSON object" (decode "{\"type\":" :: Result DividerBlock)
+  reject "invalid JSON array" (decode "[1," :: Result [Integer])
+  reject "wrong boolean" (fromValue (String "false") :: Result Bool)
+  reject "wrong text" (fromValue (Bool False) :: Result T.Text)
+  reject "wrong integer" (fromValue (String "1") :: Result Integer)
+  reject "fractional integer" (decode "1.5" :: Result Integer)
+  -- Aeson's token lexer narrows JSON exponents to Int. Check the original
+  -- bytes before that conversion can wrap, including opaque application data.
+  let tooHigh = show (toInteger (maxBound :: Int) + 1)
+      tooLow = show (toInteger (minBound :: Int) - 1)
+      overflow = "1e18446744073709551616"
+  forM_ ["1e" <> tooHigh, "1E+" <> tooHigh, "-1e" <> tooLow, "1.1e" <> show (minBound :: Int), overflow] $ \literal -> do
+    let raw = C.pack literal
+    rejectExponent "overflowing metadata exponent" (decode ("{\"metadata\":{\"nested\":[" <> raw <> "]}}") :: Result WebhookMessage)
+    rejectExponent "overflowing extension exponent" (decode ("{\"type\":\"divider\",\"extra\":" <> raw <> "}") :: Result DividerBlock)
+    rejectExponent "overflowing coerced bound" (decode ("{\"type\":\"number_input\",\"is_decimal_allowed\":true,\"min_value\":" <> raw <> "}") :: Result NumberInputElement)
+    rejectExponent "overflow split across lazy chunks" (decode (L.fromChunks (map (L.toStrict . L.singleton) (L.unpack raw))) :: Result Integer)
+  forM_ [show (maxBound :: Int), show (toInteger (minBound :: Int) + 1), "+00001000", "-1000"] $ \power -> do
+    let raw = "{\"n\":1e" <> C.pack power <> "}"
+    parsed <- accept (decode raw :: Result (Map.Map T.Text Value))
+    assert "representable exponent preserved" (decode (encode parsed) == Right parsed)
+  forM_ ["1e" <> show (minBound :: Int), "10e" <> show (maxBound :: Int), "-100e" <> show (toInteger (maxBound :: Int) - 1)] $ \literal -> do
+    let raw = C.pack literal
+    rejectExponent "encoder exponent overflow" (decode ("{\"n\":" <> raw <> "}") :: Result (Map.Map T.Text Value))
+    number <- accept (A.eitherDecode raw :: Either String Value)
+    let metadata = Map.singleton "nested" (toJSON [number])
+    rejectExponent "native metadata encoder overflow" (buildWebhookMessage (webhookMessage {webhookMessageMetadata = Just metadata}))
+    rejectExponent "native extension encoder overflow" (buildDividerBlock (dividerBlock {dividerBlockExtensions = metadata}))
+    rejectExponent "preparsed metadata encoder overflow" (fromValue (object ["metadata" .= metadata]) :: Result WebhookMessage)
+    rejectExponent "preparsed map encoder overflow" (fromValue (toJSON metadata) :: Result (Map.Map T.Text Value))
+    rejectExponent "Aeson model instance encoder overflow" (fromValue (object ["label" .= String "n", "value" .= number]) :: Result DataPoint)
+    assert "Aeson instance rejects encoder overflow" (case A.fromJSON (object ["metadata" .= metadata]) :: A.Result WebhookMessage of A.Error _ -> True; _ -> False)
+    case number of
+      Number value -> do
+        assert "decimal string conversion keeps exact representation" (decimalText value == T.pack literal)
+        _ <- accept (buildNumberInputElement ((numberInputElement True) {numberInputElementMinValue = Just (decimalText value)}))
+        pure ()
+      _ -> fail "numeric regression input was not a number"
+  forM_ ["12e" <> show (minBound :: Int), "10e" <> show (minBound :: Int), "0e" <> show (minBound :: Int), "10e" <> show (toInteger (maxBound :: Int) - 1), "1.2e" <> show (maxBound :: Int)] $ \literal -> do
+    let raw = "{\"n\":" <> C.pack literal <> "}"
+    parsed <- accept (decode raw :: Result (Map.Map T.Text Value))
+    assert "neighboring encodable exponent remains supported" (decode (encode parsed) == Right parsed)
+  forM_ ["1e1000000000", "-1e1000000000"] $ \literal -> do
+    let raw = "{\"type\":\"plain_text_input\",\"max_length\":" <> literal <> "}"
+    case decode raw :: Result PlainTextInputElement of
+      Left errors -> assert "huge bounded integer rejects before expansion" (all (\e -> errorCategory e == OutOfRange && errorPath e == [Field "max_length"]) errors)
+      Right _ -> fail "huge bounded integer accepted"
+    native <- accept (A.eitherDecode raw :: Either String Value)
+    assert "Aeson bounded integer rejects before expansion" (case A.fromJSON native :: A.Result PlainTextInputElement of A.Error _ -> True; _ -> False)
+  forM_ ["3e3", "30000e-1"] $ \literal -> do
+    value <- accept (decode ("{\"type\":\"plain_text_input\",\"max_length\":" <> literal <> "}") :: Result PlainTextInputElement)
+    assert "exact integer boundary accepted" (plainTextInputElementMaxLength (inspectPlainTextInputElement value) == Just 3000)
+  case decode "{\"type\":\"plain_text_input\",\"max_length\":3000.5}" :: Result PlainTextInputElement of
+    Left errors -> assert "fraction retains type category" (all ((== TypeMismatch) . errorCategory) errors)
+    Right _ -> fail "fractional integer accepted"
+  let quoted = "escaped \\\" quote and \\ backslash; " <> T.pack overflow <> " \\\n"
+  assert "number-looking string remains text" ((decode (encode quoted) :: Result T.Text) == Right quoted)
+  forM_ ["1e+", "1e-", "1e1.5", "\"unterminated", "\"trailing\\"] $ \raw ->
+    reject "malformed JSON still rejected" (decode raw :: Result T.Text)
+  assert "huge decimal-string bound remains supported" $
+    either
+      (const False)
+      (const True)
+      (decode "{\"type\":\"number_input\",\"is_decimal_allowed\":true,\"min_value\":\"1e18446744073709551616\"}" :: Result NumberInputElement)
+  reject "wrong list" (fromValue (Bool True) :: Result [Integer])
+  reject "wrong map" (fromValue (Bool False) :: Result (Map.Map T.Text Value))
+  reject "wrong model shape" (fromValue (Bool False) :: Result PlainText)
+  reject "wrong child role shape" (fromValue (Bool False) :: Result Block)
+  reject "wrong discriminator" (fromValue (object ["type" .= String "unknown"]) :: Result PlainText)
+  reject "unknown child role" (fromValue (object ["type" .= String "unknown"]) :: Result Block)
+  reject "required null" (fromValue (object ["type" .= String "plain_text", "text" .= Null]) :: Result PlainText)
+  optionalNull <- accept (fromValue (object ["type" .= String "plain_text", "text" .= String "text", "emoji" .= Null]) :: Result PlainText)
+  assert "optional null omitted" (isNothing (plainTextEmoji (inspectPlainText optionalNull)))
+  reject "bad plan task collection" (fromValue (object ["type" .= String "plan", "title" .= String "Plan", "tasks" .= Bool True]) :: Result PlanBlock)
+  reject "bad plan task" (fromValue (object ["type" .= String "plan", "title" .= String "Plan", "tasks" .= [Bool True]]) :: Result PlanBlock)
+  reject "bad plan task discriminator" (fromValue (object ["type" .= String "plan", "title" .= String "Plan", "tasks" .= [object ["type" .= String "wrong"]]]) :: Result PlanBlock)
+  reject "malformed surrogate" (decode "\"\\uD800\"" :: Result T.Text)
+  assert "syntax boolean false" ((decode "false\n" :: Result Bool) == Right False)
+  assert "syntax decimal exact" ((decode "{\"x\":0.125,\"y\":1.25e2}" :: Result (Map.Map T.Text Value)) == Right (Map.fromList [("x", Number 0.125), ("y", Number 125)]))
+  let semantic = object ["bool" .= True, "null" .= Null, "array" .= [Number 1, String "é"], "exact" .= (9007199254740993 :: Integer)]
+  assert "all syntax token variants" ((decode (encode semantic) :: Result (Map.Map T.Text Value)) == Right (Map.fromList [("bool", Bool True), ("null", Null), ("array", toJSON [Number 1, String "é"]), ("exact", Number 9007199254740993)]))
+  propertyRun "Unicode checked construction / exact JSON ingress" $ forAll textValue $ \value ->
+    let result = buildPlainText (plainText value)
+     in case result of Left _ -> T.length value > 3000; Right checked -> decode (encode checked) == Right checked
+  propertyRun "RGB produces valid six-digit colors" $ forAll (vectorOf 3 (chooseInteger (0, 255))) $ \case
+    [r, g, b] -> case rgbColor r g b of
+      Right color -> hexColor (colorText color) == Right color
+      _ -> False
+    _ -> False
+  propertyRun "Pagination preserves the selected content" $ forAll (chooseInt (1, 100)) $ \count ->
+    forAll (chooseInteger (1, 20)) $ \size ->
+      let items = take count (cycle blocks); pages = (fromIntegral count - 1) `div` size + 1
+       in forAll (chooseInteger (1, pages)) $ \page -> case paginate ((paginator "prop") {paginatorPage = page, paginatorPageSize = size}) items of
+            Left _ -> False
+            Right output -> take (min (fromInteger size) (count - fromInteger ((page - 1) * size))) output == take (fromInteger size) (drop (fromInteger ((page - 1) * size)) items)
+  propertyRun "Record edits must cross validation" $ forAll textValue $ \value ->
+    case buildPlainText (plainText value) of
+      Left _ -> False
+      Right checked -> either (const True) (const False) (buildPlainText ((inspectPlainText checked) {plainTextText = ""}))
+  propertyRun "Opaque metadata preserves exact integer values" $ \(integer :: Integer) ->
+    let payload = buildWebhookMessage (webhookMessage {webhookMessageMetadata = Just (Map.singleton "integer" (toJSON integer))})
+     in case payload of Left _ -> False; Right checked -> decode (encode checked) == Right checked
+  assert "no duplicate keys in helper output" $ case toJSON (BuilderJSON (Map.singleton "x" Null)) of Object fields -> KM.size fields == 1; _ -> False
+  putStrLn "Helpers, strict JSON boundaries and 2,500 property cases passed."
diff --git a/test/spec/coverage.json b/test/spec/coverage.json
new file mode 100644
--- /dev/null
+++ b/test/spec/coverage.json
@@ -0,0 +1,111 @@
+{
+  "spec_version": "1.4.0",
+  "capabilities": {
+    "blocks.actions": ["blocks/actions_block_checkboxes"],
+    "blocks.alert": ["blocks/alert_block"],
+    "blocks.card": ["blocks/card_block"],
+    "blocks.carousel": ["blocks/carousel_block"],
+    "blocks.container": ["blocks/container_block"],
+    "blocks.context": ["blocks/context_block_text_only"],
+    "blocks.context_actions": [
+      "blocks/context_actions_feedback_buttons",
+      "blocks/context_actions_icon_button"
+    ],
+    "blocks.data_table": ["blocks/data_table_block"],
+    "blocks.data_visualization": [
+      "blocks/data_visualization_area",
+      "blocks/data_visualization_bar",
+      "blocks/data_visualization_pie",
+      "blocks/data_visualization_line"
+    ],
+    "blocks.divider": ["blocks/divider_block_only"],
+    "blocks.file": ["blocks/file_block_only"],
+    "blocks.header": [
+      "blocks/header_block_only",
+      "blocks/header_block_text_at_limit",
+      "blocks/header_block_emoji_at_limit"
+    ],
+    "blocks.image": ["blocks/image_block_only", "blocks/image_block_slack_file"],
+    "blocks.input": ["blocks/input_block_only"],
+    "blocks.markdown": ["blocks/markdown_block_basic", "blocks/markdown_empty"],
+    "blocks.plan": ["blocks/plan_block"],
+    "blocks.rich_text": ["blocks/rich_text_block_basic"],
+    "blocks.section": ["blocks/section_block_text_only"],
+    "blocks.table": ["blocks/table_block", "blocks/table_ragged_rows"],
+    "blocks.task_card": ["blocks/task_card_block"],
+    "blocks.video": ["blocks/video_block_basic"],
+    "elements.button": ["elements/button_basic", "elements/button_text_at_limit", "elements/button_without_action_id", "elements/button_agent_prompt", "elements/button_agent_prompt_display", "elements/button_agent_prompt_at_limit", "elements/button_agent_prompt_empty"],
+    "elements.checkboxes": ["elements/checkbox_basic"],
+    "elements.date_picker": ["elements/date_picker_basic"],
+    "elements.datetime_picker": ["elements/datetime_picker_basic"],
+    "elements.email_input": ["elements/email_input_basic"],
+    "elements.feedback_buttons": ["blocks/context_actions_feedback_buttons"],
+    "elements.file_input": ["elements/file_input_basic"],
+    "elements.image": [
+      "elements/image_basic",
+      "elements/image_slack_file_id",
+      "elements/image_slack_file_url"
+    ],
+    "elements.icon_button": ["blocks/context_actions_icon_button"],
+    "elements.multi_select_channels": ["elements/multi_select_channel"],
+    "elements.multi_select_conversations": ["elements/multi_select_conversation"],
+    "elements.multi_select_external": ["elements/multi_select_external"],
+    "elements.multi_select_static": ["elements/multi_select_static"],
+    "elements.multi_select_users": ["elements/multi_select_user"],
+    "elements.number_input": ["elements/number_input_basic", "elements/number_input_scientific_bounds", "elements/number_input_large_exponent", "elements/number_input_bounds"],
+    "elements.overflow": ["elements/overflow_menu_basic"],
+    "elements.plain_text_input": ["elements/plaintext_input_basic"],
+    "elements.radio_buttons": ["elements/radio_button_group_basic"],
+    "elements.rich_text_input": ["elements/rich_text_input_basic"],
+    "elements.select_channels": ["elements/select_menu_channel"],
+    "elements.select_conversations": ["elements/select_menu_conversation"],
+    "elements.select_external": ["elements/select_menu_external"],
+    "elements.select_static": ["elements/select_menu_static"],
+    "elements.select_users": ["elements/select_menu_user"],
+    "elements.time_picker": ["elements/timepicker_basic"],
+    "elements.url_input": ["elements/url_input_basic"],
+    "elements.url_source": ["elements/url_source_basic"],
+    "elements.workflow_button": ["elements/workflow_button_basic"],
+    "messages.attachment": ["attachments/attachment_simple"],
+    "messages.message": ["messages/message_basic", "messages/message_two_charts"],
+    "messages.message_response": ["messages/message_response"],
+    "messages.webhook_message": ["messages/webhook_message_basic"],
+    "objects.column_settings": ["blocks/table_block"],
+    "objects.axis_config": ["blocks/data_visualization_line"],
+    "objects.chart_segment": ["blocks/data_visualization_pie"],
+    "objects.confirmation": ["objects/confirmation_dialogue_basic"],
+    "objects.conversation_filter": ["objects/conversation_filter_basic"],
+    "objects.dispatch_action_configuration": [
+      "objects/dispatch_action_configuration_basic"
+    ],
+    "objects.data_point": ["blocks/data_visualization_line"],
+    "objects.data_series": ["blocks/data_visualization_line"],
+    "objects.feedback_button": ["blocks/context_actions_feedback_buttons"],
+    "objects.input_parameter": ["objects/input_parameter_basic"],
+    "objects.markdown_text": ["objects/text_markdown_basic"],
+    "objects.option": ["objects/option_basic", "objects/option_value_at_limit"],
+    "objects.option_group": ["objects/option_group_basic"],
+    "objects.plain_text": ["objects/text_plaintext_basic"],
+    "objects.raw_text": ["blocks/table_block"],
+    "objects.raw_number": ["blocks/data_table_block"],
+    "objects.slack_icon": ["blocks/card_block"],
+    "objects.slack_file": [
+      "elements/image_slack_file_id",
+      "elements/image_slack_file_url"
+    ],
+    "objects.trigger": ["objects/trigger_basic"],
+    "objects.workflow": ["objects/workflow_basic"],
+    "rich_text.channel": ["rich_text/rich_text_channel_basic"],
+    "rich_text.code_block": ["rich_text/rich_text_code_block_basic"],
+    "rich_text.emoji": ["rich_text/rich_text_emoji_basic"],
+    "rich_text.link": ["rich_text/rich_text_link_basic"],
+    "rich_text.list": ["rich_text/rich_text_list_basic"],
+    "rich_text.quote": ["rich_text/rich_text_quote_basic"],
+    "rich_text.section": ["rich_text/rich_text_section_basic"],
+    "rich_text.text": ["rich_text/rich_text_basic"],
+    "rich_text.user": ["rich_text/rich_text_user_basic"],
+    "rich_text.user_group": ["rich_text/rich_text_user_group_basic"],
+    "views.home_tab": ["views/hometab_view"],
+    "views.modal": ["views/modal_with_blocks", "views/modal_without_blocks"]
+  }
+}
diff --git a/test/spec/fixtures/invalid/manifest.json b/test/spec/fixtures/invalid/manifest.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/invalid/manifest.json
@@ -0,0 +1,1145 @@
+{
+  "spec_version": "1.4.0",
+  "cases": [
+    {
+      "id": "text-empty",
+      "category": "length-exceeded",
+      "constraint": "text.min_length",
+      "description": "A text object with an empty value must be rejected"
+    },
+    {
+      "id": "text-too-long",
+      "category": "length-exceeded",
+      "constraint": "text.max_length",
+      "description": "A text object longer than 3000 characters must be rejected"
+    },
+    {
+      "id": "button-action-id-too-long",
+      "category": "length-exceeded",
+      "constraint": "action_id.max_length",
+      "description": "An action ID longer than 255 characters must be rejected"
+    },
+    {
+      "id": "button-text-too-long",
+      "category": "length-exceeded",
+      "constraint": "button.text.max_length",
+      "description": "Button text longer than 75 characters must be rejected"
+    },
+    {
+      "id": "button-url-too-long",
+      "category": "length-exceeded",
+      "constraint": "button.url.max_length",
+      "description": "A button URL longer than 3000 characters must be rejected"
+    },
+    {
+      "id": "button-value-too-long",
+      "category": "length-exceeded",
+      "constraint": "button.value.max_length",
+      "description": "A button value longer than 2000 characters must be rejected"
+    },
+    {
+      "id": "confirmation-title-too-long",
+      "category": "length-exceeded",
+      "constraint": "confirmation.title.max_length",
+      "description": "A confirmation title longer than 100 characters must be rejected"
+    },
+    {
+      "id": "confirmation-text-too-long",
+      "category": "length-exceeded",
+      "constraint": "confirmation.text.max_length",
+      "description": "Confirmation text longer than 300 characters must be rejected"
+    },
+    {
+      "id": "confirmation-confirm-too-long",
+      "category": "length-exceeded",
+      "constraint": "confirmation.confirm.max_length",
+      "description": "Confirmation button text longer than 30 characters must be rejected"
+    },
+    {
+      "id": "confirmation-deny-too-long",
+      "category": "length-exceeded",
+      "constraint": "confirmation.deny.max_length",
+      "description": "Denial button text longer than 30 characters must be rejected"
+    },
+    {
+      "id": "option-text-too-long",
+      "category": "length-exceeded",
+      "constraint": "option.text.max_length",
+      "description": "Option text longer than 75 characters must be rejected"
+    },
+    {
+      "id": "option-value-too-long",
+      "category": "length-exceeded",
+      "constraint": "option.value.max_length",
+      "description": "An option value longer than 150 characters must be rejected"
+    },
+    {
+      "id": "option-url-too-long",
+      "category": "length-exceeded",
+      "constraint": "option.url.max_length",
+      "description": "An option URL longer than 3000 Unicode code points must be rejected (exercised with astral-plane emoji)"
+    },
+    {
+      "id": "option-description-too-long",
+      "category": "length-exceeded",
+      "constraint": "option.description.max_length",
+      "description": "An option description longer than 75 characters must be rejected"
+    },
+    {
+      "id": "option-group-label-too-long",
+      "category": "length-exceeded",
+      "constraint": "option_group.label.max_length",
+      "description": "An option-group label longer than 75 characters must be rejected"
+    },
+    {
+      "id": "option-group-empty",
+      "category": "length-exceeded",
+      "constraint": "option_group.options.min_items",
+      "description": "An option group with no options must be rejected"
+    },
+    {
+      "id": "option-group-too-many-options",
+      "category": "length-exceeded",
+      "constraint": "option_group.options.max_items",
+      "description": "An option group with more than 100 options must be rejected"
+    },
+    {
+      "id": "select-placeholder-too-long",
+      "category": "length-exceeded",
+      "constraint": "select.placeholder.max_length",
+      "description": "A select placeholder longer than 150 characters must be rejected"
+    },
+    {
+      "id": "plain-text-input-placeholder-too-long",
+      "category": "length-exceeded",
+      "constraint": "plain_text_input.placeholder.max_length",
+      "description": "A plain-text input placeholder longer than 150 characters must be rejected"
+    },
+    {
+      "id": "email-input-placeholder-too-long",
+      "category": "length-exceeded",
+      "constraint": "email_input.placeholder.max_length",
+      "description": "A email input placeholder longer than 150 characters must be rejected"
+    },
+    {
+      "id": "url-input-placeholder-too-long",
+      "category": "length-exceeded",
+      "constraint": "url_input.placeholder.max_length",
+      "description": "A URL input placeholder longer than 150 characters must be rejected"
+    },
+    {
+      "id": "number-input-placeholder-too-long",
+      "category": "length-exceeded",
+      "constraint": "number_input.placeholder.max_length",
+      "description": "A number input placeholder longer than 150 characters must be rejected"
+    },
+    {
+      "id": "date-picker-placeholder-too-long",
+      "category": "length-exceeded",
+      "constraint": "date_picker.placeholder.max_length",
+      "description": "A date picker placeholder longer than 150 characters must be rejected"
+    },
+    {
+      "id": "time-picker-placeholder-too-long",
+      "category": "length-exceeded",
+      "constraint": "time_picker.placeholder.max_length",
+      "description": "A time picker placeholder longer than 150 characters must be rejected"
+    },
+    {
+      "id": "rich-text-input-placeholder-too-long",
+      "category": "length-exceeded",
+      "constraint": "rich_text_input.placeholder.max_length",
+      "description": "A rich text input placeholder longer than 150 characters must be rejected"
+    },
+    {
+      "id": "select-too-many-options",
+      "category": "length-exceeded",
+      "constraint": "select.options.max_items",
+      "description": "A static select with more than 100 options must be rejected"
+    },
+    {
+      "id": "select-too-many-option-groups",
+      "category": "length-exceeded",
+      "constraint": "select.option_groups.max_items",
+      "description": "A static select with more than 100 option groups must be rejected"
+    },
+    {
+      "id": "overflow-empty",
+      "category": "length-exceeded",
+      "constraint": "overflow.options.min_items",
+      "description": "An overflow menu with no options must be rejected"
+    },
+    {
+      "id": "overflow-too-many-options",
+      "category": "length-exceeded",
+      "constraint": "overflow.options.max_items",
+      "description": "An overflow menu with more than five options must be rejected"
+    },
+    {
+      "id": "checkboxes-empty",
+      "category": "length-exceeded",
+      "constraint": "checkboxes.options.min_items",
+      "description": "A checkbox group with no options must be rejected"
+    },
+    {
+      "id": "checkboxes-too-many-options",
+      "category": "length-exceeded",
+      "constraint": "checkboxes.options.max_items",
+      "description": "A checkbox group with more than ten options must be rejected"
+    },
+    {
+      "id": "radio-buttons-empty",
+      "category": "length-exceeded",
+      "constraint": "radio_buttons.options.min_items",
+      "description": "A radio button group with no options must be rejected"
+    },
+    {
+      "id": "radio-buttons-too-many-options",
+      "category": "length-exceeded",
+      "constraint": "radio_buttons.options.max_items",
+      "description": "A radio button group with more than ten options must be rejected"
+    },
+    {
+      "id": "table-too-many-rows",
+      "category": "length-exceeded",
+      "constraint": "table.rows.max_items",
+      "description": "A table with more than 100 rows must be rejected"
+    },
+    {
+      "id": "table-too-many-columns",
+      "category": "length-exceeded",
+      "constraint": "table.columns.max_items",
+      "description": "A table row with more than 20 cells must be rejected"
+    },
+    {
+      "id": "file-input-max-files-too-small",
+      "category": "out-of-range",
+      "constraint": "file_input.max_files.min",
+      "description": "File input max files below one must be rejected"
+    },
+    {
+      "id": "file-input-max-files-too-large",
+      "category": "out-of-range",
+      "constraint": "file_input.max_files.max",
+      "description": "File input max files above 10 must be rejected"
+    },
+    {
+      "id": "dispatch-action-no-triggers",
+      "category": "length-exceeded",
+      "constraint": "dispatch_action_configuration.trigger_actions_on.min_items",
+      "description": "A dispatch action configuration with no triggers must be rejected"
+    },
+    {
+      "id": "dispatch-action-missing-triggers",
+      "category": "missing-required",
+      "constraint": "dispatch_action_configuration.trigger_actions_on.required",
+      "description": "A dispatch action configuration without trigger_actions_on must be rejected"
+    },
+    {
+      "id": "dispatch-action-too-many-triggers",
+      "category": "length-exceeded",
+      "constraint": "dispatch_action_configuration.trigger_actions_on.max_items",
+      "description": "A dispatch action configuration with more than two triggers must be rejected"
+    },
+    {
+      "id": "plain-text-input-max-length-too-large",
+      "category": "out-of-range",
+      "constraint": "plain_text_input.max_length.max",
+      "description": "Plain-text input max length above 3000 must be rejected"
+    },
+    {
+      "id": "actions-too-many-elements",
+      "category": "length-exceeded",
+      "constraint": "actions.elements.max_items",
+      "description": "An actions block with more than 25 elements must be rejected"
+    },
+    {
+      "id": "context-too-many-elements",
+      "category": "length-exceeded",
+      "constraint": "context.elements.max_items",
+      "description": "A context block with more than 10 elements must be rejected"
+    },
+    {
+      "id": "header-text-too-long",
+      "category": "length-exceeded",
+      "constraint": "header.text.max_length",
+      "description": "Header text longer than 150 characters must be rejected"
+    },
+    {
+      "id": "image-url-too-long",
+      "category": "length-exceeded",
+      "constraint": "image.image_url.max_length",
+      "description": "An image URL longer than 3000 characters must be rejected"
+    },
+    {
+      "id": "image-alt-text-too-long",
+      "category": "length-exceeded",
+      "constraint": "image.alt_text.max_length",
+      "description": "Image alternative text longer than 2000 characters must be rejected"
+    },
+    {
+      "id": "input-label-too-long",
+      "category": "length-exceeded",
+      "constraint": "input.label.max_length",
+      "description": "An input-block label longer than 2000 characters must be rejected"
+    },
+    {
+      "id": "input-hint-too-long",
+      "category": "length-exceeded",
+      "constraint": "input.hint.max_length",
+      "description": "Input-block hint text longer than 2000 characters must be rejected"
+    },
+    {
+      "id": "markdown-too-long",
+      "category": "length-exceeded",
+      "constraint": "markdown.text.max_length",
+      "description": "Markdown block text longer than 12000 characters must be rejected"
+    },
+    {
+      "id": "section-text-too-long",
+      "category": "length-exceeded",
+      "constraint": "section.text.max_length",
+      "description": "Section text longer than 3000 characters must be rejected"
+    },
+    {
+      "id": "section-too-many-fields",
+      "category": "length-exceeded",
+      "constraint": "section.fields.max_items",
+      "description": "A section with more than 10 fields must be rejected"
+    },
+    {
+      "id": "section-field-too-long",
+      "category": "length-exceeded",
+      "constraint": "section.fields.item_max_length",
+      "description": "A section field longer than 2000 characters must be rejected"
+    },
+    {
+      "id": "video-alt-text-too-long",
+      "category": "length-exceeded",
+      "constraint": "video.alt_text.max_length",
+      "description": "Video alternative text longer than 2000 characters must be rejected"
+    },
+    {
+      "id": "video-title-too-long",
+      "category": "length-exceeded",
+      "constraint": "video.title.max_length",
+      "description": "A video title longer than 200 characters must be rejected"
+    },
+    {
+      "id": "video-author-name-too-long",
+      "category": "length-exceeded",
+      "constraint": "video.author_name.max_length",
+      "description": "A video author name longer than 50 characters must be rejected"
+    },
+    {
+      "id": "video-description-too-long",
+      "category": "length-exceeded",
+      "constraint": "video.description.max_length",
+      "description": "A video description longer than 200 characters must be rejected"
+    },
+    {
+      "id": "video-provider-name-too-long",
+      "category": "length-exceeded",
+      "constraint": "video.provider_name.max_length",
+      "description": "A video provider name longer than 50 characters must be rejected"
+    },
+    {
+      "id": "message-channel-empty",
+      "category": "length-exceeded",
+      "constraint": "message.channel.min_length",
+      "description": "A Web API message must name a non-empty destination channel"
+    },
+    {
+      "id": "message-too-many-blocks",
+      "category": "length-exceeded",
+      "constraint": "message.blocks.max_items",
+      "description": "A message with more than 50 blocks must be rejected"
+    },
+    {
+      "id": "message-too-many-attachments",
+      "category": "length-exceeded",
+      "constraint": "message.attachments.max_items",
+      "description": "A message with more than 100 attachments must be rejected"
+    },
+    {
+      "id": "message-invalid-block-surface",
+      "category": "type-mismatch",
+      "constraint": "message.blocks.surface",
+      "description": "A message cannot contain a modal-only alert block"
+    },
+    {
+      "id": "modal-invalid-block-surface",
+      "category": "type-mismatch",
+      "constraint": "view.modal.blocks.surface",
+      "description": "A modal cannot contain a message-only markdown block"
+    },
+    {
+      "id": "home-invalid-block-surface",
+      "category": "type-mismatch",
+      "constraint": "view.home.blocks.surface",
+      "description": "An App Home tab cannot contain a modal-only alert block"
+    },
+    {
+      "id": "modal-input-requires-submit",
+      "category": "missing-required",
+      "constraint": "view.modal.submit_for_input",
+      "description": "A modal containing an input block must define submit text"
+    },
+    {
+      "id": "view-too-many-blocks",
+      "category": "length-exceeded",
+      "constraint": "view.blocks.max_items",
+      "description": "A view with more than 100 blocks must be rejected"
+    },
+    {
+      "id": "view-private-metadata-too-long",
+      "category": "length-exceeded",
+      "constraint": "view.private_metadata.max_length",
+      "description": "View private metadata longer than 3000 characters must be rejected"
+    },
+    {
+      "id": "view-callback-id-too-long",
+      "category": "length-exceeded",
+      "constraint": "view.callback_id.max_length",
+      "description": "A view callback ID longer than 255 characters must be rejected"
+    },
+    {
+      "id": "view-title-too-long",
+      "category": "length-exceeded",
+      "constraint": "view.title.max_length",
+      "description": "A modal title longer than 24 characters must be rejected"
+    },
+    {
+      "id": "view-close-too-long",
+      "category": "length-exceeded",
+      "constraint": "view.close.max_length",
+      "description": "Modal close text longer than 24 characters must be rejected"
+    },
+    {
+      "id": "view-submit-too-long",
+      "category": "length-exceeded",
+      "constraint": "view.submit.max_length",
+      "description": "Modal submit text longer than 24 characters must be rejected"
+    },
+    {
+      "id": "section-missing-content",
+      "category": "missing-required",
+      "constraint": "section.content",
+      "description": "A section with neither text nor fields must be rejected"
+    },
+    {
+      "id": "section-empty-fields",
+      "category": "missing-required",
+      "constraint": "section.fields.non_empty",
+      "description": "An empty fields array does not satisfy a section's content requirement"
+    },
+    {
+      "id": "static-select-options-and-groups",
+      "category": "mutually-exclusive",
+      "constraint": "static_select.choice_source",
+      "description": "A static select cannot define options and option groups together"
+    },
+    {
+      "id": "image-url-and-slack-file",
+      "category": "mutually-exclusive",
+      "constraint": "image.source",
+      "description": "An image element cannot define an image URL and Slack file together"
+    },
+    {
+      "id": "number-input-inverted-range",
+      "category": "out-of-range",
+      "constraint": "number_input.range",
+      "description": "A number input minimum cannot exceed its maximum"
+    },
+    {
+      "id": "context-invalid-element",
+      "category": "type-mismatch",
+      "constraint": "context.elements",
+      "description": "A context block may contain only text and image elements"
+    },
+    {
+      "id": "input-invalid-element",
+      "category": "type-mismatch",
+      "constraint": "input.element",
+      "description": "An input block must contain an input-compatible element"
+    },
+    {
+      "id": "block-id-too-long",
+      "category": "length-exceeded",
+      "constraint": "block_id.max_length",
+      "description": "A block ID longer than 255 characters must be rejected"
+    },
+    {
+      "id": "button-accessibility-label-too-long",
+      "category": "length-exceeded",
+      "constraint": "button.accessibility_label.max_length",
+      "description": "A button accessibility label longer than 75 characters must be rejected"
+    },
+    {
+      "id": "workflow-button-text-too-long",
+      "category": "length-exceeded",
+      "constraint": "workflow_button.text.max_length",
+      "description": "A workflow button with text longer than 75 characters must be rejected"
+    },
+    {
+      "id": "workflow-button-accessibility-label-too-long",
+      "category": "length-exceeded",
+      "constraint": "workflow_button.accessibility_label.max_length",
+      "description": "A workflow button accessibility label longer than 75 characters must be rejected"
+    },
+    {
+      "id": "alert-text-too-long",
+      "category": "length-exceeded",
+      "constraint": "alert.text.max_length",
+      "description": "Alert text longer than 200 characters must be rejected"
+    },
+    {
+      "id": "card-title-too-long",
+      "category": "length-exceeded",
+      "constraint": "card.title.max_length",
+      "description": "A card title longer than 150 characters must be rejected"
+    },
+    {
+      "id": "card-subtitle-too-long",
+      "category": "length-exceeded",
+      "constraint": "card.subtitle.max_length",
+      "description": "A card subtitle longer than 150 characters must be rejected"
+    },
+    {
+      "id": "card-body-too-long",
+      "category": "length-exceeded",
+      "constraint": "card.body.max_length",
+      "description": "A card body longer than 200 characters must be rejected"
+    },
+    {
+      "id": "card-too-many-actions",
+      "category": "length-exceeded",
+      "constraint": "card.actions.max_items",
+      "description": "A card with more than three actions must be rejected"
+    },
+    {
+      "id": "card-subtext-too-long",
+      "category": "length-exceeded",
+      "constraint": "card.subtext.max_length",
+      "description": "Card subtext longer than 200 characters must be rejected"
+    },
+    {
+      "id": "carousel-empty",
+      "category": "length-exceeded",
+      "constraint": "carousel.elements.min_items",
+      "description": "A carousel without cards must be rejected"
+    },
+    {
+      "id": "carousel-too-many-cards",
+      "category": "length-exceeded",
+      "constraint": "carousel.elements.max_items",
+      "description": "A carousel with more than ten cards must be rejected"
+    },
+    {
+      "id": "container-title-too-long",
+      "category": "length-exceeded",
+      "constraint": "container.title.max_length",
+      "description": "A container title longer than 150 characters must be rejected"
+    },
+    {
+      "id": "container-subtitle-too-long",
+      "category": "length-exceeded",
+      "constraint": "container.subtitle.max_length",
+      "description": "A container subtitle longer than 150 characters must be rejected"
+    },
+    {
+      "id": "container-too-many-child-blocks",
+      "category": "length-exceeded",
+      "constraint": "container.child_blocks.max_items",
+      "description": "A container with more than ten child blocks must be rejected"
+    },
+    {
+      "id": "context-actions-too-many-elements",
+      "category": "length-exceeded",
+      "constraint": "context_actions.elements.max_items",
+      "description": "A context-actions block with more than five elements must be rejected"
+    },
+    {
+      "id": "feedback-button-text-too-long",
+      "category": "length-exceeded",
+      "constraint": "feedback_button.text.max_length",
+      "description": "Feedback button text longer than 75 characters must be rejected"
+    },
+    {
+      "id": "feedback-button-value-too-long",
+      "category": "length-exceeded",
+      "constraint": "feedback_button.value.max_length",
+      "description": "A feedback button value longer than 2000 characters must be rejected"
+    },
+    {
+      "id": "feedback-button-accessibility-label-too-long",
+      "category": "length-exceeded",
+      "constraint": "feedback_button.accessibility_label.max_length",
+      "description": "A feedback button accessibility label longer than 75 characters must be rejected"
+    },
+    {
+      "id": "icon-button-too-many-visible-users",
+      "category": "length-exceeded",
+      "constraint": "icon_button.visible_to_user_ids.max_items",
+      "description": "An icon button visible to more than ten users must be rejected"
+    },
+    {
+      "id": "icon-button-value-too-long",
+      "category": "length-exceeded",
+      "constraint": "icon_button.value.max_length",
+      "description": "An icon button value longer than 2000 characters must be rejected"
+    },
+    {
+      "id": "icon-button-accessibility-label-too-long",
+      "category": "length-exceeded",
+      "constraint": "icon_button.accessibility_label.max_length",
+      "description": "An icon button accessibility label longer than 75 characters must be rejected"
+    },
+    {
+      "id": "data-table-too-few-rows",
+      "category": "length-exceeded",
+      "constraint": "data_table.rows.min_items",
+      "description": "A data table with fewer than two rows must be rejected"
+    },
+    {
+      "id": "data-table-too-many-rows",
+      "category": "length-exceeded",
+      "constraint": "data_table.rows.max_items",
+      "description": "A data table with more than 201 rows must be rejected"
+    },
+    {
+      "id": "data-table-too-few-columns",
+      "category": "length-exceeded",
+      "constraint": "data_table.columns.min_items",
+      "description": "A data table with empty rows must be rejected"
+    },
+    {
+      "id": "data-table-too-many-columns",
+      "category": "length-exceeded",
+      "constraint": "data_table.columns.max_items",
+      "description": "A data table with more than 20 columns must be rejected"
+    },
+    {
+      "id": "data-table-page-size-too-small",
+      "category": "out-of-range",
+      "constraint": "data_table.page_size.min",
+      "description": "A data table page size below one must be rejected"
+    },
+    {
+      "id": "data-table-page-size-too-large",
+      "category": "out-of-range",
+      "constraint": "data_table.page_size.max",
+      "description": "A data table page size above 100 must be rejected"
+    },
+    {
+      "id": "data-table-cell-text-empty",
+      "category": "length-exceeded",
+      "constraint": "data_table.cell_text.min_length",
+      "description": "An empty raw data-table cell must be rejected"
+    },
+    {
+      "id": "data-table-content-too-long",
+      "category": "length-exceeded",
+      "constraint": "data_table.content.max_length",
+      "description": "Data-table cell text exceeding 20000 total characters must be rejected"
+    },
+    {
+      "id": "data-visualization-title-too-long",
+      "category": "length-exceeded",
+      "constraint": "data_visualization.title.max_length",
+      "description": "A data-visualization title longer than 50 characters must be rejected"
+    },
+    {
+      "id": "pie-chart-empty",
+      "category": "length-exceeded",
+      "constraint": "data_visualization.segments.min_items",
+      "description": "A pie chart without segments must be rejected"
+    },
+    {
+      "id": "pie-chart-too-many-segments",
+      "category": "length-exceeded",
+      "constraint": "data_visualization.segments.max_items",
+      "description": "A pie chart with more than 12 segments must be rejected"
+    },
+    {
+      "id": "chart-segment-label-too-long",
+      "category": "length-exceeded",
+      "constraint": "data_visualization.segment.label.max_length",
+      "description": "A chart segment label longer than 20 characters must be rejected"
+    },
+    {
+      "id": "chart-segment-value-not-positive",
+      "category": "out-of-range",
+      "constraint": "data_visualization.segment.value.exclusive_min",
+      "description": "A non-positive pie segment value must be rejected"
+    },
+    {
+      "id": "chart-series-empty",
+      "category": "length-exceeded",
+      "constraint": "data_visualization.series.min_items",
+      "description": "An axis-based chart without a series must be rejected"
+    },
+    {
+      "id": "chart-duplicate-point-labels",
+      "category": "invalid-usage",
+      "constraint": "data_visualization.series.axis_category_coverage",
+      "description": "A chart series must contain exactly one point for each axis category"
+    },
+    {
+      "id": "chart-too-many-series",
+      "category": "length-exceeded",
+      "constraint": "data_visualization.series.max_items",
+      "description": "An axis-based chart with more than 12 series must be rejected"
+    },
+    {
+      "id": "data-series-name-too-long",
+      "category": "length-exceeded",
+      "constraint": "data_visualization.series_name.max_length",
+      "description": "A data-series name longer than 20 characters must be rejected"
+    },
+    {
+      "id": "data-series-empty",
+      "category": "length-exceeded",
+      "constraint": "data_visualization.data.min_items",
+      "description": "A data series without points must be rejected"
+    },
+    {
+      "id": "data-series-too-many-points",
+      "category": "length-exceeded",
+      "constraint": "data_visualization.data.max_items",
+      "description": "A data series with more than 20 points must be rejected"
+    },
+    {
+      "id": "data-point-label-too-long",
+      "category": "length-exceeded",
+      "constraint": "data_visualization.point_label.max_length",
+      "description": "A data-point label longer than 20 characters must be rejected"
+    },
+    {
+      "id": "axis-categories-empty",
+      "category": "length-exceeded",
+      "constraint": "data_visualization.categories.min_items",
+      "description": "An axis configuration without categories must be rejected"
+    },
+    {
+      "id": "axis-too-many-categories",
+      "category": "length-exceeded",
+      "constraint": "data_visualization.categories.max_items",
+      "description": "An axis configuration with more than 20 categories must be rejected"
+    },
+    {
+      "id": "axis-category-label-too-long",
+      "category": "length-exceeded",
+      "constraint": "data_visualization.category_label.max_length",
+      "description": "An axis category label longer than 20 characters must be rejected"
+    },
+    {
+      "id": "axis-label-too-long",
+      "category": "length-exceeded",
+      "constraint": "data_visualization.axis_label.max_length",
+      "description": "An axis label longer than 50 characters must be rejected"
+    },
+    {
+      "id": "plain-text-input-min-length-negative",
+      "category": "out-of-range",
+      "constraint": "plain_text_input.min_length.min",
+      "description": "A plain-text input min_length below 0 must be rejected"
+    },
+    {
+      "id": "plain-text-input-min-length-too-large",
+      "category": "out-of-range",
+      "constraint": "plain_text_input.min_length.max",
+      "description": "A plain-text input min_length above 3000 must be rejected"
+    },
+    {
+      "id": "plain-text-input-max-length-too-small",
+      "category": "out-of-range",
+      "constraint": "plain_text_input.max_length.min",
+      "description": "A plain-text input max_length below 1 must be rejected"
+    },
+    {
+      "id": "rich-text-input-min-lines-too-small",
+      "category": "out-of-range",
+      "constraint": "rich_text_input.min_lines.min",
+      "description": "A rich text input min_lines below 1 must be rejected"
+    },
+    {
+      "id": "rich-text-input-min-lines-too-large",
+      "category": "out-of-range",
+      "constraint": "rich_text_input.min_lines.max",
+      "description": "A rich text input min_lines above 100 must be rejected"
+    },
+    {
+      "id": "rich-text-input-max-lines-too-small",
+      "category": "out-of-range",
+      "constraint": "rich_text_input.max_lines.min",
+      "description": "A rich text input max_lines below 1 must be rejected"
+    },
+    {
+      "id": "rich-text-input-max-lines-too-large",
+      "category": "out-of-range",
+      "constraint": "rich_text_input.max_lines.max",
+      "description": "A rich text input max_lines above 100 must be rejected"
+    },
+    {
+      "id": "multi-select-max-selected-items-too-small",
+      "category": "out-of-range",
+      "constraint": "multi_select.max_selected_items.min",
+      "description": "A multi-select max_selected_items below 1 must be rejected"
+    },
+    {
+      "id": "conversation-filter-include-empty",
+      "category": "length-exceeded",
+      "constraint": "conversation_filter.include.min_items",
+      "description": "A conversation filter with an empty include list must be rejected"
+    },
+    {
+      "id": "conversation-filter-unknown-include",
+      "category": "type-mismatch",
+      "constraint": "conversation_filter.include.values",
+      "description": "A conversation filter including a type other than im, mpim, private, or public must be rejected"
+    },
+    {
+      "id": "workflow-button-missing-action-id",
+      "category": "missing-required",
+      "constraint": "workflow_button.action_id.required",
+      "description": "A workflow button without an action_id must be rejected"
+    },
+    {
+      "id": "number-input-missing-decimal-flag",
+      "category": "missing-required",
+      "constraint": "number_input.is_decimal_allowed.required",
+      "description": "A number input without is_decimal_allowed must be rejected"
+    },
+    {
+      "id": "slack-icon-missing-name",
+      "category": "missing-required",
+      "constraint": "slack_icon.name.required",
+      "description": "A Slack icon without a name must be rejected"
+    },
+    {
+      "id": "slack-file-id-malformed",
+      "category": "type-mismatch",
+      "constraint": "slack_file.id.pattern",
+      "description": "A Slack file ID that does not match ^F[A-Z0-9]{8,}$ must be rejected"
+    },
+    {
+      "id": "image-element-url-too-long",
+      "category": "length-exceeded",
+      "constraint": "image_element.image_url.max_length",
+      "description": "An image element URL longer than 3000 characters must be rejected"
+    },
+    {
+      "id": "image-element-alt-text-too-long",
+      "category": "length-exceeded",
+      "constraint": "image_element.alt_text.max_length",
+      "description": "Image element alternative text longer than 2000 characters must be rejected"
+    },
+    {
+      "id": "image-block-title-too-long",
+      "category": "length-exceeded",
+      "constraint": "image.title.max_length",
+      "description": "An image block title longer than 2000 characters must be rejected"
+    },
+    {
+      "id": "image-block-url-and-slack-file",
+      "category": "mutually-exclusive",
+      "constraint": "image_block.source",
+      "description": "An image block with both image_url and slack_file must be rejected"
+    },
+    {
+      "id": "image-block-missing-source",
+      "category": "missing-required",
+      "constraint": "image_block.source.required",
+      "description": "An image block with neither image_url nor slack_file must be rejected"
+    },
+    {
+      "id": "container-no-child-blocks",
+      "category": "length-exceeded",
+      "constraint": "container.child_blocks.min_items",
+      "description": "A container with no child blocks must be rejected"
+    },
+    {
+      "id": "table-too-many-column-settings",
+      "category": "length-exceeded",
+      "constraint": "table.column_settings.max_items",
+      "description": "A table with more than 20 column settings must be rejected"
+    },
+    {
+      "id": "data-table-row-header-index-negative",
+      "category": "out-of-range",
+      "constraint": "data_table.row_header_column_index.min",
+      "description": "A data table row_header_column_index below 0 must be rejected"
+    },
+    {
+      "id": "data-table-total-content-too-long",
+      "category": "length-exceeded",
+      "constraint": "data_table.total_content.max_length",
+      "description": "Data table cell text exceeding 20000 characters across a message must be rejected"
+    },
+    {
+      "id": "markdown-total-too-long",
+      "category": "length-exceeded",
+      "constraint": "markdown.total_text.max_length",
+      "description": "Markdown block text exceeding 12000 characters across a payload must be rejected"
+    },
+    {
+      "id": "plan-missing-tasks",
+      "category": "missing-required",
+      "constraint": "plan.tasks.required",
+      "description": "A plan block without tasks must be rejected"
+    },
+    {
+      "id": "plan-too-many-tasks",
+      "category": "length-exceeded",
+      "constraint": "plan.tasks.max_items",
+      "description": "A plan block with more than 50 tasks must be rejected"
+    },
+    {
+      "id": "plan-duplicate-task-ids",
+      "category": "invalid-usage",
+      "constraint": "plan.tasks.unique_task_id",
+      "description": "A plan block whose tasks share a task_id must be rejected"
+    },
+    {
+      "id": "task-card-missing-status",
+      "category": "missing-required",
+      "constraint": "task_card.status.required",
+      "description": "A task card without a status must be rejected"
+    },
+    {
+      "id": "task-card-pending-status",
+      "category": "type-mismatch",
+      "constraint": "task_card.status.standalone",
+      "description": "A standalone task card with pending status must be rejected; pending is only valid for plan tasks"
+    },
+    {
+      "id": "rich-text-list-indent-negative",
+      "category": "out-of-range",
+      "constraint": "rich_text_list.indent.min",
+      "description": "A rich text list indent below 0 must be rejected"
+    },
+    {
+      "id": "rich-text-list-indent-too-large",
+      "category": "out-of-range",
+      "constraint": "rich_text_list.indent.max",
+      "description": "A rich text list indent above 8 must be rejected"
+    },
+    {
+      "id": "rich-text-list-offset-negative",
+      "category": "out-of-range",
+      "constraint": "rich_text_list.offset.min",
+      "description": "A rich text list offset below 0 must be rejected"
+    },
+    {
+      "id": "rich-text-list-border-negative",
+      "category": "out-of-range",
+      "constraint": "rich_text_list.border.min",
+      "description": "A rich text list border below 0 must be rejected"
+    },
+    {
+      "id": "rich-text-list-border-too-large",
+      "category": "out-of-range",
+      "constraint": "rich_text_list.border.max",
+      "description": "A rich text list border above 1 must be rejected"
+    },
+    {
+      "id": "rich-text-quote-border-negative",
+      "category": "out-of-range",
+      "constraint": "rich_text_quote.border.min",
+      "description": "A rich text quote border below 0 must be rejected"
+    },
+    {
+      "id": "rich-text-quote-border-too-large",
+      "category": "out-of-range",
+      "constraint": "rich_text_quote.border.max",
+      "description": "A rich text quote border above 1 must be rejected"
+    },
+    {
+      "id": "rich-text-preformatted-border-negative",
+      "category": "out-of-range",
+      "constraint": "rich_text_preformatted.border.min",
+      "description": "A preformatted rich text border below 0 must be rejected"
+    },
+    {
+      "id": "rich-text-preformatted-border-too-large",
+      "category": "out-of-range",
+      "constraint": "rich_text_preformatted.border.max",
+      "description": "A preformatted rich text border above 1 must be rejected"
+    },
+    {
+      "id": "video-thumbnail-url-too-long",
+      "category": "length-exceeded",
+      "constraint": "video.thumbnail_url.max_length",
+      "description": "A video thumbnail URL longer than 3000 characters must be rejected"
+    },
+    {
+      "id": "video-url-too-long",
+      "category": "length-exceeded",
+      "constraint": "video.video_url.max_length",
+      "description": "A video URL longer than 3000 characters must be rejected"
+    },
+    {
+      "id": "video-title-url-too-long",
+      "category": "length-exceeded",
+      "constraint": "video.title_url.max_length",
+      "description": "A video title URL longer than 2000 characters must be rejected"
+    },
+    {
+      "id": "video-provider-icon-url-too-long",
+      "category": "length-exceeded",
+      "constraint": "video.provider_icon_url.max_length",
+      "description": "A video provider icon URL longer than 2000 characters must be rejected"
+    },
+    {
+      "id": "view-external-id-too-long",
+      "category": "length-exceeded",
+      "constraint": "view.external_id.max_length",
+      "description": "A view external_id longer than 255 characters must be rejected"
+    },
+    {
+      "id": "attachment-missing-blocks",
+      "category": "missing-required",
+      "constraint": "attachment.blocks.required",
+      "description": "An attachment without blocks must be rejected"
+    },
+    {
+      "id": "number-bound-inverted-large-integer",
+      "category": "out-of-range",
+      "constraint": "number_input.bounds.inverted_large_integer",
+      "description": "Number bound decimal contract rejects inverted-large-integer"
+    },
+    {
+      "id": "number-bound-inverted-long-fraction",
+      "category": "out-of-range",
+      "constraint": "number_input.bounds.inverted_long_fraction",
+      "description": "Number bound decimal contract rejects inverted-long-fraction"
+    },
+    {
+      "id": "number-bound-hex",
+      "category": "type-mismatch",
+      "constraint": "number_input.bounds.hex",
+      "description": "Number bound decimal contract rejects hex"
+    },
+    {
+      "id": "number-bound-underscore",
+      "category": "type-mismatch",
+      "constraint": "number_input.bounds.underscore",
+      "description": "Number bound decimal contract rejects underscore"
+    },
+    {
+      "id": "number-bound-leading-space",
+      "category": "type-mismatch",
+      "constraint": "number_input.bounds.leading_space",
+      "description": "Number bound decimal contract rejects leading-space"
+    },
+    {
+      "id": "number-bound-trailing-space",
+      "category": "type-mismatch",
+      "constraint": "number_input.bounds.trailing_space",
+      "description": "Number bound decimal contract rejects trailing-space"
+    },
+    {
+      "id": "number-bound-nan",
+      "category": "type-mismatch",
+      "constraint": "number_input.bounds.nan",
+      "description": "Number bound decimal contract rejects nan"
+    },
+    {
+      "id": "number-bound-infinity",
+      "category": "type-mismatch",
+      "constraint": "number_input.bounds.infinity",
+      "description": "Number bound decimal contract rejects infinity"
+    },
+    {
+      "id": "external-multi-focus-false",
+      "category": "invalid-usage",
+      "constraint": "external_multi_select.focus_on_load.unsupported.false",
+      "description": "External multi-select must omit focus_on_load, including explicit false"
+    },
+    {
+      "id": "external-multi-focus-true",
+      "category": "invalid-usage",
+      "constraint": "external_multi_select.focus_on_load.unsupported.true",
+      "description": "External multi-select must omit focus_on_load, including explicit true"
+    },
+    {
+      "id": "message-too-many-charts",
+      "category": "length-exceeded",
+      "constraint": "message.data_visualizations.max_items",
+      "description": "A message with three data visualization blocks must be rejected after native composition"
+    },
+    {
+      "id": "plain-text-input-inverted-range",
+      "category": "out-of-range",
+      "constraint": "plain_text_input.ordered_bounds",
+      "description": "Supplied minimum input length cannot exceed the supplied maximum"
+    },
+    {
+      "id": "external-query-negative",
+      "category": "out-of-range",
+      "constraint": "select.min_query_length.min",
+      "description": "External query negative"
+    },
+    {
+      "id": "external-multi-query-negative",
+      "category": "out-of-range",
+      "constraint": "multi_external_select.nonnegative_query",
+      "description": "External multi query negative"
+    },
+    {
+      "id": "date-picker-invalid-format",
+      "category": "type-mismatch",
+      "constraint": "date_picker.initial_date.format",
+      "description": "Date picker invalid format"
+    },
+    {
+      "id": "time-picker-invalid-format",
+      "category": "type-mismatch",
+      "constraint": "time_picker.initial_time.format",
+      "description": "Time picker invalid format"
+    },
+    {
+      "id": "time-picker-invalid-range",
+      "category": "type-mismatch",
+      "constraint": "time_picker.initial_time.range",
+      "description": "Time picker invalid range"
+    },
+    {
+      "id": "static-select-empty-options",
+      "category": "length-exceeded",
+      "constraint": "select.options.min_items",
+      "description": "Static select empty options"
+    },
+    {
+      "id": "static-multi-select-empty-options",
+      "category": "length-exceeded",
+      "constraint": "multi_static_select.options.nonempty",
+      "description": "Static multi select empty options"
+    },
+    {
+      "id": "static-select-empty-groups",
+      "category": "length-exceeded",
+      "constraint": "select.option_groups.min_items",
+      "description": "Static select empty groups"
+    },
+    {
+      "id": "static-multi-select-empty-groups",
+      "category": "length-exceeded",
+      "constraint": "multi_static_select.option_groups.nonempty",
+      "description": "Static multi select empty groups"
+    },
+    {
+      "id": "button-agent-prompt-too-long",
+      "category": "length-exceeded",
+      "constraint": "button.agent_prompt.max_length",
+      "description": "A button prompt longer than 4000 Unicode code points must be rejected"
+    },
+    {
+      "id": "button-agent-prompt-display-without-prompt",
+      "category": "missing-required",
+      "constraint": "button.agent_prompt.required_with_display",
+      "description": "A supplied prompt display, including an empty string, requires an agent prompt"
+    }
+  ]
+}
diff --git a/test/spec/fixtures/valid/attachments/attachment_multi_block.json b/test/spec/fixtures/valid/attachments/attachment_multi_block.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/attachments/attachment_multi_block.json
@@ -0,0 +1,21 @@
+{
+    "blocks": [
+        {
+            "type": "section",
+            "block_id": "fake_block_id_0",
+            "text": {
+                "type": "mrkdwn",
+                "text": "I like pretty colours"
+            }
+        },
+        {
+            "type": "section",
+            "block_id": "fake_block_id_1",
+            "text": {
+                "type": "mrkdwn",
+                "text": "I don't like pretty colours"
+            }
+        }
+    ],
+    "color": "#8800ff"
+}
diff --git a/test/spec/fixtures/valid/attachments/attachment_simple.json b/test/spec/fixtures/valid/attachments/attachment_simple.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/attachments/attachment_simple.json
@@ -0,0 +1,14 @@
+{
+    "blocks": [
+        {
+            "type": "section",
+            "block_id": "fake_block_id",
+            "text": {
+                "type": "mrkdwn",
+                "text": "I like pretty colours"
+            }
+        }
+    ],
+    "color": "#000000",
+    "fallback": "Colours preference"
+}
diff --git a/test/spec/fixtures/valid/blocks/actions_block_checkboxes.json b/test/spec/fixtures/valid/blocks/actions_block_checkboxes.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/actions_block_checkboxes.json
@@ -0,0 +1,45 @@
+{
+    "type": "actions",
+    "block_id": "fake_block_id",
+    "elements": [
+        {
+            "type": "checkboxes",
+            "action_id": "actionId-0",
+            "options": [
+                {
+                    "text": {
+                        "type": "mrkdwn",
+                        "text": "*a*"
+                    },
+                    "value": "a",
+                    "description": {
+                        "type": "plain_text",
+                        "text": "*a*"
+                    }
+                },
+                {
+                    "text": {
+                        "type": "mrkdwn",
+                        "text": "*b*"
+                    },
+                    "value": "b",
+                    "description": {
+                        "type": "plain_text",
+                        "text": "*b*"
+                    }
+                },
+                {
+                    "text": {
+                        "type": "mrkdwn",
+                        "text": "*c*"
+                    },
+                    "value": "c",
+                    "description": {
+                        "type": "plain_text",
+                        "text": "*c*"
+                    }
+                }
+            ]
+        }
+    ]
+}
diff --git a/test/spec/fixtures/valid/blocks/alert_block.json b/test/spec/fixtures/valid/blocks/alert_block.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/alert_block.json
@@ -0,0 +1,9 @@
+{
+  "type": "alert",
+  "block_id": "fake_block_id",
+  "text": {
+    "type": "mrkdwn",
+    "text": "The work is mysterious and important."
+  },
+  "level": "info"
+}
diff --git a/test/spec/fixtures/valid/blocks/card_block.json b/test/spec/fixtures/valid/blocks/card_block.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/card_block.json
@@ -0,0 +1,39 @@
+{
+  "type": "card",
+  "block_id": "fake_block_id",
+  "hero_image": {
+    "type": "image",
+    "image_url": "https://picsum.photos/400/300",
+    "alt_text": "Sample hero image"
+  },
+  "title": {
+    "type": "mrkdwn",
+    "text": "Lumon Industries"
+  },
+  "subtitle": {
+    "type": "mrkdwn",
+    "text": "Committed to work-life balance"
+  },
+  "body": {
+    "type": "mrkdwn",
+    "text": "Please enjoy each card equally."
+  },
+  "actions": [
+    {
+      "type": "button",
+      "text": {
+        "type": "plain_text",
+        "text": "Action Button"
+      },
+      "action_id": "button_action"
+    }
+  ],
+  "slack_icon": {
+    "type": "icon",
+    "name": "bot"
+  },
+  "subtext": {
+    "type": "mrkdwn",
+    "text": "A card assembled by slackblocks."
+  }
+}
diff --git a/test/spec/fixtures/valid/blocks/carousel_block.json b/test/spec/fixtures/valid/blocks/carousel_block.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/carousel_block.json
@@ -0,0 +1,22 @@
+{
+  "type": "carousel",
+  "block_id": "fake_block_id",
+  "elements": [
+    {
+      "type": "card",
+      "block_id": "card_1",
+      "title": {
+        "type": "mrkdwn",
+        "text": "First result"
+      }
+    },
+    {
+      "type": "card",
+      "block_id": "card_2",
+      "title": {
+        "type": "mrkdwn",
+        "text": "Second result"
+      }
+    }
+  ]
+}
diff --git a/test/spec/fixtures/valid/blocks/container_block.json b/test/spec/fixtures/valid/blocks/container_block.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/container_block.json
@@ -0,0 +1,26 @@
+{
+  "type": "container",
+  "block_id": "fake_block_id",
+  "title": {
+    "type": "plain_text",
+    "text": "Deployment summary"
+  },
+  "subtitle": {
+    "type": "mrkdwn",
+    "text": "Production is healthy"
+  },
+  "child_blocks": [
+    {
+      "type": "section",
+      "block_id": "child_1",
+      "text": {
+        "type": "mrkdwn",
+        "text": "All systems operational."
+      }
+    }
+  ],
+  "width": "standard",
+  "is_collapsible": false,
+  "default_collapsed": false,
+  "has_header_divider": true
+}
diff --git a/test/spec/fixtures/valid/blocks/context_actions_feedback_buttons.json b/test/spec/fixtures/valid/blocks/context_actions_feedback_buttons.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/context_actions_feedback_buttons.json
@@ -0,0 +1,26 @@
+{
+  "type": "context_actions",
+  "block_id": "fake_block_id",
+  "elements": [
+    {
+      "type": "feedback_buttons",
+      "positive_button": {
+        "text": {
+          "type": "plain_text",
+          "text": "Good"
+        },
+        "value": "positive_feedback",
+        "accessibility_label": "Mark this response as good"
+      },
+      "negative_button": {
+        "text": {
+          "type": "plain_text",
+          "text": "Bad"
+        },
+        "value": "negative_feedback",
+        "accessibility_label": "Mark this response as bad"
+      },
+      "action_id": "feedback_buttons_1"
+    }
+  ]
+}
diff --git a/test/spec/fixtures/valid/blocks/context_actions_icon_button.json b/test/spec/fixtures/valid/blocks/context_actions_icon_button.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/context_actions_icon_button.json
@@ -0,0 +1,16 @@
+{
+  "type": "context_actions",
+  "block_id": "fake_block_id",
+  "elements": [
+    {
+      "type": "icon_button",
+      "icon": "trash",
+      "text": {
+        "type": "plain_text",
+        "text": "Delete"
+      },
+      "action_id": "delete_button",
+      "value": "delete_item"
+    }
+  ]
+}
diff --git a/test/spec/fixtures/valid/blocks/context_block_text_only.json b/test/spec/fixtures/valid/blocks/context_block_text_only.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/context_block_text_only.json
@@ -0,0 +1,10 @@
+{
+    "type": "context",
+    "block_id": "fake_block_id",
+    "elements": [
+        {
+            "type": "mrkdwn",
+            "text": "Hello, world!"
+        }
+    ]
+}
diff --git a/test/spec/fixtures/valid/blocks/data_table_block.json b/test/spec/fixtures/valid/blocks/data_table_block.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/data_table_block.json
@@ -0,0 +1,17 @@
+{
+  "type": "data_table",
+  "block_id": "fake_block_id",
+  "rows": [
+    [
+      { "type": "raw_text", "text": "Name" },
+      { "type": "raw_text", "text": "Score" }
+    ],
+    [
+      { "type": "raw_text", "text": "Alice" },
+      { "type": "raw_number", "value": 42, "text": "42" }
+    ]
+  ],
+  "page_size": 5,
+  "caption": "Team scores",
+  "row_header_column_index": 0
+}
diff --git a/test/spec/fixtures/valid/blocks/data_visualization_area.json b/test/spec/fixtures/valid/blocks/data_visualization_area.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/data_visualization_area.json
@@ -0,0 +1,22 @@
+{
+  "type": "data_visualization",
+  "block_id": "fake_block_id",
+  "title": "Daily Active Users",
+  "chart": {
+    "type": "area",
+    "series": [
+      {
+        "name": "Free Tier",
+        "data": [
+          { "label": "Mon", "value": 12000 },
+          { "label": "Tue", "value": 13500 }
+        ]
+      }
+    ],
+    "axis_config": {
+      "categories": ["Mon", "Tue"],
+      "x_label": "Day",
+      "y_label": "Users"
+    }
+  }
+}
diff --git a/test/spec/fixtures/valid/blocks/data_visualization_bar.json b/test/spec/fixtures/valid/blocks/data_visualization_bar.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/data_visualization_bar.json
@@ -0,0 +1,22 @@
+{
+  "type": "data_visualization",
+  "block_id": "fake_block_id",
+  "title": "Pies by Tastiness",
+  "chart": {
+    "type": "bar",
+    "series": [
+      {
+        "name": "Pies",
+        "data": [
+          { "label": "Pumpkin", "value": 70 },
+          { "label": "Blueberry", "value": 90 }
+        ]
+      }
+    ],
+    "axis_config": {
+      "categories": ["Pumpkin", "Blueberry"],
+      "x_label": "Pies",
+      "y_label": "Tastiness"
+    }
+  }
+}
diff --git a/test/spec/fixtures/valid/blocks/data_visualization_line.json b/test/spec/fixtures/valid/blocks/data_visualization_line.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/data_visualization_line.json
@@ -0,0 +1,29 @@
+{
+  "type": "data_visualization",
+  "block_id": "fake_block_id",
+  "title": "Weekly Paper Sales",
+  "chart": {
+    "type": "line",
+    "series": [
+      {
+        "name": "Website",
+        "data": [
+          { "label": "Week 1", "value": 32000 },
+          { "label": "Week 2", "value": 35000 }
+        ]
+      },
+      {
+        "name": "In-store",
+        "data": [
+          { "label": "Week 1", "value": 28000 },
+          { "label": "Week 2", "value": 31000 }
+        ]
+      }
+    ],
+    "axis_config": {
+      "categories": ["Week 1", "Week 2"],
+      "x_label": "Week",
+      "y_label": "Paper Sales (USD)"
+    }
+  }
+}
diff --git a/test/spec/fixtures/valid/blocks/data_visualization_pie.json b/test/spec/fixtures/valid/blocks/data_visualization_pie.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/data_visualization_pie.json
@@ -0,0 +1,14 @@
+{
+  "type": "data_visualization",
+  "block_id": "fake_block_id",
+  "title": "My Favorite Candy Bars",
+  "chart": {
+    "type": "pie",
+    "segments": [
+      { "label": "Kit Kat", "value": 45 },
+      { "label": "Twix", "value": 28 },
+      { "label": "Crunch", "value": 18 },
+      { "label": "Milky Way", "value": 9 }
+    ]
+  }
+}
diff --git a/test/spec/fixtures/valid/blocks/divider_block_only.json b/test/spec/fixtures/valid/blocks/divider_block_only.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/divider_block_only.json
@@ -0,0 +1,4 @@
+{
+    "type": "divider",
+    "block_id": "fake_block_id"
+}
diff --git a/test/spec/fixtures/valid/blocks/file_block_only.json b/test/spec/fixtures/valid/blocks/file_block_only.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/file_block_only.json
@@ -0,0 +1,6 @@
+{
+    "type": "file",
+    "external_id": "external_id",
+    "source": "remote",
+    "block_id": "fake_block_id"
+}
diff --git a/test/spec/fixtures/valid/blocks/header_block_emoji_at_limit.json b/test/spec/fixtures/valid/blocks/header_block_emoji_at_limit.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/header_block_emoji_at_limit.json
@@ -0,0 +1,8 @@
+{
+    "type": "header",
+    "block_id": "fake_block_id",
+    "text": {
+        "type": "plain_text",
+        "text": "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀"
+    }
+}
diff --git a/test/spec/fixtures/valid/blocks/header_block_only.json b/test/spec/fixtures/valid/blocks/header_block_only.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/header_block_only.json
@@ -0,0 +1,8 @@
+{
+    "type": "header",
+    "block_id": "fake_block_id",
+    "text": {
+        "type": "plain_text",
+        "text": "AloHa!"
+    }
+}
diff --git a/test/spec/fixtures/valid/blocks/header_block_text_at_limit.json b/test/spec/fixtures/valid/blocks/header_block_text_at_limit.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/header_block_text_at_limit.json
@@ -0,0 +1,8 @@
+{
+    "type": "header",
+    "block_id": "fake_block_id",
+    "text": {
+        "type": "plain_text",
+        "text": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+    }
+}
diff --git a/test/spec/fixtures/valid/blocks/image_block_only.json b/test/spec/fixtures/valid/blocks/image_block_only.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/image_block_only.json
@@ -0,0 +1,10 @@
+{
+    "type": "image",
+    "block_id": "fake_block_id",
+    "image_url": "https://api.slack.com/img/blocks/bkb_template_images/beagle.png",
+    "alt_text": "image1",
+    "title": {
+        "type": "plain_text",
+        "text": "image1"
+    }
+}
diff --git a/test/spec/fixtures/valid/blocks/image_block_slack_file.json b/test/spec/fixtures/valid/blocks/image_block_slack_file.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/image_block_slack_file.json
@@ -0,0 +1,8 @@
+{
+    "type": "image",
+    "block_id": "fake_block_id",
+    "slack_file": {
+        "url": "https://files.slack.com/files-pri/T0123456-F0123ABC456/kitten.png"
+    },
+    "alt_text": "An incredibly cute kitten."
+}
diff --git a/test/spec/fixtures/valid/blocks/input_block_only.json b/test/spec/fixtures/valid/blocks/input_block_only.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/input_block_only.json
@@ -0,0 +1,19 @@
+{
+    "type": "input",
+    "block_id": "fake_block_id",
+    "label": {
+        "type": "plain_text",
+        "text": "Label",
+        "emoji": true
+    },
+    "element": {
+        "type": "plain_text_input",
+        "action_id": "action"
+    },
+    "hint": {
+        "type": "plain_text",
+        "text": "Hint",
+        "emoji": true
+    },
+    "optional": true
+}
diff --git a/test/spec/fixtures/valid/blocks/markdown_block_basic.json b/test/spec/fixtures/valid/blocks/markdown_block_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/markdown_block_basic.json
@@ -0,0 +1,5 @@
+{
+    "type": "markdown",
+    "block_id": "fake_block_id",
+    "text": "**Hello**, _world_!"
+}
diff --git a/test/spec/fixtures/valid/blocks/markdown_empty.json b/test/spec/fixtures/valid/blocks/markdown_empty.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/markdown_empty.json
@@ -0,0 +1,5 @@
+{
+    "type": "markdown",
+    "block_id": "fake_block_id",
+    "text": ""
+}
diff --git a/test/spec/fixtures/valid/blocks/plan_block.json b/test/spec/fixtures/valid/blocks/plan_block.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/plan_block.json
@@ -0,0 +1,32 @@
+{
+  "type": "plan",
+  "block_id": "fake_block_id",
+  "title": "Thinking completed",
+  "tasks": [
+    {
+      "task_id": "call_001",
+      "title": "Fetched user profile information",
+      "status": "complete",
+      "output": {
+        "type": "rich_text",
+        "block_id": "plan_output",
+        "elements": [
+          {
+            "type": "rich_text_section",
+            "elements": [
+              {
+                "type": "text",
+                "text": "Profile data loaded"
+              }
+            ]
+          }
+        ]
+      }
+    },
+    {
+      "task_id": "call_002",
+      "title": "Checked user permissions",
+      "status": "pending"
+    }
+  ]
+}
diff --git a/test/spec/fixtures/valid/blocks/rich_text_block_basic.json b/test/spec/fixtures/valid/blocks/rich_text_block_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/rich_text_block_basic.json
@@ -0,0 +1,32 @@
+{
+    "type": "rich_text",
+    "block_id": "fake_block_id",
+    "elements": [
+        {
+            "type": "rich_text_section",
+            "elements": [
+                {
+                    "type": "text",
+                    "text": "You 'bout to witness hip-hop in its most purest",
+                    "style": {
+                        "bold": true
+                    }
+                },
+                {
+                    "type": "text",
+                    "text": "Most rawest form, flow almost flawless",
+                    "style": {
+                        "strike": true
+                    }
+                },
+                {
+                    "type": "text",
+                    "text": "Most hardest, most honest known artist",
+                    "style": {
+                        "italic": true
+                    }
+                }
+            ]
+        }
+    ]
+}
diff --git a/test/spec/fixtures/valid/blocks/section_block_both_text_and_fields.json b/test/spec/fixtures/valid/blocks/section_block_both_text_and_fields.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/section_block_both_text_and_fields.json
@@ -0,0 +1,19 @@
+{
+    "type": "section",
+    "block_id": "fake_block_id",
+    "text": {
+        "type": "mrkdwn",
+        "text": "Hello"
+    },
+    "fields": [
+        {
+            "type": "mrkdwn",
+            "text": "Are you"
+        },
+        {
+            "type": "plain_text",
+            "text": "There?",
+            "emoji": true
+        }
+    ]
+}
diff --git a/test/spec/fixtures/valid/blocks/section_block_empty_text_field_value.json b/test/spec/fixtures/valid/blocks/section_block_empty_text_field_value.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/section_block_empty_text_field_value.json
@@ -0,0 +1,15 @@
+{
+    "type": "section",
+    "block_id": "fake_block_id",
+    "fields": [
+        {
+            "type": "mrkdwn",
+            "text": "Highly"
+        },
+        {
+            "type": "plain_text",
+            "text": "Strung",
+            "emoji": true
+        }
+    ]
+}
diff --git a/test/spec/fixtures/valid/blocks/section_block_fields.json b/test/spec/fixtures/valid/blocks/section_block_fields.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/section_block_fields.json
@@ -0,0 +1,18 @@
+{
+    "type": "section",
+    "block_id": "fake_block_id",
+    "text": {
+        "type": "mrkdwn",
+        "text": "Test:"
+    },
+    "fields": [
+        {
+            "type": "plain_text",
+            "text": "foo"
+        },
+        {
+            "type": "mrkdwn",
+            "text": "bar"
+        }
+    ]
+}
diff --git a/test/spec/fixtures/valid/blocks/section_block_single_field_value_coercion.json b/test/spec/fixtures/valid/blocks/section_block_single_field_value_coercion.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/section_block_single_field_value_coercion.json
@@ -0,0 +1,10 @@
+{
+    "type": "section",
+    "block_id": "fake_block_id",
+    "fields": [
+        {
+            "type": "mrkdwn",
+            "text": "Lowly"
+        }
+    ]
+}
diff --git a/test/spec/fixtures/valid/blocks/section_block_text_only.json b/test/spec/fixtures/valid/blocks/section_block_text_only.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/section_block_text_only.json
@@ -0,0 +1,8 @@
+{
+    "type": "section",
+    "block_id": "fake_block_id",
+    "text": {
+        "type": "mrkdwn",
+        "text": "Hello, world!"
+    }
+}
diff --git a/test/spec/fixtures/valid/blocks/table_block.json b/test/spec/fixtures/valid/blocks/table_block.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/table_block.json
@@ -0,0 +1,66 @@
+{
+    "type": "table",
+    "block_id": "fake_block_id",
+    "rows": [
+        [
+            {
+                "type": "raw_text",
+                "text": "Header A"
+            },
+            {
+                "type": "raw_text",
+                "text": "Header B"
+            }
+        ],
+        [
+            {
+                "type": "raw_text",
+                "text": "Data 1A"
+            },
+            {
+                "type": "rich_text",
+                "elements": [
+                    {
+                        "type": "rich_text_section",
+                        "elements": [
+                            {
+                                "type": "link",
+                                "url": "https://slack.com",
+                                "text": "Data 1B"
+                            }
+                        ]
+                    }
+                ]
+            }
+        ],
+        [
+            {
+                "type": "raw_text",
+                "text": "Data 2A"
+            },
+            {
+                "type": "rich_text",
+                "elements": [
+                    {
+                        "type": "rich_text_section",
+                        "elements": [
+                            {
+                                "type": "link",
+                                "url": "https://slack.com",
+                                "text": "Data 2B"
+                            }
+                        ]
+                    }
+                ]
+            }
+        ]
+    ],
+    "column_settings": [
+        {
+            "is_wrapped": true
+        },
+        {
+            "align": "right"
+        }
+    ]
+}
diff --git a/test/spec/fixtures/valid/blocks/table_ragged_rows.json b/test/spec/fixtures/valid/blocks/table_ragged_rows.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/table_ragged_rows.json
@@ -0,0 +1,22 @@
+{
+    "type": "table",
+    "block_id": "fake_block_id",
+    "rows": [
+        [
+            {
+                "type": "raw_text",
+                "text": "Header A"
+            },
+            {
+                "type": "raw_text",
+                "text": "Header B"
+            }
+        ],
+        [
+            {
+                "type": "raw_text",
+                "text": "Only one cell"
+            }
+        ]
+    ]
+}
diff --git a/test/spec/fixtures/valid/blocks/task_card_block.json b/test/spec/fixtures/valid/blocks/task_card_block.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/task_card_block.json
@@ -0,0 +1,34 @@
+{
+  "type": "task_card",
+  "block_id": "fake_block_id",
+  "task_id": "task_1",
+  "title": "Fetching weather data",
+  "output": {
+    "type": "rich_text",
+    "block_id": "task_output",
+    "elements": [
+      {
+        "type": "rich_text_section",
+        "elements": [
+          {
+            "type": "text",
+            "text": "Found weather data for Chicago from 2 sources"
+          }
+        ]
+      }
+    ]
+  },
+  "sources": [
+    {
+      "type": "url",
+      "url": "https://weather.com/",
+      "text": "weather.com"
+    },
+    {
+      "type": "url",
+      "url": "https://www.accuweather.com/",
+      "text": "accuweather.com"
+    }
+  ],
+  "status": "in_progress"
+}
diff --git a/test/spec/fixtures/valid/blocks/video_block_basic.json b/test/spec/fixtures/valid/blocks/video_block_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/video_block_basic.json
@@ -0,0 +1,11 @@
+{
+    "type": "video",
+    "block_id": "b1",
+    "alt_text": "alt",
+    "thumbnail_url": "https://example.com/t.png",
+    "title": {
+        "type": "plain_text",
+        "text": "Title"
+    },
+    "video_url": "https://example.com/v.mp4"
+}
diff --git a/test/spec/fixtures/valid/blocks/video_block_full.json b/test/spec/fixtures/valid/blocks/video_block_full.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/blocks/video_block_full.json
@@ -0,0 +1,19 @@
+{
+    "type": "video",
+    "block_id": "video_1",
+    "alt_text": "How to use Slack",
+    "thumbnail_url": "https://example.com/thumb.png",
+    "title": {
+        "type": "plain_text",
+        "text": "Getting Started"
+    },
+    "video_url": "https://example.com/video.mp4",
+    "author_name": "Slack",
+    "description": {
+        "type": "plain_text",
+        "text": "A short intro"
+    },
+    "provider_icon_url": "https://example.com/icon.png",
+    "provider_name": "YouTube",
+    "title_url": "https://example.com"
+}
diff --git a/test/spec/fixtures/valid/elements/button_agent_prompt.json b/test/spec/fixtures/valid/elements/button_agent_prompt.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/button_agent_prompt.json
@@ -0,0 +1,8 @@
+{
+  "type": "button",
+  "text": {
+    "type": "plain_text",
+    "text": "Summarize"
+  },
+  "agent_prompt": "Summarize this incident."
+}
diff --git a/test/spec/fixtures/valid/elements/button_agent_prompt_at_limit.json b/test/spec/fixtures/valid/elements/button_agent_prompt_at_limit.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/button_agent_prompt_at_limit.json
@@ -0,0 +1,8 @@
+{
+  "type": "button",
+  "text": {
+    "type": "plain_text",
+    "text": "Summarize"
+  },
+  "agent_prompt": "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀"
+}
diff --git a/test/spec/fixtures/valid/elements/button_agent_prompt_display.json b/test/spec/fixtures/valid/elements/button_agent_prompt_display.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/button_agent_prompt_display.json
@@ -0,0 +1,10 @@
+{
+  "type": "button",
+  "text": {
+    "type": "plain_text",
+    "text": "Summarize"
+  },
+  "action_id": "summarize",
+  "agent_prompt": "Summarize this incident.",
+  "agent_prompt_display": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+}
diff --git a/test/spec/fixtures/valid/elements/button_agent_prompt_empty.json b/test/spec/fixtures/valid/elements/button_agent_prompt_empty.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/button_agent_prompt_empty.json
@@ -0,0 +1,9 @@
+{
+  "type": "button",
+  "text": {
+    "type": "plain_text",
+    "text": "Summarize"
+  },
+  "agent_prompt": "",
+  "agent_prompt_display": ""
+}
diff --git a/test/spec/fixtures/valid/elements/button_basic.json b/test/spec/fixtures/valid/elements/button_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/button_basic.json
@@ -0,0 +1,9 @@
+{
+    "type": "button",
+    "text": {
+        "type": "plain_text",
+        "text": "Click Me"
+    },
+    "action_id": "button",
+    "value": "click_me"
+}
diff --git a/test/spec/fixtures/valid/elements/button_link.json b/test/spec/fixtures/valid/elements/button_link.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/button_link.json
@@ -0,0 +1,9 @@
+{
+    "type": "button",
+    "text": {
+        "type": "plain_text",
+        "text": "Link!"
+    },
+    "action_id": "button",
+    "url": "https://ndl.im/"
+}
diff --git a/test/spec/fixtures/valid/elements/button_style.json b/test/spec/fixtures/valid/elements/button_style.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/button_style.json
@@ -0,0 +1,10 @@
+{
+    "type": "button",
+    "text": {
+        "type": "plain_text",
+        "text": "Load"
+    },
+    "action_id": "button",
+    "style": "primary",
+    "value": "im_a_style_button"
+}
diff --git a/test/spec/fixtures/valid/elements/button_text_at_limit.json b/test/spec/fixtures/valid/elements/button_text_at_limit.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/button_text_at_limit.json
@@ -0,0 +1,8 @@
+{
+    "type": "button",
+    "text": {
+        "type": "plain_text",
+        "text": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+    },
+    "action_id": "button"
+}
diff --git a/test/spec/fixtures/valid/elements/button_without_action_id.json b/test/spec/fixtures/valid/elements/button_without_action_id.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/button_without_action_id.json
@@ -0,0 +1,8 @@
+{
+    "type": "button",
+    "text": {
+        "type": "plain_text",
+        "text": "Click Me"
+    },
+    "value": "click_me"
+}
diff --git a/test/spec/fixtures/valid/elements/checkbox_basic.json b/test/spec/fixtures/valid/elements/checkbox_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/checkbox_basic.json
@@ -0,0 +1,29 @@
+{
+    "type": "checkboxes",
+    "action_id": "and...action",
+    "options": [
+        {
+            "text": {
+                "type": "plain_text",
+                "text": "A"
+            },
+            "value": "A"
+        },
+        {
+            "text": {
+                "type": "plain_text",
+                "text": "B"
+            },
+            "value": "B"
+        }
+    ],
+    "initial_options": [
+        {
+            "text": {
+                "type": "plain_text",
+                "text": "A"
+            },
+            "value": "A"
+        }
+    ]
+}
diff --git a/test/spec/fixtures/valid/elements/date_picker_basic.json b/test/spec/fixtures/valid/elements/date_picker_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/date_picker_basic.json
@@ -0,0 +1,9 @@
+{
+    "type": "datepicker",
+    "action_id": "datepicker",
+    "initial_date": "1970-01-01",
+    "placeholder": {
+        "type": "plain_text",
+        "text": "Pick a date"
+    }
+}
diff --git a/test/spec/fixtures/valid/elements/datetime_picker_basic.json b/test/spec/fixtures/valid/elements/datetime_picker_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/datetime_picker_basic.json
@@ -0,0 +1,5 @@
+{
+    "type": "datetimepicker",
+    "action_id": "datetime_picker",
+    "initial_date_time": 1628633830
+}
diff --git a/test/spec/fixtures/valid/elements/email_input_basic.json b/test/spec/fixtures/valid/elements/email_input_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/email_input_basic.json
@@ -0,0 +1,8 @@
+{
+    "type": "email_text_input",
+    "action_id": "email_input",
+    "placeholder": {
+        "type": "plain_text",
+        "text": "Enter your email"
+    }
+}
diff --git a/test/spec/fixtures/valid/elements/file_input_basic.json b/test/spec/fixtures/valid/elements/file_input_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/file_input_basic.json
@@ -0,0 +1,9 @@
+{
+    "type": "file_input",
+    "action_id": "file_input_action_id_1",
+    "filetypes": [
+        "jpg",
+        "png"
+    ],
+    "max_files": 5
+}
diff --git a/test/spec/fixtures/valid/elements/image_basic.json b/test/spec/fixtures/valid/elements/image_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/image_basic.json
@@ -0,0 +1,5 @@
+{
+    "type": "image",
+    "image_url": "https://ndl.im/img/logo.png",
+    "alt_text": "Logo for ndl.im"
+}
diff --git a/test/spec/fixtures/valid/elements/image_slack_file_id.json b/test/spec/fixtures/valid/elements/image_slack_file_id.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/image_slack_file_id.json
@@ -0,0 +1,7 @@
+{
+    "type": "image",
+    "slack_file": {
+        "id": "F0123ABC456"
+    },
+    "alt_text": "An incredibly cute kitten."
+}
diff --git a/test/spec/fixtures/valid/elements/image_slack_file_url.json b/test/spec/fixtures/valid/elements/image_slack_file_url.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/image_slack_file_url.json
@@ -0,0 +1,7 @@
+{
+    "type": "image",
+    "slack_file": {
+        "url": "https://files.slack.com/files-pri/T0123456-F0123456/xyz.png"
+    },
+    "alt_text": "An incredibly cute kitten."
+}
diff --git a/test/spec/fixtures/valid/elements/multi_select_channel.json b/test/spec/fixtures/valid/elements/multi_select_channel.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/multi_select_channel.json
@@ -0,0 +1,8 @@
+{
+    "type": "multi_channels_select",
+    "action_id": "multi_channels_select",
+    "placeholder": {
+        "type": "plain_text",
+        "text": "Select channels"
+    }
+}
diff --git a/test/spec/fixtures/valid/elements/multi_select_conversation.json b/test/spec/fixtures/valid/elements/multi_select_conversation.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/multi_select_conversation.json
@@ -0,0 +1,8 @@
+{
+    "type": "multi_conversations_select",
+    "action_id": "multi_conversations_select",
+    "placeholder": {
+        "type": "plain_text",
+        "text": "Select conversations"
+    }
+}
diff --git a/test/spec/fixtures/valid/elements/multi_select_external.json b/test/spec/fixtures/valid/elements/multi_select_external.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/multi_select_external.json
@@ -0,0 +1,9 @@
+{
+    "type": "multi_external_select",
+    "action_id": "multi_external_select",
+    "min_query_length": 3,
+    "placeholder": {
+        "type": "plain_text",
+        "text": "Select items"
+    }
+}
diff --git a/test/spec/fixtures/valid/elements/multi_select_static.json b/test/spec/fixtures/valid/elements/multi_select_static.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/multi_select_static.json
@@ -0,0 +1,24 @@
+{
+    "type": "multi_static_select",
+    "action_id": "multi_static_select",
+    "options": [
+        {
+            "text": {
+                "type": "plain_text",
+                "text": "A"
+            },
+            "value": "A"
+        },
+        {
+            "text": {
+                "type": "plain_text",
+                "text": "B"
+            },
+            "value": "B"
+        }
+    ],
+    "placeholder": {
+        "type": "plain_text",
+        "text": "Select one or more"
+    }
+}
diff --git a/test/spec/fixtures/valid/elements/multi_select_user.json b/test/spec/fixtures/valid/elements/multi_select_user.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/multi_select_user.json
@@ -0,0 +1,8 @@
+{
+    "type": "multi_users_select",
+    "action_id": "multi_users_select",
+    "placeholder": {
+        "type": "plain_text",
+        "text": "Select one or more users"
+    }
+}
diff --git a/test/spec/fixtures/valid/elements/multi_select_user_with_initial_users.json b/test/spec/fixtures/valid/elements/multi_select_user_with_initial_users.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/multi_select_user_with_initial_users.json
@@ -0,0 +1,12 @@
+{
+    "type": "multi_users_select",
+    "action_id": "multi_users_select",
+    "initial_users": [
+        "U064B5H1309",
+        "U063JR973UP"
+    ],
+    "placeholder": {
+        "type": "plain_text",
+        "text": "Select one or more users"
+    }
+}
diff --git a/test/spec/fixtures/valid/elements/number_input_basic.json b/test/spec/fixtures/valid/elements/number_input_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/number_input_basic.json
@@ -0,0 +1,5 @@
+{
+    "type": "number_input",
+    "is_decimal_allowed": false,
+    "action_id": "number_input"
+}
diff --git a/test/spec/fixtures/valid/elements/number_input_bounds.json b/test/spec/fixtures/valid/elements/number_input_bounds.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/number_input_bounds.json
@@ -0,0 +1,7 @@
+{
+  "type": "number_input",
+  "is_decimal_allowed": true,
+  "action_id": "bounds",
+  "min_value": "-1.5",
+  "max_value": "10.25"
+}
diff --git a/test/spec/fixtures/valid/elements/number_input_large_exponent.json b/test/spec/fixtures/valid/elements/number_input_large_exponent.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/number_input_large_exponent.json
@@ -0,0 +1,6 @@
+{
+  "type": "number_input",
+  "is_decimal_allowed": true,
+  "min_value": "1e309",
+  "max_value": "2e309"
+}
diff --git a/test/spec/fixtures/valid/elements/number_input_scientific_bounds.json b/test/spec/fixtures/valid/elements/number_input_scientific_bounds.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/number_input_scientific_bounds.json
@@ -0,0 +1,6 @@
+{
+  "type": "number_input",
+  "is_decimal_allowed": true,
+  "min_value": "0.1000",
+  "max_value": "1e-1"
+}
diff --git a/test/spec/fixtures/valid/elements/overflow_menu_basic.json b/test/spec/fixtures/valid/elements/overflow_menu_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/overflow_menu_basic.json
@@ -0,0 +1,27 @@
+{
+    "type": "overflow",
+    "action_id": "overflow",
+    "options": [
+        {
+            "text": {
+                "type": "plain_text",
+                "text": "A"
+            },
+            "value": "A"
+        },
+        {
+            "text": {
+                "type": "plain_text",
+                "text": "B"
+            },
+            "value": "B"
+        },
+        {
+            "text": {
+                "type": "plain_text",
+                "text": "C"
+            },
+            "value": "C"
+        }
+    ]
+}
diff --git a/test/spec/fixtures/valid/elements/plaintext_input_basic.json b/test/spec/fixtures/valid/elements/plaintext_input_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/plaintext_input_basic.json
@@ -0,0 +1,8 @@
+{
+    "type": "plain_text_input",
+    "action_id": "plaintext_input",
+    "placeholder": {
+        "type": "plain_text",
+        "text": "Enter your plain text"
+    }
+}
diff --git a/test/spec/fixtures/valid/elements/radio_button_group_basic.json b/test/spec/fixtures/valid/elements/radio_button_group_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/radio_button_group_basic.json
@@ -0,0 +1,34 @@
+{
+    "type": "radio_buttons",
+    "action_id": "radio_buttons",
+    "options": [
+        {
+            "text": {
+                "type": "plain_text",
+                "text": "A"
+            },
+            "value": "A"
+        },
+        {
+            "text": {
+                "type": "plain_text",
+                "text": "B"
+            },
+            "value": "B"
+        },
+        {
+            "text": {
+                "type": "plain_text",
+                "text": "C"
+            },
+            "value": "C"
+        }
+    ],
+    "initial_option": {
+        "text": {
+            "type": "plain_text",
+            "text": "A"
+        },
+        "value": "A"
+    }
+}
diff --git a/test/spec/fixtures/valid/elements/rich_text_input_basic.json b/test/spec/fixtures/valid/elements/rich_text_input_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/rich_text_input_basic.json
@@ -0,0 +1,23 @@
+{
+    "type": "rich_text_input",
+    "action_id": "action_id",
+    "initial_value": {
+        "type": "rich_text",
+        "elements": [
+            {
+                "type": "rich_text_section",
+                "elements": [
+                    {
+                        "type": "text",
+                        "text": "I'm rich"
+                    }
+                ]
+            }
+        ]
+    },
+    "focus_on_load": false,
+    "placeholder": {
+        "type": "plain_text",
+        "text": "Hello"
+    }
+}
diff --git a/test/spec/fixtures/valid/elements/select_menu_channel.json b/test/spec/fixtures/valid/elements/select_menu_channel.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/select_menu_channel.json
@@ -0,0 +1,8 @@
+{
+    "type": "channels_select",
+    "action_id": "channels_select",
+    "placeholder": {
+        "type": "plain_text",
+        "text": "Select a channel"
+    }
+}
diff --git a/test/spec/fixtures/valid/elements/select_menu_conversation.json b/test/spec/fixtures/valid/elements/select_menu_conversation.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/select_menu_conversation.json
@@ -0,0 +1,8 @@
+{
+    "type": "conversations_select",
+    "action_id": "conversations_select",
+    "placeholder": {
+        "type": "plain_text",
+        "text": "Select one conversation"
+    }
+}
diff --git a/test/spec/fixtures/valid/elements/select_menu_external.json b/test/spec/fixtures/valid/elements/select_menu_external.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/select_menu_external.json
@@ -0,0 +1,9 @@
+{
+    "type": "external_select",
+    "action_id": "external_select",
+    "min_query_length": 4,
+    "placeholder": {
+        "type": "plain_text",
+        "text": "Select one item"
+    }
+}
diff --git a/test/spec/fixtures/valid/elements/select_menu_static.json b/test/spec/fixtures/valid/elements/select_menu_static.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/select_menu_static.json
@@ -0,0 +1,31 @@
+{
+    "type": "static_select",
+    "action_id": "static_select",
+    "options": [
+        {
+            "text": {
+                "type": "plain_text",
+                "text": "A"
+            },
+            "value": "A"
+        },
+        {
+            "text": {
+                "type": "plain_text",
+                "text": "B"
+            },
+            "value": "B"
+        },
+        {
+            "text": {
+                "type": "plain_text",
+                "text": "C"
+            },
+            "value": "C"
+        }
+    ],
+    "placeholder": {
+        "type": "plain_text",
+        "text": "Select one item"
+    }
+}
diff --git a/test/spec/fixtures/valid/elements/select_menu_user.json b/test/spec/fixtures/valid/elements/select_menu_user.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/select_menu_user.json
@@ -0,0 +1,8 @@
+{
+    "type": "users_select",
+    "action_id": "users_select",
+    "placeholder": {
+        "type": "plain_text",
+        "text": "Select one user"
+    }
+}
diff --git a/test/spec/fixtures/valid/elements/timepicker_basic.json b/test/spec/fixtures/valid/elements/timepicker_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/timepicker_basic.json
@@ -0,0 +1,10 @@
+{
+    "type": "timepicker",
+    "action_id": "timepicker",
+    "initial_time": "12:00",
+    "placeholder": {
+        "type": "plain_text",
+        "text": "Select your time"
+    },
+    "timezone": "Australia/Sydney"
+}
diff --git a/test/spec/fixtures/valid/elements/url_input_basic.json b/test/spec/fixtures/valid/elements/url_input_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/url_input_basic.json
@@ -0,0 +1,4 @@
+{
+    "type": "url_text_input",
+    "action_id": "url_text_input"
+}
diff --git a/test/spec/fixtures/valid/elements/url_source_basic.json b/test/spec/fixtures/valid/elements/url_source_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/url_source_basic.json
@@ -0,0 +1,5 @@
+{
+  "type": "url",
+  "url": "https://docs.slack.dev/",
+  "text": "Slack API docs"
+}
diff --git a/test/spec/fixtures/valid/elements/workflow_button_basic.json b/test/spec/fixtures/valid/elements/workflow_button_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/elements/workflow_button_basic.json
@@ -0,0 +1,23 @@
+{
+    "type": "workflow_button",
+    "action_id": "run_workflow",
+    "text": {
+        "type": "plain_text",
+        "text": "Run Your Workflow"
+    },
+    "workflow": {
+        "trigger": {
+            "url": "https://slack.com/shortcuts/Ft012KXZK1MZ/8831723c452aac3e87c6d3219bebd44c",
+            "customizable_input_parameters": [
+                {
+                    "name": "name_a",
+                    "value": "value_a"
+                },
+                {
+                    "name": "name_b",
+                    "value": "value_b"
+                }
+            ]
+        }
+    }
+}
diff --git a/test/spec/fixtures/valid/messages/message_basic.json b/test/spec/fixtures/valid/messages/message_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/messages/message_basic.json
@@ -0,0 +1,15 @@
+{
+    "channel": "#slackblocks",
+    "mrkdwn": true,
+    "blocks": [
+        {
+            "type": "section",
+            "block_id": "fake_block_id",
+            "text": {
+                "type": "mrkdwn",
+                "text": "Hello, world!"
+            }
+        }
+    ],
+    "text": ""
+}
diff --git a/test/spec/fixtures/valid/messages/message_basic_attachment.json b/test/spec/fixtures/valid/messages/message_basic_attachment.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/messages/message_basic_attachment.json
@@ -0,0 +1,20 @@
+{
+    "channel": "#slackblocks",
+    "mrkdwn": true,
+    "attachments": [
+        {
+            "blocks": [
+                {
+                    "type": "section",
+                    "block_id": "block1",
+                    "text": {
+                        "type": "mrkdwn",
+                        "text": "Hello, world!"
+                    }
+                }
+            ],
+            "color": "#000000"
+        }
+    ],
+    "text": ""
+}
diff --git a/test/spec/fixtures/valid/messages/message_compound.json b/test/spec/fixtures/valid/messages/message_compound.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/messages/message_compound.json
@@ -0,0 +1,63 @@
+{
+    "channel": "#slackblocks",
+    "mrkdwn": true,
+    "blocks": [
+        {
+            "type": "section",
+            "block_id": "fake_block1",
+            "text": {
+                "type": "mrkdwn",
+                "text": "Block, One"
+            }
+        },
+        {
+            "type": "image",
+            "block_id": "fake_block3",
+            "image_url": "http://bit.ly/slack-block-test-image",
+            "alt_text": "crash",
+            "title": {
+                "type": "plain_text",
+                "text": " "
+            }
+        }
+    ],
+    "attachments": [
+        {
+            "blocks": [
+                {
+                    "type": "section",
+                    "block_id": "fake_block1",
+                    "text": {
+                        "type": "mrkdwn",
+                        "text": "Block, One"
+                    }
+                }
+            ],
+            "color": "#8800ff"
+        },
+        {
+            "blocks": [
+                {
+                    "type": "section",
+                    "block_id": "fake_block2",
+                    "text": {
+                        "type": "mrkdwn",
+                        "text": "Block, Two"
+                    }
+                },
+                {
+                    "type": "image",
+                    "block_id": "fake_block3",
+                    "image_url": "http://bit.ly/slack-block-test-image",
+                    "alt_text": "crash",
+                    "title": {
+                        "type": "plain_text",
+                        "text": " "
+                    }
+                }
+            ],
+            "color": "#ffff00"
+        }
+    ],
+    "text": ""
+}
diff --git a/test/spec/fixtures/valid/messages/message_response.json b/test/spec/fixtures/valid/messages/message_response.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/messages/message_response.json
@@ -0,0 +1,16 @@
+{
+    "mrkdwn": true,
+    "blocks": [
+        {
+            "type": "section",
+            "block_id": "fake_block_id",
+            "text": {
+                "type": "mrkdwn",
+                "text": "Hello, world!"
+            }
+        }
+    ],
+    "text": "",
+    "replace_original": false,
+    "response_type": "ephemeral"
+}
diff --git a/test/spec/fixtures/valid/messages/message_two_charts.json b/test/spec/fixtures/valid/messages/message_two_charts.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/messages/message_two_charts.json
@@ -0,0 +1,35 @@
+{
+  "channel": "C123",
+  "text": "",
+  "mrkdwn": true,
+  "blocks": [
+    {
+      "type": "data_visualization",
+      "block_id": "chart_0",
+      "title": "Chart",
+      "chart": {
+        "type": "pie",
+        "segments": [
+          {
+            "label": "A",
+            "value": 1
+          }
+        ]
+      }
+    },
+    {
+      "type": "data_visualization",
+      "block_id": "chart_1",
+      "title": "Chart",
+      "chart": {
+        "type": "pie",
+        "segments": [
+          {
+            "label": "A",
+            "value": 1
+          }
+        ]
+      }
+    }
+  ]
+}
diff --git a/test/spec/fixtures/valid/messages/message_with_attachments.json b/test/spec/fixtures/valid/messages/message_with_attachments.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/messages/message_with_attachments.json
@@ -0,0 +1,20 @@
+{
+    "channel": "#slackblocks",
+    "mrkdwn": true,
+    "attachments": [
+        {
+            "blocks": [
+                {
+                    "type": "section",
+                    "block_id": "fake_block_id",
+                    "text": {
+                        "type": "mrkdwn",
+                        "text": "Hello, world!"
+                    }
+                }
+            ],
+            "color": "#ffff00"
+        }
+    ],
+    "text": ""
+}
diff --git a/test/spec/fixtures/valid/messages/message_with_optional_arguments.json b/test/spec/fixtures/valid/messages/message_with_optional_arguments.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/messages/message_with_optional_arguments.json
@@ -0,0 +1,17 @@
+{
+    "channel": "#slackblocks",
+    "mrkdwn": true,
+    "blocks": [
+        {
+            "type": "section",
+            "block_id": "fake_block_id",
+            "text": {
+                "type": "mrkdwn",
+                "text": "Hello, world!"
+            }
+        }
+    ],
+    "text": "",
+    "unfurl_links": false,
+    "unfurl_media": false
+}
diff --git a/test/spec/fixtures/valid/messages/webhook_message_basic.json b/test/spec/fixtures/valid/messages/webhook_message_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/messages/webhook_message_basic.json
@@ -0,0 +1,39 @@
+{
+    "blocks": [
+        {
+            "type": "section",
+            "block_id": "fake_block_id",
+            "text": {
+                "type": "mrkdwn",
+                "text": "You wouldn't do ol' Hook in now, would you, lad?"
+            }
+        },
+        {
+            "type": "section",
+            "block_id": "fake_block_id",
+            "text": {
+                "type": "mrkdwn",
+                "text": "Well, all right... if you... say you're a codfish."
+            }
+        }
+    ],
+    "response_type": "ephemeral",
+    "replace_original": true,
+    "unfurl_links": false,
+    "unfurl_media": false,
+    "metadata": {
+        "sender": "Walt",
+        "event_payload": {
+            "type": "markdown",
+            "items": [
+                {
+                    "type": "data_table"
+                },
+                {
+                    "type": "markdown",
+                    "text": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+                }
+            ]
+        }
+    }
+}
diff --git a/test/spec/fixtures/valid/messages/webhook_message_delete.json b/test/spec/fixtures/valid/messages/webhook_message_delete.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/messages/webhook_message_delete.json
@@ -0,0 +1,41 @@
+{
+    "attachments": [
+        {
+            "blocks": [
+                {
+                    "type": "section",
+                    "block_id": "fake_block_id",
+                    "text": {
+                        "type": "mrkdwn",
+                        "text": "I'M A CODFISH!"
+                    }
+                }
+            ]
+        }
+    ],
+    "blocks": [
+        {
+            "type": "section",
+            "block_id": "fake_block_id",
+            "text": {
+                "type": "mrkdwn",
+                "text": "I'm a codfish."
+            }
+        },
+        {
+            "type": "section",
+            "block_id": "fake_block_id",
+            "text": {
+                "type": "mrkdwn",
+                "text": "Louder!"
+            }
+        }
+    ],
+    "response_type": "in_channel",
+    "delete_original": true,
+    "unfurl_links": true,
+    "unfurl_media": true,
+    "metadata": {
+        "sender": "Walt"
+    }
+}
diff --git a/test/spec/fixtures/valid/objects/confirmation_dialogue_basic.json b/test/spec/fixtures/valid/objects/confirmation_dialogue_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/objects/confirmation_dialogue_basic.json
@@ -0,0 +1,18 @@
+{
+    "title": {
+        "type": "plain_text",
+        "text": "Maybe?"
+    },
+    "text": {
+        "type": "plain_text",
+        "text": "Would you like to play checkers?"
+    },
+    "confirm": {
+        "type": "plain_text",
+        "text": "Yes"
+    },
+    "deny": {
+        "type": "plain_text",
+        "text": "Nope!"
+    }
+}
diff --git a/test/spec/fixtures/valid/objects/conversation_filter_basic.json b/test/spec/fixtures/valid/objects/conversation_filter_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/objects/conversation_filter_basic.json
@@ -0,0 +1,7 @@
+{
+    "include": [
+        "public",
+        "mpim"
+    ],
+    "exclude_bot_users": true
+}
diff --git a/test/spec/fixtures/valid/objects/dispatch_action_configuration_basic.json b/test/spec/fixtures/valid/objects/dispatch_action_configuration_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/objects/dispatch_action_configuration_basic.json
@@ -0,0 +1,5 @@
+{
+    "trigger_actions_on": [
+        "on_character_entered"
+    ]
+}
diff --git a/test/spec/fixtures/valid/objects/input_parameter_basic.json b/test/spec/fixtures/valid/objects/input_parameter_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/objects/input_parameter_basic.json
@@ -0,0 +1,4 @@
+{
+    "name": "name",
+    "value": "value"
+}
diff --git a/test/spec/fixtures/valid/objects/option_basic.json b/test/spec/fixtures/valid/objects/option_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/objects/option_basic.json
@@ -0,0 +1,7 @@
+{
+    "text": {
+        "type": "plain_text",
+        "text": "Canberra"
+    },
+    "value": "canberra"
+}
diff --git a/test/spec/fixtures/valid/objects/option_group_basic.json b/test/spec/fixtures/valid/objects/option_group_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/objects/option_group_basic.json
@@ -0,0 +1,29 @@
+{
+    "label": {
+        "type": "plain_text",
+        "text": "Group A"
+    },
+    "options": [
+        {
+            "text": {
+                "type": "plain_text",
+                "text": "A"
+            },
+            "value": "A"
+        },
+        {
+            "text": {
+                "type": "plain_text",
+                "text": "B"
+            },
+            "value": "B"
+        },
+        {
+            "text": {
+                "type": "plain_text",
+                "text": "C"
+            },
+            "value": "C"
+        }
+    ]
+}
diff --git a/test/spec/fixtures/valid/objects/option_value_at_limit.json b/test/spec/fixtures/valid/objects/option_value_at_limit.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/objects/option_value_at_limit.json
@@ -0,0 +1,7 @@
+{
+    "text": {
+        "type": "plain_text",
+        "text": "At limit"
+    },
+    "value": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+}
diff --git a/test/spec/fixtures/valid/objects/text_markdown_basic.json b/test/spec/fixtures/valid/objects/text_markdown_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/objects/text_markdown_basic.json
@@ -0,0 +1,4 @@
+{
+    "type": "mrkdwn",
+    "text": "hi"
+}
diff --git a/test/spec/fixtures/valid/objects/text_markdown_verbatim.json b/test/spec/fixtures/valid/objects/text_markdown_verbatim.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/objects/text_markdown_verbatim.json
@@ -0,0 +1,5 @@
+{
+    "type": "mrkdwn",
+    "text": "hi",
+    "verbatim": true
+}
diff --git a/test/spec/fixtures/valid/objects/text_plaintext_basic.json b/test/spec/fixtures/valid/objects/text_plaintext_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/objects/text_plaintext_basic.json
@@ -0,0 +1,4 @@
+{
+    "type": "plain_text",
+    "text": "hi"
+}
diff --git a/test/spec/fixtures/valid/objects/text_plaintext_emoji.json b/test/spec/fixtures/valid/objects/text_plaintext_emoji.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/objects/text_plaintext_emoji.json
@@ -0,0 +1,5 @@
+{
+    "type": "plain_text",
+    "text": "hi",
+    "emoji": true
+}
diff --git a/test/spec/fixtures/valid/objects/trigger_basic.json b/test/spec/fixtures/valid/objects/trigger_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/objects/trigger_basic.json
@@ -0,0 +1,13 @@
+{
+    "url": "https://slack.com/shortcuts/Ft012KXZK1MZ/8831723c452aac3e87c6d3219bebd44c",
+    "customizable_input_parameters": [
+        {
+            "name": "A",
+            "value": "A"
+        },
+        {
+            "name": "B",
+            "value": "B"
+        }
+    ]
+}
diff --git a/test/spec/fixtures/valid/objects/workflow_basic.json b/test/spec/fixtures/valid/objects/workflow_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/objects/workflow_basic.json
@@ -0,0 +1,15 @@
+{
+    "trigger": {
+        "url": "https://slack.com/shortcuts/Ft012KXZK1MZ/8831723c452aac3e87c6d3219bebd44c",
+        "customizable_input_parameters": [
+            {
+                "name": "A",
+                "value": "A"
+            },
+            {
+                "name": "B",
+                "value": "B"
+            }
+        ]
+    }
+}
diff --git a/test/spec/fixtures/valid/rich_text/rich_text_basic.json b/test/spec/fixtures/valid/rich_text/rich_text_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/rich_text/rich_text_basic.json
@@ -0,0 +1,9 @@
+{
+    "type": "text",
+    "text": "I am a bold rich text block!",
+    "style": {
+        "bold": true,
+        "italic": true,
+        "strike": false
+    }
+}
diff --git a/test/spec/fixtures/valid/rich_text/rich_text_channel_basic.json b/test/spec/fixtures/valid/rich_text/rich_text_channel_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/rich_text/rich_text_channel_basic.json
@@ -0,0 +1,12 @@
+{
+    "type": "channel",
+    "channel_id": "C0261C65XNY",
+    "style": {
+        "bold": true,
+        "italic": false,
+        "strike": true,
+        "highlight": true,
+        "client_highlight": true,
+        "unlink": false
+    }
+}
diff --git a/test/spec/fixtures/valid/rich_text/rich_text_code_block_basic.json b/test/spec/fixtures/valid/rich_text/rich_text_code_block_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/rich_text/rich_text_code_block_basic.json
@@ -0,0 +1,10 @@
+{
+    "type": "rich_text_preformatted",
+    "elements": [
+        {
+            "type": "text",
+            "text": "\ndef hello_world():\n    print('hello, world')"
+        }
+    ],
+    "border": 0
+}
diff --git a/test/spec/fixtures/valid/rich_text/rich_text_emoji_basic.json b/test/spec/fixtures/valid/rich_text/rich_text_emoji_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/rich_text/rich_text_emoji_basic.json
@@ -0,0 +1,4 @@
+{
+    "type": "emoji",
+    "name": "wave"
+}
diff --git a/test/spec/fixtures/valid/rich_text/rich_text_link_basic.json b/test/spec/fixtures/valid/rich_text/rich_text_link_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/rich_text/rich_text_link_basic.json
@@ -0,0 +1,12 @@
+{
+    "type": "link",
+    "url": "https://google.com/",
+    "text": "Google",
+    "unsafe": false,
+    "style": {
+        "bold": true,
+        "italic": false,
+        "strike": true,
+        "code": true
+    }
+}
diff --git a/test/spec/fixtures/valid/rich_text/rich_text_list_basic.json b/test/spec/fixtures/valid/rich_text/rich_text_list_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/rich_text/rich_text_list_basic.json
@@ -0,0 +1,36 @@
+{
+    "type": "rich_text_list",
+    "elements": [
+        {
+            "type": "rich_text_section",
+            "elements": [
+                {
+                    "type": "text",
+                    "text": "Oh"
+                }
+            ]
+        },
+        {
+            "type": "rich_text_section",
+            "elements": [
+                {
+                    "type": "text",
+                    "text": "Hi"
+                }
+            ]
+        },
+        {
+            "type": "rich_text_section",
+            "elements": [
+                {
+                    "type": "text",
+                    "text": "Mark"
+                }
+            ]
+        }
+    ],
+    "style": "bullet",
+    "indent": 0,
+    "offset": 0,
+    "border": 1
+}
diff --git a/test/spec/fixtures/valid/rich_text/rich_text_list_ordered.json b/test/spec/fixtures/valid/rich_text/rich_text_list_ordered.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/rich_text/rich_text_list_ordered.json
@@ -0,0 +1,27 @@
+{
+    "type": "rich_text_list",
+    "elements": [
+        {
+            "type": "rich_text_section",
+            "elements": [
+                {
+                    "type": "text",
+                    "text": "Oh"
+                }
+            ]
+        },
+        {
+            "type": "rich_text_section",
+            "elements": [
+                {
+                    "type": "text",
+                    "text": "Hi"
+                }
+            ]
+        }
+    ],
+    "style": "ordered",
+    "indent": 1,
+    "offset": 2,
+    "border": 1
+}
diff --git a/test/spec/fixtures/valid/rich_text/rich_text_quote_basic.json b/test/spec/fixtures/valid/rich_text/rich_text_quote_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/rich_text/rich_text_quote_basic.json
@@ -0,0 +1,10 @@
+{
+    "type": "rich_text_quote",
+    "elements": [
+        {
+            "type": "text",
+            "text": "Great and good are seldom the same man"
+        }
+    ],
+    "border": 1
+}
diff --git a/test/spec/fixtures/valid/rich_text/rich_text_section_basic.json b/test/spec/fixtures/valid/rich_text/rich_text_section_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/rich_text/rich_text_section_basic.json
@@ -0,0 +1,9 @@
+{
+    "type": "rich_text_section",
+    "elements": [
+        {
+            "type": "text",
+            "text": "The only true wisdom is in knowing you know nothing"
+        }
+    ]
+}
diff --git a/test/spec/fixtures/valid/rich_text/rich_text_user_basic.json b/test/spec/fixtures/valid/rich_text/rich_text_user_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/rich_text/rich_text_user_basic.json
@@ -0,0 +1,12 @@
+{
+    "type": "user",
+    "user_id": "DR36TNNLA",
+    "style": {
+        "bold": true,
+        "italic": false,
+        "strike": true,
+        "highlight": true,
+        "client_highlight": true,
+        "unlink": false
+    }
+}
diff --git a/test/spec/fixtures/valid/rich_text/rich_text_user_group_basic.json b/test/spec/fixtures/valid/rich_text/rich_text_user_group_basic.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/rich_text/rich_text_user_group_basic.json
@@ -0,0 +1,12 @@
+{
+    "type": "usergroup",
+    "usergroup_id": "C01RGRU0RUK",
+    "style": {
+        "bold": true,
+        "italic": false,
+        "strike": true,
+        "highlight": true,
+        "client_highlight": true,
+        "unlink": false
+    }
+}
diff --git a/test/spec/fixtures/valid/views/hometab_view.json b/test/spec/fixtures/valid/views/hometab_view.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/views/hometab_view.json
@@ -0,0 +1,13 @@
+{
+    "type": "home",
+    "blocks": [
+        {
+            "type": "section",
+            "block_id": "fake_id",
+            "text": {
+                "type": "mrkdwn",
+                "text": "Example Block"
+            }
+        }
+    ]
+}
diff --git a/test/spec/fixtures/valid/views/modal_with_blocks.json b/test/spec/fixtures/valid/views/modal_with_blocks.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/views/modal_with_blocks.json
@@ -0,0 +1,37 @@
+{
+    "type": "modal",
+    "blocks": [
+        {
+            "type": "section",
+            "block_id": "1",
+            "text": {
+                "type": "mrkdwn",
+                "text": "first section block"
+            }
+        },
+        {
+            "type": "divider",
+            "block_id": "2"
+        },
+        {
+            "type": "section",
+            "block_id": "3",
+            "text": {
+                "type": "mrkdwn",
+                "text": "second section block"
+            }
+        }
+    ],
+    "title": {
+        "type": "plain_text",
+        "text": "Hello, world!"
+    },
+    "close": {
+        "type": "plain_text",
+        "text": "Close button"
+    },
+    "submit": {
+        "type": "plain_text",
+        "text": "Submit button"
+    }
+}
diff --git a/test/spec/fixtures/valid/views/modal_without_blocks.json b/test/spec/fixtures/valid/views/modal_without_blocks.json
new file mode 100644
--- /dev/null
+++ b/test/spec/fixtures/valid/views/modal_without_blocks.json
@@ -0,0 +1,8 @@
+{
+    "type": "modal",
+    "title": {
+        "type": "plain_text",
+        "text": "Empty"
+    },
+    "blocks": []
+}
diff --git a/test/spec/limits.json b/test/spec/limits.json
new file mode 100644
--- /dev/null
+++ b/test/spec/limits.json
@@ -0,0 +1,172 @@
+{
+  "text": { "min_length": 1, "max_length": 3000 },
+  "action_id": { "max_length": 255 },
+  "block_id": { "max_length": 255 },
+  "button": {
+    "text": { "max_length": 75 },
+    "url": { "max_length": 3000 },
+    "value": { "max_length": 2000 },
+    "accessibility_label": { "max_length": 75 },
+    "agent_prompt": { "max_length": 4000 }
+  },
+  "confirmation": {
+    "title": { "max_length": 100 },
+    "text": { "max_length": 300 },
+    "confirm": { "max_length": 30 },
+    "deny": { "max_length": 30 }
+  },
+  "option": {
+    "text": { "max_length": 75 },
+    "value": { "max_length": 150 },
+    "description": { "max_length": 75 },
+    "url": { "max_length": 3000 }
+  },
+  "option_group": {
+    "label": { "max_length": 75 },
+    "options": { "min_items": 1, "max_items": 100 }
+  },
+  "select": {
+    "placeholder": { "max_length": 150 },
+    "options": { "min_items": 1, "max_items": 100 },
+    "option_groups": { "min_items": 1, "max_items": 100 },
+    "min_query_length": { "min": 0 }
+  },
+  "overflow": { "options": { "min_items": 1, "max_items": 5 } },
+  "checkboxes": { "options": { "min_items": 1, "max_items": 10 } },
+  "radio_buttons": { "options": { "min_items": 1, "max_items": 10 } },
+  "file_input": { "max_files": { "min": 1, "max": 10 } },
+  "plain_text_input": {
+    "min_length": { "min": 0, "max": 3000 },
+    "max_length": { "min": 1, "max": 3000 },
+    "placeholder": { "max_length": 150 }
+  },
+  "email_input": { "placeholder": { "max_length": 150 } },
+  "url_input": { "placeholder": { "max_length": 150 } },
+  "number_input": { "placeholder": { "max_length": 150 } },
+  "date_picker": { "placeholder": { "max_length": 150 } },
+  "time_picker": { "placeholder": { "max_length": 150 } },
+  "rich_text_input": {
+    "placeholder": { "max_length": 150 },
+    "min_lines": { "min": 1, "max": 100 },
+    "max_lines": { "min": 1, "max": 100 }
+  },
+  "multi_select": { "max_selected_items": { "min": 1 } },
+  "conversation_filter": { "include": { "min_items": 1 } },
+  "dispatch_action_configuration": { "trigger_actions_on": { "min_items": 1, "max_items": 2 } },
+  "actions": { "elements": { "max_items": 25 } },
+  "alert": { "text": { "max_length": 200 } },
+  "card": {
+    "title": { "max_length": 150 },
+    "subtitle": { "max_length": 150 },
+    "body": { "max_length": 200 },
+    "actions": { "max_items": 3 },
+    "subtext": { "max_length": 200 }
+  },
+  "carousel": { "elements": { "min_items": 1, "max_items": 10 } },
+  "container": {
+    "title": { "max_length": 150 },
+    "subtitle": { "max_length": 150 },
+    "child_blocks": { "min_items": 1, "max_items": 10 }
+  },
+  "context": { "elements": { "max_items": 10 } },
+  "context_actions": { "elements": { "max_items": 5 } },
+  "feedback_button": {
+    "text": { "max_length": 75 },
+    "value": { "max_length": 2000 },
+    "accessibility_label": { "max_length": 75 }
+  },
+  "icon_button": {
+    "value": { "max_length": 2000 },
+    "accessibility_label": { "max_length": 75 },
+    "visible_to_user_ids": { "max_items": 10 }
+  },
+  "workflow_button": {
+    "text": { "max_length": 75 },
+    "accessibility_label": { "max_length": 75 }
+  },
+  "table": {
+    "rows": { "max_items": 100 },
+    "columns": { "max_items": 20 },
+    "column_settings": { "max_items": 20 }
+  },
+  "data_table": {
+    "rows": { "min_items": 2, "max_items": 201 },
+    "columns": { "min_items": 1, "max_items": 20 },
+    "page_size": { "min": 1, "max": 100 },
+    "cell_text": { "min_length": 1 },
+    "content": { "max_length": 20000 },
+    "total_content": { "max_length": 20000 },
+    "row_header_column_index": { "min": 0 }
+  },
+  "data_visualization": {
+    "title": { "max_length": 50 },
+    "segments": { "min_items": 1, "max_items": 12 },
+    "segment": {
+      "label": { "max_length": 20 },
+      "value": { "exclusive_min": 0 }
+    },
+    "series": { "min_items": 1, "max_items": 12 },
+    "series_name": { "max_length": 20 },
+    "data": { "min_items": 1, "max_items": 20 },
+    "point_label": { "max_length": 20 },
+    "categories": { "min_items": 1, "max_items": 20 },
+    "category_label": { "max_length": 20 },
+    "axis_label": { "max_length": 50 }
+  },
+  "header": { "text": { "max_length": 150 } },
+  "image": {
+    "image_url": { "max_length": 3000 },
+    "alt_text": { "max_length": 2000 },
+    "title": { "max_length": 2000 }
+  },
+  "image_element": {
+    "image_url": { "max_length": 3000 },
+    "alt_text": { "max_length": 2000 }
+  },
+  "input": {
+    "label": { "max_length": 2000 },
+    "hint": { "max_length": 2000 }
+  },
+  "markdown": {
+    "text": { "max_length": 12000 },
+    "total_text": { "max_length": 12000 }
+  },
+  "plan": { "tasks": { "max_items": 50 } },
+  "rich_text_list": {
+    "indent": { "min": 0, "max": 8 },
+    "offset": { "min": 0 },
+    "border": { "min": 0, "max": 1 }
+  },
+  "rich_text_quote": { "border": { "min": 0, "max": 1 } },
+  "rich_text_preformatted": { "border": { "min": 0, "max": 1 } },
+  "section": {
+    "text": { "max_length": 3000 },
+    "fields": { "max_items": 10, "item_max_length": 2000 }
+  },
+  "video": {
+    "alt_text": { "max_length": 2000 },
+    "title": { "max_length": 200 },
+    "author_name": { "max_length": 50 },
+    "description": { "max_length": 200 },
+    "provider_name": { "max_length": 50 },
+    "thumbnail_url": { "max_length": 3000 },
+    "video_url": { "max_length": 3000 },
+    "title_url": { "max_length": 2000 },
+    "provider_icon_url": { "max_length": 2000 }
+  },
+  "message": {
+    "data_visualizations": { "max_items": 2 },
+    "channel": { "min_length": 1 },
+    "blocks": { "max_items": 50 },
+    "attachments": { "max_items": 100 }
+  },
+  "view": {
+    "blocks": { "max_items": 100 },
+    "private_metadata": { "max_length": 3000 },
+    "callback_id": { "max_length": 255 },
+    "external_id": { "max_length": 255 },
+    "title": { "max_length": 24 },
+    "close": { "max_length": 24 },
+    "submit": { "max_length": 24 }
+  }
+}
diff --git a/test/spec/manifest.json b/test/spec/manifest.json
new file mode 100644
--- /dev/null
+++ b/test/spec/manifest.json
@@ -0,0 +1,683 @@
+{
+  "spec_version": "1.4.0",
+  "fixtures": [
+    {
+      "id": "attachments/attachment_multi_block",
+      "description": "Attachment Multi Block",
+      "slack_docs": "https://docs.slack.dev/messaging/formatting-message-text/#attachments",
+      "since": "1.0.0"
+    },
+    {
+      "id": "attachments/attachment_simple",
+      "description": "Attachment Simple",
+      "slack_docs": "https://docs.slack.dev/messaging/formatting-message-text/#attachments",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/actions_block_checkboxes",
+      "description": "Actions Block Checkboxes",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/alert_block",
+      "description": "Alert Block",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/alert-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/card_block",
+      "description": "Card Block",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/card-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/carousel_block",
+      "description": "Carousel Block",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/carousel-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/container_block",
+      "description": "Container Block",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/container-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/context_actions_feedback_buttons",
+      "description": "Context Actions Block With Feedback Buttons",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/context-actions-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/context_actions_icon_button",
+      "description": "Context Actions Block With An Icon Button",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/context-actions-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/context_block_text_only",
+      "description": "Context Block Text Only",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/data_table_block",
+      "description": "Data Table Block",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/data-table-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/data_visualization_area",
+      "description": "Data Visualization Block With An Area Chart",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/data_visualization_bar",
+      "description": "Data Visualization Block With A Bar Chart",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/data_visualization_line",
+      "description": "Data Visualization Block With A Line Chart",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/data_visualization_pie",
+      "description": "Data Visualization Block With A Pie Chart",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/divider_block_only",
+      "description": "Divider Block Only",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/file_block_only",
+      "description": "File Block Only",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/header_block_emoji_at_limit",
+      "description": "Header Block With Text Of Exactly 150 Astral-Plane Emoji Code Points",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/header-block/",
+      "since": "1.0.1"
+    },
+    {
+      "id": "blocks/header_block_only",
+      "description": "Header Block Only",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/header_block_text_at_limit",
+      "description": "Header Block With Text At The 150-Character Limit",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/header-block/",
+      "since": "1.0.1"
+    },
+    {
+      "id": "blocks/image_block_only",
+      "description": "Image Block Only",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/image_block_slack_file",
+      "description": "Image Block Slack File",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/",
+      "since": "1.1.0"
+    },
+    {
+      "id": "blocks/input_block_only",
+      "description": "Input Block Only",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/markdown_block_basic",
+      "description": "Markdown Block Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/markdown_empty",
+      "description": "Markdown Empty",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/",
+      "since": "1.1.0"
+    },
+    {
+      "id": "blocks/plan_block",
+      "description": "Plan Block",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/plan-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/rich_text_block_basic",
+      "description": "Rich Text Block Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/section_block_both_text_and_fields",
+      "description": "Section Block Both Text And Fields",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/section_block_empty_text_field_value",
+      "description": "Section Block Empty Text Field Value",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/section_block_fields",
+      "description": "Section Block Fields",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/section_block_single_field_value_coercion",
+      "description": "Section Block Single Field Value Coercion",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/section_block_text_only",
+      "description": "Section Block Text Only",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/table_block",
+      "description": "Table Block",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/table_ragged_rows",
+      "description": "Table Ragged Rows",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/",
+      "since": "1.1.0"
+    },
+    {
+      "id": "blocks/task_card_block",
+      "description": "Task Card Block",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/task-card-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/video_block_basic",
+      "description": "Video Block Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "blocks/video_block_full",
+      "description": "Video Block Full",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/button_agent_prompt",
+      "description": "Button with a prompt and no action ID",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/button-element/",
+      "since": "1.4.0"
+    },
+    {
+      "id": "elements/button_agent_prompt_at_limit",
+      "description": "Button prompt at the Unicode code point limit",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/button-element/",
+      "since": "1.4.0"
+    },
+    {
+      "id": "elements/button_agent_prompt_display",
+      "description": "Button with a custom prompt display longer than its label limit",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/button-element/",
+      "since": "1.4.0"
+    },
+    {
+      "id": "elements/button_agent_prompt_empty",
+      "description": "Button preserves empty supplied prompt strings",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/button-element/",
+      "since": "1.4.0"
+    },
+    {
+      "id": "elements/button_basic",
+      "description": "Button Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/button_without_action_id",
+      "description": "Button Without Action Id",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.1.0"
+    },
+    {
+      "id": "elements/button_link",
+      "description": "Button Link",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/button_style",
+      "description": "Button Style",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/button_text_at_limit",
+      "description": "Button With Text At The 75-Character Limit",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/button-element/",
+      "since": "1.0.1"
+    },
+    {
+      "id": "elements/checkbox_basic",
+      "description": "Checkbox Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/date_picker_basic",
+      "description": "Date Picker Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/datetime_picker_basic",
+      "description": "Datetime Picker Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/email_input_basic",
+      "description": "Email Input Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/file_input_basic",
+      "description": "File Input Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/file-input-element/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/image_basic",
+      "description": "Image Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/image_slack_file_id",
+      "description": "Image With Slack File ID",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/composition-objects/slack-file-object/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/image_slack_file_url",
+      "description": "Image With Slack File URL",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/composition-objects/slack-file-object/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/multi_select_channel",
+      "description": "Multi Select Channel",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/multi_select_conversation",
+      "description": "Multi Select Conversation",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/multi_select_external",
+      "description": "Multi Select External",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/multi_select_static",
+      "description": "Multi Select Static",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/multi_select_user",
+      "description": "Multi Select User",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/multi_select_user_with_initial_users",
+      "description": "Multi Select User With Initial Users",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/number_input_basic",
+      "description": "Number Input Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/overflow_menu_basic",
+      "description": "Overflow Menu Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/plaintext_input_basic",
+      "description": "Plaintext Input Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/radio_button_group_basic",
+      "description": "Radio Button Group Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/rich_text_input_basic",
+      "description": "Rich Text Input Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/select_menu_channel",
+      "description": "Select Menu Channel",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/select_menu_conversation",
+      "description": "Select Menu Conversation",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/select_menu_external",
+      "description": "Select Menu External",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/select_menu_static",
+      "description": "Select Menu Static",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/select_menu_user",
+      "description": "Select Menu User",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/timepicker_basic",
+      "description": "Timepicker Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/url_input_basic",
+      "description": "Url Input Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/url_source_basic",
+      "description": "URL Source",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/url-source-element/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "elements/workflow_button_basic",
+      "description": "Workflow Button Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "messages/message_basic",
+      "description": "Message Basic",
+      "slack_docs": "https://docs.slack.dev/reference/methods/chat.postMessage/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "messages/message_basic_attachment",
+      "description": "Message Basic Attachment",
+      "slack_docs": "https://docs.slack.dev/reference/methods/chat.postMessage/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "messages/message_compound",
+      "description": "Message Compound",
+      "slack_docs": "https://docs.slack.dev/reference/methods/chat.postMessage/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "messages/message_response",
+      "description": "Message Response",
+      "slack_docs": "https://docs.slack.dev/reference/methods/chat.postMessage/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "messages/message_with_attachments",
+      "description": "Message With Attachments",
+      "slack_docs": "https://docs.slack.dev/reference/methods/chat.postMessage/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "messages/message_with_optional_arguments",
+      "description": "Message With Optional Arguments",
+      "slack_docs": "https://docs.slack.dev/reference/methods/chat.postMessage/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "messages/webhook_message_basic",
+      "description": "Webhook message with opaque application metadata, including unrelated Block Kit discriminator strings",
+      "slack_docs": "https://docs.slack.dev/reference/methods/chat.postMessage/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "messages/webhook_message_delete",
+      "description": "Webhook Message Delete",
+      "slack_docs": "https://docs.slack.dev/reference/methods/chat.postMessage/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "objects/confirmation_dialogue_basic",
+      "description": "Confirmation Dialogue Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/composition-objects/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "objects/conversation_filter_basic",
+      "description": "Conversation Filter Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/composition-objects/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "objects/dispatch_action_configuration_basic",
+      "description": "Dispatch Action Configuration Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/composition-objects/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "objects/input_parameter_basic",
+      "description": "Input Parameter Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/composition-objects/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "objects/option_basic",
+      "description": "Option Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/composition-objects/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "objects/option_group_basic",
+      "description": "Option Group Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/composition-objects/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "objects/option_value_at_limit",
+      "description": "Option With Value At The 150-Character Limit",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/composition-objects/option-object/",
+      "since": "1.0.1"
+    },
+    {
+      "id": "objects/text_markdown_basic",
+      "description": "Text Markdown Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/composition-objects/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "objects/text_markdown_verbatim",
+      "description": "Text Markdown Verbatim",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/composition-objects/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "objects/text_plaintext_basic",
+      "description": "Text Plaintext Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/composition-objects/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "objects/text_plaintext_emoji",
+      "description": "Text Plaintext Emoji",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/composition-objects/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "objects/trigger_basic",
+      "description": "Trigger Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/composition-objects/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "objects/workflow_basic",
+      "description": "Workflow Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/composition-objects/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "rich_text/rich_text_basic",
+      "description": "Rich Text Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "rich_text/rich_text_channel_basic",
+      "description": "Rich Text Channel Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "rich_text/rich_text_code_block_basic",
+      "description": "Rich Text Code Block Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "rich_text/rich_text_emoji_basic",
+      "description": "Rich Text Emoji Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "rich_text/rich_text_link_basic",
+      "description": "Rich Text Link Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "rich_text/rich_text_list_basic",
+      "description": "Rich Text List Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "rich_text/rich_text_list_ordered",
+      "description": "Rich Text List Ordered",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "rich_text/rich_text_quote_basic",
+      "description": "Rich Text Quote Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "rich_text/rich_text_section_basic",
+      "description": "Rich Text Section Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "rich_text/rich_text_user_basic",
+      "description": "Rich Text User Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "rich_text/rich_text_user_group_basic",
+      "description": "Rich Text User Group Basic",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "views/hometab_view",
+      "description": "Hometab View",
+      "slack_docs": "https://docs.slack.dev/surfaces/modals/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "views/modal_with_blocks",
+      "description": "Modal With Blocks",
+      "slack_docs": "https://docs.slack.dev/surfaces/modals/",
+      "since": "1.0.0"
+    },
+    {
+      "id": "views/modal_without_blocks",
+      "description": "Modal Without Blocks",
+      "slack_docs": "https://docs.slack.dev/surfaces/modals/",
+      "since": "1.1.0"
+    },
+    {
+      "id": "elements/number_input_bounds",
+      "description": "Number-input finite fractional bounds use strings on the wire.",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/number-input-element/",
+      "since": "1.3.0"
+    },
+    {
+      "id": "elements/number_input_scientific_bounds",
+      "description": "Exact decimal bounds: scientific_bounds",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/number-input-element/",
+      "since": "1.3.0"
+    },
+    {
+      "id": "elements/number_input_large_exponent",
+      "description": "Exact decimal bounds: large_exponent",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/block-elements/number-input-element/",
+      "since": "1.3.0"
+    },
+    {
+      "id": "messages/message_two_charts",
+      "description": "Message with the maximum two data visualization blocks",
+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block/",
+      "since": "1.3.0"
+    }
+  ]
+}
diff --git a/test/spec/model.json b/test/spec/model.json
new file mode 100644
--- /dev/null
+++ b/test/spec/model.json
@@ -0,0 +1,4604 @@
+{
+  "$comment": "Source of truth for the generated Java and C# Block Kit models and the Go builder documentation. After editing, run java/generator/generate_models.py --check-go, csharp/generator/generate_models.py, and go generate ./... in go/; see spec/SPEC.md.",
+  "interfaces": [
+    {
+      "package": "block",
+      "name": "Block",
+      "description": "A validated Slack layout block accepted directly by Slack's official Java SDK."
+    },
+    {
+      "package": "block",
+      "name": "ContextElement",
+      "description": "A value that can appear in a context block: an image element or a text object."
+    },
+    {
+      "package": "block",
+      "name": "ContextActionsElement",
+      "description": "A value that can appear in a context actions block: feedback buttons or an icon button."
+    },
+    {
+      "package": "block",
+      "name": "TableCell",
+      "description": "A value that can appear as a table block cell: raw text or a rich text block."
+    },
+    {
+      "package": "block",
+      "name": "DataTableCell",
+      "description": "A value that can appear as a data table cell: raw text, a raw number, or a rich text block."
+    },
+    {
+      "package": "element",
+      "name": "Element",
+      "description": "A validated interactive, visual, or input element nested inside a Slack block."
+    },
+    {
+      "package": "element",
+      "name": "InputElement",
+      "description": "An element that can be placed in an input block."
+    },
+    {
+      "package": "object",
+      "name": "Text",
+      "description": "A Slack text composition object: plain text or mrkdwn."
+    },
+    {
+      "package": "object",
+      "name": "Chart",
+      "description": "A chart that can be displayed by a data visualization block."
+    },
+    {
+      "package": "object",
+      "name": "RichTextBlockElement",
+      "description": "A top-level element of a rich text block: a section, list, preformatted block, or quote."
+    },
+    {
+      "package": "object",
+      "name": "RichTextSectionElement",
+      "description": "An inline rich text element: text, a link, an emoji, or a channel, user, or user group mention."
+    }
+  ],
+  "enums": [
+    {
+      "package": "block",
+      "name": "AlertLevel",
+      "description": "Severity levels supported by alert blocks.",
+      "constants": [
+        {
+          "name": "DEFAULT",
+          "wire": "default",
+          "description": "Neutral styling."
+        },
+        {
+          "name": "INFO",
+          "wire": "info",
+          "description": "Informational styling."
+        },
+        {
+          "name": "WARNING",
+          "wire": "warning",
+          "description": "Warning styling."
+        },
+        {
+          "name": "ERROR",
+          "wire": "error",
+          "description": "Error styling."
+        },
+        {
+          "name": "SUCCESS",
+          "wire": "success",
+          "description": "Success styling."
+        }
+      ]
+    },
+    {
+      "package": "block",
+      "name": "ContainerWidth",
+      "description": "Widths supported by Slack container blocks.",
+      "constants": [
+        {
+          "name": "NARROW",
+          "wire": "narrow",
+          "description": "A compact container."
+        },
+        {
+          "name": "STANDARD",
+          "wire": "standard",
+          "description": "Slack's standard container width."
+        },
+        {
+          "name": "WIDE",
+          "wire": "wide",
+          "description": "A wide container."
+        },
+        {
+          "name": "FULL",
+          "wire": "full",
+          "description": "The full available width."
+        }
+      ]
+    },
+    {
+      "package": "block",
+      "name": "TaskStatus",
+      "description": "States supported by task card blocks.",
+      "constants": [
+        {
+          "name": "PENDING",
+          "wire": "pending",
+          "description": "The task has not started."
+        },
+        {
+          "name": "IN_PROGRESS",
+          "wire": "in_progress",
+          "description": "The task is running."
+        },
+        {
+          "name": "COMPLETE",
+          "wire": "complete",
+          "description": "The task finished successfully."
+        },
+        {
+          "name": "ERROR",
+          "wire": "error",
+          "description": "The task failed."
+        }
+      ]
+    },
+    {
+      "package": "element",
+      "name": "ButtonStyle",
+      "description": "Visual emphasis for buttons and confirmation dialogs.",
+      "constants": [
+        {
+          "name": "PRIMARY",
+          "wire": "primary",
+          "description": "Green emphasis for the affirmative action."
+        },
+        {
+          "name": "DANGER",
+          "wire": "danger",
+          "description": "Red emphasis for destructive actions."
+        }
+      ]
+    },
+    {
+      "package": "element",
+      "name": "IconButtonIcon",
+      "description": "Icons supported by icon buttons.",
+      "constants": [
+        {
+          "name": "TRASH",
+          "wire": "trash",
+          "description": "A trash can, for delete actions."
+        }
+      ]
+    },
+    {
+      "package": "object",
+      "name": "ColumnAlign",
+      "description": "Horizontal alignment for table columns.",
+      "constants": [
+        {
+          "name": "LEFT",
+          "wire": "left",
+          "description": "Align cell content to the left."
+        },
+        {
+          "name": "CENTER",
+          "wire": "center",
+          "description": "Center cell content."
+        },
+        {
+          "name": "RIGHT",
+          "wire": "right",
+          "description": "Align cell content to the right."
+        }
+      ]
+    },
+    {
+      "package": "object",
+      "name": "RichTextListStyle",
+      "description": "Marker styles for rich text lists.",
+      "constants": [
+        {
+          "name": "BULLET",
+          "wire": "bullet",
+          "description": "An unordered, bulleted list."
+        },
+        {
+          "name": "ORDERED",
+          "wire": "ordered",
+          "description": "A numbered list."
+        }
+      ]
+    },
+    {
+      "package": "payload",
+      "name": "ResponseType",
+      "description": "Visibility of a slash-command or interaction response.",
+      "constants": [
+        {
+          "name": "IN_CHANNEL",
+          "wire": "in_channel",
+          "description": "Visible to everyone in the channel."
+        },
+        {
+          "name": "EPHEMERAL",
+          "wire": "ephemeral",
+          "description": "Visible only to the user who triggered the response."
+        }
+      ]
+    }
+  ],
+  "types": [
+    {
+      "name": "ActionsBlock",
+      "package": "block",
+      "goConstructor": "NewActionsBlock",
+      "wireType": "actions",
+      "description": "A block that holds interactive elements such as buttons and menus.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/actions-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "elements",
+          "goMethod": "Elements",
+          "wire": "elements",
+          "kind": "list",
+          "type": "Element",
+          "description": "Adds interactive elements in display order.",
+          "limits": "actions.elements",
+          "required": true
+        },
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "AlertBlock",
+      "package": "block",
+      "goConstructor": "NewAlertBlock",
+      "wireType": "alert",
+      "description": "A severity-labelled alert, available in modals.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/alert-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "text",
+          "goMethod": "Text",
+          "wire": "text",
+          "kind": "text",
+          "type": "Text",
+          "coerce": "mrkdwn",
+          "description": "Sets the alert message.",
+          "limits": "alert.text",
+          "required": true
+        },
+        {
+          "method": "level",
+          "goMethod": "Level",
+          "wire": "level",
+          "kind": "enum",
+          "type": "AlertLevel",
+          "description": "Sets the alert severity, which controls its color and icon."
+        },
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "CardBlock",
+      "package": "block",
+      "goConstructor": "NewCardBlock",
+      "wireType": "card",
+      "description": "A compact card with an image, text, and up to three buttons.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/card-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "heroImage",
+          "goMethod": "HeroImage",
+          "wire": "hero_image",
+          "kind": "object",
+          "type": "ImageElement",
+          "description": "Sets the image displayed prominently at the top of the card."
+        },
+        {
+          "method": "icon",
+          "goMethod": "Icon",
+          "wire": "icon",
+          "kind": "object",
+          "type": "ImageElement",
+          "description": "Sets the image displayed beside the title."
+        },
+        {
+          "method": "title",
+          "goMethod": "Title",
+          "wire": "title",
+          "kind": "text",
+          "type": "Text",
+          "coerce": "mrkdwn",
+          "description": "Sets the card title.",
+          "limits": "card.title"
+        },
+        {
+          "method": "subtitle",
+          "goMethod": "Subtitle",
+          "wire": "subtitle",
+          "kind": "text",
+          "type": "Text",
+          "coerce": "mrkdwn",
+          "description": "Sets the supporting text shown below the title.",
+          "limits": "card.subtitle"
+        },
+        {
+          "method": "body",
+          "goMethod": "Body",
+          "wire": "body",
+          "kind": "text",
+          "type": "Text",
+          "coerce": "mrkdwn",
+          "description": "Sets the main body text of the card.",
+          "limits": "card.body"
+        },
+        {
+          "method": "actions",
+          "goMethod": "Actions",
+          "wire": "actions",
+          "kind": "list",
+          "type": "ButtonElement",
+          "description": "Adds buttons displayed at the bottom of the card, in display order.",
+          "limits": "card.actions"
+        },
+        {
+          "method": "slackIcon",
+          "goMethod": "SlackIcon",
+          "wire": "slack_icon",
+          "kind": "object",
+          "type": "SlackIcon",
+          "description": "Sets a Slack-provided icon displayed beside the title. Cannot be combined with an image icon."
+        },
+        {
+          "method": "subtext",
+          "goMethod": "Subtext",
+          "wire": "subtext",
+          "kind": "text",
+          "type": "Text",
+          "coerce": "mrkdwn",
+          "description": "Sets the small print shown at the bottom of the card.",
+          "limits": "card.subtext"
+        },
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        }
+      ],
+      "rules": [
+        "Provide at least one of a hero image, title, actions, or body.",
+        "An image icon and a Slack icon cannot be combined."
+      ]
+    },
+    {
+      "name": "CarouselBlock",
+      "package": "block",
+      "goConstructor": "NewCarouselBlock",
+      "wireType": "carousel",
+      "description": "A horizontally scrolling set of cards, available in messages and App Home.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/carousel-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "elements",
+          "goMethod": "Elements",
+          "wire": "elements",
+          "kind": "list",
+          "type": "CardBlock",
+          "description": "Adds the cards in display order.",
+          "limits": "carousel.elements",
+          "required": true
+        },
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "ContainerBlock",
+      "package": "block",
+      "goConstructor": "NewContainerBlock",
+      "wireType": "container",
+      "description": "A titled group of blocks that can optionally collapse.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/container-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "childBlocks",
+          "goMethod": "ChildBlocks",
+          "wire": "child_blocks",
+          "kind": "list",
+          "type": "Block",
+          "description": "Adds the blocks grouped inside the container, in display order.",
+          "limits": "container.child_blocks",
+          "required": true
+        },
+        {
+          "method": "title",
+          "goMethod": "Title",
+          "wire": "title",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the container title. Provide either a title or a rich text title.",
+          "limits": "container.title"
+        },
+        {
+          "method": "richTextTitle",
+          "goMethod": "RichTextTitle",
+          "wire": "rich_text_title",
+          "kind": "object",
+          "type": "RichTextBlock",
+          "description": "Sets a rich text block used as the title in place of a plain-text title."
+        },
+        {
+          "method": "subtitle",
+          "goMethod": "Subtitle",
+          "wire": "subtitle",
+          "kind": "text",
+          "type": "Text",
+          "coerce": "mrkdwn",
+          "description": "Sets the supporting text shown below the title.",
+          "limits": "container.subtitle"
+        },
+        {
+          "method": "width",
+          "goMethod": "Width",
+          "wire": "width",
+          "kind": "enum",
+          "type": "ContainerWidth",
+          "description": "Sets the horizontal width of the container."
+        },
+        {
+          "method": "icon",
+          "goMethod": "Icon",
+          "wire": "icon",
+          "kind": "object",
+          "type": "ImageElement",
+          "description": "Sets the image displayed beside the title."
+        },
+        {
+          "method": "isCollapsible",
+          "goMethod": "IsCollapsible",
+          "wire": "is_collapsible",
+          "kind": "boolean",
+          "description": "Sets whether users can collapse and expand the container."
+        },
+        {
+          "method": "defaultCollapsed",
+          "goMethod": "DefaultCollapsed",
+          "wire": "default_collapsed",
+          "kind": "boolean",
+          "description": "Sets whether a collapsible container starts collapsed. Requires the container to be collapsible."
+        },
+        {
+          "method": "hasHeaderDivider",
+          "goMethod": "HasHeaderDivider",
+          "wire": "has_header_divider",
+          "kind": "boolean",
+          "description": "Sets whether Slack draws a divider below the container header. Only valid for non-collapsible containers."
+        },
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        }
+      ],
+      "rules": [
+        "Provide a title or a rich text title."
+      ]
+    },
+    {
+      "name": "ContextActionsBlock",
+      "package": "block",
+      "goConstructor": "NewContextActionsBlock",
+      "wireType": "context_actions",
+      "description": "A row of feedback or icon buttons, usually shown below AI-generated content.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/context-actions-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "elements",
+          "goMethod": "Elements",
+          "wire": "elements",
+          "kind": "list",
+          "type": "ContextActionsElement",
+          "description": "Adds feedback buttons or icon buttons in display order.",
+          "limits": "context_actions.elements",
+          "required": true
+        },
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "ContextBlock",
+      "package": "block",
+      "goConstructor": "NewContextBlock",
+      "wireType": "context",
+      "description": "A block of small, secondary images and text.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/context-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "elements",
+          "goMethod": "Elements",
+          "wire": "elements",
+          "kind": "list",
+          "type": "ContextElement",
+          "description": "Adds images and text in display order.",
+          "limits": "context.elements",
+          "required": true
+        },
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "DataTableBlock",
+      "package": "block",
+      "goConstructor": "NewDataTableBlock",
+      "wireType": "data_table",
+      "description": "A sortable, paginated table of raw text, numbers, and rich text.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/data-table-block",
+      "implements": [],
+      "defaults": {
+        "page_size": 5,
+        "row_header_column_index": 0
+      },
+      "fields": [
+        {
+          "method": "rows",
+          "goMethod": "Rows",
+          "wire": "rows",
+          "kind": "rows",
+          "type": "DataTableCell",
+          "description": "Adds complete table rows in display order. Every row must have the same number of cells.",
+          "limits": "data_table.rows",
+          "required": true
+        },
+        {
+          "method": "caption",
+          "goMethod": "Caption",
+          "wire": "caption",
+          "kind": "string",
+          "description": "Sets the caption that describes the table's contents.",
+          "required": true
+        },
+        {
+          "method": "pageSize",
+          "goMethod": "PageSize",
+          "wire": "page_size",
+          "kind": "int",
+          "description": "Sets how many rows Slack shows per page.",
+          "limits": "data_table.page_size"
+        },
+        {
+          "method": "rowHeaderColumnIndex",
+          "goMethod": "RowHeaderColumnIndex",
+          "wire": "row_header_column_index",
+          "kind": "int",
+          "description": "Sets the zero-based index of the column whose cells act as row headers.",
+          "limits": "data_table.row_header_column_index"
+        },
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        }
+      ],
+      "rules": [
+        "The first row is the header row and cannot contain rich text.",
+        "Every row must have the same number of cells, and all cell text together is limited to 20,000 characters."
+      ]
+    },
+    {
+      "name": "DataVisualizationBlock",
+      "package": "block",
+      "goConstructor": "NewDataVisualizationBlock",
+      "wireType": "data_visualization",
+      "description": "A titled pie, bar, area, or line chart.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "title",
+          "goMethod": "Title",
+          "wire": "title",
+          "kind": "string",
+          "description": "Sets the chart title.",
+          "limits": "data_visualization.title",
+          "required": true
+        },
+        {
+          "method": "chart",
+          "goMethod": "Chart",
+          "wire": "chart",
+          "kind": "object",
+          "type": "Chart",
+          "description": "Sets the chart to display.",
+          "required": true
+        },
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "DividerBlock",
+      "package": "block",
+      "goConstructor": "NewDividerBlock",
+      "wireType": "divider",
+      "description": "A horizontal rule that separates blocks.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/divider-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "FileBlock",
+      "package": "block",
+      "goConstructor": "NewFileBlock",
+      "wireType": "file",
+      "description": "A remote file previously added with the files.remote API.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/file-block",
+      "implements": [],
+      "defaults": {
+        "source": "remote"
+      },
+      "fields": [
+        {
+          "method": "externalId",
+          "goMethod": "ExternalID",
+          "wire": "external_id",
+          "kind": "string",
+          "description": "Sets the external identifier of a remote file previously added with the files.remote API.",
+          "required": true
+        },
+        {
+          "method": "source",
+          "goMethod": "Source",
+          "wire": "source",
+          "kind": "string",
+          "description": "Sets the file source. Slack currently supports only remote files.",
+          "required": true
+        },
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "HeaderBlock",
+      "package": "block",
+      "goConstructor": "NewHeaderBlock",
+      "wireType": "header",
+      "description": "Large, bold plain text that introduces a group of blocks.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/header-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "text",
+          "goMethod": "Text",
+          "wire": "text",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the header text, shown in a larger bold font.",
+          "limits": "header.text",
+          "required": true
+        },
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "ImageBlock",
+      "package": "block",
+      "goConstructor": "NewImageBlock",
+      "wireType": "image",
+      "description": "A standalone image with alternative text and an optional title.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/image-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "imageUrl",
+          "goMethod": "ImageURL",
+          "wire": "image_url",
+          "kind": "string",
+          "description": "Sets the publicly accessible URL of the image. Cannot be combined with a Slack file.",
+          "limits": "image.image_url"
+        },
+        {
+          "method": "slackFile",
+          "goMethod": "SlackFile",
+          "wire": "slack_file",
+          "kind": "object",
+          "type": "SlackFile",
+          "description": "Sets a file hosted in Slack as the image source. Cannot be combined with an image URL."
+        },
+        {
+          "method": "altText",
+          "goMethod": "AltText",
+          "wire": "alt_text",
+          "kind": "string",
+          "description": "Sets a plain-text summary of the image or video for assistive technology.",
+          "limits": "image.alt_text",
+          "required": true
+        },
+        {
+          "method": "title",
+          "goMethod": "Title",
+          "wire": "title",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the title shown above the image.",
+          "limits": "image.title"
+        },
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        }
+      ],
+      "rules": [
+        "Provide exactly one of an image URL or a Slack file."
+      ]
+    },
+    {
+      "name": "InputBlock",
+      "package": "block",
+      "goConstructor": "NewInputBlock",
+      "wireType": "input",
+      "description": "A labelled input that collects a value in a modal, App Home, or message.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/input-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "label",
+          "goMethod": "Label",
+          "wire": "label",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the label shown above the input.",
+          "limits": "input.label",
+          "required": true
+        },
+        {
+          "method": "element",
+          "goMethod": "Element",
+          "wire": "element",
+          "kind": "object",
+          "type": "InputElement",
+          "description": "Sets the input element that collects the user's value.",
+          "required": true
+        },
+        {
+          "method": "dispatchAction",
+          "goMethod": "DispatchAction",
+          "wire": "dispatch_action",
+          "kind": "boolean",
+          "description": "Sets whether changing the element sends a block_actions payload immediately."
+        },
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        },
+        {
+          "method": "hint",
+          "goMethod": "Hint",
+          "wire": "hint",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets helper text shown below the input.",
+          "limits": "input.hint"
+        },
+        {
+          "method": "optional",
+          "goMethod": "Optional",
+          "wire": "optional",
+          "kind": "boolean",
+          "description": "Sets whether the modal can be submitted when this input is empty."
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "MarkdownBlock",
+      "package": "block",
+      "goConstructor": "NewMarkdownBlock",
+      "wireType": "markdown",
+      "description": "Standard markdown rendered by Slack, intended for AI-generated content.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/markdown-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "text",
+          "goMethod": "Text",
+          "wire": "text",
+          "kind": "string",
+          "description": "Sets standard markdown content, which Slack renders directly.",
+          "limits": "markdown.text",
+          "required": true
+        },
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "PlanBlock",
+      "package": "block",
+      "goConstructor": "NewPlanBlock",
+      "wireType": "plan",
+      "description": "A titled sequence of task cards.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/plan-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "title",
+          "goMethod": "Title",
+          "wire": "title",
+          "kind": "string",
+          "description": "Sets the plan title.",
+          "required": true
+        },
+        {
+          "method": "tasks",
+          "goMethod": "Tasks",
+          "wire": "tasks",
+          "kind": "list",
+          "type": "TaskCardBlock",
+          "description": "Adds task cards in display order.",
+          "limits": "plan.tasks",
+          "required": true
+        },
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        }
+      ],
+      "rules": [
+        "Task IDs must be unique within a plan."
+      ]
+    },
+    {
+      "name": "RichTextBlock",
+      "package": "block",
+      "goConstructor": "NewRichTextBlock",
+      "wireType": "rich_text",
+      "description": "Formatted text built from sections, lists, preformatted blocks, and quotes.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block",
+      "implements": [
+        "TableCell",
+        "DataTableCell"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "elements",
+          "goMethod": "Elements",
+          "wire": "elements",
+          "kind": "list",
+          "type": "RichTextBlockElement",
+          "description": "Adds sections, lists, preformatted blocks, and quotes in display order.",
+          "required": true
+        },
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "SectionBlock",
+      "package": "block",
+      "goConstructor": "NewSectionBlock",
+      "wireType": "section",
+      "description": "Text, a grid of fields, or both, with an optional accessory element.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/section-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "text",
+          "goMethod": "Text",
+          "wire": "text",
+          "kind": "text",
+          "type": "Text",
+          "coerce": "mrkdwn",
+          "description": "Sets the main text of the section.",
+          "limits": "section.text"
+        },
+        {
+          "method": "fields",
+          "goMethod": "Fields",
+          "wire": "fields",
+          "kind": "textList",
+          "type": "Text",
+          "coerce": "mrkdwn",
+          "description": "Adds section fields, shown in a two-column grid, in display order.",
+          "limits": "section.fields"
+        },
+        {
+          "method": "accessory",
+          "goMethod": "Accessory",
+          "wire": "accessory",
+          "kind": "object",
+          "type": "Element",
+          "description": "Sets the element displayed beside the section text."
+        },
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        }
+      ],
+      "rules": [
+        "Provide text, fields, or both."
+      ]
+    },
+    {
+      "name": "TableBlock",
+      "package": "block",
+      "goConstructor": "NewTableBlock",
+      "wireType": "table",
+      "description": "A simple table of raw text and rich text cells.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/table-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "rows",
+          "goMethod": "Rows",
+          "wire": "rows",
+          "kind": "rows",
+          "type": "TableCell",
+          "description": "Adds complete table rows in display order.",
+          "limits": "table.rows",
+          "required": true
+        },
+        {
+          "method": "columnSettings",
+          "goMethod": "ColumnSettings",
+          "wire": "column_settings",
+          "kind": "list",
+          "type": "ColumnSettings",
+          "description": "Adds per-column settings in column order. Provide one entry for every column.",
+          "limits": "table.column_settings"
+        },
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "TaskCardBlock",
+      "package": "block",
+      "goConstructor": "NewTaskCardBlock",
+      "wireType": "task_card",
+      "description": "One task with its status, details, output, and sources.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/task-card-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "taskId",
+          "goMethod": "TaskID",
+          "wire": "task_id",
+          "kind": "string",
+          "description": "Sets the identifier of the task.",
+          "required": true
+        },
+        {
+          "method": "title",
+          "goMethod": "Title",
+          "wire": "title",
+          "kind": "string",
+          "description": "Sets the task title.",
+          "required": true
+        },
+        {
+          "method": "details",
+          "goMethod": "Details",
+          "wire": "details",
+          "kind": "object",
+          "type": "RichTextBlock",
+          "description": "Sets a rich text description of the task."
+        },
+        {
+          "method": "output",
+          "goMethod": "Output",
+          "wire": "output",
+          "kind": "object",
+          "type": "RichTextBlock",
+          "description": "Sets rich text describing the task's output."
+        },
+        {
+          "method": "sources",
+          "goMethod": "Sources",
+          "wire": "sources",
+          "kind": "list",
+          "type": "UrlSource",
+          "description": "Adds links to the sources the task used, in display order."
+        },
+        {
+          "method": "status",
+          "goMethod": "Status",
+          "wire": "status",
+          "kind": "enum",
+          "type": "TaskStatus",
+          "description": "Sets the task's current state.",
+          "required": true
+        },
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        }
+      ],
+      "rules": [
+        "A standalone task card's status cannot be pending; pending is only valid for plan tasks."
+      ]
+    },
+    {
+      "name": "VideoBlock",
+      "package": "block",
+      "goConstructor": "NewVideoBlock",
+      "wireType": "video",
+      "description": "An embedded video player.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/video-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "altText",
+          "goMethod": "AltText",
+          "wire": "alt_text",
+          "kind": "string",
+          "description": "Sets a plain-text summary of the image or video for assistive technology.",
+          "limits": "video.alt_text",
+          "required": true
+        },
+        {
+          "method": "thumbnailUrl",
+          "goMethod": "ThumbnailURL",
+          "wire": "thumbnail_url",
+          "kind": "string",
+          "description": "Sets the URL of the image shown before the video plays.",
+          "limits": "video.thumbnail_url",
+          "required": true
+        },
+        {
+          "method": "title",
+          "goMethod": "Title",
+          "wire": "title",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the video title.",
+          "limits": "video.title",
+          "required": true
+        },
+        {
+          "method": "videoUrl",
+          "goMethod": "VideoURL",
+          "wire": "video_url",
+          "kind": "string",
+          "description": "Sets the embeddable URL of the video. The domain must be listed in the app's unfurl domains.",
+          "limits": "video.video_url",
+          "required": true
+        },
+        {
+          "method": "blockId",
+          "goMethod": "BlockID",
+          "wire": "block_id",
+          "kind": "string",
+          "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",
+          "limits": "block_id"
+        },
+        {
+          "method": "authorName",
+          "goMethod": "AuthorName",
+          "wire": "author_name",
+          "kind": "string",
+          "description": "Sets the name of the video's author.",
+          "limits": "video.author_name"
+        },
+        {
+          "method": "description",
+          "goMethod": "Description",
+          "wire": "description",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the video description.",
+          "limits": "video.description"
+        },
+        {
+          "method": "providerIconUrl",
+          "goMethod": "ProviderIconURL",
+          "wire": "provider_icon_url",
+          "kind": "string",
+          "description": "Sets the URL of the video provider's icon.",
+          "limits": "video.provider_icon_url"
+        },
+        {
+          "method": "providerName",
+          "goMethod": "ProviderName",
+          "wire": "provider_name",
+          "kind": "string",
+          "description": "Sets the name of the video provider, such as YouTube.",
+          "limits": "video.provider_name"
+        },
+        {
+          "method": "titleUrl",
+          "goMethod": "TitleURL",
+          "wire": "title_url",
+          "kind": "string",
+          "description": "Sets the HTTPS URL opened when the title is clicked.",
+          "limits": "video.title_url"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "ButtonElement",
+      "package": "element",
+      "goConstructor": "NewButton",
+      "wireType": "button",
+      "description": "An interactive button that sends a payload or opens a URL.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/button-element",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "text",
+          "goMethod": "Text",
+          "wire": "text",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the button label.",
+          "limits": "button.text",
+          "required": true
+        },
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "url",
+          "goMethod": "URL",
+          "wire": "url",
+          "kind": "string",
+          "description": "Sets a URL opened in the user's browser when the button is clicked. Slack still sends an interaction payload.",
+          "limits": "button.url"
+        },
+        {
+          "method": "value",
+          "goMethod": "Value",
+          "wire": "value",
+          "kind": "string",
+          "description": "Sets the application-defined value sent in interaction payloads.",
+          "limits": "button.value"
+        },
+        {
+          "method": "style",
+          "goMethod": "Style",
+          "wire": "style",
+          "kind": "enum",
+          "type": "ButtonStyle",
+          "description": "Sets the button's emphasis. Omit it for the default neutral style."
+        },
+        {
+          "method": "confirm",
+          "goMethod": "Confirm",
+          "wire": "confirm",
+          "kind": "object",
+          "type": "Confirmation",
+          "description": "Sets a confirmation dialog shown before the action is sent."
+        },
+        {
+          "method": "accessibilityLabel",
+          "goMethod": "AccessibilityLabel",
+          "wire": "accessibility_label",
+          "kind": "string",
+          "description": "Sets the label read by screen readers in place of the visible text.",
+          "limits": "button.accessibility_label"
+        },
+        {
+          "method": "agentPrompt",
+          "goMethod": "AgentPrompt",
+          "wire": "agent_prompt",
+          "kind": "string",
+          "description": "Sets a prompt sent to Slackbot when AI is available to the clicking user. The app receives a block_actions payload in either case and must acknowledge it within three seconds.",
+          "limits": "button.agent_prompt"
+        },
+        {
+          "method": "agentPromptDisplay",
+          "goMethod": "AgentPromptDisplay",
+          "wire": "agent_prompt_display",
+          "kind": "string",
+          "description": "Sets the collapsed Slackbot message text in place of the full prompt. Requires agent_prompt. Omit this field to let Slack use the button text; the full prompt remains expandable."
+        }
+      ],
+      "rules": [
+        "A prompt display requires an agent prompt."
+      ]
+    },
+    {
+      "name": "ChannelMultiSelectElement",
+      "package": "element",
+      "goConstructor": "NewChannelMultiSelect",
+      "wireType": "multi_channels_select",
+      "description": "A menu for selecting multiple public channels.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "initialChannels",
+          "goMethod": "InitialChannels",
+          "wire": "initial_channels",
+          "kind": "stringList",
+          "description": "Adds channel IDs that are selected when the menu loads."
+        },
+        {
+          "method": "confirm",
+          "goMethod": "Confirm",
+          "wire": "confirm",
+          "kind": "object",
+          "type": "Confirmation",
+          "description": "Sets a confirmation dialog shown before the action is sent."
+        },
+        {
+          "method": "maxSelectedItems",
+          "goMethod": "MaxSelectedItems",
+          "wire": "max_selected_items",
+          "kind": "int",
+          "description": "Sets the maximum number of items a user can select.",
+          "limits": "multi_select.max_selected_items"
+        },
+        {
+          "method": "focusOnLoad",
+          "goMethod": "FocusOnLoad",
+          "wire": "focus_on_load",
+          "kind": "boolean",
+          "description": "Sets whether the element receives focus when the view opens. Only one element per view may do so."
+        },
+        {
+          "method": "placeholder",
+          "goMethod": "Placeholder",
+          "wire": "placeholder",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the placeholder text shown before a value is chosen.",
+          "limits": "select.placeholder"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "ChannelSelectElement",
+      "package": "element",
+      "goConstructor": "NewChannelSelect",
+      "wireType": "channels_select",
+      "description": "A menu for selecting one public channel.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "initialChannel",
+          "goMethod": "InitialChannel",
+          "wire": "initial_channel",
+          "kind": "string",
+          "description": "Sets the channel ID selected when the menu loads."
+        },
+        {
+          "method": "responseUrlEnabled",
+          "goMethod": "ResponseURLEnabled",
+          "wire": "response_url_enabled",
+          "kind": "boolean",
+          "description": "Sets whether the selected conversation receives a response URL in the view submission payload. Only valid in modals."
+        },
+        {
+          "method": "confirm",
+          "goMethod": "Confirm",
+          "wire": "confirm",
+          "kind": "object",
+          "type": "Confirmation",
+          "description": "Sets a confirmation dialog shown before the action is sent."
+        },
+        {
+          "method": "focusOnLoad",
+          "goMethod": "FocusOnLoad",
+          "wire": "focus_on_load",
+          "kind": "boolean",
+          "description": "Sets whether the element receives focus when the view opens. Only one element per view may do so."
+        },
+        {
+          "method": "placeholder",
+          "goMethod": "Placeholder",
+          "wire": "placeholder",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the placeholder text shown before a value is chosen.",
+          "limits": "select.placeholder"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "CheckboxesElement",
+      "package": "element",
+      "goConstructor": "NewCheckboxes",
+      "wireType": "checkboxes",
+      "description": "A group of checkboxes.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/checkboxes-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "options",
+          "goMethod": "Options",
+          "wire": "options",
+          "kind": "list",
+          "type": "Option",
+          "description": "Adds selectable options in display order.",
+          "limits": "checkboxes.options",
+          "required": true
+        },
+        {
+          "method": "initialOptions",
+          "goMethod": "InitialOptions",
+          "wire": "initial_options",
+          "kind": "list",
+          "type": "Option",
+          "description": "Adds options that are selected when the element loads. Each must match an option in the element."
+        },
+        {
+          "method": "confirm",
+          "goMethod": "Confirm",
+          "wire": "confirm",
+          "kind": "object",
+          "type": "Confirmation",
+          "description": "Sets a confirmation dialog shown before the action is sent."
+        },
+        {
+          "method": "focusOnLoad",
+          "goMethod": "FocusOnLoad",
+          "wire": "focus_on_load",
+          "kind": "boolean",
+          "description": "Sets whether the element receives focus when the view opens. Only one element per view may do so."
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "ConversationMultiSelectElement",
+      "package": "element",
+      "goConstructor": "NewConversationMultiSelect",
+      "wireType": "multi_conversations_select",
+      "description": "A menu for selecting multiple conversations.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "initialConversations",
+          "goMethod": "InitialConversations",
+          "wire": "initial_conversations",
+          "kind": "stringList",
+          "description": "Adds conversation IDs that are selected when the menu loads."
+        },
+        {
+          "method": "defaultToCurrentConversation",
+          "goMethod": "DefaultToCurrentConversation",
+          "wire": "default_to_current_conversation",
+          "kind": "boolean",
+          "description": "Sets whether the menu pre-selects the conversation the user is viewing."
+        },
+        {
+          "method": "filter",
+          "goMethod": "Filter",
+          "wire": "filter",
+          "kind": "object",
+          "type": "ConversationFilter",
+          "description": "Sets which conversation types the menu offers."
+        },
+        {
+          "method": "confirm",
+          "goMethod": "Confirm",
+          "wire": "confirm",
+          "kind": "object",
+          "type": "Confirmation",
+          "description": "Sets a confirmation dialog shown before the action is sent."
+        },
+        {
+          "method": "maxSelectedItems",
+          "goMethod": "MaxSelectedItems",
+          "wire": "max_selected_items",
+          "kind": "int",
+          "description": "Sets the maximum number of items a user can select.",
+          "limits": "multi_select.max_selected_items"
+        },
+        {
+          "method": "focusOnLoad",
+          "goMethod": "FocusOnLoad",
+          "wire": "focus_on_load",
+          "kind": "boolean",
+          "description": "Sets whether the element receives focus when the view opens. Only one element per view may do so."
+        },
+        {
+          "method": "placeholder",
+          "goMethod": "Placeholder",
+          "wire": "placeholder",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the placeholder text shown before a value is chosen.",
+          "limits": "select.placeholder"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "ConversationSelectElement",
+      "package": "element",
+      "goConstructor": "NewConversationSelect",
+      "wireType": "conversations_select",
+      "description": "A menu for selecting one conversation.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "initialConversation",
+          "goMethod": "InitialConversation",
+          "wire": "initial_conversation",
+          "kind": "string",
+          "description": "Sets the conversation ID selected when the menu loads."
+        },
+        {
+          "method": "defaultToCurrentConversation",
+          "goMethod": "DefaultToCurrentConversation",
+          "wire": "default_to_current_conversation",
+          "kind": "boolean",
+          "description": "Sets whether the menu pre-selects the conversation the user is viewing."
+        },
+        {
+          "method": "filter",
+          "goMethod": "Filter",
+          "wire": "filter",
+          "kind": "object",
+          "type": "ConversationFilter",
+          "description": "Sets which conversation types the menu offers."
+        },
+        {
+          "method": "responseUrlEnabled",
+          "goMethod": "ResponseURLEnabled",
+          "wire": "response_url_enabled",
+          "kind": "boolean",
+          "description": "Sets whether the selected conversation receives a response URL in the view submission payload. Only valid in modals."
+        },
+        {
+          "method": "confirm",
+          "goMethod": "Confirm",
+          "wire": "confirm",
+          "kind": "object",
+          "type": "Confirmation",
+          "description": "Sets a confirmation dialog shown before the action is sent."
+        },
+        {
+          "method": "focusOnLoad",
+          "goMethod": "FocusOnLoad",
+          "wire": "focus_on_load",
+          "kind": "boolean",
+          "description": "Sets whether the element receives focus when the view opens. Only one element per view may do so."
+        },
+        {
+          "method": "placeholder",
+          "goMethod": "Placeholder",
+          "wire": "placeholder",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the placeholder text shown before a value is chosen.",
+          "limits": "select.placeholder"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "DatePickerElement",
+      "package": "element",
+      "goConstructor": "NewDatePicker",
+      "wireType": "datepicker",
+      "description": "A calendar date picker.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/date-picker-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "initialDate",
+          "goMethod": "InitialDate",
+          "wire": "initial_date",
+          "kind": "string",
+          "description": "Sets the initially selected date, formatted as YYYY-MM-DD."
+        },
+        {
+          "method": "confirm",
+          "goMethod": "Confirm",
+          "wire": "confirm",
+          "kind": "object",
+          "type": "Confirmation",
+          "description": "Sets a confirmation dialog shown before the action is sent."
+        },
+        {
+          "method": "focusOnLoad",
+          "goMethod": "FocusOnLoad",
+          "wire": "focus_on_load",
+          "kind": "boolean",
+          "description": "Sets whether the element receives focus when the view opens. Only one element per view may do so."
+        },
+        {
+          "method": "placeholder",
+          "goMethod": "Placeholder",
+          "wire": "placeholder",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the placeholder text shown before a value is chosen.",
+          "limits": "date_picker.placeholder"
+        }
+      ],
+      "rules": [
+        "initial_date must have the YYYY-MM-DD shape. Calendar validity is application-owned; some language date parsers additionally reject impossible calendar dates."
+      ]
+    },
+    {
+      "name": "DateTimePickerElement",
+      "package": "element",
+      "goConstructor": "NewDateTimePicker",
+      "wireType": "datetimepicker",
+      "description": "A combined date and time picker.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/datetime-picker-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "initialDateTime",
+          "goMethod": "InitialDateTime",
+          "wire": "initial_date_time",
+          "kind": "long",
+          "description": "Sets the initially selected moment as a UNIX timestamp in seconds."
+        },
+        {
+          "method": "confirm",
+          "goMethod": "Confirm",
+          "wire": "confirm",
+          "kind": "object",
+          "type": "Confirmation",
+          "description": "Sets a confirmation dialog shown before the action is sent."
+        },
+        {
+          "method": "focusOnLoad",
+          "goMethod": "FocusOnLoad",
+          "wire": "focus_on_load",
+          "kind": "boolean",
+          "description": "Sets whether the element receives focus when the view opens. Only one element per view may do so."
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "EmailInputElement",
+      "package": "element",
+      "goConstructor": "NewEmailInput",
+      "wireType": "email_text_input",
+      "description": "A single-line input that accepts an email address.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/email-input-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "initialValue",
+          "goMethod": "InitialValue",
+          "wire": "initial_value",
+          "kind": "string",
+          "description": "Sets the value present when the input loads."
+        },
+        {
+          "method": "dispatchActionConfig",
+          "goMethod": "DispatchActionConfig",
+          "wire": "dispatch_action_config",
+          "kind": "object",
+          "type": "DispatchActionConfiguration",
+          "description": "Sets which user interactions send a block_actions payload."
+        },
+        {
+          "method": "focusOnLoad",
+          "goMethod": "FocusOnLoad",
+          "wire": "focus_on_load",
+          "kind": "boolean",
+          "description": "Sets whether the element receives focus when the view opens. Only one element per view may do so."
+        },
+        {
+          "method": "placeholder",
+          "goMethod": "Placeholder",
+          "wire": "placeholder",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the placeholder text shown before a value is chosen.",
+          "limits": "email_input.placeholder"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "ExternalMultiSelectElement",
+      "package": "element",
+      "goConstructor": "NewExternalMultiSelect",
+      "wireType": "multi_external_select",
+      "description": "A menu for selecting multiple options loaded from your app.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "minQueryLength",
+          "goMethod": "MinQueryLength",
+          "wire": "min_query_length",
+          "kind": "int",
+          "description": "Sets how many characters the user must type before Slack queries your options endpoint.",
+          "limits": "select.min_query_length"
+        },
+        {
+          "method": "initialOptions",
+          "goMethod": "InitialOptions",
+          "wire": "initial_options",
+          "kind": "list",
+          "type": "Option",
+          "description": "Adds options that are selected when the element loads. Each must match an option in the element."
+        },
+        {
+          "method": "confirm",
+          "goMethod": "Confirm",
+          "wire": "confirm",
+          "kind": "object",
+          "type": "Confirmation",
+          "description": "Sets a confirmation dialog shown before the action is sent."
+        },
+        {
+          "method": "maxSelectedItems",
+          "goMethod": "MaxSelectedItems",
+          "wire": "max_selected_items",
+          "kind": "int",
+          "description": "Sets the maximum number of items a user can select.",
+          "limits": "multi_select.max_selected_items"
+        },
+        {
+          "method": "focusOnLoad",
+          "goMethod": "FocusOnLoad",
+          "wire": "focus_on_load",
+          "kind": "boolean",
+          "description": "Deprecated compatibility parameter. Slack external multi-select menus do not support focus_on_load; omit it. Explicit true or false is rejected."
+        },
+        {
+          "method": "placeholder",
+          "goMethod": "Placeholder",
+          "wire": "placeholder",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the placeholder text shown before a value is chosen.",
+          "limits": "select.placeholder"
+        }
+      ],
+      "rules": [
+        "focus_on_load is unsupported and must be omitted, including explicit false."
+      ]
+    },
+    {
+      "name": "ExternalSelectElement",
+      "package": "element",
+      "goConstructor": "NewExternalSelect",
+      "wireType": "external_select",
+      "description": "A menu for selecting one option loaded from your app.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "minQueryLength",
+          "goMethod": "MinQueryLength",
+          "wire": "min_query_length",
+          "kind": "int",
+          "description": "Sets how many characters the user must type before Slack queries your options endpoint.",
+          "limits": "select.min_query_length"
+        },
+        {
+          "method": "initialOption",
+          "goMethod": "InitialOption",
+          "wire": "initial_option",
+          "kind": "object",
+          "type": "Option",
+          "description": "Sets the option selected when the element loads. It must match an option in the element."
+        },
+        {
+          "method": "confirm",
+          "goMethod": "Confirm",
+          "wire": "confirm",
+          "kind": "object",
+          "type": "Confirmation",
+          "description": "Sets a confirmation dialog shown before the action is sent."
+        },
+        {
+          "method": "focusOnLoad",
+          "goMethod": "FocusOnLoad",
+          "wire": "focus_on_load",
+          "kind": "boolean",
+          "description": "Sets whether the element receives focus when the view opens. Only one element per view may do so."
+        },
+        {
+          "method": "placeholder",
+          "goMethod": "Placeholder",
+          "wire": "placeholder",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the placeholder text shown before a value is chosen.",
+          "limits": "select.placeholder"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "FeedbackButtonsElement",
+      "package": "element",
+      "goConstructor": "NewFeedbackButtons",
+      "wireType": "feedback_buttons",
+      "description": "A pair of thumbs-up and thumbs-down feedback buttons.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/feedback-buttons-element",
+      "implements": [
+        "ContextActionsElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "positiveButton",
+          "goMethod": "PositiveButton",
+          "wire": "positive_button",
+          "kind": "object",
+          "type": "FeedbackButton",
+          "description": "Sets the button for positive feedback.",
+          "required": true
+        },
+        {
+          "method": "negativeButton",
+          "goMethod": "NegativeButton",
+          "wire": "negative_button",
+          "kind": "object",
+          "type": "FeedbackButton",
+          "description": "Sets the button for negative feedback.",
+          "required": true
+        },
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "FileInputElement",
+      "package": "element",
+      "goConstructor": "NewFileInput",
+      "wireType": "file_input",
+      "description": "An input that lets users upload files.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/file-input-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "filetypes",
+          "goMethod": "Filetypes",
+          "wire": "filetypes",
+          "kind": "stringList",
+          "description": "Adds accepted file extensions, such as pdf or png."
+        },
+        {
+          "method": "maxFiles",
+          "goMethod": "MaxFiles",
+          "wire": "max_files",
+          "kind": "int",
+          "description": "Sets the maximum number of files a user can upload.",
+          "limits": "file_input.max_files"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "IconButtonElement",
+      "package": "element",
+      "goConstructor": "NewIconButton",
+      "wireType": "icon_button",
+      "description": "A button that shows an icon instead of text.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/icon-button-element",
+      "implements": [
+        "ContextActionsElement"
+      ],
+      "defaults": {
+        "icon": "trash"
+      },
+      "fields": [
+        {
+          "method": "text",
+          "goMethod": "Text",
+          "wire": "text",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the text used as the button's accessible name.",
+          "required": true
+        },
+        {
+          "method": "icon",
+          "goMethod": "Icon",
+          "wire": "icon",
+          "kind": "enum",
+          "type": "IconButtonIcon",
+          "description": "Sets the icon shown on the button.",
+          "required": true
+        },
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "value",
+          "goMethod": "Value",
+          "wire": "value",
+          "kind": "string",
+          "description": "Sets the application-defined value sent in interaction payloads.",
+          "limits": "icon_button.value"
+        },
+        {
+          "method": "confirm",
+          "goMethod": "Confirm",
+          "wire": "confirm",
+          "kind": "object",
+          "type": "Confirmation",
+          "description": "Sets a confirmation dialog shown before the action is sent."
+        },
+        {
+          "method": "accessibilityLabel",
+          "goMethod": "AccessibilityLabel",
+          "wire": "accessibility_label",
+          "kind": "string",
+          "description": "Sets the label read by screen readers in place of the visible text.",
+          "limits": "icon_button.accessibility_label"
+        },
+        {
+          "method": "visibleToUserIds",
+          "goMethod": "VisibleToUserIDs",
+          "wire": "visible_to_user_ids",
+          "kind": "stringList",
+          "description": "Adds the IDs of users who can see this button.",
+          "limits": "icon_button.visible_to_user_ids"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "ImageElement",
+      "package": "element",
+      "goConstructor": "NewImageElement",
+      "wireType": "image",
+      "description": "An image displayed inside a section, context, or card.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/image-element",
+      "implements": [
+        "ContextElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "altText",
+          "goMethod": "AltText",
+          "wire": "alt_text",
+          "kind": "string",
+          "description": "Sets a plain-text summary of the image or video for assistive technology.",
+          "limits": "image_element.alt_text",
+          "required": true
+        },
+        {
+          "method": "imageUrl",
+          "goMethod": "ImageURL",
+          "wire": "image_url",
+          "kind": "string",
+          "description": "Sets the publicly accessible URL of the image. Cannot be combined with a Slack file.",
+          "limits": "image_element.image_url"
+        },
+        {
+          "method": "slackFile",
+          "goMethod": "SlackFile",
+          "wire": "slack_file",
+          "kind": "object",
+          "type": "SlackFile",
+          "description": "Sets a file hosted in Slack as the image source. Cannot be combined with an image URL."
+        }
+      ],
+      "rules": [
+        "Provide exactly one of an image URL or a Slack file."
+      ]
+    },
+    {
+      "name": "NumberInputElement",
+      "package": "element",
+      "goConstructor": "NewNumberInput",
+      "wireType": "number_input",
+      "description": "An input that accepts whole or decimal numbers.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/number-input-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "isDecimalAllowed",
+          "goMethod": "IsDecimalAllowed",
+          "wire": "is_decimal_allowed",
+          "kind": "boolean",
+          "description": "Sets whether the input accepts decimal numbers.",
+          "required": true
+        },
+        {
+          "method": "initialValue",
+          "goMethod": "InitialValue",
+          "wire": "initial_value",
+          "kind": "string",
+          "description": "Sets the number present when the input loads, as a string such as 42 or 3.5."
+        },
+        {
+          "method": "minValue",
+          "goMethod": "MinValue",
+          "wire": "min_value",
+          "kind": "string",
+          "description": "Sets the smallest accepted number. Sent as a finite numeric string; native numeric convenience arguments are converted to strings.",
+          "coerce": "decimal_string"
+        },
+        {
+          "method": "maxValue",
+          "goMethod": "MaxValue",
+          "wire": "max_value",
+          "kind": "string",
+          "description": "Sets the largest accepted number. Sent as a finite numeric string; native numeric convenience arguments are converted to strings.",
+          "coerce": "decimal_string"
+        },
+        {
+          "method": "dispatchActionConfig",
+          "goMethod": "DispatchActionConfig",
+          "wire": "dispatch_action_config",
+          "kind": "object",
+          "type": "DispatchActionConfiguration",
+          "description": "Sets which user interactions send a block_actions payload."
+        },
+        {
+          "method": "focusOnLoad",
+          "goMethod": "FocusOnLoad",
+          "wire": "focus_on_load",
+          "kind": "boolean",
+          "description": "Sets whether the element receives focus when the view opens. Only one element per view may do so."
+        },
+        {
+          "method": "placeholder",
+          "goMethod": "Placeholder",
+          "wire": "placeholder",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the placeholder text shown before a value is chosen.",
+          "limits": "number_input.placeholder"
+        }
+      ],
+      "rules": [
+        "The minimum value cannot exceed the maximum value."
+      ]
+    },
+    {
+      "name": "OverflowElement",
+      "package": "element",
+      "goConstructor": "NewOverflow",
+      "wireType": "overflow",
+      "description": "A compact menu of up to five options.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/overflow-menu-element",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "options",
+          "goMethod": "Options",
+          "wire": "options",
+          "kind": "list",
+          "type": "Option",
+          "description": "Adds selectable options in display order.",
+          "limits": "overflow.options",
+          "required": true
+        },
+        {
+          "method": "confirm",
+          "goMethod": "Confirm",
+          "wire": "confirm",
+          "kind": "object",
+          "type": "Confirmation",
+          "description": "Sets a confirmation dialog shown before the action is sent."
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "PlainTextInputElement",
+      "package": "element",
+      "goConstructor": "NewPlainTextInput",
+      "wireType": "plain_text_input",
+      "description": "A single-line or multi-line free-text input.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/plain-text-input-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "initialValue",
+          "goMethod": "InitialValue",
+          "wire": "initial_value",
+          "kind": "string",
+          "description": "Sets the value present when the input loads."
+        },
+        {
+          "method": "multiline",
+          "goMethod": "Multiline",
+          "wire": "multiline",
+          "kind": "boolean",
+          "description": "Sets whether the input is a multi-line text area."
+        },
+        {
+          "method": "minLength",
+          "goMethod": "MinLength",
+          "wire": "min_length",
+          "kind": "int",
+          "description": "Sets the minimum number of characters the user must enter.",
+          "limits": "plain_text_input.min_length"
+        },
+        {
+          "method": "maxLength",
+          "goMethod": "MaxLength",
+          "wire": "max_length",
+          "kind": "int",
+          "description": "Sets the maximum number of characters the user can enter.",
+          "limits": "plain_text_input.max_length"
+        },
+        {
+          "method": "dispatchActionConfig",
+          "goMethod": "DispatchActionConfig",
+          "wire": "dispatch_action_config",
+          "kind": "object",
+          "type": "DispatchActionConfiguration",
+          "description": "Sets which user interactions send a block_actions payload."
+        },
+        {
+          "method": "focusOnLoad",
+          "goMethod": "FocusOnLoad",
+          "wire": "focus_on_load",
+          "kind": "boolean",
+          "description": "Sets whether the element receives focus when the view opens. Only one element per view may do so."
+        },
+        {
+          "method": "placeholder",
+          "goMethod": "Placeholder",
+          "wire": "placeholder",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the placeholder text shown before a value is chosen.",
+          "limits": "plain_text_input.placeholder"
+        }
+      ],
+      "rules": [
+        "When both min_length and max_length are supplied, min_length must not exceed max_length; otherwise the input cannot be satisfied."
+      ]
+    },
+    {
+      "name": "RadioButtonsElement",
+      "package": "element",
+      "goConstructor": "NewRadioButtons",
+      "wireType": "radio_buttons",
+      "description": "A group of radio buttons.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/radio-button-group-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "options",
+          "goMethod": "Options",
+          "wire": "options",
+          "kind": "list",
+          "type": "Option",
+          "description": "Adds selectable options in display order.",
+          "limits": "radio_buttons.options",
+          "required": true
+        },
+        {
+          "method": "initialOption",
+          "goMethod": "InitialOption",
+          "wire": "initial_option",
+          "kind": "object",
+          "type": "Option",
+          "description": "Sets the option selected when the element loads. It must match an option in the element."
+        },
+        {
+          "method": "confirm",
+          "goMethod": "Confirm",
+          "wire": "confirm",
+          "kind": "object",
+          "type": "Confirmation",
+          "description": "Sets a confirmation dialog shown before the action is sent."
+        },
+        {
+          "method": "focusOnLoad",
+          "goMethod": "FocusOnLoad",
+          "wire": "focus_on_load",
+          "kind": "boolean",
+          "description": "Sets whether the element receives focus when the view opens. Only one element per view may do so."
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "RichTextInputElement",
+      "package": "element",
+      "goConstructor": "NewRichTextInput",
+      "wireType": "rich_text_input",
+      "description": "An input that accepts formatted rich text.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/rich-text-input-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id",
+          "required": true
+        },
+        {
+          "method": "initialValue",
+          "goMethod": "InitialValue",
+          "wire": "initial_value",
+          "kind": "object",
+          "type": "RichTextBlock",
+          "description": "Sets the rich text present when the input loads."
+        },
+        {
+          "method": "dispatchActionConfig",
+          "goMethod": "DispatchActionConfig",
+          "wire": "dispatch_action_config",
+          "kind": "object",
+          "type": "DispatchActionConfiguration",
+          "description": "Sets which user interactions send a block_actions payload."
+        },
+        {
+          "method": "focusOnLoad",
+          "goMethod": "FocusOnLoad",
+          "wire": "focus_on_load",
+          "kind": "boolean",
+          "description": "Sets whether the element receives focus when the view opens. Only one element per view may do so."
+        },
+        {
+          "method": "placeholder",
+          "goMethod": "Placeholder",
+          "wire": "placeholder",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the placeholder text shown before a value is chosen.",
+          "limits": "rich_text_input.placeholder"
+        },
+        {
+          "method": "minLines",
+          "goMethod": "MinLines",
+          "wire": "min_lines",
+          "kind": "int",
+          "description": "Sets the minimum visible height of the input, in lines.",
+          "limits": "rich_text_input.min_lines"
+        },
+        {
+          "method": "maxLines",
+          "goMethod": "MaxLines",
+          "wire": "max_lines",
+          "kind": "int",
+          "description": "Sets the maximum visible height of the input, in lines.",
+          "limits": "rich_text_input.max_lines"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "StaticMultiSelectElement",
+      "package": "element",
+      "goConstructor": "NewStaticMultiSelect",
+      "wireType": "multi_static_select",
+      "description": "A menu for selecting multiple options defined in the payload.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "options",
+          "goMethod": "Options",
+          "wire": "options",
+          "kind": "list",
+          "type": "Option",
+          "description": "Adds selectable options in display order.",
+          "limits": "select.options"
+        },
+        {
+          "method": "optionGroups",
+          "goMethod": "OptionGroups",
+          "wire": "option_groups",
+          "kind": "list",
+          "type": "OptionGroup",
+          "description": "Adds labelled groups of options in display order. Cannot be combined with ungrouped options.",
+          "limits": "select.option_groups"
+        },
+        {
+          "method": "initialOptions",
+          "goMethod": "InitialOptions",
+          "wire": "initial_options",
+          "kind": "list",
+          "type": "Option",
+          "description": "Adds options that are selected when the element loads. Each must match an option in the element."
+        },
+        {
+          "method": "confirm",
+          "goMethod": "Confirm",
+          "wire": "confirm",
+          "kind": "object",
+          "type": "Confirmation",
+          "description": "Sets a confirmation dialog shown before the action is sent."
+        },
+        {
+          "method": "maxSelectedItems",
+          "goMethod": "MaxSelectedItems",
+          "wire": "max_selected_items",
+          "kind": "int",
+          "description": "Sets the maximum number of items a user can select.",
+          "limits": "multi_select.max_selected_items"
+        },
+        {
+          "method": "focusOnLoad",
+          "goMethod": "FocusOnLoad",
+          "wire": "focus_on_load",
+          "kind": "boolean",
+          "description": "Sets whether the element receives focus when the view opens. Only one element per view may do so."
+        },
+        {
+          "method": "placeholder",
+          "goMethod": "Placeholder",
+          "wire": "placeholder",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the placeholder text shown before a value is chosen.",
+          "limits": "select.placeholder"
+        }
+      ],
+      "rules": [
+        "Use options or option groups, not both."
+      ]
+    },
+    {
+      "name": "StaticSelectElement",
+      "package": "element",
+      "goConstructor": "NewStaticSelect",
+      "wireType": "static_select",
+      "description": "A menu for selecting one option defined in the payload.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "options",
+          "goMethod": "Options",
+          "wire": "options",
+          "kind": "list",
+          "type": "Option",
+          "description": "Adds selectable options in display order.",
+          "limits": "select.options"
+        },
+        {
+          "method": "optionGroups",
+          "goMethod": "OptionGroups",
+          "wire": "option_groups",
+          "kind": "list",
+          "type": "OptionGroup",
+          "description": "Adds labelled groups of options in display order. Cannot be combined with ungrouped options.",
+          "limits": "select.option_groups"
+        },
+        {
+          "method": "initialOption",
+          "goMethod": "InitialOption",
+          "wire": "initial_option",
+          "kind": "object",
+          "type": "Option",
+          "description": "Sets the option selected when the element loads. It must match an option in the element."
+        },
+        {
+          "method": "confirm",
+          "goMethod": "Confirm",
+          "wire": "confirm",
+          "kind": "object",
+          "type": "Confirmation",
+          "description": "Sets a confirmation dialog shown before the action is sent."
+        },
+        {
+          "method": "focusOnLoad",
+          "goMethod": "FocusOnLoad",
+          "wire": "focus_on_load",
+          "kind": "boolean",
+          "description": "Sets whether the element receives focus when the view opens. Only one element per view may do so."
+        },
+        {
+          "method": "placeholder",
+          "goMethod": "Placeholder",
+          "wire": "placeholder",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the placeholder text shown before a value is chosen.",
+          "limits": "select.placeholder"
+        }
+      ],
+      "rules": [
+        "Use options or option groups, not both."
+      ]
+    },
+    {
+      "name": "TimePickerElement",
+      "package": "element",
+      "goConstructor": "NewTimePicker",
+      "wireType": "timepicker",
+      "description": "A time-of-day picker.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/time-picker-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "initialTime",
+          "goMethod": "InitialTime",
+          "wire": "initial_time",
+          "kind": "string",
+          "description": "Sets the initially selected time, formatted as HH:mm in 24-hour time."
+        },
+        {
+          "method": "timezone",
+          "goMethod": "Timezone",
+          "wire": "timezone",
+          "kind": "string",
+          "description": "Sets the IANA time zone used to display the time, such as Australia/Brisbane."
+        },
+        {
+          "method": "confirm",
+          "goMethod": "Confirm",
+          "wire": "confirm",
+          "kind": "object",
+          "type": "Confirmation",
+          "description": "Sets a confirmation dialog shown before the action is sent."
+        },
+        {
+          "method": "focusOnLoad",
+          "goMethod": "FocusOnLoad",
+          "wire": "focus_on_load",
+          "kind": "boolean",
+          "description": "Sets whether the element receives focus when the view opens. Only one element per view may do so."
+        },
+        {
+          "method": "placeholder",
+          "goMethod": "Placeholder",
+          "wire": "placeholder",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the placeholder text shown before a value is chosen.",
+          "limits": "time_picker.placeholder"
+        }
+      ],
+      "rules": [
+        "initial_time must be HH:mm in 24-hour time, from 00:00 through 23:59."
+      ]
+    },
+    {
+      "name": "UrlInputElement",
+      "package": "element",
+      "goConstructor": "NewURLInput",
+      "wireType": "url_text_input",
+      "description": "A single-line input that accepts a URL.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/url-input-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "initialValue",
+          "goMethod": "InitialValue",
+          "wire": "initial_value",
+          "kind": "string",
+          "description": "Sets the value present when the input loads."
+        },
+        {
+          "method": "dispatchActionConfig",
+          "goMethod": "DispatchActionConfig",
+          "wire": "dispatch_action_config",
+          "kind": "object",
+          "type": "DispatchActionConfiguration",
+          "description": "Sets which user interactions send a block_actions payload."
+        },
+        {
+          "method": "focusOnLoad",
+          "goMethod": "FocusOnLoad",
+          "wire": "focus_on_load",
+          "kind": "boolean",
+          "description": "Sets whether the element receives focus when the view opens. Only one element per view may do so."
+        },
+        {
+          "method": "placeholder",
+          "goMethod": "Placeholder",
+          "wire": "placeholder",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the placeholder text shown before a value is chosen.",
+          "limits": "url_input.placeholder"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "UserMultiSelectElement",
+      "package": "element",
+      "goConstructor": "NewUserMultiSelect",
+      "wireType": "multi_users_select",
+      "description": "A menu for selecting multiple workspace users.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "initialUsers",
+          "goMethod": "InitialUsers",
+          "wire": "initial_users",
+          "kind": "stringList",
+          "description": "Adds user IDs that are selected when the menu loads."
+        },
+        {
+          "method": "confirm",
+          "goMethod": "Confirm",
+          "wire": "confirm",
+          "kind": "object",
+          "type": "Confirmation",
+          "description": "Sets a confirmation dialog shown before the action is sent."
+        },
+        {
+          "method": "maxSelectedItems",
+          "goMethod": "MaxSelectedItems",
+          "wire": "max_selected_items",
+          "kind": "int",
+          "description": "Sets the maximum number of items a user can select.",
+          "limits": "multi_select.max_selected_items"
+        },
+        {
+          "method": "focusOnLoad",
+          "goMethod": "FocusOnLoad",
+          "wire": "focus_on_load",
+          "kind": "boolean",
+          "description": "Sets whether the element receives focus when the view opens. Only one element per view may do so."
+        },
+        {
+          "method": "placeholder",
+          "goMethod": "Placeholder",
+          "wire": "placeholder",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the placeholder text shown before a value is chosen.",
+          "limits": "select.placeholder"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "UserSelectElement",
+      "package": "element",
+      "goConstructor": "NewUserSelect",
+      "wireType": "users_select",
+      "description": "A menu for selecting one workspace user.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element",
+      "implements": [
+        "InputElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id"
+        },
+        {
+          "method": "initialUser",
+          "goMethod": "InitialUser",
+          "wire": "initial_user",
+          "kind": "string",
+          "description": "Sets the user ID selected when the menu loads."
+        },
+        {
+          "method": "confirm",
+          "goMethod": "Confirm",
+          "wire": "confirm",
+          "kind": "object",
+          "type": "Confirmation",
+          "description": "Sets a confirmation dialog shown before the action is sent."
+        },
+        {
+          "method": "focusOnLoad",
+          "goMethod": "FocusOnLoad",
+          "wire": "focus_on_load",
+          "kind": "boolean",
+          "description": "Sets whether the element receives focus when the view opens. Only one element per view may do so."
+        },
+        {
+          "method": "placeholder",
+          "goMethod": "Placeholder",
+          "wire": "placeholder",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the placeholder text shown before a value is chosen.",
+          "limits": "select.placeholder"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "WorkflowButtonElement",
+      "package": "element",
+      "goConstructor": "NewWorkflowButton",
+      "wireType": "workflow_button",
+      "description": "A button that starts a workflow through a link trigger.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/workflow-button-element",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "text",
+          "goMethod": "Text",
+          "wire": "text",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the button label.",
+          "limits": "workflow_button.text",
+          "required": true
+        },
+        {
+          "method": "workflow",
+          "goMethod": "Workflow",
+          "wire": "workflow",
+          "kind": "object",
+          "type": "Workflow",
+          "description": "Sets the workflow started when the button is clicked.",
+          "required": true
+        },
+        {
+          "method": "actionId",
+          "goMethod": "ActionID",
+          "wire": "action_id",
+          "kind": "string",
+          "description": "Sets the identifier Slack returns in interaction payloads when a user acts on this element. It must be unique among the elements of its block.",
+          "limits": "action_id",
+          "required": true
+        },
+        {
+          "method": "confirm",
+          "goMethod": "Confirm",
+          "wire": "confirm",
+          "kind": "object",
+          "type": "Confirmation",
+          "description": "Sets a confirmation dialog shown before the action is sent."
+        },
+        {
+          "method": "style",
+          "goMethod": "Style",
+          "wire": "style",
+          "kind": "enum",
+          "type": "ButtonStyle",
+          "description": "Sets the button's emphasis. Omit it for the default neutral style."
+        },
+        {
+          "method": "accessibilityLabel",
+          "goMethod": "AccessibilityLabel",
+          "wire": "accessibility_label",
+          "kind": "string",
+          "description": "Sets the label read by screen readers in place of the visible text.",
+          "limits": "workflow_button.accessibility_label"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "AreaChart",
+      "package": "object",
+      "goConstructor": "NewAreaChart",
+      "wireType": "area",
+      "description": "An area chart for a data visualization block.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block",
+      "implements": [
+        "Chart"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "series",
+          "goMethod": "Series",
+          "wire": "series",
+          "kind": "list",
+          "type": "DataSeries",
+          "description": "Adds data series in display order. Series names must be unique.",
+          "limits": "data_visualization.series",
+          "required": true
+        },
+        {
+          "method": "axisConfig",
+          "goMethod": "AxisConfig",
+          "wire": "axis_config",
+          "kind": "object",
+          "type": "AxisConfig",
+          "description": "Sets the axis categories and labels. Every series needs exactly one point per category.",
+          "required": true
+        }
+      ],
+      "rules": [
+        "Every series needs exactly one data point for each axis category."
+      ]
+    },
+    {
+      "name": "AxisConfig",
+      "package": "object",
+      "goConstructor": "NewAxisConfig",
+      "wireType": "",
+      "description": "The categories and labels shared by bar, area, and line chart axes.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "categories",
+          "goMethod": "Categories",
+          "wire": "categories",
+          "kind": "stringList",
+          "description": "Adds axis categories in display order. Labels must be unique.",
+          "limits": "data_visualization.categories",
+          "required": true
+        },
+        {
+          "method": "xLabel",
+          "goMethod": "XLabel",
+          "wire": "x_label",
+          "kind": "string",
+          "description": "Sets the horizontal axis label.",
+          "limits": "data_visualization.axis_label"
+        },
+        {
+          "method": "yLabel",
+          "goMethod": "YLabel",
+          "wire": "y_label",
+          "kind": "string",
+          "description": "Sets the vertical axis label.",
+          "limits": "data_visualization.axis_label"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "BarChart",
+      "package": "object",
+      "goConstructor": "NewBarChart",
+      "wireType": "bar",
+      "description": "A bar chart for a data visualization block.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block",
+      "implements": [
+        "Chart"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "series",
+          "goMethod": "Series",
+          "wire": "series",
+          "kind": "list",
+          "type": "DataSeries",
+          "description": "Adds data series in display order. Series names must be unique.",
+          "limits": "data_visualization.series",
+          "required": true
+        },
+        {
+          "method": "axisConfig",
+          "goMethod": "AxisConfig",
+          "wire": "axis_config",
+          "kind": "object",
+          "type": "AxisConfig",
+          "description": "Sets the axis categories and labels. Every series needs exactly one point per category.",
+          "required": true
+        }
+      ],
+      "rules": [
+        "Every series needs exactly one data point for each axis category."
+      ]
+    },
+    {
+      "name": "ChartSegment",
+      "package": "object",
+      "goConstructor": "NewChartSegment",
+      "wireType": "",
+      "description": "One labelled segment of a pie chart.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "label",
+          "goMethod": "Label",
+          "wire": "label",
+          "kind": "string",
+          "description": "Sets the segment label.",
+          "limits": "data_visualization.segment.label",
+          "required": true
+        },
+        {
+          "method": "value",
+          "goMethod": "Value",
+          "wire": "value",
+          "kind": "number",
+          "description": "Sets the segment's size. Must be greater than zero.",
+          "required": true
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "ColumnSettings",
+      "package": "object",
+      "goConstructor": "NewColumnSettings",
+      "wireType": "",
+      "description": "Alignment and wrapping settings for one table column.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/table-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "align",
+          "goMethod": "Align",
+          "wire": "align",
+          "kind": "enum",
+          "type": "ColumnAlign",
+          "description": "Sets how cell content in the column is aligned."
+        },
+        {
+          "method": "isWrapped",
+          "goMethod": "IsWrapped",
+          "wire": "is_wrapped",
+          "kind": "boolean",
+          "description": "Sets whether long cell content wraps instead of being truncated."
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "Confirmation",
+      "package": "object",
+      "goConstructor": "NewConfirmation",
+      "wireType": "",
+      "description": "A dialog that asks users to confirm an action before it is sent.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/composition-objects/confirmation-dialog-object",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "title",
+          "goMethod": "Title",
+          "wire": "title",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the dialog title.",
+          "limits": "confirmation.title",
+          "required": true
+        },
+        {
+          "method": "text",
+          "goMethod": "Text",
+          "wire": "text",
+          "kind": "text",
+          "type": "Text",
+          "coerce": "mrkdwn",
+          "description": "Sets the explanatory text in the dialog body.",
+          "limits": "confirmation.text",
+          "required": true
+        },
+        {
+          "method": "confirm",
+          "goMethod": "Confirm",
+          "wire": "confirm",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the label of the button that confirms the action.",
+          "limits": "confirmation.confirm",
+          "required": true
+        },
+        {
+          "method": "deny",
+          "goMethod": "Deny",
+          "wire": "deny",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the label of the button that cancels the action.",
+          "limits": "confirmation.deny",
+          "required": true
+        },
+        {
+          "method": "style",
+          "goMethod": "Style",
+          "wire": "style",
+          "kind": "enum",
+          "type": "ButtonStyle",
+          "description": "Sets the emphasis of the confirm button."
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "ConversationFilter",
+      "package": "object",
+      "goConstructor": "NewConversationFilter",
+      "wireType": "",
+      "description": "Limits which conversations a conversation menu offers.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/composition-objects/conversation-filter-object",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "include",
+          "goMethod": "Include",
+          "wire": "include",
+          "kind": "stringList",
+          "description": "Adds conversation types to offer: im, mpim, private, or public.",
+          "limits": "conversation_filter.include"
+        },
+        {
+          "method": "excludeExternalSharedChannels",
+          "goMethod": "ExcludeExternalSharedChannels",
+          "wire": "exclude_external_shared_channels",
+          "kind": "boolean",
+          "description": "Sets whether externally shared channels are excluded."
+        },
+        {
+          "method": "excludeBotUsers",
+          "goMethod": "ExcludeBotUsers",
+          "wire": "exclude_bot_users",
+          "kind": "boolean",
+          "description": "Sets whether bot users are excluded."
+        }
+      ],
+      "rules": [
+        "Set at least one filter.",
+        "Include only im, mpim, private, and public."
+      ]
+    },
+    {
+      "name": "DataPoint",
+      "package": "object",
+      "goConstructor": "NewDataPoint",
+      "wireType": "",
+      "description": "One value of a chart series, keyed by axis category.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "label",
+          "goMethod": "Label",
+          "wire": "label",
+          "kind": "string",
+          "description": "Sets the axis category this point belongs to.",
+          "limits": "data_visualization.point_label",
+          "required": true
+        },
+        {
+          "method": "value",
+          "goMethod": "Value",
+          "wire": "value",
+          "kind": "number",
+          "description": "Sets the point's value.",
+          "required": true
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "DataSeries",
+      "package": "object",
+      "goConstructor": "NewDataSeries",
+      "wireType": "",
+      "description": "A named series of chart data points.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "name",
+          "goMethod": "Name",
+          "wire": "name",
+          "kind": "string",
+          "description": "Sets the series name shown in the legend.",
+          "limits": "data_visualization.series_name",
+          "required": true
+        },
+        {
+          "method": "data",
+          "goMethod": "Data",
+          "wire": "data",
+          "kind": "list",
+          "type": "DataPoint",
+          "description": "Adds data points in axis-category order.",
+          "limits": "data_visualization.data",
+          "required": true
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "DispatchActionConfiguration",
+      "package": "object",
+      "goConstructor": "NewDispatchActionConfiguration",
+      "wireType": "",
+      "description": "Chooses which interactions with an input send a block_actions payload.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/composition-objects/dispatch-action-configuration-object",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "triggerActionsOn",
+          "goMethod": "TriggerActionsOn",
+          "wire": "trigger_actions_on",
+          "kind": "stringList",
+          "description": "Adds the interactions that send a payload: on_enter_pressed or on_character_entered.",
+          "limits": "dispatch_action_configuration.trigger_actions_on"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "FeedbackButton",
+      "package": "object",
+      "goConstructor": "NewFeedbackButton",
+      "wireType": "",
+      "description": "One button of a feedback buttons element.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/block-elements/feedback-buttons-element",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "text",
+          "goMethod": "Text",
+          "wire": "text",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the button label.",
+          "limits": "feedback_button.text",
+          "required": true
+        },
+        {
+          "method": "value",
+          "goMethod": "Value",
+          "wire": "value",
+          "kind": "string",
+          "description": "Sets the application-defined value sent in interaction payloads.",
+          "limits": "feedback_button.value",
+          "required": true
+        },
+        {
+          "method": "accessibilityLabel",
+          "goMethod": "AccessibilityLabel",
+          "wire": "accessibility_label",
+          "kind": "string",
+          "description": "Sets the label read by screen readers in place of the visible text.",
+          "limits": "feedback_button.accessibility_label"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "InputParameter",
+      "package": "object",
+      "goConstructor": "NewInputParameter",
+      "wireType": "",
+      "description": "A named value passed to a workflow trigger.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/composition-objects/trigger-object",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "name",
+          "goMethod": "Name",
+          "wire": "name",
+          "kind": "string",
+          "description": "Sets the workflow input parameter name.",
+          "required": true
+        },
+        {
+          "method": "value",
+          "goMethod": "Value",
+          "wire": "value",
+          "kind": "string",
+          "description": "Sets the value passed to the workflow input.",
+          "required": true
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "LineChart",
+      "package": "object",
+      "goConstructor": "NewLineChart",
+      "wireType": "line",
+      "description": "A line chart for a data visualization block.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block",
+      "implements": [
+        "Chart"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "series",
+          "goMethod": "Series",
+          "wire": "series",
+          "kind": "list",
+          "type": "DataSeries",
+          "description": "Adds data series in display order. Series names must be unique.",
+          "limits": "data_visualization.series",
+          "required": true
+        },
+        {
+          "method": "axisConfig",
+          "goMethod": "AxisConfig",
+          "wire": "axis_config",
+          "kind": "object",
+          "type": "AxisConfig",
+          "description": "Sets the axis categories and labels. Every series needs exactly one point per category.",
+          "required": true
+        }
+      ],
+      "rules": [
+        "Every series needs exactly one data point for each axis category."
+      ]
+    },
+    {
+      "name": "MarkdownText",
+      "package": "object",
+      "goConstructor": "NewMarkdown",
+      "wireType": "mrkdwn",
+      "description": "A mrkdwn text composition object.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/composition-objects/text-object",
+      "implements": [
+        "Text",
+        "ContextElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "text",
+          "goMethod": "Text",
+          "wire": "text",
+          "kind": "string",
+          "description": "Sets the mrkdwn-formatted text content.",
+          "required": true
+        },
+        {
+          "method": "verbatim",
+          "goMethod": "Verbatim",
+          "wire": "verbatim",
+          "kind": "boolean",
+          "description": "Sets whether Slack leaves URLs, mentions, and channel names unlinked."
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "Option",
+      "package": "object",
+      "goConstructor": "NewOption",
+      "wireType": "",
+      "description": "A selectable option for menus, checkboxes, radio buttons, and overflow menus.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/composition-objects/option-object",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "text",
+          "goMethod": "Text",
+          "wire": "text",
+          "kind": "text",
+          "type": "Text",
+          "coerce": "plain_text",
+          "description": "Sets the option label shown to users.",
+          "limits": "option.text",
+          "required": true
+        },
+        {
+          "method": "value",
+          "goMethod": "Value",
+          "wire": "value",
+          "kind": "string",
+          "description": "Sets the value sent in interaction payloads when this option is chosen.",
+          "limits": "option.value",
+          "required": true
+        },
+        {
+          "method": "description",
+          "goMethod": "Description",
+          "wire": "description",
+          "kind": "text",
+          "type": "Text",
+          "coerce": "plain_text",
+          "description": "Sets supporting text shown below the label. Only rendered by checkboxes and radio buttons.",
+          "limits": "option.description"
+        },
+        {
+          "method": "url",
+          "goMethod": "URL",
+          "wire": "url",
+          "kind": "string",
+          "description": "Sets a URL loaded in the user's browser when an overflow menu option is chosen.",
+          "limits": "option.url"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "OptionGroup",
+      "package": "object",
+      "goConstructor": "NewOptionGroup",
+      "wireType": "",
+      "description": "A labelled group of options in a static select menu.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/composition-objects/option-group-object",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "label",
+          "goMethod": "Label",
+          "wire": "label",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the group heading.",
+          "limits": "option_group.label",
+          "required": true
+        },
+        {
+          "method": "options",
+          "goMethod": "Options",
+          "wire": "options",
+          "kind": "list",
+          "type": "Option",
+          "description": "Adds selectable options in display order.",
+          "limits": "option_group.options",
+          "required": true
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "PieChart",
+      "package": "object",
+      "goConstructor": "NewPieChart",
+      "wireType": "pie",
+      "description": "A pie chart for a data visualization block.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block",
+      "implements": [
+        "Chart"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "segments",
+          "goMethod": "Segments",
+          "wire": "segments",
+          "kind": "list",
+          "type": "ChartSegment",
+          "description": "Adds pie segments in display order.",
+          "limits": "data_visualization.segments",
+          "required": true
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "PlainText",
+      "package": "object",
+      "goConstructor": "NewPlainText",
+      "wireType": "plain_text",
+      "description": "A plain-text composition object.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/composition-objects/text-object",
+      "implements": [
+        "Text",
+        "ContextElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "text",
+          "goMethod": "Text",
+          "wire": "text",
+          "kind": "string",
+          "description": "Sets the text content.",
+          "required": true
+        },
+        {
+          "method": "emoji",
+          "goMethod": "Emoji",
+          "wire": "emoji",
+          "kind": "boolean",
+          "description": "Sets whether emoji shortcodes such as :tada: are rendered as emoji."
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "RawNumber",
+      "package": "object",
+      "goConstructor": "NewRawNumber",
+      "wireType": "raw_number",
+      "description": "A numeric data table cell with display text.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/data-table-block",
+      "implements": [
+        "DataTableCell"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "value",
+          "goMethod": "Value",
+          "wire": "value",
+          "kind": "number",
+          "description": "Sets the numeric value used for sorting.",
+          "required": true
+        },
+        {
+          "method": "text",
+          "goMethod": "Text",
+          "wire": "text",
+          "kind": "string",
+          "description": "Sets the formatted text displayed for the number, such as 1,234.5.",
+          "required": true
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "RawText",
+      "package": "object",
+      "goConstructor": "NewRawText",
+      "wireType": "raw_text",
+      "description": "An unformatted table cell.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/table-block",
+      "implements": [
+        "TableCell",
+        "DataTableCell"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "text",
+          "goMethod": "Text",
+          "wire": "text",
+          "kind": "string",
+          "description": "Sets the cell text.",
+          "required": true
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "RichTextChannel",
+      "package": "object",
+      "goConstructor": "NewRichTextChannel",
+      "wireType": "channel",
+      "description": "A channel mention inside rich text.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block",
+      "implements": [
+        "RichTextSectionElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "channelId",
+          "goMethod": "ChannelID",
+          "wire": "channel_id",
+          "kind": "string",
+          "description": "Sets the ID of the mentioned channel.",
+          "required": true
+        },
+        {
+          "method": "style",
+          "goMethod": "Style",
+          "wire": "style",
+          "kind": "style",
+          "flags": [
+            "bold",
+            "italic",
+            "strike",
+            "highlight",
+            "client_highlight",
+            "unlink"
+          ],
+          "description": "Sets the mention style."
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "RichTextCodeBlock",
+      "package": "object",
+      "goConstructor": "NewRichTextCodeBlock",
+      "wireType": "rich_text_preformatted",
+      "description": "A preformatted code block inside rich text.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block",
+      "implements": [
+        "RichTextBlockElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "elements",
+          "goMethod": "Elements",
+          "wire": "elements",
+          "kind": "list",
+          "type": "RichTextSectionElement",
+          "description": "Adds inline rich text elements in display order.",
+          "required": true
+        },
+        {
+          "method": "border",
+          "goMethod": "Border",
+          "wire": "border",
+          "kind": "int",
+          "description": "Sets the width of the left border, in pixels.",
+          "limits": "rich_text_preformatted.border"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "RichTextEmoji",
+      "package": "object",
+      "goConstructor": "NewRichTextEmoji",
+      "wireType": "emoji",
+      "description": "An emoji inside rich text.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block",
+      "implements": [
+        "RichTextSectionElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "name",
+          "goMethod": "Name",
+          "wire": "name",
+          "kind": "string",
+          "description": "Sets the emoji shortcode name, without colons.",
+          "required": true
+        },
+        {
+          "method": "skinTone",
+          "goMethod": "SkinTone",
+          "wire": "skin_tone",
+          "kind": "int",
+          "description": "Sets the emoji skin tone, from 1 to 6."
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "RichTextLink",
+      "package": "object",
+      "goConstructor": "NewRichTextLink",
+      "wireType": "link",
+      "description": "A hyperlink inside rich text.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block",
+      "implements": [
+        "RichTextSectionElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "url",
+          "goMethod": "URL",
+          "wire": "url",
+          "kind": "string",
+          "description": "Sets the link target.",
+          "required": true
+        },
+        {
+          "method": "text",
+          "goMethod": "Text",
+          "wire": "text",
+          "kind": "string",
+          "description": "Sets the visible link text. Slack shows the URL when omitted."
+        },
+        {
+          "method": "style",
+          "goMethod": "Style",
+          "wire": "style",
+          "kind": "style",
+          "flags": [
+            "bold",
+            "italic",
+            "strike",
+            "code"
+          ],
+          "description": "Sets the visual style."
+        },
+        {
+          "method": "unsafe",
+          "goMethod": "Unsafe",
+          "wire": "unsafe",
+          "kind": "boolean",
+          "description": "Sets whether Slack treats the link as unsafe."
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "RichTextList",
+      "package": "object",
+      "goConstructor": "NewRichTextList",
+      "wireType": "rich_text_list",
+      "description": "A bulleted or numbered list inside rich text.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block",
+      "implements": [
+        "RichTextBlockElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "style",
+          "goMethod": "Style",
+          "wire": "style",
+          "kind": "enum",
+          "type": "RichTextListStyle",
+          "description": "Sets whether the list is bulleted or numbered.",
+          "required": true
+        },
+        {
+          "method": "elements",
+          "goMethod": "Elements",
+          "wire": "elements",
+          "kind": "list",
+          "type": "RichTextSection",
+          "description": "Adds list items, one rich text section per item.",
+          "required": true
+        },
+        {
+          "method": "indent",
+          "goMethod": "Indent",
+          "wire": "indent",
+          "kind": "int",
+          "description": "Sets the list's indentation level.",
+          "limits": "rich_text_list.indent"
+        },
+        {
+          "method": "offset",
+          "goMethod": "Offset",
+          "wire": "offset",
+          "kind": "int",
+          "description": "Sets the number of items to skip when numbering an ordered list.",
+          "limits": "rich_text_list.offset"
+        },
+        {
+          "method": "border",
+          "goMethod": "Border",
+          "wire": "border",
+          "kind": "int",
+          "description": "Sets the width of the left border, in pixels.",
+          "limits": "rich_text_list.border"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "RichTextQuote",
+      "package": "object",
+      "goConstructor": "NewRichTextQuote",
+      "wireType": "rich_text_quote",
+      "description": "A quotation inside rich text.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block",
+      "implements": [
+        "RichTextBlockElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "elements",
+          "goMethod": "Elements",
+          "wire": "elements",
+          "kind": "list",
+          "type": "RichTextSectionElement",
+          "description": "Adds inline rich text elements in display order.",
+          "required": true
+        },
+        {
+          "method": "border",
+          "goMethod": "Border",
+          "wire": "border",
+          "kind": "int",
+          "description": "Sets the width of the left border, in pixels.",
+          "limits": "rich_text_quote.border"
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "RichTextSection",
+      "package": "object",
+      "goConstructor": "NewRichTextSection",
+      "wireType": "rich_text_section",
+      "description": "A paragraph of inline rich text elements.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block",
+      "implements": [
+        "RichTextBlockElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "elements",
+          "goMethod": "Elements",
+          "wire": "elements",
+          "kind": "list",
+          "type": "RichTextSectionElement",
+          "description": "Adds inline rich text elements in display order.",
+          "required": true
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "RichTextText",
+      "package": "object",
+      "goConstructor": "NewRichText",
+      "wireType": "text",
+      "description": "A run of optionally styled text inside rich text.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block",
+      "implements": [
+        "RichTextSectionElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "text",
+          "goMethod": "Text",
+          "wire": "text",
+          "kind": "string",
+          "description": "Sets the text content.",
+          "required": true
+        },
+        {
+          "method": "style",
+          "goMethod": "Style",
+          "wire": "style",
+          "kind": "style",
+          "flags": [
+            "bold",
+            "italic",
+            "strike",
+            "code"
+          ],
+          "description": "Sets the visual style."
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "RichTextUser",
+      "package": "object",
+      "goConstructor": "NewRichTextUser",
+      "wireType": "user",
+      "description": "A user mention inside rich text.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block",
+      "implements": [
+        "RichTextSectionElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "userId",
+          "goMethod": "UserID",
+          "wire": "user_id",
+          "kind": "string",
+          "description": "Sets the ID of the mentioned user.",
+          "required": true
+        },
+        {
+          "method": "style",
+          "goMethod": "Style",
+          "wire": "style",
+          "kind": "style",
+          "flags": [
+            "bold",
+            "italic",
+            "strike",
+            "highlight",
+            "client_highlight",
+            "unlink"
+          ],
+          "description": "Sets the visual style."
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "RichTextUserGroup",
+      "package": "object",
+      "goConstructor": "NewRichTextUserGroup",
+      "wireType": "usergroup",
+      "description": "A user group mention inside rich text.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block",
+      "implements": [
+        "RichTextSectionElement"
+      ],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "userGroupId",
+          "goMethod": "UserGroupID",
+          "wire": "usergroup_id",
+          "kind": "string",
+          "description": "Sets the ID of the mentioned user group.",
+          "required": true
+        },
+        {
+          "method": "style",
+          "goMethod": "Style",
+          "wire": "style",
+          "kind": "style",
+          "flags": [
+            "bold",
+            "italic",
+            "strike",
+            "highlight",
+            "client_highlight",
+            "unlink"
+          ],
+          "description": "Sets the visual style."
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "SlackFile",
+      "package": "object",
+      "goConstructor": "NewSlackFile",
+      "wireType": "",
+      "description": "A reference to an image file hosted in Slack.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/composition-objects/slack-file-object",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "id",
+          "goMethod": "SlackFileID",
+          "wire": "id",
+          "kind": "string",
+          "description": "Sets the ID of a file previously uploaded to Slack. Cannot be combined with a URL."
+        },
+        {
+          "method": "url",
+          "goMethod": "SlackFileURL",
+          "wire": "url",
+          "kind": "string",
+          "description": "Sets the URL of a file hosted in Slack. Cannot be combined with an ID."
+        }
+      ],
+      "rules": [
+        "Provide exactly one of an ID or a URL.",
+        "An ID must match ^F[A-Z0-9]{8,}$."
+      ]
+    },
+    {
+      "name": "SlackIcon",
+      "package": "object",
+      "goConstructor": "NewSlackIcon",
+      "wireType": "icon",
+      "description": "A Slack-provided icon, referenced by name.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/card-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "name",
+          "goMethod": "Name",
+          "wire": "name",
+          "kind": "string",
+          "description": "Sets the name of the Slack-provided icon, such as rocket.",
+          "required": true
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "Trigger",
+      "package": "object",
+      "goConstructor": "NewTrigger",
+      "wireType": "",
+      "description": "A link trigger that starts a workflow.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/composition-objects/trigger-object",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "url",
+          "goMethod": "URL",
+          "wire": "url",
+          "kind": "string",
+          "description": "Sets the link trigger URL.",
+          "required": true
+        },
+        {
+          "method": "customizableInputParameters",
+          "goMethod": "CustomizableInputParameters",
+          "wire": "customizable_input_parameters",
+          "kind": "list",
+          "type": "InputParameter",
+          "description": "Adds the input values passed to the workflow's trigger."
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "UrlSource",
+      "package": "object",
+      "goConstructor": "NewURLSource",
+      "wireType": "url",
+      "description": "A linked source cited by a task card.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/task-card-block",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "url",
+          "goMethod": "URL",
+          "wire": "url",
+          "kind": "string",
+          "description": "Sets the source URL.",
+          "required": true
+        },
+        {
+          "method": "text",
+          "goMethod": "Text",
+          "wire": "text",
+          "kind": "string",
+          "description": "Sets the source link text.",
+          "required": true
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "Workflow",
+      "package": "object",
+      "goConstructor": "NewWorkflow",
+      "wireType": "",
+      "description": "The workflow started by a workflow button.",
+      "docUrl": "https://docs.slack.dev/reference/block-kit/composition-objects/workflow-object",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "trigger",
+          "goMethod": "Trigger",
+          "wire": "trigger",
+          "kind": "object",
+          "type": "Trigger",
+          "description": "Sets the link trigger that starts the workflow.",
+          "required": true
+        }
+      ],
+      "rules": []
+    },
+    {
+      "name": "Attachment",
+      "package": "payload",
+      "goConstructor": "NewAttachment",
+      "wireType": "",
+      "description": "A legacy message attachment containing blocks and an optional color bar.",
+      "docUrl": "https://docs.slack.dev/messaging/formatting-message-text#when-to-use-attachments",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "blocks",
+          "goMethod": "Blocks",
+          "wire": "blocks",
+          "kind": "list",
+          "type": "Block",
+          "description": "Adds the attachment's blocks in display order.",
+          "required": true
+        },
+        {
+          "method": "color",
+          "goMethod": "Color",
+          "wire": "color",
+          "kind": "string",
+          "description": "Sets the attachment's left border color: a six-digit hex color or good, warning, or danger."
+        },
+        {
+          "method": "fallback",
+          "goMethod": "Fallback",
+          "wire": "fallback",
+          "kind": "string",
+          "description": "Sets plain-text summary shown in clients that cannot display attachments."
+        }
+      ],
+      "rules": [
+        "Only blocks supported in messages are accepted."
+      ]
+    },
+    {
+      "name": "HomeTabView",
+      "package": "payload",
+      "goConstructor": "NewHomeTab",
+      "wireType": "home",
+      "description": "The payload for publishing an App Home tab with views.publish.",
+      "docUrl": "https://docs.slack.dev/surfaces/app-home",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "blocks",
+          "goMethod": "Blocks",
+          "wire": "blocks",
+          "kind": "list",
+          "type": "Block",
+          "description": "Adds the blocks shown on the App Home tab, in display order.",
+          "limits": "view.blocks",
+          "required": true
+        },
+        {
+          "method": "privateMetadata",
+          "goMethod": "PrivateMetadata",
+          "wire": "private_metadata",
+          "kind": "string",
+          "description": "Sets application-defined data returned in view payloads.",
+          "limits": "view.private_metadata"
+        },
+        {
+          "method": "callbackId",
+          "goMethod": "CallbackID",
+          "wire": "callback_id",
+          "kind": "string",
+          "description": "Sets an identifier returned in view payloads so your app can recognize the view.",
+          "limits": "view.callback_id"
+        },
+        {
+          "method": "externalId",
+          "goMethod": "ExternalID",
+          "wire": "external_id",
+          "kind": "string",
+          "description": "Sets a workspace-unique identifier you can use to update the view later.",
+          "limits": "view.external_id"
+        }
+      ],
+      "rules": [
+        "Only blocks supported on the App Home tab are accepted."
+      ]
+    },
+    {
+      "name": "MessagePayload",
+      "package": "payload",
+      "goConstructor": "NewMessage",
+      "wireType": "",
+      "description": "A complete payload for chat.postMessage and related Web API methods.",
+      "docUrl": "https://docs.slack.dev/reference/methods/chat.postMessage",
+      "implements": [],
+      "defaults": {
+        "mrkdwn": true,
+        "text": ""
+      },
+      "fields": [
+        {
+          "method": "channel",
+          "goMethod": "Channel",
+          "wire": "channel",
+          "kind": "string",
+          "description": "Sets the ID of the channel, private group, or conversation that receives the message.",
+          "limits": "message.channel",
+          "required": true
+        },
+        {
+          "method": "blocks",
+          "goMethod": "Blocks",
+          "wire": "blocks",
+          "kind": "list",
+          "type": "Block",
+          "description": "Adds the message blocks in display order.",
+          "limits": "message.blocks"
+        },
+        {
+          "method": "attachments",
+          "goMethod": "Attachments",
+          "wire": "attachments",
+          "kind": "list",
+          "type": "Attachment",
+          "description": "Adds legacy attachments in display order.",
+          "limits": "message.attachments"
+        },
+        {
+          "method": "text",
+          "goMethod": "Text",
+          "wire": "text",
+          "kind": "string",
+          "description": "Sets the fallback text used in notifications and by clients that cannot display blocks."
+        },
+        {
+          "method": "mrkdwn",
+          "goMethod": "Mrkdwn",
+          "wire": "mrkdwn",
+          "kind": "boolean",
+          "description": "Sets whether Slack formats the top-level text as mrkdwn."
+        },
+        {
+          "method": "unfurlLinks",
+          "goMethod": "UnfurlLinks",
+          "wire": "unfurl_links",
+          "kind": "boolean",
+          "description": "Sets whether Slack unfurls text-based links."
+        },
+        {
+          "method": "unfurlMedia",
+          "goMethod": "UnfurlMedia",
+          "wire": "unfurl_media",
+          "kind": "boolean",
+          "description": "Sets whether Slack unfurls media links."
+        },
+        {
+          "method": "metadata",
+          "goMethod": "Metadata",
+          "wire": "metadata",
+          "kind": "map",
+          "description": "Sets message metadata as a JSON-compatible map, such as event_type and event_payload."
+        }
+      ],
+      "rules": [
+        "Only blocks supported in messages are accepted.",
+        "Markdown block text is limited to 12,000 characters, and data table cell text to 20,000 characters, across the whole message.",
+        "At most two data visualization blocks may be sent per message, across blocks and attachments after helper expansion (message.data_visualizations.max_items)."
+      ]
+    },
+    {
+      "name": "MessageResponse",
+      "package": "payload",
+      "goConstructor": "NewMessageResponse",
+      "wireType": "",
+      "description": "A response body for slash commands and interaction response URLs.",
+      "docUrl": "https://docs.slack.dev/interactivity/handling-user-interaction#message_responses",
+      "implements": [],
+      "defaults": {
+        "mrkdwn": true,
+        "text": "",
+        "replace_original": false,
+        "response_type": "in_channel"
+      },
+      "fields": [
+        {
+          "method": "blocks",
+          "goMethod": "Blocks",
+          "wire": "blocks",
+          "kind": "list",
+          "type": "Block",
+          "description": "Adds the response blocks in display order.",
+          "limits": "message.blocks"
+        },
+        {
+          "method": "attachments",
+          "goMethod": "Attachments",
+          "wire": "attachments",
+          "kind": "list",
+          "type": "Attachment",
+          "description": "Adds legacy attachments in display order.",
+          "limits": "message.attachments"
+        },
+        {
+          "method": "text",
+          "goMethod": "Text",
+          "wire": "text",
+          "kind": "string",
+          "description": "Sets the fallback text used in notifications and by clients that cannot display blocks."
+        },
+        {
+          "method": "mrkdwn",
+          "goMethod": "Mrkdwn",
+          "wire": "mrkdwn",
+          "kind": "boolean",
+          "description": "Sets whether Slack formats the top-level text as mrkdwn."
+        },
+        {
+          "method": "replaceOriginal",
+          "goMethod": "ReplaceOriginal",
+          "wire": "replace_original",
+          "kind": "boolean",
+          "description": "Sets whether the response replaces the message that triggered it."
+        },
+        {
+          "method": "responseType",
+          "goMethod": "ResponseType",
+          "wire": "response_type",
+          "kind": "enum",
+          "type": "ResponseType",
+          "description": "Sets who can see the response."
+        }
+      ],
+      "rules": [
+        "Only blocks supported in messages are accepted.",
+        "Markdown block text is limited to 12,000 characters, and data table cell text to 20,000 characters, across the whole message.",
+        "At most two data visualization blocks may be sent per message, across blocks and attachments after helper expansion (message.data_visualizations.max_items)."
+      ]
+    },
+    {
+      "name": "ModalView",
+      "package": "payload",
+      "goConstructor": "NewModal",
+      "wireType": "modal",
+      "description": "The view payload for views.open, views.push, and views.update.",
+      "docUrl": "https://docs.slack.dev/surfaces/modals",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "title",
+          "goMethod": "Title",
+          "wire": "title",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the title in the modal's top bar.",
+          "limits": "view.title",
+          "required": true
+        },
+        {
+          "method": "blocks",
+          "goMethod": "Blocks",
+          "wire": "blocks",
+          "kind": "list",
+          "type": "Block",
+          "description": "Adds the blocks shown in the modal, in display order.",
+          "limits": "view.blocks",
+          "required": true
+        },
+        {
+          "method": "close",
+          "goMethod": "Close",
+          "wire": "close",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the label of the button that closes the modal.",
+          "limits": "view.close"
+        },
+        {
+          "method": "submit",
+          "goMethod": "Submit",
+          "wire": "submit",
+          "kind": "text",
+          "type": "PlainText",
+          "coerce": "plain_text",
+          "description": "Sets the label of the button that submits the modal.",
+          "limits": "view.submit"
+        },
+        {
+          "method": "privateMetadata",
+          "goMethod": "PrivateMetadata",
+          "wire": "private_metadata",
+          "kind": "string",
+          "description": "Sets application-defined data returned in view payloads.",
+          "limits": "view.private_metadata"
+        },
+        {
+          "method": "callbackId",
+          "goMethod": "CallbackID",
+          "wire": "callback_id",
+          "kind": "string",
+          "description": "Sets an identifier returned in view payloads so your app can recognize the view.",
+          "limits": "view.callback_id"
+        },
+        {
+          "method": "clearOnClose",
+          "goMethod": "ClearOnClose",
+          "wire": "clear_on_close",
+          "kind": "boolean",
+          "description": "Sets whether closing this modal closes every view in its stack."
+        },
+        {
+          "method": "notifyOnClose",
+          "goMethod": "NotifyOnClose",
+          "wire": "notify_on_close",
+          "kind": "boolean",
+          "description": "Sets whether Slack sends a view_closed event when the user closes the modal."
+        },
+        {
+          "method": "externalId",
+          "goMethod": "ExternalID",
+          "wire": "external_id",
+          "kind": "string",
+          "description": "Sets a workspace-unique identifier you can use to update the view later.",
+          "limits": "view.external_id"
+        },
+        {
+          "method": "submitDisabled",
+          "goMethod": "SubmitDisabled",
+          "wire": "submit_disabled",
+          "kind": "boolean",
+          "description": "Sets whether the submit button starts disabled in workflow configuration modals only. Omit this field for ordinary views.open/views.push modals, including when false. The general ModalView value cannot infer its receiving workflow context."
+        }
+      ],
+      "rules": [
+        "Only blocks supported in modals are accepted.",
+        "A modal that contains an input block must have a submit label."
+      ]
+    },
+    {
+      "name": "WebhookMessage",
+      "package": "payload",
+      "goConstructor": "NewWebhookMessage",
+      "wireType": "",
+      "description": "A payload for incoming webhooks.",
+      "docUrl": "https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks",
+      "implements": [],
+      "defaults": {},
+      "fields": [
+        {
+          "method": "blocks",
+          "goMethod": "Blocks",
+          "wire": "blocks",
+          "kind": "list",
+          "type": "Block",
+          "description": "Adds the message blocks in display order.",
+          "limits": "message.blocks"
+        },
+        {
+          "method": "attachments",
+          "goMethod": "Attachments",
+          "wire": "attachments",
+          "kind": "list",
+          "type": "Attachment",
+          "description": "Adds legacy attachments in display order.",
+          "limits": "message.attachments"
+        },
+        {
+          "method": "text",
+          "goMethod": "Text",
+          "wire": "text",
+          "kind": "string",
+          "description": "Sets the fallback text used in notifications and by clients that cannot display blocks."
+        },
+        {
+          "method": "responseType",
+          "goMethod": "ResponseType",
+          "wire": "response_type",
+          "kind": "enum",
+          "type": "ResponseType",
+          "description": "Sets who can see the response."
+        },
+        {
+          "method": "replaceOriginal",
+          "goMethod": "ReplaceOriginal",
+          "wire": "replace_original",
+          "kind": "boolean",
+          "description": "Sets whether the response replaces the message that triggered it."
+        },
+        {
+          "method": "deleteOriginal",
+          "goMethod": "DeleteOriginal",
+          "wire": "delete_original",
+          "kind": "boolean",
+          "description": "Sets whether the response deletes the message that triggered it."
+        },
+        {
+          "method": "unfurlLinks",
+          "goMethod": "UnfurlLinks",
+          "wire": "unfurl_links",
+          "kind": "boolean",
+          "description": "Sets whether Slack unfurls text-based links."
+        },
+        {
+          "method": "unfurlMedia",
+          "goMethod": "UnfurlMedia",
+          "wire": "unfurl_media",
+          "kind": "boolean",
+          "description": "Sets whether Slack unfurls media links."
+        },
+        {
+          "method": "metadata",
+          "goMethod": "Metadata",
+          "wire": "metadata",
+          "kind": "map",
+          "description": "Sets message metadata as a JSON-compatible map, such as event_type and event_payload."
+        }
+      ],
+      "rules": [
+        "Only blocks supported in messages are accepted.",
+        "Markdown block text is limited to 12,000 characters, and data table cell text to 20,000 characters, across the whole message.",
+        "At most two data visualization blocks may be sent per message, across blocks and attachments after helper expansion (message.data_visualizations.max_items)."
+      ]
+    }
+  ]
+}
diff --git a/test/spec/vocabulary.json b/test/spec/vocabulary.json
new file mode 100644
--- /dev/null
+++ b/test/spec/vocabulary.json
@@ -0,0 +1,113 @@
+{
+  "$comment": "Shared Slack vocabularies used by validation. Every implementation's tables must match this file exactly.",
+  "slack_icon_names": [
+    "archive",
+    "book",
+    "bookmark",
+    "bot",
+    "bug",
+    "calendar",
+    "call",
+    "caret-left",
+    "caret-right",
+    "check",
+    "clipboard",
+    "code",
+    "comment",
+    "compass",
+    "copy",
+    "cube",
+    "download",
+    "edit",
+    "email",
+    "eye-closed",
+    "eye-open",
+    "file",
+    "flag",
+    "folder",
+    "gear",
+    "globe",
+    "heart",
+    "help",
+    "image",
+    "info",
+    "key",
+    "lightbulb",
+    "link",
+    "map",
+    "mobile",
+    "new-window",
+    "pin",
+    "plus",
+    "refine",
+    "refresh",
+    "rocket",
+    "save",
+    "screen",
+    "share",
+    "sparkle",
+    "star",
+    "star-filled",
+    "tag",
+    "thumbs-down",
+    "thumbs-up",
+    "trash",
+    "upload",
+    "user",
+    "warning"
+  ],
+  "surface_block_types": {
+    "home": [
+      "actions",
+      "card",
+      "carousel",
+      "container",
+      "context",
+      "data_table",
+      "data_visualization",
+      "divider",
+      "header",
+      "image",
+      "input",
+      "rich_text",
+      "section",
+      "table",
+      "video"
+    ],
+    "message": [
+      "actions",
+      "card",
+      "carousel",
+      "container",
+      "context",
+      "context_actions",
+      "data_table",
+      "data_visualization",
+      "divider",
+      "file",
+      "header",
+      "image",
+      "input",
+      "markdown",
+      "plan",
+      "rich_text",
+      "section",
+      "table",
+      "task_card",
+      "video"
+    ],
+    "modal": [
+      "actions",
+      "alert",
+      "card",
+      "context",
+      "divider",
+      "header",
+      "image",
+      "input",
+      "rich_text",
+      "section",
+      "video"
+    ]
+  }
+}
