Skip to content

Objects

Objects are the lowest-level composable primitives in slackblocks. They don't render on their own — instead, they're passed into Elements and Blocks to populate them with text, choices, confirmations, and so on.

The most important ones:

  • Text — a piece of mrkdwn or plain_text content. Most fields that accept a str will also accept a Text for fine-grained control.
  • Option — a single selectable choice in a select menu, checkbox, or radio group.
  • ConfirmationDialogue — a "Are you sure?" prompt shown before destructive actions.

Slack reference: https://api.slack.com/reference/block-kit/composition-objects

Composition objects are the lowest-level primitives used inside of Block objects.

See: https://api.slack.com/reference/block-kit/composition-objects.

ColumnAlignment module-attribute

ColumnAlignment = Literal['left', 'center', 'right']

Allowable values for ColumnSettings.align.

ConversationType module-attribute

ConversationType = Literal[
    "im", "mpim", "private", "public"
]

The four kinds of Slack conversation that ConversationFilter.include may contain. See https://api.slack.com/reference/block-kit/composition-objects#filter_conversations.

ColumnSettings

An object that defines the settings for a column in a Table block.

Parameters:

Name Type Description Default
align ColumnAlignment | None

the alignment of the column, one of left, center, or right.

None
is_wrapped bool | None

whether the text in the column should be wrapped.

None
Source code in slackblocks/objects.py
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
class ColumnSettings:
    """
    An object that defines the settings for a column in a `Table` block.

    Args:
        align: the alignment of the column, one of `left`, `center`, or `right`.
        is_wrapped: whether the text in the column should be wrapped.
    """

    def __init__(
        self,
        align: ColumnAlignment | None = None,
        is_wrapped: bool | None = None,
    ) -> None:
        if align and align not in ["left", "center", "right"]:
            raise TypeMismatchError("`align` must be one of `left`, `center`, or `right`")
        self.align = align
        self.is_wrapped = is_wrapped

    def _resolve(self) -> dict[str, Any]:
        return omit_none({"align": self.align, "is_wrapped": self.is_wrapped})

Confirm

Alias for ConfirmationDialogue to retain backwards compatibility.

See

ConfirmationDialogue.

Source code in slackblocks/objects.py
382
383
384
385
386
387
388
389
390
391
class Confirm(ConfirmationDialogue):
    """
    Alias for `ConfirmationDialogue` to retain backwards compatibility.

    See:
        [`ConfirmationDialogue`](/slackblocks/latest/reference/objects/#objects.ConfirmationDialogue).
    """

    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)

ConfirmationDialogue

An object that defines a dialog that provides a confirmation step to any interactive element. This dialog will ask the user to confirm their action by offering confirm and deny buttons.

Parameters:

Name Type Description Default
title TextLike

the text heading presented at the top of the dialogue box (max 100 chars).

required
text TextLike

the text explaining the decision being made by the user through the dialogue box (max 300 chars).

required
confirm TextLike

the text inside the confirmation button of the dialogue box (max 30 chars).

required
deny TextLike

the text inside the deny button of the dialogue box (max 30 chars).

required
Throws

InvalidUsageError: if any of the arguments fail to pass validation checks.

Source code in slackblocks/objects.py
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
class ConfirmationDialogue(CompositionObject):
    """
    An object that defines a dialog that provides a confirmation step
    to any interactive element. This dialog will ask the user to confirm
    their action by offering confirm and deny buttons.

    Args:
        title: the text heading presented at the top of the dialogue box (max 100 chars).
        text: the text explaining the decision being made by the user through
            the dialogue box (max 300 chars).
        confirm: the text inside the confirmation button of the dialogue box (max 30 chars).
        deny: the text inside the deny button of the dialogue box (max 30 chars).

    Throws:
        InvalidUsageError: if any of the arguments fail to pass validation checks.
    """

    def __init__(
        self,
        title: TextLike,
        text: TextLike,
        confirm: TextLike,
        deny: TextLike,
    ) -> None:
        super().__init__(type_=CompositionObjectType.CONFIRM)
        self.title = Text.to_text_nonnull(title, max_length=100, force_plaintext=True)
        self.text = Text.to_text_nonnull(text, max_length=300)
        self.confirm = Text.to_text_nonnull(confirm, max_length=30, force_plaintext=True)
        self.deny = Text.to_text_nonnull(deny, max_length=30, force_plaintext=True)

    def _resolve(self) -> dict[str, Any]:
        return resolve(
            {
                "title": self.title,
                "text": self.text,
                "confirm": self.confirm,
                "deny": self.deny,
            }
        )

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> ConfirmationDialogue:
        """Parse a Slack ``confirm`` composition object back into an instance.

        Throws:
            MissingRequiredError: if any of ``title``, ``text``, ``confirm``,
                ``deny`` is absent.
        """
        for field in ("title", "text", "confirm", "deny"):
            if field not in data:
                raise MissingRequiredError(
                    f"Confirmation dialogue payload is missing required `{field}` field."
                )
        return cls(
            title=Text.from_dict(data["title"]),
            text=Text.from_dict(data["text"]),
            confirm=Text.from_dict(data["confirm"]),
            deny=Text.from_dict(data["deny"]),
        )

from_dict classmethod

from_dict(data)

Parse a Slack confirm composition object back into an instance.

Throws

MissingRequiredError: if any of title, text, confirm, deny is absent.

Source code in slackblocks/objects.py
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
@classmethod
def from_dict(cls, data: dict[str, Any]) -> ConfirmationDialogue:
    """Parse a Slack ``confirm`` composition object back into an instance.

    Throws:
        MissingRequiredError: if any of ``title``, ``text``, ``confirm``,
            ``deny`` is absent.
    """
    for field in ("title", "text", "confirm", "deny"):
        if field not in data:
            raise MissingRequiredError(
                f"Confirmation dialogue payload is missing required `{field}` field."
            )
    return cls(
        title=Text.from_dict(data["title"]),
        text=Text.from_dict(data["text"]),
        confirm=Text.from_dict(data["confirm"]),
        deny=Text.from_dict(data["deny"]),
    )

