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)
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.

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.

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.

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.

Was this helpful?