Integrations
Connect URLY.UZ to Zapier, Make, n8n and your own systems through the REST API, signed webhooks, MCP, the Telegram bot and the browser extension.
Authentication
Every request carries an API key (dashboard → API keys). Give the key only the scopes it needs, for example webhooks:write for webhooks.
Authorization: Bearer urly_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Webhooks
Add a webhook endpoint in the dashboard or with POST /api/v1/webhooks. URLY sends every event as a JSON POST; the urly-webhook-id header stays the same across retries, so use it to drop duplicates.
| Event | Purpose |
|---|---|
link.created | link created |
link.updated | link updated |
link.deleted | link moved to trash |
clicks.hourly | hourly click summary |
link.clicked | every click (Business) |
domain.verified | domain verified |
subscription.changed | plan or subscription changed |
curl -s https://urly.uz/api/v1/webhooks \
-H "Authorization: Bearer $URLY_API_KEY" -H "Content-Type: application/json" \
-d '{"url":"https://hooks.example.uz/urly","events":["link.created","clicks.hourly"]}'
Verifying the signature
The urly-signature header looks like t=<time>,v1=<signature>. The signature is HMAC-SHA256("<time>.<raw body>") keyed with the endpoint secret. For 24 hours after a secret rotation two v1 values are sent. Reject requests older than 5 minutes.
// Node.js
const crypto = require("crypto");
function verify(rawBody, header, secret) {
const items = header.split(",").map((kv) => kv.split("="));
const t = Number(items.find(([k]) => k === "t")[1]);
const want = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - t) < 300;
return fresh && items.some(([k, v]) => k === "v1" && v.length === want.length &&
crypto.timingSafeEqual(Buffer.from(v), Buffer.from(want)));
}
# Python
import hashlib, hmac, time
def verify(raw_body: bytes, header: str, secret: str) -> bool:
items = [kv.split("=", 1) for kv in header.split(",")]
t = int(next(v for k, v in items if k == "t"))
want = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return abs(time.time() - t) < 300 and any(
k == "v1" and hmac.compare_digest(v, want) for k, v in items)
On a non-2xx answer URLY retries up to 9 times, from 30 seconds to 12 hours apart. After repeated failures or a 410 answer the endpoint is turned off.
Zapier
In Zapier, connect URLY with Webhooks by Zapier and the API Request action:
- Trigger: add the "Catch Hook" URL as a URLY webhook (REST hook: subscribe with POST /api/v1/webhooks, unsubscribe with DELETE /api/v1/webhooks/{id}).
- Action: POST /api/v1/links creates a short link; the response has a short_url field.
- Polling trigger: GET /api/v1/links?limit=25 returns the newest links first (deduplicate by id).
Make
In Make:
- Create a "Webhooks → Custom webhook" module and copy its address.
- Add the address under Webhooks in the URLY dashboard and pick the events.
- For actions, call the URLY API with an "HTTP → Make a request" module and an Authorization: Bearer header.
n8n
In n8n:
- Add a "Webhook" trigger node (POST) and register its production URL as a URLY webhook.
- Verify the signature in a "Code" node with the Node.js example above (turn on "Raw body" in the Webhook node).
- For actions, use an "HTTP Request" node with Header Auth (Authorization: Bearer …).
MCP
AI assistants (Claude, IDEs and other MCP clients) connect to /api/v1/mcp over Streamable HTTP with an API key.
{
"mcpServers": {
"urly": {
"type": "http",
"url": "https://urly.uz/api/v1/mcp",
"headers": { "Authorization": "Bearer ${URLY_API_KEY}" }
}
}
}
Tools: create_link, list_links, get_link_stats, create_qr. They follow the key's scopes.
Telegram
Connect the bot in Settings → Telegram: shorten links in the chat and get statistics and QR codes.
Browser extension
The Chrome and Firefox extension shortens the current page in one click, copies the result and shows a QR code. It only uses the API key you enter and loads no remote code.