ConversationFilter

Provides a way to filter the list of options in a conversations select menu or conversations multi-select menu.

See: https://api.slack.com/reference/block-kit/composition-objects#filter_conversations.

At least one of the available arguments must be provided.

Parameters:

Name Type Description Default
include ConversationType | list[ConversationType] | None

Which types of conversations to include in the list. One of more of im, mpim, private, public.

None
exclude_external_shared_channels bool | None

whether to remove shared public channels from the list. See https://api.slack.com/enterprise/shared-channels.

None
exclude_bot_users bool | None

whether to remove bot users from the list of conversations.

None
Throws

InvalidUsageException: in the event that the user provides none of include, exclude_external_shared_channels, or exclude_bot_users arguments.

Source code in slackblocks/objects.py
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
class ConversationFilter(CompositionObject):
    """
    Provides a way to filter the list of options in a conversations select menu or
    conversations multi-select menu.

    See: <https://api.slack.com/reference/block-kit/composition-objects#filter_conversations>.

    At least one of the available arguments _must_ be provided.

    Args:
        include: Which types of conversations to include in the list.
            One of more of `im`, `mpim`, `private`, `public`.
        exclude_external_shared_channels: whether to remove shared public channels
            from the list. See <https://api.slack.com/enterprise/shared-channels>.
        exclude_bot_users: whether to remove bot users from the list of conversations.

    Throws:
        InvalidUsageException: in the event that the user provides none of `include`,
            `exclude_external_shared_channels`, or `exclude_bot_users` arguments.
    """

    def __init__(
        self,
        include: ConversationType | list[ConversationType] | None = None,
        exclude_external_shared_channels: bool | None = None,
        exclude_bot_users: bool | None = None,
    ) -> None:
        super().__init__(type_=CompositionObjectType.FILTER)
        if not (
            include or exclude_external_shared_channels is not None or exclude_bot_users is not None
        ):
            raise MissingRequiredError(
                "One of `include`, `exclude_external_shared_channels`, or "
                "`exclude_bot_users` is required."
            )
        self.include = coerce_to_list(cast("str | list[str] | None", include), str, allow_none=True)
        self.exclude_external_shared_channels = exclude_external_shared_channels
        self.exclude_bot_users = exclude_bot_users

    def _resolve(self) -> dict[str, Any]:
        # ConversationFilter does not include "type" in its rendered JSON.
        # Note that ``include`` is omitted when empty (falsy list) to match the
        # historical behaviour; ``exclude_*`` fields are kept when explicitly
        # set to False.
        return omit_none(
            {
                "include": self.include if self.include else None,
                "exclude_external_shared_channels": self.exclude_external_shared_channels,
                "exclude_bot_users": self.exclude_bot_users,
            }
        )

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> ConversationFilter:
        """Parse a Slack ``filter`` composition object.

        At least one of ``include``, ``exclude_external_shared_channels``,
        or ``exclude_bot_users`` must be present; otherwise the underlying
        constructor raises ``MissingRequiredError``.
        """
        return cls(
            include=data.get("include"),
            exclude_external_shared_channels=data.get("exclude_external_shared_channels"),
            exclude_bot_users=data.get("exclude_bot_users"),
        )

from_dict classmethod

from_dict(data)

Parse a Slack filter composition object.

At least one of include, exclude_external_shared_channels, or exclude_bot_users must be present; otherwise the underlying constructor raises MissingRequiredError.

Source code in slackblocks/objects.py
609
610
611
612
613
614
615
616
617
618
619
620
621
@classmethod
def from_dict(cls, data: dict[str, Any]) -> ConversationFilter:
    """Parse a Slack ``filter`` composition object.

    At least one of ``include``, ``exclude_external_shared_channels``,
    or ``exclude_bot_users`` must be present; otherwise the underlying
    constructor raises ``MissingRequiredError``.
    """
    return cls(
        include=data.get("include"),
        exclude_external_shared_channels=data.get("exclude_external_shared_channels"),
        exclude_bot_users=data.get("exclude_bot_users"),
    )

DispatchActionConfiguration

Determines when a plain-text input element will return a block_actions interaction payload.

Parameters:

Name Type Description Default
trigger_actions_on str | list[str] | None

a list of strings representing interaction types that should return a block_actions payload. One or both of on_enter_pressed, on_character_entered.

None
Throws

InvalidUsageError: if an invalid value is provided amongst the options for trigger_actions_on.

Source code in slackblocks/objects.py
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
class DispatchActionConfiguration(CompositionObject):
    """
    Determines when a plain-text input element will return a `block_action`s interaction payload.

    Args:
        trigger_actions_on: a list of strings representing interaction types that should return
            a `block_actions` payload. One or both of `on_enter_pressed`, `on_character_entered`.

    Throws:
        InvalidUsageError: if an invalid value is provided amongst the options for
            `trigger_actions_on`.
    """

    def __init__(self, trigger_actions_on: str | list[str] | None = None) -> None:
        super().__init__(type_=CompositionObjectType.DISPATCH)
        trigger_actions_on = trigger_actions_on or []
        self.trigger_actions_on = list(
            set(coerce_to_list_nonnull(trigger_actions_on, str, min_size=1, max_size=2))
        )
        for trigger in self.trigger_actions_on:
            if trigger not in ALLOWABLE_TRIGGERS:
                raise TypeMismatchError(
                    f"Trigger {trigger} not in allowable values ({ALLOWABLE_TRIGGERS})"
                )

    def _resolve(self) -> dict[str, Any]:
        # DispatchActionConfiguration does not include "type" in its rendered JSON.
        return {"trigger_actions_on": self.trigger_actions_on}

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> DispatchActionConfiguration:
        """Parse a Slack ``dispatch_action_config`` composition object."""
        return cls(trigger_actions_on=data.get("trigger_actions_on"))

from_dict classmethod

from_dict(data)

Parse a Slack dispatch_action_config composition object.

Source code in slackblocks/objects.py
546
547
548
549
@classmethod
def from_dict(cls, data: dict[str, Any]) -> DispatchActionConfiguration:
    """Parse a Slack ``dispatch_action_config`` composition object."""
    return cls(trigger_actions_on=data.get("trigger_actions_on"))

InputParameter

Contains information about an input parameter.

See https://api.slack.com/automation/workflows#defining-input-parameters.

Parameters:

Name Type Description Default
name str

the name of the input parameter.

required
value str

the value associated with the input parameter.

required
Source code in slackblocks/objects.py
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
class InputParameter(CompositionObject):
    """
    Contains information about an input parameter.

    See <https://api.slack.com/automation/workflows#defining-input-parameters>.

    Args:
        name: the name of the input parameter.
        value: the value associated with the input parameter.
    """

    def __init__(self, name: str, value: str) -> None:
        super().__init__(type_=CompositionObjectType.INPUT_PARAMETER)
        self.name = name
        self.value = value

    def _resolve(self) -> dict[str, Any]:
        # InputParameter does not include "type" in its rendered JSON.
        return {"name": self.name, "value": self.value}

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> InputParameter:
        """Parse a Slack ``input_parameter`` composition object."""
        for field in ("name", "value"):
            if field not in data:
                raise MissingRequiredError(
                    f"InputParameter payload is missing required `{field}` field."
                )
        return cls(name=data["name"], value=data["value"])

from_dict classmethod

from_dict(data)

Parse a Slack input_parameter composition object.

Source code in slackblocks/objects.py
644
645
646
647
648
649
650
651
652
@classmethod
def from_dict(cls, data: dict[str, Any]) -> InputParameter:
    """Parse a Slack ``input_parameter`` composition object."""
    for field in ("name", "value"):
        if field not in data:
            raise MissingRequiredError(
                f"InputParameter payload is missing required `{field}` field."
            )
    return cls(name=data["name"], value=data["value"])

Markdown

Convenience wrapper for Text with type_=TextType.MARKDOWN.

Markdown("_italic_", verbatim=True) is equivalent to Text("_italic_", type_=TextType.MARKDOWN, verbatim=True). Anywhere a Text or TextLike is accepted, a Markdown works because Markdown is a subclass of Text. The rendered JSON is identical to the equivalent Text call.

Parameters:

Name Type Description Default
text str

the markdown-formatted text to render (1-3000 characters).

required
verbatim bool

if True, links, channel names, and user names are rendered verbatim rather than as Slack-style references.

False
Throws

LengthError: if the provided text is empty or too long.

Source code in slackblocks/objects.py
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
class Markdown(Text):
    """Convenience wrapper for `Text` with `type_=TextType.MARKDOWN`.

    `Markdown("_italic_", verbatim=True)` is equivalent to
    `Text("_italic_", type_=TextType.MARKDOWN, verbatim=True)`. Anywhere a
    `Text` or `TextLike` is accepted, a `Markdown` works because `Markdown`
    is a subclass of `Text`. The rendered JSON is identical to the
    equivalent `Text` call.

    Args:
        text: the markdown-formatted text to render (1-3000 characters).
        verbatim: if `True`, links, channel names, and user names are
            rendered verbatim rather than as Slack-style references.

    Throws:
        LengthError: if the provided `text` is empty or too long.
    """

    def __init__(self, text: str, verbatim: bool = False) -> None:
        super().__init__(text=text, type_=TextType.MARKDOWN, verbatim=verbatim)

Option

An object that represents a single selectable item in a select menu, multi-select menu, checkbox group, radio button group, or overflow menu.

See https://api.slack.com/reference/block-kit/composition-objects#option.

Parameters:

Name Type Description Default
text TextLike

the text identifying the option (that the user will see).

required
value str

the underlying value of that option (not seen by the user).

required
description TextLike | None

a more detailed explanation of what the option means (user-facing).

None
url str | None

a URL to load in the user's browser when the option is clicked. Only available in OverflowMenus.

None
Throws

InvalidUsageError: when any of the provided arguments fail validation.

Source code in slackblocks/objects.py
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
class Option(CompositionObject):
    """
    An object that represents a single selectable item in a select menu, multi-select
    menu, checkbox group, radio button group, or overflow menu.

    See <https://api.slack.com/reference/block-kit/composition-objects#option>.

    Args:
        text: the text identifying the option (that the user will see).
        value: the underlying value of that option (not seen by the user).
        description: a more detailed explanation of what the option means (user-facing).
        url: a URL to load in the user's browser when the option is clicked.
            Only available in `OverflowMenus`.

    Throws:
        InvalidUsageError: when any of the provided arguments fail validation.
    """

    def __init__(
        self,
        text: TextLike,
        value: str,
        description: TextLike | None = None,
        url: str | None = None,
    ) -> None:
        super().__init__(type_=CompositionObjectType.OPTION)
        self.text = Text.to_text_nonnull(text, max_length=75)
        self.value = validate_string_nonnull(value, field_name="value", max_length=75)
        self.description = Text.to_text(
            description, max_length=75, force_plaintext=True, allow_none=True
        )
        if url and len(url) > 3000:
            raise LengthError("Option URLs must be less than 3000 characters")
        self.url = url

    def _resolve(self) -> dict[str, Any]:
        # Option does not include "type" in its rendered JSON.
        return resolve(
            {
                "text": self.text,
                "value": self.value,
                "description": self.description,
                "url": self.url,
            }
        )

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> Option:
        """Parse a Slack ``option`` composition object back into an ``Option``.

        Throws:
            MissingRequiredError: if ``text`` or ``value`` is absent.
        """
        for field in ("text", "value"):
            if field not in data:
                raise MissingRequiredError(f"Option payload is missing required `{field}` field.")
        description = data.get("description")
        return cls(
            text=Text.from_dict(data["text"]),
            value=data["value"],
            description=Text.from_dict(description) if description is not None else None,
            url=data.get("url"),
        )

    def __eq__(self, other) -> bool:
        return (
            self.type == other.type
            and self.text == other.text
            and self.value == other.value
            and self.description == other.description
            and self.url == other.url
        )

from_dict classmethod

from_dict(data)

Parse a Slack option composition object back into an Option.

Throws

MissingRequiredError: if text or value is absent.

Source code in slackblocks/objects.py
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
@classmethod
def from_dict(cls, data: dict[str, Any]) -> Option:
    """Parse a Slack ``option`` composition object back into an ``Option``.

    Throws:
        MissingRequiredError: if ``text`` or ``value`` is absent.
    """
    for field in ("text", "value"):
        if field not in data:
            raise MissingRequiredError(f"Option payload is missing required `{field}` field.")
    description = data.get("description")
    return cls(
        text=Text.from_dict(data["text"]),
        value=data["value"],
        description=Text.from_dict(description) if description is not None else None,
        url=data.get("url"),
    )

OptionGroup

Provides a way to group options in a select menu or multi-select menu.

See https://api.slack.com/reference/block-kit/composition-objects#option_group.

Parameters:

Name Type Description Default
label TextLike

a label shown above the group of options.

required
options list[Option]

a list of Option objects that will form the contents of the group (max 100).

required
Throws

InvalidUsageError: if no options are provided or the label is not valid.

Source code in slackblocks/objects.py
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
class OptionGroup(CompositionObject):
    """
    Provides a way to group options in a select menu or multi-select menu.

    See <https://api.slack.com/reference/block-kit/composition-objects#option_group>.

    Args:
        label: a label shown above the group of options.
        options: a list of `Option` objects that will form the contents of the group (max 100).

    Throws:
        InvalidUsageError: if no options are provided or the label is not valid.
    """

    def __init__(self, label: TextLike, options: list[Option]) -> None:
        super().__init__(type_=CompositionObjectType.OPTION_GROUP)
        self.label = Text.to_text(label, max_length=75, force_plaintext=True)
        self.options: list[Option] = coerce_to_list_nonnull(
            options,
            class_=Option,
            min_size=1,
            max_size=100,
        )

    def _resolve(self) -> dict[str, Any]:
        # OptionGroup does not include "type" in its rendered JSON.
        return resolve({"label": self.label, "options": self.options})

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> OptionGroup:
        """Parse a Slack ``option_group`` composition object.

        Throws:
            MissingRequiredError: if ``label`` or ``options`` is absent.
        """
        for field in ("label", "options"):
            if field not in data:
                raise MissingRequiredError(
                    f"OptionGroup payload is missing required `{field}` field."
                )
        return cls(
            label=Text.from_dict(data["label"]),
            options=[Option.from_dict(item) for item in data["options"]],
        )

from_dict classmethod

from_dict(data)

Parse a Slack option_group composition object.

Throws

MissingRequiredError: if label or options is absent.

Source code in slackblocks/objects.py
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
@classmethod
def from_dict(cls, data: dict[str, Any]) -> OptionGroup:
    """Parse a Slack ``option_group`` composition object.

    Throws:
        MissingRequiredError: if ``label`` or ``options`` is absent.
    """
    for field in ("label", "options"):
        if field not in data:
            raise MissingRequiredError(
                f"OptionGroup payload is missing required `{field}` field."
            )
    return cls(
        label=Text.from_dict(data["label"]),
        options=[Option.from_dict(item) for item in data["options"]],
    )

PlainText

Convenience wrapper for Text with type_=TextType.PLAINTEXT.

PlainText("Hi", emoji=True) is equivalent to Text("Hi", type_=TextType.PLAINTEXT, emoji=True). Anywhere a Text or TextLike is accepted, a PlainText works because PlainText is a subclass of Text. The rendered JSON is identical to the equivalent Text call.

Parameters:

Name Type Description Default
text str

the text to render (1-3000 characters).

required
emoji bool

if True, emoji (e.g. :smile:) are escaped into Unicode.

False
Throws

LengthError: if the provided text is empty or too long.

Source code in slackblocks/objects.py
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
class PlainText(Text):
    """Convenience wrapper for `Text` with `type_=TextType.PLAINTEXT`.

    `PlainText("Hi", emoji=True)` is equivalent to
    `Text("Hi", type_=TextType.PLAINTEXT, emoji=True)`. Anywhere a `Text` or
    `TextLike` is accepted, a `PlainText` works because `PlainText` is a
    subclass of `Text`. The rendered JSON is identical to the equivalent
    `Text` call.

    Args:
        text: the text to render (1-3000 characters).
        emoji: if `True`, emoji (e.g. `:smile:`) are escaped into Unicode.

    Throws:
        LengthError: if the provided `text` is empty or too long.
    """

    def __init__(self, text: str, emoji: bool = False) -> None:
        super().__init__(text=text, type_=TextType.PLAINTEXT, emoji=emoji)

RawText

An object containing some text, formatted as raw_text for use in Table blocks.

Parameters:

Name Type Description Default
text str

the text to be rendered in a message.

required
emoji bool

only usable with TextType.PLAINTEXT, if True: emoji will be escaped into text format (e.g. :smile:).

