Hono 4.13.13 deprecates app.mount(): mount a Sume webhook sub-app
Hono 4.13.13 deprecates app.mount() in favor of Mount Middleware. A Sume webhook receiver written as a Hono sub-app uses app.route() and keeps its raw body.

Hono 4.13.13, released 2026-10-04, adds hono/mount as Mount Middleware and deprecates app.mount(). That method mounts a foreign fetch-style app. A Sume webhook receiver is a Hono route, so it uses app.route() and does not depend on mount at all. The fix for a receiver is to keep the verifier in its own sub-app and read the raw body inside it.
What changed in 4.13.13
The release notes name the new middleware and the deprecation. They do not say when app.mount() will be removed. Earlier notes (4.13.10) say hono/<adapter> imports are removed in v5, so plan for one clean-up pass.
| Version | Date | Change that matters here |
|---|---|---|
| 4.13.10 | 2026-09-28 | Adapters split into @hono/* packages |
| 4.13.11 | 2026-09-29 | serveStatic double-decode fix; % rejected unless allowPercentInPath: true |
| 4.13.12 | 2026-09-30 | Patch release |
| 4.13.13 | 2026-10-04 | hono/mount added; app.mount() deprecated |
Where a Sume receiver fits
A Sume job webhook carries job.completed, job.failed or job.canceled, signed over <timestamp>.<raw_body>. The verifier must see the exact bytes, so keep it in a sub-app with no body-parsing middleware ahead of it.
Mount order matters: register any JSON middleware on other sub-apps, not on the app that handles /hooks.
A sub-app for the receiver
app.route() is the supported way to compose Hono apps. The sub-app below refuses to run without a secret.
import { Hono } from "hono";
import { verifyWebhook } from "@sume-com/sdk";
export const hooks = new Hono<{ Bindings: { SUME_COM_WEBHOOK_SIGNING_SECRET?: string } }>();
hooks.post("/sume", async (c) => {
const secret = c.env?.SUME_COM_WEBHOOK_SIGNING_SECRET ?? globalThis.process?.env?.SUME_COM_WEBHOOK_SIGNING_SECRET;
if (!secret) return c.text("not configured", 500);
const body = await c.req.text();
if (!(await verifyWebhook({ body, headers: c.req.raw.headers, secret }))) {
return c.text("bad signature", 401);
}
const { event, job_id } = JSON.parse(body);
console.log(event, job_id);
return c.body(null, 204);
});
// in the main app: app.route("/hooks", hooks);After you deploy
Check the route with a signed dummy, then wait for a real job.
POST /v1/webhooks/test-deliveriessends a signedwebhook.testto the URL you give it.- Return a 2xx after you store the event; Sume retries up to 10 times at 30 second spacing.
- Use
POST /v1/jobs/{job_id}/webhook/redeliverto replay a real terminal event with a fresh signature.
Why a sub-app is the safer shape
A receiver has one job: read bytes, verify, store, answer. Putting it in its own Hono instance keeps that job away from middleware that other routes need, such as a JSON body parser, a CORS layer or a logger that reads the body. Any one of those can consume the stream before the verifier sees it, and then every signature check fails with no obvious cause.
With app.route("/hooks", hooks) the sub-app keeps its own middleware stack. Order inside the sub-app still counts, so register nothing before the handler that touches the body.
The 4.13.10 notes say hono/<adapter> imports are removed in v5, and 4.13.13 deprecates app.mount(). Neither affects a plain route. Search your code for both once, record the result in your upgrade notes, and move on. If you do use app.mount() to host another fetch-style app, read the 4.13.13 notes for the Mount Middleware replacement before you change anything.
Sources
Related posts
More in Developers
- Why input_references fails on Imagen, Recraft, Soul and Qwen Max
Five Sume image rows list zero input_references: imagen-4 fast and ultra, recraft-v4, Soul and qwen-image-max. Check the catalog in Python first.
- Is Higgsfield Genjutsu in your Sume catalog? Check, then price it
higgsfield-genjutsu appears in the catalog only when its provider is configured. List your models, then price a 4 to 30 second source at 480p or 720p.
- Jupyter: move Sora cells to Sume, where a cell rerun is a retry
Re-running a notebook cell resubmits the request. Hold one Idempotency-Key per take in a variable so Sume returns the first video job, and show the file inline.
- Kotlin: submit and poll a Sume video job with java.net.http
A Kotlin port of a Sora videos call: POST /v1/videos with an Idempotency-Key, poll every 30 seconds until done, with the JDK client and kotlinx.serialization.
Written by Sume