Send images and files

An alert can carry up to 8 files: the screenshot of the failing test, the recording of the bug, the log that explains it. Zenhook re-hosts every one, so the alert still works after your CI artifacts expire.

Two ways in

Pick by whether your file already has a public address:

  • By URL: the file is already on the web and we can fetch it. JSON body, one line per attachment.
  • By upload: the file only exists on your machine or your runner. Send the bytes withmultipart/form-data.

Same limits, same processing, same result. Only the transport differs.

Attach by URL

Put the address in attachments. We fetch it once, re-host it, and the alert stops depending on your link staying alive.

curl -X POST https://zenhook.dev/api/webhook/YOUR_TOKEN \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Build failed",
    "level": "error",
    "attachments": [
      { "url": "https://ci.example.com/runs/123/screenshot.png" }
    ]
  }'

The URL must be http orhttps. typeis an optional hint: the real type comes from the first bytes of the file, so a mislabelled attachment still lands correctly.

Upload the file

No public URL? Send the bytes. The simplest client is a title and a file:

curl -X POST https://zenhook.dev/api/webhook/YOUR_TOKEN \
  -F 'title=Build failed' \
  -F 'level=error' \
  -F '[email protected]'

For anything richer than a title, put the JSON in apayload part and add one part per file. Every field of the webhook API works there:

curl -X POST https://zenhook.dev/api/webhook/YOUR_TOKEN \
  -F 'payload={"title":"Visual diff","level":"warning","fields":[{"label":"Suite","value":"checkout"}]};type=application/json' \
  -F '[email protected]' \
  -F '[email protected]'

Part names are yours to choose. Zenhook reads every file part it finds, in order, up to 8.

Base64, in its own field

Some senders cannot do multipart at all: Zapier, n8n, Make, any SaaS webhook that only emits JSON. For those, put the bytes indata:

curl -X POST https://zenhook.dev/api/webhook/YOUR_TOKEN \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Visual diff",
    "attachments": [
      { "data": "iVBORw0KGgoAAAANSUhEUg...", "name": "diff.png" }
    ]
  }'

Plain base64, no data: prefix. Each attachment carries exactly one source: aurl or adata, never both.

A data: URI in theurl field stays rejected. That field is capped at 2000 characters, so nothing real would fit through it anyway.

Prefer multipart when you can. Base64 inflates the payload by a third, and the caps apply to the decoded bytes, so encoding buys you nothing.

Formats and limits

  • Images: PNG, JPEG, GIF, WebP, AVIF, HEIC. Up to 10 MB.
  • Video: MP4, WebM. Up to 50 MB.
  • Audio: MP3, WAV, M4A, Ogg. Up to 25 MB.
  • Per alert: 8 files, 50 MB decoded in total.

SVG is refused. It is a script container as much as an image, and we are not going to serve one from our domain.

What happens to your file

Zenhook identifies the file by its first bytes rather than trusting its name or its content type, then re-hosts it on our own storage. The original stays untouched when it is already small and web-renderable.

  • An image past 2048 px or past 10 MB is resized to 2048 px and re-encoded to WebP. A 4000 px screenshot under 10 MB is still resized: the byte count alone is not the point.
  • iPhone photos work. HEIC is decoded and converted to WebP, so a screenshot sent straight from a phone renders everywhere.
  • Audio and video are stored as they are, within their caps.

Where they show up

Attachments render in the alert on the web app and in the iOS and Android apps. Push notifications carry the alert's title and message; open the alert to see the files.

FAQ

My attachment says it failed. Why?

The most common cause is a URL we cannot reach: a private CI artifact, a signed link that already expired, or a host that needs credentials. If the file is not publicly fetchable, upload the bytes instead of the address.

Can I send a file to an Ask?

Yes. Asks take the same attachment fields as alerts, so the person answering sees what they are deciding about.

Do attachments count towards my plan?

The alert counts as one event whatever it carries. The files themselves are not metered.