False
Throws

InvalidUsageException: if the provided text fails validation.

Source code in slackblocks/objects.py
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
class RawText:
    """
    An object containing some text, formatted as `raw_text` for use in
    `Table` blocks.

    Args:
        text: the text to be rendered in a message.
        emoji: only usable with `TextType.PLAINTEXT`, if True: emoji will be
            escaped into text format (e.g. `:smile:`).

    Throws:
        InvalidUsageException: if the provided `text` fails validation.
    """

    def __init__(
        self,
        text: str,
        emoji: bool = False,
    ) -> None:
        self.type = "raw_text"
        self.text = text
        self.emoji = emoji

    def _resolve(self) -> dict[str, Any]:
        return omit_none(
            {
                "type": self.type,
                "text": self.text,
                "emoji": self.emoji if self.emoji else None,
            }
        )

SlackFile

Defines an object containing Slack file information to be used in an image block or image element.

This file must be an image and you must provide either the URL or ID (not both).

See: https://api.slack.com/reference/block-kit/composition-objects#slack_file.

Parameters:

Name Type Description Default
url str | None

the URL can be the url_private or the permalink of the Slack file (only one of url or id can be provided).

required
id str | None

the Slack ID of the file (only one of url or id can be provided).

required
Throws

InvalidUsageError: if both url and id are provided

Source code in slackblocks/objects.py
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
class SlackFile(CompositionObject):
    """
    Defines an object containing Slack file information to be used in an image
        block or image element.

    This file must be an image and you must provide either the URL or ID (not both).

    See: <https://api.slack.com/reference/block-kit/composition-objects#slack_file>.

    Args:
        url: the URL can be the `url_private` or the `permalink` of the Slack file
            (only one of `url` or `id` can be provided).
        id: the Slack ID of the file
            (only one of `url` or `id` can be provided).

    Throws:
        InvalidUsageError: if both `url` and `id` are provided
    """

    def __init__(
        self,
        url: str | None,
        id: str | None,
    ) -> None:
        super().__init__(CompositionObjectType.SLACK_FILE)
        if url and id:
            raise MutualExclusivityError("Cannot provide both `url` and `id`.")
        self.url = url
        self.id = id

    def _resolve(self) -> dict[str, Any]:
        # SlackFile does not include "type" in its rendered JSON.
        return omit_none({"url": self.url, "id": self.id})

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> SlackFile:
        """Parse a Slack ``slack_file`` composition object."""
        return cls(url=data.get("url"), id=data.get("id"))

from_dict classmethod

from_dict(data)

Parse a Slack slack_file composition object.

Source code in slackblocks/objects.py
689
690
691
692
@classmethod
def from_dict(cls, data: dict[str, Any]) -> SlackFile:
    """Parse a Slack ``slack_file`` composition object."""
    return cls(url=data.get("url"), id=data.get("id"))

Text

An object containing some text, formatted either as plain_text or using Slack's mrkdwn.

Parameters:

Name Type Description Default
text str

the text to be rendered in a message (max 3000 characters).

required
type_ TextType

either TextType.MARKDOWN or TextType.PLAINTEXT.

MARKDOWN
emoji bool

only usable with TextType.PLAINTEXT, if True: emoji will be escaped into text format (e.g. :smile:).

False
verbatim bool

only usable with TextType.MARKDOWN, if True: links, channel names, user names will not automatically be rendered as links.

False
Throws

InvalidUsageException: if the provided text fails validation.

