Start
Quickstart
You need an HTTPS URL that returns JSON. Start with one of the examples, then replace it with your own endpoint.
1. Add an endpoint
A hook is a URL saved in the app under a name you choose. The app includes a few examples, so you can try a real widget before writing a server. Adding one is free, does not start the trial, and can be undone at any time.
You can also add a hook from a QR code. See llms.txt for the link format, or use a signed code shared by someone else.
https://webhookwidget.com/demo/hello
Every demo on this site is served the same way, at /demo/<id>, with
timestamps generated at the moment of the request. To add another, tap
Add Hook and give it a name and an address.
2. Add the widget
Touch and hold an empty area of the Home Screen, tap Edit then Add Widget, and choose Webhook Widget at the size you want. A new widget starts on your first hook; to point it at a different one, touch and hold it, choose Edit Widget, and pick the name.
{
"v": 1,
"refresh": 900,
"accessibility": "Hello, World! Fetched from webhookwidget.com just now",
"url": "https://webhookwidget.com/docs/quickstart/",
"view": {
"type": "vstack",
"align": "leading",
"spacing": 0,
"padding": 16,
"children": [
{
"type": "text",
"text": "WEBHOOK WIDGET",
"style": "caption2",
"weight": "bold",
"color": "secondary"
},
{"type": "spacer"},
{
"type": "text",
"text": "Hello, World!",
"style": "title2",
"weight": "bold",
"design": "rounded",
"color": "primary",
"lines": 2
},
{"type": "spacer"},
{
"type": "text",
"text": "FETCHED",
"style": "caption2",
"weight": "semibold",
"color": "tertiary"
},
{
"type": "timer",
"from": "2026-08-11T08:40:00Z",
"style": "relative",
"size": 13,
"color": "secondary"
}
]
}
} 3. Return your own JSON
The smallest valid response is a version and a single node. Everything else in the protocol is optional.
{
"v": 1,
"view": { "type": "text", "text": "Hello from my server" }
} Something worth looking at is not much bigger. This handler reads the appearance from the query string and answers with a heading, a number and a gauge:
import express from "express";
const app = express();
app.get("/widget", (request, response) => {
const { family, w, h, scheme } = request.query;
response.json({
v: 1,
refresh: 900,
accessibility: "Four tasks left today",
view: {
type: "vstack",
align: "leading",
spacing: 2,
padding: 16,
bg: scheme === "dark" ? "#14181F" : "white",
children: [
{ type: "text", text: "TODAY", style: "caption2",
weight: "bold", color: "secondary" },
{ type: "text", text: "4 left", style: "largeTitle",
weight: "bold", design: "rounded" },
{ type: "spacer" },
{ type: "gauge", value: 6, min: 0, max: 10, tint: "green" },
],
},
});
});
app.listen(3000); from flask import Flask, jsonify, request
app = Flask(__name__)
@app.get("/widget")
def widget():
dark = request.args.get("scheme") == "dark"
return jsonify({
"v": 1,
"refresh": 900,
"accessibility": "Four tasks left today",
"view": {
"type": "vstack",
"align": "leading",
"spacing": 2,
"padding": 16,
"bg": "#14181F" if dark else "white",
"children": [
{"type": "text", "text": "TODAY", "style": "caption2",
"weight": "bold", "color": "secondary"},
{"type": "text", "text": "4 left", "style": "largeTitle",
"weight": "bold", "design": "rounded"},
{"type": "spacer"},
{"type": "gauge", "value": 6, "min": 0, "max": 10, "tint": "green"},
],
},
}) 4. Choose a refresh interval
refresh is how many seconds the widget waits before fetching again, clamped to
between 5 minutes and a day. It is a request, not a promise: iOS decides when each fetch
actually happens and will stretch your interval when it feels like it.
Rather than fighting that, answer one fetch with several future states. That is what timelines are for, and it is the difference between a widget that is usually right and one that is always right.
Next steps
- Node reference — every node and every field, with a live demo each.
- Timelines and refreshes — how to stay correct on a tiny refresh budget.
- Authentication — four ways to keep the endpoint private.
- Playground — write a payload and watch it render as you type.