Docs/Developers
REST API
The REST API creates, updates, lists and deletes links from your own code, for example to make a short link for every new product or campaign. It's on every plan. All plans
API keys
Open API Keys under Developer and click + New key.
New API keyA key acts as you: it reaches the links you can reach.
Shop backend
90 days
✓links.view✓links.create✓links.updatelinks.delete✓analytics.view
CancelCreate
- The secret (
sk_live_…) is shown once. Store it in your secrets manager. - Give each key only the scopes it needs. A key can't have a scope its creator doesn't have, and it sees the same links its creator sees.
- Keys expire after 30 days by default. Owners and Developers can set a different lifetime, and extend a key by 30 days with +30d.
- Revoke a key from the list at any time; requests with it fail immediately.
Authentication
Send the key as a bearer token. The project comes from the key, so it isn't in the URL.
Base URL and header
Base URL: https://api.cloakingurl.com/api/v1 Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Content-Type: application/json
Create a link
Links are created on a domain by its ID. To find it, create one link in the dashboard, then list your links: every link includes its domainId.
Find the domain ID
curl https://api.cloakingurl.com/api/v1/links?take=1 \
-H "Authorization: Bearer $CLOAKINGURL_KEY"
# → { "data": [ { "id": "…", "slug": "spring", "domainId": "8f0c…-…", "domain": { "hostname": "go.yourbrand.com" }, … } ] }POST /links
curl -X POST https://api.cloakingurl.com/api/v1/links \
-H "Authorization: Bearer $CLOAKINGURL_KEY" \
-H "Content-Type: application/json" \
-d '{
"domainId": "8f0c…",
"slug": "black-friday",
"name": "Black Friday — email",
"defaultDestination": "https://yourbrand.com/bf?utm_source=newsletter",
"tags": ["bf2026", "email"],
"expireAt": "2026-11-30T23:59:00Z",
"limits": { "onLimitAction": "serve_fallback", "fallbackDestination": "https://yourbrand.com/sale-ended" }
}'
# 201 → { "link": { "id": "…", "slug": "black-friday", "status": "active", … } }| Field | Type | Notes |
|---|---|---|
domainId | uuid, required | An active domain in the project. |
defaultDestination | URL, required | Where the link goes. |
slug | string | 1–64 characters: letters, numbers, - and _. Random if omitted. |
name | string | Up to 200 characters. |
status | active | paused | Default active. |
tags | string[] | Created if they don't exist. |
startAt, expireAt | ISO 8601 | UTC. Expiry needs Starter or higher. |
limits | object | maxClicks, onLimitAction (serve_fallback, return_404, disable) and fallbackDestination. |
Good to knowTargeting rules, A/B variants, protection and cloaking are set in the dashboard. A link created through the API can be opened there to add them.
Endpoints
| Method and path | Scope | Does |
|---|---|---|
POST /links | links.create | Create a link. |
GET /links | links.view | List links. Query: status, domainId, take (max 200), skip. |
GET /links/:id | links.view | One link, with its rules, variants and tags. |
PATCH /links/:id | links.update | Change slug, name, defaultDestination, status, folder, startAt, expireAt, limits or tags. |
DELETE /links/:id | links.delete | Move to trash. Add ?hard=true to delete permanently. |
GET /analytics/clicks | analytics.view | Clicks and unique visitors, last 30 days: { range, clicks, uniqueVisitors }. |
Point a link somewhere else
curl -X PATCH https://api.cloakingurl.com/api/v1/links/LINK_ID \
-H "Authorization: Bearer $CLOAKINGURL_KEY" \
-H "Content-Type: application/json" \
-d '{ "defaultDestination": "https://yourbrand.com/new-page" }'Node.js
const res = await fetch("https://api.cloakingurl.com/api/v1/links", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CLOAKINGURL_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ domainId, slug: product.sku.toLowerCase(), defaultDestination: product.url }),
});
if (res.status === 409) throw new Error("slug already used");
const { link } = await res.json();Errors and quotas
Errors return JSON like { "error": "slug_taken" }.
| Status | error | Meaning |
|---|---|---|
| 400 | invalid_input | The body doesn't match the fields above (details in issues). |
| 401 | invalid_api_key | Missing, wrong, revoked or expired key. |
| 402 | limit_reached / feature_not_available | Plan limit (such as links) reached, or the feature needs a higher plan. |
| 403 | insufficient_scope / blocked_host | The key lacks the scope, or the destination is blocked. |
| 404 | not_found / domain_not_found | No such link or domain, or the key can't see it. |
| 409 | slug_taken | The slug is already used on that domain. |
| 429 | limit_reached | Monthly API quota used up. |
Monthly requests: 1,000 on Free, 50,000 on Starter, 500,000 on Pro, 5 million on Business, unlimited on Enterprise. The quota resets on the 1st of each month (UTC).