Source code in slackblocks/objects.py
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
class Text(CompositionObject):
    """
    An object containing some text, formatted either as `plain_text` or using
    Slack's `mrkdwn`.

    Args:
        text: the text to be rendered in a message (max 3000 characters).
        type_: either `TextType.MARKDOWN` or `TextType.PLAINTEXT`.
        emoji: only usable with `TextType.PLAINTEXT`, if True: emoji will be
            escaped into text format (e.g. `:smile:`).
        verbatim: only usable with `TextType.MARKDOWN`, if True: links, channel
            names, user names will not automatically be rendered as links.

    Throws:
        InvalidUsageException: if the provided `text` fails validation.
    """

    def __init__(
        self,
        text: str,
        type_: TextType = TextType.MARKDOWN,
        emoji: bool = False,
        verbatim: bool = False,
    ) -> None:
        super().__init__(type_=CompositionObjectType.TEXT)
        self.text_type = type_
        self.text = validate_string_nonnull(text, field_name="text", min_length=1, max_length=3000)
        if self.text_type == TextType.MARKDOWN:
            self.verbatim = verbatim
            self.emoji = False
        elif self.text_type == TextType.PLAINTEXT:
            self.verbatim = False
            self.emoji = emoji

    def _resolve(self) -> dict[str, Any]:
        return omit_none(
            {
                "type": self.text_type.value,
                "text": self.text,
                "verbatim": self.verbatim
                if self.text_type == TextType.MARKDOWN and self.verbatim
                else None,
                "emoji": self.emoji
                if self.text_type == TextType.PLAINTEXT and self.emoji
                else None,
            }
        )

    @overload
    @staticmethod
    def to_text(
        text: str | Text | None,
        force_plaintext: bool = False,
        max_length: int | None = None,
        *,
        allow_none: Literal[False] = False,
    ) -> Text: ...

    @overload
    @staticmethod
    def to_text(
        text: str | Text | None,
        force_plaintext: bool = False,
        max_length: int | None = None,
        *,
        allow_none: Literal[True],
    ) -> Text | None: ...

    @overload
    @staticmethod
    def to_text(
        text: str | Text | None,
        force_plaintext: bool = False,
        max_length: int | None = None,
        *,
        allow_none: bool,
    ) -> Text | None: ...

    @staticmethod
    def to_text(
        text: str | Text | None,
        force_plaintext: bool = False,
        max_length: int | None = None,
        allow_none: bool = False,
    ) -> Text | None:
        """
        Coerces `str` or `Text` objects into `Text` objects.

        Args:
            text: the `str` or `Text` object to ensure is in `Text` format.
            force_plaintext: if `True`, forces the `str` or `Text` object
                into a `Text` object with the type `TextType.PLAINTEXT`.
            max_length: `text` will be checked against this length in addition
                to the standard `Text` limit of 3000 characters.
            allow_none: whether to accept `None` as a valid value for `text`.
                The return type narrows based on this value:

                - ``allow_none=False`` (the default) -> always returns ``Text``.
                - ``allow_none=True`` -> returns ``Text | None``.
        """
        if text is None:
            if allow_none:
                return None
            raise MissingRequiredError("This field cannot have the value None or ''")
        return Text.to_text_nonnull(
            text,
            force_plaintext,
            max_length,
        )

    @staticmethod
    def to_text_nonnull(
        text: str | Text,
        force_plaintext: bool = False,
        max_length: int | None = None,
    ) -> Text:
        """
        Coerces `str` or `Text` objects into `Text` objects, but does not allow `None` values.

        Args:
            text: the `str` or `Text` object to ensure is in `Text` format.
            force_plaintext: if `True`, forces the `str` or `Text` object
                into a `Text` object with the type `TextType.PLAINTEXT`.
            max_length: `text` will be checked against this length in addition
                to the standard `Text` limit of 3000 characters.

        Returns:
            A `Text` object created from the input.

        Throws:
            InvalidUsageError: if the text length exceeds the specified max_length.
        """
        original_type = text.text_type if isinstance(text, Text) else None
        type_ = TextType.PLAINTEXT if force_plaintext else original_type or TextType.MARKDOWN
        if text and max_length and len(text) > max_length:
            raise LengthError(f"`text` length ({len(text)}) exceeds `max_length` ({max_length})")
        if isinstance(text, str):
            return Text(text=text, type_=type_)
        elif isinstance(text, Text):
            return Text(text=text.text, type_=type_, emoji=text.emoji, verbatim=text.verbatim)
        else:
            raise TypeMismatchError("This field must be a string or Text object")

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> Text:
        """Parse a Slack-shaped ``text`` composition object back into a ``Text``.

        Unknown fields are ignored so that future Slack additions do not
        break round-tripping.

        Args:
            data: a dict matching the Slack ``text`` composition-object shape,
                e.g. ``{"type": "mrkdwn", "text": "hi", "verbatim": True}``.

        Returns:
            A ``Text`` instance.

        Throws:
            MissingRequiredError: if ``data["text"]`` is absent.
            TypeMismatchError: if ``data["type"]`` is not one of the
                allowable ``TextType`` values.
        """
        if "text" not in data:
            raise MissingRequiredError("Text payload is missing required `text` field.")
        type_str = data.get("type", TextType.MARKDOWN.value)
        try:
            text_type = TextType(type_str)
        except ValueError as exc:
            raise TypeMismatchError(
                f"Text `type` must be one of {[t.value for t in TextType]}, got {type_str!r}."
            ) from exc
        return cls(
            text=data["text"],
            type_=text_type,
            emoji=bool(data.get("emoji", False)),
            verbatim=bool(data.get("verbatim", False)),
        )

    def __str__(self) -> str:
        return dumps(self._resolve())

    def __len__(self) -> int:
        return len(self.text)

    def __eq__(self, other) -> bool:
        return (
            self.text_type == other.text_type
            and self.text == other.text
            and self.emoji == other.emoji
            and self.verbatim == other.verbatim
        )

from_dict classmethod

from_dict(data)

Parse a Slack-shaped text composition object back into a Text.

Unknown fields are ignored so that future Slack additions do not break round-tripping.

Parameters:

Name Type Description Default
data dict[str, Any]

a dict matching the Slack text composition-object shape, e.g. {"type": "mrkdwn", "text": "hi", "verbatim": True}.

required

Returns:

Type Description
Text

A Text instance.

Throws

MissingRequiredError: if data["text"] is absent. TypeMismatchError: if data["type"] is not one of the allowable TextType values.

Source code in slackblocks/objects.py
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
@classmethod
def from_dict(cls, data: dict[str, Any]) -> Text:
    """Parse a Slack-shaped ``text`` composition object back into a ``Text``.

    Unknown fields are ignored so that future Slack additions do not
    break round-tripping.

    Args:
        data: a dict matching the Slack ``text`` composition-object shape,
            e.g. ``{"type": "mrkdwn", "text": "hi", "verbatim": True}``.

    Returns:
        A ``Text`` instance.

    Throws:
        MissingRequiredError: if ``data["text"]`` is absent.
        TypeMismatchError: if ``data["type"]`` is not one of the
            allowable ``TextType`` values.
    """
    if "text" not in data:
        raise MissingRequiredError("Text payload is missing required `text` field.")
    type_str = data.get("type", TextType.MARKDOWN.value)
    try:
        text_type = TextType(type_str)
    except ValueError as exc:
        raise TypeMismatchError(
            f"Text `type` must be one of {[t.value for t in TextType]}, got {type_str!r}."
        ) from exc
    return cls(
        text=data["text"],
        type_=text_type,
        emoji=bool(data.get("emoji", False)),
        verbatim=bool(data.get("verbatim", False)),
    )

to_text staticmethod

to_text(
    text: str | Text | None,
    force_plaintext: bool = False,
    max_length: int | None = None,
    *,
    allow_none: Literal[False] = False,
) -> Text
to_text(
    text: str | Text | None,
    force_plaintext: bool = False,
    max_length: int | None = None,
    *,
    allow_none: Literal[True],
) -> Text | None
to_text(
    text: str | Text | None,
    force_plaintext: bool = False,
    max_length: int | None = None,
    *,
    allow_none: bool,
) -> Text | None
to_text(
    text,
    force_plaintext=False,
    max_length=None,
    allow_none=False,
)

Coerces str or Text objects into Text objects.

Parameters:

Name Type Description Default
text str | Text | None

the str or Text object to ensure is in Text format.

required
force_plaintext bool

if True, forces the str or Text object into a Text object with the type TextType.PLAINTEXT.

False
max_length int | None

text will be checked against this length in addition to the standard Text limit of 3000 characters.

None
allow_none bool

whether to accept None as a valid value for text. The return type narrows based on this value:

  • allow_none=False (the default) -> always returns Text.
  • allow_none=True -> returns Text | None.
False
Source code in slackblocks/objects.py
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
@staticmethod
def to_text(
    text: str | Text | None,
    force_plaintext: bool = False,
    max_length: int | None = None,
    allow_none: bool = False,
) -> Text | None:
    """
    Coerces `str` or `Text` objects into `Text` objects.

    Args:
        text: the `str` or `Text` object to ensure is in `Text` format.
        force_plaintext: if `True`, forces the `str` or `Text` object
            into a `Text` object with the type `TextType.PLAINTEXT`.
        max_length: `text` will be checked against this length in addition
            to the standard `Text` limit of 3000 characters.
        allow_none: whether to accept `None` as a valid value for `text`.
            The return type narrows based on this value:

            - ``allow_none=False`` (the default) -> always returns ``Text``.
            - ``allow_none=True`` -> returns ``Text | None``.
    """
    if text is None:
        if allow_none:
            return None
        raise MissingRequiredError("This field cannot have the value None or ''")
    return Text.to_text_nonnull(
        text,
        force_plaintext,
        max_length,
    )

to_text_nonnull staticmethod

to_text_nonnull(
    text, force_plaintext=False, max_length=None
)

Coerces str or Text objects into Text objects, but does not allow None values.

Parameters:

Name Type Description Default
text str | Text

the str or Text object to ensure is in Text format.

required
force_plaintext bool

if True, forces the str or Text object into a Text object with the type TextType.PLAINTEXT.

False
max_length int | None

text will be checked against this length in addition to the standard Text limit of 3000 characters.

None

Returns:

Type Description
Text

A Text object created from the input.

Throws

InvalidUsageError: if the text length exceeds the specified max_length.

Source code in slackblocks/objects.py
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
@staticmethod
def to_text_nonnull(
    text: str | Text,
    force_plaintext: bool = False,
    max_length: int | None = None,
) -> Text:
    """
    Coerces `str` or `Text` objects into `Text` objects, but does not allow `None` values.

    Args:
        text: the `str` or `Text` object to ensure is in `Text` format.
        force_plaintext: if `True`, forces the `str` or `Text` object
            into a `Text` object with the type `TextType.PLAINTEXT`.
        max_length: `text` will be checked against this length in addition
            to the standard `Text` limit of 3000 characters.

    Returns:
        A `Text` object created from the input.

    Throws:
        InvalidUsageError: if the text length exceeds the specified max_length.
    """
    original_type = text.text_type if isinstance(text, Text) else None
    type_ = TextType.PLAINTEXT if force_plaintext else original_type or TextType.MARKDOWN
    if text and max_length and len(text) > max_length:
        raise LengthError(f"`text` length ({len(text)}) exceeds `max_length` ({max_length})")
    if isinstance(text, str):
        return Text(text=text, type_=type_)
    elif isinstance(text, Text):
        return Text(text=text.text, type_=type_, emoji=text.emoji, verbatim=text.verbatim)
    else:
        raise TypeMismatchError("This field must be a string or Text object")

TextType

Allowable types for Slack Text objects.

traditional markdown formatting, see

https://api.slack.com/reference/surfaces/formatting#basic-formatting

PLAINTEXT: simple Unicode text with no formatting (e.g. bold) features.

N.B: some usages of Text objects only allow the PLAINTEXT variety.

Source code in slackblocks/objects.py
66
67
68
69
70
71
72
73
74
75
76
77
78
class TextType(Enum):
    """
    Allowable types for Slack Text objects.

    MARKDOWN: traditional markdown formatting, see
        <https://api.slack.com/reference/surfaces/formatting#basic-formatting>
    PLAINTEXT: simple Unicode text with no formatting (e.g. bold) features.

    N.B: some usages of Text objects only allow the `PLAINTEXT` variety.
    """

    MARKDOWN = "mrkdwn"
    PLAINTEXT = "plain_text"

Trigger

Contains information about a trigger.

See: https://api.slack.com/automation/triggers.

Parameters:

Name Type Description Default
url str required
customizable_input_parameters InputParameter | list[InputParameter] | None

a list of InputParameter objects which map to those parameters defined on the Workflow in which they are provided.

required
Throws

InvalidUsageError: when any of the items in customizable_input_parameters is not a valid InputParameter.

Source code in slackblocks/objects.py
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
class Trigger(CompositionObject):
    """
    Contains information about a trigger.

    See: <https://api.slack.com/automation/triggers>.

    Args:
        url: a link trigger URL, see
            <https://api.slack.com/automation/triggers/link>
        customizable_input_parameters: a list of `InputParameter` objects
            which map to those parameters defined on the Workflow in
            which they are provided.

    Throws:
        InvalidUsageError: when any of the items in
            `customizable_input_parameters` is not a valid `InputParameter`.
    """

    def __init__(
        self,
        url: str,
        customizable_input_parameters: InputParameter | list[InputParameter] | None,
    ) -> None:
        super().__init__(type_=CompositionObjectType.TRIGGER)
        self.url = url
        self.customizable_input_parameters = coerce_to_list(
            customizable_input_parameters, InputParameter, allow_none=True
        )

    def _resolve(self) -> dict[str, Any]:
        # Trigger does not include "type" in its rendered JSON.
        return resolve(
            {
                "url": self.url,
                "customizable_input_parameters": self.customizable_input_parameters
                if self.customizable_input_parameters
                else None,
            }
        )

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> Trigger:
        """Parse a Slack ``trigger`` composition object."""
        if "url" not in data:
            raise MissingRequiredError("Trigger payload is missing required `url` field.")
        raw_params = data.get("customizable_input_parameters")
        params: list[InputParameter] | None
        if raw_params is None:
            params = None
        else:
            params = [InputParameter.from_dict(p) for p in raw_params]
        return cls(url=data["url"], customizable_input_parameters=params)

