Upload Custom Error Template

POST /v1/templates

Upload a custom HTML page that Ngris will serve in place of the default error page for a given error condition (firewall block, rate limit, bad gateway, etc.). The upload is gated by the custom_error_pages entitlement.

The request is multipart/form-data, not JSON — the HTML is passed as a file part named template. Each upload is auto-validated by an AI content-safety check (XSS, miners, trackers, phishing) before being stored; failing content is rejected with 400.

Request (multipart/form-data)

template file Req
The HTML file to upload. Max size 100 KB.
type string Req
Which error this page replaces. One of: firewall, rate_limit, bad_gateway, service_unavailable, not_found, request_timeout, internal_error, gateway_timeout.
active string
Pass the literal string "true" to mark the new template active immediately. Any other value (or omission) uploads it as inactive — you can activate later via activate.

Response (201 Created)

The newly-created template row (same shape as a list entry, including the rendered content). Its uuid addresses it in activate and delete.

Deprecated: until the sunset this response also carries the legacy integer id and *_id fields. Don't use them; read uuid and the *_uuid fields.

Errors

400 Bad Request
File missing, file > 100 KB, unknown type, or AI safety check found unsafe content. Error body is human-readable (e.g. Security Check Failed: contains tracking pixel).
401 Unauthorized
Missing or invalid auth.
403 Forbidden
Caller's plan doesn't include the custom_error_pages entitlement.
503 Service Unavailable
AI validation service is offline. Upload is rejected fail-closed; retry later.
Request
curl -X POST "https://api.ngris.com/v1/templates" \ -H "X-API-KEY: <your_api_key>" \ -F "template=@./403.html;type=text/html" \ -F "type=firewall" \ -F "active=true"
Python
from ngris import Ngris client = Ngris(api_key="<your_api_key>") # Sent as multipart/form-data: the page as the "template" file part. with open("403.html", "rb") as f: tpl = client.templates.upload("firewall", f.read(), active=True) print(f"Uploaded {tpl.uuid} ({tpl.type})")
Response — 201 Created
{ "uuid": "019b0817-a55a-7007-b556-eea4fe532a62", "user_uuid": "019a52c2-ca1e-78ba-ab5c-50362cc29134", "type": "firewall", "content": "<!doctype html><html><body>Blocked.</body></html>", "is_active": true, "created_at": "2026-04-29T13:00:00Z", "updated_at": "2026-04-29T13:00:00Z" }
Was this helpful?
Was this helpful?