Short links
All paths below are relative to https://api-v1.zingasuite.com/papi/v1.
| Method | Path | Purpose |
|---|---|---|
GET |
/links |
List links |
POST |
/links |
Create a link |
GET |
/links/{id} |
Read a link |
PATCH |
/links/{id} |
Re-point / retitle / change status |
DELETE |
/links/{id} |
Delete a link (stops resolving) |
Create a link
curl -X POST https://api-v1.zingasuite.com/papi/v1/links \
-H "Authorization: Bearer zk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"targetUrl": "https://shop.example.com/collections/spring?utm_source=print",
"slug": "spring-sale",
"title": "Spring sale — print",
"tags": ["print", "spring-2026"]
}'
{
"id": "8f1c…",
"slug": "spring-sale",
"shortUrl": "https://zngl.ink/spring-sale",
"targetUrl": "https://shop.example.com/collections/spring?utm_source=print",
"title": "Spring sale — print",
"domainId": null,
"source": "app",
"status": "active",
"clickCount": 0,
"lastClickAt": null,
"expiresAt": null,
"createdAt": "2026-08-17T10:04:00Z"
}
| Field | Notes |
|---|---|
targetUrl |
Required — where the link sends people |
slug |
Optional. Omit for a short, unguessable generated slug. A–Z a–z 0–9 _ -, up to 255 chars |
domainId |
Optional. One of your verified hosts (Starter+); omit for the shared zngl.ink |
title |
Your own label; never shown to visitors |
tags |
Free-form list for filtering |
utm |
Object of UTM parameters appended to the destination |
expiresAt |
Optional ISO-8601 expiry. Links never expire unless you set one |
Slugs are unique per domain. A taken slug returns 409 slug_taken; a slug
with illegal characters or a reserved word returns 400 invalid_slug.
The response's shortUrl is what you hand to people — build it from that field
rather than concatenating the host and slug yourself.
List links
curl "https://api-v1.zingasuite.com/papi/v1/links?limit=50&source=app" \
-H "Authorization: Bearer zk_live_xxx"
| Parameter | Purpose |
|---|---|
source |
app (default — links you or your integration made), crm (minted by ZingaConnect for campaigns), or all |
status |
active | archived | disabled |
q |
Substring match on slug or title |
limit / offset |
Paging (default 50, max 200) |
The default source=app matters: ZingaConnect mints one link per campaign step
and URL, and those would otherwise bury your own links.
Update a link
curl -X PATCH https://api-v1.zingasuite.com/papi/v1/links/<id> \
-H "Authorization: Bearer zk_live_xxx" \
-H "Content-Type: application/json" \
-d '{"targetUrl": "https://shop.example.com/collections/summer"}'
Accepts targetUrl, title, tags, utm, expiresAt and status
(active | archived | disabled). The edge is updated in the same call, so a
disabled link stops resolving immediately and a re-pointed link starts sending
people to the new destination right away — printed QR codes included.
The slug is immutable by design. A link that has been printed or shared must
not start resolving somewhere unexpected because someone renamed it; create a new
link instead.
Delete a link
curl -X DELETE https://api-v1.zingasuite.com/papi/v1/links/<id> \
-H "Authorization: Bearer zk_live_xxx"
The link is soft-deleted and dropped from the edge, so the short URL stops
resolving. Its recorded clicks stay in your history. Prefer
status: "disabled" when you only want to take a link out of circulation.