from_dict classmethod

from_dict(data)

Parse a Slack trigger composition object.

Source code in slackblocks/objects.py
735
736
737
738
739
740
741
742
743
744
745
746
@classmethod
def from_dict(cls, data: dict[str, Any]) -> Trigger:
    """Parse a Slack ``trigger`` composition object."""
    if "url" not in data:
        raise MissingRequiredError("Trigger payload is missing required `url` field.")
    raw_params = data.get("customizable_input_parameters")
    params: list[InputParameter] | None
    if raw_params is None:
        params = None
    else:
        params = [InputParameter.from_dict(p) for p in raw_params]
    return cls(url=data["url"], customizable_input_parameters=params)

Workflow

Contains information about a workflow.

See https://api.slack.com/automation/workflows.

Parameters:

Name Type Description Default
trigger Trigger

a Trigger object that will initiate the workflow.

required
Source code in slackblocks/objects.py
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
class Workflow(CompositionObject):
    """
    Contains information about a workflow.

    See <https://api.slack.com/automation/workflows>.

    Args:
        trigger: a `Trigger` object that will initiate the workflow.
    """

    def __init__(self, trigger: Trigger) -> None:
        super().__init__(type_=CompositionObjectType.WORKFLOW)
        self.trigger = trigger

    def _resolve(self) -> dict[str, Any]:
        # Workflow does not include "type" in its rendered JSON.
        return resolve({"trigger": self.trigger})

    @classmethod
    def from_url(cls, url: str, **input_parameters: str) -> Workflow:
        """Build a `Workflow` from a trigger URL and input parameters in one call.

        ``Workflow.from_url(url, a='1', b='2')`` is equivalent to::

            Workflow(
                trigger=Trigger(
                    url=url,
                    customizable_input_parameters=[
                        InputParameter(name='a', value='1'),
                        InputParameter(name='b', value='2'),
                    ],
                ),
            )

        When no input parameters are supplied, the resulting trigger has
        ``customizable_input_parameters=None`` (the key is omitted from JSON).

        Args:
            url: the link trigger URL.
            **input_parameters: zero or more ``name=value`` pairs that become
                ``InputParameter`` entries on the trigger.

        Returns:
            A new ``Workflow`` instance.
        """
        params: list[InputParameter] | None
        if input_parameters:
            params = [
                InputParameter(name=name, value=value) for name, value in input_parameters.items()
            ]
        else:
            params = None
        return cls(trigger=Trigger(url=url, customizable_input_parameters=params))

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> Workflow:
        """Parse a Slack ``workflow`` composition object."""
        if "trigger" not in data:
            raise MissingRequiredError("Workflow payload is missing required `trigger` field.")
        return cls(trigger=Trigger.from_dict(data["trigger"]))

from_dict classmethod

from_dict(data)

Parse a Slack workflow composition object.

Source code in slackblocks/objects.py
803
804
805
806
807
808
@classmethod
def from_dict(cls, data: dict[str, Any]) -> Workflow:
    """Parse a Slack ``workflow`` composition object."""
    if "trigger" not in data:
        raise MissingRequiredError("Workflow payload is missing required `trigger` field.")
    return cls(trigger=Trigger.from_dict(data["trigger"]))

from_url classmethod

from_url(url, **input_parameters)

Build a Workflow from a trigger URL and input parameters in one call.

Workflow.from_url(url, a='1', b='2') is equivalent to::

Workflow(
    trigger=Trigger(
        url=url,
        customizable_input_parameters=[
            InputParameter(name='a', value='1'),
            InputParameter(name='b', value='2'),
        ],
    ),
)

When no input parameters are supplied, the resulting trigger has customizable_input_parameters=None (the key is omitted from JSON).

Parameters:

Name Type Description Default
url str

the link trigger URL.

required
**input_parameters str

zero or more name=value pairs that become InputParameter entries on the trigger.

{}

Returns:

Type Description
Workflow

A new Workflow instance.

Source code in slackblocks/objects.py
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
@classmethod
def from_url(cls, url: str, **input_parameters: str) -> Workflow:
    """Build a `Workflow` from a trigger URL and input parameters in one call.

    ``Workflow.from_url(url, a='1', b='2')`` is equivalent to::

        Workflow(
            trigger=Trigger(
                url=url,
                customizable_input_parameters=[
                    InputParameter(name='a', value='1'),
                    InputParameter(name='b', value='2'),
                ],
            ),
        )

    When no input parameters are supplied, the resulting trigger has
    ``customizable_input_parameters=None`` (the key is omitted from JSON).

    Args:
        url: the link trigger URL.
        **input_parameters: zero or more ``name=value`` pairs that become
            ``InputParameter`` entries on the trigger.

    Returns:
        A new ``Workflow`` instance.
    """
    params: list[InputParameter] | None
    if input_parameters:
        params = [
            InputParameter(name=name, value=value) for name, value in input_parameters.items()
        ]
    else:
        params = None
    return cls(trigger=Trigger(url=url, customizable_input_parameters=params))