Slack Workflow webhook trigger: flatten a Sume result, no nested JSON

Slack's webhook-started workflows take up to 20 flat variables and 1 request per second. Flatten Sume's nested artifacts into plain text values before you post.

4 min readSume
All posts

A Slack workflow that starts with a webhook accepts only flat variables, so a Sume job.completed body cannot be forwarded as it is. Its artifacts sit in a nested payload.artifacts[] array, and Slack documents that nested JSON is not currently possible in workflow variables. Put a small relay between the two, copy four plain text values, and keep the rate at one request per second.

What Slack documents

The facts below come from Slack's help page on webhook-started workflows.

Slack webhook trigger facts (Slack Help Center, read 2026-10-05)
TopicSlack says
PlanAvailable on paid plans only
VariablesUp to 20 per workflow; types are channel ID, user ID, user email and text
StructureNested JSON in workflow variables is not currently possible
RateWebhook workflows are limited to one request per second
SecrecyAnyone with the request URL can start the workflow, so keep it secret

Flatten the Sume payload

A Sume job webhook has event, request_id, job_id, status (OK or ERROR) and a payload. For a completed job, payload.artifacts[] holds the artifacts with a url and a type. Slack variables are text, so pick the first artifact and copy its URL and type. Name the variables in the workflow exactly as the keys that you send.

import json, urllib.request

def flatten(event):
    arts = (event.get("payload") or {}).get("artifacts") or []
    first = arts[0] if arts else {}
    return {
        "job_id": event["job_id"],
        "status": event["status"],
        "kind": first.get("type", ""),
        "media_url": first.get("url", ""),
    }

def send_to_slack(trigger_url, event):
    req = urllib.request.Request(
        trigger_url,
        data=json.dumps(flatten(event)).encode(),
        headers={"Content-Type": "application/json"},
    )
    urllib.request.urlopen(req, timeout=10).read()

Pace the relay, then answer Sume first

Sume gives your receiver 10 seconds per attempt and wants a 2xx once you have stored the event. Do not call Slack inside that window. Write the event to a queue, return 204, and let a worker drain the queue at one send per second or slower. If many jobs finish together, the queue absorbs the burst; Slack never sees more than its limit.

Failed jobs too

job.failed and job.canceled use status: "ERROR" and carry no artifacts. Let the workflow branch on status, so the channel gets a short failure notice and the link stays empty. Keep job_id in every message: it is the key that you need for Redeliver, for status reads and for support.

When not to use this

If you need buttons, threads, or file uploads, a Slack app is the better tool. A webhook-started workflow is a good fit for a one-way track-is-ready message. And because the trigger URL is a bearer secret, store it like an API key and rotate it if it leaks.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume