Mattermost incoming webhook for Sume jobs: markdown, 16,383 chars

Post a Sume job result to Mattermost with an incoming webhook: text is markdown, attachments sit at top level, and posts up to 16,383 characters are supported.

4 min readSume
All posts

A Mattermost incoming webhook takes a JSON body with a text field written in markdown, so a Sume job result fits in one short message: the job id, the status and the artifact link. Mattermost documents support for posts of up to 16,383 characters, and says that longer text is split into several posts. That is far more than a link needs.

What Mattermost documents

The incoming webhook page is short, and these are the points that matter for a job notifier.

Mattermost incoming webhook facts (Mattermost Developers, read 2026-10-05)
TopicMattermost documents
BodyJSON with a text field
Formattingtext is markdown
AttachmentsMust be top-level in the JSON, not nested in another object
LengthPosts up to 16,383 characters; longer text is split
Rate limitNone documented on that page

Sume payload in, markdown out

A Sume job webhook has event, job_id, status (OK or ERROR) and payload.artifacts[]. Verify the signature first. The header is x-sume-webhook-signature: sume-v1=<hex>, an HMAC-SHA256 over <timestamp>.<raw_body>; the SDK ships verifyWebhook. Then build the markdown.

import json, urllib.request

def to_markdown(event: dict) -> str:
    arts = (event.get("payload") or {}).get("artifacts") or []
    lines = ["**Sume job** `%s` is %s" % (event["job_id"], event["status"])]
    for a in arts:
        lines.append("- [%s](%s)" % (a.get("type", "file"), a["url"]))
    return "\n".join(lines)

def post(hook_url: str, event: dict) -> None:
    body = json.dumps({"text": to_markdown(event)}).encode()
    req = urllib.request.Request(hook_url, data=body,
                                 headers={"Content-Type": "application/json"})
    urllib.request.urlopen(req, timeout=8).read()

No documented rate limit is not no limit

Mattermost does not state a rate limit on that page, but your server and any proxy in front of it may have one. Sume sends terminal events only, one per job, and retries a failed delivery up to 10 times at 30 second spacing by default. If many jobs finish at once, put a queue between the receiver and Mattermost, return 2xx to Sume first, and post at a modest pace.

Limits

The webhook can only post, so it cannot update the message when a job changes state. A failed job arrives as status: "ERROR" with no artifacts; show the id and an error line. Do not paste the Sume API key or the signing secret into a message, and do not include private prompts in a shared channel.

Before you ship

  • Post the artifact URL, not the file bytes.
  • Include the job id so a person can ask for a Redeliver.
  • Keep attachments at the top level of the JSON.
  • Treat the webhook URL as a secret.
  • Verify the Sume signature before you build any message.
  • Skip the post for events you do not need, such as test deliveries.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume