Quick links

Images

Attach up to five PNG, JPEG or WebP screenshots with multipart/form-data, stay inside the plan’s byte budget, and retry without duplicates.

On this page

A failing test, a broken dashboard, a console full of red: sometimes a screenshot says more than the body text. To attach images, send the alert as multipart/form-data instead of JSON.

Request format

Link to this section
  • Send each normal field (title, body, category, severity) as its own form part.
  • Send data and todo, if you use them, as form parts whose value is a JSON-encoded string, not as nested form fields.
  • Add one part named attachment per image, repeating the name, up to five times.
  • Keep the Authorization and Idempotency-Key headers exactly as for JSON.

What is accepted

Link to this section
Table, scrolls horizontally when narrow
Rule Detail
Count Up to five images per alert.
Formats PNG, JPEG and WebP. SVG, GIF, PDF and every other type are rejected with 400 INVALID_PAYLOAD.
Detection The server reads each file’s leading bytes. File extensions and the MIME type you send are ignored.
Plans A guest account, Standard and Zona Plus. A guest sends images within 1 MiB per alert and 1 MiB a UTC day.
Image budget All images together must fit the plan’s byte budget: 1 MiB on a guest account, 5 MiB for standard accounts by default, 10 MiB on Zona Plus.
Daily budget Each account can store 1 MiB of images a UTC day on a guest account, 10 MiB on Standard and 50 MiB on Zona Plus. It starts again at 00:00 UTC.
Request size The whole multipart request may be at most the image budget plus 64 KiB. Anything larger returns 413 PAYLOAD_TOO_LARGE.

The budget is shared, not per file. Five 1.5 MiB screenshots do not fit a 5 MiB standard budget; downscale or crop them, or send fewer.

curl --request POST \
  "https://gerncrjtrdjtjvybvseb.supabase.co/functions/v1/notify" \
  --header "Authorization: Bearer $ZONA_SOURCE_TOKEN" \
  --header "Idempotency-Key: build-20260726-15" \
  --form "title=Build failed" \
  --form "body=Unit tests failed on the release branch; screenshots attached." \
  --form "category=build" \
  --form "severity=high" \
  --form 'data={"buildId":"2026.07.26.15","branch":"release"}' \
  --form "attachment=@failure-screenshot.png" \
  --form "attachment=@failure-console.png"

Each attachment=@file is one image. There is no Content-Type header on purpose.

Images and idempotency

Link to this section

The image set is part of the payload. Zona fingerprints each file with SHA-256, so:

  • Retrying with the same key, the same fields and the same files in the same order returns 200 and the original alert.
  • Reusing the key with any different set of images, whether one was added, removed, swapped or reordered, returns 409 IDEMPOTENCY_CONFLICT.

When the upload fails after acceptance

Link to this section

Image storage happens after the alert itself is accepted, and it is best effort. If storage fails, you still get 202, with the alert in the inbox and no images:

{
  "idempotentReplay": false,
  "attachmentAccepted": false,
  "attachmentError": "UPLOAD_FAILED"
}

A replay with the same key does not try the upload again; it only returns the stored result. If the images matter, send a new alert with a new key.

When the day’s image budget is used up

Link to this section

If the images would pass what is left of the account’s daily image budget, Zona keeps the alert and drops the images. You still get 202, with the alert in the inbox and no images:

{
  "idempotentReplay": false,
  "attachmentAccepted": false,
  "attachmentError": "IMAGE_BUDGET_REACHED"
}

The budget starts again at 00:00 UTC.

When attachments are switched off

Link to this section

The operator can pause image attachments. While they are paused, a multipart request with images returns 403 ATTACHMENTS_DISABLED. Send the same event without images, as JSON, using a new idempotency key.

Privacy and retention

Link to this section

Images are private. Only the owning account can view them, through short-lived signed links in the app, and they follow the same retention window as their alert. Like the title and body, images end up on your phone, so never attach a screenshot that shows a token, a password or other secrets.